Compare commits

...

1 Commits

Author SHA1 Message Date
8bc4f4fe04 Tui 模块开发完成,数据正常写入数据库 2026-03-06 16:33:08 +08:00
7 changed files with 674 additions and 535 deletions

View File

@@ -3,6 +3,7 @@ package database
import ( import (
"database/sql" "database/sql"
"fmt" "fmt"
"strings"
"sync" "sync"
"sunhpc/pkg/config" "sunhpc/pkg/config"
@@ -22,22 +23,20 @@ var (
dbErr error dbErr error
) )
// =========================================================
// 封装数据库函数使用Go实现 // 封装数据库函数使用Go实现
// =========================================================
// MapCategory - 根据类别名称查ID // MapCategory - 根据类别名称查ID
// 查询方式: globalID, err := db.MapCategory(conn, "global") // 查询方式: globalID, err := db.MapCategory(conn, "global")
func MapCategory(conn *sql.DB, catname string) (int, error) { func MapCategory(conn *sql.DB, catname string) (int, error) {
var id int var id int
query := "select id from categories where name = ?" query := "select id from categories where name = ?"
logger.Debugf("查询SQL: %s", query) fullSQL := ReplaceSQLQuery(query, catname)
logger.Debugf("查询类别ID: %s", catname)
err := conn.QueryRow(query, catname).Scan(&id) err := conn.QueryRow(query, catname).Scan(&id)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
logger.Debugf("未找到类别 %s, 返回ID=0", catname) logger.Debugf("未找到类别 %s, 返回ID=0", catname)
return 0, nil // 无匹配返回0 return 0, nil // 无匹配返回0
} }
logger.Debugf("查询到类别 %s, ID=%d", catname, id) logger.Debugf("查询语句: %s , CatName=%s, ID=%d", fullSQL, catname, id)
return id, nil return id, nil
} }
@@ -49,14 +48,15 @@ func MapCategoryIndex(conn *sql.DB, catindexName, categoryIndex string) (int, er
select index_id from vmapCategoryIndex select index_id from vmapCategoryIndex
where categoryName = ? and categoryIndex = ? where categoryName = ? and categoryIndex = ?
` `
logger.Debugf("查询SQL: %s", query) fullSQL := ReplaceSQLQuery(query, catindexName, categoryIndex)
logger.Debugf("查询索引ID: %s, 类别: %s", catindexName, categoryIndex)
err := conn.QueryRow(query, catindexName, categoryIndex).Scan(&id) err := conn.QueryRow(query, catindexName, categoryIndex).Scan(&id)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
logger.Debugf("未找到索引 %s, 返回ID=0", catindexName) logger.Debugf("未找到索引 %s, 返回ID=0", catindexName)
return 0, nil // 无匹配返回0 return 0, nil // 无匹配返回0
} }
logger.Debugf("查询到索引 %s, ID=%d", catindexName, id) logger.Debugf("查询语句: %s , CatIndexName=%s, CategoryIndex=%s, ID=%d",
fullSQL, catindexName, categoryIndex, id)
return id, nil return id, nil
} }
@@ -165,6 +165,13 @@ func GetDB() (*sql.DB, error) {
return return
} }
var version string
err = sqlDB.QueryRow("select sqlite_version()").Scan(&version)
if err != nil {
version = "unknown"
}
logger.Debugf("数据库版本: %s", version)
logger.Debug("数据库连接成功") logger.Debug("数据库连接成功")
dbInstance = sqlDB dbInstance = sqlDB
}) })
@@ -178,10 +185,44 @@ func GetDB() (*sql.DB, error) {
func InitTables(db *sql.DB, force bool) error { func InitTables(db *sql.DB, force bool) error {
// 临时关闭外键约束(解决外键依赖删除报错问题)
_, err := db.Exec("PRAGMA foreign_keys = OFF;")
if err != nil {
logger.Errorf("关闭外键约束失败: %v", err)
return err
}
defer func() {
// 延迟恢复外键约束(确保在函数退出时恢复)
_, err := db.Exec("PRAGMA foreign_keys = ON;")
if err != nil {
logger.Errorf("恢复外键约束失败: %v", err)
}
}()
// ✅ 调用 schema.go 中的函数 // ✅ 调用 schema.go 中的函数
//for _, ddl := range CreateTableStatements() { for name, ddl := range BaseTables() {
for _, ddl := range BaseTables() { // 删除表或者试图(如果存在)
logger.Debugf("执行: %s", ddl) logger.Debugf("执行删除 - %s", name)
// 先尝试作为表进行删除
query := fmt.Sprintf("DROP TABLE IF EXISTS %s;", name)
logger.Debugf("执行语句: %s", query)
_, err := db.Exec(query)
if err != nil {
// 如果作为表删除失败,尝试作为试图删除
logger.Debugf("删除表失败: %v", err)
query = fmt.Sprintf("DROP VIEW IF EXISTS %s;", name)
logger.Debugf("执行语句: %s", query)
_, err = db.Exec(query)
if err != nil {
return fmt.Errorf("删除失败: %w", err)
}
}
logger.Debugf("执行图表 - %s", name)
logger.Debugf("执行语句: %s", ddl)
if _, err := db.Exec(ddl); err != nil { if _, err := db.Exec(ddl); err != nil {
return fmt.Errorf("数据表创建失败: %w", err) return fmt.Errorf("数据表创建失败: %w", err)
} }
@@ -291,28 +332,36 @@ func ExecWithTransaction(ddl []string) error {
return err return err
} }
var finished bool
// 延迟处理:如果函数异常,回滚事务 // 延迟处理:如果函数异常,回滚事务
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
if !finished {
// 捕获 panic 并回滚事务 // 捕获 panic 并回滚事务
tx.Rollback() tx.Rollback()
logger.Errorf("事务执行中发生 panic: %v", r) logger.Errorf("事务执行中发生 panic: %v", r)
} }
panic(r)
}
}() }()
// 遍历执行 DDL 语句 // 遍历执行 DDL 语句
for idx, sql := range ddl { for idx, sql := range ddl {
logger.Debugf("执行 DDL 语句 %d: %s", idx+1, sql) logger.Debugf("执行 DDL 语句 %d: %s", idx+1, sql)
_, err = tx.Exec(sql) _, err = tx.Exec(sql)
if err != nil { if err != nil {
// 执行失败时,回滚事务 // 执行失败时,回滚事务
rollbackErr := tx.Rollback() rollbackErr := tx.Rollback()
finished = true // 标记事务已完成
if rollbackErr != nil { if rollbackErr != nil {
logger.Errorf("执行失败: 回滚失败: %v (原错误: %v, SQL: %s)", rollbackErr, err, sql) logger.Errorf("执行失败: 回滚失败: %v (原错误: %v, SQL: %s)", rollbackErr, err, sql)
} else { } else {
logger.Errorf("执行失败: 回滚事务: %v, SQL: %s", err, sql) logger.Errorf("执行失败: 回滚事务: %v, SQL: %s", err, sql)
} }
logger.Errorf("执行 %d 条, 失败: %w (SQL: %s)", idx+1, err, sql) logger.Errorf("执行 %d 条, 失败: %w (SQL: %s)", idx+1, err, sql)
return fmt.Errorf("执行 %d 条, 失败: %w (SQL: %s)", idx+1, err, sql)
} }
} }
@@ -323,6 +372,21 @@ func ExecWithTransaction(ddl []string) error {
return err return err
} }
finished = true // 标记事务已完成
logger.Debugf("成功执行 %d 条 SQL 语句, 事务已提交.", len(ddl)) logger.Debugf("成功执行 %d 条 SQL 语句, 事务已提交.", len(ddl))
return nil return nil
} }
func ReplaceSQLQuery(query string, args ...interface{}) string {
for _, arg := range args {
switch v := arg.(type) {
case string:
query = strings.Replace(query, "?", fmt.Sprintf("'%s'", v), 1)
case int, int64, float64:
query = strings.Replace(query, "?", fmt.Sprintf("%v", v), 1)
default:
query = strings.Replace(query, "?", fmt.Sprintf("%v", v), 1)
}
}
return strings.TrimSpace(strings.ReplaceAll(query, "\n", " "))
}

View File

@@ -4,13 +4,13 @@ package database
import ( import (
"database/sql" "database/sql"
"fmt" "fmt"
"sunhpc/pkg/logger"
) )
func BaseTables() []string { func BaseTables() map[string]string {
datalist := []string{} return map[string]string{
"appliances": `
Appliances := `
CREATE TABLE IF NOT EXISTS appliances ( CREATE TABLE IF NOT EXISTS appliances (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Name varchar(32) not null default '', Name varchar(32) not null default '',
@@ -18,10 +18,8 @@ func BaseTables() []string {
Node varchar(64) not null default '', Node varchar(64) not null default '',
OS varchar(64) not null default 'linux' OS varchar(64) not null default 'linux'
); );
` `,
datalist = append(datalist, Appliances) "memberships": `
Memberships := `
CREATE TABLE IF NOT EXISTS memberships ( CREATE TABLE IF NOT EXISTS memberships (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Name varchar(64) not null default '', Name varchar(64) not null default '',
@@ -29,30 +27,24 @@ func BaseTables() []string {
Distribution integer(11) default '1', Distribution integer(11) default '1',
Public varchar(64) not null default 'no' Public varchar(64) not null default 'no'
); );
` `,
datalist = append(datalist, Memberships) "categories": `
Categories := `
CREATE TABLE IF NOT EXISTS categories ( CREATE TABLE IF NOT EXISTS categories (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Name varchar(64) not null unique default '0', Name varchar(64) not null unique default '0',
Description varchar(255) default null, Description varchar(255) default null,
UNIQUE(Name) UNIQUE(Name)
); );
` `,
datalist = append(datalist, Categories) "catindex": `
Catindex := `
CREATE TABLE IF NOT EXISTS catindex ( CREATE TABLE IF NOT EXISTS catindex (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Name varchar(64) not null unique default '0', Name varchar(64) not null unique default '0',
Category integer not null, Category integer not null,
Foreign key(Category) references categories(ID) on delete cascade Foreign key(Category) references categories(ID) on delete cascade
); );
` `,
datalist = append(datalist, Catindex) "resolvechain": `
Resolvechain := `
CREATE TABLE IF NOT EXISTS resolvechain ( CREATE TABLE IF NOT EXISTS resolvechain (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Name varchar(64) not null default '0', Name varchar(64) not null default '0',
@@ -61,107 +53,93 @@ func BaseTables() []string {
UNIQUE(Name, Category) UNIQUE(Name, Category)
Foreign key(Category) references categories(ID) on delete cascade Foreign key(Category) references categories(ID) on delete cascade
); );
` `,
datalist = append(datalist, Resolvechain) "nodes": `
Nodes := `
CREATE TABLE IF NOT EXISTS nodes ( CREATE TABLE IF NOT EXISTS nodes (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Name varchar(128) default null, Name varchar default null,
Membership integer(11) default '2', Membership integer default '2',
CPUs integer(11) not null default '1', CPUs integer not null default '1',
Rack varchar(11) default null, Rack varchar default null,
Rank integer(11) default null, Rank integer default null,
Arch varchar(32) default null, Arch varchar default null,
OS varchar(64) not null default 'linux', OS varchar default null,
RunAction varchar(64) default 'os', RunAction varchar(64) default 'os',
InstallAction varchar(64) default 'install' InstallAction varchar(64) default 'install'
); );
create index if not exists idx_nodes_name on nodes(Name); create index if not exists idx_nodes_name on nodes(Name);
` `,
datalist = append(datalist, Nodes) "aliases": `
Aliases := `
CREATE TABLE IF NOT EXISTS aliases ( CREATE TABLE IF NOT EXISTS aliases (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Node integer(15) not null default '0', Node integer not null default '0',
Name varchar(32) default null, Name varchar default null,
Foreign key(Node) references nodes(ID) on delete cascade Foreign key(Node) references nodes(ID) on delete cascade
); );
` create index if not exists idx_aliases_name on aliases(Name);
datalist = append(datalist, Aliases) `,
"networks": `
Networks := `
CREATE TABLE IF NOT EXISTS networks ( CREATE TABLE IF NOT EXISTS networks (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Node integer(11) default null, Node integer not null default '0',
MAC varchar(64) default null, MAC varchar default null,
IP varchar(64) default null, IP varchar default null,
Name varchar(128) default null, Name varchar default null,
Device varchar(32) default null, Device varchar default null,
Subnet integer(11) default null, Subnet integer default null,
Module varchar(128) default null, Module varchar default null,
VlanID integer(11) default null, VlanID integer default null,
Options varchar(128) default null, Options varchar default null,
Channel varchar(128) default null, Channel varchar default null,
Foreign key(Node) references nodes(ID) on delete cascade, Foreign key(Node) references nodes(ID) on delete cascade,
Foreign key(Subnet) references subnets(ID) on delete cascade Foreign key(Subnet) references subnets(ID) on delete cascade
); );
` create index if not exists idx_networks_name on networks(Name);
datalist = append(datalist, Networks) `,
"globalroutes": `
GlobalRoutes := `
CREATE TABLE IF NOT EXISTS globalroutes ( CREATE TABLE IF NOT EXISTS globalroutes (
Network varchar(32) not null default '', Network varchar(32) not null default '',
Netmask varchar(32) not null default '', Netmask varchar(32) not null default '',
Gateway varchar(32) not null default '', Gateway varchar(32) not null default '',
Subnet integer(11) default null, Subnet integer default null,
Primary key(Network, Netmask) Primary key(Network, Netmask)
Foreign key(Subnet) references subnets(ID) on delete cascade Foreign key(Subnet) references subnets(ID) on delete cascade
); );
` `,
datalist = append(datalist, GlobalRoutes) "osroutes": `
OSRoutes := `
CREATE TABLE IF NOT EXISTS osroutes ( CREATE TABLE IF NOT EXISTS osroutes (
OS varchar(64) not null default 'linux', OS varchar(64) not null default 'linux',
Network varchar(32) not null default '', Network varchar(32) not null default '',
Netmask varchar(32) not null default '', Netmask varchar(32) not null default '',
Gateway varchar(32) not null default '', Gateway varchar(32) not null default '',
Subnet integer(11) default null, Subnet integer default null,
Primary key(OS, Network, Netmask) Primary key(OS, Network, Netmask)
Foreign key(Subnet) references subnets(ID) on delete cascade Foreign key(Subnet) references subnets(ID) on delete cascade
); );
` `,
datalist = append(datalist, OSRoutes) "applianceroutes": `
ApplianceRoutes := `
CREATE TABLE IF NOT EXISTS applianceroutes ( CREATE TABLE IF NOT EXISTS applianceroutes (
Appliance varchar(11) not null default '0', Appliance varchar(11) not null default '0',
Network varchar(32) not null default '', Network varchar(32) not null default '',
Netmask varchar(32) not null default '', Netmask varchar(32) not null default '',
Gateway varchar(32) not null default '', Gateway varchar(32) not null default '',
Subnet integer(11) default null, Subnet integer default null,
Primary key(Appliance, Network, Netmask) Primary key(Appliance, Network, Netmask)
Foreign key(Subnet) references subnets(ID) on delete cascade Foreign key(Subnet) references subnets(ID) on delete cascade
); );
` `,
datalist = append(datalist, ApplianceRoutes) "noderoutes": `
NodeRoutes := `
CREATE TABLE IF NOT EXISTS noderoutes ( CREATE TABLE IF NOT EXISTS noderoutes (
Node varchar(11) not null default '0', Node varchar(11) not null default '0',
Network varchar(32) not null default '', Network varchar(32) not null default '',
Netmask varchar(32) not null default '', Netmask varchar(32) not null default '',
Gateway varchar(32) not null default '', Gateway varchar(32) not null default '',
Subnet integer(11) default null, Subnet integer default null,
Primary key(Node, Network, Netmask) Primary key(Node, Network, Netmask)
Foreign key(Subnet) references subnets(ID) on delete cascade Foreign key(Subnet) references subnets(ID) on delete cascade
); );
` `,
datalist = append(datalist, NodeRoutes) "subnets": `
Subnets := `
CREATE TABLE IF NOT EXISTS subnets ( CREATE TABLE IF NOT EXISTS subnets (
ID integer primary key autoincrement, ID integer primary key autoincrement,
name varchar(32) unique not null, name varchar(32) unique not null,
@@ -171,10 +149,8 @@ func BaseTables() []string {
mtu integer(11) default '1500', mtu integer(11) default '1500',
servedns boolean default false servedns boolean default false
); );
` `,
datalist = append(datalist, Subnets) "publickeys": `
PublicKeys := `
CREATE TABLE IF NOT EXISTS publickeys ( CREATE TABLE IF NOT EXISTS publickeys (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Node integer(11) not null default '0', Node integer(11) not null default '0',
@@ -182,20 +158,16 @@ func BaseTables() []string {
Description varchar(8192) default null, Description varchar(8192) default null,
Foreign key(Node) references nodes(ID) on delete cascade Foreign key(Node) references nodes(ID) on delete cascade
); );
` `,
datalist = append(datalist, PublicKeys) "secglobal": `
SecGlobal := `
CREATE TABLE IF NOT EXISTS secglobal ( CREATE TABLE IF NOT EXISTS secglobal (
Attr varchar(128) default null, Attr varchar(128) default null,
Value text, Value text,
Enc varchar(128) default null, Enc varchar(128) default null,
Primary key(Attr) Primary key(Attr)
); );
` `,
datalist = append(datalist, SecGlobal) "secnodes": `
SecNodes := `
CREATE TABLE IF NOT EXISTS secnodes ( CREATE TABLE IF NOT EXISTS secnodes (
Attr varchar(128) default null, Attr varchar(128) default null,
Enc varchar(128) default null, Enc varchar(128) default null,
@@ -203,10 +175,8 @@ func BaseTables() []string {
Node integer(15) not null default '0', Node integer(15) not null default '0',
Primary key(Attr, Node) Primary key(Attr, Node)
); );
` `,
datalist = append(datalist, SecNodes) "attributes": `
Attributes := `
CREATE TABLE IF NOT EXISTS attributes ( CREATE TABLE IF NOT EXISTS attributes (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Attr varchar(128) not null, Attr varchar(128) not null,
@@ -217,10 +187,8 @@ func BaseTables() []string {
UNIQUE(Attr, Category, Catindex), UNIQUE(Attr, Category, Catindex),
Foreign key(Catindex) references catindex(ID) on delete cascade Foreign key(Catindex) references catindex(ID) on delete cascade
); );
` `,
datalist = append(datalist, Attributes) "partitions": `
Partitions := `
CREATE TABLE IF NOT EXISTS partitions ( CREATE TABLE IF NOT EXISTS partitions (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Node integer(15) not null default '0', Node integer(15) not null default '0',
@@ -232,10 +200,8 @@ func BaseTables() []string {
PartitionFlags varchar(128) not null default '', PartitionFlags varchar(128) not null default '',
FormatFlags varchar(128) not null default '' FormatFlags varchar(128) not null default ''
); );
` `,
datalist = append(datalist, Partitions) "firewalls": `
Firewalls := `
CREATE TABLE IF NOT EXISTS firewalls ( CREATE TABLE IF NOT EXISTS firewalls (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Rulename varchar(128) not null, Rulename varchar(128) not null,
@@ -254,10 +220,8 @@ func BaseTables() []string {
UNIQUE(Rulename, Category, Catindex), UNIQUE(Rulename, Category, Catindex),
Foreign key(Catindex) references catindex(ID) on delete cascade Foreign key(Catindex) references catindex(ID) on delete cascade
); );
` `,
datalist = append(datalist, Firewalls) "rolls": `
Rolls := `
CREATE TABLE IF NOT EXISTS rolls ( CREATE TABLE IF NOT EXISTS rolls (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Name varchar(128) not null default '', Name varchar(128) not null default '',
@@ -268,19 +232,15 @@ func BaseTables() []string {
Check(Enabled IN ('yes', 'no')) Check(Enabled IN ('yes', 'no'))
Check(OS IN ('linux', 'other')) Check(OS IN ('linux', 'other'))
); );
` `,
datalist = append(datalist, Rolls) "noderolls": `
NodeRolls := `
CREATE TABLE IF NOT EXISTS noderolls ( CREATE TABLE IF NOT EXISTS noderolls (
Node varchar(11) not null default '0', Node varchar(11) not null default '0',
RollID varchar(11) not null, RollID varchar(11) not null,
Primary key(Node, RollID) Primary key(Node, RollID)
); );
` `,
datalist = append(datalist, NodeRolls) "bootactions": `
Bootactions := `
CREATE TABLE IF NOT EXISTS bootactions ( CREATE TABLE IF NOT EXISTS bootactions (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Action varchar(256) default null, Action varchar(256) default null,
@@ -288,29 +248,23 @@ func BaseTables() []string {
Ramdisk varchar(256) default null, Ramdisk varchar(256) default null,
Args varchar(1024) default null Args varchar(1024) default null
); );
` `,
datalist = append(datalist, Bootactions) "bootflags": `
BootFlags := `
CREATE TABLE IF NOT EXISTS bootflags ( CREATE TABLE IF NOT EXISTS bootflags (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Node integer(11) not null default '0', Node integer(11) not null default '0',
Flags varchar(256) default null Flags varchar(256) default null
); );
` `,
datalist = append(datalist, BootFlags) "distributions": `
Distributions := `
CREATE TABLE IF NOT EXISTS distributions ( CREATE TABLE IF NOT EXISTS distributions (
ID integer primary key autoincrement, ID integer primary key autoincrement,
Name varchar(32) not null default '', Name varchar(32) not null default '',
OS varchar(32) default '', OS varchar(32) default '',
Release varchar(32) default '' Release varchar(32) default ''
); );
` `,
datalist = append(datalist, Distributions) "vnet": `
View_vnet := `
DROP VIEW IF EXISTS vnet; DROP VIEW IF EXISTS vnet;
CREATE VIEW vnet AS CREATE VIEW vnet AS
SELECT SELECT
@@ -330,10 +284,8 @@ func BaseTables() []string {
inner join networks nt on n.id=nt.node /* 连接networks表,on只保留满足条件的行 */ inner join networks nt on n.id=nt.node /* 连接networks表,on只保留满足条件的行 */
inner join subnets s on nt.subnet=s.id /* 连接subnets表,on只保留满足条件的行 */ inner join subnets s on nt.subnet=s.id /* 连接subnets表,on只保留满足条件的行 */
; ;
` `,
datalist = append(datalist, View_vnet) "hostselections": `
View_hostselections := `
DROP VIEW IF EXISTS hostselections; DROP VIEW IF EXISTS hostselections;
CREATE VIEW hostselections AS CREATE VIEW hostselections AS
SELECT SELECT
@@ -354,10 +306,8 @@ func BaseTables() []string {
(c.name = 'appliance' and ci.name = a.name) or (c.name = 'appliance' and ci.name = a.name) or
(c.name = 'host' and ci.name = n.name) (c.name = 'host' and ci.name = n.name)
; ;
` `,
datalist = append(datalist, View_hostselections) "vcatindex": `
View_vcatindex := `
-- 视图vcatindex: 类别索引可读试图 -- 视图vcatindex: 类别索引可读试图
DROP VIEW IF EXISTS vcatindex; DROP VIEW IF EXISTS vcatindex;
CREATE VIEW vcatindex AS CREATE VIEW vcatindex AS
@@ -369,10 +319,8 @@ func BaseTables() []string {
categories cat categories cat
inner join catindex ci on ci.category=cat.id inner join catindex ci on ci.category=cat.id
; ;
` `,
datalist = append(datalist, View_vcatindex) "vresolvechain": `
View_vresolvechain := `
-- 视图vresolvechain: 解析链可读试图 -- 视图vresolvechain: 解析链可读试图
DROP VIEW IF EXISTS vresolvechain; DROP VIEW IF EXISTS vresolvechain;
CREATE VIEW vresolvechain AS CREATE VIEW vresolvechain AS
@@ -385,10 +333,8 @@ func BaseTables() []string {
inner join categories cat on r.category=cat.id inner join categories cat on r.category=cat.id
order by chain, precedence order by chain, precedence
; ;
` `,
datalist = append(datalist, View_vresolvechain) "vattributes": `
View_vattributes := `
-- 视图vattributes: 属性可读试图 -- 视图vattributes: 属性可读试图
DROP VIEW IF EXISTS vattributes; DROP VIEW IF EXISTS vattributes;
CREATE VIEW vattributes AS CREATE VIEW vattributes AS
@@ -405,10 +351,8 @@ func BaseTables() []string {
inner join categories cat on a.category=cat.id inner join categories cat on a.category=cat.id
order by attr, catindex, category order by attr, catindex, category
; ;
` `,
datalist = append(datalist, View_vattributes) "vfirewalls": `
View_vfirewalls := `
-- 视图vfirewalls: 防火墙规则可读试图 -- 视图vfirewalls: 防火墙规则可读试图
DROP VIEW IF EXISTS vfirewalls; DROP VIEW IF EXISTS vfirewalls;
CREATE VIEW vfirewalls AS CREATE VIEW vfirewalls AS
@@ -432,10 +376,8 @@ func BaseTables() []string {
inner join categories cat on f.category=cat.id inner join categories cat on f.category=cat.id
order by f.Rulename, catindex, category order by f.Rulename, catindex, category
; ;
` `,
datalist = append(datalist, View_vfirewalls) "vhostselections": `
View_vhostselections := `
-- 视图vhostselections: 主机选择可读试图 -- 视图vhostselections: 主机选择可读试图
DROP VIEW IF EXISTS vhostselections; DROP VIEW IF EXISTS vhostselections;
CREATE VIEW vhostselections AS CREATE VIEW vhostselections AS
@@ -449,10 +391,8 @@ func BaseTables() []string {
inner join catindex ci on hs.selection=ci.id inner join catindex ci on hs.selection=ci.id
order by host, category, selection order by host, category, selection
; ;
` `,
datalist = append(datalist, View_vhostselections) "vmapcategoryindex": `
View_vmapCategoryIndex := `
-- 视图vmapcategoryindex: 类别索引映射可读试图 -- 视图vmapcategoryindex: 类别索引映射可读试图
DROP VIEW IF EXISTS vmapcategoryindex; DROP VIEW IF EXISTS vmapcategoryindex;
CREATE VIEW vmapCategoryIndex AS CREATE VIEW vmapCategoryIndex AS
@@ -464,13 +404,12 @@ func BaseTables() []string {
cateindex ci cateindex ci
inner join categories cat on ci.category=cat.id inner join categories cat on ci.category=cat.id
; ;
` `,
datalist = append(datalist, View_vmapCategoryIndex) }
return datalist
} }
func InitBaseData(conn *sql.DB) error { func InitBaseData(conn *sql.DB) error {
logger.Debug("初始化基础数据...")
// ========== 第一步:插入 categories 数据 ========== // ========== 第一步:插入 categories 数据 ==========
categoryData := []struct { categoryData := []struct {
Name string Name string
@@ -484,15 +423,26 @@ func InitBaseData(conn *sql.DB) error {
} }
// 批量插入 categories (忽略重复) // 批量插入 categories (忽略重复)
logger.Debug("插入 categories 数据...")
for _, cd := range categoryData { for _, cd := range categoryData {
query := ` query := `
insert or ignore into categories (Name, Description) insert or ignore into categories (Name, Description)
values (?, ?) values (?, ?)
` `
_, err := conn.Exec(query, cd.Name, cd.Description) fullSQL := ReplaceSQLQuery(query, cd.Name, cd.Description)
logger.Debugf("执行语句: %s", fullSQL)
// 执行 SQL 语句仍用占位符避免SQL注入
result, err := conn.Exec(query, cd.Name, cd.Description)
if err != nil { if err != nil {
return fmt.Errorf("error inserting category %s: %w", cd.Name, err) return fmt.Errorf("error inserting category %s: %w", cd.Name, err)
} }
id, err := result.LastInsertId()
if err != nil {
return fmt.Errorf("error getting last insert ID: %w", err)
}
logger.Debugf("执行语句: %s, 插入ID: %d", fullSQL, id)
} }
// ========== 第二步:插入 catindex 数据 ========== // ========== 第二步:插入 catindex 数据 ==========
@@ -511,7 +461,9 @@ func InitBaseData(conn *sql.DB) error {
{"devel-server", "appliance"}, {"devel-server", "appliance"},
{"login", "appliance"}, {"login", "appliance"},
} }
// 批量插入 catindex (忽略重复) // 批量插入 catindex (忽略重复)
logger.Debug("插入 Catindex 数据...")
for _, ci := range catindexData { for _, ci := range catindexData {
// 动态获取类别ID (复用MapCategory函数) // 动态获取类别ID (复用MapCategory函数)
catID, err := MapCategory(conn, ci.Category) catID, err := MapCategory(conn, ci.Category)
@@ -527,10 +479,19 @@ func InitBaseData(conn *sql.DB) error {
insert or ignore into catindex (Name, Category) insert or ignore into catindex (Name, Category)
values (?, ?) values (?, ?)
` `
_, err = conn.Exec(query, ci.Name, catID) fullSQL := ReplaceSQLQuery(query, ci.Name, catID)
// 执行 SQL 语句仍用占位符避免SQL注入
result, err := conn.Exec(query, ci.Name, catID)
if err != nil { if err != nil {
return fmt.Errorf("error inserting catindex %s: %w", ci.Name, err) return fmt.Errorf("error inserting catindex %s: %w", ci.Name, err)
} }
id, err := result.LastInsertId()
if err != nil {
return fmt.Errorf("error getting last insert ID: %w", err)
}
logger.Debugf("执行语句: %s, 插入ID: %d", fullSQL, id)
} }
// ========== 第三步:插入 resolvechain 数据 ========== // ========== 第三步:插入 resolvechain 数据 ==========
@@ -546,6 +507,7 @@ func InitBaseData(conn *sql.DB) error {
{"default", "host", 50}, {"default", "host", 50},
} }
// 批量插入 resolvechain (忽略重复) // 批量插入 resolvechain (忽略重复)
logger.Debugf("插入 resolvechain 数据...")
for _, rcd := range resolveChainData { for _, rcd := range resolveChainData {
// 动态获取类别ID (复用MapCategory函数) // 动态获取类别ID (复用MapCategory函数)
catID, err := MapCategory(conn, rcd.Category) catID, err := MapCategory(conn, rcd.Category)
@@ -561,10 +523,19 @@ func InitBaseData(conn *sql.DB) error {
insert or ignore into resolvechain (Name, Category, Precedence) insert or ignore into resolvechain (Name, Category, Precedence)
values (?, ?, ?) values (?, ?, ?)
` `
_, err = conn.Exec(query, rcd.Name, catID, rcd.Precedence) fullSQL := ReplaceSQLQuery(query, rcd.Name, catID, rcd.Precedence)
// 执行 SQL 语句仍用占位符避免SQL注入
result, err := conn.Exec(query, rcd.Name, catID, rcd.Precedence)
if err != nil { if err != nil {
return fmt.Errorf("error inserting resolvechain %s: %w", rcd.Name, err) return fmt.Errorf("error inserting resolvechain %s: %w", rcd.Name, err)
} }
id, err := result.LastInsertId()
if err != nil {
return fmt.Errorf("error getting last insert ID: %w", err)
}
logger.Debugf("执行语句: %s, 插入ID: %d", fullSQL, id)
} }
return nil return nil

View File

@@ -80,6 +80,72 @@ type LogConfig struct {
ShowColor bool `mapstructure:"show_color" yaml:"show_color"` ShowColor bool `mapstructure:"show_color" yaml:"show_color"`
} }
type LevelFilterWriter struct {
writer io.Writer
maxLevel logrus.Level // 控制台: 只输出 <= 该级别
minLevel logrus.Level // 文件: 只输出 >= 该级别
isConsole bool // 是否是控制台输出
}
// Write 实现io.Writer接口核心过滤逻辑
func (f *LevelFilterWriter) Write(p []byte) (n int, err error) {
// 解析日志级别适配logrus默认格式和CustomFormatter
logLevel := parseLogLevelFromContent(p)
// 控制台:只输出 Info 及以下级别Trace/Debug/Info
if f.isConsole {
if logLevel <= f.maxLevel {
return f.writer.Write(p)
}
return len(p), nil // 过滤掉返回长度避免Writer报错
}
// 文件:只输出 Warn 及以上级别Warn/Error/Fatal/Panic
if logLevel >= f.minLevel {
return f.writer.Write(p)
}
return len(p), nil
}
// parseLogLevelFromContent 解析日志内容中的级别(兼容自定义格式)
func parseLogLevelFromContent(p []byte) logrus.Level {
content := string(p)
// 适配常见的级别关键字兼容你的CustomFormatter
switch {
case contains(content, "TRACE"):
return logrus.TraceLevel
case contains(content, "DEBUG"):
return logrus.DebugLevel
case contains(content, "INFO"):
return logrus.InfoLevel
case contains(content, "WARN") || contains(content, "WARNING"):
return logrus.WarnLevel
case contains(content, "ERROR"):
return logrus.ErrorLevel
case contains(content, "FATAL"):
return logrus.FatalLevel
case contains(content, "PANIC"):
return logrus.PanicLevel
default:
return logrus.InfoLevel // 解析失败默认Info级别
}
}
// contains 辅助函数:判断字符串是否包含子串
func contains(s, substr string) bool {
return len(s) >= len(substr) && indexOf(s, substr) != -1
}
// indexOf 简易字符串查找(避免依赖额外库)
func indexOf(s, substr string) int {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return i
}
}
return -1
}
// 默认配置 // 默认配置
var defaultConfig = LogConfig{ var defaultConfig = LogConfig{
Level: "info", Level: "info",
@@ -216,34 +282,7 @@ func Init(cfg LogConfig) {
// 1. 创建logrus实例 // 1. 创建logrus实例
logrusInst := logrus.New() logrusInst := logrus.New()
// 2. 配置输出(控制台 + 文件,可选) // 2. 配置日志级别(总开关,必须在输出配置前)
var outputs []io.Writer
outputs = append(outputs, os.Stdout) // 控制台输出
// 如果配置了日志文件,添加文件输出
if cfg.LogFile != "" {
// 确保日志目录存在
dir := filepath.Dir(cfg.LogFile)
if err := os.MkdirAll(dir, 0755); err != nil {
// 目录创建失败,只输出警告,不影响程序运行
logrusInst.Warnf("创建日志目录失败: %v,仅输出到控制台", err)
} else {
file, err := os.OpenFile(cfg.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err == nil {
outputs = append(outputs, file)
} else {
logrusInst.Warnf("打开日志文件失败: %v,仅输出到控制台", err)
}
}
}
logrusInst.SetOutput(io.MultiWriter(outputs...))
// 3. 配置格式
logrusInst.SetFormatter(&CustomFormatter{
ShowColor: cfg.ShowColor,
})
// 4. 配置日志级别
lvl, err := logrus.ParseLevel(cfg.Level) lvl, err := logrus.ParseLevel(cfg.Level)
if err != nil { if err != nil {
lvl = logrus.InfoLevel // 解析失败默认Info级别 lvl = logrus.InfoLevel // 解析失败默认Info级别
@@ -257,6 +296,45 @@ func Init(cfg LogConfig) {
// 启用文件行号(必须开启否则getCallerInfo拿不到数据) // 启用文件行号(必须开启否则getCallerInfo拿不到数据)
logrusInst.SetReportCaller(true) logrusInst.SetReportCaller(true)
// 3. 配置输出(控制台 + 文件,可选)
var outputs []io.Writer
// 控制台输出: 只输出 Info 及以下级别
consoleWriter := &LevelFilterWriter{
writer: os.Stdout,
minLevel: logrus.InfoLevel,
isConsole: true,
}
outputs = append(outputs, consoleWriter)
// 如果配置了日志文件,添加文件输出
if cfg.LogFile != "" {
// 确保日志目录存在
dir := filepath.Dir(cfg.LogFile)
if err := os.MkdirAll(dir, 0755); err != nil {
// 目录创建失败,只输出警告,不影响程序运行
logrusInst.Warnf("创建日志目录失败: %v,仅输出到控制台", err)
} else {
file, err := os.OpenFile(cfg.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err == nil {
fileWriter := &LevelFilterWriter{
writer: file,
minLevel: logrus.WarnLevel,
isConsole: false,
}
outputs = append(outputs, fileWriter)
} else {
logrusInst.Warnf("打开日志文件失败: %v,仅输出到控制台", err)
}
}
}
logrusInst.SetOutput(io.MultiWriter(outputs...))
// 4. 配置格式
logrusInst.SetFormatter(&CustomFormatter{
ShowColor: cfg.ShowColor,
})
// 5. 赋值给全局默认实例 // 5. 赋值给全局默认实例
DefaultLogger = &logrusLogger{logrusInst} DefaultLogger = &logrusLogger{logrusInst}
}) })

View File

@@ -3,6 +3,7 @@ package utils
import ( import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"fmt"
"os" "os"
"os/exec" "os/exec"
"time" "time"
@@ -32,6 +33,29 @@ func GetTimestamp() string {
return time.Now().Format("2006-01-02 15:04:05") return time.Now().Format("2006-01-02 15:04:05")
} }
func OutputMaps(maps map[string]string) []string {
output := []string{}
maxLen := 0
for key := range maps {
if len(key) > maxLen {
maxLen = len(key)
}
}
// 使用动态宽度的格式化字符串输出
// %-*s 的含义
// %: 格式化开始
// -: 左对齐,默认是右对齐
// *: 表示宽度由后续参数指定(maxLen)
// s: 表示字符串类型
for key, value := range maps {
output = append(output, fmt.Sprintf("%-*s: %s", maxLen, key, value))
}
return output
}
// 定义短语 // 定义短语
const ( const (
NoAvailableNetworkInterfaces = "No available network interfaces" NoAvailableNetworkInterfaces = "No available network interfaces"

View File

@@ -201,7 +201,7 @@ func loadConfig() (*ConfigMapping, error) {
if _, err := toml.DecodeFile(cfgfile, configs); err != nil { if _, err := toml.DecodeFile(cfgfile, configs); err != nil {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
// 文件不存在,直接返回默认配置 // 文件不存在,直接返回默认配置
logger.Infof("Config file %s not exist, use default config", cfgfile) logger.Debugf("Config file %s not exist, use default config", cfgfile)
return configs, nil return configs, nil
} }
// 其他错误,返回错误 // 其他错误,返回错误
@@ -209,7 +209,7 @@ func loadConfig() (*ConfigMapping, error) {
return nil, err return nil, err
} }
logger.Infof("Load config file %s success", cfgfile) logger.Debugf("Load config file %s success", cfgfile)
return configs, nil return configs, nil
} }
@@ -310,7 +310,9 @@ func (m *model) saveConfig() error {
return err return err
} }
logger.Debugf("Result: %v", result) for _, value := range utils.OutputMaps(result) {
logger.Debugf("%s", value)
}
return nil return nil
} }
@@ -433,15 +435,15 @@ func insertDataToDB(result map[string]string) error {
lesArgs := fmt.Sprintf("%s vnc vncip=%s vncpassword=sunhpc", result["boot_args"], result["private_address"]) lesArgs := fmt.Sprintf("%s vnc vncip=%s vncpassword=sunhpc", result["boot_args"], result["private_address"])
bootaction := []string{ bootaction := []string{
fmt.Sprintf("insert into bootactions values (1, 'install', '%s', '%s', '%s');", fmt.Sprintf("insert or replace into bootactions values (1, 'install', '%s', '%s', '%s');",
vmlinuz, initrds, insArgs), vmlinuz, initrds, insArgs),
"insert into bootactions values (2, 'os', 'localboot 0', '', '');", "insert or replace into bootactions values (2, 'os', 'localboot 0', '', '');",
"insert into bootactions values (3, 'memtest', 'kernel memtest', '', '');", "insert or replace into bootactions values (3, 'memtest', 'kernel memtest', '', '');",
fmt.Sprintf("insert into bootactions values (4, 'install headless', '%s', '%s', '%s');", fmt.Sprintf("insert or replace into bootactions values (4, 'install headless', '%s', '%s', '%s');",
vmlinuz, initrds, lesArgs), vmlinuz, initrds, lesArgs),
fmt.Sprintf("insert into bootactions values (5, 'rescue', '%s', '%s', '%s');", fmt.Sprintf("insert or replace into bootactions values (5, 'rescue', '%s', '%s', '%s');",
vmlinuz, initrds, resArgs), vmlinuz, initrds, resArgs),
"insert into bootactions values (6, 'pxeflash', 'kernel memdisk bigraw', 'pxeflash.img', 'keeppxe');", "insert or replace into bootactions values (6, 'pxeflash', 'kernel memdisk bigraw', 'pxeflash.img', 'keeppxe');",
} }
insertData = append(insertData, bootaction...) insertData = append(insertData, bootaction...)
@@ -453,42 +455,41 @@ func insertDataToDB(result map[string]string) error {
category := item.Category category := item.Category
catindex := item.Catindex catindex := item.Catindex
insertData = append(insertData, insertData = append(insertData,
fmt.Sprintf("insert into attributes values ('%s', '%s', '%s', %d, %d);", fmt.Sprintf("insert or replace into attributes values (NULL, '%s', '%s', '%s', %d, %d);",
key, value, shadow, category, catindex)) key, value, shadow, category, catindex))
} }
nodes := []string{ nodes := []string{
fmt.Sprintf( fmt.Sprintf(
"insert into nodes values (1, '%s', '%d', 0, 0, '%s', '%s', '', 'install');", "insert or replace into nodes values (1, '%s', '2', '%d', 0, 0, '%s', '%s', '', 'install');",
result["private_hostname"], result["private_hostname"],
info.GetSystemInfo().NumCPU, info.GetSystemInfo().NumCPU,
info.GetSystemInfo().Arch, info.GetSystemInfo().Arch,
info.GetSystemInfo().OS), info.GetSystemInfo().OS),
fmt.Sprintf( fmt.Sprintf(
"insert into networks values (1, 1, '%s', '%s', '%s', '%s', '2');", `insert or replace into subnets values (1, 'private', '%s', '%s', '%s', '%s', '1');`,
result["public_mac"],
result["public_address"],
result["private_hostname"],
result["public_interface"]),
fmt.Sprintf(
"insert into networks values (2, 1, '%s', '%s', '%s', '%s', '1');",
result["private_mac"],
result["private_address"],
result["private_hostname"],
result["private_interface"]),
fmt.Sprintf(
"insert into subnets values (1, 'private', '%s', '%s', '%s', '%s', '1');",
result["private_domain"], result["private_domain"],
result["private_network"], result["private_network"],
result["private_netmask"], result["private_netmask"],
result["private_mtu"]), result["private_mtu"]),
fmt.Sprintf( fmt.Sprintf(
"insert into subnets values (2, 'public', '%s', '%s', '%s', '%s', '0');", `insert or replace into subnets values (2, 'public', '%s', '%s', '%s', '%s', '0');`,
result["public_domain"], result["public_domain"],
result["public_network"], result["public_network"],
result["public_netmask"], result["public_netmask"],
result["public_mtu"]), result["public_mtu"]),
fmt.Sprintf(
`insert or replace into networks values (1, 1, '%s', '%s', '%s', '%s', '2', NULL, NULL,NULL,NULL);`,
result["public_mac"],
result["public_address"],
result["private_hostname"],
result["public_interface"]),
fmt.Sprintf(
`insert or replace into networks values (2, 1, '%s', '%s', '%s', '%s', '1', NULL, NULL, NULL, NULL);`,
result["private_mac"],
result["private_address"],
result["private_hostname"],
result["private_interface"]),
} }
insertData = append(insertData, nodes...) insertData = append(insertData, nodes...)

View File

@@ -227,7 +227,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyMsg: case tea.KeyMsg:
switch msg.String() { switch msg.String() {
case "ctrl+c": case "ctrl+c":
m.saveConfig()
//m.quitting = true //m.quitting = true
return m, tea.Quit return m, tea.Quit
@@ -257,6 +256,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// 页6确认配置 → 退出并保存 // 页6确认配置 → 退出并保存
case "confirm_btn": case "confirm_btn":
m.saveConfig()
m.done = true m.done = true
//m.quitting = true //m.quitting = true
return m, tea.Quit return m, tea.Quit

View File

@@ -24,11 +24,12 @@ var (
// 基础布局样式 // 基础布局样式
appStyle = lipgloss.NewStyle(). appStyle = lipgloss.NewStyle().
Padding(1, 1). Padding(1, 1).
MarginBottom(1).
BorderStyle(lipgloss.RoundedBorder()). BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(primaryColor). BorderForeground(primaryColor).
Foreground(textColor). Foreground(textColor).
Align(lipgloss.Center). Align(lipgloss.Center)
Height(40) //Height(40)
// 标题样式 // 标题样式
titleStyle = lipgloss.NewStyle(). titleStyle = lipgloss.NewStyle().