Compare commits
36 Commits
d968ce2a3e
...
dev-web
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6d0239fe60 | ||
![]() |
09ae980ab5 | ||
![]() |
c52f7e2097 | ||
![]() |
1844a843eb | ||
![]() |
593185a431 | ||
![]() |
ab7ef546ea | ||
![]() |
8fd70be906 | ||
![]() |
d342a5bae9 | ||
![]() |
6b5231d169 | ||
![]() |
c7cc2e36fd | ||
![]() |
2fabbf5c0f | ||
![]() |
e37da632df | ||
![]() |
219dcdc372 | ||
![]() |
ca849de37f | ||
![]() |
e14df1fd99 | ||
![]() |
2e37eae3ca | ||
![]() |
9f7f0c9dd7 | ||
![]() |
1102a60425 | ||
![]() |
8a29bf610c | ||
![]() |
056caf5d06 | ||
![]() |
d550942cd2 | ||
![]() |
c22384fbc0 | ||
![]() |
d42b89cc8b | ||
![]() |
c32db8c8f6 | ||
![]() |
f723c12d42 | ||
![]() |
da54e71c27 | ||
![]() |
0d46bb7d07 | ||
![]() |
cffb1ee0f4 | ||
![]() |
143cf61a57 | ||
![]() |
1dc2d77e82 | ||
![]() |
37748ecb7f | ||
![]() |
5de8b45b2f | ||
![]() |
3fb8cd4dad | ||
![]() |
274a4d93df | ||
![]() |
c383e1737f | ||
![]() |
2a40d560d4 |
@@ -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)
|
||||
|
@@ -7,7 +7,34 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var startTitle = `
|
||||
----------------------------------------------------------------------------
|
||||
_____ _____ _____
|
||||
/\ \ /\ \ /\ \
|
||||
/::\ \ /::\ \ /::\____\
|
||||
/::::\ \ /::::\ \ /::::| |
|
||||
/::::::\ \ /::::::\ \ /:::::| |
|
||||
/:::/\:::\ \ /:::/\:::\ \ /::::::| |
|
||||
/:::/ \:::\ \ /:::/__\:::\ \ /:::/|::| |
|
||||
/:::/ \:::\ \ /::::\ \:::\ \ /:::/ |::| |
|
||||
/:::/ / \:::\ \ /::::::\ \:::\ \ /:::/ |::|___|______
|
||||
/:::/ / \:::\ ___\ /:::/\:::\ \:::\____\ /:::/ |::::::::\ \
|
||||
/:::/____/ ___\:::| |/:::/ \:::\ \:::| |/:::/ |:::::::::\____\
|
||||
\:::\ \ /\ /:::|____|\::/ \:::\ /:::|____|\::/ / ~~~~~/:::/ /
|
||||
\:::\ /::\ \::/ / \/_____/\:::\/:::/ / \/____/ /:::/ /
|
||||
\:::\ \:::\ \/____/ \::::::/ / /:::/ /
|
||||
\:::\ \:::\____\ \::::/ / /:::/ /
|
||||
\:::\ /:::/ / \::/____/ /:::/ /
|
||||
\:::\/:::/ / ~~ /:::/ /
|
||||
\::::::/ / /:::/ /
|
||||
\::::/ / /:::/ /
|
||||
\::/____/ \::/ /
|
||||
\/____/
|
||||
----------------------------------------------------------------------------
|
||||
`
|
||||
|
||||
func main() {
|
||||
print(startTitle)
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
route.Route()
|
||||
}
|
||||
|
@@ -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:"-"`
|
||||
}
|
||||
|
@@ -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()
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
})
|
||||
|
@@ -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()
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -1,6 +0,0 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
SECRET_KEY = "secret"
|
||||
CONSOLE = "console"
|
||||
)
|
@@ -1,7 +0,0 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
CTXFLG_USER_NAME = "user"
|
||||
CTXFLG_ROLE = "role"
|
||||
CTXFLG_ERR = "err"
|
||||
)
|
@@ -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"
|
||||
)
|
@@ -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
|
||||
)
|
@@ -1,10 +0,0 @@
|
||||
package constants
|
||||
|
||||
type Role int
|
||||
|
||||
const (
|
||||
ROLE_ROOT Role = iota
|
||||
ROLE_ADMIN
|
||||
ROLE_USER
|
||||
ROLE_GUEST
|
||||
)
|
@@ -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
|
||||
)
|
6
internal/app/eum/consts.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package eum
|
||||
|
||||
const (
|
||||
SecretKey = "secret"
|
||||
Console = "console"
|
||||
)
|
11
internal/app/eum/event.go
Normal 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
@@ -0,0 +1,6 @@
|
||||
package eum
|
||||
|
||||
const (
|
||||
CtxUserName = "user"
|
||||
CtxRole = "role"
|
||||
)
|
11
internal/app/eum/permission.go
Normal 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"
|
||||
)
|
17
internal/app/eum/process.go
Normal 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
@@ -0,0 +1,10 @@
|
||||
package eum
|
||||
|
||||
type Role int
|
||||
|
||||
const (
|
||||
RoleRoot Role = iota
|
||||
RoleAdmin
|
||||
RoleUser
|
||||
RoleGuest
|
||||
)
|
21
internal/app/eum/task.go
Normal 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{}
|
35
internal/app/logic/event.go
Normal 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)
|
||||
}
|
||||
}
|
@@ -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:
|
||||
|
@@ -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("终端类型错误")
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
},
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
|
19
internal/app/model/event.go
Normal 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"
|
||||
}
|
@@ -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"`
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -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)
|
||||
}
|
||||
|
14
internal/app/repository/event.go
Normal 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)
|
||||
}
|
33
internal/app/repository/gen.go
Normal 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)
|
||||
}
|
10
internal/app/repository/gen_ignore.go
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build !gen
|
||||
// +build !gen
|
||||
|
||||
package repository
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
func gormGen(*gorm.DB) {
|
||||
|
||||
}
|
@@ -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)
|
||||
|
@@ -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) {
|
||||
|
398
internal/app/repository/query/event.gen.go
Normal 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
|
||||
}
|
@@ -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),
|
||||
|
@@ -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
|
||||
|
@@ -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)).
|
||||
|
@@ -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,
|
||||
|
@@ -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) {
|
||||
|
@@ -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 != "" {
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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()
|
||||
|
4
resources/.browserslistrc
Normal file
@@ -0,0 +1,4 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
2
resources/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
npm-debug.log
|
5
resources/.editorconfig
Normal 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
@@ -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
@@ -0,0 +1 @@
|
||||
VITE_API_BASE_URL=https://api.example.com
|
7
resources/.env.template
Normal 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
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
32
resources/.gitignore
vendored
Normal 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
|
3
resources/.vite/deps_temp_61bc278e/package.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
13
resources/Dockerfile
Normal 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
@@ -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
@@ -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
@@ -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
|
||||
|
||||

|
||||
|
||||
## 💬 連絡
|
||||
|
||||
- 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
@@ -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
|
||||
|
||||

|
||||
|
||||
## 💬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
@@ -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
|
||||
- 📝 富文本编辑器
|
||||
- 📇 响应式多平台自适应
|
||||
|
||||
## 📈 项目活跃度
|
||||
|
||||

|
||||
|
||||
## 💬 联络我
|
||||
|
||||
- 邮箱 <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
@@ -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"];
|
||||
}
|
25
resources/docker-compose.yml
Normal 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
@@ -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
@@ -0,0 +1,2 @@
|
||||
[build.environment]
|
||||
NODE_VERSION = "21.1.0"
|
7444
resources/package-lock.json
generated
Normal file
72
resources/package.json
Normal 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"
|
||||
}
|
||||
}
|
6
resources/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
1
resources/public/_redirects
Normal file
@@ -0,0 +1 @@
|
||||
/* /index.html 200
|
BIN
resources/public/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
resources/public/favicon.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
51
resources/src/App.vue
Normal 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>
|
67
resources/src/api/aiApi.ts
Normal 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
@@ -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);
|
13
resources/src/api/config.ts
Normal 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);
|
||||
}
|
39
resources/src/api/githubApi.ts
Normal 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");
|
||||
};
|
33
resources/src/api/googleApi.ts
Normal 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 DeepMind’s groundbreaking research in WaveNet and Google’s 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
@@ -0,0 +1,5 @@
|
||||
import api from "./api";
|
||||
|
||||
export function getLog(data: any) {
|
||||
return api.post("/log", data).then((res) => res);
|
||||
}
|
12
resources/src/api/login.ts
Normal 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);
|
||||
};
|
50
resources/src/api/process.ts
Normal 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
@@ -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);
|
||||
}
|
39
resources/src/api/stableDiffusionApi.ts
Normal 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
@@ -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);
|
||||
}
|
160
resources/src/api/unsplashApi.ts
Normal 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 photo’s statistics 获取照片的统计数据
|
||||
export const getPhotoStatisticsApi = (id: string) => {
|
||||
return instance.get("/photos/" + id + "/statistics");
|
||||
};
|
||||
|
||||
// Get a photo’s 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 topic’s 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 user’s portfolio
|
||||
export const getUserPortfolioApi = (username: string | string[]) => {
|
||||
return instance.get("/users/" + username + "/portfolio");
|
||||
};
|
||||
// List a user’s photos
|
||||
export const getUserPhotosApi = (
|
||||
username: string | string[],
|
||||
query?: Query
|
||||
) => {
|
||||
return instance.get("/users/" + username + "/photos", { params: query });
|
||||
};
|
||||
// List a user’s liked photos
|
||||
export const getUserLikesApi = (username: string | string[], query?: Query) => {
|
||||
return instance.get("/users/" + username + "/likes", { params: query });
|
||||
};
|
||||
// List a user’s collections
|
||||
export const getUserCollectionsApi = (
|
||||
username: string | string[],
|
||||
query?: Query
|
||||
) => {
|
||||
return instance.get("/users/" + username + "/collections", { params: query });
|
||||
};
|
||||
// Get a user’s 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 collection’s photos 获取该图集下所有图片
|
||||
export const getCollectionPhotosApi = (
|
||||
id: string | string[],
|
||||
query?: Query
|
||||
) => {
|
||||
return instance.get("/collections/" + id + "/photos", { params: query });
|
||||
};
|
||||
// List a collection’s 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
@@ -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);
|
||||
}
|
1
resources/src/assets/images/404.svg
Normal file
After Width: | Height: | Size: 17 KiB |
1
resources/src/assets/images/500.svg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
resources/src/assets/images/avatars/avatar_assistant.jpg
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
resources/src/assets/images/avatars/avatar_user.jpg
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
resources/src/assets/images/card2/yoimiya.png
Normal file
After Width: | Height: | Size: 248 KiB |