diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 293f58a39..d7f94d7b1 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -748,7 +748,6 @@ func upgradeInstall(req request.AppInstallUpgrade) error { if err != nil { return err } - dockerCLi, _ := docker.NewClient() if req.PullImage { composeContent := []byte(detail.DockerCompose) if req.DockerCompose != "" { @@ -758,6 +757,11 @@ func upgradeInstall(req request.AppInstallUpgrade) error { if err != nil { return err } + dockerCLi, err := docker.NewClient() + if err != nil { + return err + } + defer dockerCLi.Close() for _, image := range images { t.Log(i18n.GetWithName("PullImageStart", image)) if err = dockerCLi.PullImageWithProcess(t, image); err != nil { @@ -1182,6 +1186,7 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error if err != nil { return err } + defer dockerCLi.Close() for _, image := range images { if imagePrefix != "" { lastSlashIndex := strings.LastIndex(image, "/") diff --git a/agent/app/service/snapshot_create.go b/agent/app/service/snapshot_create.go index 928794d54..7e23c0159 100644 --- a/agent/app/service/snapshot_create.go +++ b/agent/app/service/snapshot_create.go @@ -377,6 +377,7 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err snap.Task.Log("load docker client failed, skip save app images") return nil } + defer client.Close() images, err := client.ImageList(context.Background(), image.ListOptions{}) if err != nil { snap.Task.Log("list docker images failed, skip save app images") diff --git a/agent/utils/mysql/client/remote.go b/agent/utils/mysql/client/remote.go index a4e07521c..ddce96c8a 100644 --- a/agent/utils/mysql/client/remote.go +++ b/agent/utils/mysql/client/remote.go @@ -503,6 +503,7 @@ func loadImage(dbType, version string) (string, error) { if err != nil { return "", err } + defer cli.Close() images, err := cli.ImageList(context.Background(), image.ListOptions{}) if err != nil { return "", err diff --git a/agent/utils/req_helper/request.go b/agent/utils/req_helper/request.go index 378c43f96..02059015f 100644 --- a/agent/utils/req_helper/request.go +++ b/agent/utils/req_helper/request.go @@ -40,6 +40,7 @@ func HandleGet(url string) (*http.Response, error) { } } if resp.StatusCode == 404 { + _ = resp.Body.Close() return nil, buserr.New("ErrHttpReqNotFound") } @@ -71,6 +72,7 @@ func HandleRequestWithClient(client *http.Client, url, method string, timeout in if err != nil { return 0, nil, err } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return 0, nil, errors.New(resp.Status) } @@ -78,7 +80,6 @@ func HandleRequestWithClient(client *http.Client, url, method string, timeout in if err != nil { return 0, nil, err } - defer resp.Body.Close() return resp.StatusCode, body, nil } @@ -143,6 +144,7 @@ func RequestFile(url, method string, timeout int) (io.ReadCloser, context.Cancel return nil, cancel, err } if resp.StatusCode != http.StatusOK { + _ = resp.Body.Close() return nil, cancel, errors.New(resp.Status) } return resp.Body, cancel, nil diff --git a/agent/utils/version/version.go b/agent/utils/version/version.go index a8b77e3e0..cecf00093 100644 --- a/agent/utils/version/version.go +++ b/agent/utils/version/version.go @@ -190,6 +190,7 @@ func HandleRequest(url, method string, timeout int) (int, []byte, error) { if err != nil { return 0, nil, err } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return 0, nil, errors.New(resp.Status) } @@ -197,7 +198,6 @@ func HandleRequest(url, method string, timeout int) (int, []byte, error) { if err != nil { return 0, nil, err } - defer resp.Body.Close() return resp.StatusCode, body, nil } diff --git a/core/app/service/setting.go b/core/app/service/setting.go index cfc22c73a..278a4c814 100644 --- a/core/app/service/setting.go +++ b/core/app/service/setting.go @@ -807,10 +807,10 @@ func checkProxy(req dto.ProxyUpdate) error { if err != nil { return buserr.WithErr("ErrProxySetting", err) } + defer resp.Body.Close() if _, err := io.ReadAll(resp.Body); err != nil { return buserr.WithErr("ErrProxySetting", err) } - defer resp.Body.Close() return nil } diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go index 5bfa914e6..fa369a0e0 100644 --- a/core/app/service/upgrade.go +++ b/core/app/service/upgrade.go @@ -314,11 +314,11 @@ func (u *UpgradeService) LoadRelease() ([]dto.ReleasesNotes, error) { if err != nil { return notes, err } + defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return notes, err } - defer resp.Body.Close() var nodeItem noteHelper if err := json.Unmarshal(body, &nodeItem); err != nil { return notes, err diff --git a/core/utils/req_helper/requset.go b/core/utils/req_helper/requset.go index 21ccdb322..e6503c973 100644 --- a/core/utils/req_helper/requset.go +++ b/core/utils/req_helper/requset.go @@ -54,11 +54,11 @@ func handleRequestWithTransport(url, method string, transport *http.Transport, t if err != nil { return 0, nil, err } + defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return 0, nil, err } - defer resp.Body.Close() return resp.StatusCode, body, nil } @@ -106,6 +106,7 @@ func handleGetWithTransport(url string, transport *http.Transport) (*http.Respon } } if resp.StatusCode == 404 { + _ = resp.Body.Close() return nil, buserr.WithErr("ErrHttpReqNotFound", errors.New("no such resource")) } diff --git a/frontend/src/api/interface/auth.ts b/frontend/src/api/interface/auth.ts index 78031774c..67dc65c6e 100644 --- a/frontend/src/api/interface/auth.ts +++ b/frontend/src/api/interface/auth.ts @@ -55,6 +55,7 @@ export namespace Login { mfaInterval: number; role: string; permissions: string[]; + masterOnlyPermissions?: string[]; nodeRoles: Array<{ nodeId: number; nodeName: string; roleId: number; roleName: string }>; apiInterfaceStatus: string; diff --git a/frontend/src/store/interface/index.ts b/frontend/src/store/interface/index.ts index f99edaa7c..7de3ad8ef 100644 --- a/frontend/src/store/interface/index.ts +++ b/frontend/src/store/interface/index.ts @@ -57,6 +57,7 @@ export interface GlobalState { // tags isAdmin: boolean; permissions: string[]; + masterOnlyPermissions: string[]; nodeRoles: Array<{ nodeId: number; nodeName: string; roleId: number; roleName: string }>; isEnterprise: boolean; isIntl: boolean; diff --git a/frontend/src/store/modules/global.ts b/frontend/src/store/modules/global.ts index 2ec1d8b69..948ec4569 100644 --- a/frontend/src/store/modules/global.ts +++ b/frontend/src/store/modules/global.ts @@ -4,7 +4,7 @@ import piniaPersistConfig from '@/config/pinia-persist'; import { GlobalState } from '../interface'; import { DeviceType } from '@/enums/app'; import i18n, { setActiveLocale } from '@/lang'; -import { isMasterOnlyPermissionCode, toManageCode } from '@/utils/permission-codes'; +import { isMasterOnlyPermissionCode, setMasterOnlyPermissionCodes, toManageCode } from '@/utils/permission-codes'; const CN_DOCS_URL = 'https://1panel.cn/docs/v2'; const INTL_DOCS_URL = 'https://docs.1panel.pro/v2'; @@ -55,6 +55,7 @@ const GlobalStore = defineStore('GlobalState', { // tags isAdmin: false, permissions: [], + masterOnlyPermissions: [], nodeRoles: [], isEnterprise: false, isIntl: false, @@ -109,18 +110,24 @@ const GlobalStore = defineStore('GlobalState', { setAuthInfo(payload: { isAdmin: boolean; permissions: string[]; + masterOnlyPermissions?: string[]; nodeRoles?: Array<{ nodeId: number; nodeName: string; roleId: number; roleName: string }>; }) { this.isAdmin = !!payload.isAdmin; this.permissions = payload.permissions || []; + this.masterOnlyPermissions = payload.masterOnlyPermissions || []; this.nodeRoles = payload.nodeRoles || []; + setMasterOnlyPermissionCodes(this.masterOnlyPermissions); }, clearAuthInfo() { this.permissions = []; + this.masterOnlyPermissions = []; this.nodeRoles = []; this.isAdmin = false; + setMasterOnlyPermissionCodes([]); }, hasPermission(permission: string) { + setMasterOnlyPermissionCodes(this.masterOnlyPermissions); const normalizedPermission = permission.trim(); if (!normalizedPermission) { return false; diff --git a/frontend/src/utils/permission-codes.ts b/frontend/src/utils/permission-codes.ts index ee4c2fbb9..c99523ee9 100644 --- a/frontend/src/utils/permission-codes.ts +++ b/frontend/src/utils/permission-codes.ts @@ -1,14 +1,15 @@ -const masterOnlyPermissionCodes = new Set([ - 'ai_proxy_view', - 'ai_proxy_manage', - 'ai_proxy_key_view', - 'ai_benchmark_view', - 'ai_benchmark_manage', - 'ai_skills_hub_view', - 'ai_skills_hub_manage', - 'xpack_ops_report_view', - 'xpack_ops_report_manage', -]); +let masterOnlyPermissionCodes = new Set(); +let masterOnlyPermissionCodesKey = ''; + +export const setMasterOnlyPermissionCodes = (permissions?: string[]) => { + const codes = (permissions || []).filter(Boolean); + const key = codes.join('\n'); + if (key === masterOnlyPermissionCodesKey) { + return; + } + masterOnlyPermissionCodesKey = key; + masterOnlyPermissionCodes = new Set(codes); +}; export const toManageCode = (permission: string): string => { if (!permission) { diff --git a/frontend/src/utils/permission.ts b/frontend/src/utils/permission.ts index 5928b10ee..76a715e91 100644 --- a/frontend/src/utils/permission.ts +++ b/frontend/src/utils/permission.ts @@ -1,6 +1,6 @@ import router from '@/routers'; import { GlobalStore } from '@/store'; -import { isMasterOnlyPermissionCode, normalizeToManageCode } from '@/utils/permission-codes'; +import { normalizeToManageCode } from '@/utils/permission-codes'; export type PermissionBindingValue = string | string[] | undefined; export type PermissionMode = 'manage' | 'view'; @@ -51,15 +51,7 @@ const hasPermissionAccessByMode = (mode: PermissionMode, value?: PermissionBindi const permissions = toPermissionList(value); const normalizedPermissions = mode === 'manage' ? permissions.map(toManagePermission).filter(Boolean) : permissions; if (normalizedPermissions.length === 0) { - return globalStore.isAdmin || globalStore.isNodeAdmin; - } - if ( - mode === 'manage' && - globalStore.isNodeAdmin && - !globalStore.isMaster && - normalizedPermissions.some((permission) => !isMasterOnlyPermissionCode(permission)) - ) { - return true; + return globalStore.isAdmin; } return normalizedPermissions.some((permission) => globalStore.hasPermission(permission)); }; diff --git a/frontend/src/utils/rbac.ts b/frontend/src/utils/rbac.ts index 78196a52c..8d9528d24 100644 --- a/frontend/src/utils/rbac.ts +++ b/frontend/src/utils/rbac.ts @@ -25,6 +25,7 @@ export const syncAuthInfo = async (currentNode?: string) => { globalStore.setAuthInfo({ isAdmin: res.data.role === 'ADMIN', permissions: res.data.permissions || [], + masterOnlyPermissions: res.data.masterOnlyPermissions || [], nodeRoles: res.data.nodeRoles || [], }); return res.data; @@ -33,6 +34,7 @@ export const syncAuthInfo = async (currentNode?: string) => { globalStore.setAuthInfo({ isAdmin: res.data.role === 'ADMIN', permissions: res.data.permissions || [], + masterOnlyPermissions: res.data.masterOnlyPermissions || [], nodeRoles: res.data.nodeRoles || [], }); return res.data;