Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions agent/app/api/v2/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/gin-gonic/gin"
)

Expand Down Expand Up @@ -30,6 +32,31 @@ func (b *BaseApi) PageTasks(c *gin.Context) {
})
}

// @Tags TaskLog
// @Summary Read task log by Line
// @Param request body request.TaskLogReadReq true "request"
// @Success 200 {object} response.FileLineContent
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /logs/tasks/read [post]
func (b *BaseApi) ReadTaskLogByLine(c *gin.Context) {
var req request.TaskLogReadReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
var res *response.FileLineContent
res, err := taskService.ReadByLine(req)
if err != nil {
helper.InternalServer(c, err)
return
}
if res.TotalLines > 100 {
helper.SuccessWithDataGzipped(c, res)
} else {
helper.SuccessWithData(c, res)
}
}

// @Tags TaskLog
// @Summary Get the number of executing tasks
// @Success 200 {integer} int64
Expand Down
7 changes: 7 additions & 0 deletions agent/app/dto/request/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ type FileReadByLineReq struct {
TaskReq
}

type TaskLogReadReq struct {
Page int `json:"page" validate:"required,min=1"`
PageSize int `json:"pageSize" validate:"required,min=1"`
Latest bool `json:"latest"`
TaskReq
}

type TaskReq struct {
TaskID string `json:"taskID"`
TaskType string `json:"taskType"`
Expand Down
62 changes: 62 additions & 0 deletions agent/app/service/task.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package service

import (
"os"

"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/utils/files"
)

type TaskLogService struct{}

type ITaskLogService interface {
Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error)
ReadByLine(req request.TaskLogReadReq) (*response.FileLineContent, error)
SyncForRestart() error
CountExecutingTask() (int64, error)
}
Expand Down Expand Up @@ -46,6 +52,62 @@ func (u *TaskLogService) Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, e
return total, items, err
}

func (u *TaskLogService) ReadByLine(req request.TaskLogReadReq) (*response.FileLineContent, error) {
opts := []repo.DBOption{}
if req.TaskID != "" {
opts = append(opts, taskRepo.WithByID(req.TaskID))
} else {
opts = append(opts, repo.WithOrderRuleBy("created_at", "desc"), repo.WithByType(req.TaskType), taskRepo.WithOperate(req.TaskOperate), taskRepo.WithResourceID(req.ResourceID))
}
taskModel, err := taskRepo.GetFirst(opts...)
if err != nil {
return nil, err
}

file, err := os.Open(taskModel.LogFile)
if err != nil {
return nil, err
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
return nil, err
}

var (
lines []string
isEndOfFile bool
scope string
logFileRes *dto.LogFileRes
)
if stat.Size() > files.MaxReadFileSize {
lines, _ = files.TailFromEnd(taskModel.LogFile, req.PageSize)
isEndOfFile = true
scope = "tail"
} else {
logFileRes, err = files.ReadFileByLine(taskModel.LogFile, req.Page, req.PageSize, req.Latest)
if err != nil {
return nil, err
}
scope = "page"
lines = logFileRes.Lines
}

res := &response.FileLineContent{
End: isEndOfFile,
Path: taskModel.LogFile,
TaskStatus: taskModel.Status,
Lines: lines,
Scope: scope,
}
if logFileRes != nil {
res.TotalLines = logFileRes.TotalLines
res.Total = logFileRes.TotalPages
res.End = logFileRes.IsEndOfFile
}
return res, nil
}

func (u *TaskLogService) SyncForRestart() error {
return taskRepo.UpdateRunningTaskToFailed()
}
Expand Down
1 change: 1 addition & 0 deletions agent/router/ro_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func (s *LogRouter) InitRouter(Router *gin.RouterGroup) {
{
operationRouter.GET("/system/files", baseApi.GetSystemFiles)
operationRouter.POST("/tasks/search", baseApi.PageTasks)
operationRouter.POST("/tasks/read", baseApi.ReadTaskLogByLine)
operationRouter.GET("/tasks/executing/count", baseApi.CountExecutingTasks)
}
}
17 changes: 17 additions & 0 deletions core/app/api/v2/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,23 @@ func (b *BaseApi) MFABind(c *gin.Context) {
helper.Success(c)
}

// @Tags System Setting
// @Summary Close mfa
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /core/auth/mfa/close [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"mfa 关闭","formatEN":"close mfa"}
func (b *BaseApi) MFAClose(c *gin.Context) {
if err := xpack.AuthProvider.MFAClose(c); err != nil {
helper.InternalServer(c, err)
return
}

helper.Success(c)
}

// @Tags Auth
// @Summary generate api key
// @Accept json
Expand Down
14 changes: 13 additions & 1 deletion core/app/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ func MFABind(req dto.MfaCredential) error {
}
return nil
}

func MFAClose() error {
return repo.NewISettingRepo().Update("MFAStatus", constant.StatusDisable)
}

func GetCurrentUserInfo() (*dto.CurrentUserInfo, error) {
setting, err := repo.NewISettingRepo().List()
if err != nil {
Expand Down Expand Up @@ -249,6 +254,11 @@ func GetCurrentUserInfo() (*dto.CurrentUserInfo, error) {
}
func UpdateCurrentUserInfo(c *gin.Context, req dto.CurrentUserUpdate) error {
settingRepo := repo.NewISettingRepo()
currentName, err := settingRepo.GetValueByKey("UserName")
if err != nil {
return err
}
shouldDeleteSession := len(req.Password) != 0 || req.Name != currentName
if len(req.Password) != 0 {
if len(req.OldPassword) == 0 {
return buserr.New("ErrInitialPassword")
Expand Down Expand Up @@ -282,7 +292,9 @@ func UpdateCurrentUserInfo(c *gin.Context, req dto.CurrentUserUpdate) error {
if err := settingRepo.Update("ExpirationTime", expirationTime); err != nil {
return err
}
deleteCurrentSession(c)
if shouldDeleteSession {
deleteCurrentSession(c)
}
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion core/init/migration/helper/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func LoadMenus() string {
item[i].Children = UpsertMenuByLabel(item[i].Children, dto.ShowMenu{
ID: "121",
Disabled: false,
Title: "xpack.user.accessControl",
Title: "xpack.user.userManage",
IsShow: true,
Label: "UserManagement",
Path: "/enterprise/users",
Expand Down
2 changes: 1 addition & 1 deletion core/init/migration/migrations/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -1075,7 +1075,7 @@ var AddUserManagementMenu = &gormigrate.Migration{
newItem := dto.ShowMenu{
ID: "121",
Disabled: false,
Title: "xpack.user.accessControl",
Title: "xpack.user.userManage",
IsShow: true,
Label: "UserManagement",
Path: "/enterprise/users",
Expand Down
1 change: 0 additions & 1 deletion core/init/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ var baseSettingKeys = map[string]struct{}{
"NoAuthSetting": {},
"DashboardMemoVisible": {},
"DashboardSimpleNodeVisible": {},
"MFAStatus": {},
"Edition": {},
"DocSource": {},
"IsOffline": {},
Expand Down
1 change: 1 addition & 0 deletions core/router/ro_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func (s *BaseRouter) InitRouter(Router *gin.RouterGroup) {

authRouter.POST("/mfa", baseApi.LoadMFA)
authRouter.POST("/mfa/bind", baseApi.MFABind)
authRouter.POST("/mfa/close", baseApi.MFAClose)

authRouter.POST("/passkey/register/begin", baseApi.PasskeyRegisterBegin)
authRouter.POST("/passkey/register/finish", baseApi.PasskeyRegisterFinish)
Expand Down
3 changes: 3 additions & 0 deletions core/utils/xpack/helper/auth_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ func (a *authHelper) LoadMFA(_ *gin.Context, req baseDto.MfaRequest) (mfa.Otp, e
func (a *authHelper) MFABind(_ *gin.Context, req baseDto.MfaCredential) error {
return auth.MFABind(req)
}
func (a *authHelper) MFAClose(_ *gin.Context) error {
return auth.MFAClose()
}
func (a *authHelper) GenerateApiKey(_ *gin.Context) (string, error) {
return auth.GenerateApiKey()
}
Expand Down
1 change: 1 addition & 0 deletions core/utils/xpack/providers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type AuthProvider interface {

LoadMFA(c *gin.Context, req dto.MfaRequest) (mfa.Otp, error)
MFABind(c *gin.Context, req dto.MfaCredential) error
MFAClose(c *gin.Context) error

GenerateApiKey(c *gin.Context) (string, error)
UpdateApiConfig(c *gin.Context, req dto.ApiInterfaceConfig) error
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/api/interface/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ export namespace Log {
taskID?: string;
}

export interface TaskLogReadReq {
page: number;
pageSize: number;
latest?: boolean;
taskID?: string;
taskType?: string;
taskOperate?: string;
resourceID?: number;
}

export interface Task {
id: string;
name: string;
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/api/modules/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export const loadMFA = (params: Login.MFARequest) => {
export const bindMFA = (params: Login.MFABind) => {
return http.post(`/core/auth/mfa/bind`, params);
};
export const closeMFA = () => {
return http.post(`/core/auth/mfa/close`);
};
export const generateApiKey = () => {
return http.post<string>(`/core/auth/api/generate`);
};
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/api/modules/log.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import http from '@/api';
import { ResPage } from '../interface';
import { Log } from '../interface/log';
import { TimeoutEnum } from '@/enums/http-enum';

export const getOperationLogs = (info: Log.SearchOpLog) => {
return http.post<ResPage<Log.OperationLog>>(`/core/logs/operation`, info);
Expand All @@ -24,6 +25,11 @@ export const searchTasks = (req: Log.SearchTaskReq, node?: string) => {
return http.post<ResPage<Log.Task>>(`/logs/tasks/search${params}`, req);
};

export const readTaskLogByLine = (req: Log.TaskLogReadReq, node?: string) => {
const params = node ? `?operateNode=${node}` : '';
return http.post<any>(`/logs/tasks/read${params}`, req, TimeoutEnum.T_40S);
};

export const countExecutingTask = () => {
return http.get<number>(`/logs/tasks/executing/count`);
};
8 changes: 7 additions & 1 deletion frontend/src/components/log/file/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import { nextTick, onMounted, onUnmounted, reactive, ref, computed } from 'vue';
import { downloadFile } from '@/utils/file';
import { readByLine } from '@/api/modules/files';
import { readTaskLogByLine } from '@/api/modules/log';
import { GlobalStore } from '@/store';
import bus from '@/global/bus';
import Highlight from '@/components/log/custom-highlight/index.vue';
Expand Down Expand Up @@ -268,7 +269,12 @@ const getContent = async (pre: boolean) => {

let res;
try {
res = await readByLine(readReq, props.config.operateNode || globalStore.currentNode);
const operateNode = props.config.operateNode || globalStore.currentNode;
if (readReq.type === 'task') {
res = await readTaskLogByLine(readReq, operateNode);
} else {
res = await readByLine(readReq, operateNode);
}
} catch (error) {
isLoading.value = false;
firstLoading.value = false;
Expand Down
21 changes: 13 additions & 8 deletions frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const message = {
conn: 'Connect',
disConn: 'Disconnect',
clean: 'Clear',
selectAll: 'Select all',
login: 'Sign in',
close: 'Close',
stop: 'Stop',
Expand Down Expand Up @@ -243,7 +244,7 @@ const message = {
composeName:
'Supports non-special characters at the beginning, lowercase letters, numbers, - and _, length 1-256',
complexityPassword:
'This field must consist of English, numbers with a length of 8-30 and contain at least two special characters.',
'Supports password combinations with a length of 8-30 that contain at least two of letters, numbers, and special characters.',
commonPassword: 'This field length must be more than 6.',
linuxName:
'This field length must be between 1 and 128. The field mustn\'t contain these special characters: "{0}".',
Expand Down Expand Up @@ -2223,6 +2224,10 @@ const message = {
language: 'Language',
runtimeEnv: 'Runtime environment',
offlineEnv: 'Offline environment',
offlineEnvHelper:
'After it is enabled, resources such as the App Store are read from the local server directory by default.\nSystem upgrades and agent upgrades require manually downloaded offline packages.',
offlineEnvOpenHelper: 'Are you sure you want to enable the offline environment?',
offlineEnvCloseHelper: 'Are you sure you want to disable the offline environment?',
docSource: 'Documentation Source',
withByRegion: 'Match Region Setting (Default)',
withByLang: 'Match System Language',
Expand Down Expand Up @@ -2454,14 +2459,13 @@ const message = {
unSetting: 'Not set',
noneSetting:
'Set the expiration time for the panel password. After the expiration, you need to reset the password',
expirationHelper: 'If the password expiration time is [0] days, the password expiration function is disabled',
days: 'Expiration Days',
expirationHelper: 'If the password expiration time is 0 days, the password expiration function is disabled',
days: 'Password expiration days',
expiredHelper: 'The current password has expired. Please change the password again.',
timeoutHelper:
'[ {0} days ] The panel password is about to expire. After the expiration, you need to reset the password',
timeoutHelper: 'The panel password will expire in {0} days. After it expires, you need to reset the password',
complexity: 'Complexity validation',
complexityHelper:
'After you enable it, the password validation rule will be: 8-30 characters, including English, numbers, and at least two special characters.',
'After it is enabled, the password must be 8-30 characters long and contain at least two of letters, numbers, and special characters.',
bindDomain: 'Bind domain',
unBindDomain: 'Unbind domain',
panelSSL: 'Panel SSL',
Expand Down Expand Up @@ -3737,15 +3741,16 @@ const message = {
upage: 'AI Website Builder',
proAlert: 'Upgrade to Pro to use this feature',
user: {
accessControl: 'Permission Management',
user: 'User',
userInfo: 'User Info',
userManage: 'User Management',
superAdmin: 'Super Admin',
superAdminDesc: 'Has full system management permissions and can manage all resources and configurations.',
nodeAdmin: 'Node Admin',
nodeAdminDesc:
'Has management permissions for specified nodes and can manage resources and configurations within those nodes.',
bindNode: 'Bind Node',
role: 'Role',
roleManage: 'Role Management',
roleName: 'Name',
permission: 'Permissions',
permissionDuplicate: 'Only one role can be assigned to each node',
Expand Down
Loading
Loading