Initial commit (code only without large binaries)
This commit is contained in:
65
EdgeAPI/internal/db/utils/disk.go
Normal file
65
EdgeAPI/internal/db/utils/disk.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package dbutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"time"
|
||||
)
|
||||
|
||||
const minFreeSpaceGB = 3
|
||||
|
||||
var HasFreeSpace = true
|
||||
var IsLocalDatabase = false
|
||||
var LocalDatabaseDataDir = ""
|
||||
|
||||
func init() {
|
||||
var ticker = time.NewTicker(5 * time.Minute)
|
||||
|
||||
dbs.OnReadyDone(func() {
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
HasFreeSpace = CheckHasFreeSpace()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// CheckHasFreeSpace 检查当前数据库是否有剩余空间
|
||||
func CheckHasFreeSpace() bool {
|
||||
db, _ := dbs.Default()
|
||||
if db == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
config, _ := db.Config()
|
||||
if config == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dsnConfig, _ := mysql.ParseDSN(config.Dsn)
|
||||
if dsnConfig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if IsLocalAddr(dsnConfig.Addr) {
|
||||
IsLocalDatabase = true
|
||||
|
||||
// only for local database
|
||||
one, err := db.FindOne("SHOW VARIABLES WHERE variable_name='datadir'")
|
||||
if err != nil || len(one) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
var dir = one.GetString("Value")
|
||||
if len(dir) == 0 {
|
||||
return true
|
||||
}
|
||||
LocalDatabaseDataDir = dir
|
||||
|
||||
return checkHasFreeSpace(dir)
|
||||
}
|
||||
return true
|
||||
}
|
||||
14
EdgeAPI/internal/db/utils/disk_test.go
Normal file
14
EdgeAPI/internal/db/utils/disk_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package dbutils_test
|
||||
|
||||
import (
|
||||
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHasFreeSpace(t *testing.T) {
|
||||
t.Log(dbutils.CheckHasFreeSpace())
|
||||
t.Log(dbutils.LocalDatabaseDataDir)
|
||||
}
|
||||
18
EdgeAPI/internal/db/utils/disk_unix.go
Normal file
18
EdgeAPI/internal/db/utils/disk_unix.go
Normal file
@@ -0,0 +1,18 @@
|
||||
//go:build !windows
|
||||
|
||||
package dbutils
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func checkHasFreeSpace(dir string) bool {
|
||||
var stat unix.Statfs_t
|
||||
err := unix.Statfs(dir, &stat)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
var availableSpace = (stat.Bavail * uint64(stat.Bsize)) / (1 << 30) // GB
|
||||
return availableSpace > minFreeSpaceGB
|
||||
}
|
||||
7
EdgeAPI/internal/db/utils/disk_windows.go
Normal file
7
EdgeAPI/internal/db/utils/disk_windows.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build windows
|
||||
|
||||
package dbutils
|
||||
|
||||
func checkHasFreeSpace(dir string) bool {
|
||||
return true
|
||||
}
|
||||
214
EdgeAPI/internal/db/utils/utils.go
Normal file
214
EdgeAPI/internal/db/utils/utils.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package dbutils
|
||||
|
||||
import (
|
||||
executils "github.com/TeaOSLab/EdgeAPI/internal/utils/exec"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewQuery 构造Query
|
||||
func NewQuery(tx *dbs.Tx, dao dbs.DAOWrapper, adminId int64, userId int64) *dbs.Query {
|
||||
query := dao.Object().Query(tx)
|
||||
if adminId > 0 {
|
||||
//query.Attr("adminId", adminId)
|
||||
query.Gt("adminId", 0)
|
||||
}
|
||||
if userId > 0 {
|
||||
query.Attr("userId", userId)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QuoteLikeKeyword 处理关键词中的特殊字符
|
||||
func QuoteLikeKeyword(keyword string) string {
|
||||
keyword = strings.ReplaceAll(keyword, "%", "\\%")
|
||||
keyword = strings.ReplaceAll(keyword, "_", "\\_")
|
||||
return keyword
|
||||
}
|
||||
|
||||
func QuoteLike(keyword string) string {
|
||||
return "%" + QuoteLikeKeyword(keyword) + "%"
|
||||
}
|
||||
|
||||
func QuoteLikePrefix(keyword string) string {
|
||||
return QuoteLikeKeyword(keyword) + "%"
|
||||
}
|
||||
|
||||
func QuoteLikeSuffix(keyword string) string {
|
||||
return "%" + QuoteLikeKeyword(keyword)
|
||||
}
|
||||
|
||||
// SetGlobalVarMin 设置变量最小值
|
||||
func SetGlobalVarMin(db *dbs.DB, variableName string, minValue int) error {
|
||||
result, err := db.FindOne("SHOW VARIABLES WHERE variable_name=?", variableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
var oldValue = result.GetInt("Value")
|
||||
if oldValue > 0 /** 小于等于0通常表示不限制 **/ && oldValue < minValue {
|
||||
_, err = db.Exec("SET GLOBAL "+variableName+"=?", minValue)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetGlobalVarMax 设置变量最大值
|
||||
func SetGlobalVarMax(db *dbs.DB, variableName string, maxValue int) error {
|
||||
result, err := db.FindOne("SHOW VARIABLES WHERE variable_name=?", variableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
var oldValue = result.GetInt("Value")
|
||||
if oldValue > maxValue {
|
||||
_, err = db.Exec("SET GLOBAL "+variableName+"=?", maxValue)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLocalAddr 是否为本地数据库
|
||||
func IsLocalAddr(addr string) bool {
|
||||
var host = addr
|
||||
if strings.Contains(addr, ":") {
|
||||
host, _, _ = net.SplitHostPort(addr)
|
||||
if len(host) == 0 {
|
||||
host = addr
|
||||
}
|
||||
}
|
||||
|
||||
if host == "127.0.0.1" || host == "::1" || host == "localhost" {
|
||||
return true
|
||||
}
|
||||
|
||||
interfaceAddrs, _ := net.InterfaceAddrs()
|
||||
for _, interfaceAddr := range interfaceAddrs {
|
||||
if strings.HasPrefix(interfaceAddr.String(), host+"/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MySQLVersion 读取当前MySQL版本
|
||||
func MySQLVersion() (version string, err error) {
|
||||
db, err := dbs.Default()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result, err := db.FindCol(0, "SELECT VERSION()")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version = types.String(result)
|
||||
var suffixIndex = strings.Index(version, "-")
|
||||
if suffixIndex > 0 {
|
||||
version = version[:suffixIndex]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func MySQLVersionFrom8() (bool, error) {
|
||||
version, err := MySQLVersion()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(version) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
var dotIndex = strings.Index(version, ".")
|
||||
if dotIndex > 0 {
|
||||
return types.Int(version[:dotIndex]) >= 8, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// FindMySQLPath find out mysqld_safe path from system processes
|
||||
func FindMySQLPath() string {
|
||||
psExe, err := executils.LookPath("ps")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var cmd = executils.NewTimeoutCmd(3*time.Second, psExe, "-ef").
|
||||
WithStdout().
|
||||
WithStderr()
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var reg = regexp.MustCompile(`\s(/\S+/mysqld_safe)\s`)
|
||||
var matches = reg.FindStringSubmatch(cmd.Stdout())
|
||||
if len(matches) > 1 {
|
||||
var path = matches[1]
|
||||
_, err = os.Stat(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// FindMySQLPathAndRemember find out mysqld_safe path then remember it for future usage
|
||||
func FindMySQLPathAndRemember() {
|
||||
var path = FindMySQLPath()
|
||||
if len(path) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var cacheFile = Tea.Root + "/data/mysql-path.cache"
|
||||
_ = os.WriteFile(cacheFile, []byte(path), 0666) // ignore error
|
||||
}
|
||||
|
||||
// StartLocalMySQL try to start local mysql server
|
||||
func StartLocalMySQL() {
|
||||
// possible installed paths
|
||||
var mysqldSafeFiles = []string{}
|
||||
|
||||
// read last path from cache file
|
||||
var cacheFile = Tea.Root + "/data/mysql-path.cache"
|
||||
cacheData, err := os.ReadFile(cacheFile)
|
||||
if err == nil && len(cacheData) > 0 {
|
||||
mysqldSafeFiles = append(mysqldSafeFiles, string(cacheData))
|
||||
}
|
||||
|
||||
// from $PATH variable
|
||||
exePath, lookErr := executils.LookPath("mysqld_safe")
|
||||
if lookErr == nil && len(exePath) > 0 && !lists.ContainsString(mysqldSafeFiles, exePath) {
|
||||
mysqldSafeFiles = append(mysqldSafeFiles, exePath)
|
||||
}
|
||||
|
||||
// these installed by edge-boot or foolish-mysql
|
||||
for _, path := range []string{
|
||||
"/usr/local/mysql/bin/mysqld_safe",
|
||||
"/usr/local/mysql8/bin/mysqld_safe",
|
||||
} {
|
||||
if !lists.ContainsString(mysqldSafeFiles, path) {
|
||||
mysqldSafeFiles = append(mysqldSafeFiles, path)
|
||||
}
|
||||
}
|
||||
|
||||
for _, mysqldSafeFile := range mysqldSafeFiles {
|
||||
_, err := os.Stat(mysqldSafeFile)
|
||||
if err == nil {
|
||||
logs.Println("[API_NODE]try to start local mysql server from '" + mysqldSafeFile + "' ...")
|
||||
var mysqlCmd = exec.Command(mysqldSafeFile)
|
||||
_ = mysqlCmd.Start()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
45
EdgeAPI/internal/db/utils/utils_test.go
Normal file
45
EdgeAPI/internal/db/utils/utils_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package dbutils_test
|
||||
|
||||
import (
|
||||
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQuoteLike(t *testing.T) {
|
||||
for _, s := range []string{"abc", "abc%", "_abc%%%"} {
|
||||
t.Log(s + " => " + dbutils.QuoteLike(s))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsLocalAddr(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
a.IsTrue(dbutils.IsLocalAddr("127.0.0.1"))
|
||||
a.IsTrue(dbutils.IsLocalAddr("localhost"))
|
||||
a.IsTrue(dbutils.IsLocalAddr("::1"))
|
||||
a.IsTrue(dbutils.IsLocalAddr("127.0.0.1:3306"))
|
||||
a.IsFalse(dbutils.IsLocalAddr("192.168.2.200"))
|
||||
a.IsFalse(dbutils.IsLocalAddr("192.168.2.200:3306"))
|
||||
}
|
||||
|
||||
func TestMySQLVersion(t *testing.T) {
|
||||
version, err := dbutils.MySQLVersion()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("version:", version)
|
||||
}
|
||||
|
||||
func TestMySQLVersionFrom8(t *testing.T) {
|
||||
t.Log(dbutils.MySQLVersionFrom8())
|
||||
}
|
||||
|
||||
func TestFindMySQLPath(t *testing.T) {
|
||||
t.Log(dbutils.FindMySQLPath())
|
||||
}
|
||||
|
||||
func TestStartLocalMySQL(t *testing.T) {
|
||||
dbutils.StartLocalMySQL()
|
||||
}
|
||||
Reference in New Issue
Block a user