Compare commits

39 Commits

Author SHA1 Message Date
lzh
3fb8cd4dad fix cron init 2025-08-13 13:12:23 +08:00
akrike
274a4d93df add kill signal log print 2025-08-11 20:58:39 +08:00
akrike
c383e1737f add start title 2025-08-11 20:48:00 +08:00
akrike
2a40d560d4 1 2025-08-11 20:43:55 +08:00
akrike
d968ce2a3e 1 2025-07-27 21:37:47 +08:00
akrike
51ff529a88 1 2025-07-27 21:28:28 +08:00
akrike
ed237e1ddb fix time sort 2025-07-27 20:59:09 +08:00
akrike
f980e79e02 search params support 2025-07-27 01:04:23 +08:00
akrike
cb15544ea3 use auto ceate index 2025-07-26 21:25:10 +08:00
akrike
c69056d1b7 use auto ceate index 2025-07-26 21:24:52 +08:00
lzh
39b199eac6 remove req struct 2025-07-25 10:25:42 +08:00
lzh
4343dfaa31 update to jwt v5 2025-07-16 16:55:52 +08:00
akrike
0e09afe962 add login resp code 2025-07-14 23:01:02 +08:00
lzh
5356eb4199 1 2025-07-14 13:38:45 +08:00
lzh
b4c7ab9f2c add build params 2025-07-11 15:42:00 +08:00
lzh
b307808b70 config bleve 2025-07-10 16:23:35 +08:00
lzh
6aac70de9a update gin handle 2025-07-10 11:02:19 +08:00
lzh
b598b590e7 edit task logic 2025-07-09 16:58:08 +08:00
lzh
c3262afe13 add build tag support 2025-07-09 15:23:34 +08:00
lzh
9823ca0cef optimal storge choice 2025-07-09 14:51:22 +08:00
lzh
b4ad29e9cf edit default storge type 2025-07-09 11:56:26 +08:00
lzh
dca76fd5d3 support sqlite es highlight 2025-07-09 11:55:09 +08:00
lzh
ba36662759 support bleve highlight 2025-07-09 11:41:15 +08:00
lzh
3f5deab95a add bleve support 2025-07-09 10:49:27 +08:00
lzh
a9657d35b8 1 2025-07-07 15:32:44 +08:00
lzh
0b4133e510 1 2025-07-07 09:22:08 +08:00
lzh
c54e81321e support shell cmd split 2025-07-07 09:20:15 +08:00
lzh
a1ab697848 rename 2025-07-07 09:15:36 +08:00
akrike
c154abe568 1 2025-07-07 00:54:02 +08:00
akrike
39dc4f13e4 update 2025-07-06 22:12:39 +08:00
akrike
8aa700407c 1 2025-07-06 20:46:27 +08:00
akrike
726c5f861d 1 2025-07-06 19:59:06 +08:00
akrike
0db3e60460 More Standardization 2025-07-06 19:52:00 +08:00
akrike
ec9f1fefdb 1 2025-07-06 16:08:41 +08:00
akrike
a05d9c3f03 1 2025-07-05 23:35:41 +08:00
akrike
cf184745ed 1 2025-07-05 23:14:44 +08:00
akrike
be73582e28 1 2025-07-05 12:47:40 +08:00
akrike
0fae7cac25 Standardization 2025-07-05 11:50:41 +08:00
lzh
efcf8d9ca6 update 2025-07-04 17:01:34 +08:00
50 changed files with 1219 additions and 876 deletions

3
.gitignore vendored
View File

@@ -5,4 +5,5 @@ data.db
config.yaml
ltest
gtest
mtest
mtest
log.bleve

View File

@@ -25,7 +25,6 @@ func init() {
initArgs()
initLogHandle()
initLog()
initEs()
initLogHanler()
initWaitCond()
initProcess()
@@ -82,10 +81,6 @@ func initLog() {
logger.InitLog()
}
func initEs() {
logic.EsLogic.InitEs()
}
func initProcess() {
logic.ProcessCtlLogic.ProcessInit()
}
@@ -128,6 +123,7 @@ func initListenKillSignal() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
logger.Logger.Info("进程正在退出,等待全部进程停止")
logic.ProcessCtlLogic.KillAllProcess()
log.Print("已停止所有进程")
os.Exit(0)

View File

@@ -1,4 +1,5 @@
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build -ldflags="-s -w" -o go_process_manager cmd/go_process_manager/main.go
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
@REM go build -ldflags="-s -w" -tags="slim" -o go_process_manager -trimpath cmd/go_process_manager/main.go
go build -ldflags="-s -w" -o go_process_manager -trimpath cmd/go_process_manager/main.go

5
build.sh Normal file
View File

@@ -0,0 +1,5 @@
export CGO_ENABLED=0
export GOOS=linux
export GOARCH=amd64
# go build -ldflags="-s -w" -tags="slim" -o go_process_manager -trimpath cmd/go_process_manager/main.go
go build -ldflags="-s -w" -o go_process_manager -trimpath cmd/go_process_manager/main.go

View File

@@ -7,7 +7,34 @@ import (
"github.com/gin-gonic/gin"
)
var startTitle = `
----------------------------------------------------------------------------
_____ _____ _____
/\ \ /\ \ /\ \
/::\ \ /::\ \ /::\____\
/::::\ \ /::::\ \ /::::| |
/::::::\ \ /::::::\ \ /:::::| |
/:::/\:::\ \ /:::/\:::\ \ /::::::| |
/:::/ \:::\ \ /:::/__\:::\ \ /:::/|::| |
/:::/ \:::\ \ /::::\ \:::\ \ /:::/ |::| |
/:::/ / \:::\ \ /::::::\ \:::\ \ /:::/ |::|___|______
/:::/ / \:::\ ___\ /:::/\:::\ \:::\____\ /:::/ |::::::::\ \
/:::/____/ ___\:::| |/:::/ \:::\ \:::| |/:::/ |:::::::::\____\
\:::\ \ /\ /:::|____|\::/ \:::\ /:::|____|\::/ / ~~~~~/:::/ /
\:::\ /::\ \::/ / \/_____/\:::\/:::/ / \/____/ /:::/ /
\:::\ \:::\ \/____/ \::::::/ / /:::/ /
\:::\ \:::\____\ \::::/ / /:::/ /
\:::\ /:::/ / \::/____/ /:::/ /
\:::\/:::/ / ~~ /:::/ /
\::::::/ / /:::/ /
\::::/ / /:::/ /
\::/____/ \::/ /
\/____/
----------------------------------------------------------------------------
`
func main() {
print(startTitle)
gin.SetMode(gin.ReleaseMode)
route.Route()
}

View File

@@ -6,7 +6,7 @@ var CF = new(configuration)
type configuration struct {
LogLevel string `default:"debug" describe:"日志等级[debug,info]"`
Listen string `default:":8797" describe:"监听端口"`
EsEnable bool `default:"false" describe:"启用Elasticsearch"`
StorgeType string `default:"sqlite" describe:"存储引擎[sqlite、es、bleve]"`
EsUrl string `default:"" describe:"Elasticsearch url"`
EsIndex string `default:"server_log_v1" describe:"Elasticsearch index"`
EsUsername string `default:"" describe:"Elasticsearch用户名"`

38
go.mod
View File

@@ -6,6 +6,35 @@ require (
github.com/containerd/cgroups/v3 v3.0.4
github.com/gorilla/websocket v1.5.1
github.com/opencontainers/runtime-spec v1.2.0
github.com/vcaesar/gse-bleve v0.40.0
)
require (
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
github.com/blevesearch/geo v0.2.3 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
github.com/go-ego/gse v0.70.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/vcaesar/cedar v0.20.1 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
)
require (
@@ -48,7 +77,7 @@ require (
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/tcell/v2 v2.7.1 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/google/uuid v1.3.0
github.com/google/uuid v1.6.0
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
@@ -86,23 +115,24 @@ require (
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/blevesearch/bleve/v2 v2.5.2
github.com/creack/pty v1.1.21
github.com/gin-gonic/gin v1.9.1
github.com/glebarez/sqlite v1.11.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang-jwt/jwt/v5 v5.2.3
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/olivere/elastic/v7 v7.0.32
github.com/panjf2000/ants/v2 v2.10.0
github.com/robfig/cron/v3 v3.0.1
github.com/runletapp/go-console v0.0.0-20211204140000-27323a28410a
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/timandy/routine v1.1.4
go.uber.org/zap v1.26.0
golang.org/x/net v0.31.0 // indirect
gorm.io/gen v0.3.27

86
go.sum
View File

@@ -1,5 +1,46 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blevesearch/bleve/v2 v2.5.2 h1:Ab0r0MODV2C5A6BEL87GqLBySqp/s9xFgceCju6BQk8=
github.com/blevesearch/bleve/v2 v2.5.2/go.mod h1:5Dj6dUQxZM6aqYT3eutTD/GpWKGFSsV8f7LDidFbwXo=
github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y=
github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/geo v0.2.3 h1:K9/vbGI9ehlXdxjxDRJtoAMt7zGAsMIzc6n8zWcwnhg=
github.com/blevesearch/geo v0.2.3/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8=
github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U=
github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 h1:Yqk0XD1mE0fDZAJXTjawJ8If/85JxnLd8v5vG/jWE/s=
github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A=
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs=
github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE=
github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks=
github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0=
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww=
github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
@@ -45,6 +86,8 @@ github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9g
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-ego/gse v0.70.2 h1:y2UMOHJMtI+0b2GjxTtQfKON5DMmlyX1hOQHTo8UVVs=
github.com/go-ego/gse v0.70.2/go.mod h1:kesekpZfcFQ/kwd9b27VZHUOH5dQUjaaQUZ4OGt4Hj4=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -66,19 +109,27 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/iamacarpet/go-winpty v1.0.2 h1:jwPVTYrjAHZx6Mcm6K5i9G4opMp5TblEHH5EQCl/Gzw=
@@ -139,6 +190,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
@@ -181,10 +234,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/timandy/routine v1.1.4 h1:L9eAli/ROJcW6LhmwZcusYQcdAqxAXGOQhEXLQSNWOA=
github.com/timandy/routine v1.1.4/go.mod h1:siBcl8iIsGmhLCajRGRcy7Y7FVcicNXkr97JODdt9fc=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
@@ -193,9 +245,17 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vcaesar/cedar v0.20.1 h1:cDOmYWdprO7ZW8cngJrDi8Zivnscj9dA/y8Y+2SB1P0=
github.com/vcaesar/cedar v0.20.1/go.mod h1:iMDweyuW76RvSrCkQeZeQk4iCbshiPzcCvcGCtpM7iI=
github.com/vcaesar/gse-bleve v0.40.0 h1:Qnv/9v8uyqNkFHxdK4VLN4EwZ0WLxudSgBcUzd2uxQs=
github.com/vcaesar/gse-bleve v0.40.0/go.mod h1:ORZ+jIAEIQeC6PEai24UJr8wisHAWrSukKXTPVVPXzc=
github.com/vcaesar/tt v0.20.0 h1:9t2Ycb9RNHcP0WgQgIaRKJBB+FrRdejuaL6uWIHuoBA=
github.com/vcaesar/tt v0.20.0/go.mod h1:GHPxQYhn+7OgKakRusH7KJ0M5MhywoeLb8Fcffs/Gtg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
@@ -237,8 +297,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -259,12 +319,16 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4=

View File

@@ -1,42 +1,14 @@
package api
import (
"net/http"
"reflect"
"strconv"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/repository"
"github.com/lzh-1625/go_process_manager/log"
"github.com/gin-gonic/gin"
)
func rOk(ctx *gin.Context, message string, data any) {
jsonData := map[string]any{
"code": 0,
"msg": message,
}
if data != nil {
jsonData["data"] = data
}
ctx.JSON(http.StatusOK, jsonData)
}
func errCheck(ctx *gin.Context, isErr bool, errData any) {
if !isErr {
return
}
if err, ok := errData.(error); ok {
log.Logger.Warn(errData)
ctx.Set(constants.CTXFLG_ERR, err.Error())
}
if err, ok := errData.(string); ok {
ctx.Set(constants.CTXFLG_ERR, err)
}
panic(0)
}
func getRole(ctx *gin.Context) constants.Role {
if v, ok := ctx.Get(constants.CTXFLG_ROLE); ok {
return v.(constants.Role)
@@ -56,20 +28,37 @@ func hasOprPermission(ctx *gin.Context, uuid int, op constants.OprPermission) bo
return isAdmin(ctx) || reflect.ValueOf(repository.PermissionRepository.GetPermission(getUserName(ctx), uuid)).FieldByName(string(op)).Bool()
}
func getQueryInt(ctx *gin.Context, query string) (i int) {
i, err := strconv.Atoi(ctx.Query(query))
errCheck(ctx, err != nil, "Invalid parameters!")
return
type Response struct {
StatusCode int
Code int
Data any
Msg string
}
func getQueryString(ctx *gin.Context, query string) (s string) {
s = ctx.Query(query)
errCheck(ctx, s == "", "Invalid parameters!")
return
func NewResponse() *Response {
return &Response{StatusCode: 200}
}
func bind[T any](ctx *gin.Context) T {
var data T
errCheck(ctx, ctx.ShouldBind(&data) != nil, "Invalid parameters!")
return data
func (r *Response) SetStatusCode(code int) *Response {
r.StatusCode = code
return r
}
func (r *Response) SetDate(data any) *Response {
r.Data = data
return r
}
func (r *Response) SetCode(code int) *Response {
r.Code = code
return r
}
func (r *Response) SetMessage(msg any) *Response {
if str, ok := msg.(string); ok {
r.Msg = str
} else if err, ok := msg.(error); ok {
r.Msg = err.Error()
}
return r
}

View File

@@ -2,6 +2,7 @@ package api
import (
"github.com/lzh-1625/go_process_manager/internal/app/logic"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/gin-gonic/gin"
)
@@ -10,18 +11,19 @@ type configApi struct{}
var ConfigApi = new(configApi)
func (c *configApi) GetSystemConfiguration(ctx *gin.Context) {
result := logic.ConfigLogic.GetSystemConfiguration()
rOk(ctx, "Operation successful!", result)
func (c *configApi) GetSystemConfiguration(ctx *gin.Context, _ any) []model.SystemConfigurationVo {
return logic.ConfigLogic.GetSystemConfiguration()
}
func (c *configApi) SetSystemConfiguration(ctx *gin.Context) {
req := bind[map[string]string](ctx)
errCheck(ctx, logic.ConfigLogic.SetSystemConfiguration(req) != nil, "Set config fail!")
rOk(ctx, "Operation successful!", nil)
func (c *configApi) SetSystemConfiguration(ctx *gin.Context, _ any) (err error) {
req := map[string]string{}
if err = ctx.BindJSON(&req); err != nil {
return err
}
err = logic.ConfigLogic.SetSystemConfiguration(req)
return
}
func (c *configApi) EsConfigReload(ctx *gin.Context) {
errCheck(ctx, !logic.EsLogic.InitEs(), "Incorrect username or password!")
rOk(ctx, "Operation successful!", nil)
func (c *configApi) LogConfigReload(ctx *gin.Context, _ any) (err error) {
return logic.LogLogicImpl.Init()
}

View File

@@ -2,6 +2,7 @@ package api
import (
"github.com/lzh-1625/go_process_manager/internal/app/logic"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/gin-gonic/gin"
)
@@ -10,24 +11,25 @@ type file struct{}
var FileApi = new(file)
func (f *file) FilePathHandler(ctx *gin.Context) {
path := getQueryString(ctx, "path")
rOk(ctx, "Operation successful!", logic.FileLogic.GetFileAndDirByPath(path))
func (f *file) FilePathHandler(ctx *gin.Context, req model.FilePathHandlerReq) []model.FileStruct {
return logic.FileLogic.GetFileAndDirByPath(req.Path)
}
func (f *file) FileWriteHandler(ctx *gin.Context) {
func (f *file) FileWriteHandler(ctx *gin.Context, _ any) (err error) {
path := ctx.PostForm("filePath")
fi, err := ctx.FormFile("data")
errCheck(ctx, err != nil, "Read file data failed!")
if err != nil {
return err
}
fiReader, _ := fi.Open()
err = logic.FileLogic.UpdateFileData(path, fiReader, fi.Size)
errCheck(ctx, err != nil, "Update file data operation failed!")
rOk(ctx, "Operation successful!", nil)
return
}
func (f *file) FileReadHandler(ctx *gin.Context) {
path := getQueryString(ctx, "filePath")
bytes, err := logic.FileLogic.ReadFileFromPath(path)
errCheck(ctx, err != nil, "Operation failed!")
rOk(ctx, "Operation successful!", string(bytes))
func (f *file) FileReadHandler(ctx *gin.Context, req model.FileReadHandlerReq) any {
bytes, err := logic.FileLogic.ReadFileFromPath(req.FilePath)
if err != nil {
return err
}
return string(bytes)
}

View File

@@ -1,6 +1,7 @@
package api
import (
"errors"
"slices"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
@@ -15,10 +16,9 @@ type logApi struct{}
var LogApi = new(logApi)
func (a *logApi) GetLog(ctx *gin.Context) {
req := bind[model.GetLogReq](ctx)
func (a *logApi) GetLog(ctx *gin.Context, req model.GetLogReq) any {
if isAdmin(ctx) {
rOk(ctx, "Query successful!", logic.LogLogicImpl.Search(req, req.FilterName...))
return logic.LogLogicImpl.Search(req, req.FilterName...)
} else {
processNameList := repository.PermissionRepository.GetProcessNameByPermission(getUserName(ctx), constants.OPERATION_LOG)
filterName := slices.DeleteFunc(req.FilterName, func(s string) bool {
@@ -27,11 +27,13 @@ func (a *logApi) GetLog(ctx *gin.Context) {
if len(filterName) == 0 {
filterName = processNameList
}
errCheck(ctx, len(filterName) == 0, "No information found!")
rOk(ctx, "Query successful!", logic.LogLogicImpl.Search(req, filterName...))
if len(filterName) == 0 {
return errors.New("no information found")
}
return logic.LogLogicImpl.Search(req, filterName...)
}
}
func (a *logApi) GetRunningLog(ctx *gin.Context) {
rOk(ctx, "Query successful!", logic.Loghandler.GetRunning())
func (a *logApi) GetRunningLog(ctx *gin.Context, _ any) int {
return logic.Loghandler.GetRunning()
}

View File

@@ -11,14 +11,12 @@ var PermissionApi = new(permissionApi)
type permissionApi struct{}
func (p *permissionApi) EditPermssion(ctx *gin.Context) {
req := bind[model.Permission](ctx)
err := repository.PermissionRepository.EditPermssion(req)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (p *permissionApi) EditPermssion(ctx *gin.Context, req model.Permission) (err error) {
return repository.PermissionRepository.EditPermssion(req)
}
func (p *permissionApi) GetPermissionList(ctx *gin.Context) {
result := repository.PermissionRepository.GetPermssionList(getQueryString(ctx, "account"))
rOk(ctx, "Query successful!", result)
func (p *permissionApi) GetPermissionList(ctx *gin.Context, req struct {
Account string `form:"account" binding:"required"`
}) any {
return repository.PermissionRepository.GetPermssionList(req.Account)
}

View File

@@ -1,6 +1,7 @@
package api
import (
"errors"
"time"
"github.com/google/uuid"
@@ -16,114 +17,126 @@ type procApi struct{}
var ProcApi = new(procApi)
func (p *procApi) CreateNewProcess(ctx *gin.Context) {
req := bind[model.Process](ctx)
func (p *procApi) CreateNewProcess(ctx *gin.Context, req model.Process) any {
index, err := repository.ProcessRepository.AddProcessConfig(req)
errCheck(ctx, err != nil, err)
if err != nil {
return err
}
req.Uuid = index
proc, err := logic.ProcessCtlLogic.NewProcess(req)
errCheck(ctx, err != nil, err)
logic.ProcessCtlLogic.AddProcess(req.Uuid, proc)
rOk(ctx, "Operation successful!", gin.H{
"id": req.Uuid,
})
}
func (p *procApi) DeleteNewProcess(ctx *gin.Context) {
uuid := getQueryInt(ctx, "uuid")
logic.ProcessCtlLogic.KillProcess(uuid)
logic.ProcessCtlLogic.DeleteProcess(uuid)
err := repository.ProcessRepository.DeleteProcessConfig(uuid)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
}
func (p *procApi) KillProcess(ctx *gin.Context) {
uuid := getQueryInt(ctx, "uuid")
err := logic.ProcessCtlLogic.KillProcess(uuid)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
}
func (p *procApi) StartProcess(ctx *gin.Context) {
uuid := getQueryInt(ctx, "uuid")
prod, err := logic.ProcessCtlLogic.GetProcess(uuid)
if err != nil { // 进程不存在则创建
proc, err := logic.ProcessCtlLogic.RunNewProcess(repository.ProcessRepository.GetProcessConfigById(uuid))
errCheck(ctx, err != nil, err)
logic.ProcessCtlLogic.AddProcess(uuid, proc)
rOk(ctx, "Operation successful!", nil)
return
if err != nil {
return err
}
logic.ProcessCtlLogic.AddProcess(req.Uuid, proc)
return gin.H{
"id": req.Uuid,
}
}
func (p *procApi) DeleteNewProcess(ctx *gin.Context, req struct {
Uuid int `form:"uuid" binding:"required"`
}) (err error) {
logic.ProcessCtlLogic.KillProcess(req.Uuid)
logic.ProcessCtlLogic.DeleteProcess(req.Uuid)
return repository.ProcessRepository.DeleteProcessConfig(req.Uuid)
}
func (p *procApi) KillProcess(ctx *gin.Context, req struct {
Uuid int `form:"uuid" binding:"required"`
}) (err error) {
return logic.ProcessCtlLogic.KillProcess(req.Uuid)
}
func (p *procApi) StartProcess(ctx *gin.Context, req struct {
Uuid int `form:"uuid" binding:"required"`
}) (err error) {
prod, err := logic.ProcessCtlLogic.GetProcess(req.Uuid)
if err != nil { // 进程不存在则创建
proConfig, err := repository.ProcessRepository.GetProcessConfigById(req.Uuid)
if err != nil {
return err
}
proc, err := logic.ProcessCtlLogic.RunNewProcess(proConfig)
if err != nil {
return err
}
logic.ProcessCtlLogic.AddProcess(req.Uuid, proc)
return nil
}
if prod.State.State == 1 {
return errors.New("process is currently running")
}
errCheck(ctx, prod.State.State == 1, "The process is currently running.")
prod.ResetRestartTimes()
err = prod.Start()
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
return
}
func (p *procApi) StartAllProcess(ctx *gin.Context) {
func (p *procApi) StartAllProcess(ctx *gin.Context, _ any) (err error) {
if isAdmin(ctx) {
logic.ProcessCtlLogic.ProcessStartAll()
} else {
logic.ProcessCtlLogic.ProcesStartAllByUsername(getUserName(ctx))
}
rOk(ctx, "Operation successful!", nil)
return
}
func (p *procApi) KillAllProcess(ctx *gin.Context) {
func (p *procApi) KillAllProcess(ctx *gin.Context, _ any) (err error) {
if isAdmin(ctx) {
logic.ProcessCtlLogic.KillAllProcess()
} else {
logic.ProcessCtlLogic.KillAllProcessByUserName(getUserName(ctx))
}
rOk(ctx, "Operation successful!", nil)
return
}
func (p *procApi) GetProcessList(ctx *gin.Context) {
func (p *procApi) GetProcessList(ctx *gin.Context, _ any) any {
if isAdmin(ctx) {
rOk(ctx, "Query successful!", logic.ProcessCtlLogic.GetProcessList())
return logic.ProcessCtlLogic.GetProcessList()
} else {
rOk(ctx, "Query successful!", logic.ProcessCtlLogic.GetProcessListByUser(getUserName(ctx)))
return logic.ProcessCtlLogic.GetProcessListByUser(getUserName(ctx))
}
}
func (p *procApi) UpdateProcessConfig(ctx *gin.Context) {
req := bind[model.Process](ctx)
func (p *procApi) UpdateProcessConfig(ctx *gin.Context, req model.Process) (err error) {
logic.ProcessCtlLogic.UpdateProcessConfig(req)
err := repository.ProcessRepository.UpdateProcessConfig(req)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
err = repository.ProcessRepository.UpdateProcessConfig(req)
return
}
func (p *procApi) GetProcessConfig(ctx *gin.Context) {
uuid := getQueryInt(ctx, "uuid")
data := repository.ProcessRepository.GetProcessConfigById(uuid)
errCheck(ctx, data.Uuid == 0, "No information found!")
rOk(ctx, "Query successful!", data)
func (p *procApi) GetProcessConfig(ctx *gin.Context, req struct {
Uuid int `form:"uuid" binding:"required"`
}) any {
data, err := repository.ProcessRepository.GetProcessConfigById(req.Uuid)
if err != nil {
return err
}
return data
}
func (p *procApi) ProcessControl(ctx *gin.Context) {
func (p *procApi) ProcessControl(ctx *gin.Context, req struct {
Uuid int `form:"uuid" binding:"required"`
}) (err error) {
user := getUserName(ctx)
uuid := getQueryInt(ctx, "uuid")
proc, err := logic.ProcessCtlLogic.GetProcess(uuid)
errCheck(ctx, err != nil, err)
proc, err := logic.ProcessCtlLogic.GetProcess(req.Uuid)
if err != nil {
return err
}
proc.ProcessControl(user)
rOk(ctx, "Operation successful!", nil)
return
}
func (p *procApi) ProcessCreateShare(ctx *gin.Context) {
req := bind[model.ProcessShare](ctx)
func (p *procApi) ProcessCreateShare(ctx *gin.Context, req model.ProcessShare) any {
token := utils.UnwarpIgnore(uuid.NewRandom()).String()
err := repository.WsShare.AddShareData(model.WsShare{
if err := repository.WsShare.AddShareData(model.WsShare{
ExpireTime: time.Now().Add(time.Minute * time.Duration(req.Minutes)),
Write: req.Write,
Token: token,
Pid: req.Pid,
CreateBy: getUserName(ctx),
})
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", gin.H{
}); err != nil {
return err
}
return gin.H{
"token": token,
})
}
}

View File

@@ -11,32 +11,26 @@ type pushApi struct{}
var PushApi = new(pushApi)
func (p *pushApi) GetPushList(ctx *gin.Context) {
rOk(ctx, "Query successful!", repository.PushRepository.GetPushList())
func (p *pushApi) GetPushList(ctx *gin.Context, __ any) any {
return repository.PushRepository.GetPushList()
}
func (p *pushApi) GetPushById(ctx *gin.Context) {
id := getQueryInt(ctx, "id")
rOk(ctx, "Query successful!", repository.PushRepository.GetPushConfigById(id))
func (p *pushApi) GetPushById(ctx *gin.Context, req struct {
Id int `form:"id" binding:"required"`
}) any {
return repository.PushRepository.GetPushConfigById(req.Id)
}
func (p *pushApi) AddPushConfig(ctx *gin.Context) {
req := bind[model.Push](ctx)
err := repository.PushRepository.AddPushConfig(req)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (p *pushApi) AddPushConfig(ctx *gin.Context, req model.Push) (err error) {
return repository.PushRepository.AddPushConfig(req)
}
func (p *pushApi) UpdatePushConfig(ctx *gin.Context) {
req := bind[model.Push](ctx)
err := repository.PushRepository.UpdatePushConfig(req)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (p *pushApi) UpdatePushConfig(ctx *gin.Context, req model.Push) (err error) {
return repository.PushRepository.UpdatePushConfig(req)
}
func (p *pushApi) DeletePushConfig(ctx *gin.Context) {
id := getQueryInt(ctx, "id")
err := repository.PushRepository.DeletePushConfig(id)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (p *pushApi) DeletePushConfig(ctx *gin.Context, req struct {
Id int `form:"id" binding:"required"`
}) (err error) {
return repository.PushRepository.DeletePushConfig(req.Id)
}

View File

@@ -12,62 +12,58 @@ type taskApi struct{}
var TaskApi = new(taskApi)
func (t *taskApi) CreateTask(ctx *gin.Context) {
req := bind[model.Task](ctx)
err := logic.TaskLogic.CreateTask(req)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (t *taskApi) CreateTask(ctx *gin.Context, req model.Task) (err error) {
return logic.TaskLogic.CreateTask(req)
}
func (t *taskApi) GetTaskById(ctx *gin.Context) {
result, err := repository.TaskRepository.GetTaskById(getQueryInt(ctx, "id"))
errCheck(ctx, err != nil, "Query failed!")
rOk(ctx, "Operation successful!", result)
func (t *taskApi) GetTaskById(ctx *gin.Context, req struct {
Id int `form:"id" binding:"required"`
}) any {
result, err := repository.TaskRepository.GetTaskById(req.Id)
if err != nil {
return err
}
return result
}
func (t *taskApi) GetTaskList(ctx *gin.Context) {
result := logic.TaskLogic.GetAllTaskJob()
rOk(ctx, "Operation successful!", result)
func (t *taskApi) GetTaskList(ctx *gin.Context, _ any) any {
return logic.TaskLogic.GetAllTaskJob()
}
func (t *taskApi) DeleteTaskById(ctx *gin.Context) {
err := logic.TaskLogic.DeleteTask(getQueryInt(ctx, "id"))
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (t *taskApi) DeleteTaskById(ctx *gin.Context, req struct {
Id int `form:"id" binding:"required"`
}) (err error) {
return logic.TaskLogic.DeleteTask(req.Id)
}
func (t *taskApi) StartTask(ctx *gin.Context) {
go logic.TaskLogic.RunTaskById(getQueryInt(ctx, "id"))
rOk(ctx, "Operation successful!", nil)
func (t *taskApi) StartTask(ctx *gin.Context, req struct {
Id int `form:"id" binding:"required"`
}) (err error) {
go logic.TaskLogic.RunTaskById(req.Id)
return
}
func (t *taskApi) StopTask(ctx *gin.Context) {
errCheck(ctx, logic.TaskLogic.StopTaskJob(getQueryInt(ctx, "id")) != nil, "Operation failed!")
rOk(ctx, "Operation successful!", nil)
func (t *taskApi) StopTask(ctx *gin.Context, req struct {
Id int `form:"id" binding:"required"`
}) (err error) {
return logic.TaskLogic.StopTaskJob(req.Id)
}
func (t *taskApi) EditTask(ctx *gin.Context) {
req := bind[model.Task](ctx)
err := logic.TaskLogic.EditTask(req)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (t *taskApi) EditTask(ctx *gin.Context, req model.Task) (err error) {
return logic.TaskLogic.EditTask(req)
}
func (t *taskApi) EditTaskEnable(ctx *gin.Context) {
req := bind[model.Task](ctx)
err := logic.TaskLogic.EditTaskEnable(req.Id, req.Enable)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (t *taskApi) EditTaskEnable(ctx *gin.Context, req model.Task) (err error) {
return logic.TaskLogic.EditTaskEnable(req.Id, req.Enable)
}
func (t *taskApi) RunTaskByKey(ctx *gin.Context) {
err := logic.TaskLogic.RunTaskByKey(ctx.Param("key"))
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (t *taskApi) RunTaskByKey(ctx *gin.Context, _ any) (err error) {
return logic.TaskLogic.RunTaskByKey(ctx.Param("key"))
}
func (t *taskApi) CreateTaskApiKey(ctx *gin.Context) {
err := logic.TaskLogic.CreateApiKey(getQueryInt(ctx, "id"))
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (t *taskApi) CreateTaskApiKey(ctx *gin.Context, req struct {
Id int `form:"id" binding:"required"`
}) (err error) {
return logic.TaskLogic.CreateApiKey(req.Id)
}

View File

@@ -1,6 +1,8 @@
package api
import (
"errors"
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/model"
@@ -16,57 +18,64 @@ var UserApi = new(userApi)
const DEFAULT_ROOT_PASSWORD = "root"
func (u *userApi) LoginHandler(ctx *gin.Context) {
req := bind[map[string]string](ctx)
account := req["account"]
password := req["password"]
errCheck(ctx, !u.checkLoginInfo(account, password), "Incorrect username or password!")
token, err := utils.GenToken(account)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", gin.H{
func (u *userApi) LoginHandler(ctx *gin.Context, req model.LoginHandlerReq) any {
if !u.checkLoginInfo(req.Account, req.Password) {
return errors.New("incorrect username or password")
}
token, err := utils.GenerateToken(req.Account)
if err != nil {
return err
}
return gin.H{
"code": 0,
"token": token,
"username": account,
"role": repository.UserRepository.GetUserByName(account).Role,
})
"username": req.Account,
"role": repository.UserRepository.GetUserByName(req.Account).Role,
}
}
func (u *userApi) CreateUser(ctx *gin.Context) {
req := bind[model.User](ctx)
errCheck(ctx, req.Role == constants.ROLE_ROOT, "Creation of root accounts is forbidden!")
errCheck(ctx, req.Account == constants.CONSOLE, "Operation failed!")
errCheck(ctx, len(req.Password) < config.CF.UserPassWordMinLength, "Password is too short")
err := repository.UserRepository.CreateUser(req)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func (u *userApi) CreateUser(ctx *gin.Context, req model.User) (err error) {
if req.Role == constants.ROLE_ROOT {
return errors.New("creation of root accounts is forbidden")
}
if req.Account == constants.CONSOLE {
return errors.New("operation failed")
}
if len(req.Password) < config.CF.UserPassWordMinLength {
return errors.New("password is too short")
}
err = repository.UserRepository.CreateUser(req)
return
}
func (u *userApi) ChangePassword(ctx *gin.Context) {
req := bind[model.User](ctx)
func (u *userApi) ChangePassword(ctx *gin.Context, req model.User) (err error) {
reqUser := getUserName(ctx)
errCheck(ctx, getRole(ctx) != constants.ROLE_ROOT && req.Account != "", "Invalid parameters!")
if getRole(ctx) != constants.ROLE_ROOT && req.Account != "" {
return errors.New("invalid parameters")
}
var userName string
if req.Account != "" {
userName = req.Account
} else {
userName = reqUser
}
errCheck(ctx, len(req.Password) < config.CF.UserPassWordMinLength, "Password is too short")
err := repository.UserRepository.UpdatePassword(userName, req.Password)
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
if len(req.Password) < config.CF.UserPassWordMinLength {
return errors.New("password is too short")
}
err = repository.UserRepository.UpdatePassword(userName, req.Password)
return
}
func (u *userApi) DeleteUser(ctx *gin.Context) {
account := getQueryString(ctx, "account")
errCheck(ctx, account == "root", "Deletion of root accounts is forbidden!")
err := repository.UserRepository.DeleteUser(account)
errCheck(ctx, err != nil, "Deletion of root accounts failed!")
rOk(ctx, "Operation successful!", nil)
func (u *userApi) DeleteUser(ctx *gin.Context, req model.User) (err error) {
if req.Account == "root" {
return errors.New("deletion of root accounts is forbidden")
}
err = repository.UserRepository.DeleteUser(req.Account)
return
}
func (u *userApi) GetUserList(ctx *gin.Context) {
rOk(ctx, "Query successful!", repository.UserRepository.GetUserList())
func (u *userApi) GetUserList(ctx *gin.Context, _ any) any {
return repository.UserRepository.GetUserList()
}
func (u *userApi) checkLoginInfo(account, password string) bool {

View File

@@ -2,6 +2,7 @@ package api
import (
"context"
"errors"
"strconv"
"sync"
"time"
@@ -9,9 +10,9 @@ import (
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/logic"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/internal/app/repository"
"github.com/lzh-1625/go_process_manager/log"
"github.com/lzh-1625/go_process_manager/utils"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
@@ -47,19 +48,22 @@ var upgrader = websocket.Upgrader{
WriteBufferSize: 1024,
}
func (w *wsApi) WebsocketHandle(ctx *gin.Context) {
func (w *wsApi) WebsocketHandle(ctx *gin.Context, req model.WebsocketHandleReq) (err error) {
reqUser := getUserName(ctx)
uuid := getQueryInt(ctx, "uuid")
proc, err := logic.ProcessCtlLogic.GetProcess(uuid)
errCheck(ctx, err != nil, "Operation failed!")
errCheck(ctx, proc.HasWsConn(reqUser), "A connection already exists; unable to establish a new one!")
errCheck(ctx, proc.Control.Controller != reqUser && !proc.VerifyControl(), "Insufficient permissions; please check your access rights!")
proc, err := logic.ProcessCtlLogic.GetProcess(req.Uuid)
if err != nil {
return err
}
if proc.HasWsConn(reqUser) {
return errors.New("connection already exists")
}
if proc.Control.Controller != reqUser && !proc.VerifyControl() {
return errors.New("insufficient permissions")
}
conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
errCheck(ctx, err != nil, "WebSocket connection upgrade failed!")
log.Logger.AddAdditionalInfo("processName", proc.Name)
log.Logger.AddAdditionalInfo("userName", reqUser)
defer log.Logger.DeleteAdditionalInfo(2)
if err != nil {
return err
}
log.Logger.Infow("ws连接成功")
@@ -71,8 +75,8 @@ func (w *wsApi) WebsocketHandle(ctx *gin.Context) {
}
proc.ReadCache(wci)
if proc.State.State == 1 {
proc.SetTerminalSize(utils.GetIntByString(ctx.Query("cols")), utils.GetIntByString(ctx.Query("rows")))
w.startWsConnect(wci, cancel, proc, hasOprPermission(ctx, uuid, constants.OPERATION_TERMINAL_WRITE))
proc.SetTerminalSize(req.Cols, req.Rows)
w.startWsConnect(wci, cancel, proc, hasOprPermission(ctx, req.Uuid, constants.OPERATION_TERMINAL_WRITE))
proc.AddConn(reqUser, wci)
defer proc.DeleteConn(reqUser)
}
@@ -89,27 +93,41 @@ func (w *wsApi) WebsocketHandle(ctx *gin.Context) {
log.Logger.Infow("ws连接断开", "操作类型", "tcp连接建立已被关闭")
}
conn.Close()
return
}
func (w *wsApi) WebsocketShareHandle(ctx *gin.Context) {
token := getQueryString(ctx, "token")
data, err := repository.WsShare.GetWsShareDataByToken(token)
errCheck(ctx, err != nil, "Operation failed!")
errCheck(ctx, data.ExpireTime.Unix() <= time.Now().Unix(), "Share expired!")
func (w *wsApi) WebsocketShareHandle(ctx *gin.Context, req model.WebsocketHandleReq) (err error) {
data, err := repository.WsShare.GetWsShareDataByToken(req.Token)
if err != nil {
return err
}
if data.ExpireTime.Before(time.Now()) {
return errors.New("share expired")
}
proc, err := logic.ProcessCtlLogic.GetProcess(data.Pid)
errCheck(ctx, err != nil, err)
if err != nil {
return err
}
guestName := "guest-" + strconv.Itoa(int(data.ID)) // 构造访客用户名
errCheck(ctx, proc.HasWsConn(guestName), "A connection already exists; unable to establish a new one!")
errCheck(ctx, proc.State.State != 1, "The process is currently running.")
errCheck(ctx, !proc.VerifyControl(), "Insufficient permissions; please check your access rights!")
if proc.HasWsConn(guestName) {
return errors.New("connection already exists")
}
if proc.State.State != 1 {
return errors.New("process not is running")
}
if !proc.VerifyControl() {
return errors.New("insufficient permissions")
}
conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
errCheck(ctx, err != nil, "WebSocket connection upgrade failed!")
if err != nil {
return err
}
log.Logger.Infow("ws连接成功")
data.UpdatedAt = time.Now()
repository.WsShare.Edit(data)
proc.SetTerminalSize(utils.GetIntByString(ctx.Query("cols")), utils.GetIntByString(ctx.Query("rows")))
proc.SetTerminalSize(req.Cols, req.Rows)
wsCtx, cancel := context.WithCancel(context.Background())
wci := &WsConnetInstance{
WsConnect: conn,
@@ -135,6 +153,7 @@ func (w *wsApi) WebsocketShareHandle(ctx *gin.Context) {
log.Logger.Infow("ws连接断开", "操作类型", "分享时间已结束")
}
conn.Close()
return
}
func (w *wsApi) startWsConnect(wci *WsConnetInstance, cancel context.CancelFunc, proc logic.Process, write bool) {
@@ -178,12 +197,10 @@ func (w *wsApi) startWsConnect(wci *WsConnetInstance, cancel context.CancelFunc,
}
func GetWsShareList(ctx *gin.Context) {
rOk(ctx, "Operation successful!", logic.WsSahreLogic.GetWsShareList())
func GetWsShareList(ctx *gin.Context, _ any) any {
return logic.WsSahreLogic.GetWsShareList()
}
func DeleteWsShareById(ctx *gin.Context) {
err := logic.WsSahreLogic.DeleteById(ctx.GetInt("id"))
errCheck(ctx, err != nil, err)
rOk(ctx, "Operation successful!", nil)
func DeleteWsShareById(ctx *gin.Context, _ any) any {
return logic.WsSahreLogic.DeleteById(ctx.GetInt("id"))
}

View File

@@ -20,7 +20,7 @@ var (
func InitLogHandle() {
Loghandler.antsPool, _ = ants.NewPool(config.CF.LogHandlerPoolSize, ants.WithNonblocking(true), ants.WithExpiryDuration(3*time.Second), ants.WithPanicHandler(func(i interface{}) {
log.Logger.Error("es消息储存失败")
log.Logger.Warnw("日志储存失败", "err", i)
}))
}

View File

@@ -2,55 +2,16 @@ package logic
import (
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/internal/app/repository"
"github.com/lzh-1625/go_process_manager/internal/app/search"
_ "github.com/lzh-1625/go_process_manager/internal/app/search/bleve"
_ "github.com/lzh-1625/go_process_manager/internal/app/search/es"
_ "github.com/lzh-1625/go_process_manager/internal/app/search/sqlite"
)
type LogLogic interface {
Search(req model.GetLogReq, filterProcessName ...string) model.LogResp
Insert(log string, processName string, using string, ts int64)
}
var LogLogicImpl LogLogic
var LogLogicImpl search.LogLogic
func InitLog() {
if config.CF.EsEnable {
LogLogicImpl = LogEs
} else {
LogLogicImpl = LogSqlite
}
}
type logSqlite struct{}
var LogSqlite = new(logSqlite)
func (l *logSqlite) Search(req model.GetLogReq, filterProcessName ...string) model.LogResp {
req.FilterName = filterProcessName
data, total := repository.LogRepository.SearchLog(req)
return model.LogResp{
Data: data,
Total: total,
}
}
func (l *logSqlite) Insert(log string, processName string, using string, ts int64) {
repository.LogRepository.InsertLog(model.ProcessLog{
Log: log,
Name: processName,
Using: using,
Time: ts,
})
}
type logEs struct{}
var LogEs = new(logEs)
func (l *logEs) Search(req model.GetLogReq, filterProcessName ...string) model.LogResp {
return EsLogic.Search(req, filterProcessName...)
}
func (l *logEs) Insert(log string, processName string, using string, ts int64) {
EsLogic.Insert(log, processName, using, ts)
LogLogicImpl = search.GetSearchImpl(config.CF.StorgeType)
LogLogicImpl.Init()
}

View File

@@ -33,7 +33,7 @@ type Process interface {
type ProcessBase struct {
Process
p *os.Process
op *os.Process
Name string
Pid int
StartCommand []string
@@ -84,7 +84,7 @@ type ConnectInstance interface {
}
func (p *ProcessBase) watchDog() {
state, _ := p.p.Wait()
state, _ := p.op.Wait()
if p.cgroup.enable && p.cgroup.delete != nil {
err := p.cgroup.delete()
if err != nil {
@@ -128,7 +128,7 @@ func (p *ProcessBase) pInit() {
p.State.manualStopFlag = false
p.State.startTime = time.Now()
p.ws = make(map[string]ConnectInstance)
p.Pid = p.p.Pid
p.Pid = p.op.Pid
p.doOnInit()
p.InitPerformanceStatus()
p.initPsutil()
@@ -263,8 +263,6 @@ func (p *ProcessBase) monitorHanler() {
if !p.monitor.enable {
return
}
log.Logger.AddAdditionalInfo("name", p.Name)
log.Logger.AddAdditionalInfo("pid", p.Pid)
defer log.Logger.Infow("性能监控结束")
ticker := time.NewTicker(time.Second * time.Duration(config.CF.PerformanceInfoInterval))
defer ticker.Stop()
@@ -308,7 +306,7 @@ func (p *ProcessBase) initPsutil() {
}
func (p *ProcessBase) Kill() error {
p.p.Signal(syscall.SIGINT)
p.op.Signal(syscall.SIGINT)
select {
case <-p.StopChan:
{
@@ -317,7 +315,7 @@ func (p *ProcessBase) Kill() error {
case <-time.After(time.Second * time.Duration(config.CF.KillWaitTime)):
{
log.Logger.Debugw("进程kill超时,强制停止进程", "name", p.Name)
return p.p.Kill()
return p.op.Kill()
}
}
}

View File

@@ -1,3 +1,6 @@
//go:build windows
// +build windows
package logic
import "github.com/lzh-1625/go_process_manager/log"

View File

@@ -47,11 +47,11 @@ func (p *processCtlLogic) KillProcess(uuid int) error {
func (p *processCtlLogic) GetProcess(uuid int) (*ProcessBase, error) {
process, ok := p.processMap.Load(uuid)
if !ok {
return nil, errors.New("进程获取失败")
return nil, errors.New("process not exist")
}
result, ok := process.(*ProcessBase)
if !ok {
return nil, errors.New("进程类型错误")
return nil, errors.New("process type error")
}
return result, nil
@@ -114,25 +114,25 @@ func (p *processCtlLogic) getProcessInfoList(processConfiglist []model.Process)
Name: v.Name,
Uuid: v.Uuid,
}
if value, ok := p.processMap.Load(v.Uuid); ok {
process := value.(*ProcessBase)
pi.State.Info = process.State.Info
pi.State.State = process.State.State
pi.StartTime = process.GetStartTimeFormat()
pi.User = process.GetUserString()
pi.Usage.Cpu = process.performanceStatus.cpu
pi.Usage.Mem = process.performanceStatus.mem
if config.CF.PerformanceCapacityDisplay {
pi.Usage.CpuCapacity = float64(runtime.NumCPU()) * 100.0
pi.Usage.MemCapacity = float64(utils.UnwarpIgnore(mem.VirtualMemory()).Total >> 10)
}
pi.Usage.Time = process.performanceStatus.time
pi.TermType = process.Type()
pi.CgroupEnable = process.Config.cgroupEnable
pi.CpuLimit = process.Config.cpuLimit
pi.MemoryLimit = process.Config.memoryLimit
process, err := p.GetProcess(v.Uuid)
if err != nil {
continue
}
pi.State.Info = process.State.Info
pi.State.State = process.State.State
pi.StartTime = process.GetStartTimeFormat()
pi.User = process.GetUserString()
pi.Usage.Cpu = process.performanceStatus.cpu
pi.Usage.Mem = process.performanceStatus.mem
if config.CF.PerformanceCapacityDisplay {
pi.Usage.CpuCapacity = float64(runtime.NumCPU()) * 100.0
pi.Usage.MemCapacity = float64(utils.UnwarpIgnore(mem.VirtualMemory()).Total >> 10)
}
pi.Usage.Time = process.performanceStatus.time
pi.TermType = process.Type()
pi.CgroupEnable = process.Config.cgroupEnable
pi.CpuLimit = process.Config.cpuLimit
pi.MemoryLimit = process.Config.memoryLimit
processInfoList = append(processInfoList, pi)
}
return processInfoList
@@ -150,7 +150,10 @@ func (p *processCtlLogic) ProcessStartAll() {
}
func (p *processCtlLogic) RunPrcessById(id int) (*ProcessBase, error) {
config := repository.ProcessRepository.GetProcessConfigById(id)
config, err := repository.ProcessRepository.GetProcessConfigById(id)
if err != nil {
return nil, err
}
proc, err := p.RunNewProcess(config)
if err != nil {
log.Logger.Warnw("初始化启动进程失败", config.Name, "name", "err", err)
@@ -163,7 +166,6 @@ func (p *processCtlLogic) RunPrcessById(id int) (*ProcessBase, error) {
func (p *processCtlLogic) ProcessInit() {
config := repository.ProcessRepository.GetAllProcessConfig()
for _, v := range config {
proc, err := p.NewProcess(v)
if err != nil {
log.Logger.Warnw("初始化启动进程失败", v.Name, "name", "err", err)

View File

@@ -6,6 +6,7 @@ import (
"os/exec"
"strings"
"github.com/google/shlex"
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/model"
@@ -31,14 +32,12 @@ func (p *ProcessPty) Type() constants.TerminalType {
func (p *ProcessPty) Start() (err error) {
defer func() {
log.Logger.DeleteAdditionalInfo(1)
if err != nil {
p.Config.AutoRestart = false
p.SetState(constants.PROCESS_WARNNING)
p.State.Info = "进程启动失败:" + err.Error()
}
}()
log.Logger.AddAdditionalInfo("进程名称", p.Name)
if ok := p.SetState(constants.PROCESS_START, func() bool {
return p.State.State != 1
}); !ok {
@@ -58,7 +57,7 @@ func (p *ProcessPty) Start() (err error) {
})
p.pty = pf
log.Logger.Infow("进程启动成功", "进程名称", p.Name, "重启次数", p.State.restartTimes)
p.p = cmd.Process
p.op = cmd.Process
p.pInit()
p.push("进程启动成功")
return nil
@@ -115,7 +114,9 @@ func (p *ProcessPty) readInit() {
}
func (p *ProcessPty) ReadCache(ws ConnectInstance) {
ws.Write(p.cacheBytesBuf.Bytes())
if p.cacheBytesBuf != nil {
ws.Write(p.cacheBytesBuf.Bytes())
}
}
func (p *ProcessPty) bufHanle(b []byte) {
@@ -135,7 +136,7 @@ func (p *ProcessPty) doOnInit() {
func NewProcessPty(pconfig model.Process) *ProcessBase {
p := ProcessBase{
Name: pconfig.Name,
StartCommand: strings.Split(pconfig.Cmd, " "),
StartCommand: utils.UnwarpIgnore(shlex.Split(pconfig.Cmd)),
WorkDir: pconfig.Cwd,
}
processPty := ProcessPty{

View File

@@ -30,14 +30,12 @@ func (p *ProcessPty) Type() constants.TerminalType {
func (p *ProcessPty) Start() (err error) {
defer func() {
log.Logger.DeleteAdditionalInfo(1)
if err != nil {
p.Config.AutoRestart = false
p.SetState(constants.PROCESS_WARNNING)
p.State.Info = "进程启动失败:" + err.Error()
}
}()
log.Logger.AddAdditionalInfo("进程名称", p.Name)
if ok := p.SetState(constants.PROCESS_START, func() bool {
return p.State.State != 1
}); !ok {
@@ -61,7 +59,7 @@ func (p *ProcessPty) Start() (err error) {
log.Logger.Errorw("进程启动失败", "err", err)
return err
}
p.p, err = os.FindProcess(pid)
p.op, err = os.FindProcess(pid)
if err != nil {
log.Logger.Errorw("进程启动失败", "err", err)
return err

View File

@@ -4,7 +4,8 @@ import (
"bufio"
"io"
"os/exec"
"strings"
"github.com/google/shlex"
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
@@ -38,14 +39,12 @@ func (p *ProcessStd) Write(input string) (err error) {
func (p *ProcessStd) Start() (err error) {
defer func() {
log.Logger.DeleteAdditionalInfo(1)
if err != nil {
p.Config.AutoRestart = false
p.SetState(constants.PROCESS_WARNNING)
p.State.Info = "进程启动失败:" + err.Error()
}
}()
log.Logger.AddAdditionalInfo("进程名称", p.Name)
if ok := p.SetState(constants.PROCESS_START, func() bool {
return p.State.State != 1
}); !ok {
@@ -72,7 +71,7 @@ func (p *ProcessStd) Start() (err error) {
return err
}
log.Logger.Infow("进程启动成功", "重启次数", p.State.restartTimes)
p.p = cmd.Process
p.op = cmd.Process
p.pInit()
p.push("进程启动成功")
return nil
@@ -135,7 +134,7 @@ func (p *ProcessStd) Read() string {
func NewProcessStd(pconfig model.Process) *ProcessBase {
p := ProcessBase{
Name: pconfig.Name,
StartCommand: strings.Split(pconfig.Cmd, " "),
StartCommand: utils.UnwarpIgnore(shlex.Split(pconfig.Cmd)),
WorkDir: pconfig.Cwd,
}
processStd := ProcessStd{

View File

@@ -2,57 +2,62 @@ package logic
import (
"context"
"errors"
"time"
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/middle"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/log"
"github.com/robfig/cron/v3"
)
func (t *taskLogic) RunTaskById(id int) error {
task, err := t.getTaskJob(id)
if err != nil {
return errors.New("id不存在")
}
if task.Running {
return errors.New("task is running")
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
task.Cancel = cancel
t.run(ctx, task)
return nil
type TaskJob struct {
Cron *cron.Cron `json:"-"`
TaskConfig *model.Task `json:"task"`
Running bool `json:"running"`
Cancel context.CancelFunc `json:"-"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
}
func (t *taskLogic) run(ctx context.Context, data *model.TaskJob) {
data.Running = true
func NewTaskJob(data model.Task) (*TaskJob, error) {
tj := &TaskJob{
TaskConfig: &data,
StartTime: time.Now(),
}
if data.Enable && data.CronExpression != "" {
err := tj.InitCronHandle()
if err != nil {
log.Logger.Warnw("定时任务启动失败", "err", err, "task", data.Id)
}
}
return tj, nil
}
func (t *TaskJob) Run(ctx context.Context) {
t.Running = true
middle.TaskWaitCond.Trigger()
defer func() {
data.Running = false
t.Running = false
middle.TaskWaitCond.Trigger()
}()
log.Logger.AddAdditionalInfo("taskId", data.Task.Id)
defer log.Logger.DeleteAdditionalInfo(1)
var ok bool
// 判断条件是否满足
if data.Task.Condition == constants.PASS {
if t.TaskConfig.Condition == constants.PASS {
ok = true
} else {
proc, err := ProcessCtlLogic.GetProcess(data.Task.OperationTarget)
proc, err := ProcessCtlLogic.GetProcess(t.TaskConfig.OperationTarget)
if err != nil {
return
}
ok = conditionHandle[data.Task.Condition](data.Task, proc)
ok = conditionHandle[t.TaskConfig.Condition](t.TaskConfig, proc)
}
log.Logger.Debugw("任务条件判断", "pass", ok)
if !ok {
return
}
proc, err := ProcessCtlLogic.GetProcess(data.Task.OperationTarget)
proc, err := ProcessCtlLogic.GetProcess(t.TaskConfig.OperationTarget)
if err != nil {
log.Logger.Debugw("不存在该进程,结束任务")
return
@@ -60,98 +65,66 @@ func (t *taskLogic) run(ctx context.Context, data *model.TaskJob) {
// 执行操作
log.Logger.Infow("任务开始执行")
if !OperationHandle[data.Task.Operation](data.Task, proc) {
if !OperationHandle[t.TaskConfig.Operation](t.TaskConfig, proc) {
log.Logger.Errorw("任务执行失败")
return
}
log.Logger.Infow("任务执行成功", "target", data.Task.OperationTarget)
log.Logger.Infow("任务执行成功", "target", t.TaskConfig.OperationTarget)
if data.Task.NextId != nil {
nextTask, err := t.getTaskJob(*data.Task.NextId)
if t.TaskConfig.NextId != nil {
nextTask, err := TaskLogic.getTaskJob(*t.TaskConfig.NextId)
if err != nil {
log.Logger.Errorw("无法获取到下一个节点,结束任务", "nextId", data.Task.NextId)
log.Logger.Errorw("无法获取到下一个节点,结束任务", "nextId", t.TaskConfig.NextId)
return
}
select {
case <-ctx.Done():
log.Logger.Infow("任务流被手动结束")
default:
log.Logger.Debugw("执行下一个节点", "nextId", *data.Task.NextId)
log.Logger.Debugw("执行下一个节点", "nextId", *t.TaskConfig.NextId)
if nextTask.Running {
log.Logger.Errorw("下一个节点已在运行,结束任务", "nextId", data.Task.NextId)
log.Logger.Errorw("下一个节点已在运行,结束任务", "nextId", t.TaskConfig.NextId)
return
}
t.run(ctx, nextTask)
nextTask.Run(ctx)
}
} else {
log.Logger.Infow("任务流结束")
}
}
type conditionFunc func(data *model.Task, proc *ProcessBase) bool
var conditionHandle = map[constants.Condition]conditionFunc{
constants.RUNNING: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State == 1
},
constants.NOT_RUNNING: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State != 1
},
constants.EXCEPTION: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State == 2
},
func (t *TaskJob) InitCronHandle() error {
if _, err := cron.ParseStandard(t.TaskConfig.CronExpression); err != nil { // cron表达式校验
log.Logger.Errorw("cron解析失败", "cron", t.TaskConfig.CronExpression, "err", err)
return err
}
c := cron.New()
_, err := c.AddFunc(t.TaskConfig.CronExpression, func() {
log.Logger.Infow("定时任务启动")
if t.Running {
log.Logger.Infow("任务已在运行,跳过当前任务")
return
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
t.Cancel = cancel
t.Run(ctx)
log.Logger.Infow("定时任务结束")
})
if err != nil {
return err
}
c.Start()
t.Cron = c
return nil
}
// 执行操作,返回结果是否成功
type operationFunc func(data *model.Task, proc *ProcessBase) bool
var OperationHandle = map[constants.TaskOperation]operationFunc{
constants.TASK_START: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State == 1 {
log.Logger.Debugw("进程已在运行")
return false
}
go proc.Start()
return true
},
constants.TASK_START_WAIT_DONE: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State == 1 {
log.Logger.Debugw("进程已在运行")
return false
}
if err := proc.Start(); err != nil {
log.Logger.Debugw("进程启动失败")
return false
}
select {
case <-proc.StopChan:
log.Logger.Debugw("进程停止,任务完成")
return true
case <-time.After(time.Second * time.Duration(config.CF.TaskTimeout)):
log.Logger.Errorw("任务超时")
return false
}
},
constants.TASK_STOP: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State != 1 {
log.Logger.Debugw("进程未在运行")
return false
}
log.Logger.Debugw("异步停止任务")
proc.State.manualStopFlag = true
go proc.Kill()
return true
},
constants.TASK_STOP_WAIT_DONE: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State != 1 {
log.Logger.Debugw("进程未在运行")
return false
}
log.Logger.Debugw("停止任务并等待结束")
proc.State.manualStopFlag = true
return proc.Kill() == nil
},
func (t *TaskJob) EditStatus(status bool) error {
if t.Cron != nil && !status {
t.Cron.Stop()
} else if err := t.InitCronHandle(); err != nil {
return err
}
t.TaskConfig.Enable = status
return nil
}

View File

@@ -0,0 +1,78 @@
package logic
import (
"time"
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/log"
)
type conditionFunc func(data *model.Task, proc *ProcessBase) bool
var conditionHandle = map[constants.Condition]conditionFunc{
constants.RUNNING: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State == 1
},
constants.NOT_RUNNING: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State != 1
},
constants.EXCEPTION: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State == 2
},
}
// 执行操作,返回结果是否成功
type operationFunc func(data *model.Task, proc *ProcessBase) bool
var OperationHandle = map[constants.TaskOperation]operationFunc{
constants.TASK_START: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State == 1 {
log.Logger.Debugw("进程已在运行")
return false
}
go proc.Start()
return true
},
constants.TASK_START_WAIT_DONE: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State == 1 {
log.Logger.Debugw("进程已在运行")
return false
}
if err := proc.Start(); err != nil {
log.Logger.Debugw("进程启动失败")
return false
}
select {
case <-proc.StopChan:
log.Logger.Debugw("进程停止,任务完成")
return true
case <-time.After(time.Second * time.Duration(config.CF.TaskTimeout)):
log.Logger.Errorw("任务超时")
return false
}
},
constants.TASK_STOP: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State != 1 {
log.Logger.Debugw("进程未在运行")
return false
}
log.Logger.Debugw("异步停止任务")
proc.State.manualStopFlag = true
go proc.Kill()
return true
},
constants.TASK_STOP_WAIT_DONE: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State != 1 {
log.Logger.Debugw("进程未在运行")
return false
}
log.Logger.Debugw("停止任务并等待结束")
proc.State.manualStopFlag = true
return proc.Kill() == nil
},
}

View File

@@ -3,17 +3,13 @@ package logic
import (
"context"
"errors"
"fmt"
"sync"
"time"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/internal/app/repository"
"github.com/lzh-1625/go_process_manager/log"
"github.com/lzh-1625/go_process_manager/utils"
"github.com/robfig/cron/v3"
)
type taskLogic struct {
@@ -22,53 +18,25 @@ type taskLogic struct {
var TaskLogic = new(taskLogic)
func (t *taskLogic) getTaskJob(id int) (*model.TaskJob, error) {
func (t *taskLogic) getTaskJob(id int) (*TaskJob, error) {
c, ok := t.taskJobMap.Load(id)
if !ok {
return nil, errors.New("don't exist this task id")
}
return c.(*model.TaskJob), nil
return c.(*TaskJob), nil
}
func (t *taskLogic) InitTaskJob() {
for _, v := range repository.TaskRepository.GetAllTask() {
tj := &model.TaskJob{
Task: &v,
StartTime: time.Now(),
}
if tj.Task.Cron != "" {
c := cron.New()
_, err := c.AddFunc(v.Cron, t.cronHandle(tj))
if err != nil {
log.Logger.Errorw("定时任务创建失败", "err", err, "id", v.Id)
continue
}
if v.Enable {
c.Start()
}
tj.Cron = c
tj, err := NewTaskJob(v)
if err != nil {
log.Logger.Warnw("任务初始化失败", "err", err)
continue
}
t.taskJobMap.Store(v.Id, tj)
}
}
func (t *taskLogic) cronHandle(data *model.TaskJob) func() {
return func() {
log.Logger.AddAdditionalInfo("id", data.Task.Id)
defer log.Logger.DeleteAdditionalInfo(1)
log.Logger.Infow("定时任务启动")
if data.Running {
log.Logger.Infow("任务已在运行,跳过当前任务")
return
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
data.Cancel = cancel
t.run(ctx, data)
log.Logger.Infow("定时任务结束")
}
}
func (t *taskLogic) StopTaskJob(id int) error {
taskJob, err := t.getTaskJob(id)
if err != nil {
@@ -96,9 +64,9 @@ func (t *taskLogic) GetAllTaskJob() []model.TaskVo {
if err != nil {
continue
}
result[i].Id = task.Task.Id
result[i].Id = task.TaskConfig.Id
result[i].Running = task.Running
result[i].Enable = task.Task.Enable
result[i].Enable = task.TaskConfig.Enable
}
return result
}
@@ -115,37 +83,27 @@ func (t *taskLogic) DeleteTask(id int) (err error) {
}
func (t *taskLogic) CreateTask(data model.Task) error {
tj := &model.TaskJob{
Task: &data,
StartTime: time.Now(),
}
if data.Cron != "" {
if _, err := cron.ParseStandard(data.Cron); err != nil { // cron表达式校验
log.Logger.Errorw("cron解析失败", "cron", data.Cron, "err", err)
return err
} else {
c := cron.New()
c.AddFunc(data.Cron, t.cronHandle(tj))
tj.Cron = c
}
tj, err := NewTaskJob(data)
if err != nil {
return err
}
taskId, err := repository.TaskRepository.AddTask(data)
if err != nil {
return err
}
data.Id = taskId
t.taskJobMap.Store(data.Id, tj)
tj.TaskConfig.Id = taskId
t.taskJobMap.Store(taskId, tj)
return nil
}
func (t *taskLogic) EditTask(data model.Task) error {
tj, err := t.getTaskJob(data.Id)
if err != nil {
return fmt.Errorf("task with id %v does not exist", data.Id)
return errors.New("task id not exist")
}
if tj.Running {
return errors.New("can't edit task while it is running")
return errors.New("can't edit running task")
}
if tj.Cron != nil {
@@ -153,37 +111,9 @@ func (t *taskLogic) EditTask(data model.Task) error {
tj.Cron = nil
}
// 更新任务
tj.Task = &data
tj.TaskConfig = &data
tj.TaskConfig.Enable = false
// 如果 Cron 字段为空,直接禁用任务并返回
if tj.Task.Cron == "" {
tj.Task.Enable = false
return repository.TaskRepository.EditTask(data)
}
// 校验 Cron 表达式
if _, err := cron.ParseStandard(tj.Task.Cron); err != nil {
tj.Task.Enable = false
return fmt.Errorf("invalid cron expression: %v", err)
}
// 创建 Cron 调度器
c := cron.New()
_, err = c.AddFunc(data.Cron, t.cronHandle(tj))
if err != nil {
log.Logger.Errorw("failed to create cron job", "err", err, "id", data.Id)
tj.Task.Enable = false
return fmt.Errorf("failed to create cron job: %v", err)
}
// 启动 Cron 调度器
if data.Enable {
c.Start()
}
tj.Cron = c
// 更新任务到数据库
return repository.TaskRepository.EditTask(data)
}
@@ -192,16 +122,12 @@ func (t *taskLogic) EditTaskEnable(id int, status bool) error {
if err != nil {
return errors.New("don't exist this task id")
}
if tj.Cron != nil {
if status {
tj.Cron.Start()
} else {
tj.Cron.Stop()
}
} else if status {
return errors.New("cron job create failed")
if tj.TaskConfig.CronExpression == "" {
return errors.New("task cron expression is empty")
}
if err := tj.EditStatus(status); err != nil {
return err
}
tj.Task.Enable = status
if err := repository.TaskRepository.EditTaskEnable(id, status); err != nil {
return err
}
@@ -239,3 +165,18 @@ func (t *taskLogic) RunTaskByTriggerEvent(processName string, event constants.Pr
t.RunTaskById(v.Id)
}
}
func (t *taskLogic) RunTaskById(id int) error {
task, err := t.getTaskJob(id)
if err != nil {
return errors.New("id不存在")
}
if task.Running {
return errors.New("task is running")
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
task.Cancel = cancel
task.Run(ctx)
return nil
}

View File

@@ -28,7 +28,14 @@ func Logger() gin.HandlerFunc {
if user, ok := ctx.Get(constants.CTXFLG_USER_NAME); ok {
logKv = append(logKv, "user", user)
}
log.Logger.Infow("GIN", logKv...)
switch {
case ctx.Writer.Status() >= 200 && ctx.Writer.Status() < 300:
log.Logger.Infow("\033[102mGIN\033[0m", logKv...)
case ctx.Writer.Status() >= 500:
log.Logger.Infow("\033[101mGIN\033[0m", logKv...)
default:
log.Logger.Infow("\033[103mGIN\033[0m", logKv...)
}
}
}

View File

@@ -1,26 +0,0 @@
package middle
import (
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/gin-gonic/gin"
)
func PanicMiddle() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err == 0 {
if err, ok := c.Get(constants.CTXFLG_ERR); ok {
rErr(c, -1, err.(string), nil)
} else {
rErr(c, -1, "Internal error!", nil)
}
} else {
if err != nil {
panic(err)
}
}
}()
c.Next()
}
}

View File

@@ -51,7 +51,7 @@ func CheckToken() gin.HandlerFunc {
} else {
token = c.Query("token")
}
if _, err := utils.ParseToken(token); err != nil {
if _, err := utils.VerifyToken(token); err != nil {
rErr(c, -2, "token校验失败", err)
return
}
@@ -73,8 +73,8 @@ func getUser(ctx *gin.Context) (string, error) {
} else {
token = ctx.Query("token")
}
if mc, err := utils.ParseToken(token); err == nil && mc != nil {
return mc.UserName, nil
if mc, err := utils.VerifyToken(token); err == nil && mc != nil {
return mc.Username, nil
} else {
return "", errors.Join(errors.New("用户信息获取失败"), err)
}

View File

@@ -89,10 +89,10 @@ type LogResp struct {
type ProcessLog struct {
Id int `json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id" `
Log string `json:"log" gorm:"column:log" type:"text"`
Time int64 `json:"time" gorm:"column:time" type:"long"`
Name string `json:"name" gorm:"column:name" type:"text"`
Using string `json:"using" gorm:"column:using" type:"keyword"`
Log string `json:"log" gorm:"column:log"`
Time int64 `json:"time" gorm:"column:time"`
Name string `json:"name" gorm:"column:name"`
Using string `json:"using" gorm:"column:using"`
}
func (n *ProcessLog) TableName() string {

View File

@@ -4,3 +4,11 @@ type FileStruct struct {
Name string `json:"name"`
IsDir bool `json:"isDir"`
}
type FilePathHandlerReq struct {
Path string `form:"path" binding:"required"`
}
type FileReadHandlerReq struct {
FilePath string `form:"filePath" binding:"required"`
}

View File

@@ -4,7 +4,7 @@ import "github.com/lzh-1625/go_process_manager/internal/app/constants"
type Process struct {
Uuid int `gorm:"primaryKey;autoIncrement;column:uuid" json:"uuid"`
Name string `gorm:"column:name;uniqueIndex;type:text" json:"name"`
Name string `gorm:"column:name;uniqueIndex;type:text" json:"name" binding:"required"`
Cmd string `gorm:"column:args" json:"cmd"`
Cwd string `gorm:"column:cwd" json:"cwd"`
AutoRestart bool `gorm:"column:auto_restart" json:"autoRestart"`

View File

@@ -1,12 +1,9 @@
package model
import (
"context"
"time"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/robfig/cron/v3"
)
type Task struct {
@@ -18,7 +15,7 @@ type Task struct {
TriggerEvent *constants.ProcessState `gorm:"column:trigger_event;" json:"triggerEvent" `
TriggerTarget *int `gorm:"column:trigger_target;" json:"triggerTarget" `
OperationTarget int `gorm:"column:operation_target;NOT NULL" json:"operationTarget" `
Cron string `gorm:"column:cron;" json:"cron" `
CronExpression string `gorm:"column:cron;" json:"cron" `
Enable bool `gorm:"column:enable;" json:"enable" `
ApiEnable bool `gorm:"column:api_enable;" json:"apiEnable" `
Key *string `gorm:"column:key;" json:"key" `
@@ -28,15 +25,6 @@ func (*Task) TableName() string {
return "task"
}
type TaskJob struct {
Cron *cron.Cron `json:"-"`
Task *Task `json:"task"`
Running bool `json:"running"`
Cancel context.CancelFunc `json:"-"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
}
type TaskVo struct {
Task
ProcessName string `gorm:"column:process_name;" json:"processName"`

View File

@@ -17,3 +17,12 @@ type User struct {
func (*User) TableName() string {
return "users"
}
type LoginHandlerReq struct {
Account string `form:"account" binding:"required"`
Password string `form:"password" binding:"required"`
}
type DeleteUserReq struct {
Account string `form:"account" binding:"required"`
}

View File

@@ -14,3 +14,14 @@ type WsShare struct {
CreateBy string `gorm:"column:create_by" json:"createBy"`
Token string `gorm:"column:token" json:"token"`
}
func (WsShare) TableName() string {
return "ws_share"
}
type WebsocketHandleReq struct {
Uuid int `form:"uuid"`
Cols int `form:"cols"`
Rows int `form:"rows"`
Token string `form:"token"`
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/internal/app/repository/query"
"github.com/lzh-1625/go_process_manager/internal/app/search"
"github.com/lzh-1625/go_process_manager/log"
)
@@ -18,7 +19,7 @@ func (l *logRepository) InsertLog(data model.ProcessLog) {
}
}
func (l *logRepository) SearchLog(req model.GetLogReq) (result []*model.ProcessLog, total int64) {
func (l *logRepository) SearchLog(req model.GetLogReq, logQuery []search.Query) (result []*model.ProcessLog, total int64) {
q := query.ProcessLog.WithContext(context.TODO())
if req.Match.Name != "" {
q = q.Where(query.ProcessLog.Name.Eq(req.Match.Name))
@@ -26,9 +27,16 @@ func (l *logRepository) SearchLog(req model.GetLogReq) (result []*model.ProcessL
if req.Match.Using != "" {
q = q.Where(query.ProcessLog.Using.Eq(req.Match.Using))
}
if req.Match.Log != "" {
q = q.Where(query.ProcessLog.Log.Like("%" + req.Match.Log + "%"))
for _, v := range logQuery {
switch v.Cond {
case search.Match, search.WildCard:
q = q.Where(query.ProcessLog.Log.Like("%" + v.Content + "%"))
case search.NotMatch, search.NotWildCard:
q = q.Where(query.ProcessLog.Log.NotLike("%" + v.Content + "%"))
}
}
if req.Sort == "desc" {
q = q.Order(query.ProcessLog.Time.Desc())
}

View File

@@ -12,7 +12,6 @@ var ProcessRepository = new(processRepository)
func (p *processRepository) GetAllProcessConfig() []model.Process {
result := []model.Process{}
tx := db.Find(&result)
if tx.Error != nil {
log.Logger.Error(tx.Error)
@@ -35,17 +34,13 @@ func (p *processRepository) GetProcessConfigByUser(username string) []model.Proc
}
func (p *processRepository) UpdateProcessConfig(process model.Process) error {
tx := db.Save(&process)
return tx.Error
return db.Save(&process).Error
}
func (p *processRepository) AddProcessConfig(process model.Process) (int, error) {
tx := db.Create(&process)
if tx.Error != nil {
log.Logger.Error(tx.Error)
return 0, tx.Error
}
return process.Uuid, nil
func (p *processRepository) AddProcessConfig(process model.Process) (id int, err error) {
err = db.Create(&process).Error
id = process.Uuid
return
}
func (p *processRepository) DeleteProcessConfig(uuid int) error {
@@ -54,12 +49,7 @@ func (p *processRepository) DeleteProcessConfig(uuid int) error {
}).Error
}
func (p *processRepository) GetProcessConfigById(uuid int) model.Process {
result := model.Process{}
tx := db.Model(&model.Process{}).Where(&model.Process{Uuid: uuid}).First(&result)
if tx.Error != nil {
log.Logger.Error(tx.Error)
return model.Process{}
}
return result
func (p *processRepository) GetProcessConfigById(uuid int) (data model.Process, err error) {
err = db.Model(&model.Process{}).Where(&model.Process{Uuid: uuid}).First(&data).Error
return
}

View File

@@ -1,13 +1,9 @@
package repository
import (
"errors"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/internal/app/repository/query"
"gorm.io/gorm"
)
type taskRepository struct{}
@@ -41,11 +37,6 @@ func (t *taskRepository) DeleteTask(id int) (err error) {
}
func (t *taskRepository) EditTask(data model.Task) (err error) {
err = db.Model(&model.Task{}).Where(&model.Task{Id: data.Id}).First(&model.Task{}).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
err = db.Model(&model.Task{}).Where(&model.Task{Id: data.Id}).Save(data).Error
return
}

View File

@@ -25,20 +25,14 @@ func (u *userRepository) CreateUser(user model.User) error {
}
func (u *userRepository) UpdatePassword(name string, password string) error {
tx := db.Model(&model.User{}).Where(&model.User{Account: name}).Updates(&model.User{Password: utils.Md5(password)})
return tx.Error
return db.Model(&model.User{}).Where(&model.User{Account: name}).Updates(&model.User{Password: utils.Md5(password)}).Error
}
func (u *userRepository) DeleteUser(name string) error {
if err := db.Model(&model.User{}).Where(&model.User{Account: name}).First(&model.User{}).Error; err != nil {
return err
}
tx := db.Delete(&model.User{Account: name})
return tx.Error
return db.Delete(&model.User{Account: name}).Error
}
func (u *userRepository) GetUserList() []model.User {
result := []model.User{}
func (u *userRepository) GetUserList() (result []model.User) {
db.Find(&result)
return result
return
}

View File

@@ -51,94 +51,160 @@ func pprofInit(r *gin.Engine) {
}
func routePathInit(r *gin.Engine) {
apiGroup := r.Group("/api")
apiGroup.Use(middle.CheckToken())
apiGroup.Use(middle.PanicMiddle())
// apiGroup.Use(middle.DemoMiddle())
{
wsGroup := apiGroup.Group("/ws")
{
wsGroup.GET("", middle.OprPermission(constants.OPERATION_TERMINAL), api.WsApi.WebsocketHandle)
wsGroup.GET("/share", api.WsApi.WebsocketShareHandle)
wsGroup.GET("", middle.OprPermission(constants.OPERATION_TERMINAL), bind(api.WsApi.WebsocketHandle, Query))
wsGroup.GET("/share", bind(api.WsApi.WebsocketShareHandle, Query))
}
processGroup := apiGroup.Group("/process")
{
processGroup.DELETE("", middle.OprPermission(constants.OPERATION_STOP), api.ProcApi.KillProcess)
processGroup.GET("", api.ProcApi.GetProcessList)
processGroup.GET("/wait", middle.ProcessWaitCond.WaitGetMiddel, api.ProcApi.GetProcessList)
processGroup.PUT("", middle.OprPermission(constants.OPERATION_START), api.ProcApi.StartProcess)
processGroup.PUT("/all", api.ProcApi.StartAllProcess)
processGroup.DELETE("/all", api.ProcApi.KillAllProcess)
processGroup.POST("/share", middle.RolePermission(constants.ROLE_ADMIN), api.ProcApi.ProcessCreateShare)
processGroup.GET("/control", middle.RolePermission(constants.ROLE_ROOT), middle.ProcessWaitCond.WaitTriggerMiddel, api.ProcApi.ProcessControl)
processGroup.DELETE("", middle.OprPermission(constants.OPERATION_STOP), bind(api.ProcApi.KillProcess, Query))
processGroup.GET("", bind(api.ProcApi.GetProcessList, None))
processGroup.GET("/wait", middle.ProcessWaitCond.WaitGetMiddel, bind(api.ProcApi.GetProcessList, None))
processGroup.PUT("", middle.OprPermission(constants.OPERATION_START), bind(api.ProcApi.StartProcess, Query))
processGroup.PUT("/all", bind(api.ProcApi.StartAllProcess, None))
processGroup.DELETE("/all", bind(api.ProcApi.KillAllProcess, None))
processGroup.POST("/share", middle.RolePermission(constants.ROLE_ADMIN), bind(api.ProcApi.ProcessCreateShare, Body))
processGroup.GET("/control", middle.RolePermission(constants.ROLE_ROOT), middle.ProcessWaitCond.WaitTriggerMiddel, bind(api.ProcApi.ProcessControl, Query))
proConfigGroup := processGroup.Group("/config")
{
proConfigGroup.POST("", middle.RolePermission(constants.ROLE_ROOT), middle.ProcessWaitCond.WaitTriggerMiddel, api.ProcApi.CreateNewProcess)
proConfigGroup.DELETE("", middle.RolePermission(constants.ROLE_ROOT), middle.ProcessWaitCond.WaitTriggerMiddel, api.ProcApi.DeleteNewProcess)
proConfigGroup.PUT("", middle.RolePermission(constants.ROLE_ROOT), api.ProcApi.UpdateProcessConfig)
proConfigGroup.GET("", middle.RolePermission(constants.ROLE_ADMIN), api.ProcApi.GetProcessConfig)
proConfigGroup.POST("", middle.RolePermission(constants.ROLE_ROOT), middle.ProcessWaitCond.WaitTriggerMiddel, bind(api.ProcApi.CreateNewProcess, Body))
proConfigGroup.DELETE("", middle.RolePermission(constants.ROLE_ROOT), middle.ProcessWaitCond.WaitTriggerMiddel, bind(api.ProcApi.DeleteNewProcess, Query))
proConfigGroup.PUT("", middle.RolePermission(constants.ROLE_ROOT), bind(api.ProcApi.UpdateProcessConfig, Body))
proConfigGroup.GET("", middle.RolePermission(constants.ROLE_ADMIN), bind(api.ProcApi.GetProcessConfig, Query))
}
}
taskGroup := apiGroup.Group("/task")
{
taskGroup.GET("", middle.RolePermission(constants.ROLE_ADMIN), api.TaskApi.GetTaskById)
taskGroup.GET("/all", middle.RolePermission(constants.ROLE_ADMIN), api.TaskApi.GetTaskList)
taskGroup.GET("/all/wait", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitGetMiddel, api.TaskApi.GetTaskList)
taskGroup.POST("", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitTriggerMiddel, api.TaskApi.CreateTask)
taskGroup.DELETE("", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitTriggerMiddel, api.TaskApi.DeleteTaskById)
taskGroup.PUT("", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitTriggerMiddel, api.TaskApi.EditTask)
taskGroup.PUT("/enable", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitTriggerMiddel, api.TaskApi.EditTaskEnable)
taskGroup.GET("/start", middle.RolePermission(constants.ROLE_ADMIN), api.TaskApi.StartTask)
taskGroup.GET("/stop", middle.RolePermission(constants.ROLE_ADMIN), api.TaskApi.StopTask)
taskGroup.POST("/key", middle.RolePermission(constants.ROLE_ADMIN), api.TaskApi.CreateTaskApiKey)
taskGroup.GET("/api-key/:key", api.TaskApi.RunTaskByKey)
taskGroup.GET("", middle.RolePermission(constants.ROLE_ADMIN), bind(api.TaskApi.GetTaskById, Query))
taskGroup.GET("/all", middle.RolePermission(constants.ROLE_ADMIN), bind(api.TaskApi.GetTaskList, None))
taskGroup.GET("/all/wait", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitGetMiddel, bind(api.TaskApi.GetTaskList, None))
taskGroup.POST("", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitTriggerMiddel, bind(api.TaskApi.CreateTask, Body))
taskGroup.DELETE("", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitTriggerMiddel, bind(api.TaskApi.DeleteTaskById, Query))
taskGroup.PUT("", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitTriggerMiddel, bind(api.TaskApi.EditTask, Body))
taskGroup.PUT("/enable", middle.RolePermission(constants.ROLE_ADMIN), middle.TaskWaitCond.WaitTriggerMiddel, bind(api.TaskApi.EditTaskEnable, Body))
taskGroup.GET("/start", middle.RolePermission(constants.ROLE_ADMIN), bind(api.TaskApi.StartTask, Query))
taskGroup.GET("/stop", middle.RolePermission(constants.ROLE_ADMIN), bind(api.TaskApi.StopTask, Query))
taskGroup.POST("/key", middle.RolePermission(constants.ROLE_ADMIN), bind(api.TaskApi.CreateTaskApiKey, Body))
taskGroup.GET("/api-key/:key", bind(api.TaskApi.RunTaskByKey, None))
}
userGroup := apiGroup.Group("/user")
{
userGroup.POST("/login", api.UserApi.LoginHandler)
userGroup.POST("", middle.RolePermission(constants.ROLE_ROOT), api.UserApi.CreateUser)
userGroup.PUT("/password", middle.RolePermission(constants.ROLE_USER), api.UserApi.ChangePassword)
userGroup.DELETE("", middle.RolePermission(constants.ROLE_ROOT), api.UserApi.DeleteUser)
userGroup.GET("", middle.RolePermission(constants.ROLE_ROOT), api.UserApi.GetUserList)
userGroup.POST("/login", bind(api.UserApi.LoginHandler, Body))
userGroup.POST("", middle.RolePermission(constants.ROLE_ROOT), bind(api.UserApi.CreateUser, Body))
userGroup.PUT("/password", middle.RolePermission(constants.ROLE_USER), bind(api.UserApi.ChangePassword, Body))
userGroup.DELETE("", middle.RolePermission(constants.ROLE_ROOT), bind(api.UserApi.DeleteUser, Query))
userGroup.GET("", middle.RolePermission(constants.ROLE_ROOT), bind(api.UserApi.GetUserList, None))
}
pushGroup := apiGroup.Group("/push").Use(middle.RolePermission(constants.ROLE_ADMIN))
{
pushGroup.GET("/list", api.PushApi.GetPushList)
pushGroup.GET("", api.PushApi.GetPushById)
pushGroup.POST("", api.PushApi.AddPushConfig)
pushGroup.PUT("", api.PushApi.UpdatePushConfig)
pushGroup.DELETE("", api.PushApi.DeletePushConfig)
pushGroup.GET("/list", bind(api.PushApi.GetPushList, None))
pushGroup.GET("", bind(api.PushApi.GetPushById, Query))
pushGroup.POST("", bind(api.PushApi.AddPushConfig, Body))
pushGroup.PUT("", bind(api.PushApi.UpdatePushConfig, Body))
pushGroup.DELETE("", bind(api.PushApi.DeletePushConfig, Query))
}
fileGroup := apiGroup.Group("/file").Use(middle.RolePermission(constants.ROLE_ADMIN))
{
fileGroup.GET("/list", api.FileApi.FilePathHandler)
fileGroup.PUT("", api.FileApi.FileWriteHandler)
fileGroup.GET("", api.FileApi.FileReadHandler)
fileGroup.GET("/list", bind(api.FileApi.FilePathHandler, Query))
fileGroup.PUT("", bind(api.FileApi.FileWriteHandler, None))
fileGroup.GET("", bind(api.FileApi.FileReadHandler, Query))
}
permissionGroup := apiGroup.Group("/permission").Use(middle.RolePermission(constants.ROLE_ROOT))
{
permissionGroup.GET("/list", api.PermissionApi.GetPermissionList)
permissionGroup.PUT("", middle.ProcessWaitCond.WaitTriggerMiddel, api.PermissionApi.EditPermssion)
permissionGroup.GET("/list", bind(api.PermissionApi.GetPermissionList, Query))
permissionGroup.PUT("", middle.ProcessWaitCond.WaitTriggerMiddel, bind(api.PermissionApi.EditPermssion, Body))
}
logGroup := apiGroup.Group("/log").Use(middle.RolePermission(constants.ROLE_USER))
{
logGroup.POST("", api.LogApi.GetLog)
logGroup.GET("/running", api.LogApi.GetRunningLog)
logGroup.POST("", bind(api.LogApi.GetLog, Body))
logGroup.GET("/running", bind(api.LogApi.GetRunningLog, None))
}
configGroup := apiGroup.Group("/config").Use(middle.RolePermission(constants.ROLE_ROOT))
{
configGroup.GET("", api.ConfigApi.GetSystemConfiguration)
configGroup.PUT("", api.ConfigApi.SetSystemConfiguration)
configGroup.PUT("/es", api.ConfigApi.EsConfigReload)
configGroup.GET("", bind(api.ConfigApi.GetSystemConfiguration, None))
configGroup.PUT("", bind(api.ConfigApi.SetSystemConfiguration, None))
configGroup.PUT("/log", bind(api.ConfigApi.LogConfigReload, None))
}
}
}
const (
None = 0
Header = 1 << iota
Body
Query
)
func bind[T any, R any](fn func(*gin.Context, T) R, bindOption int) func(*gin.Context) {
return func(ctx *gin.Context) {
var req T
if bindOption&Body != 0 {
if err := ctx.BindJSON(&req); err != nil {
rErr(ctx, err)
return
}
}
if bindOption&Header != 0 {
if err := ctx.BindHeader(&req); err != nil {
rErr(ctx, err)
return
}
}
if bindOption&Query != 0 {
if err := ctx.BindQuery(&req); err != nil {
rErr(ctx, err)
return
}
}
result := fn(ctx, req)
switch v := any(result).(type) {
case error:
if v != nil {
rErr(ctx, v)
return
} else {
ctx.JSON(200, gin.H{
"code": 0,
"message": "success",
})
return
}
case *api.Response:
ctx.JSON(v.StatusCode, gin.H{
"data": v.Data,
"msg": v.Msg,
"code": v.Code,
})
return
default:
ctx.JSON(200, gin.H{
"code": 0,
"message": "success",
"data": v,
})
}
}
}
func rErr(ctx *gin.Context, err error) {
log.Logger.Warn(err)
ctx.JSON(500, gin.H{
"code": -1,
"message": err.Error(),
})
}

View File

@@ -0,0 +1,169 @@
//go:build !slim
// +build !slim
package bleve
import (
"fmt"
"time"
"github.com/blevesearch/bleve/v2"
"github.com/blevesearch/bleve/v2/search"
_ "github.com/blevesearch/bleve/v2/search/highlight/highlighter/ansi"
"github.com/google/uuid"
"github.com/lzh-1625/go_process_manager/internal/app/model"
sr "github.com/lzh-1625/go_process_manager/internal/app/search"
logger "github.com/lzh-1625/go_process_manager/log"
gse "github.com/vcaesar/gse-bleve"
// gse "github.com/lzh-1625/gse-bleve"
)
func init() {
sr.Register("bleve", new(bleveSearch))
}
type bleveSearch struct {
index bleve.Index
}
func (b *bleveSearch) Init() error {
opt := gse.Option{
Dicts: "embed, zh_s",
Stop: "",
Opt: "search-hmm",
Trim: "trim",
}
indexMapping, err := gse.NewMapping(opt)
if err != nil {
logger.Logger.Errorw("bleve init fail", "err", err)
return err
}
mapping := bleve.NewDocumentMapping()
log := bleve.NewTextFieldMapping()
log.Index = true
time := bleve.NewNumericFieldMapping()
time.Index = true
name := bleve.NewKeywordFieldMapping()
name.Index = true
using := bleve.NewKeywordFieldMapping()
using.Index = true
mapping.AddFieldMappingsAt("log", log)
mapping.AddFieldMappingsAt("time", time)
mapping.AddFieldMappingsAt("name", name)
mapping.AddFieldMappingsAt("using", using)
indexMapping.AddDocumentMapping("server_log_v1", mapping)
index, err := bleve.Open("log.bleve")
if err != nil {
index, err = bleve.New("log.bleve", indexMapping)
if err != nil {
logger.Logger.Errorw("bleve init error", "err", err)
return err
}
}
b.index = index
return nil
}
func (b *bleveSearch) Insert(logContent string, processName string, using string, ts int64) {
if err := b.index.Index(uuid.NewString(), model.ProcessLog{
Log: logContent,
Name: processName,
Using: using,
Time: ts,
}); err != nil {
logger.Logger.Warnw("bleve log insert failed", "err", err)
}
fmt.Printf("using: %v\n", using)
}
func (b *bleveSearch) Search(req model.GetLogReq, filterProcessName ...string) (result model.LogResp) {
buildQuery := bleve.NewBooleanQuery()
for _, v := range sr.QueryStringAnalysis(req.Match.Log) {
switch v.Cond {
case sr.Match, sr.WildCard:
logQuery := bleve.NewMatchQuery(v.Content)
logQuery.SetField("log")
buildQuery.AddMust(logQuery)
case sr.NotMatch, sr.NotWildCard:
logQuery := bleve.NewMatchQuery(v.Content)
logQuery.SetField("log")
buildQuery.AddMustNot(logQuery)
// case sr.WildCard:
// logQuery := bleve.NewWildcardQuery("*" + v.Content + "*")
// logQuery.SetField("log")
// buildQuery.AddMust(logQuery)
// case sr.NotWildCard:
// logQuery := bleve.NewWildcardQuery("*" + v.Content + "*")
// logQuery.SetField("log")
// buildQuery.AddMustNot(logQuery)
}
}
if req.Match.Name != "" {
nameQuery := bleve.NewTermQuery(req.Match.Name)
nameQuery.SetField("name")
buildQuery.AddMust(nameQuery)
}
if req.Match.Using != "" {
usingQuery := bleve.NewWildcardQuery("*" + req.Match.Using + "*")
usingQuery.SetField("using")
buildQuery.AddMust(usingQuery)
}
if req.TimeRange.EndTime != 0 || req.TimeRange.StartTime != 0 {
st := float64(req.TimeRange.StartTime)
et := float64(req.TimeRange.EndTime)
timeQuery := bleve.NewNumericRangeQuery(&st, &et)
buildQuery.AddMust(timeQuery)
} else {
st := float64(0)
et := float64(time.Now().UnixMilli())
timeQuery := bleve.NewNumericRangeQuery(&st, &et)
buildQuery.AddMust(timeQuery)
}
if len(filterProcessName) != 0 {
for _, v := range filterProcessName {
filterQuery := bleve.NewTermQuery(v)
filterQuery.SetField("name")
buildQuery.AddShould(filterQuery)
}
}
sortArgs := ([]string{"-_score"})
if req.Sort == "desc" {
sortArgs = ([]string{"-time"})
}
if req.Sort == "asc" {
sortArgs = ([]string{"time"})
}
hl := bleve.HighlightRequest{}
hl.AddField("log")
style := "ansi"
res, err := b.index.Search(&bleve.SearchRequest{
Query: buildQuery,
Fields: []string{"log", "name", "using", "time"},
From: req.Page.From,
Size: req.Page.Size,
Sort: search.ParseSortOrderStrings(sortArgs),
Highlight: &bleve.HighlightRequest{
Style: &style,
Fields: []string{"log"},
},
})
if err != nil {
logger.Logger.Warnw("bleve search failed", "err", err)
return
}
data := []*model.ProcessLog{}
for _, v := range res.Hits {
data = append(data, &model.ProcessLog{
Log: v.Fragments["log"][0],
Time: int64(v.Fields["time"].(float64)),
Using: v.Fields["using"].(string),
Name: v.Fields["name"].(string),
})
}
result.Data = data
result.Total = int64(res.Total)
return
}

View File

@@ -0,0 +1,4 @@
//go:build slim
// +build slim
package bleve

View File

@@ -1,36 +1,31 @@
package logic
package es
import (
"context"
"encoding/json"
"fmt"
"net/http"
"reflect"
"time"
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/model"
sr "github.com/lzh-1625/go_process_manager/internal/app/search"
"github.com/lzh-1625/go_process_manager/log"
"github.com/olivere/elastic/v7"
)
type esLogic struct {
func init() {
sr.Register("es", new(esSearch))
}
type esSearch struct {
esClient *elastic.Client
}
var (
EsLogic = new(esLogic)
)
func (e *esLogic) InitEs() bool {
if !config.CF.EsEnable {
log.Logger.Debug("不使用es")
return false
}
func (e *esSearch) Init() error {
var err error
EsLogic.esClient, err = elastic.NewClient(
e.esClient, err = elastic.NewClient(
elastic.SetURL(config.CF.EsUrl),
elastic.SetBasicAuth(config.CF.EsUsername, config.CF.EsPassword),
elastic.SetSniff(false),
@@ -42,15 +37,13 @@ func (e *esLogic) InitEs() bool {
}),
)
if err != nil {
config.CF.EsEnable = false
log.Logger.Warnw("Failed to connect to es", "err", err)
return false
return err
}
EsLogic.CreateIndexIfNotExists(config.CF.EsIndex)
return true
return nil
}
func (e *esLogic) Insert(logContent string, processName string, using string, ts int64) {
func (e *esSearch) Insert(logContent string, processName string, using string, ts int64) {
data := model.ProcessLog{
Log: logContent,
Name: processName,
@@ -63,28 +56,7 @@ func (e *esLogic) Insert(logContent string, processName string, using string, ts
}
}
func (e *esLogic) CreateIndexIfNotExists(index string) error {
ctx := context.Background()
exists, err := e.esClient.IndexExists(index).Do(ctx)
if err != nil {
return err
}
if exists {
return nil
}
info, err := e.esClient.CreateIndex(index).BodyString(e.structToJSON()).Do(ctx)
if err != nil {
return err
}
if !info.Acknowledged {
return fmt.Errorf("ES 创建索引 [%s] 失败", index)
}
return nil
}
func (e *esLogic) Search(req model.GetLogReq, filterProcessName ...string) model.LogResp {
func (e *esSearch) Search(req model.GetLogReq, filterProcessName ...string) model.LogResp {
// 检查 req 是否为 nil
if req.Page.From < 0 || req.Page.Size <= 0 {
log.Logger.Error("无效的分页请求参数")
@@ -107,16 +79,29 @@ func (e *esLogic) Search(req model.GetLogReq, filterProcessName ...string) model
if req.TimeRange.EndTime != 0 {
queryList = append(queryList, timeRangeQuery.Lte(req.TimeRange.EndTime))
}
if req.Match.Log != "" {
queryList = append(queryList, elastic.NewMatchQuery("log", req.Match.Log))
notQuery := []elastic.Query{}
for _, v := range sr.QueryStringAnalysis(req.Match.Log) {
switch v.Cond {
case sr.Match:
queryList = append(queryList, elastic.NewMatchQuery("log", v.Content))
case sr.NotMatch:
notQuery = append(notQuery, elastic.NewMatchQuery("log", v.Content))
case sr.WildCard:
queryList = append(queryList, elastic.NewWildcardQuery("log.keyword", "*"+v.Content+"*"))
case sr.NotWildCard:
notQuery = append(notQuery, elastic.NewWildcardQuery("log.keyword", "*"+v.Content+"*"))
}
fmt.Printf("v.Cond: %v\n", v.Cond)
fmt.Printf("v.Content: %v\n", v.Content)
}
if req.Match.Name != "" {
queryList = append(queryList, elastic.NewMatchQuery("name", req.Match.Name))
}
if req.Match.Using != "" {
queryList = append(queryList, elastic.NewMatchQuery("using", req.Match.Using))
}
if len(filterProcessName) != 0 { // 过滤进程名
shouldQueryList := []elastic.Query{}
for _, fpn := range filterProcessName {
@@ -129,7 +114,7 @@ func (e *esLogic) Search(req model.GetLogReq, filterProcessName ...string) model
}
result := model.LogResp{}
resp, err := search.Query(elastic.NewBoolQuery().Must(queryList...)).Do(context.TODO())
resp, err := search.Query(elastic.NewBoolQuery().Must(queryList...).MustNot(notQuery...)).Highlight(elastic.NewHighlight().Field("log").PreTags("\033[43m").PostTags("\033[0m")).Do(context.TODO())
if err != nil {
log.Logger.Errorw("es查询失败", "err", err, "reason", resp.Error.Reason)
return result
@@ -140,6 +125,9 @@ func (e *esLogic) Search(req model.GetLogReq, filterProcessName ...string) model
if v.Source != nil {
var data model.ProcessLog
if err := json.Unmarshal(v.Source, &data); err == nil {
if len(v.Highlight) > 0 && len(v.Highlight["log"]) > 0 {
data.Log = v.Highlight["log"][0]
}
result.Data = append(result.Data, &data)
} else {
log.Logger.Errorw("JSON 解码失败", "err", err)
@@ -150,28 +138,3 @@ func (e *esLogic) Search(req model.GetLogReq, filterProcessName ...string) model
result.Total = resp.TotalHits()
return result
}
// 通过反射得到mapping
func (e *esLogic) structToJSON() string {
typ := reflect.TypeOf(model.ProcessLog{})
properties := make(map[string]map[string]string)
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldTag := field.Tag.Get("type")
if fieldTag != "" {
properties[field.Tag.Get("json")] = map[string]string{
"type": fieldTag,
}
}
}
result := map[string]interface{}{
"mappings": map[string]interface{}{
"properties": properties,
},
}
jsonData, err := json.Marshal(result)
if err != nil {
return ""
}
return string(jsonData)
}

View File

@@ -0,0 +1,69 @@
package search
import (
"fmt"
"strings"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/log"
)
type LogLogic interface {
Search(req model.GetLogReq, filterProcessName ...string) model.LogResp
Insert(log string, processName string, using string, ts int64)
Init() error
}
var searchImplMap map[string]LogLogic = map[string]LogLogic{}
func Register(name string, impl LogLogic) {
searchImplMap[name] = impl
}
func GetSearchImpl(name string) LogLogic {
v, ok := searchImplMap[name]
if ok {
return v
}
log.Logger.Warnw("未找到对应的存储引擎,使用默认[sqlite]", "name", name)
return searchImplMap["sqlite"]
}
type Cond int
const (
Match Cond = iota // ^
WildCard // ~
NotMatch // !^
NotWildCard // !~
)
type Query struct {
Cond Cond
Content string
}
func QueryStringAnalysis(s string) (query []Query) {
if strings.TrimSpace(s) == "" {
return
}
strList := strings.Split(s, " ")
for _, v := range strList {
switch {
case strings.HasPrefix(v, "!^"):
query = append(query, Query{NotMatch, v[2:]})
case strings.HasPrefix(v, "!~"):
query = append(query, Query{NotWildCard, v[2:]})
case strings.HasPrefix(v, "!"):
query = append(query, Query{NotMatch, v[1:]})
case strings.HasPrefix(v, "^"):
query = append(query, Query{Match, v[1:]})
case strings.HasPrefix(v, "~"):
query = append(query, Query{WildCard, v[1:]})
default:
query = append(query, Query{Match, v})
}
}
fmt.Printf("query: %v\n", query)
return
}

View File

@@ -0,0 +1,47 @@
package sqlite
import (
"slices"
"strings"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/internal/app/repository"
"github.com/lzh-1625/go_process_manager/internal/app/search"
)
func init() {
search.Register("sqlite", new(sqliteSearch))
}
type sqliteSearch struct{}
func (l *sqliteSearch) Search(req model.GetLogReq, filterProcessName ...string) model.LogResp {
req.FilterName = filterProcessName
query := search.QueryStringAnalysis(req.Match.Log)
data, total := repository.LogRepository.SearchLog(req, query)
for _, v := range slices.DeleteFunc(query, func(q search.Query) bool {
return q.Cond == search.NotMatch || q.Cond == search.NotWildCard
}) {
for i := range data {
data[i].Log = strings.ReplaceAll(data[i].Log, v.Content, "\033[43m"+v.Content+"\033[0m")
}
}
return model.LogResp{
Data: data,
Total: total,
}
}
func (l *sqliteSearch) Insert(log string, processName string, using string, ts int64) {
repository.LogRepository.InsertLog(model.ProcessLog{
Log: log,
Name: processName,
Using: using,
Time: ts,
})
}
func (l *sqliteSearch) Init() error {
return nil
}

View File

@@ -5,57 +5,13 @@ import (
"github.com/lzh-1625/go_process_manager/config"
"github.com/timandy/routine"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var Logger *logWithAdditional
type logWithAdditional struct {
*zap.SugaredLogger
threadLocal routine.ThreadLocal[[]any]
}
func (l *logWithAdditional) Infow(msg string, keysAndValues ...interface{}) {
keysAndValues = append(keysAndValues, l.threadLocal.Get()...)
l.SugaredLogger.WithOptions(zap.AddCallerSkip(1)).Infow(msg, keysAndValues...)
}
func (l *logWithAdditional) Debugw(msg string, keysAndValues ...interface{}) {
keysAndValues = append(keysAndValues, l.threadLocal.Get()...)
l.SugaredLogger.WithOptions(zap.AddCallerSkip(1)).Debugw(msg, keysAndValues...)
}
func (l *logWithAdditional) Errorw(msg string, keysAndValues ...interface{}) {
keysAndValues = append(keysAndValues, l.threadLocal.Get()...)
l.SugaredLogger.WithOptions(zap.AddCallerSkip(1)).Errorw(msg, keysAndValues...)
}
func (l *logWithAdditional) Warnw(msg string, keysAndValues ...interface{}) {
keysAndValues = append(keysAndValues, l.threadLocal.Get()...)
l.SugaredLogger.WithOptions(zap.AddCallerSkip(1)).Warnw(msg, keysAndValues...)
}
func (l *logWithAdditional) AddAdditionalInfo(k, v any) {
l.threadLocal.Set(append(l.threadLocal.Get(), k, v))
}
func (l *logWithAdditional) DeleteAdditionalInfo(layer int) {
if layer < 0 {
l.threadLocal.Set([]any{})
return
}
oldKv := l.threadLocal.Get()
if len(oldKv) < layer*2 {
l.threadLocal.Set([]any{})
return
}
l.threadLocal.Set(oldKv[:len(oldKv)-2*layer])
}
var Logger *zap.SugaredLogger
func InitLog() {
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
@@ -89,8 +45,5 @@ func InitLog() {
ErrorOutputPaths: []string{"stderr"},
}
log, _ := config.Build()
Logger = &logWithAdditional{
SugaredLogger: log.Sugar(),
threadLocal: routine.NewThreadLocal[[]any](),
}
Logger = log.Sugar()
}

View File

@@ -1,67 +1,59 @@
package utils
import (
"errors"
"fmt"
"time"
"github.com/lzh-1625/go_process_manager/config"
"github.com/golang-jwt/jwt"
"github.com/golang-jwt/jwt/v5"
)
var mySecret []byte
func SetSecret(secret []byte) {
mySecret = secret
}
func keyFunc(_ *jwt.Token) (i interface{}, err error) {
return mySecret, nil
}
var jwtKey []byte
type MyClaims struct {
UserName string `json:"user_name"`
jwt.StandardClaims
Username string `json:"username"`
jwt.RegisteredClaims
}
func GenToken(UserName string) (string, error) {
// 创建一个我们自己的声明的数据
c := MyClaims{
UserName,
jwt.StandardClaims{
ExpiresAt: time.Now().Add(
time.Duration(config.CF.TokenExpirationTime) * time.Hour).Unix(), // 过期时间
Issuer: "jwt", // 签发人
func SetSecret(secret []byte) {
jwtKey = []byte(secret)
}
func GenerateToken(username string) (string, error) {
expirationTime := time.Now().Add(3 * 24 * time.Hour)
claims := &MyClaims{
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
// 使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
return token.SignedString(mySecret)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtKey)
if err != nil {
return "", err
}
return tokenString, nil
}
func ParseToken(tokenString string) (*MyClaims, error) {
var mc = new(MyClaims)
token, err := jwt.ParseWithClaims(tokenString, mc, keyFunc)
func VerifyToken(tokenString string) (*MyClaims, error) {
claims := &MyClaims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
return nil, err
}
if token.Valid {
return mc, nil
}
return nil, errors.New("invalid token")
}
func RefreshToken(aToken, rToken string) (newAToken, newRToken string, err error) {
if _, err = jwt.Parse(rToken, keyFunc); err != nil {
return
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
var claims MyClaims
_, err = jwt.ParseWithClaims(aToken, &claims, keyFunc)
v, _ := err.(*jwt.ValidationError)
if v.Errors == jwt.ValidationErrorExpired {
token, _ := GenToken(claims.UserName)
return token, "", nil
}
return
return claims, nil
}