init
This commit is contained in:
commit
b10790c212
40 changed files with 4149 additions and 0 deletions
157
db/db.go
Normal file
157
db/db.go
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
//_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// TemplateEntry 对应 templates 表
|
||||
type TemplateEntry struct {
|
||||
Filename string `json:"filename"`
|
||||
TemplateType string `json:"template_type"`
|
||||
Content []byte `json:"content"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ParamsEntry 对应 config_params 表
|
||||
type ParamsEntry struct {
|
||||
Filename string `json:"filename"`
|
||||
TemplateType string `json:"template_type"`
|
||||
ParamsGOB []byte `json:"params_gob"`
|
||||
ParamsOrigin []byte `json:"params_origin"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
// RenderedConfigEntry 对应 rendered_configs 表
|
||||
type RenderedConfigEntry struct {
|
||||
Filename string `json:"filename"`
|
||||
RenderedContent []byte `json:"rendered_content"`
|
||||
RenderedAt int64 `json:"rendered_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
// UsersTable
|
||||
type UsersTable struct {
|
||||
UserName string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ConfigDB
|
||||
type ConfigDB struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
func InitDB(filepath string) (*ConfigDB, error) {
|
||||
db, err := loadDB(filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cdb := &ConfigDB{DB: db}
|
||||
// 尝试 Ping 数据库以验证连接是否成功
|
||||
if err = db.Ping(); err != nil {
|
||||
db.Close() // 如果连接失败,确保关闭数据库句柄
|
||||
return nil, fmt.Errorf("db: failed to connect to database: %w", err)
|
||||
}
|
||||
err = cdb.createTables()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cdb, nil
|
||||
}
|
||||
|
||||
func loadDB(filepath string) (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite", fmt.Sprintf("file:%s?cache=shared&_journal=WAL", filepath))
|
||||
if err != nil {
|
||||
log.Fatalf("Can not connect to database: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (cdb *ConfigDB) createTables() error {
|
||||
tx, err := cdb.DB.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to begin transaction for table creation: %w", err)
|
||||
}
|
||||
defer tx.Rollback() // 确保在出错时回滚事务
|
||||
|
||||
// 创建 templates 表
|
||||
_, err = tx.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS templates (
|
||||
filename TEXT PRIMARY KEY,
|
||||
template_type TEXT NOT NULL,
|
||||
content BLOB NOT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create 'templates' table: %w", err)
|
||||
}
|
||||
|
||||
// 2. 创建 config_params 表
|
||||
_, err = tx.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS config_params (
|
||||
filename TEXT PRIMARY KEY,
|
||||
template_type TEXT NOT NULL,
|
||||
params_gob BLOB NOT NULL,
|
||||
params_origin BLOB NOT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
-- FOREIGN KEY(filename) REFERENCES templates(filename) ON DELETE CASCADE -- 已移除
|
||||
);`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create 'config_params' table: %w", err)
|
||||
}
|
||||
|
||||
// 3. 创建 rendered_configs 表 (外键指向 config_params 表)
|
||||
_, err = tx.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS rendered_configs (
|
||||
filename TEXT PRIMARY KEY,
|
||||
rendered_content BLOB NOT NULL,
|
||||
rendered_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
FOREIGN KEY(filename) REFERENCES config_params(filename) ON DELETE CASCADE -- 修改外键引用
|
||||
);`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create 'rendered_configs' table: %w", err)
|
||||
}
|
||||
|
||||
//
|
||||
// 4. 创建 users 表
|
||||
_, err = tx.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
username TEXT PRIMARY KEY,
|
||||
password TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create 'users' table: %w", err)
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
if err := tx.Commit(); err != nil {
|
||||
return fmt.Errorf("failed to commit transaction for table creation: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cdb *ConfigDB) CloseDB() error {
|
||||
if cdb.DB == nil {
|
||||
return nil // 数据库未打开或已关闭
|
||||
}
|
||||
err := cdb.DB.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to close database: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
504
db/operation.go
Normal file
504
db/operation.go
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// 用户校验操作
|
||||
/*
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
username TEXT PRIMARY KEY,
|
||||
password TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);`)
|
||||
*/
|
||||
|
||||
// AddUser 向 'users' 表中添加一个新用户.
|
||||
func (cdb *ConfigDB) AddUser(username, password string) error {
|
||||
insertSQL := `
|
||||
INSERT INTO users (username, password)
|
||||
VALUES (?, ?);
|
||||
`
|
||||
_, err := cdb.DB.Exec(insertSQL, username, password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to add user '%s': %w", username, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserByUsername 从 'users' 表中根据用户名获取用户信息.
|
||||
func (cdb *ConfigDB) GetUserByUsername(username string) (*UsersTable, error) {
|
||||
querySQL := `SELECT username, password, created_at, updated_at FROM users WHERE username = ?;`
|
||||
row := cdb.DB.QueryRow(querySQL, username)
|
||||
|
||||
user := &UsersTable{}
|
||||
err := row.Scan(&user.UserName, &user.Password, &user.CreatedAt, &user.UpdatedAt)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("db: user '%s' not found: %w", username, err)
|
||||
}
|
||||
return nil, fmt.Errorf("db: failed to get user '%s': %w", username, err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// DeleteUser 从 'users' 表中删除一个用户.
|
||||
func (cdb *ConfigDB) DeleteUser(username string) error {
|
||||
_, err := cdb.DB.Exec(`DELETE FROM users WHERE username = ?;`, username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to delete user '%s': %w", username, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateUserPassword 更新用户的密码.
|
||||
func (cdb *ConfigDB) UpdateUserPassword(username, newPassword string) error {
|
||||
updateSQL := `
|
||||
UPDATE users
|
||||
SET password = ?, updated_at = strftime('%s', 'now')
|
||||
WHERE username = ?;
|
||||
`
|
||||
result, err := cdb.DB.Exec(updateSQL, newPassword, username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to update user '%s' password: %w", username, err)
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to get rows affected by update: %w", err)
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("db: no user with username '%s' found to update", username)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RangeUserNames 获取所有用户的用户名.
|
||||
func (cdb *ConfigDB) RangeUserNames() ([]string, error) {
|
||||
querySQL := `SELECT username FROM users;`
|
||||
rows, err := cdb.DB.Query(querySQL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: failed to get usernames from users: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var usernames []string
|
||||
for rows.Next() {
|
||||
var username string
|
||||
if err := rows.Scan(&username); err != nil {
|
||||
return nil, fmt.Errorf("db: failed to scan username: %w", err)
|
||||
}
|
||||
usernames = append(usernames, username)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("db: error during user rows iteration: %w", err)
|
||||
}
|
||||
|
||||
return usernames, nil
|
||||
}
|
||||
|
||||
// HasAnyUser 检查 'users' 表中是否存在任何用户.
|
||||
func (cdb *ConfigDB) HasAnyUser() (bool, error) {
|
||||
querySQL := `SELECT EXISTS(SELECT 1 FROM users LIMIT 1);`
|
||||
var exists bool
|
||||
err := cdb.DB.QueryRow(querySQL).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("db: failed to check if any user exists: %w", err)
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// IsUserExists 检查指定用户名的用户是否存在.
|
||||
func (cdb *ConfigDB) IsUserExists(username string) (bool, error) {
|
||||
querySQL := `SELECT EXISTS(SELECT 1 FROM users WHERE username = ? LIMIT 1);`
|
||||
var exists bool
|
||||
err := cdb.DB.QueryRow(querySQL, username).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("db: failed to check if user '%s' exists: %w", username, err)
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// GetPasswordByUsername 从 'users' 表中根据用户名获取密码.
|
||||
func (cdb *ConfigDB) GetPasswordByUsername(username string) (string, error) {
|
||||
querySQL := `SELECT password FROM users WHERE username = ?;`
|
||||
var password string
|
||||
err := cdb.DB.QueryRow(querySQL, username).Scan(&password)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return "", fmt.Errorf("db: user '%s' not found: %w", username, err)
|
||||
}
|
||||
return "", fmt.Errorf("db: failed to get password for user '%s': %w", username, err)
|
||||
}
|
||||
return password, nil
|
||||
}
|
||||
|
||||
// --- 模板操作 (Templates Table) ---
|
||||
|
||||
// SaveTemplate 在 'templates' 表中保存或更新一个模板.
|
||||
func (cdb *ConfigDB) SaveTemplate(entry TemplateEntry) error {
|
||||
insertSQL := `
|
||||
INSERT INTO templates (filename, template_type, content)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(filename) DO UPDATE SET
|
||||
template_type = EXCLUDED.template_type,
|
||||
content = EXCLUDED.content,
|
||||
updated_at = strftime('%s', 'now');
|
||||
`
|
||||
_, err := cdb.DB.Exec(insertSQL, entry.Filename, entry.TemplateType, entry.Content)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to save template '%s': %w", entry.Filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTemplate 从 'templates' 表中获取一个模板内容.
|
||||
func (cdb *ConfigDB) GetTemplate(filename string) (*TemplateEntry, error) {
|
||||
querySQL := `SELECT filename, template_type, content, created_at, updated_at FROM templates WHERE filename = ?;`
|
||||
row := cdb.DB.QueryRow(querySQL, filename)
|
||||
|
||||
entry := &TemplateEntry{}
|
||||
err := row.Scan(&entry.Filename, &entry.TemplateType, &entry.Content, &entry.CreatedAt, &entry.UpdatedAt)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("db: template '%s' not found: %w", filename, err)
|
||||
}
|
||||
return nil, fmt.Errorf("db: failed to get template '%s': %w", filename, err)
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// DeleteTemplate 从 'templates' 表中删除一个模板.
|
||||
// 请注意: 此操作不会级联删除 'config_params' 或 'rendered_configs' 中的关联数据;
|
||||
// 因为 'templates' 表不再是它们的外键父表.
|
||||
func (cdb *ConfigDB) DeleteTemplate(filename string) error {
|
||||
_, err := cdb.DB.Exec(`DELETE FROM templates WHERE filename = ?;`, filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to delete template '%s': %w", filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RangeTempaltes 获取所有模板的名称
|
||||
func (cdb *ConfigDB) RangeTemplates() ([]string, error) {
|
||||
querySQL := `SELECT filename FROM templates;`
|
||||
rows, err := cdb.DB.Query(querySQL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: failed to get filenames from templates: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var filenames []string
|
||||
for rows.Next() {
|
||||
var filename string
|
||||
if err := rows.Scan(&filename); err != nil {
|
||||
return nil, fmt.Errorf("db: failed to scan template filename: %w", err)
|
||||
}
|
||||
filenames = append(filenames, filename)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("db: error during template rows iteration: %w", err)
|
||||
}
|
||||
|
||||
return filenames, nil
|
||||
}
|
||||
|
||||
// GetAllTempaltes
|
||||
func (cdb *ConfigDB) GetAllTemplates() ([]TemplateEntry, error) {
|
||||
querySQL := `SELECT filename, template_type, content, created_at, updated_at FROM templates;`
|
||||
rows, err := cdb.DB.Query(querySQL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: failed to get all templates: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var templates []TemplateEntry
|
||||
for rows.Next() {
|
||||
var entry TemplateEntry
|
||||
if err := rows.Scan(&entry.Filename, &entry.TemplateType, &entry.Content, &entry.CreatedAt, &entry.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("db: failed to scan template entry: %w", err)
|
||||
}
|
||||
templates = append(templates, entry)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("db: error during templates rows iteration: %w", err)
|
||||
}
|
||||
|
||||
return templates, nil
|
||||
}
|
||||
|
||||
// --- 参数操作 (Config_Params Table) ---
|
||||
|
||||
/*
|
||||
filename TEXT PRIMARY KEY,
|
||||
template_type TEXT NOT NULL,
|
||||
params_gob BLOB NOT NULL,
|
||||
params_origin BLOB NOT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
*/
|
||||
|
||||
// SaveParams 在 'config_params' 表中保存或更新一个模板的参数.
|
||||
// entry.ParamsGOB 应该是一个经过 GOB 编码的字节切片.
|
||||
func (cdb *ConfigDB) SaveParams(entry ParamsEntry) error {
|
||||
insertSQL := `
|
||||
INSERT INTO config_params (filename, template_type, params_gob, params_origin)
|
||||
VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(filename) DO UPDATE SET
|
||||
template_type = EXCLUDED.template_type,
|
||||
params_gob = EXCLUDED.params_gob,
|
||||
params_origin = EXCLUDED.params_origin,
|
||||
updated_at = strftime('%s', 'now');
|
||||
`
|
||||
_, err := cdb.DB.Exec(insertSQL, entry.Filename, entry.TemplateType, entry.ParamsGOB, entry.ParamsOrigin)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to save params for '%s': %w", entry.Filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetParams 从 'config_params' 表中获取一个模板的参数.
|
||||
// 返回的 ParamsGOB 是 GOB 编码的字节切片; 调用方需要自行解码.
|
||||
func (cdb *ConfigDB) GetParams(filename string) (*ParamsEntry, error) {
|
||||
querySQL := `SELECT filename, template_type, params_gob, params_origin, created_at, updated_at FROM config_params WHERE filename = ?;`
|
||||
row := cdb.DB.QueryRow(querySQL, filename)
|
||||
|
||||
entry := &ParamsEntry{}
|
||||
err := row.Scan(&entry.Filename, &entry.TemplateType, &entry.ParamsGOB, &entry.ParamsOrigin, &entry.CreatedAt, &entry.UpdatedAt)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("db: params for '%s' not found: %w", filename, err)
|
||||
}
|
||||
return nil, fmt.Errorf("db: failed to get params for '%s': %w", filename, err)
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// DeleteParams 从 'config_params' 表中删除一个模板的参数.
|
||||
// 此操作将级联删除 'rendered_configs' 表中与该 filename 关联的所有渲染产物.
|
||||
func (cdb *ConfigDB) DeleteParams(filename string) error {
|
||||
_, err := cdb.DB.Exec(`DELETE FROM config_params WHERE filename = ?;`, filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to delete params for '%s': %w", filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFileNames
|
||||
func (cdb *ConfigDB) GetFileNames() ([]string, error) {
|
||||
querySQL := `SELECT filename FROM config_params;`
|
||||
rows, err := cdb.DB.Query(querySQL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: failed to get filenames from config_params: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var filenames []string
|
||||
for rows.Next() {
|
||||
var filename string
|
||||
if err := rows.Scan(&filename); err != nil {
|
||||
return nil, fmt.Errorf("db: failed to scan filename: %w", err)
|
||||
}
|
||||
filenames = append(filenames, filename)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("db: error during rows iteration: %w", err)
|
||||
}
|
||||
|
||||
return filenames, nil
|
||||
}
|
||||
|
||||
// RangeAllParams
|
||||
func (cdb *ConfigDB) RangeAllParams() ([]ParamsEntry, error) {
|
||||
querySQL := `SELECT filename, template_type, params_gob, params_origin, created_at, updated_at FROM config_params;`
|
||||
rows, err := cdb.DB.Query(querySQL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: failed to get all params: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var params []ParamsEntry
|
||||
for rows.Next() {
|
||||
var entry ParamsEntry
|
||||
if err := rows.Scan(&entry.Filename, &entry.TemplateType, &entry.ParamsGOB, &entry.CreatedAt, &entry.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("db: failed to scan params entry: %w", err)
|
||||
}
|
||||
params = append(params, entry)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("db: error during params rows iteration: %w", err)
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// --- 渲染产物操作 (Rendered_Configs Table) ---
|
||||
|
||||
// SaveRenderedConfig 在 'rendered_configs' 表中保存或更新一个渲染后的配置文件内容.
|
||||
// 注意: 该操作依赖于 'config_params' 表中已存在对应的 filename; 否则会违反外键约束.
|
||||
func (cdb *ConfigDB) SaveRenderedConfig(entry RenderedConfigEntry) error {
|
||||
insertSQL := `
|
||||
INSERT INTO rendered_configs (filename, rendered_content, rendered_at)
|
||||
VALUES (?, ?, strftime('%s', 'now'))
|
||||
ON CONFLICT(filename) DO UPDATE SET
|
||||
rendered_content = EXCLUDED.rendered_content,
|
||||
rendered_at = strftime('%s', 'now'),
|
||||
updated_at = strftime('%s', 'now');
|
||||
`
|
||||
_, err := cdb.DB.Exec(insertSQL, entry.Filename, entry.RenderedContent)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to save rendered config for '%s': %w", entry.Filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRenderedConfig 从 'rendered_configs' 表中获取一个渲染后的配置文件内容.
|
||||
func (cdb *ConfigDB) GetRenderedConfig(filename string) (*RenderedConfigEntry, error) {
|
||||
querySQL := `SELECT filename, rendered_content, rendered_at, updated_at FROM rendered_configs WHERE filename = ?;`
|
||||
row := cdb.DB.QueryRow(querySQL, filename)
|
||||
|
||||
entry := &RenderedConfigEntry{}
|
||||
err := row.Scan(&entry.Filename, &entry.RenderedContent, &entry.RenderedAt, &entry.UpdatedAt)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("db: rendered config '%s' not found: %w", filename, err)
|
||||
}
|
||||
return nil, fmt.Errorf("db: failed to get rendered config '%s': %w", filename, err)
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// DeleteRenderedConfig 从 'rendered_configs' 表中删除一个渲染后的配置文件内容.
|
||||
func (cdb *ConfigDB) DeleteRenderedConfig(filename string) error {
|
||||
_, err := cdb.DB.Exec(`DELETE FROM rendered_configs WHERE filename = ?;`, filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to delete rendered config for '%s': %w", filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RangeAllReandered
|
||||
func (cdb *ConfigDB) RangeAllReandered() ([]RenderedConfigEntry, error) {
|
||||
querySQL := `SELECT filename, rendered_content, rendered_at, updated_at FROM rendered_configs;`
|
||||
rows, err := cdb.DB.Query(querySQL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: failed to get all rendered configs: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var renderedConfigs []RenderedConfigEntry
|
||||
for rows.Next() {
|
||||
var entry RenderedConfigEntry
|
||||
if err := rows.Scan(&entry.Filename, &entry.RenderedContent, &entry.RenderedAt, &entry.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("db: failed to scan rendered config entry: %w", err)
|
||||
}
|
||||
renderedConfigs = append(renderedConfigs, entry)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("db: error during rendered configs rows iteration: %w", err)
|
||||
}
|
||||
|
||||
return renderedConfigs, nil
|
||||
}
|
||||
|
||||
// --- GOB 编码/解码辅助函数 ---
|
||||
|
||||
// gobEncode 将 Go 值编码为 GOB 格式的字节切片.
|
||||
// 'data' 必须是可被 GOB 编码的值; 例如基本类型, 切片, 映射, 结构体.
|
||||
func gobEncode(data interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
if err := enc.Encode(data); err != nil {
|
||||
return nil, fmt.Errorf("db: failed to GOB encode data: %w", err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// gobDecode 将 GOB 格式的字节切片解码到 Go 值.
|
||||
// 'data' 是 GOB 编码的字节切片.
|
||||
// 'valuePtr' 必须是指向目标 Go 值的指针; 其类型必须与编码时的数据类型兼容.
|
||||
func gobDecode(data []byte, valuePtr interface{}) error {
|
||||
buf := bytes.NewBuffer(data)
|
||||
dec := gob.NewDecoder(buf)
|
||||
if err := dec.Decode(valuePtr); err != nil {
|
||||
return fmt.Errorf("db: failed to GOB decode data: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- 业务逻辑: 渲染并保存 (由使用者调用) ---
|
||||
// RenderAndSaveConfig 从数据库获取模板和参数; 渲染后保存到 'rendered_configs' 表.
|
||||
// 这是一个组合操作; 通常由应用程序逻辑在需要时调用.
|
||||
// filename: 要渲染的配置文件的唯一标识.
|
||||
// templateParser: 一个实现了 TemplateParser 接口的模板解析器实例.
|
||||
// dynamicParams: 运行时动态提供的参数; 它们会覆盖存储在数据库中的同名参数.
|
||||
func (cdb *ConfigDB) RenderAndSaveConfig(filename string, dynamicParams map[string]interface{}) error {
|
||||
// 1. 获取模板内容.
|
||||
tmplEntry, err := cdb.GetTemplate(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db: failed to get template '%s' for rendering: %w", filename, err)
|
||||
}
|
||||
|
||||
// 2. 获取存储的参数.
|
||||
paramsEntry, err := cdb.GetParams(filename)
|
||||
var storedParams map[string]interface{}
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
storedParams = make(map[string]interface{}) // 参数不存在; 使用空 map.
|
||||
} else {
|
||||
return fmt.Errorf("db: failed to get parameters for '%s': %w", filename, err)
|
||||
}
|
||||
} else {
|
||||
// 解码 GOB 参数到 map.
|
||||
if err := gobDecode(paramsEntry.ParamsGOB, &storedParams); err != nil {
|
||||
return fmt.Errorf("db: failed to decode stored parameters for '%s': %w", filename, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 合并参数: 动态传入的参数覆盖存储的参数.
|
||||
if dynamicParams != nil {
|
||||
for k, v := range dynamicParams {
|
||||
storedParams[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 渲染模板.
|
||||
var parsedTmpl *template.Template
|
||||
var parseErr error
|
||||
|
||||
// 使用传入的 templateParser 实例来解析模板内容.
|
||||
// 注意: templateParser.Parse 是在提供的实例上调用; 以解析特定内容.
|
||||
//parsedTmpl, parseErr = templateParser.Parse(string(tmplEntry.Content))
|
||||
parsedTmpl, parseErr = template.New(tmplEntry.Filename).Parse(string(tmplEntry.Content))
|
||||
|
||||
if parseErr != nil {
|
||||
return fmt.Errorf("db: failed to parse template content for '%s': %w", tmplEntry.Filename, parseErr)
|
||||
}
|
||||
|
||||
var renderedContentBuilder strings.Builder
|
||||
if err := parsedTmpl.Execute(&renderedContentBuilder, storedParams); err != nil {
|
||||
return fmt.Errorf("db: failed to render template '%s': %w", tmplEntry.Filename, err)
|
||||
}
|
||||
|
||||
// 4. 保存渲染结果.
|
||||
renderedEntry := RenderedConfigEntry{
|
||||
Filename: filename,
|
||||
RenderedContent: []byte(renderedContentBuilder.String()),
|
||||
}
|
||||
if err := cdb.SaveRenderedConfig(renderedEntry); err != nil {
|
||||
return fmt.Errorf("db: failed to save rendered config for '%s': %w", filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
40
db/struct.go
Normal file
40
db/struct.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package db
|
||||
|
||||
/*
|
||||
// 通用配置结构
|
||||
type CaddyfileConfig struct {
|
||||
Domain string `json:"domain"`
|
||||
TmplType string `json:"tmpl_type"`
|
||||
UpStream UpStreamConfig `json:"upstream"`
|
||||
FileServer FileServerConfig `json:"file_server"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Log LogConfig `json:"log"`
|
||||
ErrorPage ErrorPageConfig `json:"error_page"`
|
||||
Encode EncodeConfig `json:"encode"`
|
||||
}
|
||||
|
||||
type UpStreamConfig struct {
|
||||
UpStream string `json:"upstream"`
|
||||
MutiUpStream bool `json:"muti_upstream"`
|
||||
UpStreams []string `json:"upstream_servers"`
|
||||
UpStreamHeaders map[string][]string `json:"upstream_headers"`
|
||||
}
|
||||
|
||||
type FileServerConfig struct {
|
||||
FileDirPath string `json:"file_dir_path"`
|
||||
EnableBrowser bool `json:"enable_browser"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
EnableLog bool `json:"enable_log"`
|
||||
LogDomain string `json:"log_domain"`
|
||||
}
|
||||
|
||||
type ErrorPageConfig struct {
|
||||
EnableErrorPage bool `json:"enable_error_page"`
|
||||
}
|
||||
|
||||
type EncodeConfig struct {
|
||||
EnableEncode bool `json:"enable_encode"`
|
||||
}
|
||||
*/
|
||||
Loading…
Add table
Add a link
Reference in a new issue