diff --git a/.gitignore b/.gitignore index 398baf21b..b2dd901d3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ # vendor/ .idea +backend/__debug_bin diff --git a/backend/app/api/v1/entry.go b/backend/app/api/v1/entry.go index 9376f672d..bfc3aa075 100644 --- a/backend/app/api/v1/entry.go +++ b/backend/app/api/v1/entry.go @@ -9,5 +9,6 @@ type ApiGroup struct { var ApiGroupApp = new(ApiGroup) var ( - userService = service.ServiceGroupApp.UserService + userService = service.ServiceGroupApp.UserService + operationService = service.ServiceGroupApp.OperationService ) diff --git a/backend/app/api/v1/helper/helper.go b/backend/app/api/v1/helper/helper.go index d6127da44..ec7fbc39a 100644 --- a/backend/app/api/v1/helper/helper.go +++ b/backend/app/api/v1/helper/helper.go @@ -12,23 +12,23 @@ import ( "github.com/gin-gonic/gin" ) -func GeneratePaginationFromReq(c *gin.Context) (dto.PageInfo, bool) { +func GeneratePaginationFromReq(c *gin.Context) (*dto.PageInfo, bool) { p, ok1 := c.GetQuery("page") ps, ok2 := c.GetQuery("pageSize") if !(ok1 && ok2) { - return dto.PageInfo{Page: 1, PageSize: 10}, false + return nil, false } page, err := strconv.Atoi(p) if err != nil { - return dto.PageInfo{Page: 1, PageSize: 10}, false + return nil, false } pageSize, err := strconv.Atoi(ps) if err != nil { - return dto.PageInfo{Page: 1, PageSize: 10}, false + return nil, false } - return dto.PageInfo{Page: page, PageSize: pageSize}, false + return &dto.PageInfo{Page: page, PageSize: pageSize}, true } func ErrorWithDetail(ctx *gin.Context, code int, msgKey string, err error) { diff --git a/backend/app/api/v1/operation_log.go b/backend/app/api/v1/operation_log.go new file mode 100644 index 000000000..ab32a7faf --- /dev/null +++ b/backend/app/api/v1/operation_log.go @@ -0,0 +1,27 @@ +package v1 + +import ( + "github.com/1Panel-dev/1Panel/app/api/v1/helper" + "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/constant" + "github.com/gin-gonic/gin" +) + +func (b *BaseApi) GetOperationList(c *gin.Context) { + pagenation, isOK := helper.GeneratePaginationFromReq(c) + if !isOK { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, constant.ErrPageGenerate) + return + } + + total, list, err := operationService.Page(pagenation.Page, pagenation.PageSize) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, dto.PageResult{ + Items: list, + Total: total, + }) +} diff --git a/backend/app/api/v1/user.go b/backend/app/api/v1/user.go index b557e7aaa..e1c974869 100644 --- a/backend/app/api/v1/user.go +++ b/backend/app/api/v1/user.go @@ -36,6 +36,14 @@ func (b *BaseApi) Login(c *gin.Context) { helper.SuccessWithData(c, user) } +func (b *BaseApi) LogOut(c *gin.Context) { + if err := userService.LogOut(c); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + func (b *BaseApi) Captcha(c *gin.Context) { captcha, err := captcha.CreateCaptcha() if err != nil { diff --git a/backend/app/dto/operation_log.go b/backend/app/dto/operation_log.go new file mode 100644 index 000000000..32768b328 --- /dev/null +++ b/backend/app/dto/operation_log.go @@ -0,0 +1,25 @@ +package dto + +import ( + "time" +) + +type OperationLogBack struct { + Group string `json:"group"` + Source string `json:"source"` + Action string `json:"action"` + + IP string `json:"ip"` + Path string `json:"path"` + Method string `json:"method"` + UserAgent string `json:"userAgent"` + Body string `json:"body"` + Resp string `json:"resp"` + + Status int `json:"status"` + Latency time.Duration `json:"latency"` + ErrorMessage string `json:"errorMessage"` + + Detail string `json:"detail"` + CreatedAt time.Time `json:"createdAt"` +} diff --git a/backend/app/model/operate_log.go b/backend/app/model/operate_log.go index 0be5f9d59..ae48fa347 100644 --- a/backend/app/model/operate_log.go +++ b/backend/app/model/operate_log.go @@ -8,20 +8,18 @@ import ( type OperationLog struct { gorm.Model - Group string `gorm:"type:varchar(64)" json:"type"` + Group string `gorm:"type:varchar(64)" json:"group"` Source string `gorm:"type:varchar(64)" json:"source"` Action string `gorm:"type:varchar(64)" json:"action"` IP string `gorm:"type:varchar(64)" json:"ip"` Path string `gorm:"type:varchar(64)" json:"path"` Method string `gorm:"type:varchar(64)" json:"method"` - UserAgent string `gorm:"type:text(65535)" json:"userAgent"` - Body string `gorm:"type:text(65535)" json:"body"` - Resp string `gorm:"type:text(65535)" json:"resp"` + UserAgent string `gorm:"type:varchar(256)" json:"userAgent"` + Body string `gorm:"type:longText" json:"body"` + Resp string `gorm:"type:longText" json:"resp"` - Status int `gorm:"type:varchar(64)" json:"status"` - Latency time.Duration `gorm:"type:varchar(64)" json:"latency"` - ErrorMessage string `gorm:"type:varchar(256)" json:"errorMessage"` + Latency time.Duration `gorm:"type:varchar(64)" json:"latency"` Detail string `gorm:"type:longText" json:"detail"` } diff --git a/backend/app/repo/operation_log.go b/backend/app/repo/operation_log.go index f3e21ce68..67b24b97b 100644 --- a/backend/app/repo/operation_log.go +++ b/backend/app/repo/operation_log.go @@ -9,6 +9,7 @@ type OperationRepo struct{} type IOperationRepo interface { Create(user *model.OperationLog) error + Page(limit, offset int, opts ...DBOption) (int64, []model.OperationLog, error) } func NewIOperationService() IOperationRepo { @@ -18,3 +19,15 @@ func NewIOperationService() IOperationRepo { 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 +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index 5bb09146f..fdabc3884 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -4,6 +4,7 @@ import "github.com/1Panel-dev/1Panel/app/repo" type ServiceGroup struct { UserService + OperationService } var ServiceGroupApp = new(ServiceGroup) diff --git a/backend/app/service/operation_log.go b/backend/app/service/operation_log.go index 37f16e07b..8caf1ba4c 100644 --- a/backend/app/service/operation_log.go +++ b/backend/app/service/operation_log.go @@ -1,10 +1,20 @@ package service -import "github.com/1Panel-dev/1Panel/app/model" +import ( + "encoding/json" + + "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/app/model" + "github.com/1Panel-dev/1Panel/constant" + "github.com/1Panel-dev/1Panel/global" + "github.com/jinzhu/copier" + "github.com/pkg/errors" +) type OperationService struct{} type IOperationService interface { + Page(page, size int) (int64, interface{}, error) Create(operation model.OperationLog) error } @@ -15,3 +25,48 @@ func NewIOperationService() IOperationService { func (u *OperationService) Create(operation model.OperationLog) error { return operationRepo.Create(&operation) } + +func (u *OperationService) Page(page, size int) (int64, interface{}, error) { + total, ops, err := operationRepo.Page(page, size, 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 filterSensitive(vars string) string { + var Sensitives = []string{"password", "Password"} + ops := make(map[string]string) + 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) +} diff --git a/backend/app/service/user.go b/backend/app/service/user.go index d850e3143..2a34a6c36 100644 --- a/backend/app/service/user.go +++ b/backend/app/service/user.go @@ -21,6 +21,7 @@ type IUserService interface { Page(search dto.UserPage) (int64, interface{}, error) Register(userDto dto.UserCreate) error Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) + LogOut(c *gin.Context) error Delete(name string) error Save(req model.User) error Update(upMap map[string]interface{}) error @@ -33,7 +34,7 @@ func NewIUserService() IUserService { func (u *UserService) Get(name string) (*dto.UserBack, error) { user, err := userRepo.Get(commonRepo.WithByName(name)) if err != nil { - return nil, err + return nil, constant.ErrRecordNotFound } var dtoUser dto.UserBack if err := copier.Copy(&dtoUser, &user); err != nil { @@ -56,14 +57,13 @@ func (u *UserService) Page(search dto.UserPage) (int64, interface{}, error) { } func (u *UserService) Register(userDto dto.UserCreate) error { - var user model.User + user, _ := userRepo.Get(commonRepo.WithByName(userDto.Name)) + if user.ID != 0 { + return constant.ErrRecordExist + } if err := copier.Copy(&user, &userDto); err != nil { return errors.WithMessage(constant.ErrStructTransform, err.Error()) } - _ = global.DB.Where("name = ?", user.Name).First(&user).Error - if user.ID != 0 { - return errors.Wrap(constant.ErrRecordExist, "data exist") - } return userRepo.Create(&user) } @@ -111,6 +111,23 @@ func (u *UserService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, return &dto.UserLoginInfo{Name: user.Name}, err } +func (u *UserService) LogOut(c *gin.Context) error { + sID, _ := c.Cookie(global.CONF.Session.SessionName) + if sID != "" { + c.SetCookie(global.CONF.Session.SessionName, "", -1, "", "", false, false) + } + sessionItem, err := global.SESSION.Get(c.Request, global.CONF.Session.SessionName) + if err != nil { + return err + } + sessionItem.Options.MaxAge = -1 + if err := global.SESSION.Save(c.Request, c.Writer, sessionItem); err != nil { + return err + } + + return nil +} + func (u *UserService) Delete(name string) error { return userRepo.Delete(commonRepo.WithByName(name)) } diff --git a/backend/init/router/router.go b/backend/init/router/router.go index 384e03b1b..c87af5d5e 100644 --- a/backend/init/router/router.go +++ b/backend/init/router/router.go @@ -33,12 +33,11 @@ func Routers() *gin.Engine { c.JSON(200, "ok") }) } + PrivateGroup := Router.Group("/api/v1") { - systemRouter.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权 - } - PrivateGroup := Router.Group("") - { - systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由 + systemRouter.InitBaseRouter(PrivateGroup) + systemRouter.InitUserRouter(PrivateGroup) + systemRouter.InitOperationLogRouter(PrivateGroup) } return Router diff --git a/backend/middleware/operation.go b/backend/middleware/operation.go index 8a2ff7734..8d6310161 100644 --- a/backend/middleware/operation.go +++ b/backend/middleware/operation.go @@ -2,10 +2,8 @@ package middleware import ( "bytes" - "encoding/json" "io/ioutil" "net/http" - "net/url" "strings" "time" @@ -18,26 +16,17 @@ import ( func OperationRecord() gin.HandlerFunc { return func(c *gin.Context) { var body []byte - if c.Request.Method != http.MethodGet { - var err error - body, err = ioutil.ReadAll(c.Request.Body) - if err != nil { - global.LOG.Errorf("read body from request failed, err: %v", err) - } else { - c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - } + if c.Request.Method == http.MethodGet || strings.Contains(c.Request.URL.Path, "search") { + c.Next() + return + } + + var err error + body, err = ioutil.ReadAll(c.Request.Body) + if err != nil { + global.LOG.Errorf("read body from request failed, err: %v", err) } else { - query := c.Request.URL.RawQuery - query, _ = url.QueryUnescape(query) - split := strings.Split(query, "&") - m := make(map[string]string) - for _, v := range split { - kv := strings.Split(v, "=") - if len(kv) == 2 { - m[kv[0]] = kv[1] - } - } - body, _ = json.Marshal(&m) + c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) } pathInfo := loadLogInfo(c.Request.URL.Path) @@ -62,8 +51,6 @@ func OperationRecord() gin.HandlerFunc { c.Next() latency := time.Since(now) - record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String() - record.Status = c.Writer.Status() record.Latency = latency record.Resp = writer.body.String() @@ -90,6 +77,7 @@ type pathInfo struct { } func loadLogInfo(path string) pathInfo { + path = strings.ReplaceAll(path, "/api/v1", "") if !strings.Contains(path, "/") { return pathInfo{} } diff --git a/backend/router/entry.go b/backend/router/entry.go index 9f86ba974..f4d080cb5 100644 --- a/backend/router/entry.go +++ b/backend/router/entry.go @@ -3,6 +3,7 @@ package router type RouterGroup struct { BaseRouter UserRouter + OperationLogRouter } var RouterGroupApp = new(RouterGroup) diff --git a/backend/router/operation_log.go b/backend/router/operation_log.go new file mode 100644 index 000000000..5d2864bac --- /dev/null +++ b/backend/router/operation_log.go @@ -0,0 +1,19 @@ +package router + +import ( + v1 "github.com/1Panel-dev/1Panel/app/api/v1" + "github.com/1Panel-dev/1Panel/middleware" + + "github.com/gin-gonic/gin" +) + +type OperationLogRouter struct{} + +func (s *OperationLogRouter) InitOperationLogRouter(Router *gin.RouterGroup) { + operationRouter := Router.Group("operations") + operationRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) + baseApi := v1.ApiGroupApp.BaseApi + { + operationRouter.GET("", baseApi.GetOperationList) + } +} diff --git a/backend/router/ro_base.go b/backend/router/ro_base.go index 0c06b8674..f474259f7 100644 --- a/backend/router/ro_base.go +++ b/backend/router/ro_base.go @@ -14,6 +14,7 @@ func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) { baseApi := v1.ApiGroupApp.BaseApi { withRecordRouter.POST("login", baseApi.Login) + withRecordRouter.POST("logout", baseApi.LogOut) baseRouter.GET("captcha", baseApi.Captcha) } return baseRouter diff --git a/backend/utils/copier/copier.go b/backend/utils/copier/copier.go index eb99b8978..bfc684ce9 100644 --- a/backend/utils/copier/copier.go +++ b/backend/utils/copier/copier.go @@ -11,11 +11,8 @@ func Copy(to, from interface{}) error { if err != nil { return errors.Wrap(err, "marshal from data err") } - - err = json.Unmarshal(b, to) - if err != nil { + if err = json.Unmarshal(b, to); err != nil { return errors.Wrap(err, "unmarshal to data err") } - return nil } diff --git a/frontend/.env.development b/frontend/.env.development index 6713236c1..9bc64bdc6 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -2,4 +2,4 @@ NODE_ENV = 'development' # 本地环境接口地址 -VITE_API_URL = '/api' \ No newline at end of file +VITE_API_URL = '/api/v1' \ No newline at end of file diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 8c4ab9027..afad8957a 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -6,7 +6,14 @@ import { ResultEnum } from '@/enums/http-enum'; import { checkStatus } from './helper/check-status'; import { ElMessage } from 'element-plus'; import router from '@/routers'; -import i18n from '@/lang'; + +/** + * pinia 错误使用说明示例 + * https://github.com/vuejs/pinia/discussions/971 + * https://github.com/vuejs/pinia/discussions/664#discussioncomment-1329898 + * https://pinia.vuejs.org/core-concepts/outside-component-usage.html#single-page-applications + */ +// const globalStore = GlobalStore(); const axiosCanceler = new AxiosCanceler(); @@ -34,10 +41,6 @@ class RequestHttp { }, ); - /** - * @description 响应拦截器 - * 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息 - */ this.service.interceptors.response.use( (response: AxiosResponse) => { const { data, config } = response; @@ -59,8 +62,7 @@ class RequestHttp { async (error: AxiosError) => { const { response } = error; tryHideFullScreenLoading(); - if (error.message.indexOf('timeout') !== -1) - ElMessage.error(i18n.global.t('commons.msg.requestTimeout')); + if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时!请您稍后重试'); if (response) checkStatus(response.status); if (!window.navigator.onLine) router.replace({ path: '/500' }); return Promise.reject(error); @@ -68,7 +70,6 @@ class RequestHttp { ); } - // * 常用请求方法封装 get(url: string, params?: object, _object = {}): Promise> { return this.service.get(url, { params, ..._object }); } diff --git a/frontend/src/api/interface/index.ts b/frontend/src/api/interface/index.ts index d7a2d1eb5..8beffffce 100644 --- a/frontend/src/api/interface/index.ts +++ b/frontend/src/api/interface/index.ts @@ -5,7 +5,7 @@ export interface Result { } // * 请求响应参数(包含data) -export interface ResultData { +export interface ResultData { code: number; message: string; data: T; diff --git a/frontend/src/api/interface/operation-log.ts b/frontend/src/api/interface/operation-log.ts new file mode 100644 index 000000000..23c3e0b1f --- /dev/null +++ b/frontend/src/api/interface/operation-log.ts @@ -0,0 +1,20 @@ +import { DateTimeFormats } from '@intlify/core-base'; + +export interface ResOperationLog { + 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; +} diff --git a/frontend/src/api/modules/login.ts b/frontend/src/api/modules/login.ts index b959a62a4..6481acff6 100644 --- a/frontend/src/api/modules/login.ts +++ b/frontend/src/api/modules/login.ts @@ -8,3 +8,7 @@ export const loginApi = (params: Login.ReqLoginForm) => { export const getCaptcha = () => { return http.get(`/auth/captcha`); }; + +export const logOutApi = () => { + return http.post(`/auth/logout`); +}; diff --git a/frontend/src/api/modules/operation-log.ts b/frontend/src/api/modules/operation-log.ts new file mode 100644 index 000000000..674aed412 --- /dev/null +++ b/frontend/src/api/modules/operation-log.ts @@ -0,0 +1,7 @@ +import http from '@/api'; +import { ResPage } from '../interface'; +import { ResOperationLog } from '../interface/operation-log'; + +export const getOperationList = (currentPage: number, pageSize: number) => { + return http.get>(`/operations?page=${currentPage}&pageSize=${pageSize}`); +}; diff --git a/frontend/src/components/app-layout/header/components/Avatar.vue b/frontend/src/components/app-layout/header/components/Avatar.vue index b3f94479a..44a4913c4 100644 --- a/frontend/src/components/app-layout/header/components/Avatar.vue +++ b/frontend/src/components/app-layout/header/components/Avatar.vue @@ -13,9 +13,7 @@ - - @@ -26,32 +24,38 @@ import PasswordDialog from './password-dialog.vue'; import { ElMessageBox, ElMessage } from 'element-plus'; import { useRouter } from 'vue-router'; import { GlobalStore } from '@/store'; +import { logOutApi } from '@/api/modules/login'; +import i18n from '@/lang'; const router = useRouter(); const globalStore = GlobalStore(); -// 退出登录 const logout = () => { - ElMessageBox.confirm('您是否确认退出登录?', '温馨提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', + ElMessageBox.confirm(i18n.global.t('commons.msg.sureLogOut'), i18n.global.t('commons.msg.infoTitle'), { + confirmButtonText: i18n.global.t('commons.button.confirm'), + cancelButtonText: i18n.global.t('commons.button.cancel'), type: 'warning', }).then(() => { + systemLogOut(); router.push({ name: 'login' }); - globalStore.setUserInfo(''); + globalStore.setLogStatus(false); ElMessage({ type: 'success', - message: '退出登录成功!', + message: i18n.global.t('commons.msg.operationSuccess'), }); }); }; +const systemLogOut = async () => { + await logOutApi(); +}; + interface DialogExpose { openDialog: () => void; } const infoRef = ref(null); const passwordRef = ref(null); -// 打开修改密码和个人信息弹窗 + const openDialog = (refName: string) => { if (refName == 'infoRef') return infoRef.value?.openDialog(); passwordRef.value?.openDialog(); diff --git a/frontend/src/hooks/use-delete-data.ts b/frontend/src/hooks/use-delete-data.ts index 94e5bd0e9..a50f3dc23 100644 --- a/frontend/src/hooks/use-delete-data.ts +++ b/frontend/src/hooks/use-delete-data.ts @@ -17,7 +17,7 @@ export const useDeleteData =

( confirmType: HandleData.MessageType = 'error', ) => { return new Promise((resolve, reject) => { - ElMessageBox.confirm(i18n.global.t(`${message}`) + '?', i18n.global.t('commons.msg.title'), { + ElMessageBox.confirm(i18n.global.t(`${message}`) + '?', i18n.global.t('commons.msg.deleteTitle'), { confirmButtonText: i18n.global.t('commons.button.confirm'), cancelButtonText: i18n.global.t('commons.button.cancel'), type: confirmType, diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 9ab5ed8a8..6cf78b030 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -14,12 +14,13 @@ export default { createdAt: 'Creation Time', updatedAt: 'Update Time', operate: 'Operations', + message: 'message', }, msg: { delete: 'This operation cannot be rolled back. Do you want to continue', title: 'Delete', deleteSuccess: 'Delete Success', - loginSuccss: 'Login Success', + loginSuccess: 'Login Success', requestTimeout: 'The request timed out, please try again later', }, login: { @@ -41,6 +42,7 @@ export default { menu: { home: 'Dashboard', demo: 'Demo', + operations: 'Operation logs', }, home: { welcome: 'Welcome', @@ -66,4 +68,19 @@ export default { changePassword: 'Change Password', logout: 'Logout', }, + operations: { + detail: { + users: 'User', + auth: 'User', + login: ' login', + logout: ' logout', + post: ' create', + update: ' update', + delete: ' delete', + }, + operatoin: 'operatoin', + status: 'status', + request: 'request', + response: 'response', + }, }; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 641b337d0..6b7856c33 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -12,15 +12,20 @@ export default { table: { name: '名称', createdAt: '创建时间', + date: '时间', updatedAt: '更新时间', operate: '操作', + message: '信息', }, msg: { delete: '此操作不可回滚,是否继续', - title: '删除', + deleteTitle: '删除', deleteSuccess: '删除成功', - loginSuccss: '登陆成功', + loginSuccess: '登陆成功', + operationSuccess: '操作成功', requestTimeout: '请求超时,请稍后重试', + infoTitle: '提示', + sureLogOut: '您是否确认退出登录?', }, login: { captchaHelper: '请输入验证码', @@ -41,6 +46,7 @@ export default { menu: { home: '概览', demo: '样例', + operations: '操作记录', }, home: { @@ -67,4 +73,19 @@ export default { changePassword: '修改密码', logout: '退出登录', }, + operations: { + detail: { + users: '用户', + auth: '用户', + post: '创建', + update: '更新', + delete: '删除', + login: '登录', + logout: '退出', + }, + operatoin: '操作', + status: '状态', + request: '请求', + response: '响应', + }, }; diff --git a/frontend/src/routers/modules/operation-log.ts b/frontend/src/routers/modules/operation-log.ts new file mode 100644 index 000000000..ea5027942 --- /dev/null +++ b/frontend/src/routers/modules/operation-log.ts @@ -0,0 +1,26 @@ +import { Layout } from '@/routers/constant'; + +const operationRouter = { + sort: 2, + path: '/operations', + component: Layout, + redirect: '/operation', + meta: { + title: 'menu.operations', + icon: 'notebook', + }, + children: [ + { + path: '/operation', + name: 'OperationLog', + component: () => import('@/views/operation-log/index.vue'), + meta: { + keepAlive: true, + requiresAuth: true, + key: 'OperationLog', + }, + }, + ], +}; + +export default operationRouter; diff --git a/frontend/src/routers/router.ts b/frontend/src/routers/router.ts index 2dc6b3008..99846969e 100644 --- a/frontend/src/routers/router.ts +++ b/frontend/src/routers/router.ts @@ -65,7 +65,6 @@ export const routes: RouteRecordRaw[] = [ }, ...routerArray, { - // 找不到路由重定向到404页面 path: '/:pathMatch(.*)', redirect: { name: '404' }, }, diff --git a/frontend/src/views/login/components/login-form.vue b/frontend/src/views/login/components/login-form.vue index 59766d685..9adeb5d2d 100644 --- a/frontend/src/views/login/components/login-form.vue +++ b/frontend/src/views/login/components/login-form.vue @@ -97,7 +97,7 @@ const login = (formEl: FormInstance | undefined) => { globalStore.setUserInfo(res.data.name); globalStore.setLogStatus(true); menuStore.setMenuList([]); - ElMessage.success(i18n.global.t('commons.msg.loginSuccss')); + ElMessage.success(i18n.global.t('commons.msg.loginSuccess')); router.push({ name: 'home' }); } catch (error) { loginVerify(); diff --git a/frontend/src/views/operation-log/index.vue b/frontend/src/views/operation-log/index.vue new file mode 100644 index 000000000..a7a465e75 --- /dev/null +++ b/frontend/src/views/operation-log/index.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index ab798f089..9c3fcdab8 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -41,10 +41,10 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { open: viteEnv.VITE_OPEN, // https: false, proxy: { - '/api': { + '/api/v1': { target: 'http://localhost:9999', changeOrigin: true, - rewrite: (path) => path.replace(/^\/api/, ''), + // rewrite: (path) => path.replace(/^\/api/, ''), }, }, },