36 Commits

Author SHA1 Message Date
akrike
6d0239fe60 optimal task add dialog 2025-09-18 21:08:43 +08:00
akrike
09ae980ab5 add task edit dialog 2025-09-15 21:01:09 +08:00
akrike
c52f7e2097 add task ui 2025-09-11 22:36:58 +08:00
akrike
1844a843eb add task ui 2025-09-07 21:15:47 +08:00
akrike
593185a431 update 2025-09-07 11:10:09 +08:00
akrike
ab7ef546ea update 2025-09-06 13:48:13 +08:00
lzh
8fd70be906 1 2025-09-05 13:48:29 +08:00
lzh
d342a5bae9 1 2025-09-05 13:11:10 +08:00
lzh
6b5231d169 1 2025-09-05 13:10:02 +08:00
lzh
c7cc2e36fd 1 2025-09-04 13:43:29 +08:00
lzh
2fabbf5c0f 1 2025-09-04 11:27:28 +08:00
lzh
e37da632df 1 2025-09-04 11:20:19 +08:00
lzh
219dcdc372 1 2025-09-04 11:19:04 +08:00
akrike
ca849de37f ui update 2025-09-03 22:06:35 +08:00
akrike
e14df1fd99 edit token check 2025-09-03 22:06:30 +08:00
lzh
2e37eae3ca 1 2025-09-03 16:52:16 +08:00
lzh
9f7f0c9dd7 1 2025-09-03 16:44:57 +08:00
lzh
1102a60425 1 2025-09-03 16:44:21 +08:00
lzh
8a29bf610c 1 2025-09-03 16:36:38 +08:00
lzh
056caf5d06 edit es search rule 2025-09-03 16:34:19 +08:00
lzh
d550942cd2 1 2025-09-01 09:57:30 +08:00
akrike
c22384fbc0 event create bug fix 2025-08-31 20:54:25 +08:00
akrike
d42b89cc8b ui update 2025-08-31 20:37:11 +08:00
akrike
c32db8c8f6 ui update 2025-08-31 20:22:55 +08:00
akrike
f723c12d42 edit process kill 2025-08-29 17:34:16 +08:00
akrike
da54e71c27 optimal process state 2025-08-29 17:25:23 +08:00
lzh
0d46bb7d07 update 2025-08-29 17:02:00 +08:00
lzh
cffb1ee0f4 fix task running bug 2025-08-29 16:59:19 +08:00
lzh
143cf61a57 add event 2025-08-29 16:25:43 +08:00
akrike
1dc2d77e82 add api file 2025-08-14 22:22:08 +08:00
akrike
37748ecb7f init 2025-08-13 22:48:03 +08:00
akrike
5de8b45b2f init web ui 2025-08-13 21:39:13 +08:00
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
502 changed files with 56873 additions and 410 deletions

View File

@@ -9,7 +9,7 @@ import (
"syscall"
"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/eum"
"github.com/lzh-1625/go_process_manager/internal/app/logic"
"github.com/lzh-1625/go_process_manager/internal/app/middle"
"github.com/lzh-1625/go_process_manager/internal/app/repository"
@@ -86,12 +86,12 @@ func initProcess() {
}
func initJwtSecret() {
if secret, err := repository.ConfigRepository.GetConfigValue(constants.SECRET_KEY); err == nil {
if secret, err := repository.ConfigRepository.GetConfigValue(eum.SecretKey); err == nil {
utils.SetSecret([]byte(secret))
return
}
secret := utils.RandString(32)
repository.ConfigRepository.SetConfigValue(constants.SECRET_KEY, secret)
repository.ConfigRepository.SetConfigValue(eum.SecretKey, secret)
utils.SetSecret([]byte(secret))
}
@@ -123,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

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

View File

@@ -4,34 +4,33 @@ var CF = new(configuration)
// 只支持 float64、int、int64、bool、string类型
type configuration struct {
LogLevel string `default:"debug" describe:"日志等级[debug,info]"`
Listen string `default:":8797" describe:"监听端口"`
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用户名"`
EsPassword string `default:"" describe:"Elasticsearch密码"`
EsWindowLimit bool `default:"true" describe:"Es分页10000条限制"`
FileSizeLimit float64 `default:"10.0" describe:"文件大小限制MB"`
ProcessInputPrefix string `default:">" describe:"进程输入前缀"`
ProcessRestartsLimit int `default:"2" describe:"进程重启次数限制"`
ProcessMsgCacheLinesLimit int `default:"50" describe:"std进程缓存消息行数"`
ProcessMsgCacheBufLimit int `default:"4096" describe:"pty进程缓存消息字节长度"`
ProcessExpireTime int64 `default:"60" describe:"进程控制权过期时间(秒)"`
PerformanceInfoListLength int `default:"30" describe:"性能信息存储长度"`
PerformanceInfoInterval int `default:"60" describe:"监控获取间隔时间(秒)"`
TerminalConnectTimeout int `default:"10" describe:"终端连接超时时间(分钟)"`
UserPassWordMinLength int `default:"4" describe:"用户密码最小长度"`
LogMinLenth int `default:"0" describe:"过滤日志最小长度"`
LogHandlerPoolSize int `default:"10" describe:"日志处理并行数"`
PprofEnable bool `default:"true" describe:"启用pprof分析工具"`
KillWaitTime int `default:"5" describe:"kill信号等待时间"`
TaskTimeout int `default:"60" describe:"任务执行超时时间(秒)"`
TokenExpirationTime int64 `default:"720" describe:"token过期时间小时"`
WsHealthCheckInterval int `default:"3" describe:"ws主动健康检查间隔"`
CgroupPeriod int64 `default:"100000" describe:"CgroupPeriod"`
CgroupSwapLimit bool `default:"false" describe:"cgroup swap限制"`
CondWaitTime int `default:"30" describe:"长轮询等待时间(秒)"`
PerformanceCapacityDisplay bool `default:"false" describe:"性能资源容量显示"`
Tui bool `default:"-"`
LogLevel string `default:"debug" describe:"日志等级[debug,info]"`
Listen string `default:":8797" describe:"监听端口"`
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用户名"`
EsPassword string `default:"" describe:"Elasticsearch密码"`
EsWindowLimit bool `default:"true" describe:"Es分页10000条限制"`
FileSizeLimit float64 `default:"10.0" describe:"文件大小限制MB"`
ProcessInputPrefix string `default:">" describe:"进程输入前缀"`
ProcessRestartsLimit int `default:"2" describe:"进程重启次数限制"`
ProcessMsgCacheLinesLimit int `default:"50" describe:"std进程缓存消息行数"`
ProcessMsgCacheBufLimit int `default:"4096" describe:"pty进程缓存消息字节长度"`
ProcessExpireTime int64 `default:"60" describe:"进程控制权过期时间(秒)"`
PerformanceInfoListLength int `default:"30" describe:"性能信息存储长度"`
PerformanceInfoInterval int `default:"60" describe:"监控获取间隔时间(秒)"`
TerminalConnectTimeout int `default:"10" describe:"终端连接超时时间(分钟)"`
UserPassWordMinLength int `default:"4" describe:"用户密码最小长度"`
LogMinLenth int `default:"0" describe:"过滤日志最小长度"`
LogHandlerPoolSize int `default:"10" describe:"日志处理并行数"`
PprofEnable bool `default:"true" describe:"启用pprof分析工具"`
KillWaitTime int `default:"5" describe:"kill信号等待时间"`
TaskTimeout int `default:"60" describe:"任务执行超时时间(秒)"`
TokenExpirationTime int64 `default:"720" describe:"token过期时间小时"`
WsHealthCheckInterval int `default:"3" describe:"ws主动健康检查间隔"`
CgroupPeriod int64 `default:"100000" describe:"CgroupPeriod"`
CgroupSwapLimit bool `default:"false" describe:"cgroup swap限制"`
CondWaitTime int `default:"30" describe:"长轮询等待时间(秒)"`
Tui bool `default:"-"`
}

View File

@@ -3,28 +3,28 @@ package api
import (
"reflect"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"github.com/lzh-1625/go_process_manager/internal/app/repository"
"github.com/gin-gonic/gin"
)
func getRole(ctx *gin.Context) constants.Role {
if v, ok := ctx.Get(constants.CTXFLG_ROLE); ok {
return v.(constants.Role)
func getRole(ctx *gin.Context) eum.Role {
if v, ok := ctx.Get(eum.CtxRole); ok {
return v.(eum.Role)
}
return constants.ROLE_GUEST
return eum.RoleGuest
}
func getUserName(ctx *gin.Context) string {
return ctx.GetString(constants.CTXFLG_USER_NAME)
return ctx.GetString(eum.CtxUserName)
}
func isAdmin(ctx *gin.Context) bool {
return getRole(ctx) <= constants.ROLE_ADMIN
return getRole(ctx) <= eum.RoleAdmin
}
func hasOprPermission(ctx *gin.Context, uuid int, op constants.OprPermission) bool {
func hasOprPermission(ctx *gin.Context, uuid int, op eum.OprPermission) bool {
return isAdmin(ctx) || reflect.ValueOf(repository.PermissionRepository.GetPermission(getUserName(ctx), uuid)).FieldByName(string(op)).Bool()
}

View File

@@ -4,7 +4,7 @@ import (
"errors"
"slices"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"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"
@@ -20,7 +20,7 @@ func (a *logApi) GetLog(ctx *gin.Context, req model.GetLogReq) any {
if isAdmin(ctx) {
return logic.LogLogicImpl.Search(req, req.FilterName...)
} else {
processNameList := repository.PermissionRepository.GetProcessNameByPermission(getUserName(ctx), constants.OPERATION_LOG)
processNameList := repository.PermissionRepository.GetProcessNameByPermission(getUserName(ctx), eum.OperationLog)
filterName := slices.DeleteFunc(req.FilterName, func(s string) bool {
return !slices.Contains(processNameList, s)
})

View File

@@ -5,6 +5,7 @@ import (
"time"
"github.com/google/uuid"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"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"
@@ -44,12 +45,18 @@ func (p *procApi) DeleteNewProcess(ctx *gin.Context, req struct {
func (p *procApi) KillProcess(ctx *gin.Context, req struct {
Uuid int `form:"uuid" binding:"required"`
}) (err error) {
if !hasOprPermission(ctx, req.Uuid, eum.OperationStop) {
return errors.New("not permission")
}
return logic.ProcessCtlLogic.KillProcess(req.Uuid)
}
func (p *procApi) StartProcess(ctx *gin.Context, req struct {
Uuid int `form:"uuid" binding:"required"`
Uuid int `json:"uuid" binding:"required"`
}) (err error) {
if !hasOprPermission(ctx, req.Uuid, eum.OperationStart) {
return errors.New("not permission")
}
prod, err := logic.ProcessCtlLogic.GetProcess(req.Uuid)
if err != nil { // 进程不存在则创建
proConfig, err := repository.ProcessRepository.GetProcessConfigById(req.Uuid)
@@ -63,7 +70,7 @@ func (p *procApi) StartProcess(ctx *gin.Context, req struct {
logic.ProcessCtlLogic.AddProcess(req.Uuid, proc)
return nil
}
if prod.State.State == 1 {
if prod.State.State == eum.ProcessStateStart || prod.State.State == eum.ProcessStateRunning {
return errors.New("process is currently running")
}
prod.ResetRestartTimes()

View File

@@ -4,7 +4,7 @@ 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/eum"
"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/utils"
@@ -27,7 +27,6 @@ func (u *userApi) LoginHandler(ctx *gin.Context, req model.LoginHandlerReq) any
return err
}
return gin.H{
"code": 0,
"token": token,
"username": req.Account,
"role": repository.UserRepository.GetUserByName(req.Account).Role,
@@ -35,10 +34,10 @@ func (u *userApi) LoginHandler(ctx *gin.Context, req model.LoginHandlerReq) any
}
func (u *userApi) CreateUser(ctx *gin.Context, req model.User) (err error) {
if req.Role == constants.ROLE_ROOT {
if req.Role == eum.RoleRoot {
return errors.New("creation of root accounts is forbidden")
}
if req.Account == constants.CONSOLE {
if req.Account == eum.Console {
return errors.New("operation failed")
}
if len(req.Password) < config.CF.UserPassWordMinLength {
@@ -50,7 +49,7 @@ func (u *userApi) CreateUser(ctx *gin.Context, req model.User) (err error) {
func (u *userApi) ChangePassword(ctx *gin.Context, req model.User) (err error) {
reqUser := getUserName(ctx)
if getRole(ctx) != constants.ROLE_ROOT && req.Account != "" {
if getRole(ctx) != eum.RoleRoot && req.Account != "" {
return errors.New("invalid parameters")
}
var userName string
@@ -84,7 +83,7 @@ func (u *userApi) checkLoginInfo(account, password string) bool {
repository.UserRepository.CreateUser(model.User{
Account: "root",
Password: DEFAULT_ROOT_PASSWORD,
Role: constants.ROLE_ROOT,
Role: eum.RoleRoot,
})
return password == DEFAULT_ROOT_PASSWORD
}

View File

@@ -3,12 +3,13 @@ package api
import (
"context"
"errors"
"net/http"
"strconv"
"sync"
"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/eum"
"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"
@@ -46,9 +47,15 @@ func (w *WsConnetInstance) Cancel() {
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true // 允许所有跨域请求
},
}
func (w *wsApi) WebsocketHandle(ctx *gin.Context, req model.WebsocketHandleReq) (err error) {
if !hasOprPermission(ctx, req.Uuid, eum.OperationTerminal) {
return errors.New("not permission")
}
reqUser := getUserName(ctx)
proc, err := logic.ProcessCtlLogic.GetProcess(req.Uuid)
if err != nil {
@@ -64,7 +71,7 @@ func (w *wsApi) WebsocketHandle(ctx *gin.Context, req model.WebsocketHandleReq)
if err != nil {
return err
}
defer conn.Close()
log.Logger.Infow("ws连接成功")
wsCtx, cancel := context.WithCancel(context.Background())
@@ -72,11 +79,13 @@ func (w *wsApi) WebsocketHandle(ctx *gin.Context, req model.WebsocketHandleReq)
WsConnect: conn,
CancelFunc: cancel,
wsLock: sync.Mutex{},
}
if err := proc.ReadCache(wci); err != nil {
return nil
}
proc.ReadCache(wci)
if proc.State.State == 1 {
if proc.State.State == eum.ProcessStateRunning {
proc.SetTerminalSize(req.Cols, req.Rows)
w.startWsConnect(wci, cancel, proc, hasOprPermission(ctx, req.Uuid, constants.OPERATION_TERMINAL_WRITE))
w.startWsConnect(wci, cancel, proc, hasOprPermission(ctx, req.Uuid, eum.OperationTerminalWrite))
proc.AddConn(reqUser, wci)
defer proc.DeleteConn(reqUser)
}
@@ -92,7 +101,6 @@ func (w *wsApi) WebsocketHandle(ctx *gin.Context, req model.WebsocketHandleReq)
case <-wsCtx.Done():
log.Logger.Infow("ws连接断开", "操作类型", "tcp连接建立已被关闭")
}
conn.Close()
return
}
@@ -112,7 +120,7 @@ func (w *wsApi) WebsocketShareHandle(ctx *gin.Context, req model.WebsocketHandle
if proc.HasWsConn(guestName) {
return errors.New("connection already exists")
}
if proc.State.State != 1 {
if proc.State.State != eum.ProcessStateRunning {
return errors.New("process not is running")
}
if !proc.VerifyControl() {
@@ -122,7 +130,7 @@ func (w *wsApi) WebsocketShareHandle(ctx *gin.Context, req model.WebsocketHandle
if err != nil {
return err
}
defer conn.Close()
log.Logger.Infow("ws连接成功")
data.UpdatedAt = time.Now()
repository.WsShare.Edit(data)
@@ -134,7 +142,9 @@ func (w *wsApi) WebsocketShareHandle(ctx *gin.Context, req model.WebsocketHandle
CancelFunc: cancel,
wsLock: sync.Mutex{},
}
proc.ReadCache(wci)
if err := proc.ReadCache(wci); err != nil {
return nil
}
w.startWsConnect(wci, cancel, proc, data.Write)
proc.AddConn(guestName, wci)
defer proc.DeleteConn(guestName)
@@ -152,7 +162,6 @@ func (w *wsApi) WebsocketShareHandle(ctx *gin.Context, req model.WebsocketHandle
case <-time.After(time.Until(data.ExpireTime)):
log.Logger.Infow("ws连接断开", "操作类型", "分享时间已结束")
}
conn.Close()
return
}

View File

@@ -1,6 +0,0 @@
package constants
const (
SECRET_KEY = "secret"
CONSOLE = "console"
)

View File

@@ -1,7 +0,0 @@
package constants
const (
CTXFLG_USER_NAME = "user"
CTXFLG_ROLE = "role"
CTXFLG_ERR = "err"
)

View File

@@ -1,11 +0,0 @@
package constants
type OprPermission string
const (
OPERATION_START OprPermission = "Start"
OPERATION_STOP OprPermission = "Stop"
OPERATION_TERMINAL OprPermission = "Terminal"
OPERATION_TERMINAL_WRITE OprPermission = "Write"
OPERATION_LOG OprPermission = "Log"
)

View File

@@ -1,16 +0,0 @@
package constants
type TerminalType string
const (
TERMINAL_PTY TerminalType = "pty"
TERMINAL_STD TerminalType = "std"
)
type ProcessState int32
const (
PROCESS_STOP ProcessState = iota
PROCESS_START
PROCESS_WARNNING
)

View File

@@ -1,10 +0,0 @@
package constants
type Role int
const (
ROLE_ROOT Role = iota
ROLE_ADMIN
ROLE_USER
ROLE_GUEST
)

View File

@@ -1,19 +0,0 @@
package constants
type Condition int
const (
RUNNING Condition = iota
NOT_RUNNING
EXCEPTION
PASS
)
type TaskOperation int
const (
TASK_START TaskOperation = iota
TASK_STOP
TASK_START_WAIT_DONE
TASK_STOP_WAIT_DONE
)

View File

@@ -0,0 +1,6 @@
package eum
const (
SecretKey = "secret"
Console = "console"
)

11
internal/app/eum/event.go Normal file
View File

@@ -0,0 +1,11 @@
package eum
type EventType string
const (
EventProcessStart EventType = "ProcessStart"
EventProcessStop EventType = "ProcessStop"
EventProcessWarning EventType = "ProcessWarning"
EventTaskStart EventType = "TaskStart"
EventTaskStop EventType = "TaskStop"
)

6
internal/app/eum/gin.go Normal file
View File

@@ -0,0 +1,6 @@
package eum
const (
CtxUserName = "user"
CtxRole = "role"
)

View File

@@ -0,0 +1,11 @@
package eum
type OprPermission string
const (
OperationStart OprPermission = "Start"
OperationStop OprPermission = "Stop"
OperationTerminal OprPermission = "Terminal"
OperationTerminalWrite OprPermission = "Write"
OperationLog OprPermission = "Log"
)

View File

@@ -0,0 +1,17 @@
package eum
type TerminalType string
const (
TerminalPty TerminalType = "pty"
TerminalStd TerminalType = "std"
)
type ProcessState int32
const (
ProcessStateStop ProcessState = iota
ProcessStateStart
ProcessStateWarnning
ProcessStateRunning
)

10
internal/app/eum/role.go Normal file
View File

@@ -0,0 +1,10 @@
package eum
type Role int
const (
RoleRoot Role = iota
RoleAdmin
RoleUser
RoleGuest
)

21
internal/app/eum/task.go Normal file
View File

@@ -0,0 +1,21 @@
package eum
type Condition int
const (
TaskCondRunning Condition = iota
TaskCondNotRunning
TaskCondException
TaskCondPass
)
type TaskOperation int
const (
TaskStart TaskOperation = iota
TaskStop
TaskStartWaitDone
TaskStopWaitDone
)
type CtxTaskTraceId struct{}

View File

@@ -0,0 +1,35 @@
package logic
import (
"time"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"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"
)
type eventLogic struct{}
var EventLogic = new(eventLogic)
func (e *eventLogic) Create(name string, eventType eum.EventType, additionalKv ...string) {
if len(additionalKv)%2 != 0 {
log.Logger.Errorw("参数长度错误", "args", additionalKv)
return
}
data := model.Event{
Name: name,
CreatedTime: time.Now(),
Type: eventType,
}
m := map[string]string{}
for i := range len(additionalKv) / 2 {
m[additionalKv[2*i]] = additionalKv[2*i+1]
}
data.Additional = utils.StructToJsonStr(m)
if err := repository.EventRepository.Create(data); err != nil {
log.Logger.Errorw("事件创建失败", "err", err)
}
}

View File

@@ -1,6 +1,7 @@
package logic
import (
"errors"
"fmt"
"os"
"strconv"
@@ -10,7 +11,7 @@ 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/eum"
"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"
@@ -20,14 +21,14 @@ import (
)
type Process interface {
ReadCache(ConnectInstance)
ReadCache(ConnectInstance) error
Write(string) error
WriteBytes([]byte) error
readInit()
doOnInit()
doOnKilled()
Start() error
Type() constants.TerminalType
Type() eum.TerminalType
SetTerminalSize(int, int)
}
@@ -58,7 +59,7 @@ type ProcessBase struct {
State struct {
startTime time.Time
Info string
State constants.ProcessState //0 为未运行1为运作中2为异常状态
State eum.ProcessState //0 为未运行1为运作中2为异常状态
stateLock sync.Mutex
restartTimes int
manualStopFlag bool
@@ -93,7 +94,7 @@ func (p *ProcessBase) watchDog() {
}
close(p.StopChan)
p.doOnKilled()
p.SetState(constants.PROCESS_STOP)
p.SetState(eum.ProcessStateStop)
if state.ExitCode() != 0 {
log.Logger.Infow("进程停止", "进程名称", p.Name, "exitCode", state.ExitCode(), "进程类型", p.Type())
p.push(fmt.Sprintf("进程停止,退出码 %d", state.ExitCode()))
@@ -117,7 +118,7 @@ func (p *ProcessBase) watchDog() {
return
}
log.Logger.Warnw("重启次数达到上限", "name", p.Name, "limit", config.CF.ProcessRestartsLimit)
p.SetState(constants.PROCESS_WARNNING)
p.SetState(eum.ProcessStateWarnning)
p.State.Info = "重启次数异常"
p.push("进程重启次数达到上限")
}
@@ -139,7 +140,7 @@ func (p *ProcessBase) pInit() {
}
// fn 函数执行成功的情况下对state赋值
func (p *ProcessBase) SetState(state constants.ProcessState, fn ...func() bool) bool {
func (p *ProcessBase) SetState(state eum.ProcessState, fn ...func() bool) bool {
p.State.stateLock.Lock()
defer p.State.stateLock.Unlock()
for _, v := range fn {
@@ -149,10 +150,26 @@ func (p *ProcessBase) SetState(state constants.ProcessState, fn ...func() bool)
}
p.State.State = state
middle.ProcessWaitCond.Trigger()
p.createEvent(state)
go TaskLogic.RunTaskByTriggerEvent(p.Name, state)
return true
}
func (p *ProcessBase) createEvent(state eum.ProcessState) {
var eventType eum.EventType
switch state {
case eum.ProcessStateRunning:
eventType = eum.EventProcessStart
case eum.ProcessStateStop:
eventType = eum.EventProcessStop
case eum.ProcessStateWarnning:
eventType = eum.EventProcessWarning
default:
return
}
EventLogic.Create(p.Name, eventType)
}
func (p *ProcessBase) GetUserString() string {
return strings.Join(p.GetUserList(), ";")
}
@@ -267,7 +284,7 @@ func (p *ProcessBase) monitorHanler() {
ticker := time.NewTicker(time.Second * time.Duration(config.CF.PerformanceInfoInterval))
defer ticker.Stop()
for {
if p.State.State != 1 {
if p.State.State != eum.ProcessStateRunning {
log.Logger.Debugw("进程未在运行", "state", p.State.State)
return
}
@@ -306,6 +323,10 @@ func (p *ProcessBase) initPsutil() {
}
func (p *ProcessBase) Kill() error {
if p.State.State != eum.ProcessStateRunning {
return errors.New("can't kill not running process")
}
p.State.manualStopFlag = true
p.op.Signal(syscall.SIGINT)
select {
case <-p.StopChan:

View File

@@ -4,11 +4,10 @@ import (
"errors"
"runtime"
"slices"
"strings"
"sync"
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/google/shlex"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"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"
@@ -37,10 +36,6 @@ func (p *processCtlLogic) KillProcess(uuid int) error {
if !ok {
return errors.New("进程类型错误")
}
if result.State.State != 1 {
return nil
}
result.State.manualStopFlag = true
return result.Kill()
}
@@ -61,13 +56,9 @@ func (p *processCtlLogic) KillAllProcess() {
wg := sync.WaitGroup{}
p.processMap.Range(func(key, value any) bool {
process := value.(*ProcessBase)
if process.State.State != 1 {
return true
}
wg.Add(1)
go func() {
defer wg.Done()
process.State.manualStopFlag = true
process.Kill()
}()
return true
@@ -76,7 +67,7 @@ func (p *processCtlLogic) KillAllProcess() {
}
func (p *processCtlLogic) KillAllProcessByUserName(userName string) {
stopPermissionProcess := repository.PermissionRepository.GetProcessNameByPermission(userName, constants.OPERATION_STOP)
stopPermissionProcess := repository.PermissionRepository.GetProcessNameByPermission(userName, eum.OperationStop)
wg := sync.WaitGroup{}
p.processMap.Range(func(key, value any) bool {
process := value.(*ProcessBase)
@@ -124,10 +115,8 @@ func (p *processCtlLogic) getProcessInfoList(processConfiglist []model.Process)
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.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
@@ -183,7 +172,7 @@ func (p *processCtlLogic) ProcessInit() {
}
func (p *processCtlLogic) ProcesStartAllByUsername(userName string) {
startPermissionProcess := repository.PermissionRepository.GetProcessNameByPermission(userName, constants.OPERATION_START)
startPermissionProcess := repository.PermissionRepository.GetProcessNameByPermission(userName, eum.OperationStart)
p.processMap.Range(func(key, value any) bool {
process := value.(*ProcessBase)
if !slices.Contains(startPermissionProcess, process.Name) {
@@ -217,7 +206,7 @@ func (p *processCtlLogic) UpdateProcessConfig(config model.Process) error {
result.Config.cpuLimit = config.CpuLimit
result.Config.AutoRestart = config.AutoRestart
result.Config.compulsoryRestart = config.CompulsoryRestart
result.StartCommand = strings.Fields(config.Cmd)
result.StartCommand = utils.UnwarpIgnore(shlex.Split(config.Cmd))
result.WorkDir = config.Cwd
result.Name = config.Name
return nil
@@ -225,9 +214,9 @@ func (p *processCtlLogic) UpdateProcessConfig(config model.Process) error {
func (p *processCtlLogic) NewProcess(config model.Process) (proc *ProcessBase, err error) {
switch config.TermType {
case constants.TERMINAL_STD:
case eum.TerminalStd:
proc = NewProcessStd(config)
case constants.TERMINAL_PTY:
case eum.TerminalPty:
proc = NewProcessPty(config)
default:
err = errors.New("终端类型错误")

View File

@@ -2,13 +2,14 @@ package logic
import (
"bytes"
"errors"
"os"
"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/eum"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/log"
"github.com/lzh-1625/go_process_manager/utils"
@@ -26,20 +27,20 @@ func (p *ProcessPty) doOnKilled() {
p.pty.Close()
}
func (p *ProcessPty) Type() constants.TerminalType {
return constants.TERMINAL_PTY
func (p *ProcessPty) Type() eum.TerminalType {
return eum.TerminalPty
}
func (p *ProcessPty) Start() (err error) {
defer func() {
if err != nil {
p.Config.AutoRestart = false
p.SetState(constants.PROCESS_WARNNING)
p.SetState(eum.ProcessStateWarnning)
p.State.Info = "进程启动失败:" + err.Error()
}
}()
if ok := p.SetState(constants.PROCESS_START, func() bool {
return p.State.State != 1
if ok := p.SetState(eum.ProcessStateStart, func() bool {
return p.State.State != eum.ProcessStateStart
}); !ok {
log.Logger.Warnw("进程已在运行,跳过启动")
return nil
@@ -113,10 +114,12 @@ func (p *ProcessPty) readInit() {
}
}
func (p *ProcessPty) ReadCache(ws ConnectInstance) {
if p.cacheBytesBuf != nil {
ws.Write(p.cacheBytesBuf.Bytes())
func (p *ProcessPty) ReadCache(ws ConnectInstance) error {
if p.cacheBytesBuf == nil {
return errors.New("cache is null")
}
ws.Write(p.cacheBytesBuf.Bytes())
return nil
}
func (p *ProcessPty) bufHanle(b []byte) {
@@ -139,10 +142,7 @@ func NewProcessPty(pconfig model.Process) *ProcessBase {
StartCommand: utils.UnwarpIgnore(shlex.Split(pconfig.Cmd)),
WorkDir: pconfig.Cwd,
}
processPty := ProcessPty{
ProcessBase: &p,
}
p.Process = &processPty
processPty.setProcessConfig(pconfig)
p.Process = &ProcessPty{ProcessBase: &p}
p.setProcessConfig(pconfig)
return &p
}

View File

@@ -2,11 +2,13 @@ package logic
import (
"bytes"
"errors"
"os"
"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/eum"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/log"
"github.com/lzh-1625/go_process_manager/utils"
@@ -24,20 +26,20 @@ func (p *ProcessPty) doOnKilled() {
p.pty.Close()
}
func (p *ProcessPty) Type() constants.TerminalType {
return constants.TERMINAL_PTY
func (p *ProcessPty) Type() eum.TerminalType {
return eum.TerminalPty
}
func (p *ProcessPty) Start() (err error) {
defer func() {
if err != nil {
p.Config.AutoRestart = false
p.SetState(constants.PROCESS_WARNNING)
p.SetState(eum.ProcessStateWarnning)
p.State.Info = "进程启动失败:" + err.Error()
}
}()
if ok := p.SetState(constants.PROCESS_START, func() bool {
return p.State.State != 1
if ok := p.SetState(eum.ProcessStateStart, func() bool {
return p.State.State != eum.ProcessStateRunning && p.State.State != eum.ProcessStateStart
}); !ok {
log.Logger.Warnw("进程已在运行,跳过启动")
return nil
@@ -66,6 +68,11 @@ func (p *ProcessPty) Start() (err error) {
}
log.Logger.Infow("进程启动成功", "进程名称", p.Name, "重启次数", p.State.restartTimes)
p.pInit()
if !p.SetState(eum.ProcessStateRunning, func() bool {
return p.State.State == eum.ProcessStateStart
}) {
return errors.New("状态异常启动失败")
}
p.push("进程启动成功")
return nil
}
@@ -115,8 +122,12 @@ func (p *ProcessPty) readInit() {
}
}
func (p *ProcessPty) ReadCache(ws ConnectInstance) {
func (p *ProcessPty) ReadCache(ws ConnectInstance) error {
if p.cacheBytesBuf == nil {
return errors.New("cache is null")
}
ws.Write(p.cacheBytesBuf.Bytes())
return nil
}
func (p *ProcessPty) bufHanle(b []byte) {
@@ -135,13 +146,10 @@ 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{
ProcessBase: &p,
}
p.Process = &processPty
processPty.setProcessConfig(pconfig)
p.Process = &ProcessPty{ProcessBase: &p}
p.setProcessConfig(pconfig)
return &p
}

View File

@@ -2,13 +2,14 @@ package logic
import (
"bufio"
"errors"
"io"
"os/exec"
"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/eum"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/log"
"github.com/lzh-1625/go_process_manager/utils"
@@ -21,8 +22,8 @@ type ProcessStd struct {
stdout *bufio.Scanner
}
func (p *ProcessStd) Type() constants.TerminalType {
return constants.TERMINAL_STD
func (p *ProcessStd) Type() eum.TerminalType {
return eum.TerminalStd
}
func (p *ProcessStd) WriteBytes(input []byte) (err error) {
@@ -41,12 +42,12 @@ func (p *ProcessStd) Start() (err error) {
defer func() {
if err != nil {
p.Config.AutoRestart = false
p.SetState(constants.PROCESS_WARNNING)
p.SetState(eum.ProcessStateWarnning)
p.State.Info = "进程启动失败:" + err.Error()
}
}()
if ok := p.SetState(constants.PROCESS_START, func() bool {
return p.State.State != 1
if ok := p.SetState(eum.ProcessStateStart, func() bool {
return p.State.State != eum.ProcessStateRunning && p.State.State != eum.ProcessStateStart
}); !ok {
log.Logger.Warnw("进程已在运行,跳过启动")
return nil
@@ -73,6 +74,11 @@ func (p *ProcessStd) Start() (err error) {
log.Logger.Infow("进程启动成功", "重启次数", p.State.restartTimes)
p.op = cmd.Process
p.pInit()
if !p.SetState(eum.ProcessStateRunning, func() bool {
return p.State.State == eum.ProcessStateStart
}) {
return errors.New("状态异常启动失败")
}
p.push("进程启动成功")
return nil
}
@@ -81,10 +87,14 @@ func (p *ProcessStd) doOnInit() {
p.cacheLine = make([]string, config.CF.ProcessMsgCacheLinesLimit)
}
func (p *ProcessStd) ReadCache(ws ConnectInstance) {
func (p *ProcessStd) ReadCache(ws ConnectInstance) error {
if len(p.cacheLine) == 0 {
return errors.New("cache is null")
}
for _, line := range p.cacheLine {
ws.WriteString(line)
}
return nil
}
func (p *ProcessStd) doOnKilled() {
@@ -137,10 +147,7 @@ func NewProcessStd(pconfig model.Process) *ProcessBase {
StartCommand: utils.UnwarpIgnore(shlex.Split(pconfig.Cmd)),
WorkDir: pconfig.Cwd,
}
processStd := ProcessStd{
ProcessBase: &p,
}
p.Process = &processStd
processStd.setProcessConfig(pconfig)
p.Process = &ProcessStd{ProcessBase: &p}
p.setProcessConfig(pconfig)
return &p
}

View File

@@ -4,7 +4,8 @@ import (
"context"
"time"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/google/uuid"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"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"
@@ -25,7 +26,7 @@ func NewTaskJob(data model.Task) (*TaskJob, error) {
TaskConfig: &data,
StartTime: time.Now(),
}
if data.CronExpression != "" {
if data.Enable && data.CronExpression != "" {
err := tj.InitCronHandle()
if err != nil {
log.Logger.Warnw("定时任务启动失败", "err", err, "task", data.Id)
@@ -35,6 +36,11 @@ func NewTaskJob(data model.Task) (*TaskJob, error) {
}
func (t *TaskJob) Run(ctx context.Context) {
if ctx.Value(eum.CtxTaskTraceId{}) == nil {
ctx = context.WithValue(ctx, eum.CtxTaskTraceId{}, uuid.NewString())
}
EventLogic.Create(t.TaskConfig.Name, eum.EventTaskStart, "traceId", ctx.Value(eum.CtxTaskTraceId{}).(string))
defer EventLogic.Create(t.TaskConfig.Name, eum.EventTaskStop, "traceId", ctx.Value(eum.CtxTaskTraceId{}).(string))
t.Running = true
middle.TaskWaitCond.Trigger()
defer func() {
@@ -43,7 +49,7 @@ func (t *TaskJob) Run(ctx context.Context) {
}()
var ok bool
// 判断条件是否满足
if t.TaskConfig.Condition == constants.PASS {
if t.TaskConfig.Condition == eum.TaskCondPass {
ok = true
} else {
proc, err := ProcessCtlLogic.GetProcess(t.TaskConfig.OperationTarget)
@@ -66,7 +72,7 @@ func (t *TaskJob) Run(ctx context.Context) {
// 执行操作
log.Logger.Infow("任务开始执行")
if !OperationHandle[t.TaskConfig.Operation](t.TaskConfig, proc) {
log.Logger.Errorw("任务执行失败")
log.Logger.Warnw("任务执行失败")
return
}
log.Logger.Infow("任务执行成功", "target", t.TaskConfig.OperationTarget)
@@ -114,6 +120,7 @@ func (t *TaskJob) InitCronHandle() error {
if err != nil {
return err
}
c.Start()
t.Cron = c
return nil
}

View File

@@ -4,50 +4,50 @@ 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/eum"
"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
var conditionHandle = map[eum.Condition]conditionFunc{
eum.TaskCondRunning: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State == eum.ProcessStateRunning
},
constants.NOT_RUNNING: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State != 1
eum.TaskCondNotRunning: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State != eum.ProcessStateRunning && proc.State.State != eum.ProcessStateStart
},
constants.EXCEPTION: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State == 2
eum.TaskCondException: func(data *model.Task, proc *ProcessBase) bool {
return proc.State.State == eum.ProcessStateWarnning
},
}
// 执行操作,返回结果是否成功
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("进程已在运行")
var OperationHandle = map[eum.TaskOperation]operationFunc{
eum.TaskStart: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State == eum.ProcessStateRunning || proc.State.State == eum.ProcessStateStart {
log.Logger.Debugw("进程已在运行", "proc", proc.Name)
return false
}
go proc.Start()
proc.Start()
return true
},
constants.TASK_START_WAIT_DONE: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State == 1 {
log.Logger.Debugw("进程已在运行")
eum.TaskStartWaitDone: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State == eum.ProcessStateRunning || proc.State.State == eum.ProcessStateStart {
log.Logger.Debugw("进程已在运行", "proc", proc.Name)
return false
}
if err := proc.Start(); err != nil {
log.Logger.Debugw("进程启动失败")
log.Logger.Debugw("进程启动失败", "proc", proc.Name)
return false
}
select {
case <-proc.StopChan:
log.Logger.Debugw("进程停止,任务完成")
log.Logger.Debugw("进程停止,任务完成", "proc", proc.Name)
return true
case <-time.After(time.Second * time.Duration(config.CF.TaskTimeout)):
log.Logger.Errorw("任务超时")
@@ -55,24 +55,22 @@ var OperationHandle = map[constants.TaskOperation]operationFunc{
}
},
constants.TASK_STOP: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State != 1 {
log.Logger.Debugw("进程未在运行")
eum.TaskStop: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State != eum.ProcessStateRunning {
log.Logger.Debugw("进程未在运行", "proc", proc.Name)
return false
}
log.Logger.Debugw("异步停止任务")
proc.State.manualStopFlag = true
log.Logger.Debugw("异步停止任务", "proc", proc.Name)
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("进程未在运行")
eum.TaskStopWaitDone: func(data *model.Task, proc *ProcessBase) bool {
if proc.State.State != eum.ProcessStateRunning {
log.Logger.Debugw("进程未在运行", "proc", proc.Name)
return false
}
log.Logger.Debugw("停止任务并等待结束")
proc.State.manualStopFlag = true
log.Logger.Debugw("停止任务并等待结束", "proc", proc.Name)
return proc.Kill() == nil
},
}

View File

@@ -5,7 +5,7 @@ import (
"errors"
"sync"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"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"
@@ -154,7 +154,7 @@ func (t *taskLogic) RunTaskByKey(key string) error {
return nil
}
func (t *taskLogic) RunTaskByTriggerEvent(processName string, event constants.ProcessState) {
func (t *taskLogic) RunTaskByTriggerEvent(processName string, event eum.ProcessState) {
taskList := repository.TaskRepository.GetTriggerTask(processName, event)
if len(taskList) == 0 {
return

View File

@@ -6,7 +6,7 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"github.com/lzh-1625/go_process_manager/log"
)
@@ -25,7 +25,7 @@ func Logger() gin.HandlerFunc {
logKv = append(logKv, "Status", ctx.Writer.Status())
logKv = append(logKv, "Path", path)
logKv = append(logKv, "耗时", fmt.Sprintf("%dms", time.Now().UnixMilli()-start.UnixMilli()))
if user, ok := ctx.Get(constants.CTXFLG_USER_NAME); ok {
if user, ok := ctx.Get(eum.CtxUserName); ok {
logKv = append(logKv, "user", user)
}
switch {

View File

@@ -1,39 +1,14 @@
package middle
import (
"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/internal/app/eum"
"github.com/gin-gonic/gin"
)
func RolePermission(needPermission constants.Role) func(ctx *gin.Context) {
func RolePermission(needPermission eum.Role) func(ctx *gin.Context) {
return func(ctx *gin.Context) {
if v, ok := ctx.Get(constants.CTXFLG_ROLE); !ok || v.(constants.Role) > needPermission {
rErr(ctx, -1, "Insufficient permissions; please check your access rights!", nil)
ctx.Abort()
return
}
ctx.Next()
}
}
func OprPermission(op constants.OprPermission) func(ctx *gin.Context) {
return func(ctx *gin.Context) {
uuid, err := strconv.Atoi(ctx.Query("uuid"))
if err != nil {
rErr(ctx, -1, "Invalid parameters!", nil)
ctx.Abort()
return
}
if v, ok := ctx.Get(constants.CTXFLG_ROLE); !ok || v.(constants.Role) <= constants.ROLE_ADMIN {
ctx.Next()
return
}
if !reflect.ValueOf(repository.PermissionRepository.GetPermission(ctx.GetString(constants.CTXFLG_USER_NAME), uuid)).FieldByName(string(op)).Bool() {
if v, ok := ctx.Get(eum.CtxRole); !ok || v.(eum.Role) > needPermission {
rErr(ctx, -1, "Insufficient permissions; please check your access rights!", nil)
ctx.Abort()
return

View File

@@ -1,11 +1,10 @@
package middle
import (
"errors"
"slices"
"strings"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"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"
@@ -46,36 +45,19 @@ func CheckToken() gin.HandlerFunc {
return strings.HasPrefix(c.Request.URL.Path, s)
}) {
var token string
if c.Request.Header.Get("token") != "" {
token = c.Request.Header.Get("token")
if c.Request.Header.Get("Authorization") != "" {
token = strings.TrimPrefix(c.Request.Header.Get("Authorization"), "bearer ")
} else {
token = c.Query("token")
}
if _, err := utils.VerifyToken(token); err != nil {
if mc, err := utils.VerifyToken(token); err != nil {
rErr(c, -2, "token校验失败", err)
return
}
if username, err := getUser(c); err != nil {
rErr(c, -1, "无法获取user信息", err)
} else {
c.Set(constants.CTXFLG_USER_NAME, username)
c.Set(constants.CTXFLG_ROLE, repository.UserRepository.GetUserByName(username).Role)
c.Set(eum.CtxUserName, mc.Username)
c.Set(eum.CtxRole, repository.UserRepository.GetUserByName(mc.Username).Role)
}
}
c.Next()
}
}
func getUser(ctx *gin.Context) (string, error) {
var token string
if ctx.Request.Header.Get("token") != "" {
token = ctx.Request.Header.Get("token")
} else {
token = ctx.Query("token")
}
if mc, err := utils.VerifyToken(token); err == nil && mc != nil {
return mc.Username, nil
} else {
return "", errors.Join(errors.New("用户信息获取失败"), err)
}
}

View File

@@ -43,7 +43,7 @@ func (p *waitCond) Trigger() {
}
func (p *waitCond) WaitGetMiddel(c *gin.Context) {
reqUser := c.GetHeader("token")
reqUser := c.GetHeader("Uuid")
defer p.timeMap.Store(reqUser, p.ts)
if ts, ok := p.timeMap.Load(reqUser); !ok || ts.(int64) > p.ts {
c.Next()

View File

@@ -0,0 +1,19 @@
package model
import (
"time"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
)
type Event struct {
Id uint64 `gorm:"primaryKey;autoIncrement;column:id" json:"id"`
Name string `gorm:"column:name" json:"name"`
Type eum.EventType `gorm:"column:type" json:"type"`
Additional string `gorm:"column:additional" json:"additional"`
CreatedTime time.Time `gorm:"column:created_time" json:"createdTime"`
}
func (*Event) TableName() string {
return "event"
}

View File

@@ -1,18 +1,18 @@
package model
import "github.com/lzh-1625/go_process_manager/internal/app/constants"
import "github.com/lzh-1625/go_process_manager/internal/app/eum"
type ProcessInfo struct {
Name string `json:"name"`
Uuid int `json:"uuid"`
StartTime string `json:"startTime"`
User string `json:"user"`
Usage Usage `json:"usage"`
State State `json:"state"`
TermType constants.TerminalType `json:"termType"`
CgroupEnable bool `json:"cgroupEnable"`
MemoryLimit *float32 `json:"memoryLimit"`
CpuLimit *float32 `json:"cpuLimit"`
Name string `json:"name"`
Uuid int `json:"uuid"`
StartTime string `json:"startTime"`
User string `json:"user"`
Usage Usage `json:"usage"`
State State `json:"state"`
TermType eum.TerminalType `json:"termType"`
CgroupEnable bool `json:"cgroupEnable"`
MemoryLimit *float32 `json:"memoryLimit"`
CpuLimit *float32 `json:"cpuLimit"`
}
type Usage struct {
@@ -24,6 +24,6 @@ type Usage struct {
}
type State struct {
State constants.ProcessState `json:"state"`
Info string `json:"info"`
State eum.ProcessState `json:"state"`
Info string `json:"info"`
}

View File

@@ -1,20 +1,20 @@
package model
import "github.com/lzh-1625/go_process_manager/internal/app/constants"
import "github.com/lzh-1625/go_process_manager/internal/app/eum"
type Process struct {
Uuid int `gorm:"primaryKey;autoIncrement;column:uuid" json:"uuid"`
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"`
CompulsoryRestart bool `gorm:"column:compulsory_restart" json:"compulsoryRestart"`
PushIds string `gorm:"column:push_ids" json:"pushIds"`
LogReport bool `gorm:"column:log_report" json:"logReport"`
TermType constants.TerminalType `gorm:"column:term_type" json:"termType"`
CgroupEnable bool `gorm:"column:cgroup_enable" json:"cgroupEnable"`
MemoryLimit *float32 `gorm:"column:memory_limit" json:"memoryLimit"`
CpuLimit *float32 `gorm:"column:cpu_limit" json:"cpuLimit"`
Uuid int `gorm:"primaryKey;autoIncrement;column:uuid" json:"uuid"`
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"`
CompulsoryRestart bool `gorm:"column:compulsory_restart" json:"compulsoryRestart"`
PushIds string `gorm:"column:push_ids;type:json" json:"pushIds"`
LogReport bool `gorm:"column:log_report" json:"logReport"`
TermType eum.TerminalType `gorm:"column:term_type" json:"termType"`
CgroupEnable bool `gorm:"column:cgroup_enable" json:"cgroupEnable"`
MemoryLimit *float32 `gorm:"column:memory_limit" json:"memoryLimit"`
CpuLimit *float32 `gorm:"column:cpu_limit" json:"cpuLimit"`
}
func (*Process) TableName() string {

View File

@@ -3,22 +3,23 @@ package model
import (
"time"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
)
type Task struct {
Id int `gorm:"column:id;NOT NULL;primaryKey;autoIncrement;" json:"id" `
ProcessId int `gorm:"column:process_id;NOT NULL" json:"processId" `
Condition constants.Condition `gorm:"column:condition;NOT NULL" json:"condition" `
NextId *int `gorm:"column:next_id;" json:"nextId" `
Operation constants.TaskOperation `gorm:"column:operation;NOT NULL" json:"operation" `
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" `
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" `
Id int `gorm:"column:id;NOT NULL;primaryKey;autoIncrement;" json:"id" `
Name string `gorm:"column:name" json:"name" `
ProcessId int `gorm:"column:process_id;NOT NULL" json:"processId" `
Condition eum.Condition `gorm:"column:condition;NOT NULL" json:"condition" `
NextId *int `gorm:"column:next_id;" json:"nextId" `
Operation eum.TaskOperation `gorm:"column:operation;NOT NULL" json:"operation" `
TriggerEvent *eum.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" `
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" `
}
func (*Task) TableName() string {

View File

@@ -3,15 +3,15 @@ package model
import (
"time"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
)
type User struct {
Account string `json:"account" gorm:"primaryKey;column:account" `
Password string `json:"password" gorm:"column:password" `
Role constants.Role `json:"role" gorm:"column:role" `
CreateTime time.Time `json:"createTime" gorm:"column:create_time" `
Remark string `json:"remark" gorm:"column:remark" `
Account string `json:"account" gorm:"primaryKey;column:account" `
Password string `json:"password" gorm:"column:password" `
Role eum.Role `json:"role" gorm:"column:role" `
CreateTime time.Time `json:"createTime" gorm:"column:create_time" `
Remark string `json:"remark" gorm:"column:remark" `
}
func (*User) TableName() string {

View File

@@ -41,15 +41,18 @@ func InitDb() {
}
sqlDB.SetConnMaxLifetime(time.Hour)
db = gdb.Session(&defaultConfig)
// db = db.Debug()
db.AutoMigrate(&model.Process{}, &model.User{}, &model.Permission{}, &model.Push{}, &model.Config{}, &model.ProcessLog{}, &model.Task{}, &model.WsShare{})
// g := gen.NewGenerator(gen.Config{
// OutPath: "internal/app/repository/query",
// Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
// })
// g.UseDB(db)
// g.ApplyBasic(&model.Process{}, &model.User{}, &model.Permission{}, &model.Push{}, &model.Config{}, &model.ProcessLog{}, &model.Task{}, &model.WsShare{})
// g.Execute()
db = db.Debug()
db.AutoMigrate(
&model.Process{},
&model.User{},
&model.Permission{},
&model.Push{},
&model.Config{},
&model.ProcessLog{},
&model.Task{},
&model.WsShare{},
&model.Event{},
)
gormGen(db)
query.SetDefault(db)
}

View File

@@ -0,0 +1,14 @@
package repository
import (
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/internal/app/repository/query"
)
type eventRepository struct{}
var EventRepository = new(eventRepository)
func (e *eventRepository) Create(event model.Event) error {
return query.Event.Create(&event)
}

View File

@@ -0,0 +1,33 @@
//go:build gen
// +build gen
package repository
import (
"os"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"gorm.io/gen"
"gorm.io/gorm"
)
func gormGen(db *gorm.DB) {
g := gen.NewGenerator(gen.Config{
OutPath: "internal/app/repository/query",
Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
})
g.UseDB(db)
g.ApplyBasic(
&model.Process{},
&model.User{},
&model.Permission{},
&model.Push{},
&model.Config{},
&model.ProcessLog{},
&model.Task{},
&model.WsShare{},
&model.Event{},
)
g.Execute()
os.Exit(0)
}

View File

@@ -0,0 +1,10 @@
//go:build !gen
// +build !gen
package repository
import "gorm.io/gorm"
func gormGen(*gorm.DB) {
}

View File

@@ -3,7 +3,7 @@ package repository
import (
"errors"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"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/log"
@@ -64,18 +64,18 @@ func (p *permissionRepository) GetPermission(user string, pid int) (result model
return
}
func (p *permissionRepository) GetProcessNameByPermission(user string, op constants.OprPermission) (result []string) {
func (p *permissionRepository) GetProcessNameByPermission(user string, op eum.OprPermission) (result []string) {
tx := query.Permission.Select(query.Process.Name).RightJoin(query.Process, query.Process.Uuid.EqCol(query.Permission.Pid)).Where(query.Permission.Account.Eq(user)).Where(query.Permission.Owned.Is(true))
switch op {
case constants.OPERATION_LOG:
case eum.OperationLog:
tx = tx.Where(query.Permission.Log.Is(true))
case constants.OPERATION_START:
case eum.OperationStart:
tx = tx.Where(query.Permission.Start.Is(true))
case constants.OPERATION_STOP:
case eum.OperationStop:
tx = tx.Where(query.Permission.Stop.Is(true))
case constants.OPERATION_TERMINAL:
case eum.OperationTerminal:
tx = tx.Where(query.Permission.Terminal.Is(true))
case constants.OPERATION_TERMINAL_WRITE:
case eum.OperationTerminalWrite:
tx = tx.Where(query.Permission.Write.Is(true))
}
tx.Scan(&result)

View File

@@ -15,7 +15,7 @@ func (p *processRepository) GetAllProcessConfig() []model.Process {
tx := db.Find(&result)
if tx.Error != nil {
log.Logger.Error(tx.Error)
return []model.Process{}
return nil
}
return result
}
@@ -28,7 +28,7 @@ func (p *processRepository) GetProcessConfigByUser(username string) []model.Proc
Scan(&result)
if err != nil {
log.Logger.Error(err)
return []model.Process{}
return nil
}
return result
}
@@ -44,9 +44,8 @@ func (p *processRepository) AddProcessConfig(process model.Process) (id int, err
}
func (p *processRepository) DeleteProcessConfig(uuid int) error {
return db.Delete(&model.Process{
Uuid: uuid,
}).Error
_, err := query.Process.Where(query.Process.Uuid.Eq(uuid)).Delete()
return err
}
func (p *processRepository) GetProcessConfigById(uuid int) (data model.Process, err error) {

View File

@@ -0,0 +1,398 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"database/sql"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
)
func newEvent(db *gorm.DB, opts ...gen.DOOption) event {
_event := event{}
_event.eventDo.UseDB(db, opts...)
_event.eventDo.UseModel(&model.Event{})
tableName := _event.eventDo.TableName()
_event.ALL = field.NewAsterisk(tableName)
_event.Id = field.NewUint64(tableName, "id")
_event.Name = field.NewString(tableName, "name")
_event.Type = field.NewString(tableName, "type")
_event.Additional = field.NewString(tableName, "additional")
_event.CreatedTime = field.NewTime(tableName, "created_time")
_event.fillFieldMap()
return _event
}
type event struct {
eventDo
ALL field.Asterisk
Id field.Uint64
Name field.String
Type field.String
Additional field.String
CreatedTime field.Time
fieldMap map[string]field.Expr
}
func (e event) Table(newTableName string) *event {
e.eventDo.UseTable(newTableName)
return e.updateTableName(newTableName)
}
func (e event) As(alias string) *event {
e.eventDo.DO = *(e.eventDo.As(alias).(*gen.DO))
return e.updateTableName(alias)
}
func (e *event) updateTableName(table string) *event {
e.ALL = field.NewAsterisk(table)
e.Id = field.NewUint64(table, "id")
e.Name = field.NewString(table, "name")
e.Type = field.NewString(table, "type")
e.Additional = field.NewString(table, "additional")
e.CreatedTime = field.NewTime(table, "created_time")
e.fillFieldMap()
return e
}
func (e *event) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := e.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (e *event) fillFieldMap() {
e.fieldMap = make(map[string]field.Expr, 5)
e.fieldMap["id"] = e.Id
e.fieldMap["name"] = e.Name
e.fieldMap["type"] = e.Type
e.fieldMap["additional"] = e.Additional
e.fieldMap["created_time"] = e.CreatedTime
}
func (e event) clone(db *gorm.DB) event {
e.eventDo.ReplaceConnPool(db.Statement.ConnPool)
return e
}
func (e event) replaceDB(db *gorm.DB) event {
e.eventDo.ReplaceDB(db)
return e
}
type eventDo struct{ gen.DO }
type IEventDo interface {
gen.SubQuery
Debug() IEventDo
WithContext(ctx context.Context) IEventDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IEventDo
WriteDB() IEventDo
As(alias string) gen.Dao
Session(config *gorm.Session) IEventDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IEventDo
Not(conds ...gen.Condition) IEventDo
Or(conds ...gen.Condition) IEventDo
Select(conds ...field.Expr) IEventDo
Where(conds ...gen.Condition) IEventDo
Order(conds ...field.Expr) IEventDo
Distinct(cols ...field.Expr) IEventDo
Omit(cols ...field.Expr) IEventDo
Join(table schema.Tabler, on ...field.Expr) IEventDo
LeftJoin(table schema.Tabler, on ...field.Expr) IEventDo
RightJoin(table schema.Tabler, on ...field.Expr) IEventDo
Group(cols ...field.Expr) IEventDo
Having(conds ...gen.Condition) IEventDo
Limit(limit int) IEventDo
Offset(offset int) IEventDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IEventDo
Unscoped() IEventDo
Create(values ...*model.Event) error
CreateInBatches(values []*model.Event, batchSize int) error
Save(values ...*model.Event) error
First() (*model.Event, error)
Take() (*model.Event, error)
Last() (*model.Event, error)
Find() ([]*model.Event, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Event, err error)
FindInBatches(result *[]*model.Event, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.Event) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IEventDo
Assign(attrs ...field.AssignExpr) IEventDo
Joins(fields ...field.RelationField) IEventDo
Preload(fields ...field.RelationField) IEventDo
FirstOrInit() (*model.Event, error)
FirstOrCreate() (*model.Event, error)
FindByPage(offset int, limit int) (result []*model.Event, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Rows() (*sql.Rows, error)
Row() *sql.Row
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IEventDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (e eventDo) Debug() IEventDo {
return e.withDO(e.DO.Debug())
}
func (e eventDo) WithContext(ctx context.Context) IEventDo {
return e.withDO(e.DO.WithContext(ctx))
}
func (e eventDo) ReadDB() IEventDo {
return e.Clauses(dbresolver.Read)
}
func (e eventDo) WriteDB() IEventDo {
return e.Clauses(dbresolver.Write)
}
func (e eventDo) Session(config *gorm.Session) IEventDo {
return e.withDO(e.DO.Session(config))
}
func (e eventDo) Clauses(conds ...clause.Expression) IEventDo {
return e.withDO(e.DO.Clauses(conds...))
}
func (e eventDo) Returning(value interface{}, columns ...string) IEventDo {
return e.withDO(e.DO.Returning(value, columns...))
}
func (e eventDo) Not(conds ...gen.Condition) IEventDo {
return e.withDO(e.DO.Not(conds...))
}
func (e eventDo) Or(conds ...gen.Condition) IEventDo {
return e.withDO(e.DO.Or(conds...))
}
func (e eventDo) Select(conds ...field.Expr) IEventDo {
return e.withDO(e.DO.Select(conds...))
}
func (e eventDo) Where(conds ...gen.Condition) IEventDo {
return e.withDO(e.DO.Where(conds...))
}
func (e eventDo) Order(conds ...field.Expr) IEventDo {
return e.withDO(e.DO.Order(conds...))
}
func (e eventDo) Distinct(cols ...field.Expr) IEventDo {
return e.withDO(e.DO.Distinct(cols...))
}
func (e eventDo) Omit(cols ...field.Expr) IEventDo {
return e.withDO(e.DO.Omit(cols...))
}
func (e eventDo) Join(table schema.Tabler, on ...field.Expr) IEventDo {
return e.withDO(e.DO.Join(table, on...))
}
func (e eventDo) LeftJoin(table schema.Tabler, on ...field.Expr) IEventDo {
return e.withDO(e.DO.LeftJoin(table, on...))
}
func (e eventDo) RightJoin(table schema.Tabler, on ...field.Expr) IEventDo {
return e.withDO(e.DO.RightJoin(table, on...))
}
func (e eventDo) Group(cols ...field.Expr) IEventDo {
return e.withDO(e.DO.Group(cols...))
}
func (e eventDo) Having(conds ...gen.Condition) IEventDo {
return e.withDO(e.DO.Having(conds...))
}
func (e eventDo) Limit(limit int) IEventDo {
return e.withDO(e.DO.Limit(limit))
}
func (e eventDo) Offset(offset int) IEventDo {
return e.withDO(e.DO.Offset(offset))
}
func (e eventDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IEventDo {
return e.withDO(e.DO.Scopes(funcs...))
}
func (e eventDo) Unscoped() IEventDo {
return e.withDO(e.DO.Unscoped())
}
func (e eventDo) Create(values ...*model.Event) error {
if len(values) == 0 {
return nil
}
return e.DO.Create(values)
}
func (e eventDo) CreateInBatches(values []*model.Event, batchSize int) error {
return e.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (e eventDo) Save(values ...*model.Event) error {
if len(values) == 0 {
return nil
}
return e.DO.Save(values)
}
func (e eventDo) First() (*model.Event, error) {
if result, err := e.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.Event), nil
}
}
func (e eventDo) Take() (*model.Event, error) {
if result, err := e.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.Event), nil
}
}
func (e eventDo) Last() (*model.Event, error) {
if result, err := e.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.Event), nil
}
}
func (e eventDo) Find() ([]*model.Event, error) {
result, err := e.DO.Find()
return result.([]*model.Event), err
}
func (e eventDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Event, err error) {
buf := make([]*model.Event, 0, batchSize)
err = e.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (e eventDo) FindInBatches(result *[]*model.Event, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return e.DO.FindInBatches(result, batchSize, fc)
}
func (e eventDo) Attrs(attrs ...field.AssignExpr) IEventDo {
return e.withDO(e.DO.Attrs(attrs...))
}
func (e eventDo) Assign(attrs ...field.AssignExpr) IEventDo {
return e.withDO(e.DO.Assign(attrs...))
}
func (e eventDo) Joins(fields ...field.RelationField) IEventDo {
for _, _f := range fields {
e = *e.withDO(e.DO.Joins(_f))
}
return &e
}
func (e eventDo) Preload(fields ...field.RelationField) IEventDo {
for _, _f := range fields {
e = *e.withDO(e.DO.Preload(_f))
}
return &e
}
func (e eventDo) FirstOrInit() (*model.Event, error) {
if result, err := e.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.Event), nil
}
}
func (e eventDo) FirstOrCreate() (*model.Event, error) {
if result, err := e.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.Event), nil
}
}
func (e eventDo) FindByPage(offset int, limit int) (result []*model.Event, count int64, err error) {
result, err = e.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = e.Offset(-1).Limit(-1).Count()
return
}
func (e eventDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = e.Count()
if err != nil {
return
}
err = e.Offset(offset).Limit(limit).Scan(result)
return
}
func (e eventDo) Scan(result interface{}) (err error) {
return e.DO.Scan(result)
}
func (e eventDo) Delete(models ...*model.Event) (result gen.ResultInfo, err error) {
return e.DO.Delete(models)
}
func (e *eventDo) withDO(do gen.Dao) *eventDo {
e.DO = *do.(*gen.DO)
return e
}

View File

@@ -18,6 +18,7 @@ import (
var (
Q = new(Query)
Config *config
Event *event
Permission *permission
Process *process
ProcessLog *processLog
@@ -30,6 +31,7 @@ var (
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
*Q = *Use(db, opts...)
Config = &Q.Config
Event = &Q.Event
Permission = &Q.Permission
Process = &Q.Process
ProcessLog = &Q.ProcessLog
@@ -43,6 +45,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
return &Query{
db: db,
Config: newConfig(db, opts...),
Event: newEvent(db, opts...),
Permission: newPermission(db, opts...),
Process: newProcess(db, opts...),
ProcessLog: newProcessLog(db, opts...),
@@ -57,6 +60,7 @@ type Query struct {
db *gorm.DB
Config config
Event event
Permission permission
Process process
ProcessLog processLog
@@ -72,6 +76,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
return &Query{
db: db,
Config: q.Config.clone(db),
Event: q.Event.clone(db),
Permission: q.Permission.clone(db),
Process: q.Process.clone(db),
ProcessLog: q.ProcessLog.clone(db),
@@ -94,6 +99,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
return &Query{
db: db,
Config: q.Config.replaceDB(db),
Event: q.Event.replaceDB(db),
Permission: q.Permission.replaceDB(db),
Process: q.Process.replaceDB(db),
ProcessLog: q.ProcessLog.replaceDB(db),
@@ -106,6 +112,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
type queryCtx struct {
Config IConfigDo
Event IEventDo
Permission IPermissionDo
Process IProcessDo
ProcessLog IProcessLogDo
@@ -118,6 +125,7 @@ type queryCtx struct {
func (q *Query) WithContext(ctx context.Context) *queryCtx {
return &queryCtx{
Config: q.Config.WithContext(ctx),
Event: q.Event.WithContext(ctx),
Permission: q.Permission.WithContext(ctx),
Process: q.Process.WithContext(ctx),
ProcessLog: q.ProcessLog.WithContext(ctx),

View File

@@ -28,6 +28,7 @@ func newTask(db *gorm.DB, opts ...gen.DOOption) task {
tableName := _task.taskDo.TableName()
_task.ALL = field.NewAsterisk(tableName)
_task.Id = field.NewInt(tableName, "id")
_task.Name = field.NewString(tableName, "name")
_task.ProcessId = field.NewInt(tableName, "process_id")
_task.Condition = field.NewInt(tableName, "condition")
_task.NextId = field.NewInt(tableName, "next_id")
@@ -35,7 +36,7 @@ func newTask(db *gorm.DB, opts ...gen.DOOption) task {
_task.TriggerEvent = field.NewInt32(tableName, "trigger_event")
_task.TriggerTarget = field.NewInt(tableName, "trigger_target")
_task.OperationTarget = field.NewInt(tableName, "operation_target")
_task.Cron = field.NewString(tableName, "cron")
_task.CronExpression = field.NewString(tableName, "cron")
_task.Enable = field.NewBool(tableName, "enable")
_task.ApiEnable = field.NewBool(tableName, "api_enable")
_task.Key = field.NewString(tableName, "key")
@@ -50,6 +51,7 @@ type task struct {
ALL field.Asterisk
Id field.Int
Name field.String
ProcessId field.Int
Condition field.Int
NextId field.Int
@@ -57,7 +59,7 @@ type task struct {
TriggerEvent field.Int32
TriggerTarget field.Int
OperationTarget field.Int
Cron field.String
CronExpression field.String
Enable field.Bool
ApiEnable field.Bool
Key field.String
@@ -78,6 +80,7 @@ func (t task) As(alias string) *task {
func (t *task) updateTableName(table string) *task {
t.ALL = field.NewAsterisk(table)
t.Id = field.NewInt(table, "id")
t.Name = field.NewString(table, "name")
t.ProcessId = field.NewInt(table, "process_id")
t.Condition = field.NewInt(table, "condition")
t.NextId = field.NewInt(table, "next_id")
@@ -85,7 +88,7 @@ func (t *task) updateTableName(table string) *task {
t.TriggerEvent = field.NewInt32(table, "trigger_event")
t.TriggerTarget = field.NewInt(table, "trigger_target")
t.OperationTarget = field.NewInt(table, "operation_target")
t.Cron = field.NewString(table, "cron")
t.CronExpression = field.NewString(table, "cron")
t.Enable = field.NewBool(table, "enable")
t.ApiEnable = field.NewBool(table, "api_enable")
t.Key = field.NewString(table, "key")
@@ -105,8 +108,9 @@ func (t *task) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (t *task) fillFieldMap() {
t.fieldMap = make(map[string]field.Expr, 12)
t.fieldMap = make(map[string]field.Expr, 13)
t.fieldMap["id"] = t.Id
t.fieldMap["name"] = t.Name
t.fieldMap["process_id"] = t.ProcessId
t.fieldMap["condition"] = t.Condition
t.fieldMap["next_id"] = t.NextId
@@ -114,7 +118,7 @@ func (t *task) fillFieldMap() {
t.fieldMap["trigger_event"] = t.TriggerEvent
t.fieldMap["trigger_target"] = t.TriggerTarget
t.fieldMap["operation_target"] = t.OperationTarget
t.fieldMap["cron"] = t.Cron
t.fieldMap["cron"] = t.CronExpression
t.fieldMap["enable"] = t.Enable
t.fieldMap["api_enable"] = t.ApiEnable
t.fieldMap["key"] = t.Key

View File

@@ -1,7 +1,7 @@
package repository
import (
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/internal/app/repository/query"
)
@@ -26,7 +26,7 @@ func (t *taskRepository) GetTaskByKey(key string) (result model.Task, err error)
}
func (t *taskRepository) AddTask(data model.Task) (taskId int, err error) {
err = db.Create(&data).Error
err = query.Task.Create(&data)
taskId = data.Id
return
}
@@ -64,7 +64,7 @@ func (t *taskRepository) GetAllTaskWithProcessName() (result []model.TaskVo) {
return
}
func (t *taskRepository) GetTriggerTask(processName string, event constants.ProcessState) []model.Task {
func (t *taskRepository) GetTriggerTask(processName string, event eum.ProcessState) []model.Task {
result := []model.Task{}
query.Task.Select(query.Task.ALL).
LeftJoin(query.Process, query.Process.Uuid.EqCol(query.Task.TriggerTarget)).

View File

@@ -6,7 +6,7 @@ import (
"github.com/lzh-1625/go_process_manager/config"
"github.com/lzh-1625/go_process_manager/internal/app/api"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"github.com/lzh-1625/go_process_manager/internal/app/middle"
"github.com/lzh-1625/go_process_manager/log"
"github.com/lzh-1625/go_process_manager/resources"
@@ -58,55 +58,55 @@ func routePathInit(r *gin.Engine) {
{
wsGroup := apiGroup.Group("/ws")
{
wsGroup.GET("", middle.OprPermission(constants.OPERATION_TERMINAL), bind(api.WsApi.WebsocketHandle, Query))
wsGroup.GET("", bind(api.WsApi.WebsocketHandle, Query))
wsGroup.GET("/share", bind(api.WsApi.WebsocketShareHandle, Query))
}
processGroup := apiGroup.Group("/process")
{
processGroup.DELETE("", middle.OprPermission(constants.OPERATION_STOP), bind(api.ProcApi.KillProcess, Query))
processGroup.DELETE("", 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("", bind(api.ProcApi.StartProcess, Body))
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))
processGroup.POST("/share", middle.RolePermission(eum.RoleAdmin), bind(api.ProcApi.ProcessCreateShare, Body))
processGroup.GET("/control", middle.RolePermission(eum.RoleRoot), middle.ProcessWaitCond.WaitTriggerMiddel, bind(api.ProcApi.ProcessControl, Query))
proConfigGroup := processGroup.Group("/config")
{
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))
proConfigGroup.POST("", middle.RolePermission(eum.RoleRoot), middle.ProcessWaitCond.WaitTriggerMiddel, bind(api.ProcApi.CreateNewProcess, Body))
proConfigGroup.DELETE("", middle.RolePermission(eum.RoleRoot), middle.ProcessWaitCond.WaitTriggerMiddel, bind(api.ProcApi.DeleteNewProcess, Query))
proConfigGroup.PUT("", middle.RolePermission(eum.RoleRoot), bind(api.ProcApi.UpdateProcessConfig, Body))
proConfigGroup.GET("", middle.RolePermission(eum.RoleAdmin), bind(api.ProcApi.GetProcessConfig, Query))
}
}
taskGroup := apiGroup.Group("/task")
{
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("", middle.RolePermission(eum.RoleAdmin), bind(api.TaskApi.GetTaskById, Query))
taskGroup.GET("/all", middle.RolePermission(eum.RoleAdmin), bind(api.TaskApi.GetTaskList, None))
taskGroup.GET("/all/wait", middle.RolePermission(eum.RoleAdmin), middle.TaskWaitCond.WaitGetMiddel, bind(api.TaskApi.GetTaskList, None))
taskGroup.POST("", middle.RolePermission(eum.RoleAdmin), middle.TaskWaitCond.WaitTriggerMiddel, bind(api.TaskApi.CreateTask, Body))
taskGroup.DELETE("", middle.RolePermission(eum.RoleAdmin), middle.TaskWaitCond.WaitTriggerMiddel, bind(api.TaskApi.DeleteTaskById, Query))
taskGroup.PUT("", middle.RolePermission(eum.RoleAdmin), middle.TaskWaitCond.WaitTriggerMiddel, bind(api.TaskApi.EditTask, Body))
taskGroup.PUT("/enable", middle.RolePermission(eum.RoleAdmin), middle.TaskWaitCond.WaitTriggerMiddel, bind(api.TaskApi.EditTaskEnable, Body))
taskGroup.GET("/start", middle.RolePermission(eum.RoleAdmin), bind(api.TaskApi.StartTask, Query))
taskGroup.GET("/stop", middle.RolePermission(eum.RoleAdmin), bind(api.TaskApi.StopTask, Query))
taskGroup.POST("/key", middle.RolePermission(eum.RoleAdmin), bind(api.TaskApi.CreateTaskApiKey, Body))
taskGroup.GET("/api-key/:key", bind(api.TaskApi.RunTaskByKey, None))
}
userGroup := apiGroup.Group("/user")
{
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))
userGroup.POST("", middle.RolePermission(eum.RoleRoot), bind(api.UserApi.CreateUser, Body))
userGroup.PUT("/password", middle.RolePermission(eum.RoleUser), bind(api.UserApi.ChangePassword, Body))
userGroup.DELETE("", middle.RolePermission(eum.RoleRoot), bind(api.UserApi.DeleteUser, Query))
userGroup.GET("", middle.RolePermission(eum.RoleRoot), bind(api.UserApi.GetUserList, None))
}
pushGroup := apiGroup.Group("/push").Use(middle.RolePermission(constants.ROLE_ADMIN))
pushGroup := apiGroup.Group("/push").Use(middle.RolePermission(eum.RoleAdmin))
{
pushGroup.GET("/list", bind(api.PushApi.GetPushList, None))
pushGroup.GET("", bind(api.PushApi.GetPushById, Query))
@@ -115,30 +115,30 @@ func routePathInit(r *gin.Engine) {
pushGroup.DELETE("", bind(api.PushApi.DeletePushConfig, Query))
}
fileGroup := apiGroup.Group("/file").Use(middle.RolePermission(constants.ROLE_ADMIN))
fileGroup := apiGroup.Group("/file").Use(middle.RolePermission(eum.RoleAdmin))
{
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 := apiGroup.Group("/permission").Use(middle.RolePermission(eum.RoleRoot))
{
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 := apiGroup.Group("/log").Use(middle.RolePermission(eum.RoleUser))
{
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 := apiGroup.Group("/config").Use(middle.RolePermission(eum.RoleRoot))
{
configGroup.GET("", bind(api.ConfigApi.GetSystemConfiguration, None))
configGroup.PUT("", bind(api.ConfigApi.SetSystemConfiguration, None))
configGroup.PUT("/log", bind(api.ConfigApi.LogConfigReload, None))
configGroup.GET("/reload", bind(api.ConfigApi.LogConfigReload, None))
}
}
}
@@ -184,7 +184,7 @@ func bind[T any, R any](fn func(*gin.Context, T) R, bindOption int) func(*gin.Co
})
return
}
case api.Response:
case *api.Response:
ctx.JSON(v.StatusCode, gin.H{
"data": v.Data,
"msg": v.Msg,

View File

@@ -4,7 +4,6 @@
package bleve
import (
"fmt"
"time"
"github.com/blevesearch/bleve/v2"
@@ -74,7 +73,6 @@ func (b *bleveSearch) Insert(logContent string, processName string, using string
}); 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) {

View File

@@ -3,7 +3,6 @@ package es
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
@@ -84,16 +83,15 @@ func (e *esSearch) Search(req model.GetLogReq, filterProcessName ...string) mode
for _, v := range sr.QueryStringAnalysis(req.Match.Log) {
switch v.Cond {
case sr.Match:
queryList = append(queryList, elastic.NewMatchQuery("log", v.Content))
queryList = append(queryList, elastic.NewMatchQuery("log", v.Content).Boost(2))
queryList = append(queryList, elastic.NewMatchPhraseQuery("log", v.Content))
case sr.NotMatch:
notQuery = append(notQuery, elastic.NewMatchQuery("log", v.Content))
notQuery = append(notQuery, elastic.NewMatchPhraseQuery("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 != "" {

View File

@@ -1,11 +1,12 @@
package search
import (
"fmt"
"strings"
"github.com/google/shlex"
"github.com/lzh-1625/go_process_manager/internal/app/model"
"github.com/lzh-1625/go_process_manager/log"
"github.com/lzh-1625/go_process_manager/utils"
)
type LogLogic interface {
@@ -47,7 +48,7 @@ func QueryStringAnalysis(s string) (query []Query) {
if strings.TrimSpace(s) == "" {
return
}
strList := strings.Split(s, " ")
strList := utils.UnwarpIgnore(shlex.Split(s))
for _, v := range strList {
switch {
case strings.HasPrefix(v, "!^"):
@@ -64,6 +65,5 @@ func QueryStringAnalysis(s string) (query []Query) {
query = append(query, Query{Match, v})
}
}
fmt.Printf("query: %v\n", query)
return
}

View File

@@ -6,7 +6,7 @@ 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/eum"
"github.com/lzh-1625/go_process_manager/internal/app/logic"
"github.com/lzh-1625/go_process_manager/log"
"github.com/lzh-1625/go_process_manager/utils"
@@ -49,7 +49,7 @@ func (t *tui) drawProcessList() {
i++
}
list.AddItem(v.Name, utils.NewKVStr().Add("user_name", v.User).Add("start_time", v.StartTime).Add("state", v.State.State).Build(), 'a'+rune(i), func() {
if v.State.State != 1 || v.TermType != constants.TERMINAL_PTY {
if v.State.State != 1 || v.TermType != eum.TerminalPty {
return
}
t.teminal(v.Uuid)
@@ -75,8 +75,8 @@ func (t *tui) teminal(uuid int) {
tci := &TermConnectInstance{
CancelFunc: cancel,
}
p.AddConn(constants.CONSOLE, tci)
defer p.DeleteConn(constants.CONSOLE)
p.AddConn(eum.Console, tci)
defer p.DeleteConn(eum.Console)
os.Stdin.Write([]byte("\033[H\033[2J")) // 清空屏幕
p.ReadCache(tci)
go t.startConnect(p, ctx, cancel)
@@ -91,11 +91,11 @@ func (t *tui) teminal(uuid int) {
func (t *tui) startConnect(p logic.Process, ctx context.Context, cancel context.CancelFunc) {
switch p.Type() {
case constants.TERMINAL_PTY:
case eum.TerminalPty:
{
t.ptyConnect(p, ctx, cancel)
}
case constants.TERMINAL_STD:
case eum.TerminalStd:
{
t.stdConnect(p, ctx, cancel)
}

View File

@@ -5,7 +5,7 @@ import (
"time"
_ "github.com/lzh-1625/go_process_manager/boot"
"github.com/lzh-1625/go_process_manager/internal/app/constants"
"github.com/lzh-1625/go_process_manager/internal/app/eum"
"github.com/lzh-1625/go_process_manager/internal/app/logic"
"github.com/lzh-1625/go_process_manager/internal/app/model"
@@ -32,7 +32,7 @@ func TestCgroup(t *testing.T) {
Name: "test",
Cmd: "bash",
Cwd: `/root`,
TermType: constants.TERMINAL_PTY,
TermType: eum.TerminalPty,
})
if err != nil {
t.FailNow()

View File

@@ -0,0 +1,4 @@
> 1%
last 2 versions
not dead
not ie 11

2
resources/.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
node_modules
npm-debug.log

5
resources/.editorconfig Normal file
View File

@@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

10
resources/.env.dev Normal file
View File

@@ -0,0 +1,10 @@
# Firebase 🔥
VITE_FIREBASE_API_KEY=
VITE_FIREBASE_AUTH_DOMAIN=
VITE_FIREBASE_PROJECT_ID=
VITE_FIREBASE_STORAGE_BUCKET=
VITE_FIREBASE_MESSAGING_SENDER_ID=
VITE_FIREBASE_APP_ID=
VITE_FIREBASE_MEASUREMENT_ID=

1
resources/.env.pro Normal file
View File

@@ -0,0 +1 @@
VITE_API_BASE_URL=https://api.example.com

7
resources/.env.template Normal file
View File

@@ -0,0 +1,7 @@
VITE_OPENAI_API_KEY = XXXXXXXXXXXX
VITE_UNSPLASH_ACCESS_KEY = XXXXXXXXXXXX
VITE_GITHUB_CLIENT_ID = XXXXXXXXXXXX
# Aruze TextToSpeech Key (required for tts)
VITE_TTS_KEY=XXXXXXXXXXXX
# Aruze TextToSpeech Region
VITE_TTS_REGION = XXXXXXXXXXXX

2
resources/.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

32
resources/.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
.DS_Store
node_modules
/dist
/.vite_cache
# local env files
.env
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Local Netlify folder
.netlify
.env
# yarn.lock
yarn.lock

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

13
resources/Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
# 构建阶段
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

6
resources/Dockerfile.dev Normal file
View File

@@ -0,0 +1,6 @@
FROM node:lts-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
EXPOSE 8080
CMD ["npm", "run", "dev"]

21
resources/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 jk.yang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

169
resources/README.jp.md Normal file
View File

@@ -0,0 +1,169 @@
<br><br>
<p align='center' >
<img src='/src/assets/logo.png' alt='Vuetify3' width='300'/>
</p>
<br><br>
<p align="center">
<a href="https://vuejs.org/">
<img src="https://img.shields.io/badge/vue-v3.2.47-brightgreen.svg" alt="vue">
</a>
<a href="https://vuetifyjs.com/">
<img src="https://img.shields.io/badge/vuetify-v3.1.13-blue.svg" alt="element-ui">
</a>
<a href="https://vitejs.dev/">
<img src="https://img.shields.io/badge/vite-v4.2.1-blueviolet.svg" alt="element-ui">
</a>
<a href="https://github.com/yangjiakai/lux-admin-vuetify3/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>
</p>
<h4 align='center'>
<a href="https://lux.vuetify3.com/">ライブ・デモ</a>
</h4>
<br>
<p align='center'>
<a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.md">English</a> | <a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.zh-CN.md">简体中文</a>| <b >日本語</b>
</p>
## 📖 序文
> 目標は、最も優れた Vuetify 3 の Admin オープンソーステンプレートを作成することです。
Vuetify の洗練されたテーマを基盤に、明確で効率的なプロジェクト構造を構築し、最新の技術フレームワークを統合しています。このプロジェクトは、さまざまな一般的な技術要件や機能に対応することを目指し、AI アシスタントを組み込むことで、よりインテリジェントな体験を提供します。さらに、すべてのページが複数のデバイスで適応的に表示されるようにし、シームレスなクロスプラットフォーム互換性を実現しています。
## 📖Other Versions
SPA Full Version: [lux-ui](https://github.com/yangjiakai/lux-nuxt3)
SPA Simplified i18n Version[lux-ui-i18n](https://github.com/yangjiakai/vuetify3-lux-admin-template-i18n)
SPA Simplified chinese Version[lux-ui-zh](https://github.com/yangjiakai/vuetify3-lux-admin-template-zh)
Nuxt3 version:
Nuxt3 Full Version [lux-nuxt3](https://github.com/yangjiakai/lux-nuxt3)
Nuxt3 Simplified Version [lux-nuxt3-template](https://github.com/yangjiakai/lux-nuxt3-template)
## 📖Documents
- 📖 [Document 1.0 Chinese](https://www.craft.me/s/tAMVv4hUxZIH6G)
## 📚 特徴
- 📖 [Vue 3.2](https://github.com/vuejs/core)
- 📖 [Vite 4.x](https://github.com/vitejs/vite)
- 📖 UI Framework [Vuetify 3](https://next.vuetifyjs.com/en/)
- 📖 TypeScript
- 📦 Component Auto Importing
- 🍍 [Pinia](https://pinia.vuejs.org/)
- 📔 `<script setup>`
- 📚 Use icons from any icon sets in [Iconify](https://icon-sets.iconify.design/)
- ☁️ Deploy on Netlify, zero-config
- 🔑 Firebase auth
- 📈 Echarts, ApexChart
- 🧭 Openai, Chatgpt
- 🌍 vue-i18n
- 📚 virtual-scroller , vuedraggable , perfect-scrollbar
- 📝 Rich Text Editor
- 📇 Responsive multi-platform adaptive
## 📈 Project Activity
![Alt](https://repobeats.axiom.co/api/embed/306361b2af1a8556f64a0a828e1726a94bff36f0.svg "Repobeats analytics image")
## 💬 連絡
- Email <a href="mailto:yjkbako@gmail.com">yjkbako@gmail.com</a>
- Twitter https://twitter.com/baibaixiang
- Wechat <img src='/src/assets/wechat-qrcode.png' alt='DashBoard' width='300' />
## 💌 プレビュー
<img src='/src/assets/previews/DashBoard.png' alt='DashBoard' />
<img src='/src/assets/previews/TaskBoard.png' alt='ChatGPT' />
<img src='/src/assets/previews/DataTable.png' alt='DataTable' />
<img src='/src/assets/previews/Todo.png' alt='ChatGPT' />
<img src='/src/assets/previews/ChatGPT.png' alt='ChatGPT' />
<img src='/src/assets/previews/Card.png' alt='Card' />
<img src='/src/assets/previews/Color.png' alt='Color' />
<img src='/src/assets/previews/Gradient.png' alt='Gradient' />
<img src='/src/assets/previews/Login.png' alt='ChatGPT' />
<img src='/src/assets/previews/Unsplash.png' alt='ChatGPT' />
<img src='/src/assets/previews/Unsplash2.png' alt='ChatGPT' />
<br>
## 📦 プリパック
### 🏷UI Frameworks
- [Vuetify3](https://next.vuetifyjs.com/en/) - Vuetify は、美しく手作りされた Vue コンポーネントで構成された、デザインスキル不要の UI フレームワークです。
### 🏷Icons
- [Iconify](https://iconify.design) - 任意のアイコンセットを使用 [🔍Icônes](https://icones.netlify.app/)
- [Pure CSS Icons via UnoCSS](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
### 🏷️ プラグイン
- [Vue Router4](https://router.vuejs.org/)
- [VueUse](https://github.com/antfu/vueuse) - 便利なコンポジション API 集
- [VuedDaggable](https://github.com/SortableJS/Vue.Draggable) - 配列モデルと同期したドラッグ&ドロップによる配置操作が可能
- [Vue-Masonry-Wall](https://github.com/DerYeger/yeger/tree/main/packages/vue-masonry-wall) -Vue 3 のレスポンシブな Masonry レイアウト SSR をサポートしています
- [Vue-Virtual-Scroller](https://github.com/Akryum/vue-virtual-scroller) - 超高速の任意のデータ量のスクロール
## 👻 今すぐ試す!
```
git clone https://github.com/yangjiakai/lux-admin-vuetify3.git
cd lux-admin-vuetify3
yarn install
yarn dev
```
## 👻Docker it!
1. 开发环境构建镜像:
```
docker-compose build dev
```
2. 启动开发环境:
```
docker-compose up dev
```
3. 生产环境构建镜像:
```
docker-compose build app
```
4. 启动生产环境:
```
docker-compose up app
```
### 🔑Set ApiKey
Find the `.env.template` file in the root directory, remove the `.template` suffix, and replace` VITE_OPENAI_API_KEY`, `VITE_UNSPLASH_ACCESS_KEY`, and `VITE_GITHUB_CLIENT_ID`, and `VITE_TTS_KEY` and `VITE_TTS_REGION` with your own keys.
> openai apikey https://platform.openai.com/account/api-keys
> unsplash apikey https://unsplash.com/oauth/applications
> github apikey https://github.com/settings/tokens
> azure textToSpeech : https://speech.microsoft.com/

171
resources/README.md Normal file
View File

@@ -0,0 +1,171 @@
<br><br>
<p align='center' >
<img src='/src/assets/logo.png' alt='Vuetify3' width='300'/>
</p>
<br><br>
<p align="center">
<a href="https://vuejs.org/">
<img src="https://img.shields.io/badge/vue-v3.2.47-brightgreen.svg" alt="vue">
</a>
<a href="https://vuetifyjs.com/">
<img src="https://img.shields.io/badge/vuetify-v3.1.13-blue.svg" alt="element-ui">
</a>
<a href="https://vitejs.dev/">
<img src="https://img.shields.io/badge/vite-v4.2.1-blueviolet.svg" alt="element-ui">
</a>
<a href="https://github.com/yangjiakai/lux-admin-vuetify3/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>
</p>
<h4 align='center'>
<a href="https://lux.vuetify3.com/">Live Demo</a>
</h4>
<br>
<p align='center'>
<b>English</b> | <a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.zh-CN.md">简体中文</a>| <a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.jp.md">日本語</a>
</p>
## 📖Introduction
> Goal: Creating the best Vuetify 3 Admin open-source template.
Built upon the elegant themes of Vuetify, we have established a clear and efficient project structure, integrating the latest technology frameworks. This project aims to address a wide range of common technical requirements and features, while incorporating an AI assistant for a more intelligent experience. Additionally, we ensure that all pages are adaptive across multiple devices, achieving a seamless cross-platform compatibility.
## 📖Other Versions
SPA Full Version: [lux-vuetify3](https://github.com/yangjiakai/lux-nuxt3)
SPA Simplified i18n Version[lux-vuetify3-i18n](https://github.com/yangjiakai/vuetify3-lux-admin-template-i18n)
SPA Simplified chinese Version[lux-vuetify3-zh](https://github.com/yangjiakai/vuetify3-lux-admin-template-zh)
Nuxt3 version:
Nuxt3 Full Version [lux-nuxt3](https://github.com/yangjiakai/lux-nuxt3)
Nuxt3 Simplified Version [lux-nuxt3-template](https://github.com/yangjiakai/lux-nuxt3-template)
## 📖Documents
- 📖 [Document 1.0 Chinese](https://www.craft.me/s/tAMVv4hUxZIH6G)
## 📚Features
- 📖 [Vue 3.2](https://github.com/vuejs/core)
- 📖 [Vite 4.x](https://github.com/vitejs/vite)
- 📖 UI Framework [Vuetify 3](https://next.vuetifyjs.com/en/)
- 📖 TypeScript
- 📦 Component Auto Importing
- 🍍 [Pinia](https://pinia.vuejs.org/)
- 📔 `<script setup>`
- 📚 Use icons from any icon sets in [Iconify](https://icon-sets.iconify.design/)
- ☁️ Deploy on Netlify, zero-config
- 🔑 Firebase auth
- 📈 Echarts, ApexChart
- 🧭 Openai, Chatgpt
- 🌍 vue-i18n
- 📚 virtual-scroller , vuedraggable , perfect-scrollbar
- 📝 Rich Text Editor
- 📇 Responsive multi-platform adaptive
## 📈 Project Activity
![Alt](https://repobeats.axiom.co/api/embed/306361b2af1a8556f64a0a828e1726a94bff36f0.svg "Repobeats analytics image")
## 💬Contact Me
- Email <a href="mailto:yjkbako@gmail.com">yjkbako@gmail.com</a>
- Twitter https://twitter.com/baibaixiang
- Wechat <img src='/src/assets/wechat-qrcode.png' alt='DashBoard' width='300' />
## 💌Preview
<img src='/src/assets/previews/DashBoard.png' alt='DashBoard' />
<img src='/src/assets/previews/TaskBoard.png' alt='ChatGPT' />
<img src='/src/assets/previews/DataTable.png' alt='DataTable' />
<img src='/src/assets/previews/Todo.png' alt='ChatGPT' />
<img src='/src/assets/previews/ChatGPT.png' alt='ChatGPT' />
<img src='/src/assets/previews/Card.png' alt='Card' />
<img src='/src/assets/previews/Color.png' alt='Color' />
<img src='/src/assets/previews/Gradient.png' alt='Gradient' />
<img src='/src/assets/previews/Login.png' alt='ChatGPT' />
<img src='/src/assets/previews/Unsplash.png' alt='ChatGPT' />
<img src='/src/assets/previews/Unsplash2.png' alt='ChatGPT' />
<br>
## 📦Pre-packed
### 🏷️ UI Frameworks
- [Vuetify3](https://next.vuetifyjs.com/en/) - Vuetify is a no design skills required UI Framework with beautifully handcrafted Vue Components.
### 🏷️ Icons
- [Iconify](https://iconify.design) - use icons from any icon sets [🔍Icônes](https://icones.netlify.app/)
- [Pure CSS Icons via UnoCSS](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
### 🏷️ Plugins
- [Vue Router4](https://router.vuejs.org/)
- [VueUse](https://github.com/antfu/vueuse) - collection of useful composition APIs
- [VuedDaggable](https://github.com/SortableJS/Vue.Draggable) - allowing drag-and-drop and synchronization with view model array.
- [Vue-Masonry-Wall](https://github.com/DerYeger/yeger/tree/main/packages/vue-masonry-wall) - Responsive masonry layout with SSR support and zero dependencies for Vue 3.
- [Vue-Virtual-Scroller](https://github.com/Akryum/vue-virtual-scroller) - Blazing fast scrolling of any amount of data
## 👻Try it now!
```
git clone https://github.com/yangjiakai/lux-admin-vuetify3.git
cd lux-admin-vuetify3
yarn install
yarn dev
```
## 👻Docker it!
1. Build the development environment image:
```
docker-compose build dev
```
2. Start the development environment:
```
docker-compose up dev
```
3. Build the production environment image:
```
docker-compose build app
```
4. Start the production environment:
```
docker-compose up app
```
这应该能解决实时更新的问题。如果您还有任何疑问或遇到其他问题,请随时告诉我。
### 🔑Set ApiKey
Find the `.env.template` file in the root directory, remove the `.template` suffix, and replace` VITE_OPENAI_API_KEY`, `VITE_UNSPLASH_ACCESS_KEY`, and `VITE_GITHUB_CLIENT_ID`, and `VITE_TTS_KEY` and `VITE_TTS_REGION` with your own keys.
> openai apikey https://platform.openai.com/account/api-keys
> unsplash apikey https://unsplash.com/oauth/applications
> github apikey https://github.com/settings/tokens
> azure textToSpeech : https://speech.microsoft.com/

210
resources/README.zh-CN.md Normal file
View File

@@ -0,0 +1,210 @@
<br><br>
<p align='center' >
<img src='/src/assets/logo.png' alt='Vuetify3' width='300'/>
</p>
<br><br>
<p align="center">
<a href="https://vuejs.org/">
<img src="https://img.shields.io/badge/vue-v3.2.47-brightgreen.svg" alt="vue">
</a>
<a href="https://vuetifyjs.com/">
<img src="https://img.shields.io/badge/vuetify-v3.1.13-blue.svg" alt="element-ui">
</a>
<a href="https://vitejs.dev/">
<img src="https://img.shields.io/badge/vite-v4.2.1-blueviolet.svg" alt="element-ui">
</a>
<a href="https://github.com/yangjiakai/lux-admin-vuetify3/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>
</p>
<h4 align='center'>
<a href="https://lux.vuetify3.com/">在线 Demo</a>
</h4>
<br>
<p align='center'>
<a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.md">English</a> | <b>简体中文</b>| <a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.jp.md">日本語</a>
</p>
## 📖 序文
> 目标创造最优秀的 vuetify3 的 Admin 开源模板
在 Vuetify 精美的主题基础上,我们构建了一个清晰且高效的项目逻辑架构,整合了最新的技术框架。本项目旨在实现各种常见的技术需求和功能,同时融合了 AI 助手,以提供更智能化的体验。此外,我们确保所有页面在多种设备上均能自适应展示,实现优雅的跨平台兼容性。
## 📖 其他版本
### SPA Version
SPA 完整版: [lux-vuetify3](https://github.com/yangjiakai/lux-nuxt3)
SPA 简化国际化模板 [lux-vuetify3-i18n](https://github.com/yangjiakai/vuetify3-lux-admin-template-i18n)
SPA 简化中文模板 [lux-vuetify3-zh](https://github.com/yangjiakai/vuetify3-lux-admin-template-zh)
### Nuxt3 Version
Nuxt3 完整版 [lux-nuxt3](https://github.com/yangjiakai/lux-nuxt3)
Nuxt3 简化版 [lux-nuxt3-template](https://github.com/yangjiakai/lux-nuxt3-template)
## 文档
- 📖 [中文版文档 1.0 ](https://www.craft.me/s/tAMVv4hUxZIH6G)
## 📚 特性
- 📖 [Vue 3.2](https://github.com/vuejs/core)
- 📖 [Vite 4.x](https://github.com/vitejs/vite)
- 📖 UI Framework [Vuetify 3](https://next.vuetifyjs.com/en/)
- 📖 TypeScript
- 📦 组件自动导入
- 🍍 通过 [Pinia](https://pinia.vuejs.org/)进行状态管理
- 📔 使用新的 `<script setup>` 语法
- 📚 使用任意的图标集 [Iconify](https://icon-sets.iconify.design/)
- ☁️ 零配置部署在 Netlify
- 🔑 Firebase 授权
- 📈 Echarts, ApexChart
- 🧭 Openai, Chatgpt 支持
- 🌍 vue-i18n 多语言支持
- 📚 virtual-scroller , vuedraggable , perfect-scrollbar
- 📝 富文本编辑器
- 📇 响应式多平台自适应
## 📈 项目活跃度
![Alt](https://repobeats.axiom.co/api/embed/306361b2af1a8556f64a0a828e1726a94bff36f0.svg "Repobeats analytics image")
## 💬 联络我
- 邮箱 <a href="mailto:yjkbako@gmail.com">yjkbako@gmail.com</a>
- 推特 https://twitter.com/baibaixiang
- 微信 <img src='/src/assets/wechat-qrcode.png' alt='DashBoard' width='300' />
## 💌 预览
<img src='/src/assets/previews/DashBoard.png' alt='DashBoard' />
<img src='/src/assets/previews/TaskBoard.png' alt='ChatGPT' />
<img src='/src/assets/previews/DataTable.png' alt='DataTable' />
<img src='/src/assets/previews/Todo.png' alt='ChatGPT' />
<img src='/src/assets/previews/ChatGPT.png' alt='ChatGPT' />
<img src='/src/assets/previews/Card.png' alt='Card' />
<img src='/src/assets/previews/Color.png' alt='Color' />
<img src='/src/assets/previews/Gradient.png' alt='Gradient' />
<img src='/src/assets/previews/Login.png' alt='ChatGPT' />
<img src='/src/assets/previews/Unsplash.png' alt='ChatGPT' />
<img src='/src/assets/previews/Unsplash2.png' alt='ChatGPT' />
<br>
## 📦Pre-packed
### 🏷UI 框架
- [Vuetify3](https://next.vuetifyjs.com/en/) - Vuetify 是一个不要求设计能力的 Vue 界面组件框架,自带了许多自行设计实现的 Vue 组件。
### 🏷Icons
- [Iconify](https://iconify.design) - 使用任意的图标集 [🔍Icônes](https://icones.netlify.app/)
- [Pure CSS Icons via UnoCSS](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
## 目标功能
- [x] 明暗主题切换 -- 完成
- [x] 主题色切换 -- 完成
- [x] 中日英三语言切换-- 完成
- [x] 整合 ChatGpt-- 完成
## 目标页面
### 认证相关
- [x] 登录 -- 完成
- [x] 注册 -- 完成
- [x] 验证邮件 -- 完成
- [ ] 密码重置 -- 施工中
### 公共页面
- [x] 404 -- 完成
- [x] 500 -- 施工中
- [x] 系统维护 -- 施工中
- [x] 常见问题 -- 施工中
### UI 相关
- [x] 瀑布流布局 -- 完成
- [x] 大数据虚拟列表 -- 完成
- [ ] 骨架屏 -- 施工中
### 功能页面
- [x] 任务版(拖拽功能) -- 完成
- [x] 任务列表() -- 施工中
### 站点仿写
- [ ] ......
### 🏷️ 插件
- [Vue Router4](https://router.vuejs.org/)
- [VueUse](https://github.com/antfu/vueuse) - 非常有用的组合式 API 合集
- [VuedDaggable](https://github.com/SortableJS/Vue.Draggable) - 允许进行与数组模型同步的拖拽放置操作
- [Vue-Masonry-Wall](https://github.com/DerYeger/yeger/tree/main/packages/vue-masonry-wall) - 是一种 Vue3 响应式,支持 SSR,且零依的的瀑布流布局方案
- [Vue-Virtual-Scroller](https://github.com/Akryum/vue-virtual-scroller) - 大数据快速虚拟滚动插件
## 👻 现在可以试试!
```
git clone https://github.com/yangjiakai/lux-admin-vuetify3.git
cd lux-admin-vuetify3
yarn install
yarn dev
```
## 👻Docker it!
1. 开发环境构建镜像:
```
docker-compose build dev
```
2. 启动开发环境:
```
docker-compose up dev
```
3. 生产环境构建镜像:
```
docker-compose build app
```
4. 启动生产环境:
```
docker-compose up app
```
### 🔑 配置 ApiKey
找到根目录下的`.env.template`文件,去掉`.template`后缀
把`VITE_OPENAI_API_KEY`,`VITE_UNSPLASH_ACCESS_KEY`,`VITE_GITHUB_CLIENT_ID`,`VITE_TTS_KEY` , `VITE_TTS_REGION`分别替换成你自己的
> openai apikey https://platform.openai.com/account/api-keys
> unsplash apikey https://unsplash.com/oauth/applications
> github apikey https://github.com/settings/tokens
> azure textToSpeech : https://speech.microsoft.com/

73
resources/auto-imports.d.ts vendored Normal file
View File

@@ -0,0 +1,73 @@
// Generated by 'unplugin-auto-import'
export {};
declare global {
const EffectScope: typeof import("vue")["EffectScope"];
const acceptHMRUpdate: typeof import("pinia")["acceptHMRUpdate"];
const computed: typeof import("vue")["computed"];
const createApp: typeof import("vue")["createApp"];
const createPinia: typeof import("pinia")["createPinia"];
const customRef: typeof import("vue")["customRef"];
const defineAsyncComponent: typeof import("vue")["defineAsyncComponent"];
const defineComponent: typeof import("vue")["defineComponent"];
const defineStore: typeof import("pinia")["defineStore"];
const effectScope: typeof import("vue")["effectScope"];
const getActivePinia: typeof import("pinia")["getActivePinia"];
const getCurrentInstance: typeof import("vue")["getCurrentInstance"];
const getCurrentScope: typeof import("vue")["getCurrentScope"];
const h: typeof import("vue")["h"];
const inject: typeof import("vue")["inject"];
const isProxy: typeof import("vue")["isProxy"];
const isReactive: typeof import("vue")["isReactive"];
const isReadonly: typeof import("vue")["isReadonly"];
const isRef: typeof import("vue")["isRef"];
const mapActions: typeof import("pinia")["mapActions"];
const mapGetters: typeof import("pinia")["mapGetters"];
const mapState: typeof import("pinia")["mapState"];
const mapStores: typeof import("pinia")["mapStores"];
const mapWritableState: typeof import("pinia")["mapWritableState"];
const markRaw: typeof import("vue")["markRaw"];
const nextTick: typeof import("vue")["nextTick"];
const onActivated: typeof import("vue")["onActivated"];
const onBeforeMount: typeof import("vue")["onBeforeMount"];
const onBeforeRouteLeave: typeof import("vue-router")["onBeforeRouteLeave"];
const onBeforeRouteUpdate: typeof import("vue-router")["onBeforeRouteUpdate"];
const onBeforeUnmount: typeof import("vue")["onBeforeUnmount"];
const onBeforeUpdate: typeof import("vue")["onBeforeUpdate"];
const onDeactivated: typeof import("vue")["onDeactivated"];
const onErrorCaptured: typeof import("vue")["onErrorCaptured"];
const onMounted: typeof import("vue")["onMounted"];
const onRenderTracked: typeof import("vue")["onRenderTracked"];
const onRenderTriggered: typeof import("vue")["onRenderTriggered"];
const onScopeDispose: typeof import("vue")["onScopeDispose"];
const onServerPrefetch: typeof import("vue")["onServerPrefetch"];
const onUnmounted: typeof import("vue")["onUnmounted"];
const onUpdated: typeof import("vue")["onUpdated"];
const provide: typeof import("vue")["provide"];
const reactive: typeof import("vue")["reactive"];
const readonly: typeof import("vue")["readonly"];
const ref: typeof import("vue")["ref"];
const resolveComponent: typeof import("vue")["resolveComponent"];
const resolveDirective: typeof import("vue")["resolveDirective"];
const setActivePinia: typeof import("pinia")["setActivePinia"];
const setMapStoreSuffix: typeof import("pinia")["setMapStoreSuffix"];
const shallowReactive: typeof import("vue")["shallowReactive"];
const shallowReadonly: typeof import("vue")["shallowReadonly"];
const shallowRef: typeof import("vue")["shallowRef"];
const storeToRefs: typeof import("pinia")["storeToRefs"];
const toRaw: typeof import("vue")["toRaw"];
const toRef: typeof import("vue")["toRef"];
const toRefs: typeof import("vue")["toRefs"];
const triggerRef: typeof import("vue")["triggerRef"];
const unref: typeof import("vue")["unref"];
const useAttrs: typeof import("vue")["useAttrs"];
const useCssModule: typeof import("vue")["useCssModule"];
const useCssVars: typeof import("vue")["useCssVars"];
const useLink: typeof import("vue-router")["useLink"];
const useRoute: typeof import("vue-router")["useRoute"];
const useRouter: typeof import("vue-router")["useRouter"];
const useSlots: typeof import("vue")["useSlots"];
const watch: typeof import("vue")["watch"];
const watchEffect: typeof import("vue")["watchEffect"];
const watchPostEffect: typeof import("vue")["watchPostEffect"];
const watchSyncEffect: typeof import("vue")["watchSyncEffect"];
}

View File

@@ -0,0 +1,25 @@
version: "3.8"
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"
dev:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/node_modules
ports:
- "8080:8080"
environment:
- NODE_ENV=development
command: npm run dev
volumes:
node_modules:

32
resources/index.html Normal file
View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vuetify-Lux</title>
<!-- Google tag (gtag.js) -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=G-WVWG4VLCL2"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "G-WVWG4VLCL2");
</script>
<link
href="https://fonts.loli.net/css2?family=Quicksand:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

2
resources/netlify.toml Normal file
View File

@@ -0,0 +1,2 @@
[build.environment]
NODE_VERSION = "21.1.0"

7444
resources/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

72
resources/package.json Normal file
View File

@@ -0,0 +1,72 @@
{
"name": "vuetify3-design",
"version": "0.0.0",
"scripts": {
"dev": "vite --mode dev",
"build": "vite build --base=/ --mode pro",
"preview": "vite preview",
"test": "vitest",
"test:ui": "vitest --ui",
"coverage": "vitest run --coverage"
},
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@mdi/font": "7.4.47",
"@tiptap/pm": "^2.5.9",
"@tiptap/starter-kit": "^2.5.9",
"@tiptap/vue-3": "^2.5.9",
"@vueup/vue-quill": "^1.2.0",
"@vueuse/core": "^10.11.0",
"@vueuse/integrations": "^10.11.0",
"@xterm/addon-canvas": "^0.7.0",
"@yeger/vue-masonry-wall": "^5.0.14",
"apexcharts": "^3.52.0",
"axios": "^1.7.5",
"clipboard": "^2.0.11",
"echarts": "^5.5.1",
"flag-icons": "^7.2.3",
"focus-trap": "^7.5.4",
"happy-dom": "^14.12.3",
"md-editor-v3": "^4.18.0",
"microsoft-cognitiveservices-speech-sdk": "^1.38.0",
"moment": "^2.30.1",
"openai": "^4.55.1",
"pinia": "^2.2.1",
"pinia-plugin-persist": "^1.0.0",
"plantuml-encoder": "^1.4.0",
"roboto-fontface": "*",
"unsplash-js": "^7.0.19",
"vue": "^3.4.36",
"vue-echarts": "^7.0.1",
"vue-i18n": "^9.13.1",
"vue-router": "^4.4.3",
"vue-virtual-scroller": "^2.0.0-beta.8",
"vue-waterfall-plugin-next": "^2.4.3",
"vue3-apexcharts": "^1.5.3",
"vue3-lottie": "^3.3.0",
"vue3-perfect-scrollbar": "^2.0.0",
"vuedraggable": "^4.1.0",
"vuetify": "^3.6.14",
"webfontloader": "^1.6.28",
"xterm": "^5.3.0",
"xterm-addon-attach": "^0.9.0",
"xterm-addon-fit": "^0.8.0"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@fortawesome/fontawesome-free": "^6.6.0",
"@iconify/vue": "^4.1.2",
"@types/node": "^22.1.0",
"@vitejs/plugin-vue": "^5.1.2",
"@vitest/ui": "^2.0.5",
"@vue/test-utils": "^2.4.6",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.41",
"sass": "^1.77.8",
"tailwindcss": "^3.4.8",
"unplugin-auto-import": "^0.18.2",
"vite": "^5.4.0",
"vite-plugin-vuetify": "^2.0.4",
"vitest": "^2.0.5"
}
}

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -0,0 +1 @@
/* /index.html 200

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

51
resources/src/App.vue Normal file
View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
import UILayout from "@/layouts/UILayout.vue";
import LandingLayout from "@/layouts/LandingLayout.vue";
import DefaultLayout from "@/layouts/DefaultLayout.vue";
import AuthLayout from "@/layouts/AuthLayout.vue";
import Snackbar from "@/components/common/Snackbar.vue";
import { useAppStore } from "@/stores/appStore";
import { useTheme } from "vuetify";
const appStore = useAppStore();
const theme = useTheme();
const route = useRoute();
const isRouterLoaded = computed(() => {
if (route.name !== null) return true;
return false;
});
const layouts = {
default: DefaultLayout,
ui: UILayout,
landing: LandingLayout,
auth: AuthLayout,
};
type LayoutName = "default" | "ui" | "landing" | "auth" | "error";
const currentLayout = computed(() => {
const layoutName = route.meta.layout as LayoutName;
if (!layoutName) {
return DefaultLayout;
}
return layouts[layoutName];
});
onMounted(() => {
theme.global.name.value = appStore.theme;
});
</script>
<template>
<v-app>
<component :is="currentLayout" v-if="isRouterLoaded">
<router-view> </router-view>
</component>
<Snackbar />
</v-app>
</template>
<style scoped></style>

View File

@@ -0,0 +1,67 @@
import axios from "axios";
import { useSnackbarStore } from "@/stores/snackbarStore";
import { useChatGPTStore } from "@/stores/chatGPTStore";
const gptInstance = axios.create({
timeout: 100000,
});
gptInstance.interceptors.request.use((config) => {
const chatGPTStore = useChatGPTStore();
config.baseURL = chatGPTStore.proxyUrl;
return config;
});
gptInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
const snackbarStore = useSnackbarStore();
if (error.response) {
const status = error.response.status;
const data = error.response.data;
snackbarStore.showErrorMessage(data.error);
} else {
snackbarStore.showErrorMessage("Network Error");
}
return Promise.reject(error);
}
);
// Get all models.
export const getModelsApi = (apiKey: string) => {
return gptInstance.get("/v1/models", {
headers: {
Authorization: "Bearer " + apiKey,
},
});
};
// Get account balance information.
export const getBalanceApi = (apiKey: string) => {
return gptInstance.get("/dashboard/billing/credit_grants", {
headers: {
Authorization: "Bearer " + apiKey,
},
});
};
// speech-to-text
export const createTranscriptionApi = (formData: any, apiKey: string) => {
return gptInstance.post("/v1/audio/transcriptions", formData, {
headers: {
Authorization: "Bearer " + apiKey,
},
});
};
// completions(Stream UnUsed)
export const createCompletionApi = (data: any, apiKey: string) => {
return gptInstance.post("/v1/chat/completions", data, {
headers: {
Authorization: "Bearer " + apiKey,
},
});
};

102
resources/src/api/api.ts Normal file
View File

@@ -0,0 +1,102 @@
import axios, {
AxiosInstance,
AxiosError,
AxiosRequestConfig,
AxiosResponse,
} from "axios";
import { useSnackbarStore } from "@/stores/snackbarStore";
import router from "../router";
const snackbarStore = useSnackbarStore();
interface Result {
code: number;
msg: string;
}
// 请求响应参数包含data
interface ResultData<T = any> extends Result {
data?: T;
}
const URL: string = "";
enum RequestEnums {
TIMEOUT = 20000,
}
const config = {
// 默认地址
baseURL: URL as string,
// 设置超时时间
timeout: RequestEnums.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true,
};
class RequestHttp {
service: AxiosInstance;
public constructor(config: AxiosRequestConfig) {
// 实例化axios
this.service = axios.create(config);
this.service.interceptors.request.use(
(config) => {
const token = localStorage.getItem("token") || "";
config.headers.Authorization = "bearer " + token;
config.url = "/api" + config.url;
return config;
},
(error: AxiosError) => {
snackbarStore.showErrorMessage(error);
return Promise.reject(error);
}
);
/**
* 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data } = response; // 解构
if (data.code !== 0) {
snackbarStore.showErrorMessage(data.message);
return Promise.reject(data);
}
return data;
},
(error: AxiosError) => {
const { response } = error;
if (response) {
this.handleCode(response.status);
}
//@ts-ignore
snackbarStore.showErrorMessage(response.data.message);
}
);
}
handleCode(code: number): void {
switch (code) {
case 401:
router.replace("/login");
break;
default:
break;
}
}
// 常用方法封装
get<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, { params });
}
post<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.post(url, params);
}
put<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.put(url, params);
}
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.delete(url, { params });
}
}
// 导出一个实例对象
export default new RequestHttp(config);

View File

@@ -0,0 +1,13 @@
import api from "./api";
export function getConfig() {
return api.get("/config", undefined).then((res) => res);
}
export function setConfig(data) {
return api.put("/config", data).then((res) => res);
}
export function esReload() {
return api.put("/config/reload").then((res) => res);
}

View File

@@ -0,0 +1,39 @@
import axios from "axios";
import { useSnackbarStore } from "@/stores/snackbarStore";
const snackbarStore = useSnackbarStore();
// change the access key to your own
const ACCESS_KEY = import.meta.env.VITE_GITHUB_CLIENT_ID;
const instance = axios.create({
baseURL: "https://api.github.com",
timeout: 20000,
headers: { Authorization: "Bearer" + " " + ACCESS_KEY },
});
instance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response) {
const status = error.response.status;
const data = error.response.data;
snackbarStore.showErrorMessage(data.message);
} else {
snackbarStore.showErrorMessage("Network Error");
}
return Promise.reject(error);
}
);
// https://api.github.com/users/yangjiakai/events/public
// Get public events for a user
export const getPublicEventsApi = (username: string) => {
return instance.get("/users/" + username + "/events/public");
};
// Get public events for a network of repositories
export const getPublicEventsForNetworkApi = (username: string) => {
return instance.get("/networks/" + username + "/events");
};

View File

@@ -0,0 +1,33 @@
import axios from "axios";
export const textToSpeech = async () => {
const googleInstance = axios.create({
baseURL: "https://us-central1-texttospeech.googleapis.com",
timeout: 100000,
});
const res = await googleInstance.post(
"/v1/chat/completions/text:synthesize",
{
audioConfig: {
audioEncoding: "LINEAR16",
effectsProfileId: ["small-bluetooth-speaker-class-device"],
pitch: 0,
speakingRate: 1,
},
input: {
text: "Google Cloud Text-to-Speech enables developers to synthesize natural-sounding speech with 100+ voices, available in multiple languages and variants. It applies DeepMinds groundbreaking research in WaveNet and Googles powerful neural networks to deliver the highest fidelity possible. As an easy-to-use API, you can create lifelike interactions with your users, across many applications and devices.",
},
voice: {
languageCode: "en-US",
name: "en-US-Neural2-J",
},
},
{
headers: {
"Content-Type": "application/json",
"x-goog-api-key": "AIzaSyBSXdkeyAvIZX5n_bj4KsqSjJf1W-_TfCntvk",
},
}
);
};

5
resources/src/api/log.ts Normal file
View File

@@ -0,0 +1,5 @@
import api from "./api";
export function getLog(data: any) {
return api.post("/log", data).then((res) => res);
}

View File

@@ -0,0 +1,12 @@
import api from "./api";
interface LoginRes {
code: number;
token: string;
username: string;
role: number;
}
export const login = (query: any) => {
return api.post<LoginRes>("/user/login", query).then((res) => res);
};

View File

@@ -0,0 +1,50 @@
import { ProcessConfig, ProcessItem } from "../types/process/process";
import api from "./api";
export function getProcessList() {
return api.get<ProcessItem[]>("/process", undefined).then((res) => res);
}
export function getProcessListWait() {
return api.get<ProcessItem[]>("/process/wait", undefined).then((res) => res);
}
export function killProcessAll() {
return api.delete("/process/all", { }).then((res) => res);
}
export function startProcessAll() {
return api.put("/process/all", { }).then((res) => res);
}
export function killProcess(uuid) {
return api.delete("/process", { uuid }).then((res) => res);
}
export function startProcess(uuid) {
return api.put("/process", { uuid }).then((res) => res);
}
export function getContorl(uuid) {
return api.get("/process/control", { uuid }).then((res) => res);
}
export function getProcessConfig(uuid) {
return api.get<ProcessConfig>("/process/config", { uuid }).then((res) => res);
}
export function deleteProcessConfig(uuid) {
return api.delete("/process/config", { uuid }).then((res) => res);
}
export function putProcessConfig(data) {
return api.put("/process/config", data).then((res) => res);
}
export function postProcessConfig(data) {
return api.post("/process/config", data).then((res) => res);
}
export function createProcessShare(data) {
return api.post("/process/share", data).then((res) => res);
}

22
resources/src/api/push.ts Normal file
View File

@@ -0,0 +1,22 @@
import { PushItem } from "../types/push/push";
import api from "./api";
export function createPush(data) {
return api.post("/push", data).then((res) => res);
}
export function getPushList() {
return api.get<PushItem[]>("/push/list", undefined).then((res) => res);
}
export function deletePush(id) {
return api.delete("/push", { id }).then((res) => res);
}
export function getPush(id) {
return api.get("/push", { id }).then((res) => res);
}
export function editPush(data) {
return api.put("/push", data).then((res) => res);
}

View File

@@ -0,0 +1,39 @@
// import Axios library
import axios from "axios";
// Set default validation status for Axios
axios.defaults.validateStatus = function (status) {
// Return true if status is between 200 and 300 inclusive
return status >= 200 && status < 300;
};
// Create a new Axios instance with base URL and timeout
const diffusion = axios.create({
baseURL: "/sdApi",
// baseURL: 'http://127.0.0.1:7861',
timeout: 100000,
});
// 添加请求拦截器
diffusion.interceptors.request.use();
// 添加响应拦截器
diffusion.interceptors.response.use();
//getmodels
//txt2img
export const txt2imgApi = (data: any) => {
return diffusion.post("/sdapi/v1/txt2img", data);
};
//img2img
export const img2imgApi = (data: any) => {
return diffusion.post("/sdapi/v1/img2img", data);
};
//getProgeress
export const getProgressApi = (data: any) => {
return diffusion.get("/sdapi/v1/progress");
};
//getSamplers
export const getSamplersApi = () => {
return diffusion.get("/sdapi/v1/samplers");
};

42
resources/src/api/task.ts Normal file
View File

@@ -0,0 +1,42 @@
import { TaskItem } from "../types/tassk/task";
import api from "./api";
export function getTaskAll() {
return api.get<TaskItem[]>("/task/all", undefined).then((res) => res);
}
export function getTaskAllWait() {
return api.get("/task/all/wait", undefined).then((res) => res);
}
export function getTaskById(id) {
return api.get<TaskItem>("/task", { id }).then((res) => res);
}
export function startTaskById(id) {
return api.get("/task/start", { id }).then((res) => res);
}
export function stopTaskById(id) {
return api.get("/task/stop", { id }).then((res) => res);
}
export function editTaskEnable(body) {
return api.put("/task/enable", body).then((res) => res);
}
export function editTask(body) {
return api.put("/task", body).then((res) => res);
}
export function addTask(body) {
return api.post("/task", body).then((res) => res);
}
export function deleteTaskById(id) {
return api.delete("/task", { id }).then((res) => res);
}
export function changeTaskKey(id) {
return api.post("/task/key", { id }).then((res) => res);
}

View File

@@ -0,0 +1,160 @@
import axios from "axios";
import { useSnackbarStore } from "@/stores/snackbarStore";
const snackbarStore = useSnackbarStore();
// change the access key to your own
const ACCESS_KEY = import.meta.env.VITE_UNSPLASH_ACCESS_KEY;
const instance = axios.create({
baseURL: "https://api.unsplash.com",
timeout: 20000,
headers: { Authorization: "Client-ID" + " " + ACCESS_KEY },
});
instance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response) {
const status = error.response.status;
const data = error.response.data;
snackbarStore.showErrorMessage(data.errors[0]);
} else {
snackbarStore.showErrorMessage("Network Error");
}
return Promise.reject(error);
}
);
interface Query {
page?: number;
per_page?: number;
}
// List photos 图片一览
export const getPhotosApi = (query?: Query) => {
return instance.get("/photos/", { params: query });
};
// Get a photo 获取图片信息
export const getPhotoApi = (id: string) => {
return instance.get("/photos/" + id);
};
// Get a random photo 获取一张随机图片
export const getRandomPhotoApi = () => {
return instance.get("/photos/random");
};
// Get a photos statistics 获取照片的统计数据
export const getPhotoStatisticsApi = (id: string) => {
return instance.get("/photos/" + id + "/statistics");
};
// Get a photos related 获取照片的相关照片
export const getPhotoRelatedApi = (id: string) => {
return instance.get("/photos/" + id + "/related");
};
// Track a photo download
// Update a photo
// Like a photo
// Unlike a photo
// Topic
// List topics
export const getTopicsApi = (query?: Query) => {
return instance.get("/topics", { params: query });
};
// Get a topic
export const getTopicApi = (id_or_slug: string | string[]) => {
return instance.get("/topics/" + id_or_slug);
};
// Get a topics photos
export const getTopicPhotosApi = (
id_or_slug: string | string[],
query?: Query
) => {
return instance.get("/topics/" + id_or_slug + "/photos", { params: query });
};
// Get a user
export const getUserApi = (username: string | string[]) => {
return instance.get("/users/" + username);
};
// Get a users portfolio
export const getUserPortfolioApi = (username: string | string[]) => {
return instance.get("/users/" + username + "/portfolio");
};
// List a users photos
export const getUserPhotosApi = (
username: string | string[],
query?: Query
) => {
return instance.get("/users/" + username + "/photos", { params: query });
};
// List a users liked photos
export const getUserLikesApi = (username: string | string[], query?: Query) => {
return instance.get("/users/" + username + "/likes", { params: query });
};
// List a users collections
export const getUserCollectionsApi = (
username: string | string[],
query?: Query
) => {
return instance.get("/users/" + username + "/collections", { params: query });
};
// Get a users statistics
export const getUserStatisticsApi = (username: string | string[]) => {
return instance.get("/users/" + username + "/statistics");
};
// Collections 图集
// List collections 图集一览
export const getCollectionsApi = (query?: Query) => {
return instance.get("/collections", { params: query });
};
// Get a collection 获取图集信息
export const getCollectionApi = (id: string | string[]) => {
return instance.get("/collections/" + id);
};
// Get a collections photos 获取该图集下所有图片
export const getCollectionPhotosApi = (
id: string | string[],
query?: Query
) => {
return instance.get("/collections/" + id + "/photos", { params: query });
};
// List a collections related collections 获取该图集相关联图集
export const getCollectionRelatedApi = (id: string | string[]) => {
return instance.get("/collections/" + id + "/related");
};
// Create a new collection 新增图集
// Update an existing collection 更新现存图集
// Delete a collection 删除某个图集
// Add a photo to a collection 添加图片到图集
// Remove a photo from a collection 从图集删除图片
// Search
// Search All
export const searchAllApi = (query?: Query) => {
return instance.get("/search", { params: query });
};
// Search photos
export const searchPhotosApi = (query?: Query) => {
return instance.get("/search/photos", { params: query });
};
// Search collections
export const searchCollectionsApi = (query?: Query) => {
return instance.get("/search/collections", { params: query });
};
// Search users
export const searchUsersApi = (query?: Query) => {
return instance.get("/search/users", { params: query });
};

39
resources/src/api/user.ts Normal file
View File

@@ -0,0 +1,39 @@
import api from "./api";
// 登录方法
export function login(data) {
return api.post("/user/login", {
account: data.account,
password: data.password,
}).then((res) => res);
}
export function createUser(data) {
return api.post("/user", data).then((res) => res);
}
export function deleteUser(account) {
return api.delete("/user", { account }).then((res) => res);
}
export function changePassword(data) {
return api.put("/user/password", data).then((res) => res);
}
export function registerAdmin(password) {
return api.get("/user/register/admin", { password }).then((res) => res);
}
export function getUserList() {
return api.get("/user", undefined).then((res) => res);
}
export function getPermission(account, pid) {
return api.get("/permission/list", { account, pid }).then((res) => res);
}
export function editPermission(data) {
return api.put("/permission", data).then((res) => res);
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Some files were not shown because too many files have changed in this diff Show More