diff --git a/agent/cmd/server/docs/x-log.json b/agent/cmd/server/docs/x-log.json index a8099fc87b5f..a8fa190090a9 100644 --- a/agent/cmd/server/docs/x-log.json +++ b/agent/cmd/server/docs/x-log.json @@ -805,6 +805,51 @@ "formatZH": "更新快捷命令 [name]", "formatEN": "update quick command [name]" }, + "/core/enterprise/user": { + "bodyKeys": [ + "name" + ], + "paramKeys": [], + "beforeFunctions": [], + "formatZH": "添加用户 [name]", + "formatEN": "add user [name]" + }, + "/core/enterprise/users/del": { + "bodyKeys": [ + "ids" + ], + "paramKeys": [], + "beforeFunctions": [ + { + "input_column": "id", + "input_value": "ids", + "isList": true, + "db": "users", + "output_column": "name", + "output_value": "names" + } + ], + "formatZH": "删除用户 [names]", + "formatEN": "delete user [names]" + }, + "/core/enterprise/users/info/update": { + "bodyKeys": [ + "name" + ], + "paramKeys": [], + "beforeFunctions": [], + "formatZH": "更新 [name] 认证信息", + "formatEN": "update user [name] auth info" + }, + "/core/enterprise/users/update": { + "bodyKeys": [ + "name" + ], + "paramKeys": [], + "beforeFunctions": [], + "formatZH": "更新 [name]", + "formatEN": "update user [name]" + }, "/core/groups": { "bodyKeys": [ "name", diff --git a/agent/global/global.go b/agent/global/global.go index 3782179a3ff1..56e14f9539b9 100644 --- a/agent/global/global.go +++ b/agent/global/global.go @@ -55,10 +55,10 @@ func RepoURL() string { } func ResourceURL() string { if CONF.Base.IsEnterprise { - return "https://resource.fit2cloud.com/1panel/resource/enterprise" + return "https://resource.fit2cloud.com/1panel/resource/v2" } if CONF.Base.IsFxplay { - return "https://resource.fit2cloud.com/1panel/resource/fusionxplay" + return "https://resource.fit2cloud.com/1panel/resource/v2" } if CONF.Base.Edition != "intl" { return "https://resource.fit2cloud.com/1panel/resource/v2" diff --git a/agent/init/lang/lang.go b/agent/init/lang/lang.go index ffe903801f33..c55514ceab7f 100644 --- a/agent/init/lang/lang.go +++ b/agent/init/lang/lang.go @@ -109,7 +109,7 @@ func loadRestorePath(upgradeDir string) (string, error) { } func downloadLangFromRemote(fileOp files.FileOp) { - path := fmt.Sprintf("%s/language/lang.tar.gz", global.RepoURL()) + path := fmt.Sprintf("%s/language/lang.tar.gz", global.ResourceURL()) if err := fileOp.DownloadFile(path, "/usr/local/bin/lang.tar.gz"); err != nil { global.LOG.Errorf("download lang.tar.gz failed, err: %v", err) return @@ -127,7 +127,7 @@ func downloadLangFromRemote(fileOp files.FileOp) { } func downloadGeoFromRemote(fileOp files.FileOp, targetPath string) { _ = os.MkdirAll(path.Dir(targetPath), os.ModePerm) - pathItem := fmt.Sprintf("%s/geo/GeoIP.mmdb", global.RepoURL()) + pathItem := fmt.Sprintf("%s/geo/GeoIP.mmdb", global.ResourceURL()) if err := fileOp.DownloadFile(pathItem, targetPath); err != nil { global.LOG.Errorf("download geo ip failed, err: %v", err) return diff --git a/core/cmd/server/docs/docs.go b/core/cmd/server/docs/docs.go index b3905951f467..ce038ad3e567 100644 --- a/core/cmd/server/docs/docs.go +++ b/core/cmd/server/docs/docs.go @@ -14155,6 +14155,41 @@ const docTemplate = `{ } } }, + "/files/decompress/stop": { + "post": { + "consumes": [ + "application/json" + ], + "parameters": [ + { + "description": "request", + "in": "body", + "name": "request", + "required": true, + "schema": { + "$ref": "#/definitions/request.FileDeCompressStopReq" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Stop decompress task", + "tags": [ + "File" + ] + } + }, "/files/del": { "post": { "consumes": [ @@ -20162,6 +20197,30 @@ const docTemplate = `{ } } }, + "/settings/website/dir": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load website dir", + "tags": [ + "System Setting" + ] + } + }, "/toolbox/clam": { "post": { "consumes": [ @@ -34334,6 +34393,9 @@ const docTemplate = `{ "proxyType": { "type": "string" }, + "scriptSync": { + "type": "string" + }, "securityEntrance": { "type": "string" }, @@ -36577,6 +36639,9 @@ const docTemplate = `{ "secret": { "type": "string" }, + "taskID": { + "type": "string" + }, "type": { "type": "string" } @@ -36588,6 +36653,17 @@ const docTemplate = `{ ], "type": "object" }, + "request.FileDeCompressStopReq": { + "properties": { + "taskID": { + "type": "string" + } + }, + "required": [ + "taskID" + ], + "type": "object" + }, "request.FileDelete": { "properties": { "forceDelete": { @@ -36877,8 +36953,7 @@ const docTemplate = `{ }, "required": [ "page", - "pageSize", - "type" + "pageSize" ], "type": "object" }, @@ -41850,4 +41925,4 @@ var SwaggerInfo = &swag.Spec{ func init() { swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} +} \ No newline at end of file diff --git a/core/cmd/server/docs/swagger.json b/core/cmd/server/docs/swagger.json index a44023eb23a9..34360b8a98ce 100644 --- a/core/cmd/server/docs/swagger.json +++ b/core/cmd/server/docs/swagger.json @@ -14151,6 +14151,41 @@ } } }, + "/files/decompress/stop": { + "post": { + "consumes": [ + "application/json" + ], + "parameters": [ + { + "description": "request", + "in": "body", + "name": "request", + "required": true, + "schema": { + "$ref": "#/definitions/request.FileDeCompressStopReq" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Stop decompress task", + "tags": [ + "File" + ] + } + }, "/files/del": { "post": { "consumes": [ @@ -20158,6 +20193,30 @@ } } }, + "/settings/website/dir": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load website dir", + "tags": [ + "System Setting" + ] + } + }, "/toolbox/clam": { "post": { "consumes": [ @@ -34330,6 +34389,9 @@ "proxyType": { "type": "string" }, + "scriptSync": { + "type": "string" + }, "securityEntrance": { "type": "string" }, @@ -36573,6 +36635,9 @@ "secret": { "type": "string" }, + "taskID": { + "type": "string" + }, "type": { "type": "string" } @@ -36584,6 +36649,17 @@ ], "type": "object" }, + "request.FileDeCompressStopReq": { + "properties": { + "taskID": { + "type": "string" + } + }, + "required": [ + "taskID" + ], + "type": "object" + }, "request.FileDelete": { "properties": { "forceDelete": { @@ -36873,8 +36949,7 @@ }, "required": [ "page", - "pageSize", - "type" + "pageSize" ], "type": "object" }, @@ -41829,4 +41904,4 @@ "type": "object" } } -} +} \ No newline at end of file diff --git a/core/cmd/server/docs/swagger_test.go b/core/cmd/server/docs/swagger_test.go index f9bd22dbd9f4..2c97bfe3084f 100644 --- a/core/cmd/server/docs/swagger_test.go +++ b/core/cmd/server/docs/swagger_test.go @@ -22,6 +22,8 @@ func TestGenerateXlog(t *testing.T) { filepath.Join(workDir, "core/app/api/v2"), filepath.Join(workDir, "agent/xpack/app/api/v2"), filepath.Join(workDir, "core/xpack/app/api/v2"), + filepath.Join(workDir, "agent/enterprise/app/api/v2"), + filepath.Join(workDir, "core/enterprise/app/api/v2"), } xlogMap := make(map[string]operationJson) @@ -89,7 +91,7 @@ func TestGenerateSwaggerDoc(t *testing.T) { cmd2.Dir = workDir std2, err := cmd2.CombinedOutput() if err != nil { - fmt.Printf("generate swagger doc of core failed, std1: %v, err: %v", string(std2), err) + fmt.Printf("generate swagger doc of core failed, std2: %v, err: %v", string(std2), err) return } diff --git a/core/cmd/server/docs/x-log.json b/core/cmd/server/docs/x-log.json index a8099fc87b5f..a8fa190090a9 100644 --- a/core/cmd/server/docs/x-log.json +++ b/core/cmd/server/docs/x-log.json @@ -805,6 +805,51 @@ "formatZH": "更新快捷命令 [name]", "formatEN": "update quick command [name]" }, + "/core/enterprise/user": { + "bodyKeys": [ + "name" + ], + "paramKeys": [], + "beforeFunctions": [], + "formatZH": "添加用户 [name]", + "formatEN": "add user [name]" + }, + "/core/enterprise/users/del": { + "bodyKeys": [ + "ids" + ], + "paramKeys": [], + "beforeFunctions": [ + { + "input_column": "id", + "input_value": "ids", + "isList": true, + "db": "users", + "output_column": "name", + "output_value": "names" + } + ], + "formatZH": "删除用户 [names]", + "formatEN": "delete user [names]" + }, + "/core/enterprise/users/info/update": { + "bodyKeys": [ + "name" + ], + "paramKeys": [], + "beforeFunctions": [], + "formatZH": "更新 [name] 认证信息", + "formatEN": "update user [name] auth info" + }, + "/core/enterprise/users/update": { + "bodyKeys": [ + "name" + ], + "paramKeys": [], + "beforeFunctions": [], + "formatZH": "更新 [name]", + "formatEN": "update user [name]" + }, "/core/groups": { "bodyKeys": [ "name", diff --git a/core/init/geo/lang.go b/core/init/geo/lang.go index 39b8a0850cde..a8a952f89f0e 100644 --- a/core/init/geo/lang.go +++ b/core/init/geo/lang.go @@ -108,7 +108,7 @@ func loadRestorePath(upgradeDir string) (string, error) { } func downloadLangFromRemote() { - path := fmt.Sprintf("%s/language/lang.tar.gz", global.RepoURL()) + path := fmt.Sprintf("%s/language/lang.tar.gz", global.ResourceURL()) if err := fileUtils.DownloadFile(path, "/usr/local/bin/lang.tar.gz"); err != nil { global.LOG.Errorf("download lang.tar.gz failed, err: %v", err) return @@ -126,7 +126,7 @@ func downloadLangFromRemote() { } func downloadGeoFromRemote(targetPath string) { _ = os.MkdirAll(path.Dir(targetPath), os.ModePerm) - pathItem := fmt.Sprintf("%s/geo/GeoIP.mmdb", global.RepoURL()) + pathItem := fmt.Sprintf("%s/geo/GeoIP.mmdb", global.ResourceURL()) if err := fileUtils.DownloadFile(pathItem, targetPath); err != nil { global.LOG.Errorf("download geo ip failed, err: %v", err) return diff --git a/core/init/router/router.go b/core/init/router/router.go index 6771b6bf0243..8cb6fc97bb73 100644 --- a/core/init/router/router.go +++ b/core/init/router/router.go @@ -84,6 +84,7 @@ func Routers() *gin.Engine { Router.Use(middleware.DemoHandle()) } + Router.Use(middleware.FrontendFallback()) Router.Use(middleware.OperationLog()) Router.Use(middleware.GlobalLoading()) Router.Use(xpack.AuthProvider.CoreAPIAuthMiddleware()) diff --git a/core/middleware/frontend_fallback.go b/core/middleware/frontend_fallback.go new file mode 100644 index 000000000000..699a32642370 --- /dev/null +++ b/core/middleware/frontend_fallback.go @@ -0,0 +1,44 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/1Panel-dev/1Panel/core/utils/security" + "github.com/gin-gonic/gin" +) + +func FrontendFallback() gin.HandlerFunc { + return func(c *gin.Context) { + if !isFrontendFallbackRequest(c) { + c.Next() + return + } + if security.CanServeFrontendPath(c) { + security.ToIndexHtml(c) + c.Abort() + return + } + security.HandleNotSecurity(c, "") + c.Abort() + } +} + +func isFrontendFallbackRequest(c *gin.Context) bool { + if c.Request.Method != http.MethodGet && c.Request.Method != http.MethodHead { + return false + } + path := c.Request.URL.Path + if strings.HasPrefix(path, "/api/") || + strings.HasPrefix(path, "/assets/") || + strings.HasPrefix(path, "/public") || + strings.HasPrefix(path, "/1panel/swagger/") || + path == "/favicon.ico" { + return false + } + if !security.IsFrontendPath(path) { + return false + } + accept := c.GetHeader("Accept") + return accept == "" || strings.Contains(accept, "text/html") +} diff --git a/core/utils/security/security.go b/core/utils/security/security.go index be258850cfb2..5b56c357c829 100644 --- a/core/utils/security/security.go +++ b/core/utils/security/security.go @@ -29,7 +29,7 @@ func HandleNotRoute(c *gin.Context) bool { HandleNotSecurity(c, "err_ip_limit") return false } - if checkFrontendPath(c) { + if CanServeFrontendPath(c) { ToIndexHtml(c) return false } @@ -122,8 +122,8 @@ func HandleNotSecurity(c *gin.Context, resType string) { c.Data(statusCode, "text/html; charset=utf-8", data) } -func isFrontendPath(c *gin.Context) bool { - reqUri := strings.TrimSuffix(c.Request.URL.Path, "/") +func IsFrontendPath(path string) bool { + reqUri := strings.TrimSuffix(path, "/") if _, ok := constant.WebUrlMap[reqUri]; ok { return true } @@ -135,8 +135,8 @@ func isFrontendPath(c *gin.Context) bool { return false } -func checkFrontendPath(c *gin.Context) bool { - if !isFrontendPath(c) { +func CanServeFrontendPath(c *gin.Context) bool { + if !IsFrontendPath(c.Request.URL.Path) { return false } if isPublicFileSharePagePath(c.Request.URL.Path) { @@ -144,7 +144,7 @@ func checkFrontendPath(c *gin.Context) bool { } authService := service.NewIAuthService() if authService.GetSecurityEntrance() != "" { - return authService.IsLogin(c) + return checkEntrance(c) || authService.IsLogin(c) } return true } diff --git a/frontend/src/api/helper/check-status.ts b/frontend/src/api/helper/check-status.ts index e61aac37df30..5cd2b61e1cfe 100644 --- a/frontend/src/api/helper/check-status.ts +++ b/frontend/src/api/helper/check-status.ts @@ -1,10 +1,10 @@ import i18n from '@/lang'; import router from '@/routers'; import { MsgError } from '@/utils/message'; -import { GlobalStore } from '@/store'; +import { useGlobalStore } from '@/composables/useGlobalStore'; export const checkStatus = (status: number, msg: string): void => { - const globalStore = GlobalStore(); + const { entrance, isLogin } = useGlobalStore(); switch (status) { case 400: MsgError(msg ? msg : i18n.global.t('commons.res.paramError')); @@ -13,8 +13,8 @@ export const checkStatus = (status: number, msg: string): void => { MsgError(msg ? msg : i18n.global.t('commons.res.notFound')); break; case 403: - globalStore.isLogin = false; - router.replace({ name: 'entrance', params: { code: globalStore.entrance } }); + isLogin.value = false; + router.replace({ name: 'entrance', params: { code: entrance.value } }); MsgError(msg ? msg : i18n.global.t('commons.res.forbidden')); break; case 500: diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 3d3c87344a28..83eced8ebeb0 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -3,13 +3,13 @@ import { ResultData } from '@/api/interface'; import { ResultEnum } from '@/enums/http-enum'; import { checkStatus } from './helper/check-status'; import router from '@/routers'; -import { GlobalStore } from '@/store'; import { MsgError } from '@/utils/message'; import { encodeBase64 } from '@/utils/base64'; import i18n from '@/lang'; import { changeToLocal } from '@/utils/node'; import { getCookie } from '@/utils/auth'; import { handleAuthResponseCode } from '@/utils/auth-response'; +import { GlobalStore } from '@/store'; const config = { baseURL: import.meta.env.VITE_API_URL as string, diff --git a/frontend/src/components/complex-table/index.vue b/frontend/src/components/complex-table/index.vue index b2b7dd6c3fbe..adfaaa596c14 100644 --- a/frontend/src/components/complex-table/index.vue +++ b/frontend/src/components/complex-table/index.vue @@ -107,7 +107,6 @@ const props = defineProps({ }, }); const emit = defineEmits(['search', 'update:selects', 'update:paginationConfig']); - const tableRef = ref(); const tableHeight = ref(''); const menuRef = ref(null); diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 732343d41bd1..d255a5e299c0 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -4145,6 +4145,8 @@ const message = { nodeAdminMasterForbidden: 'Node Admin permissions cannot be added to the master node', permissionLinkageTip: 'Related permissions are selected automatically when dependencies exist; after manual removal, some features may show "Current user has no permission".', + allViewPermissionHelper: 'Grants all view permissions for AI Gateway.', + apiKeyViewPermissionHelper: 'Only allows viewing AI Gateway API Keys.', view: 'View', manage: 'Manage', }, diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 18565613e383..a9d976ffa3f9 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -4199,6 +4199,8 @@ const message = { nodeAdminMasterForbidden: 'No se pueden agregar permisos de administrador de nodo al nodo principal', permissionLinkageTip: 'Si hay dependencias, los permisos relacionados se seleccionarán automáticamente; tras quitarlos manualmente, algunas funciones pueden mostrar "El usuario actual no tiene permiso".', + allViewPermissionHelper: 'Concede todos los permisos de visualización para AI Gateway.', + apiKeyViewPermissionHelper: 'Solo permite ver las API Keys de AI Gateway.', view: 'Ver', manage: 'Gestionar', }, diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index f08732599597..8d121cc07d6a 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -4181,6 +4181,8 @@ const message = { nodeAdminMasterForbidden: 'マスターノードにノード管理者権限を追加することはできません', permissionLinkageTip: '依存関係がある場合、関連権限は自動選択されます。手動で解除すると、一部機能で「現在のユーザーには権限がありません」と表示される場合があります。', + allViewPermissionHelper: 'AI ゲートウェイのすべての表示権限を付与します。', + apiKeyViewPermissionHelper: 'AI ゲートウェイの API Key のみ表示できます。', view: '表示', manage: '管理', }, diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index bbbb02c805d6..c774456da40e 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -4097,6 +4097,8 @@ const message = { nodeAdminMasterForbidden: '마스터 노드에는 노드 관리자 권한을 추가할 수 없습니다', permissionLinkageTip: '의존 관계가 있으면 관련 권한이 자동 선택되며, 수동으로 해제하면 일부 기능에서 "현재 사용자에게 권한이 없습니다"가 표시될 수 있습니다.', + allViewPermissionHelper: 'AI 게이트웨이의 모든 보기 권한을 부여합니다.', + apiKeyViewPermissionHelper: 'AI 게이트웨이 API Key만 볼 수 있습니다.', view: '보기', manage: '관리', }, diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 833d158a233b..00766eb18c06 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -4238,6 +4238,8 @@ const message = { nodeAdminMasterForbidden: 'Kebenaran Pentadbir Nod tidak boleh ditambah pada nod utama', permissionLinkageTip: 'Kebenaran berkaitan akan dipilih automatik jika ada kebergantungan; selepas dialih keluar manual, sesetengah ciri mungkin memaparkan "Pengguna semasa tiada kebenaran".', + allViewPermissionHelper: 'Memberikan semua kebenaran paparan untuk AI Gateway.', + apiKeyViewPermissionHelper: 'Hanya membenarkan paparan API Key AI Gateway.', view: 'Lihat', manage: 'Urus', }, diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index fdd0a69841dc..63155060f3a0 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -4377,6 +4377,8 @@ const message = { nodeAdminMasterForbidden: 'Permissões de administrador de nó não podem ser adicionadas ao nó principal', permissionLinkageTip: 'Permissões relacionadas serão selecionadas automaticamente quando houver dependências; após removê-las manualmente, alguns recursos podem mostrar "O usuário atual não tem permissão".', + allViewPermissionHelper: 'Concede todas as permissões de visualização do AI Gateway.', + apiKeyViewPermissionHelper: 'Permite visualizar apenas as API Keys do AI Gateway.', view: 'Visualizar', manage: 'Gerenciar', }, diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 271b6a5c873a..aee4ab7d752a 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -4232,6 +4232,8 @@ const message = { nodeAdminMasterForbidden: 'Права администратора узла нельзя добавить к главному узлу', permissionLinkageTip: 'При наличии зависимостей связанные права выбираются автоматически; после ручного снятия некоторые функции могут показать "У текущего пользователя нет разрешения".', + allViewPermissionHelper: 'Предоставляет все права просмотра для AI Gateway.', + apiKeyViewPermissionHelper: 'Разрешает просматривать только API Key AI Gateway.', view: 'Просмотр', manage: 'Управление', }, diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index b393cb5337b8..e893579800d7 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -4231,6 +4231,8 @@ const message = { nodeAdminMasterForbidden: 'Ana düğüme düğüm yöneticisi izni eklenemez', permissionLinkageTip: 'Bağımlılık varsa ilişkili izinler otomatik seçilir; manuel kaldırıldıktan sonra bazı özelliklerde "Geçerli kullanıcının izni yok" gösterilebilir.', + allViewPermissionHelper: 'AI Gateway için tüm görüntüleme izinlerini verir.', + apiKeyViewPermissionHelper: 'Yalnızca AI Gateway API Key görüntülemeye izin verir.', view: 'Görüntüle', manage: 'Yönet', }, diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 8b42c1ae53c3..0d9b42911dd0 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -3875,6 +3875,8 @@ const message = { permissionDuplicate: '每個節點只能新增一種角色', nodeAdminMasterForbidden: '主節點不允許新增節點管理員權限', permissionLinkageTip: '存在依賴時將自動勾選關聯權限,手動取消後部分功能可能提示「目前使用者無權限」。', + allViewPermissionHelper: '擁有 AI 閘道的所有檢視權限。', + apiKeyViewPermissionHelper: '僅能檢視 AI 閘道 API Key。', view: '檢視', manage: '管理', }, diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index a322b35bb2f1..76d355184741 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -4463,6 +4463,8 @@ const message = { permissionDuplicate: '每个节点只能添加一种角色', nodeAdminMasterForbidden: '主节点不允许添加节点管理员权限', permissionLinkageTip: '存在依赖时将自动勾选关联权限,手动取消后部分功能可能提示“当前用户无权限”。', + allViewPermissionHelper: '拥有 AI 网关的所有查看权限。', + apiKeyViewPermissionHelper: '仅能查看 AI 网关 API Key。', view: '查看', manage: '管理', }, diff --git a/frontend/src/routers/modules/website.ts b/frontend/src/routers/modules/website.ts index 6e3c777317ad..ef061d71b846 100644 --- a/frontend/src/routers/modules/website.ts +++ b/frontend/src/routers/modules/website.ts @@ -9,7 +9,6 @@ const webSiteRouter = { meta: { icon: 'p-website', title: 'menu.website', - permission: 'website_view', }, children: [ { diff --git a/frontend/src/utils/auth-response.ts b/frontend/src/utils/auth-response.ts index e2f3d597f872..051b05829825 100644 --- a/frontend/src/utils/auth-response.ts +++ b/frontend/src/utils/auth-response.ts @@ -1,7 +1,7 @@ import { ResultEnum } from '@/enums/http-enum'; import i18n from '@/lang'; import router from '@/routers'; -import { GlobalStore } from '@/store'; +import { useGlobalStore } from '@/composables/useGlobalStore'; import { MsgError } from '@/utils/message'; export type AuthResponseAction = 'reject' | 'return'; @@ -19,11 +19,11 @@ interface AuthResponseOptions { const forbiddenMessage = () => i18n.global.t('commons.res.forbidden'); export const redirectToEntrance = () => { - const globalStore = GlobalStore(); - globalStore.isLogin = false; + const { entrance, isLogin } = useGlobalStore(); + isLogin.value = false; router.push({ name: 'entrance', - params: { code: globalStore.entrance }, + params: { code: entrance.value }, }); }; diff --git a/frontend/src/utils/rbac.ts b/frontend/src/utils/rbac.ts index ed23770edb10..c78b3c724354 100644 --- a/frontend/src/utils/rbac.ts +++ b/frontend/src/utils/rbac.ts @@ -1,6 +1,6 @@ import { getUserInfo } from '@/api/modules/auth'; import { getEnterpriseUserInfo } from '@/extensions/xpack'; -import { GlobalStore } from '@/store'; +import { useGlobalStore } from '@/composables/useGlobalStore'; import type { RouteMeta } from 'vue-router'; type RouteAccessMeta = { @@ -17,12 +17,12 @@ type RouteAccessTarget = { }; export const syncAuthInfo = async (currentNode?: string) => { - const globalStore = GlobalStore(); - if (!globalStore.isEnterprise) { + const { globalStore, currentNode: storeCurrentNode, isEnterprise } = useGlobalStore(); + if (!isEnterprise.value) { const res = await getUserInfo(); return res.data; } - const res = await getEnterpriseUserInfo(currentNode ?? globalStore.currentNode); + const res = await getEnterpriseUserInfo(currentNode ?? storeCurrentNode.value); globalStore.setAuthInfo({ isAdmin: res.data.role === 'ADMIN', permissions: res.data.permissions || [], @@ -32,7 +32,7 @@ export const syncAuthInfo = async (currentNode?: string) => { }; export const hasPermission = (permission: string) => { - return GlobalStore().hasPermission(permission); + return useGlobalStore().globalStore.hasPermission(permission); }; export const hasRouteRoleAccess = (meta?: RouteMeta & RouteAccessMeta) => { diff --git a/frontend/src/utils/stream-auth.ts b/frontend/src/utils/stream-auth.ts index ace03a923053..1edc45bb4dbb 100644 --- a/frontend/src/utils/stream-auth.ts +++ b/frontend/src/utils/stream-auth.ts @@ -1,16 +1,16 @@ -import { GlobalStore } from '@/store'; +import { useGlobalStore } from '@/composables/useGlobalStore'; import { handleAuthResponseCode, handleAuthResponseStatus } from '@/utils/auth-response'; export const checkStreamAuth = async (url: string, currentNode?: string) => { - const globalStore = GlobalStore(); + const { currentNode: storeCurrentNode, language } = useGlobalStore(); const controller = new AbortController(); const timeout = window.setTimeout(() => controller.abort(), 5000); try { const res = await fetch(url.replace(/^ws/, 'http'), { credentials: 'include', headers: { - 'Accept-Language': globalStore.language, - CurrentNode: encodeURIComponent(currentNode || globalStore.currentNode), + 'Accept-Language': language.value, + CurrentNode: encodeURIComponent(currentNode || storeCurrentNode.value), }, signal: controller.signal, }); diff --git a/frontend/src/utils/xpack.ts b/frontend/src/utils/xpack.ts index b83bf637cdc0..b1b00796cfff 100644 --- a/frontend/src/utils/xpack.ts +++ b/frontend/src/utils/xpack.ts @@ -136,7 +136,7 @@ export async function loadMasterProductProFromDB() { globalStore.isProductPro = res.data.status === 'Bound'; } } - switchTheme(); + useTheme().switchTheme(); initFavicon(); loadDataFromDB(); } @@ -167,7 +167,7 @@ export async function getXpackSettingForTheme() { } else { resetXSetting(); } - switchTheme(); + useTheme().switchTheme(); initFavicon(); } diff --git a/frontend/src/views/ai/agents/agent/config/tabs/channels/openclaw/discord.vue b/frontend/src/views/ai/agents/agent/config/tabs/channels/openclaw/discord.vue index ef46a72528a1..19901ff1c43f 100644 --- a/frontend/src/views/ai/agents/agent/config/tabs/channels/openclaw/discord.vue +++ b/frontend/src/views/ai/agents/agent/config/tabs/channels/openclaw/discord.vue @@ -44,6 +44,7 @@ import { reactive, ref } from 'vue'; import type { FormInstance } from 'element-plus'; import { ElMessageBox } from 'element-plus'; +import { useMenuManagePermission } from '@/composables/useMenuManagePermission'; import { useI18n } from 'vue-i18n'; import { AI } from '@/api/interface/ai'; import { approveAgentChannelPairing, getAgentDiscordConfig, updateAgentDiscordConfig } from '@/api/modules/ai'; diff --git a/frontend/src/views/ai/agents/agent/config/tabs/channels/openclaw/telegram.vue b/frontend/src/views/ai/agents/agent/config/tabs/channels/openclaw/telegram.vue index 601d628478f0..6d12dd46db68 100644 --- a/frontend/src/views/ai/agents/agent/config/tabs/channels/openclaw/telegram.vue +++ b/frontend/src/views/ai/agents/agent/config/tabs/channels/openclaw/telegram.vue @@ -78,6 +78,7 @@ import { reactive, ref } from 'vue'; import type { FormInstance } from 'element-plus'; import { ElMessageBox } from 'element-plus'; +import { useMenuManagePermission } from '@/composables/useMenuManagePermission'; import { useI18n } from 'vue-i18n'; import { AI } from '@/api/interface/ai'; import { approveAgentChannelPairing, getAgentTelegramConfig, updateAgentTelegramConfig } from '@/api/modules/ai'; diff --git a/frontend/src/views/host/file-management/index.vue b/frontend/src/views/host/file-management/index.vue index 7bc6a9b15419..dd4dee4df5f4 100644 --- a/frontend/src/views/host/file-management/index.vue +++ b/frontend/src/views/host/file-management/index.vue @@ -999,10 +999,6 @@ const paginationConfig = reactive({ total: 0, }); -const mobile = computed(() => { - return globalStore.isMobile(); -}); - const btnWrapperRefs = ref>({}); const setBtnWrapperRef = (key: string, el: any) => { @@ -1169,7 +1165,7 @@ const loadInitialExistingPath = async (url: string) => { sortOrder: oldSortOrder, showHidden, }); - globalStore.lastFilePath = req.path; + lastFilePath.value = req.path; getPaths(req.path); updateTab(req.path); paths.value = buildPaths(req.path); diff --git a/frontend/src/views/setting/panel/index.vue b/frontend/src/views/setting/panel/index.vue index fd496725eca8..fdd9321d39de 100644 --- a/frontend/src/views/setting/panel/index.vue +++ b/frontend/src/views/setting/panel/index.vue @@ -215,6 +215,19 @@ const { watermarkShow, } = useGlobalStore(); +const { + globalStore, + isEnterprise, + isIntl, + isMobile, + isOffline, + isXpackOrEE, + openMenuTabs, + themeConfig, + watermark, + watermarkShow, +} = useGlobalStore(); + const loading = ref(false); const { switchTheme } = useTheme();