mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 完成面板日志功能
This commit is contained in:
parent
0dfb9bd5c7
commit
3a3670f660
@ -5,10 +5,12 @@ import (
|
|||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/captcha"
|
"github.com/1Panel-dev/1Panel/backend/utils/captcha"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/qqwry"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,6 +32,7 @@ func (b *BaseApi) Login(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user, err := authService.Login(c, req)
|
user, err := authService.Login(c, req)
|
||||||
|
go saveLoginLogs(c, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
@ -102,3 +105,22 @@ func (b *BaseApi) SafeEntrance(c *gin.Context) {
|
|||||||
|
|
||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func saveLoginLogs(c *gin.Context, err error) {
|
||||||
|
var logs model.LoginLog
|
||||||
|
if err != nil {
|
||||||
|
logs.Status = constant.StatusFailed
|
||||||
|
logs.Message = err.Error()
|
||||||
|
} else {
|
||||||
|
logs.Status = constant.StatusSuccess
|
||||||
|
}
|
||||||
|
logs.IP = c.ClientIP()
|
||||||
|
qqWry, err := qqwry.NewQQwry()
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("load qqwry datas failed: %s", err)
|
||||||
|
}
|
||||||
|
res := qqWry.Find(logs.IP)
|
||||||
|
logs.Agent = c.GetHeader("User-Agent")
|
||||||
|
logs.Address = res.Area
|
||||||
|
_ = logService.CreateLoginLog(logs)
|
||||||
|
}
|
||||||
|
@ -32,7 +32,6 @@ var (
|
|||||||
settingService = service.ServiceGroupApp.SettingService
|
settingService = service.ServiceGroupApp.SettingService
|
||||||
backupService = service.ServiceGroupApp.BackupService
|
backupService = service.ServiceGroupApp.BackupService
|
||||||
|
|
||||||
operationService = service.ServiceGroupApp.OperationService
|
|
||||||
commandService = service.ServiceGroupApp.CommandService
|
commandService = service.ServiceGroupApp.CommandService
|
||||||
|
|
||||||
websiteGroupService = service.ServiceGroupApp.WebsiteGroupService
|
websiteGroupService = service.ServiceGroupApp.WebsiteGroupService
|
||||||
@ -40,4 +39,6 @@ var (
|
|||||||
websiteDnsAccountService = service.ServiceGroupApp.WebSiteDnsAccountService
|
websiteDnsAccountService = service.ServiceGroupApp.WebSiteDnsAccountService
|
||||||
websiteSSLService = service.ServiceGroupApp.WebSiteSSLService
|
websiteSSLService = service.ServiceGroupApp.WebSiteSSLService
|
||||||
websiteAcmeAccountService = service.ServiceGroupApp.WebSiteAcmeAccountService
|
websiteAcmeAccountService = service.ServiceGroupApp.WebSiteAcmeAccountService
|
||||||
|
|
||||||
|
logService = service.ServiceGroupApp.LogService
|
||||||
)
|
)
|
||||||
|
@ -4,18 +4,17 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *BaseApi) GetOperationList(c *gin.Context) {
|
func (b *BaseApi) GetLoginLogs(c *gin.Context) {
|
||||||
var req dto.PageInfo
|
var req dto.PageInfo
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
total, list, err := operationService.Page(req)
|
total, list, err := logService.PageLoginLog(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
@ -27,20 +26,21 @@ func (b *BaseApi) GetOperationList(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) DeleteOperation(c *gin.Context) {
|
func (b *BaseApi) GetOperationLogs(c *gin.Context) {
|
||||||
var req dto.BatchDeleteReq
|
var req dto.PageInfo
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := global.VALID.Struct(req); err != nil {
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := operationService.BatchDelete(req.Ids); err != nil {
|
total, list, err := logService.PageOperationLog(req)
|
||||||
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
helper.SuccessWithData(c, nil)
|
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Items: list,
|
||||||
|
Total: total,
|
||||||
|
})
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OperationLogBack struct {
|
type OperationLog struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Group string `json:"group"`
|
Group string `json:"group"`
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
@ -24,3 +24,13 @@ type OperationLogBack struct {
|
|||||||
Detail string `json:"detail"`
|
Detail string `json:"detail"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LoginLog struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Agent string `json:"agent"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
}
|
@ -21,3 +21,12 @@ type OperationLog struct {
|
|||||||
|
|
||||||
Detail string `gorm:"type:longText" json:"detail"`
|
Detail string `gorm:"type:longText" json:"detail"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LoginLog struct {
|
||||||
|
BaseModel
|
||||||
|
IP string `gorm:"type:varchar(64)" json:"ip"`
|
||||||
|
Address string `gorm:"type:varchar(64)" json:"address"`
|
||||||
|
Agent string `gorm:"type:varchar(256)" json:"agent"`
|
||||||
|
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||||
|
Message string `gorm:"type:longText" json:"message"`
|
||||||
|
}
|
@ -19,13 +19,13 @@ type RepoGroup struct {
|
|||||||
GroupRepo
|
GroupRepo
|
||||||
SettingRepo
|
SettingRepo
|
||||||
BackupRepo
|
BackupRepo
|
||||||
OperationRepo
|
|
||||||
WebSiteRepo
|
WebSiteRepo
|
||||||
WebSiteDomainRepo
|
WebSiteDomainRepo
|
||||||
WebSiteGroupRepo
|
WebSiteGroupRepo
|
||||||
WebsiteDnsAccountRepo
|
WebsiteDnsAccountRepo
|
||||||
WebsiteSSLRepo
|
WebsiteSSLRepo
|
||||||
WebsiteAcmeAccountRepo
|
WebsiteAcmeAccountRepo
|
||||||
|
LogRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
var RepoGroupApp = new(RepoGroup)
|
var RepoGroupApp = new(RepoGroup)
|
||||||
|
52
backend/app/repo/logs.go
Normal file
52
backend/app/repo/logs.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogRepo struct{}
|
||||||
|
|
||||||
|
type ILogRepo interface {
|
||||||
|
CreateLoginLog(user *model.LoginLog) error
|
||||||
|
PageLoginLog(limit, offset int, opts ...DBOption) (int64, []model.LoginLog, error)
|
||||||
|
|
||||||
|
CreateOperationLog(user *model.OperationLog) error
|
||||||
|
PageOperationLog(limit, offset int, opts ...DBOption) (int64, []model.OperationLog, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewILogRepo() ILogRepo {
|
||||||
|
return &LogRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *LogRepo) CreateLoginLog(log *model.LoginLog) error {
|
||||||
|
return global.DB.Create(log).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *LogRepo) PageLoginLog(page, size int, opts ...DBOption) (int64, []model.LoginLog, error) {
|
||||||
|
var ops []model.LoginLog
|
||||||
|
db := global.DB.Model(&model.LoginLog{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error
|
||||||
|
return count, ops, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *LogRepo) CreateOperationLog(log *model.OperationLog) error {
|
||||||
|
return global.DB.Create(log).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *LogRepo) PageOperationLog(page, size int, opts ...DBOption) (int64, []model.OperationLog, error) {
|
||||||
|
var ops []model.OperationLog
|
||||||
|
db := global.DB.Model(&model.OperationLog{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error
|
||||||
|
return count, ops, err
|
||||||
|
}
|
@ -1,43 +0,0 @@
|
|||||||
package repo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
|
||||||
)
|
|
||||||
|
|
||||||
type OperationRepo struct{}
|
|
||||||
|
|
||||||
type IOperationRepo interface {
|
|
||||||
Create(user *model.OperationLog) error
|
|
||||||
Page(limit, offset int, opts ...DBOption) (int64, []model.OperationLog, error)
|
|
||||||
Delete(opts ...DBOption) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIOperationRepo() IOperationRepo {
|
|
||||||
return &OperationRepo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *OperationRepo) Create(user *model.OperationLog) error {
|
|
||||||
return global.DB.Create(user).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *OperationRepo) Page(page, size int, opts ...DBOption) (int64, []model.OperationLog, error) {
|
|
||||||
var ops []model.OperationLog
|
|
||||||
db := global.DB.Model(&model.OperationLog{})
|
|
||||||
for _, opt := range opts {
|
|
||||||
db = opt(db)
|
|
||||||
}
|
|
||||||
count := int64(0)
|
|
||||||
db = db.Count(&count)
|
|
||||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error
|
|
||||||
return count, ops, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *OperationRepo) Delete(opts ...DBOption) error {
|
|
||||||
db := global.DB
|
|
||||||
for _, opt := range opts {
|
|
||||||
db = opt(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.Delete(&model.OperationLog{}).Error
|
|
||||||
}
|
|
@ -27,12 +27,13 @@ type ServiceGroup struct {
|
|||||||
SettingService
|
SettingService
|
||||||
BackupService
|
BackupService
|
||||||
|
|
||||||
OperationService
|
|
||||||
WebsiteGroupService
|
WebsiteGroupService
|
||||||
WebsiteService
|
WebsiteService
|
||||||
WebSiteDnsAccountService
|
WebSiteDnsAccountService
|
||||||
WebSiteSSLService
|
WebSiteSSLService
|
||||||
WebSiteAcmeAccountService
|
WebSiteAcmeAccountService
|
||||||
|
|
||||||
|
LogService
|
||||||
}
|
}
|
||||||
|
|
||||||
var ServiceGroupApp = new(ServiceGroup)
|
var ServiceGroupApp = new(ServiceGroup)
|
||||||
@ -63,11 +64,12 @@ var (
|
|||||||
settingRepo = repo.RepoGroupApp.SettingRepo
|
settingRepo = repo.RepoGroupApp.SettingRepo
|
||||||
backupRepo = repo.RepoGroupApp.BackupRepo
|
backupRepo = repo.RepoGroupApp.BackupRepo
|
||||||
|
|
||||||
operationRepo = repo.RepoGroupApp.OperationRepo
|
|
||||||
websiteRepo = repo.RepoGroupApp.WebSiteRepo
|
websiteRepo = repo.RepoGroupApp.WebSiteRepo
|
||||||
websiteGroupRepo = repo.RepoGroupApp.WebSiteGroupRepo
|
websiteGroupRepo = repo.RepoGroupApp.WebSiteGroupRepo
|
||||||
websiteDomainRepo = repo.RepoGroupApp.WebSiteDomainRepo
|
websiteDomainRepo = repo.RepoGroupApp.WebSiteDomainRepo
|
||||||
websiteDnsRepo = repo.RepoGroupApp.WebsiteDnsAccountRepo
|
websiteDnsRepo = repo.RepoGroupApp.WebsiteDnsAccountRepo
|
||||||
websiteSSLRepo = repo.RepoGroupApp.WebsiteSSLRepo
|
websiteSSLRepo = repo.RepoGroupApp.WebsiteSSLRepo
|
||||||
websiteAcmeRepo = repo.RepoGroupApp.WebsiteAcmeAccountRepo
|
websiteAcmeRepo = repo.RepoGroupApp.WebsiteAcmeAccountRepo
|
||||||
|
|
||||||
|
logRepo = repo.RepoGroupApp.LogRepo
|
||||||
)
|
)
|
||||||
|
@ -71,7 +71,7 @@ func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error {
|
|||||||
deamonMap["insecure-registries"] = k
|
deamonMap["insecure-registries"] = k
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newJson, err := json.Marshal(deamonMap)
|
newJson, err := json.MarshalIndent(deamonMap, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
92
backend/app/service/logs.go
Normal file
92
backend/app/service/logs.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogService struct{}
|
||||||
|
|
||||||
|
type ILogService interface {
|
||||||
|
CreateLoginLog(operation model.LoginLog) error
|
||||||
|
PageLoginLog(search dto.PageInfo) (int64, interface{}, error)
|
||||||
|
|
||||||
|
CreateOperationLog(operation model.OperationLog) error
|
||||||
|
PageOperationLog(search dto.PageInfo) (int64, interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewILogService() ILogService {
|
||||||
|
return &LogService{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *LogService) CreateLoginLog(operation model.LoginLog) error {
|
||||||
|
return logRepo.CreateLoginLog(&operation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *LogService) PageLoginLog(search dto.PageInfo) (int64, interface{}, error) {
|
||||||
|
total, ops, err := logRepo.PageLoginLog(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
|
||||||
|
var dtoOps []dto.LoginLog
|
||||||
|
for _, op := range ops {
|
||||||
|
var item dto.LoginLog
|
||||||
|
if err := copier.Copy(&item, &op); err != nil {
|
||||||
|
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
dtoOps = append(dtoOps, item)
|
||||||
|
}
|
||||||
|
return total, dtoOps, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *LogService) CreateOperationLog(operation model.OperationLog) error {
|
||||||
|
return logRepo.CreateOperationLog(&operation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *LogService) PageOperationLog(search dto.PageInfo) (int64, interface{}, error) {
|
||||||
|
total, ops, err := logRepo.PageOperationLog(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
|
||||||
|
var dtoOps []dto.OperationLog
|
||||||
|
for _, op := range ops {
|
||||||
|
var item dto.OperationLog
|
||||||
|
if err := copier.Copy(&item, &op); err != nil {
|
||||||
|
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
item.Body = filterSensitive(item.Body)
|
||||||
|
var res dto.Response
|
||||||
|
if err := json.Unmarshal([]byte(item.Resp), &res); err != nil {
|
||||||
|
global.LOG.Errorf("unmarshal failed, err: %+v", err)
|
||||||
|
dtoOps = append(dtoOps, item)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.Status = res.Code
|
||||||
|
if item.Status != 200 {
|
||||||
|
item.ErrorMessage = res.Msg
|
||||||
|
}
|
||||||
|
dtoOps = append(dtoOps, item)
|
||||||
|
}
|
||||||
|
return total, dtoOps, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterSensitive(vars string) string {
|
||||||
|
var Sensitives = []string{"password", "Password", "credential", "privateKey"}
|
||||||
|
ops := make(map[string]interface{})
|
||||||
|
if err := json.Unmarshal([]byte(vars), &ops); err != nil {
|
||||||
|
return vars
|
||||||
|
}
|
||||||
|
for k := range ops {
|
||||||
|
for _, sen := range Sensitives {
|
||||||
|
if k == sen {
|
||||||
|
delete(ops, k)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
backStr, err := json.Marshal(ops)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(backStr)
|
||||||
|
}
|
@ -1,77 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
|
||||||
"github.com/jinzhu/copier"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type OperationService struct{}
|
|
||||||
|
|
||||||
type IOperationService interface {
|
|
||||||
Page(search dto.PageInfo) (int64, interface{}, error)
|
|
||||||
Create(operation model.OperationLog) error
|
|
||||||
BatchDelete(ids []uint) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIOperationService() IOperationService {
|
|
||||||
return &OperationService{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *OperationService) Create(operation model.OperationLog) error {
|
|
||||||
return operationRepo.Create(&operation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *OperationService) Page(search dto.PageInfo) (int64, interface{}, error) {
|
|
||||||
total, ops, err := operationRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
|
|
||||||
var dtoOps []dto.OperationLogBack
|
|
||||||
for _, op := range ops {
|
|
||||||
var item dto.OperationLogBack
|
|
||||||
if err := copier.Copy(&item, &op); err != nil {
|
|
||||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
|
||||||
}
|
|
||||||
item.Body = filterSensitive(item.Body)
|
|
||||||
var res dto.Response
|
|
||||||
if err := json.Unmarshal([]byte(item.Resp), &res); err != nil {
|
|
||||||
global.LOG.Errorf("unmarshal failed, err: %+v", err)
|
|
||||||
dtoOps = append(dtoOps, item)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
item.Status = res.Code
|
|
||||||
if item.Status != 200 {
|
|
||||||
item.ErrorMessage = res.Msg
|
|
||||||
}
|
|
||||||
dtoOps = append(dtoOps, item)
|
|
||||||
}
|
|
||||||
return total, dtoOps, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *OperationService) BatchDelete(ids []uint) error {
|
|
||||||
return operationRepo.Delete(commonRepo.WithIdsIn(ids))
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterSensitive(vars string) string {
|
|
||||||
var Sensitives = []string{"password", "Password", "credential", "privateKey"}
|
|
||||||
ops := make(map[string]interface{})
|
|
||||||
if err := json.Unmarshal([]byte(vars), &ops); err != nil {
|
|
||||||
return vars
|
|
||||||
}
|
|
||||||
for k := range ops {
|
|
||||||
for _, sen := range Sensitives {
|
|
||||||
if k == sen {
|
|
||||||
delete(ops, k)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
backStr, err := json.Marshal(ops)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return string(backStr)
|
|
||||||
}
|
|
@ -19,6 +19,7 @@ func Init() {
|
|||||||
migrations.AddTableImageRepo,
|
migrations.AddTableImageRepo,
|
||||||
migrations.AddTableWebsite,
|
migrations.AddTableWebsite,
|
||||||
migrations.AddTableDatabaseMysql,
|
migrations.AddTableDatabaseMysql,
|
||||||
|
migrations.AddTableLoginLog,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
var AddTableOperationLog = &gormigrate.Migration{
|
var AddTableOperationLog = &gormigrate.Migration{
|
||||||
ID: "20200809-add-table-operation-log",
|
ID: "20200809-add-table-operation-log",
|
||||||
Migrate: func(tx *gorm.DB) error {
|
Migrate: func(tx *gorm.DB) error {
|
||||||
return tx.AutoMigrate(&model.OperationLog{})
|
return tx.AutoMigrate(&model.OperationLog{}, &model.LoginLog{})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ func Routers() *gin.Engine {
|
|||||||
systemRouter.InitCommandRouter(PrivateGroup)
|
systemRouter.InitCommandRouter(PrivateGroup)
|
||||||
systemRouter.InitTerminalRouter(PrivateGroup)
|
systemRouter.InitTerminalRouter(PrivateGroup)
|
||||||
systemRouter.InitMonitorRouter(PrivateGroup)
|
systemRouter.InitMonitorRouter(PrivateGroup)
|
||||||
systemRouter.InitOperationLogRouter(PrivateGroup)
|
systemRouter.InitLogRouter(PrivateGroup)
|
||||||
systemRouter.InitFileRouter(PrivateGroup)
|
systemRouter.InitFileRouter(PrivateGroup)
|
||||||
systemRouter.InitCronjobRouter(PrivateGroup)
|
systemRouter.InitCronjobRouter(PrivateGroup)
|
||||||
systemRouter.InitSettingRouter(PrivateGroup)
|
systemRouter.InitSettingRouter(PrivateGroup)
|
||||||
|
@ -70,7 +70,7 @@ func OperationRecord() gin.HandlerFunc {
|
|||||||
record.Latency = latency
|
record.Latency = latency
|
||||||
record.Resp = writer.body.String()
|
record.Resp = writer.body.String()
|
||||||
|
|
||||||
if err := service.NewIOperationService().Create(record); err != nil {
|
if err := service.NewILogService().CreateOperationLog(record); err != nil {
|
||||||
global.LOG.Errorf("create operation record failed, err: %v", err)
|
global.LOG.Errorf("create operation record failed, err: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ type RouterGroup struct {
|
|||||||
ContainerRouter
|
ContainerRouter
|
||||||
CommandRouter
|
CommandRouter
|
||||||
MonitorRouter
|
MonitorRouter
|
||||||
OperationLogRouter
|
LogRouter
|
||||||
FileRouter
|
FileRouter
|
||||||
TerminalRouter
|
TerminalRouter
|
||||||
CronjobRouter
|
CronjobRouter
|
||||||
|
@ -2,7 +2,6 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||||
"github.com/1Panel-dev/1Panel/backend/middleware"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,12 +9,11 @@ type BaseRouter struct{}
|
|||||||
|
|
||||||
func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) {
|
func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) {
|
||||||
baseRouter := Router.Group("auth")
|
baseRouter := Router.Group("auth")
|
||||||
withRecordRouter := Router.Group("auth").Use(middleware.OperationRecord())
|
|
||||||
baseApi := v1.ApiGroupApp.BaseApi
|
baseApi := v1.ApiGroupApp.BaseApi
|
||||||
{
|
{
|
||||||
baseRouter.GET("captcha", baseApi.Captcha)
|
baseRouter.GET("captcha", baseApi.Captcha)
|
||||||
baseRouter.POST("mfalogin", baseApi.MFALogin)
|
baseRouter.POST("mfalogin", baseApi.MFALogin)
|
||||||
withRecordRouter.POST("login", baseApi.Login)
|
baseRouter.POST("login", baseApi.Login)
|
||||||
withRecordRouter.POST("logout", baseApi.LogOut)
|
baseRouter.POST("logout", baseApi.LogOut)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,14 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OperationLogRouter struct{}
|
type LogRouter struct{}
|
||||||
|
|
||||||
func (s *OperationLogRouter) InitOperationLogRouter(Router *gin.RouterGroup) {
|
func (s *LogRouter) InitLogRouter(Router *gin.RouterGroup) {
|
||||||
operationRouter := Router.Group("operations")
|
operationRouter := Router.Group("logs")
|
||||||
operationRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
|
operationRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
|
||||||
baseApi := v1.ApiGroupApp.BaseApi
|
baseApi := v1.ApiGroupApp.BaseApi
|
||||||
{
|
{
|
||||||
operationRouter.POST("", baseApi.GetOperationList)
|
operationRouter.POST("login", baseApi.GetLoginLogs)
|
||||||
operationRouter.POST("/del", baseApi.DeleteOperation)
|
operationRouter.POST("operation", baseApi.GetOperationLogs)
|
||||||
}
|
}
|
||||||
}
|
}
|
168
backend/utils/qqwry/qqwry.go
Normal file
168
backend/utils/qqwry/qqwry.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package qqwry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/text/encoding/simplifiedchinese"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
indexLen = 7
|
||||||
|
redirectMode1 = 0x01
|
||||||
|
redirectMode2 = 0x02
|
||||||
|
)
|
||||||
|
|
||||||
|
var IpCommonDictionary []byte
|
||||||
|
|
||||||
|
type QQwry struct {
|
||||||
|
Data []byte
|
||||||
|
Offset int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQQwry() (*QQwry, error) {
|
||||||
|
IpCommonDictionary, err := ioutil.ReadFile("/opt/1Panel/conf/qqwry.dat")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &QQwry{Data: IpCommonDictionary}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readData 从文件中读取数据
|
||||||
|
func (q *QQwry) readData(num int, offset ...int64) (rs []byte) {
|
||||||
|
if len(offset) > 0 {
|
||||||
|
q.setOffset(offset[0])
|
||||||
|
}
|
||||||
|
nums := int64(num)
|
||||||
|
end := q.Offset + nums
|
||||||
|
dataNum := int64(len(q.Data))
|
||||||
|
if q.Offset > dataNum {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if end > dataNum {
|
||||||
|
end = dataNum
|
||||||
|
}
|
||||||
|
rs = q.Data[q.Offset:end]
|
||||||
|
q.Offset = end
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// setOffset 设置偏移量
|
||||||
|
func (q *QQwry) setOffset(offset int64) {
|
||||||
|
q.Offset = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find ip地址查询对应归属地信息
|
||||||
|
func (q *QQwry) Find(ip string) (res ResultQQwry) {
|
||||||
|
res = ResultQQwry{}
|
||||||
|
res.IP = ip
|
||||||
|
if strings.Count(ip, ".") != 3 {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
offset := q.searchIndex(binary.BigEndian.Uint32(net.ParseIP(ip).To4()))
|
||||||
|
if offset <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var area []byte
|
||||||
|
mode := q.readMode(offset + 4)
|
||||||
|
if mode == redirectMode1 {
|
||||||
|
countryOffset := q.readUInt24()
|
||||||
|
mode = q.readMode(countryOffset)
|
||||||
|
if mode == redirectMode2 {
|
||||||
|
c := q.readUInt24()
|
||||||
|
area = q.readString(c)
|
||||||
|
} else {
|
||||||
|
area = q.readString(countryOffset)
|
||||||
|
}
|
||||||
|
} else if mode == redirectMode2 {
|
||||||
|
countryOffset := q.readUInt24()
|
||||||
|
area = q.readString(countryOffset)
|
||||||
|
} else {
|
||||||
|
area = q.readString(offset + 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := simplifiedchinese.GBK.NewDecoder()
|
||||||
|
res.Area, _ = enc.String(string(area))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultQQwry struct {
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Area string `json:"area"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// readMode 获取偏移值类型
|
||||||
|
func (q *QQwry) readMode(offset uint32) byte {
|
||||||
|
mode := q.readData(1, int64(offset))
|
||||||
|
return mode[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// readString 获取字符串
|
||||||
|
func (q *QQwry) readString(offset uint32) []byte {
|
||||||
|
q.setOffset(int64(offset))
|
||||||
|
data := make([]byte, 0, 30)
|
||||||
|
for {
|
||||||
|
buf := q.readData(1)
|
||||||
|
if buf[0] == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data = append(data, buf[0])
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// searchIndex 查找索引位置
|
||||||
|
func (q *QQwry) searchIndex(ip uint32) uint32 {
|
||||||
|
header := q.readData(8, 0)
|
||||||
|
|
||||||
|
start := binary.LittleEndian.Uint32(header[:4])
|
||||||
|
end := binary.LittleEndian.Uint32(header[4:])
|
||||||
|
|
||||||
|
for {
|
||||||
|
mid := q.getMiddleOffset(start, end)
|
||||||
|
buf := q.readData(indexLen, int64(mid))
|
||||||
|
_ip := binary.LittleEndian.Uint32(buf[:4])
|
||||||
|
|
||||||
|
if end-start == indexLen {
|
||||||
|
offset := byteToUInt32(buf[4:])
|
||||||
|
buf = q.readData(indexLen)
|
||||||
|
if ip < binary.LittleEndian.Uint32(buf[:4]) {
|
||||||
|
return offset
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if _ip > ip {
|
||||||
|
end = mid
|
||||||
|
} else if _ip < ip {
|
||||||
|
start = mid
|
||||||
|
} else if _ip == ip {
|
||||||
|
return byteToUInt32(buf[4:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readUInt24
|
||||||
|
func (q *QQwry) readUInt24() uint32 {
|
||||||
|
buf := q.readData(3)
|
||||||
|
return byteToUInt32(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMiddleOffset
|
||||||
|
func (q *QQwry) getMiddleOffset(start uint32, end uint32) uint32 {
|
||||||
|
records := ((end - start) / indexLen) >> 1
|
||||||
|
return start + records*indexLen
|
||||||
|
}
|
||||||
|
|
||||||
|
// byteToUInt32 将 byte 转换为uint32
|
||||||
|
func byteToUInt32(data []byte) uint32 {
|
||||||
|
i := uint32(data[0]) & 0xff
|
||||||
|
i |= (uint32(data[1]) << 8) & 0xff00
|
||||||
|
i |= (uint32(data[2]) << 16) & 0xff0000
|
||||||
|
return i
|
||||||
|
}
|
31
frontend/src/api/interface/log.ts
Normal file
31
frontend/src/api/interface/log.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { DateTimeFormats } from '@intlify/core-base';
|
||||||
|
|
||||||
|
export namespace Log {
|
||||||
|
export interface OperationLog {
|
||||||
|
id: number;
|
||||||
|
group: string;
|
||||||
|
source: string;
|
||||||
|
action: string;
|
||||||
|
ip: string;
|
||||||
|
path: string;
|
||||||
|
method: string;
|
||||||
|
userAgent: string;
|
||||||
|
body: string;
|
||||||
|
resp: string;
|
||||||
|
|
||||||
|
status: number;
|
||||||
|
latency: number;
|
||||||
|
errorMessage: string;
|
||||||
|
|
||||||
|
detail: string;
|
||||||
|
createdAt: DateTimeFormats;
|
||||||
|
}
|
||||||
|
export interface LoginLogs {
|
||||||
|
ip: string;
|
||||||
|
address: string;
|
||||||
|
agent: string;
|
||||||
|
status: string;
|
||||||
|
message: string;
|
||||||
|
createdAt: DateTimeFormats;
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
import { DateTimeFormats } from '@intlify/core-base';
|
|
||||||
|
|
||||||
export interface ResOperationLog {
|
|
||||||
id: number;
|
|
||||||
group: string;
|
|
||||||
source: string;
|
|
||||||
action: string;
|
|
||||||
ip: string;
|
|
||||||
path: string;
|
|
||||||
method: string;
|
|
||||||
userAgent: string;
|
|
||||||
body: string;
|
|
||||||
resp: string;
|
|
||||||
|
|
||||||
status: number;
|
|
||||||
latency: number;
|
|
||||||
errorMessage: string;
|
|
||||||
|
|
||||||
detail: string;
|
|
||||||
createdAt: DateTimeFormats;
|
|
||||||
}
|
|
11
frontend/src/api/modules/log.ts
Normal file
11
frontend/src/api/modules/log.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import http from '@/api';
|
||||||
|
import { ResPage, ReqPage } from '../interface';
|
||||||
|
import { Log } from '../interface/log';
|
||||||
|
|
||||||
|
export const getOperationLogs = (info: ReqPage) => {
|
||||||
|
return http.post<ResPage<Log.OperationLog>>(`/logs/operation`, info);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLoginLogs = (info: ReqPage) => {
|
||||||
|
return http.post<ResPage<Log.OperationLog>>(`/logs/login`, info);
|
||||||
|
};
|
@ -1,7 +0,0 @@
|
|||||||
import http from '@/api';
|
|
||||||
import { ResPage, ReqPage } from '../interface';
|
|
||||||
import { ResOperationLog } from '../interface/operation-log';
|
|
||||||
|
|
||||||
export const getOperationList = (info: ReqPage) => {
|
|
||||||
return http.post<ResPage<ResOperationLog>>(`/operations`, info);
|
|
||||||
};
|
|
@ -142,7 +142,7 @@ export default {
|
|||||||
terminal: 'Terminal',
|
terminal: 'Terminal',
|
||||||
settings: 'Setting',
|
settings: 'Setting',
|
||||||
toolbox: 'Toolbox',
|
toolbox: 'Toolbox',
|
||||||
operations: 'Operation Records',
|
logs: 'Log',
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
welcome: 'Welcome',
|
welcome: 'Welcome',
|
||||||
@ -475,6 +475,13 @@ export default {
|
|||||||
emptyTerminal: 'No terminal is currently connected',
|
emptyTerminal: 'No terminal is currently connected',
|
||||||
},
|
},
|
||||||
operations: {
|
operations: {
|
||||||
|
operation: 'Operation logs',
|
||||||
|
login: 'Login logs',
|
||||||
|
system: 'System logs',
|
||||||
|
loginIP: 'Login IP',
|
||||||
|
loginAddress: 'Login address',
|
||||||
|
loginAgent: 'Login agent',
|
||||||
|
loginStatus: 'Login status',
|
||||||
detail: {
|
detail: {
|
||||||
users: 'User',
|
users: 'User',
|
||||||
hosts: 'Host',
|
hosts: 'Host',
|
||||||
@ -483,6 +490,7 @@ export default {
|
|||||||
backups: 'Backup Account',
|
backups: 'Backup Account',
|
||||||
settings: 'Panel Setting',
|
settings: 'Panel Setting',
|
||||||
cronjobs: 'Cronjob',
|
cronjobs: 'Cronjob',
|
||||||
|
databases: 'Database',
|
||||||
status: ' Update status',
|
status: ' Update status',
|
||||||
auth: 'User',
|
auth: 'User',
|
||||||
login: ' login',
|
login: ' login',
|
||||||
@ -493,7 +501,6 @@ export default {
|
|||||||
delete: ' delete',
|
delete: ' delete',
|
||||||
del: 'delete',
|
del: 'delete',
|
||||||
},
|
},
|
||||||
operatoin: 'operatoin',
|
|
||||||
status: 'status',
|
status: 'status',
|
||||||
request: 'request',
|
request: 'request',
|
||||||
response: 'response',
|
response: 'response',
|
||||||
|
@ -142,7 +142,7 @@ export default {
|
|||||||
terminal: '终端',
|
terminal: '终端',
|
||||||
settings: '面板设置',
|
settings: '面板设置',
|
||||||
toolbox: '工具箱',
|
toolbox: '工具箱',
|
||||||
operations: '操作日志',
|
logs: '面板日志',
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
welcome: '欢迎使用',
|
welcome: '欢迎使用',
|
||||||
@ -487,7 +487,14 @@ export default {
|
|||||||
key: '密钥',
|
key: '密钥',
|
||||||
emptyTerminal: '暂无终端连接',
|
emptyTerminal: '暂无终端连接',
|
||||||
},
|
},
|
||||||
operations: {
|
logs: {
|
||||||
|
operation: '操作日志',
|
||||||
|
login: '登录日志',
|
||||||
|
loginIP: '登录 IP',
|
||||||
|
loginAddress: '登录地址',
|
||||||
|
loginAgent: '用户代理',
|
||||||
|
loginStatus: '登录状态',
|
||||||
|
system: '系统日志',
|
||||||
detail: {
|
detail: {
|
||||||
users: '用户',
|
users: '用户',
|
||||||
hosts: '主机',
|
hosts: '主机',
|
||||||
@ -496,6 +503,7 @@ export default {
|
|||||||
backups: '备份账号',
|
backups: '备份账号',
|
||||||
settings: '面板设置',
|
settings: '面板设置',
|
||||||
cronjobs: '计划任务',
|
cronjobs: '计划任务',
|
||||||
|
databases: '数据库',
|
||||||
status: '状态修改',
|
status: '状态修改',
|
||||||
auth: '用户',
|
auth: '用户',
|
||||||
post: '创建',
|
post: '创建',
|
||||||
@ -503,6 +511,8 @@ export default {
|
|||||||
update: '更新',
|
update: '更新',
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
login: '登录',
|
login: '登录',
|
||||||
|
backup: '备份',
|
||||||
|
recover: '恢复',
|
||||||
logout: '退出',
|
logout: '退出',
|
||||||
del: '删除',
|
del: '删除',
|
||||||
},
|
},
|
||||||
|
43
frontend/src/routers/modules/log.ts
Normal file
43
frontend/src/routers/modules/log.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Layout } from '@/routers/constant';
|
||||||
|
|
||||||
|
const logsRouter = {
|
||||||
|
sort: 10,
|
||||||
|
path: '/logs',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/logs',
|
||||||
|
meta: {
|
||||||
|
title: 'menu.logs',
|
||||||
|
icon: 'p-log',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'LoginLog',
|
||||||
|
component: () => import('@/views/log/login/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/logs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'operation',
|
||||||
|
name: 'OperationLog',
|
||||||
|
component: () => import('@/views/log/operation/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/logs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'system',
|
||||||
|
name: 'SystemLog',
|
||||||
|
component: () => import('@/views/log/system/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/logs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default logsRouter;
|
@ -1,22 +0,0 @@
|
|||||||
import { Layout } from '@/routers/constant';
|
|
||||||
|
|
||||||
const operationRouter = {
|
|
||||||
sort: 10,
|
|
||||||
path: '/operations',
|
|
||||||
component: Layout,
|
|
||||||
redirect: '/operation',
|
|
||||||
meta: {
|
|
||||||
title: 'menu.operations',
|
|
||||||
icon: 'p-log',
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '/operation',
|
|
||||||
name: 'OperationLog',
|
|
||||||
component: () => import('@/views/operation-log/index.vue'),
|
|
||||||
meta: {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default operationRouter;
|
|
@ -332,7 +332,7 @@ const weekOptions = [
|
|||||||
const timeRangeLoad = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
const timeRangeLoad = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||||
const searchInfo = reactive({
|
const searchInfo = reactive({
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 5,
|
pageSize: 10,
|
||||||
recordTotal: 0,
|
recordTotal: 0,
|
||||||
cronjobID: 0,
|
cronjobID: 0,
|
||||||
startTime: new Date(new Date().setHours(0, 0, 0, 0)),
|
startTime: new Date(new Date().setHours(0, 0, 0, 0)),
|
||||||
|
@ -49,14 +49,11 @@ const data = ref();
|
|||||||
const selects = ref<any>([]);
|
const selects = ref<any>([]);
|
||||||
const paginationConfig = reactive({
|
const paginationConfig = reactive({
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 5,
|
pageSize: 10,
|
||||||
total: 0,
|
total: 0,
|
||||||
});
|
});
|
||||||
const commandSearch = reactive({
|
const info = ref();
|
||||||
page: 1,
|
|
||||||
pageSize: 5,
|
|
||||||
info: '',
|
|
||||||
});
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
const commandInfoRef = ref<FormInstance>();
|
const commandInfoRef = ref<FormInstance>();
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
@ -136,9 +133,12 @@ const buttons = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
commandSearch.page = paginationConfig.page;
|
let params = {
|
||||||
commandSearch.pageSize = paginationConfig.pageSize;
|
page: paginationConfig.page,
|
||||||
const res = await getCommandPage(commandSearch);
|
pageSize: paginationConfig.pageSize,
|
||||||
|
info: info.value,
|
||||||
|
};
|
||||||
|
const res = await getCommandPage(params);
|
||||||
data.value = res.data.items;
|
data.value = res.data.items;
|
||||||
paginationConfig.total = res.data.total;
|
paginationConfig.total = res.data.total;
|
||||||
};
|
};
|
||||||
|
73
frontend/src/views/log/index.vue
Normal file
73
frontend/src/views/log/index.vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card class="topCard">
|
||||||
|
<el-radio-group v-model="active">
|
||||||
|
<el-radio-button class="topButton" size="large" @click="routerTo('/logs')" label="login">
|
||||||
|
{{ $t('logs.login') }}
|
||||||
|
</el-radio-button>
|
||||||
|
<el-radio-button class="topButton" size="large" @click="routerTo('/logs/operation')" label="operation">
|
||||||
|
{{ $t('logs.operation') }}
|
||||||
|
</el-radio-button>
|
||||||
|
<el-radio-button class="topButton" size="large" @click="routerTo('/logs/system')" label="system">
|
||||||
|
{{ $t('logs.system') }}
|
||||||
|
</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const router = useRouter();
|
||||||
|
interface MenuProps {
|
||||||
|
activeName: string;
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<MenuProps>(), {
|
||||||
|
activeName: 'operation',
|
||||||
|
});
|
||||||
|
|
||||||
|
const active = ref('operation');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.activeName) {
|
||||||
|
active.value = props.activeName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const routerTo = (path: string) => {
|
||||||
|
router.push({ path: path });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.topCard {
|
||||||
|
--el-card-border-color: var(--el-border-color-light);
|
||||||
|
--el-card-border-radius: 4px;
|
||||||
|
--el-card-padding: 0px;
|
||||||
|
--el-card-bg-color: var(--el-fill-color-blank);
|
||||||
|
}
|
||||||
|
.topButton .el-radio-button__inner {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
background: var(--el-button-bg-color, var(--el-fill-color-blank));
|
||||||
|
border: 0;
|
||||||
|
font-weight: 350;
|
||||||
|
border-left: 0;
|
||||||
|
color: var(--el-button-text-color, var(--el-text-color-regular));
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
outline: 0;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--el-transition-all);
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
padding: 8px 15px;
|
||||||
|
font-size: var(--el-font-size-base);
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
</style>
|
69
frontend/src/views/log/login/index.vue
Normal file
69
frontend/src/views/log/login/index.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Submenu activeName="login" />
|
||||||
|
<el-card style="margin-top: 20px">
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search">
|
||||||
|
<el-table-column min-width="40" :label="$t('logs.loginIP')" prop="ip" />
|
||||||
|
<el-table-column min-width="40" :label="$t('logs.loginAddress')" prop="address" />
|
||||||
|
<el-table-column :label="$t('logs.loginAgent')" show-overflow-tooltip prop="agent" />
|
||||||
|
<el-table-column min-width="40" :label="$t('logs.loginStatus')" prop="status">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row.status === 'Success'">
|
||||||
|
<el-tag type="success">{{ row.status }}</el-tag>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<el-tooltip class="box-item" effect="dark" :content="row.message" placement="top-start">
|
||||||
|
<el-tag type="danger">{{ row.status }}</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="createdAt"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
:formatter="dateFromat"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
|
import { dateFromat } from '@/utils/util';
|
||||||
|
import { getLoginLogs } from '@/api/modules/log';
|
||||||
|
import Submenu from '@/views/log/index.vue';
|
||||||
|
import { onMounted, reactive, ref } from '@vue/runtime-core';
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 15,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
let params = {
|
||||||
|
page: paginationConfig.currentPage,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
};
|
||||||
|
const res = await getLoginLogs(params);
|
||||||
|
data.value = res.data.items;
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
white-space: -moz-pre-wrap;
|
||||||
|
white-space: -pre-wrap;
|
||||||
|
white-space: -o-pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
147
frontend/src/views/log/operation/index.vue
Normal file
147
frontend/src/views/log/operation/index.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Submenu activeName="operation" />
|
||||||
|
<el-card style="margin-top: 20px">
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search">
|
||||||
|
<el-table-column :label="$t('logs.operatoin')" fix>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ fmtOperation(row) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('logs.status')" prop="status">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.status == '200'" class="ml-2" type="success">{{ row.status }}</el-tag>
|
||||||
|
<div v-else>
|
||||||
|
<el-popover
|
||||||
|
placement="top-start"
|
||||||
|
:title="$t('commons.table.message')"
|
||||||
|
:width="400"
|
||||||
|
trigger="hover"
|
||||||
|
:content="row.errorMessage"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-tag class="ml-2" type="warning">{{ row.status }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="IP" prop="ip" />
|
||||||
|
<el-table-column :label="$t('logs.request')" prop="path">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>
|
||||||
|
<el-popover :width="500" v-if="row.body" placement="left-start" trigger="click">
|
||||||
|
<div style="word-wrap: break-word; font-size: 12px; white-space: normal">
|
||||||
|
<pre class="pre">{{ fmtBody(row.body) }}</pre>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<el-icon style="cursor: pointer"><warning /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('logs.response')" prop="path">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>
|
||||||
|
<el-popover :width="500" v-if="row.resp" placement="left-start" trigger="click">
|
||||||
|
<div style="word-wrap: break-word; font-size: 12px; white-space: normal">
|
||||||
|
<pre class="pre">{{ fmtBody(row.resp) }}</pre>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<el-icon style="cursor: pointer"><warning /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="createdAt"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
:formatter="dateFromat"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
|
import { dateFromat } from '@/utils/util';
|
||||||
|
import { getOperationLogs } from '@/api/modules/log';
|
||||||
|
import Submenu from '@/views/log/index.vue';
|
||||||
|
import { onMounted, reactive, ref } from '@vue/runtime-core';
|
||||||
|
import { Log } from '@/api/interface/log';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 15,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
let params = {
|
||||||
|
page: paginationConfig.currentPage,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
};
|
||||||
|
const res = await getOperationLogs(params);
|
||||||
|
data.value = res.data.items;
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fmtOperation = (row: Log.OperationLog) => {
|
||||||
|
if (row.method.toLocaleLowerCase() === 'post') {
|
||||||
|
if (row.source == '' && row.action == '') {
|
||||||
|
return (
|
||||||
|
i18n.global.t('logs.detail.' + row.group.toLocaleLowerCase()) +
|
||||||
|
i18n.global.t('logs.detail.' + row.method.toLocaleLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (row.action == '') {
|
||||||
|
return (
|
||||||
|
i18n.global.t('logs.detail.' + row.group.toLocaleLowerCase()) +
|
||||||
|
i18n.global.t('logs.detail.' + row.source.toLocaleLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (row.action == '') {
|
||||||
|
return (
|
||||||
|
i18n.global.t('logs.detail.' + row.group.toLocaleLowerCase()) +
|
||||||
|
i18n.global.t('logs.detail.' + row.method.toLocaleLowerCase())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
i18n.global.t('logs.detail.' + row.group.toLocaleLowerCase()) +
|
||||||
|
i18n.global.t('logs.detail.' + row.source.toLocaleLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fmtBody = (value: string) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch (err) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
white-space: -moz-pre-wrap;
|
||||||
|
white-space: -pre-wrap;
|
||||||
|
white-space: -o-pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
42
frontend/src/views/log/system/index.vue
Normal file
42
frontend/src/views/log/system/index.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Submenu activeName="system" />
|
||||||
|
<el-card style="margin-top: 20px">
|
||||||
|
<codemirror
|
||||||
|
:autofocus="true"
|
||||||
|
placeholder="None data"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
style="height: calc(100vh - 150px)"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
v-model="logs"
|
||||||
|
:readOnly="true"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import Submenu from '@/views/log/index.vue';
|
||||||
|
import { LoadFile } from '@/api/modules/files';
|
||||||
|
|
||||||
|
const extensions = [javascript(), oneDark];
|
||||||
|
const logs = ref();
|
||||||
|
|
||||||
|
const loadSystemlogs = async () => {
|
||||||
|
const res = await LoadFile({ path: '/opt/1Panel/log/1Panel.log' });
|
||||||
|
logs.value = res.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadSystemlogs();
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,141 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search">
|
|
||||||
<el-table-column :label="$t('operations.operatoin')" fix>
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ fmtOperation(row) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="$t('operations.status')" prop="status">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-tag v-if="row.status == '200'" class="ml-2" type="success">{{ row.status }}</el-tag>
|
|
||||||
<div v-else>
|
|
||||||
<el-popover
|
|
||||||
placement="top-start"
|
|
||||||
:title="$t('commons.table.message')"
|
|
||||||
:width="400"
|
|
||||||
trigger="hover"
|
|
||||||
:content="row.errorMessage"
|
|
||||||
>
|
|
||||||
<template #reference>
|
|
||||||
<el-tag class="ml-2" type="warning">{{ row.status }}</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="IP" prop="ip" />
|
|
||||||
<el-table-column :label="$t('operations.request')" prop="path">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<div>
|
|
||||||
<el-popover :width="500" v-if="row.body" placement="left-start" trigger="click">
|
|
||||||
<div style="word-wrap: break-word; font-size: 12px; white-space: normal">
|
|
||||||
<pre class="pre">{{ fmtBody(row.body) }}</pre>
|
|
||||||
</div>
|
|
||||||
<template #reference>
|
|
||||||
<el-icon style="cursor: pointer"><warning /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
<span v-else>无</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="$t('operations.response')" prop="path">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<div>
|
|
||||||
<el-popover :width="500" v-if="row.resp" placement="left-start" trigger="click">
|
|
||||||
<div style="word-wrap: break-word; font-size: 12px; white-space: normal">
|
|
||||||
<pre class="pre">{{ fmtBody(row.resp) }}</pre>
|
|
||||||
</div>
|
|
||||||
<template #reference>
|
|
||||||
<el-icon style="cursor: pointer"><warning /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
<span v-else>无</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="createdAt"
|
|
||||||
:label="$t('commons.table.date')"
|
|
||||||
:formatter="dateFromat"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
|
||||||
</ComplexTable>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import ComplexTable from '@/components/complex-table/index.vue';
|
|
||||||
import { dateFromat } from '@/utils/util';
|
|
||||||
import { getOperationList } from '@/api/modules/operation-log';
|
|
||||||
import { onMounted, reactive, ref } from '@vue/runtime-core';
|
|
||||||
import { ResOperationLog } from '@/api/interface/operation-log';
|
|
||||||
import i18n from '@/lang';
|
|
||||||
|
|
||||||
const data = ref();
|
|
||||||
const paginationConfig = reactive({
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 5,
|
|
||||||
total: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
const search = async () => {
|
|
||||||
let params = {
|
|
||||||
page: paginationConfig.currentPage,
|
|
||||||
pageSize: paginationConfig.pageSize,
|
|
||||||
};
|
|
||||||
const res = await getOperationList(params);
|
|
||||||
data.value = res.data.items;
|
|
||||||
paginationConfig.total = res.data.total;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fmtOperation = (row: ResOperationLog) => {
|
|
||||||
if (row.method.toLocaleLowerCase() === 'post') {
|
|
||||||
if (row.source == '' && row.action == '') {
|
|
||||||
return (
|
|
||||||
i18n.global.t('operations.detail.' + row.group.toLocaleLowerCase()) +
|
|
||||||
i18n.global.t('operations.detail.' + row.method.toLocaleLowerCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (row.action == '') {
|
|
||||||
return (
|
|
||||||
i18n.global.t('operations.detail.' + row.group.toLocaleLowerCase()) +
|
|
||||||
i18n.global.t('operations.detail.' + row.source.toLocaleLowerCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (row.action == '') {
|
|
||||||
return (
|
|
||||||
i18n.global.t('operations.detail.' + row.group.toLocaleLowerCase()) +
|
|
||||||
i18n.global.t('operations.detail.' + row.method.toLocaleLowerCase())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
i18n.global.t('operations.detail.' + row.group.toLocaleLowerCase()) +
|
|
||||||
i18n.global.t('operations.detail.' + row.source.toLocaleLowerCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fmtBody = (value: string) => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(value);
|
|
||||||
} catch (err) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
search();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
white-space: -moz-pre-wrap;
|
|
||||||
white-space: -pre-wrap;
|
|
||||||
white-space: -o-pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
</style>
|
|
3
go.mod
3
go.mod
@ -25,7 +25,7 @@ require (
|
|||||||
github.com/gwatts/gin-adapter v1.0.0
|
github.com/gwatts/gin-adapter v1.0.0
|
||||||
github.com/jinzhu/copier v0.3.5
|
github.com/jinzhu/copier v0.3.5
|
||||||
github.com/joho/godotenv v1.4.0
|
github.com/joho/godotenv v1.4.0
|
||||||
github.com/kr/pty v1.1.5
|
github.com/kr/pty v1.1.8
|
||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7
|
github.com/mholt/archiver/v4 v4.0.0-alpha.7
|
||||||
github.com/minio/minio-go/v7 v7.0.36
|
github.com/minio/minio-go/v7 v7.0.36
|
||||||
github.com/mojocn/base64Captcha v1.3.5
|
github.com/mojocn/base64Captcha v1.3.5
|
||||||
@ -69,6 +69,7 @@ require (
|
|||||||
github.com/containerd/cgroups v1.0.3 // indirect
|
github.com/containerd/cgroups v1.0.3 // indirect
|
||||||
github.com/containerd/containerd v1.6.8 // indirect
|
github.com/containerd/containerd v1.6.8 // indirect
|
||||||
github.com/containerd/continuity v0.3.0 // indirect
|
github.com/containerd/continuity v0.3.0 // indirect
|
||||||
|
github.com/creack/pty v1.1.11 // indirect
|
||||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||||
github.com/distribution/distribution/v3 v3.0.0-20220725133111-4bf3547399eb // indirect
|
github.com/distribution/distribution/v3 v3.0.0-20220725133111-4bf3547399eb // indirect
|
||||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -314,6 +314,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
@ -709,8 +710,9 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
|
|||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
|
|
||||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||||
|
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user