Merge pull request 'bhc' (#3) from bhc into dev

Reviewed-on: ProjectWIND/ProjectWIND#3
This commit is contained in:
Sheyiyuan 2025-02-25 10:43:20 +08:00
commit e22b2f980d
3 changed files with 551 additions and 109 deletions

View File

@ -818,7 +818,7 @@ func (a *apiInfo) Log(content string, args ...interface{}) {
} }
//database模块 //database模块
//TODO: 数据库模块待实现 //数据库部分允许字符串变量的读写操作,允许获取配置项操作
// 文件管理模块 // 文件管理模块
//TODO: 文件管理模块待实现 //TODO: 文件管理模块待实现

View File

@ -11,6 +11,9 @@ import (
"time" "time"
) )
const address = "./data/database/datamaps.wdb"
const core = "./data/core.json"
type unit struct { type unit struct {
Id string Id string
Data map[string]string Data map[string]string
@ -20,24 +23,76 @@ type User unit
type Group unit type Group unit
type Global unit type Global unit
type Database struct { type Configs struct {
Number map[string]int64
String map[string]string
Float map[string]float64
Number_Slice map[string][]int64
String_Slice map[string][]string
Hash string
}
type Datamap struct {
Id string Id string
Permission string
Users map[string]User Users map[string]User
Groups map[string]Group Groups map[string]Group
Global map[string]Global Global map[string]Global
Configs Configs
} }
func newDatabase(id string) Database { type Database struct {
// 创建数据库 Datamaps map[string]Datamap
db := &Database{ }
func newDatamap(id string) Datamap {
// 创建数据表
db := &Datamap{
Id: id, Id: id,
Permission: "private",
Users: make(map[string]User), Users: make(map[string]User),
Groups: make(map[string]Group), Groups: make(map[string]Group),
Global: make(map[string]Global), Global: make(map[string]Global),
Configs: Configs{
Number: make(map[string]int64),
String: make(map[string]string),
Float: make(map[string]float64),
Number_Slice: make(map[string][]int64),
String_Slice: make(map[string][]string),
Hash: "",
},
} }
return *db return *db
} }
func newDatabase() Database {
// 创建数据库
db := &Database{
Datamaps: make(map[string]Datamap),
}
return *db
}
func (this *Database) addDatamap(id string) {
// 创建新数据表
db := newDatamap(id)
this.Datamaps[id] = db
}
// func hash(word string) string {
// hash, err := bcrypt.GenerateFromPassword([]byte(word), bcrypt.DefaultCost)
// if err != nil {
// LOG.Error("[Error]Error while hash password: %v", err)
// return ""
// }
// return string(hash)
// }
// func hashCheck(word string, hash string) bool {
// err := bcrypt.CompareHashAndPassword(hash, []byte(word))
// return err == nil
// }
func folderCheck(filename string) { func folderCheck(filename string) {
if _, err := os.Stat(filename); os.IsNotExist(err) { if _, err := os.Stat(filename); os.IsNotExist(err) {
err := os.MkdirAll(filename, 0755) err := os.MkdirAll(filename, 0755)
@ -90,6 +145,29 @@ func printContent(file string) (string, error) {
} }
} }
func getCorePassword() string {
// 获取核心密码
filename := core
fileCheck(filename)
dataJson, err := printContent(filename)
if err != nil {
LOG.Error("[Error]Error while read file %s: %v", filename, err)
return ""
}
config := make(map[string]string)
err = json.Unmarshal([]byte(dataJson), config)
if err != nil {
LOG.Error("[Error]Error while unmarshal data: %v", err)
return ""
}
password, ok := config["password"]
if !ok {
LOG.Warn("[Warning]Password not found in core.json")
return ""
}
return password
}
func saveData(db *Database) error { func saveData(db *Database) error {
// 保存数据到文件 // 保存数据到文件
dataJson, err := json.Marshal(db) dataJson, err := json.Marshal(db)
@ -97,7 +175,7 @@ func saveData(db *Database) error {
LOG.Error("[Error]:Error while marshal data: %v", err) LOG.Error("[Error]:Error while marshal data: %v", err)
return err return err
} }
filename := "./database/" + db.Id + ".wdb" filename := address
file, err := os.Create(filename) file, err := os.Create(filename)
if err != nil { if err != nil {
LOG.Error("[Error]:Error while create file %s: %v", filename, err) LOG.Error("[Error]:Error while create file %s: %v", filename, err)
@ -109,7 +187,7 @@ func saveData(db *Database) error {
func loadData(db *Database) error { func loadData(db *Database) error {
// 读取配置文件 // 读取配置文件
filename := "./database/" + db.Id + ".wdb" filename := address
fileCheck(filename) fileCheck(filename)
dataJson, err := printContent(filename) dataJson, err := printContent(filename)
if err != nil { if err != nil {
@ -120,7 +198,7 @@ func loadData(db *Database) error {
err = json.Unmarshal([]byte(dataJson), db) err = json.Unmarshal([]byte(dataJson), db)
if err != nil { if err != nil {
// log.Printf("[Error]:Error while unmarshal data: %v", err) // log.Printf("[Error]:Error while unmarshal data: %v", err)
LOG.Warn("[WARNING]:Error while unmarshal data: %v", err) LOG.Warn("[Warning]:Error while unmarshal data: %v", err)
return err return err
} }
return nil return nil
@ -128,116 +206,239 @@ func loadData(db *Database) error {
var DB *Database var DB *Database
func dataGet(db *Database, category string, id string, key string) (string, bool) { func dataSet(datamap string, unit string, id string, key string, value interface{}, allowed bool) {
// 查询数据
switch category {
case "user":
user, ok := db.Users[id]
if !ok {
LOG.Warn("[WARNING]:User %s not found", id)
return "", false
}
if user.Data == nil {
LOG.Warn("[WARNING]:User %s's data is nil", id)
return "", false
}
value, ok := user.Data[key]
if !ok {
LOG.Warn("[WARNING]:User %s's data %s not found", id, key)
return "", false
}
return value, true
case "group":
group, ok := db.Groups[id]
if !ok {
LOG.Warn("[WARNING]:Group %s not found", id)
return "", false
}
if group.Data == nil {
LOG.Warn("[WARNING]:Group %s's data is nil", id)
return "", false
}
value, ok := group.Data[key]
if !ok {
LOG.Warn("[WARNING]:Group %s's data %s not found", id, key)
return "", false
}
return value, true
case "global":
global, ok := db.Global[id]
if !ok {
LOG.Warn("[WARNING]:Global %s not found", id)
return "", false
}
if global.Data == nil {
LOG.Warn("[WARNING]:Global data of %s is nil", id)
return "", false
}
value, ok := global.Data[key]
if !ok {
LOG.Warn("[WARNING]:Global data of %s's %s not found", id, key)
return "", false
}
return value, true
default:
LOG.Error("[Error]:Invalid category %s", category)
return "", false
}
}
func dataSet(db *Database, category string, id string, key string, value string) {
// 修改数据 // 修改数据
switch category { dm, ok := DB.Datamaps[datamap]
case "user":
user, ok := db.Users[id]
if !ok { if !ok {
db.Users[id] = User{ // 创建新数据表
DB.addDatamap(datamap)
dm = DB.Datamaps[datamap]
}
if !allowed && dm.Permission != "private" {
LOG.Warn("[Warning]:Permission denied")
return
}
switch unit {
case "config":
switch id {
case "number":
valueInt64, ok := value.(int64) // 断言value为int64类型
if !ok {
LOG.Error("[Error]:Value cannot be asserted to int64")
return
}
dm.Configs.Number[key] = valueInt64 // 使用断言后的int64值
case "string":
valueStr, ok := value.(string) // 断言value为string类型
if !ok {
LOG.Error("[Error]:Value cannot be asserted to string")
return
}
dm.Configs.String[key] = valueStr // 使用断言后的string值
case "float":
valueFloat64, ok := value.(float64) // 断言value为float64类型
if !ok {
LOG.Error("[Error]:Value cannot be asserted to float64")
return
}
dm.Configs.Float[key] = valueFloat64 // 使用断言后的float64值
case "number_slice":
valueInt64Slice, ok := value.([]int64) // 断言value为[]int64类型
if !ok {
LOG.Error("[Error]:Value cannot be asserted to []int64")
return
}
dm.Configs.Number_Slice[key] = valueInt64Slice // 使用断言后的[]int64值
case "string_slice":
valueStrSlice, ok := value.([]string) // 断言value为[]string类型
if !ok {
LOG.Error("[Error]:Value cannot be asserted to []string")
return
}
dm.Configs.String_Slice[key] = valueStrSlice // 使用断言后的[]string值
case "hash":
valueStr, ok := value.(string) // 断言value为string类型
if !ok {
LOG.Error("[Error]:Value cannot be asserted to string")
return
}
dm.Configs.Hash = valueStr // 使用断言后的string值
default:
LOG.Error("[Error]:Invalid id %s", id)
}
case "user":
valueStr, ok := value.(string) // 断言value为string类型
if !ok {
LOG.Error("[Error]:Value cannot be asserted to string")
return
}
user, ok := dm.Users[id]
if !ok {
dm.Users[id] = User{
Id: id, Id: id,
Data: make(map[string]string), Data: make(map[string]string),
} }
user = db.Users[id] user = dm.Users[id]
} }
if user.Data == nil { if user.Data == nil {
user.Data = make(map[string]string) user.Data = make(map[string]string)
} }
user.Data[key] = value user.Data[key] = valueStr // 使用断言后的string值
case "group": case "group":
group, ok := db.Groups[id] valueStr, ok := value.(string) // 断言value为string类型
if !ok { if !ok {
db.Groups[id] = Group{ LOG.Error("[Error]:Value cannot be asserted to string")
return
}
group, ok := dm.Groups[id]
if !ok {
dm.Groups[id] = Group{
Id: id, Id: id,
Data: make(map[string]string), Data: make(map[string]string),
} }
group = db.Groups[id] group = dm.Groups[id]
} }
if group.Data == nil { if group.Data == nil {
group.Data = make(map[string]string) group.Data = make(map[string]string)
} }
group.Data[key] = value group.Data[key] = valueStr // 使用断言后的string值
case "global": case "global":
global, ok := db.Global[id] valueStr, ok := value.(string) // 断言value为string类型
if !ok { if !ok {
db.Global[id] = Global{ LOG.Error("[Error]:Value cannot be asserted to string")
return
}
global, ok := dm.Global[id]
if !ok {
dm.Global[id] = Global{
Id: id, Id: id,
Data: make(map[string]string), Data: make(map[string]string),
} }
global = db.Global[id] global = dm.Global[id]
} }
if global.Data == nil { if global.Data == nil {
global.Data = make(map[string]string) global.Data = make(map[string]string)
} }
global.Data[key] = value global.Data[key] = valueStr // 使用断言后的string值
default: default:
LOG.Error("[Error]:Invalid category %s", category) LOG.Error("[Error]:Invalid unit %s", unit)
}
}
func dataGet(datamap string, unit string, id string, key string, allowed bool) (interface{}, bool) {
dm, ok := DB.Datamaps[datamap]
if !ok {
LOG.Warn("[Warning]:Datamap %s not found", datamap)
return "", false
}
if !allowed && dm.Permission != "private" {
LOG.Warn("[Warning]:Permission denied")
return "", false
}
switch unit {
case "config":
switch id {
case "number":
value, ok := dm.Configs.Number[key]
if !ok {
LOG.Warn("[Warning]:Config %s not found", key)
return 0, false
}
return value, true
case "string":
value, ok := dm.Configs.String[key]
if !ok {
LOG.Warn("[Warning]:Config %s not found", key)
return "", false
}
return value, true
case "float":
value, ok := dm.Configs.Float[key]
if !ok {
LOG.Warn("[Warning]:Config %s not found", key)
return 0.0, false
}
return value, true
case "number_slice":
value, ok := dm.Configs.Number_Slice[key]
if !ok {
LOG.Warn("[Warning]:Config %s not found", key)
return []int64{}, false
}
return value, true
case "string_slice":
value, ok := dm.Configs.String_Slice[key]
if !ok {
LOG.Warn("[Warning]:Config %s not found", key)
return []string{}, false
}
return value, true
case "hash":
return dm.Configs.Hash, true
default:
LOG.Error("[Error]:Invalid id %s", id)
return "", false
}
case "user":
user, ok := dm.Users[id]
if !ok {
LOG.Warn("[Warning]:User %s not found", id)
return "", false
}
if user.Data == nil {
LOG.Warn("[Warning]:User %s's data is nil", id)
return "", false
}
value, ok := user.Data[key]
if !ok {
LOG.Warn("[Warning]:User %s's data %s not found", id, key)
return "", false
}
return value, true
case "group":
group, ok := dm.Groups[id]
if !ok {
LOG.Warn("[Warning]:Group %s not found", id)
return "", false
}
if group.Data == nil {
LOG.Warn("[Warning]:Group %s's data is nil", id)
return "", false
}
value, ok := group.Data[key]
if !ok {
LOG.Warn("[Warning]:Group %s's data %s not found", id, key)
return "", false
}
return value, true
case "global":
global, ok := dm.Global[id]
if !ok {
LOG.Warn("[Warning]:Global %s not found", id)
return "", false
}
if global.Data == nil {
LOG.Warn("[Warning]:Global data of %s is nil", id)
return "", false
}
value, ok := global.Data[key]
if !ok {
LOG.Warn("[Warning]:Global data of %s's %s not found", id, key)
return "", false
}
return value, true
default:
LOG.Error("[Error]:Invalid unit %s", unit)
return "", false
} }
} }
func initializeDatabase() *Database { func initializeDatabase() *Database {
// 启动并检查程序 // 启动并检查程序
LOG.Info("正在启动数据库 ...") LOG.Info("Starting database ...")
db := newDatabase("datamap") db := newDatabase()
loadData(&db) loadData(&db)
LOG.Info("数据库启动成功") LOG.Info("Database started successfully.")
return &db return &db
} }
@ -258,21 +459,13 @@ func Start() {
select { select {
case <-dataChan: case <-dataChan:
// 接收到信号,保存数据并退出程序 // 接收到信号,保存数据并退出程序
LOG.Info("即将退出程序,正在保存数据...") LOG.Info("Received signal, saving data and exiting...")
err := saveData(DB) saveData(DB)
if err != nil {
LOG.Error("退出程序时保存数据出错: %v", err)
return
}
os.Exit(0) os.Exit(0)
case <-saveTicker.C: case <-saveTicker.C:
// 定时保存数据 // 定时保存数据
LOG.Info("自动保存") LOG.Info("Saving data automatically...")
err := saveData(DB) saveData(DB)
if err != nil {
LOG.Error("自动保存出错: %v", err)
return
}
} }
} }
}() }()
@ -280,12 +473,153 @@ func Start() {
select {} // 阻塞 select {} // 阻塞
} }
func Get(category string, id string, key string) (string, bool) { func CreatePublicDatamap(id string) {
// 查询数据 // 创建公开数据表
return dataGet(DB, category, id, key) db := newDatamap(id)
db.Permission = "public"
DB.Datamaps[id] = db
} }
func Set(category string, id string, key string, value string) { func Get(appName string, datamap string, unit string, id string, key string, isGettingConfig bool) (interface{}, bool) {
// 修改数据 // 查询数据
dataSet(DB, category, id, key, value) if unit == "config" && id == "hash" {
// app不允许访问hash数据
LOG.Error("[Error]:App %s is not allowed to access hash data", appName)
return "", false
} }
if !isGettingConfig && unit == "config" {
// 不允许在非config数据表中访问config数据
LOG.Error("[Error]:App %s is not allowed to access config data", appName)
return "", false
}
if appName != datamap {
// 需要master密码来访问其他app的数据
hash := getCorePassword()
if hash == "" {
// 删除数据表哈希
dataSet(appName, "config", "hash", "", "", false)
}
datahash, ok := dataGet(appName, "config", "hash", "", false)
if !ok {
LOG.Error("[Error]:Error while get hash of %s", appName)
}
if hash != datahash {
LOG.Warn("[Warning]:App %s is not allowed to access data of %s", appName, datamap)
return dataGet(appName, unit, id, key, false)
}
}
return dataGet(appName, unit, id, key, true)
}
func Set(appName string, datamap string, unit string, id string, key string, value interface{}) {
// 修改数据
if unit == "config" {
// app不允许修改config数据
LOG.Error("[Error]:App %s is not allowed to modify config data", appName)
return
}
if appName != datamap {
// 需要master密码来访问其他app的数据
hash := getCorePassword()
if hash == "" {
// 删除数据表哈希
dataSet(appName, "config", "hash", "", "", false)
}
datahash, ok := dataGet(appName, "config", "hash", "", false)
if !ok {
LOG.Error("[Error]:Error while get hash of %s", appName)
}
if hash != datahash {
LOG.Warn("[Warning]:App %s is not allowed to access data of %s", appName, datamap)
dataSet(appName, unit, id, key, value, false)
}
}
dataSet(appName, unit, id, key, value, true)
}
type DatabaseHandlerImpl struct{}
func (dbh *DatabaseHandlerImpl) Set(appName string, datamap string, unit string, id string, key string, value interface{}) {
Set(appName, datamap, unit, id, key, value)
}
func (dbh *DatabaseHandlerImpl) Get(appName string, datamap string, unit string, id string, key string, isGettingConfig bool) (interface{}, bool) {
return Get(appName, datamap, unit, id, key, isGettingConfig)
}
// func VarSet(app wba.AppInfo, datamap string, unit string, id string, key string, value string) {
// Set(app.Name, datamap, unit, id, key, value)
// }
// func VarGet(app wba.AppInfo, datamap string, unit string, id string, key string) (string, bool) {
// res, ok := Get(app.Name, datamap, unit, id, key, false)
// if !ok {
// return "", false
// }
// resStr, ok := res.(string)
// if !ok {
// return "", false
// }
// return resStr, true
// }
// func GetIntConfig(app wba.AppInfo, datamap string, key string) (int64, bool) {
// res, ok := Get(app.Name, datamap, "config", "number", key, true)
// if !ok {
// return 0, false
// }
// resInt, ok := res.(int64)
// if !ok {
// return 0, false
// }
// return resInt, true
// }
// func GetStringConfig(app wba.AppInfo, datamap string, key string) (string, bool) {
// res, ok := Get(app.Name, datamap, "config", "string", key, true)
// if !ok {
// return "", false
// }
// resStr, ok := res.(string)
// if !ok {
// return "", false
// }
// return resStr, true
// }
// func GetFloatConfig(app wba.AppInfo, datamap string, key string) (float64, bool) {
// res, ok := Get(app.Name, datamap, "config", "float", key, true)
// if !ok {
// return 0, false
// }
// resFloat, ok := res.(float64)
// if !ok {
// return 0, false
// }
// return resFloat, true
// }
// func GetIntSliceConfig(app wba.AppInfo, datamap string, key string) ([]int64, bool) {
// res, ok := Get(app.Name, datamap, "config", "number_slice", key, true)
// if !ok {
// return nil, false
// }
// resSlice, ok := res.([]int64)
// if !ok {
// return nil, false
// }
// return resSlice, true
// }
// func GetStringSliceConfig(app wba.AppInfo, datamap string, key string) ([]string, bool) {
// res, ok := Get(app.Name, datamap, "config", "string_slice", key, true)
// if !ok {
// return nil, false
// }
// resSlice, ok := res.([]string)
// if !ok {
// return nil, false
// }
// return resSlice, true
// }

View File

@ -265,6 +265,11 @@ type WindStandardProtocolAPI interface {
Log(log string, args ...interface{}) Log(log string, args ...interface{})
} }
type DataBaseHandler interface {
Set(appName string, datamap string, unit string, id string, key string, value interface{})
Get(appName string, datamap string, unit string, id string, key string, isGettingConfig bool) (interface{}, bool)
}
type AppInfo struct { type AppInfo struct {
Name string Name string
Version string Version string
@ -282,6 +287,7 @@ type AppInfo struct {
MetaEventHandler func(msg MetaEventInfo) MetaEventHandler func(msg MetaEventInfo)
ScheduledTasks map[string]ScheduledTaskInfo ScheduledTasks map[string]ScheduledTaskInfo
API map[string]interface{} API map[string]interface{}
dbHandler DataBaseHandler
} }
func (ai AppInfo) Get() AppInfo { func (ai AppInfo) Get() AppInfo {
@ -301,6 +307,108 @@ func (ai *AppInfo) AddNoticeEventHandler(ScheduledTask ScheduledTaskInfo) {
ai.ScheduledTasks[ScheduledTask.Name] = ScheduledTask ai.ScheduledTasks[ScheduledTask.Name] = ScheduledTask
} }
func (ai *AppInfo) VarSet(datamap string, unit string, id string, key string, value string) {
if ai.dbHandler != nil {
ai.dbHandler.Set(ai.Name, datamap, unit, id, key, value)
}
}
// VarGet 获取变量
func (ai *AppInfo) VarGet(datamap string, unit string, id string, key string) (string, bool) {
if ai.dbHandler != nil {
res, ok := ai.dbHandler.Get(ai.Name, datamap, unit, id, key, false)
if !ok {
return "", false
}
resStr, ok := res.(string)
if !ok {
return "", false
}
return resStr, true
}
return "", false
}
// GetIntConfig 获取整数配置
func (ai *AppInfo) GetIntConfig(datamap string, key string) (int64, bool) {
if ai.dbHandler != nil {
res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "number", key, true)
if !ok {
return 0, false
}
resInt, ok := res.(int64)
if !ok {
return 0, false
}
return resInt, true
}
return 0, false
}
// GetStringConfig 获取字符串配置
func (ai *AppInfo) GetStringConfig(datamap string, key string) (string, bool) {
if ai.dbHandler != nil {
res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "string", key, true)
if !ok {
return "", false
}
resStr, ok := res.(string)
if !ok {
return "", false
}
return resStr, true
}
return "", false
}
// GetFloatConfig 获取浮点数配置
func (ai *AppInfo) GetFloatConfig(datamap string, key string) (float64, bool) {
if ai.dbHandler != nil {
res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "float", key, true)
if !ok {
return 0, false
}
resFloat, ok := res.(float64)
if !ok {
return 0, false
}
return resFloat, true
}
return 0, false
}
// GetIntSliceConfig 获取整数切片配置
func (ai *AppInfo) GetIntSliceConfig(datamap string, key string) ([]int64, bool) {
if ai.dbHandler != nil {
res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "number_slice", key, true)
if !ok {
return nil, false
}
resSlice, ok := res.([]int64)
if !ok {
return nil, false
}
return resSlice, true
}
return nil, false
}
// GetStringSliceConfig 获取字符串切片配置
func (ai *AppInfo) GetStringSliceConfig(datamap string, key string) ([]string, bool) {
if ai.dbHandler != nil {
res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "string_slice", key, true)
if !ok {
return nil, false
}
resSlice, ok := res.([]string)
if !ok {
return nil, false
}
return resSlice, true
}
return nil, false
}
type AppInfoOption func(ei *AppInfo) type AppInfoOption func(ei *AppInfo)
func WithName(name string) AppInfoOption { func WithName(name string) AppInfoOption {