From 7ec48dccdde09c109734e74cec2078b4ab46d681 Mon Sep 17 00:00:00 2001 From: "NaYeong,Kim" Date: Tue, 22 Apr 2025 13:41:34 +0900 Subject: [PATCH 01/51] refactor: refactor global-config codes (#5792) * feat: create dynamic-schema-manager Signed-off-by: NaYeong,Kim * chore: add example code Signed-off-by: NaYeong,Kim * chore: moved merge-config file position Signed-off-by: NaYeong,Kim * refactor: separate conditions in initTaskmanagementTemplate Signed-off-by: NaYeong,Kim * refactor: apply dynamic menu logic to menuList in useAdvancedMenuDisplay Signed-off-by: NaYeong,Kim --------- Signed-off-by: NaYeong,Kim --- .../global-config/dynamic-schema-manager.ts | 78 +++++++++++++++ .../global-config/feature-schema-manager.ts | 22 +++-- .../global-config/helpers}/merge-config.ts | 0 apps/web/src/lib/menu/config.ts | 3 + apps/web/src/lib/site-initializer/index.ts | 15 ++- .../initTaskManagementTemplate.ts | 14 --- .../web/src/services/ops-flow/configurator.ts | 3 - .../composables/use-advanced-menu-display.ts | 59 ----------- apps/web/src/store/display/display-store.ts | 6 +- .../global-config-schema-store.ts | 98 +++++++++++++++---- 10 files changed, 189 insertions(+), 109 deletions(-) create mode 100644 apps/web/src/lib/config/global-config/dynamic-schema-manager.ts rename apps/web/src/lib/{site-initializer => config/global-config/helpers}/merge-config.ts (100%) delete mode 100644 apps/web/src/lib/site-initializer/initTaskManagementTemplate.ts delete mode 100644 apps/web/src/store/display/composables/use-advanced-menu-display.ts diff --git a/apps/web/src/lib/config/global-config/dynamic-schema-manager.ts b/apps/web/src/lib/config/global-config/dynamic-schema-manager.ts new file mode 100644 index 0000000000..7d81451971 --- /dev/null +++ b/apps/web/src/lib/config/global-config/dynamic-schema-manager.ts @@ -0,0 +1,78 @@ +import { useGlobalConfigSchemaStore } from '@/store/global-config-schema/global-config-schema-store'; +import { pinia } from '@/store/pinia'; + +import type { + GeneratedMenuSchema, + GeneratedRouteSchema, + GeneratedRouteMetadataSchema, + GeneratedUiAffectSchema, +} from '@/lib/config/global-config/types/type'; + +type DynamicMenuHandler = (menuSchema: GeneratedMenuSchema) => Promise; + +export class DynamicSchemaManager { + private static instance: DynamicSchemaManager; + + private globalConfigSchemaStore = useGlobalConfigSchemaStore(pinia); + + private dynamicMenuHandlers: Map = new Map(); + + // private constructor() { + // this.initializeHandlers(); + // } + + static getInstance(): DynamicSchemaManager { + if (!DynamicSchemaManager.instance) { + DynamicSchemaManager.instance = new DynamicSchemaManager(); + } + return DynamicSchemaManager.instance; + } + + // private initializeHandlers() { + // // Example + // this.registerDynamicMenuHandler('OPS_FLOW', async (menuSchema: GeneratedMenuSchema) => { + // const updatedMenu = { ...menuSchema }; + // if (!isEmpty(menuSchema?.OPS_FLOW)) {} + // return updatedMenu; + // }); + + // watch(() => this.taskManagementTemplateStore.state, () => { + // const currentMenuSchema = this.globalConfigSchemaStore.state.menuSchema; + // if (!isEmpty(currentMenuSchema?.OPS_FLOW)) { + // this.updateMenuSchema(currentMenuSchema); + // } + // }, { deep: true }); + // } + + registerDynamicMenuHandler(serviceName: string, handler: DynamicMenuHandler) { + this.dynamicMenuHandlers.set(serviceName, handler); + } + + private async updateMenuSchema(menuSchema: GeneratedMenuSchema) { + const updatedMenuSchema = await this.applyDynamicMenu(menuSchema); + this.globalConfigSchemaStore.setMenuSchema(updatedMenuSchema); + } + + private async applyDynamicMenu(menuSchema: GeneratedMenuSchema): Promise { + return Array.from(this.dynamicMenuHandlers.values()).reduce( + async (acc, handler) => handler(await acc), + Promise.resolve(menuSchema), + ); + } + + async updateSchema( + menuSchema: GeneratedMenuSchema, + routeSchema: GeneratedRouteSchema, + routeMetadataSchema: GeneratedRouteMetadataSchema, + uiAffectSchema: GeneratedUiAffectSchema, + ) { + const dynamicMenuSchema = await this.applyDynamicMenu(menuSchema); + + this.globalConfigSchemaStore.setMenuSchema(dynamicMenuSchema); + this.globalConfigSchemaStore.setRouteSchema(routeSchema); + this.globalConfigSchemaStore.setRouteMetadataSchema(routeMetadataSchema); + this.globalConfigSchemaStore.setUiAffectsSchema(uiAffectSchema); + } +} + +export default DynamicSchemaManager.getInstance(); diff --git a/apps/web/src/lib/config/global-config/feature-schema-manager.ts b/apps/web/src/lib/config/global-config/feature-schema-manager.ts index a98d2c889f..46be67762b 100644 --- a/apps/web/src/lib/config/global-config/feature-schema-manager.ts +++ b/apps/web/src/lib/config/global-config/feature-schema-manager.ts @@ -1,6 +1,4 @@ -import { useGlobalConfigSchemaStore } from '@/store/global-config-schema/global-config-schema-store'; -import { pinia } from '@/store/pinia'; - +import dynamicSchemaManager from '@/lib/config/global-config/dynamic-schema-manager'; import { getFeatureConfigurator } from '@/lib/config/global-config/helpers/get-feature-configurator'; import type { FeatureConfigurator, @@ -22,17 +20,23 @@ export class FeatureSchemaManager { }, }; - initialize(config: GlobalServiceConfig) { - const globalConfigSchemaStore = useGlobalConfigSchemaStore(pinia); + async initialize(config: GlobalServiceConfig) { this.config = { ...this.config, ...config, }; - globalConfigSchemaStore.setMenuSchema(this.createMenuSchema()); - globalConfigSchemaStore.setRouteSchema(this.createRouteSchema()); - globalConfigSchemaStore.setRouteMetadataSchema(this.createRouteMetadata()); - globalConfigSchemaStore.setUiAffectsSchema(this.createUiAffectsSchema()); + const menuSchema = this.createMenuSchema(); + const routeSchema = this.createRouteSchema(); + const routeMetadataSchema = this.createRouteMetadata(); + const uiAffectSchema = this.createUiAffectsSchema(); + + await dynamicSchemaManager.updateSchema( + menuSchema, + routeSchema, + routeMetadataSchema, + uiAffectSchema, + ); } private forEachEnabledFeature(callback: (feature: string, configurator: FeatureConfigurator, currentVersion: FeatureVersion) => T): T[] { diff --git a/apps/web/src/lib/site-initializer/merge-config.ts b/apps/web/src/lib/config/global-config/helpers/merge-config.ts similarity index 100% rename from apps/web/src/lib/site-initializer/merge-config.ts rename to apps/web/src/lib/config/global-config/helpers/merge-config.ts diff --git a/apps/web/src/lib/menu/config.ts b/apps/web/src/lib/menu/config.ts index f419e34380..5c94c55b37 100644 --- a/apps/web/src/lib/menu/config.ts +++ b/apps/web/src/lib/menu/config.ts @@ -1,4 +1,6 @@ // Menu Ids' Rule: All menu ids are dot-delimited in depth, up to two depths. +import type { TranslateResult } from 'vue-i18n'; + import type { HighlightTagType } from '@/store/display/type'; @@ -54,6 +56,7 @@ export type MenuId = typeof MENU_ID[keyof typeof MENU_ID]; export interface Menu { id: MenuId; + label?: string|TranslateResult; needPermissionByRole?: boolean; subMenuList?: Menu[]; hideOnGNB?: boolean; diff --git a/apps/web/src/lib/site-initializer/index.ts b/apps/web/src/lib/site-initializer/index.ts index f8cfc979e7..276539981f 100644 --- a/apps/web/src/lib/site-initializer/index.ts +++ b/apps/web/src/lib/site-initializer/index.ts @@ -18,6 +18,8 @@ import { useUserStore } from '@/store/user/user-store'; import config from '@/lib/config'; import featureSchemaManager from '@/lib/config/global-config/feature-schema-manager'; +import { mergeConfig } from '@/lib/config/global-config/helpers/merge-config'; +import type { GlobalServiceConfig } from '@/lib/config/global-config/types/type'; import { initRequestIdleCallback } from '@/lib/request-idle-callback-polyfill'; import { initAmcharts5 } from '@/lib/site-initializer/amcharts5'; import { initGtag, initGtm } from '@/lib/site-initializer/analysis'; @@ -27,12 +29,13 @@ import { initDomain } from '@/lib/site-initializer/domain'; import { initDomainSettings } from '@/lib/site-initializer/domain-settings'; import { initEcharts } from '@/lib/site-initializer/echarts'; import { initErrorHandler } from '@/lib/site-initializer/error-handler'; -import { mergeConfig } from '@/lib/site-initializer/merge-config'; import { initModeSetting } from '@/lib/site-initializer/mode-setting'; import { checkSsoAccessToken } from '@/lib/site-initializer/sso'; import { initUserAndAuth } from '@/lib/site-initializer/user-auth'; import { initWorkspace } from '@/lib/site-initializer/workspace'; +import { useTaskManagementTemplateStore } from '@/services/ops-flow/task-management-templates/stores/use-task-management-template-store'; + const initQueryHelper = () => { const userStore = useUserStore(pinia); QueryHelper.init(computed(() => userStore.state.timezone)); @@ -75,6 +78,15 @@ const initI18n = () => { setI18nLocale(userStore.state.language || ''); }; +const initOpsFlowTaskManagementTemplate = async (mergedConfig: GlobalServiceConfig) => { + if (!mergedConfig.OPS_FLOW.ENABLED) return; + const taskManagementTemplateStore = useTaskManagementTemplateStore(pinia); + await Promise.allSettled([ + taskManagementTemplateStore.setInitialTemplateId(), + taskManagementTemplateStore.setInitialLandingData(), + ]); +}; + const removeInitializer = () => { const el = document.getElementById('site-loader-wrapper'); if (el?.parentElement) el.parentElement.removeChild(el); @@ -91,6 +103,7 @@ const init = async () => { initDomainSettings(); await initModeSetting(); await initWorkspace(userId); + await initOpsFlowTaskManagementTemplate(mergedConfig); await featureSchemaManager.initialize(mergedConfig); await APIClientManager.initialize(mergedConfig); initRouter(domainId); diff --git a/apps/web/src/lib/site-initializer/initTaskManagementTemplate.ts b/apps/web/src/lib/site-initializer/initTaskManagementTemplate.ts deleted file mode 100644 index 8c69456436..0000000000 --- a/apps/web/src/lib/site-initializer/initTaskManagementTemplate.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { pinia } from '@/store/pinia'; - -import { - useTaskManagementTemplateStore, -} from '@/services/ops-flow/task-management-templates/stores/use-task-management-template-store'; - - -export const initTaskManagementTemplate = async () => { - const taskManagementTemplateStore = useTaskManagementTemplateStore(pinia); - await Promise.allSettled([ - taskManagementTemplateStore.setInitialTemplateId(), - taskManagementTemplateStore.setInitialLandingData(), - ]); -}; diff --git a/apps/web/src/services/ops-flow/configurator.ts b/apps/web/src/services/ops-flow/configurator.ts index 50575c6016..dc568c40b6 100644 --- a/apps/web/src/services/ops-flow/configurator.ts +++ b/apps/web/src/services/ops-flow/configurator.ts @@ -9,7 +9,6 @@ import type { } from '@/lib/config/global-config/types/type'; import type { Menu } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; -import { initTaskManagementTemplate } from '@/lib/site-initializer/initTaskManagementTemplate'; import adminOpsFlowRoutes from '@/services/ops-flow/routes/admin/routes'; import opsFlowRoutes from '@/services/ops-flow/routes/routes'; @@ -34,8 +33,6 @@ class OpsFlowConfigurator implements FeatureConfigurator { } getMenu(): GeneratedMenuConfig { - initTaskManagementTemplate(); - const baseMenu: Menu = { id: MENU_ID.OPS_FLOW, needPermissionByRole: true, diff --git a/apps/web/src/store/display/composables/use-advanced-menu-display.ts b/apps/web/src/store/display/composables/use-advanced-menu-display.ts deleted file mode 100644 index a1f989a275..0000000000 --- a/apps/web/src/store/display/composables/use-advanced-menu-display.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { DisplayMenu } from '@/store/display/type'; - -import type { MenuId } from '@/lib/menu/config'; -import { MENU_ID } from '@/lib/menu/config'; - -import { useContentsAccessibility } from '@/common/composables/contents-accessibility'; - -import { - useTaskManagementTemplateStore, -} from '@/services/ops-flow/task-management-templates/stores/use-task-management-template-store'; - -const ADVANCED_SERVICE_NAMES: MenuId[] = [MENU_ID.OPS_FLOW]; -export const useAdvancedMenuDisplay = () => { - const taskManagementTemplateStore = useTaskManagementTemplateStore(); - - const isMenuDisplayable = (menuId: MenuId): boolean => { - if (!ADVANCED_SERVICE_NAMES.includes(menuId)) return true; - const { visibleContents } = useContentsAccessibility(menuId); - return !!visibleContents; - }; - - const refineOpsflowSubMenu = (menu: DisplayMenu): DisplayMenu[]|undefined => { - const sub = menu.subMenuList; - if (!sub) return undefined; - const refined: DisplayMenu[] = sub.map((s) => { - if (s.id === MENU_ID.OPS_FLOW_LANDING) { - const label = taskManagementTemplateStore.templates.TemplateName; - const hideOnGNB = taskManagementTemplateStore.state.templateId === 'default' || !taskManagementTemplateStore.state.enableLanding; - const hideOnSiteMap = taskManagementTemplateStore.state.templateId === 'default' || !taskManagementTemplateStore.state.enableLanding; - return { - ...s, - label, - hideOnGNB, - hideOnSiteMap, - }; - } - if (s.id === MENU_ID.TASK_BOARD) { - const label = taskManagementTemplateStore.templates.TaskBoard; - return { ...s, label }; - } - return s; - }); - - return refined; - }; - - const refineGNBMenuList = (allGNBMenuList: DisplayMenu[]): DisplayMenu[] => allGNBMenuList.filter((menu) => isMenuDisplayable(menu.id)).map((menu) => { - if (menu.id === MENU_ID.OPS_FLOW) { - return { - ...menu, - subMenuList: refineOpsflowSubMenu(menu), - }; - } - return menu; - }); - return { - refineGNBMenuList, - }; -}; diff --git a/apps/web/src/store/display/display-store.ts b/apps/web/src/store/display/display-store.ts index 63bd0fc2f7..add60f3192 100644 --- a/apps/web/src/store/display/display-store.ts +++ b/apps/web/src/store/display/display-store.ts @@ -23,7 +23,6 @@ import { makeAdminRouteName } from '@/router/helpers/route-helper'; import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useAdvancedMenuDisplay } from '@/store/display/composables/use-advanced-menu-display'; import { SIDEBAR_TYPE } from '@/store/display/constant'; import type { DisplayMenu, DisplayStoreState, SidebarProps, SidebarType, @@ -79,7 +78,7 @@ const filterMenuByAccessPermission = (menuList: DisplayMenu[], pagePermissionLis const getDisplayMenuList = (menuList: Menu[], isAdminMode?: boolean, currentWorkspaceId?: string): DisplayMenu[] => menuList.map((d) => { const menuInfo: MenuInfo = MENU_INFO_MAP[d.id]; const routeName = isAdminMode ? makeAdminRouteName(MENU_INFO_MAP[d.id].routeName) : MENU_INFO_MAP[d.id].routeName; - const label = i18n.t(menuInfo.translationId); + const label = d.label || i18n.t(menuInfo.translationId); return { ...d, @@ -107,7 +106,6 @@ export const useDisplayStore = defineStore('display-store', () => { gnbNotificationLastReadTime: '', }); - const advancedMenuDisplay = useAdvancedMenuDisplay(); const getters = reactive({ hasUncheckedNotifications: computed(() => !!(state.uncheckedNotificationCount && state.uncheckedNotificationCount > 0)), isHandbookVisible: computed(() => state.visibleSidebar && (state.sidebarType === SIDEBAR_TYPE.handbook)), @@ -330,8 +328,6 @@ export const useDisplayStore = defineStore('display-store', () => { }); } - // advanced service menu display - _allGnbMenuList = advancedMenuDisplay.refineGNBMenuList(_allGnbMenuList); return _allGnbMenuList; }; diff --git a/apps/web/src/store/global-config-schema/global-config-schema-store.ts b/apps/web/src/store/global-config-schema/global-config-schema-store.ts index 6cb2939fe4..5b81e6dd7d 100644 --- a/apps/web/src/store/global-config-schema/global-config-schema-store.ts +++ b/apps/web/src/store/global-config-schema/global-config-schema-store.ts @@ -4,24 +4,52 @@ import { orderBy } from 'lodash'; import { defineStore } from 'pinia'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import type { DisplayMenu } from '@/store/display/type'; import type { GeneratedRouteSchema, GeneratedMenuSchema, GeneratedRouteMetadataSchema, GeneratedUiAffectSchema, } from '@/lib/config/global-config/types/type'; import type { Menu, MenuId } from '@/lib/menu/config'; +import { MENU_ID } from '@/lib/menu/config'; import { DEFAULT_MENU_LIST, DEFAULT_ADMIN_MENU_LIST } from '@/lib/menu/menu-architecture'; +import { useTaskManagementTemplateStore } from '@/services/ops-flow/task-management-templates/stores/use-task-management-template-store'; + +export type FlattenedMenuMap = Partial>; + interface GlobalConfigSchemaStoreState { uiAffectsSchema: GeneratedUiAffectSchema; menuSchema: GeneratedMenuSchema; routeMetadataSchema: GeneratedRouteMetadataSchema; routeSchema: GeneratedRouteSchema; } -export type FlattenedMenuMap = Partial>; + +interface MenuTransformer { + menuId: MenuId; + transform: (menu: DisplayMenu, getters: any) => Partial; + shouldInclude?: (getters: any) => boolean; +} + +const MENU_TRANSFORMERS_MAP = new Map([ + [MENU_ID.OPS_FLOW_LANDING, { + menuId: MENU_ID.OPS_FLOW_LANDING, + transform: (menu: DisplayMenu, getters: any) => ({ + label: getters.templateName, + }), + shouldInclude: (getters: any) => getters.templateId !== 'default' && getters.enableLanding, + }], + [MENU_ID.TASK_BOARD, { + menuId: MENU_ID.TASK_BOARD, + transform: (menu: DisplayMenu, getters: any) => ({ + label: getters.taskBoardName, + }), + }], +]); export const useGlobalConfigSchemaStore = defineStore('global-config-schema-store', () => { const appContextStore = useAppContextStore(); + const taskManagementTemplateStore = useTaskManagementTemplateStore(); const state = reactive({ uiAffectsSchema: {} as GeneratedUiAffectSchema, @@ -32,28 +60,62 @@ export const useGlobalConfigSchemaStore = defineStore('global-config-schema-stor const _getters = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), + templateName: computed(() => taskManagementTemplateStore.templates.TemplateName), + taskBoardName: computed(() => taskManagementTemplateStore.templates.TaskBoard), + templateId: computed(() => taskManagementTemplateStore.state.templateId), + enableLanding: computed(() => taskManagementTemplateStore.state.enableLanding), }); - const getters = reactive({ - menuList: computed(() => { - const menuList: Menu[] = _getters.isAdminMode ? [] : DEFAULT_MENU_LIST; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - Object.entries(state.menuSchema).forEach(([_, featureSetting]) => { - if (featureSetting) { - const menu = _getters.isAdminMode ? featureSetting.adminMenu : featureSetting.menu; - if (menu && !menuList.some((existingMenu) => existingMenu.id === menu.id)) { - menuList.push(menu); - } - } - }); + const baseMenuList = computed(() => { + const menuList: Menu[] = _getters.isAdminMode ? [] : [...DEFAULT_MENU_LIST]; - if (_getters.isAdminMode) { - menuList.push(...DEFAULT_ADMIN_MENU_LIST); + Object.values(state.menuSchema).forEach((featureSetting) => { + if (featureSetting) { + const menu = _getters.isAdminMode ? featureSetting.adminMenu : featureSetting.menu; + if (menu && !menuList.some((existingMenu) => existingMenu.id === menu.id)) { + menuList.push(menu); + } } + }); + + if (_getters.isAdminMode) { + menuList.push(...DEFAULT_ADMIN_MENU_LIST); + } + + return menuList; + }); + + const transformSubMenu = (subMenu: DisplayMenu): DisplayMenu | null => { + const transformer = MENU_TRANSFORMERS_MAP.get(subMenu.id); + if (!transformer) return subMenu; + if (transformer.shouldInclude && !transformer.shouldInclude(_getters)) return null; + + return { + ...subMenu, + ...transformer.transform(subMenu, _getters), + }; + }; + + const transformMenu = (menu: DisplayMenu): DisplayMenu => { + if (!menu.subMenuList) return menu; + + const transformedSubMenus = menu.subMenuList + .map(transformSubMenu) + .filter((subMenu): subMenu is DisplayMenu => subMenu !== null); + + return { + ...menu, + subMenuList: transformedSubMenus, + }; + }; + + const getters = reactive({ + menuList: computed(() => { + const refinedMenuList = baseMenuList.value + .map((menu) => transformMenu(menu as DisplayMenu)); - const orderedMenus = menuList.filter((menu) => menu.order !== undefined); - const unorderedMenus = menuList.filter((menu) => menu.order === undefined); + const orderedMenus = refinedMenuList.filter((menu) => menu.order !== undefined); + const unorderedMenus = refinedMenuList.filter((menu) => menu.order === undefined); return [...orderBy(orderedMenus, ['order'], ['asc']), ...unorderedMenus]; }), From 1bdd5982ee81b9ab35482d367e56de7253ae2fcd Mon Sep 17 00:00:00 2001 From: "NaYeong,Kim" Date: Tue, 22 Apr 2025 14:07:38 +0900 Subject: [PATCH 02/51] chore: add missed api endpoint (#5793) * chore: add missed api endpoint Signed-off-by: NaYeong,Kim * chore: moved api-client-manager file Signed-off-by: NaYeong,Kim --------- Signed-off-by: NaYeong,Kim --- .../config/global-config}/api-client-manager.ts | 0 .../src/lib/config/global-config/schema/api-client-schema.ts | 3 +++ apps/web/src/lib/config/global-config/types/type.ts | 1 + apps/web/src/lib/site-initializer/index.ts | 2 +- .../src/store/reference/escalation-policy-reference-store.ts | 3 ++- apps/web/src/store/reference/service-reference-store.ts | 2 +- apps/web/src/store/reference/webhook-reference-store.ts | 2 +- 7 files changed, 9 insertions(+), 4 deletions(-) rename apps/web/src/{api-clients => lib/config/global-config}/api-client-manager.ts (100%) diff --git a/apps/web/src/api-clients/api-client-manager.ts b/apps/web/src/lib/config/global-config/api-client-manager.ts similarity index 100% rename from apps/web/src/api-clients/api-client-manager.ts rename to apps/web/src/lib/config/global-config/api-client-manager.ts diff --git a/apps/web/src/lib/config/global-config/schema/api-client-schema.ts b/apps/web/src/lib/config/global-config/schema/api-client-schema.ts index 069bd075f5..bb1728c9c0 100644 --- a/apps/web/src/lib/config/global-config/schema/api-client-schema.ts +++ b/apps/web/src/lib/config/global-config/schema/api-client-schema.ts @@ -8,6 +8,9 @@ export const ApiClientEndpoint: ApiClientsSchemaType = { PROJECT: { V1: 'identity', }, + SERVICE_ACCOUNT: { + V1: 'identity', + }, ASSET_INVENTORY: { V1: 'inventory', }, diff --git a/apps/web/src/lib/config/global-config/types/type.ts b/apps/web/src/lib/config/global-config/types/type.ts index ca1eb4a382..b5d7f5a62a 100644 --- a/apps/web/src/lib/config/global-config/types/type.ts +++ b/apps/web/src/lib/config/global-config/types/type.ts @@ -68,6 +68,7 @@ type apiClientType = { export type ApiClientsSchemaType = { DASHBOARDS: apiClientType, PROJECT: apiClientType, + SERVICE_ACCOUNT: apiClientType, ASSET_INVENTORY: apiClientType, COST_ANALYSIS: apiClientType, OPS_FLOW: apiClientType, diff --git a/apps/web/src/lib/site-initializer/index.ts b/apps/web/src/lib/site-initializer/index.ts index 276539981f..476e6c81ce 100644 --- a/apps/web/src/lib/site-initializer/index.ts +++ b/apps/web/src/lib/site-initializer/index.ts @@ -2,7 +2,6 @@ import { computed, watch } from 'vue'; import { QueryHelper } from '@cloudforet/core-lib/query'; -import APIClientManager from '@/api-clients/api-client-manager'; import { SpaceRouter } from '@/router'; import { setI18nLocale } from '@/translations'; @@ -17,6 +16,7 @@ import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import { useUserStore } from '@/store/user/user-store'; import config from '@/lib/config'; +import APIClientManager from '@/lib/config/global-config/api-client-manager'; import featureSchemaManager from '@/lib/config/global-config/feature-schema-manager'; import { mergeConfig } from '@/lib/config/global-config/helpers/merge-config'; import type { GlobalServiceConfig } from '@/lib/config/global-config/types/type'; diff --git a/apps/web/src/store/reference/escalation-policy-reference-store.ts b/apps/web/src/store/reference/escalation-policy-reference-store.ts index e9f33bac29..3018a03ed4 100644 --- a/apps/web/src/store/reference/escalation-policy-reference-store.ts +++ b/apps/web/src/store/reference/escalation-policy-reference-store.ts @@ -5,7 +5,6 @@ import { computed, reactive } from 'vue'; import { defineStore } from 'pinia'; -import APIClientManager from '@/api-clients/api-client-manager'; import type { EscalationPolicyModel } from '@/schema/alert-manager/escalation-policy/model'; import type { EscalationPolicyModel as EscalationPolicyModelV1 } from '@/schema/monitoring/escalation-policy/model'; @@ -14,6 +13,8 @@ import type { } from '@/store/reference/type'; import { useUserStore } from '@/store/user/user-store'; +import APIClientManager from '@/lib/config/global-config/api-client-manager'; + import ErrorHandler from '@/common/composables/error/errorHandler'; diff --git a/apps/web/src/store/reference/service-reference-store.ts b/apps/web/src/store/reference/service-reference-store.ts index dd7672f7b9..5d28f2c735 100644 --- a/apps/web/src/store/reference/service-reference-store.ts +++ b/apps/web/src/store/reference/service-reference-store.ts @@ -3,7 +3,6 @@ import { computed, reactive } from 'vue'; import { defineStore } from 'pinia'; -import APIClientManager from '@/api-clients/api-client-manager'; import type { ServiceListParameters } from '@/schema/alert-manager/service/api-verbs/list'; import type { ServiceModel } from '@/schema/alert-manager/service/model'; @@ -13,6 +12,7 @@ import type { } from '@/store/reference/type'; import { useUserStore } from '@/store/user/user-store'; +import APIClientManager from '@/lib/config/global-config/api-client-manager'; import { MANAGED_VARIABLE_MODELS } from '@/lib/variable-models/managed-model-config/base-managed-model-config'; import ErrorHandler from '@/common/composables/error/errorHandler'; diff --git a/apps/web/src/store/reference/webhook-reference-store.ts b/apps/web/src/store/reference/webhook-reference-store.ts index a08534c669..021e630212 100644 --- a/apps/web/src/store/reference/webhook-reference-store.ts +++ b/apps/web/src/store/reference/webhook-reference-store.ts @@ -3,7 +3,6 @@ import { computed, reactive } from 'vue'; import { defineStore } from 'pinia'; -import APIClientManager from '@/api-clients/api-client-manager'; import type { WebhookModel } from '@/schema/alert-manager/webhook/model'; import type { WebhookModel as WebhookModelV1 } from '@/schema/monitoring/webhook/model'; @@ -12,6 +11,7 @@ import type { } from '@/store/reference/type'; import { useUserStore } from '@/store/user/user-store'; +import APIClientManager from '@/lib/config/global-config/api-client-manager'; import { MANAGED_VARIABLE_MODELS } from '@/lib/variable-models/managed-model-config/base-managed-model-config'; import ErrorHandler from '@/common/composables/error/errorHandler'; From 63aacfaa5e61f95af3eca8d57719ad9f6ab6fa7c Mon Sep 17 00:00:00 2001 From: "NaYeong,Kim" Date: Tue, 22 Apr 2025 17:12:09 +0900 Subject: [PATCH 03/51] refactor: apply merged config to service-api (#5798) * refactor: apply merged config to service-api Signed-off-by: NaYeong,Kim * chore: fixed lint error Signed-off-by: NaYeong,Kim --------- Signed-off-by: NaYeong,Kim --- .../global-config/api-client-manager.ts | 88 ++++++++++++++----- .../src/lib/site-initializer/api-client.ts | 2 - apps/web/src/lib/site-initializer/index.ts | 2 +- .../escalation-policy-reference-store.ts | 2 - .../core-lib/src/space-connector/index.ts | 20 +++-- .../src/space-connector/service-api.ts | 11 ++- 6 files changed, 86 insertions(+), 39 deletions(-) diff --git a/apps/web/src/lib/config/global-config/api-client-manager.ts b/apps/web/src/lib/config/global-config/api-client-manager.ts index 4ea744e4c0..c51fd79ac7 100644 --- a/apps/web/src/lib/config/global-config/api-client-manager.ts +++ b/apps/web/src/lib/config/global-config/api-client-manager.ts @@ -3,58 +3,102 @@ import { SpaceConnector } from '@cloudforet/core-lib/space-connector'; import { ApiClientEndpoint } from '@/lib/config/global-config/schema/api-client-schema'; import type { ApiClientsSchemaType, GlobalServiceConfig } from '@/lib/config/global-config/types/type'; +interface ServiceEndpoint { + endpoint: any; + version: string; +} + +class APIClientManagerError extends Error { + constructor(message: string) { + super(`[APIClientManager] ${message}`); + this.name = 'APIClientManagerError'; + } +} class APIClientManager { // eslint-disable-next-line no-undef [key: string]: any; - private config: GlobalServiceConfig = {} as GlobalServiceConfig; + private config: GlobalServiceConfig | null = null; - private apiClientsSchema: ApiClientsSchemaType = {} as ApiClientsSchemaType; + private apiClientsSchema: ApiClientsSchemaType | null = null; - async initialize(mergedConfig) { - this.config = mergedConfig; + private serviceEndpoints: Map = new Map(); + async initialize(mergedConfig: GlobalServiceConfig): Promise { + this.config = mergedConfig; this.apiClientsSchema = JSON.parse(JSON.stringify(ApiClientEndpoint)); + await this.initializeServiceConfig(); this.defineDynamicServices(); } - private defineDynamicServices() { + private async initializeServiceConfig(): Promise { + if (!this.config || !this.apiClientsSchema) { + throw new APIClientManagerError('Configuration or schema is not initialized'); + } + + const serviceConfig: Record = {}; + + Object.entries(this.config).forEach(([serviceName, config]) => { + if (this.apiClientsSchema?.[serviceName]) { + const version = config.VERSION; + const endpoint = this.apiClientsSchema[serviceName][version]; + if (endpoint) { + const formattedEndpoint = APIClientManager.formatEndpoint(endpoint); + serviceConfig[formattedEndpoint] = { + enabled: config.ENABLED, + }; + } + } + }); + + SpaceConnector.setServiceConfig(serviceConfig); + } + + private static formatEndpoint(endpoint: string): string { + return endpoint.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); + } + + private defineDynamicServices(): void { if (!this.apiClientsSchema) { - throw new Error('[APIClientManager] APIClientManager is not initialized. Call initialize() first.'); + throw new APIClientManagerError('APIClientManager is not initialized'); } Object.keys(this.apiClientsSchema).forEach((serviceName) => { - const propertyName = serviceName.toLowerCase() - .replace(/_([a-z])/g, (_, char) => char.toUpperCase()); - + const propertyName = APIClientManager.formatPropertyName(serviceName); Object.defineProperty(this, propertyName, { - get: () => this.createServiceHandler(serviceName), + get: () => this.getServiceEndpoint(serviceName), enumerable: true, + configurable: true, }); }); } - private createServiceHandler(serviceName: string) { - const serviceConfig = this.config?.[serviceName] || null; - if (!serviceConfig || !serviceConfig.ENABLED) { - return null; - } + private static formatPropertyName(serviceName: string): string { + return serviceName.toLowerCase() + .replace(/_([a-z])/g, (_, char) => char.toUpperCase()); + } - const apiClientSchemaByService = this.apiClientsSchema[serviceName]; - if (!apiClientSchemaByService) { - return null; + private getServiceEndpoint(serviceName: string): ServiceEndpoint | null { + if (this.serviceEndpoints.has(serviceName)) { + return this.serviceEndpoints.get(serviceName) ?? null; } + const serviceConfig = this.config?.[serviceName]; + if (!serviceConfig?.ENABLED) return null; + + const apiClientSchemaByService = this.apiClientsSchema?.[serviceName]; + if (!apiClientSchemaByService) return null; + const version = serviceConfig.VERSION; const clientEndpoint = apiClientSchemaByService[version]; - if (!clientEndpoint) { - return null; - } + if (!clientEndpoint) return null; const endpoint = SpaceConnector.clientV2[clientEndpoint]; + const serviceEndpoint: ServiceEndpoint = { endpoint, version }; - return { endpoint, version }; + this.serviceEndpoints.set(serviceName, serviceEndpoint); + return serviceEndpoint; } } diff --git a/apps/web/src/lib/site-initializer/api-client.ts b/apps/web/src/lib/site-initializer/api-client.ts index 8f6f503339..97198e2e26 100644 --- a/apps/web/src/lib/site-initializer/api-client.ts +++ b/apps/web/src/lib/site-initializer/api-client.ts @@ -424,14 +424,12 @@ export const initApiClient = async (config) => { mockConfig: getMockConfig(config), authConfig: getAuthConfig(config), }; - const serviceConfig = config.get('SERVICES') || {}; await SpaceConnector.init( endpoints, tokenApi, apiSettings, devConfig, getAfterCallApiMap(), - serviceConfig, ); const existingRefreshToken = SpaceConnector.getRefreshToken(); diff --git a/apps/web/src/lib/site-initializer/index.ts b/apps/web/src/lib/site-initializer/index.ts index 476e6c81ce..246b1f55a5 100644 --- a/apps/web/src/lib/site-initializer/index.ts +++ b/apps/web/src/lib/site-initializer/index.ts @@ -100,12 +100,12 @@ const init = async () => { const domainId = await initDomain(config); const userId = await initUserAndAuth(config); const mergedConfig = await mergeConfig(config, domainId); + await APIClientManager.initialize(mergedConfig); initDomainSettings(); await initModeSetting(); await initWorkspace(userId); await initOpsFlowTaskManagementTemplate(mergedConfig); await featureSchemaManager.initialize(mergedConfig); - await APIClientManager.initialize(mergedConfig); initRouter(domainId); // prefetchResources(); initI18n(); diff --git a/apps/web/src/store/reference/escalation-policy-reference-store.ts b/apps/web/src/store/reference/escalation-policy-reference-store.ts index 3018a03ed4..7bd6a87a7a 100644 --- a/apps/web/src/store/reference/escalation-policy-reference-store.ts +++ b/apps/web/src/store/reference/escalation-policy-reference-store.ts @@ -1,5 +1,3 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck import { asyncComputed } from '@vueuse/core'; import { computed, reactive } from 'vue'; diff --git a/packages/core-lib/src/space-connector/index.ts b/packages/core-lib/src/space-connector/index.ts index 455c3451f4..a33c937af3 100644 --- a/packages/core-lib/src/space-connector/index.ts +++ b/packages/core-lib/src/space-connector/index.ts @@ -59,23 +59,19 @@ export class SpaceConnector { private static interceptorIds: number[] = []; // [v1 id, v2 id] - private readonly serviceConfig: Record = {}; - constructor( endpoints: string[], tokenApi: TokenAPI, apiSettings: CreateAxiosDefaults[], devConfig: DevConfig|undefined, afterCallApiMap: AfterCallApiMap, - serviceConfig: Record, ) { SpaceConnector.mockConfig = devConfig?.mockConfig ?? {}; SpaceConnector.authConfig = devConfig?.authConfig ?? {}; SpaceConnector.isDevMode = devConfig?.enabled ?? false; - this.serviceConfig = serviceConfig; this.tokenApi = tokenApi; - this.serviceApi = new ServiceAPI(endpoints[0], this.tokenApi, apiSettings[0], this.serviceConfig); - const serviceApiV2 = new ServiceAPI(endpoints[1], this.tokenApi, apiSettings[1], this.serviceConfig); + this.serviceApi = new ServiceAPI(endpoints[0], this.tokenApi, apiSettings[0]); + const serviceApiV2 = new ServiceAPI(endpoints[1], this.tokenApi, apiSettings[1]); this.serviceApiV2 = serviceApiV2; this.afterCallApiMap = afterCallApiMap; this._restClient = serviceApiV2.instance; @@ -99,10 +95,9 @@ export class SpaceConnector { apiSettings: CreateAxiosDefaults[] = [], devConfig: DevConfig = {}, afterCallApiMap: AfterCallApiMap = {}, - serviceConfig: Record = {}, ): Promise { if (!SpaceConnector.instance) { - SpaceConnector.instance = new SpaceConnector(endpoints, tokenApi, apiSettings, devConfig, afterCallApiMap, serviceConfig); + SpaceConnector.instance = new SpaceConnector(endpoints, tokenApi, apiSettings, devConfig, afterCallApiMap); await Promise.allSettled([ SpaceConnector.instance.loadAPI(1), SpaceConnector.instance.loadAPI(2), @@ -151,6 +146,15 @@ export class SpaceConnector { SpaceConnector.instance.tokenApi.flushToken(); } + static setServiceConfig(serviceConfig: Record): void { + if (SpaceConnector.instance) { + SpaceConnector.instance.serviceApi.updateServiceConfig(serviceConfig); + SpaceConnector.instance.serviceApiV2.updateServiceConfig(serviceConfig); + } else { + throw new Error('Not initialized client!'); + } + } + static async refreshAccessToken(executeSessionTimeoutCallback: boolean): Promise { return SpaceConnector.instance.tokenApi.refreshAccessToken(executeSessionTimeoutCallback); } diff --git a/packages/core-lib/src/space-connector/service-api.ts b/packages/core-lib/src/space-connector/service-api.ts index 7e5ef823d0..d0477253f9 100644 --- a/packages/core-lib/src/space-connector/service-api.ts +++ b/packages/core-lib/src/space-connector/service-api.ts @@ -17,7 +17,7 @@ export default class ServiceAPI { serviceConfig: Record = {}; - constructor(baseURL: string, tokenApi: TokenAPI, settings: CreateAxiosDefaults = {}, serviceConfig: Record = {}) { + constructor(baseURL: string, tokenApi: TokenAPI, settings: CreateAxiosDefaults = {}) { this.instance = axios.create({ ...settings, headers: { @@ -26,7 +26,6 @@ export default class ServiceAPI { baseURL, }); this.tokenApi = tokenApi; - this.serviceConfig = serviceConfig; tokenApi.loadToken(); this.setAxiosInterceptors(); } @@ -60,7 +59,7 @@ export default class ServiceAPI { // Check if the service is enabled const serviceName = ServiceAPI.extractServiceNameFromEndpoint(request.url || ''); const serviceInfo = this.serviceConfig[serviceName]; - if (serviceInfo && serviceInfo.ENABLED === false) { + if (serviceInfo && serviceInfo.enabled === false) { throw new Error(`[ServiceAPI] ${serviceName} service is disabled.`); } @@ -76,7 +75,11 @@ export default class ServiceAPI { private static extractServiceNameFromEndpoint(url: string): string { const service = url.split('/')[1]; - if (service) return service.replace(/-/g, '_').toUpperCase(); + if (service) return service; return ''; } + + updateServiceConfig(serviceConfig: Record): void { + this.serviceConfig = serviceConfig; + } } From 573b8beb1331213c4df31ce59473fb8685410198 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Tue, 22 Apr 2025 15:26:12 +0900 Subject: [PATCH 04/51] feat(global-store): separate global store concern Signed-off-by: samuel.park --- .../composables/use-ui-affects-schema.ts | 11 ++ .../global-config/constants/constants.ts | 2 +- .../lib/config/global-config/types/type.ts | 10 +- .../services/iam/constants/role-constant.ts | 22 +-- .../src/store/derived/access-control-store.ts | 78 ++++++++++ apps/web/src/store/derived/menu-store.ts | 136 ++++++++++++++++++ .../src/store/display/use-all-menu-list.ts | 104 ++++++++++++++ 7 files changed, 346 insertions(+), 17 deletions(-) create mode 100644 apps/web/src/lib/config/global-config/composables/use-ui-affects-schema.ts create mode 100644 apps/web/src/store/derived/access-control-store.ts create mode 100644 apps/web/src/store/derived/menu-store.ts create mode 100644 apps/web/src/store/display/use-all-menu-list.ts diff --git a/apps/web/src/lib/config/global-config/composables/use-ui-affects-schema.ts b/apps/web/src/lib/config/global-config/composables/use-ui-affects-schema.ts new file mode 100644 index 0000000000..3886080fe8 --- /dev/null +++ b/apps/web/src/lib/config/global-config/composables/use-ui-affects-schema.ts @@ -0,0 +1,11 @@ +import { computed } from 'vue'; + +import { useGlobalConfigSchemaStore } from '@/store/global-config-schema/global-config-schema-store'; + +import type { FeatureKeyType } from '@/lib/config/global-config/types/type'; + +export const useUiAffectsSchema = (featureKey: FeatureKeyType) => { + const globalConfigSchemaStore = useGlobalConfigSchemaStore(); + + return computed(() => globalConfigSchemaStore.state.uiAffectsSchema[featureKey]); +}; diff --git a/apps/web/src/lib/config/global-config/constants/constants.ts b/apps/web/src/lib/config/global-config/constants/constants.ts index c10e39b046..914f28347f 100644 --- a/apps/web/src/lib/config/global-config/constants/constants.ts +++ b/apps/web/src/lib/config/global-config/constants/constants.ts @@ -1,4 +1,4 @@ -export const FEATURES = { +export const SERVICE_FEATURES = { DASHBOARDS: 'DASHBOARDS', PROJECT: 'PROJECT', SERVICE_ACCOUNT: 'SERVICE_ACCOUNT', diff --git a/apps/web/src/lib/config/global-config/types/type.ts b/apps/web/src/lib/config/global-config/types/type.ts index b5d7f5a62a..5baaf36d0c 100644 --- a/apps/web/src/lib/config/global-config/types/type.ts +++ b/apps/web/src/lib/config/global-config/types/type.ts @@ -1,16 +1,16 @@ import type { RouteConfig } from 'vue-router'; -import type { FEATURES } from '@/lib/config/global-config/constants/constants'; +import type { SERVICE_FEATURES } from '@/lib/config/global-config/constants/constants'; import type { Menu } from '@/lib/menu/config'; -export type FeatureKeyType = typeof FEATURES[keyof typeof FEATURES]; +export type FeatureKeyType = typeof SERVICE_FEATURES[keyof typeof SERVICE_FEATURES]; export type FeatureVersion = 'V1' | 'V2'; export type ServiceConfig = { ENABLED: boolean; VERSION: FeatureVersion; }; -export type GlobalServiceConfig = Record; +export type GlobalServiceConfig = Record; export interface FeatureConfigurator { uiAffect: GeneratedUiAffectConfig[]; @@ -25,7 +25,7 @@ export interface GeneratedMenuConfig { menu: Menu; adminMenu?: Menu|null; } -export type GeneratedMenuSchema = Record; +export type GeneratedMenuSchema = Record; type UiAffectConfig = { method: string; @@ -35,7 +35,7 @@ export interface GeneratedUiAffectConfig { feature: string; affects: UiAffectConfig[]; } -export type GeneratedUiAffectSchema = Record>; +export type GeneratedUiAffectSchema = Record>; export interface RouteVersionInfo { name: string; diff --git a/apps/web/src/services/iam/constants/role-constant.ts b/apps/web/src/services/iam/constants/role-constant.ts index fe7f75ccd0..ab279da733 100644 --- a/apps/web/src/services/iam/constants/role-constant.ts +++ b/apps/web/src/services/iam/constants/role-constant.ts @@ -3,7 +3,7 @@ import type { KeyItemSet } from '@cloudforet/mirinae/types/controls/search/query import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; -import { FEATURES } from '@/lib/config/global-config/constants/constants'; +import { SERVICE_FEATURES } from '@/lib/config/global-config/constants/constants'; import type { ExcelDataField } from '@/lib/helper/file-download-helper/type'; import { MENU_ID } from '@/lib/menu/config'; @@ -92,12 +92,12 @@ export const MANAGED_PAGE_ACCESS = [ ] as const; export const DEFAULT_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] = [ - { id: MENU_ID.DASHBOARDS, key: FEATURES.DASHBOARDS }, - { id: MENU_ID.SERVICE_ACCOUNT, key: FEATURES.SERVICE_ACCOUNT }, - { id: MENU_ID.PROJECT, key: FEATURES.PROJECT }, + { id: MENU_ID.DASHBOARDS, key: SERVICE_FEATURES.DASHBOARDS }, + { id: MENU_ID.SERVICE_ACCOUNT, key: SERVICE_FEATURES.SERVICE_ACCOUNT }, + { id: MENU_ID.PROJECT, key: SERVICE_FEATURES.PROJECT }, { id: MENU_ID.ASSET_INVENTORY, - key: FEATURES.ASSET_INVENTORY, + key: SERVICE_FEATURES.ASSET_INVENTORY, subMenuList: [ { id: MENU_ID.CLOUD_SERVICE }, { id: MENU_ID.SERVER }, @@ -108,7 +108,7 @@ export const DEFAULT_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] = }, { id: MENU_ID.COST_EXPLORER, - key: FEATURES.COST_EXPLORER, + key: SERVICE_FEATURES.COST_EXPLORER, subMenuList: [ { id: MENU_ID.COST_ANALYSIS }, { id: MENU_ID.BUDGET }, @@ -117,7 +117,7 @@ export const DEFAULT_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] = }, { id: MENU_ID.OPS_FLOW, - key: FEATURES.OPS_FLOW, + key: SERVICE_FEATURES.OPS_FLOW, subMenuList: [ { id: MENU_ID.OPS_FLOW_LANDING }, { id: MENU_ID.TASK_BOARD }, @@ -127,7 +127,7 @@ export const DEFAULT_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] = export const ALERT_V2_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] = [ { id: MENU_ID.ALERT_MANAGER, - key: FEATURES.ALERT_MANAGER, + key: SERVICE_FEATURES.ALERT_MANAGER, subMenuList: [ { id: MENU_ID.SERVICE }, { id: MENU_ID.ALERTS }, @@ -135,7 +135,7 @@ export const ALERT_V2_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] }, { id: MENU_ID.IAM, - key: FEATURES.IAM, + key: SERVICE_FEATURES.IAM, subMenuList: [ { id: MENU_ID.USER }, { id: MENU_ID.USER_GROUP }, @@ -147,7 +147,7 @@ export const ALERT_V2_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] export const ALERT_V1_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] = [ { id: MENU_ID.ALERT_MANAGER, - key: FEATURES.ALERT_MANAGER, + key: SERVICE_FEATURES.ALERT_MANAGER, subMenuList: [ { id: MENU_ID.ALERT_MANAGER_DASHBOARD }, { id: MENU_ID.ALERTS }, @@ -156,7 +156,7 @@ export const ALERT_V1_WORKSPACE_PAGE_ACCESS_MENU_LIST: PageAccessMenuByConfig[] }, { id: MENU_ID.IAM, - key: FEATURES.IAM, + key: SERVICE_FEATURES.IAM, subMenuList: [ { id: MENU_ID.USER }, { id: MENU_ID.APP }, diff --git a/apps/web/src/store/derived/access-control-store.ts b/apps/web/src/store/derived/access-control-store.ts new file mode 100644 index 0000000000..8a2a817f99 --- /dev/null +++ b/apps/web/src/store/derived/access-control-store.ts @@ -0,0 +1,78 @@ +import { computed } from 'vue'; + +import { defineStore } from 'pinia'; + +import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; + +import { useMenuStore } from '@/store/derived/menu-store'; +import { useUserStore } from '@/store/user/user-store'; + +import type { PageAccessMap } from '@/lib/access-control/config'; +import { + checkAllMenuReadonly, + getDefaultPageAccessPermissionList, + getMinimalPageAccessPermissionList, + getPageAccessMapFromRawData, +} from '@/lib/access-control/page-access-helper'; +import type { MenuId } from '@/lib/menu/config'; + +export const useAccessControlStore = defineStore('derived-access-control-store', () => { + const userStore = useUserStore(); + const menuStore = useMenuStore(); + + const getters = { + pageAccessPermissionList: computed(() => { + const roleType = userStore.state.currentRoleInfo?.roleType ?? 'USER'; + const roleBasePagePermissions = userStore.state.currentRoleInfo?.pageAccess ?? ['my_page.*']; + const pagePermissionMap = getPageAccessMapFromRawData({ + pageAccessPermissions: roleBasePagePermissions, + menuList: menuStore.getters.menuList, + }); + const minimalPagePermissionList = getMinimalPageAccessPermissionList(roleType); + const defaultPagePermissionList = getDefaultPageAccessPermissionList(roleType); + + Object.keys(pagePermissionMap).forEach((menuId) => { + if (minimalPagePermissionList.includes(menuId as MenuId)) pagePermissionMap[menuId] = { read: true, write: true, access: true }; + }); + let result = [...minimalPagePermissionList]; + Object.keys(pagePermissionMap).forEach((menuId) => { + const _menuId = menuId as MenuId; + if (defaultPagePermissionList.includes(_menuId) && !minimalPagePermissionList.includes(_menuId) && pagePermissionMap[_menuId].access) result = [...result, _menuId]; + }); + + return result; + }), + pageAccessPermissionMap: computed(() => { + const result: PageAccessMap = {}; + + const roleType = userStore.state.currentRoleInfo?.roleType ?? 'USER'; + const roleBasePagePermissions = userStore.state.currentRoleInfo?.pageAccess ?? ['my_page.*']; + const pagePermissionMap = getPageAccessMapFromRawData({ + pageAccessPermissions: roleBasePagePermissions, + menuList: menuStore.getters.menuList, + }); + const minimalPagePermissionList = getMinimalPageAccessPermissionList(roleType); + + const isAllReadOnly = checkAllMenuReadonly(roleBasePagePermissions); + + getters.pageAccessPermissionList.value?.forEach((menuId) => { + if (!result[menuId]) { + if (roleType === ROLE_TYPE.DOMAIN_ADMIN) { + result[menuId] = { + write: !isAllReadOnly, + }; + } else { + result[menuId] = { + write: minimalPagePermissionList.includes(menuId) ? true : pagePermissionMap[menuId]?.write, + }; + } + } + }); + return result; + }), + }; + + return { + getters, + }; +}); diff --git a/apps/web/src/store/derived/menu-store.ts b/apps/web/src/store/derived/menu-store.ts new file mode 100644 index 0000000000..646872f5a6 --- /dev/null +++ b/apps/web/src/store/derived/menu-store.ts @@ -0,0 +1,136 @@ +import { computed, reactive } from 'vue'; + +import { orderBy } from 'lodash'; +import { defineStore } from 'pinia'; + +import type { Menu, MenuId } from '@/lib/menu/config'; +import { MENU_ID } from '@/lib/menu/config'; +import { DEFAULT_ADMIN_MENU_LIST, DEFAULT_MENU_LIST } from '@/lib/menu/menu-architecture'; + +import { useTaskManagementTemplateStore } from '@/services/ops-flow/task-management-templates/stores/use-task-management-template-store'; + +import { useAppContextStore } from '../app-context/app-context-store'; +import type { DisplayMenu } from '../display/type'; +import { useGlobalConfigSchemaStore } from '../global-config-schema/global-config-schema-store'; + +export type FlattenedMenuMap = Partial>; + +interface MenuContext { + templateName: string; + taskBoardName: string; + templateId: string; + enableLanding: boolean; +} +interface MenuCustomization { + menuId: MenuId; + applyLabel: (menu: DisplayMenu, context: MenuContext) => Partial; + shouldShow?: (context: MenuContext) => boolean; +} + +const MENU_CUSTOMIZATIONS = new Map([ + [MENU_ID.OPS_FLOW_LANDING, { + menuId: MENU_ID.OPS_FLOW_LANDING, + applyLabel: (menu, ctx) => ({ label: ctx.templateName }), + shouldShow: (ctx) => ctx.templateId !== 'default' && ctx.enableLanding, + }], + [MENU_ID.TASK_BOARD, { + menuId: MENU_ID.TASK_BOARD, + applyLabel: (menu, ctx) => ({ label: ctx.taskBoardName }), + }], +]); + +export const useMenuStore = defineStore('derived-menu', () => { + const globalConfigSchemaStore = useGlobalConfigSchemaStore(); + const taskManagementTemplateStore = useTaskManagementTemplateStore(); + const appContextStore = useAppContextStore(); + + const _isAdminMode = computed(() => appContextStore.getters.isAdminMode); + + const _menuContext = computed(() => ({ + templateName: taskManagementTemplateStore.templates.TemplateName, + taskBoardName: taskManagementTemplateStore.templates.TaskBoard, + templateId: taskManagementTemplateStore.state.templateId, + enableLanding: taskManagementTemplateStore.state.enableLanding, + })); + const _baseMenuList = computed(() => { + const menuList: Menu[] = _isAdminMode.value ? [] : [...DEFAULT_MENU_LIST]; + + Object.values(globalConfigSchemaStore.state.menuSchema).forEach((featureSetting) => { + if (featureSetting) { + const menu = _isAdminMode.value ? featureSetting.adminMenu : featureSetting.menu; + if (menu && !menuList.some((existingMenu) => existingMenu.id === menu.id)) { + menuList.push(menu); + } + } + }); + + if (_isAdminMode.value) { + menuList.push(...DEFAULT_ADMIN_MENU_LIST); + } + + return menuList; + }); + + + const getters = reactive({ + menuList: computed(() => { + const refinedMenuList = _baseMenuList.value + .map((menu) => _applyCustomizations(menu as DisplayMenu, _menuContext.value)); + + const orderedMenus = refinedMenuList.filter((menu) => menu.order !== undefined); + const unorderedMenus = refinedMenuList.filter((menu) => menu.order === undefined); + + return [...orderBy(orderedMenus, ['order'], ['asc']), ...unorderedMenus]; + }), + generateFlattenedMenuMap: computed(() => { + const map: FlattenedMenuMap = {}; + + getters.menuList.forEach((menu) => { + _getSubMenuIdsToMap(menu, map); + }); + + return map; + }), + }); + + return { + getters, + }; +}); + +/* menu list utils */ +const _applyCustomizations = (menu: DisplayMenu, context: MenuContext): DisplayMenu => { + const transformedSubMenus = (menu.subMenuList ?? []) + .map((sub) => _customizeSubMenu(sub, context)) + .filter((sub): sub is DisplayMenu => sub !== null); + + return { + ...menu, + subMenuList: transformedSubMenus, + }; +}; + +const _customizeSubMenu = (subMenu: DisplayMenu, context: MenuContext): DisplayMenu | null => { + const customizer = MENU_CUSTOMIZATIONS.get(subMenu.id); + if (!customizer) return subMenu; + + if (customizer.shouldShow && !customizer.shouldShow(context)) return null; + + return { + ...subMenu, + ...customizer.applyLabel(subMenu, context), + }; +}; + +/* menu flattened map utils */ +const _getSubMenuIdsToMap = (menu: Menu, flattenedMenuMap: FlattenedMenuMap) => { + let results: MenuId[] = []; + const subMenuList = menu.subMenuList; + if (subMenuList) { + results = subMenuList.map((d) => d.id); + subMenuList.forEach((subMenu) => { + _getSubMenuIdsToMap(subMenu, flattenedMenuMap); + }); + } + flattenedMenuMap[menu.id] = results; +}; diff --git a/apps/web/src/store/display/use-all-menu-list.ts b/apps/web/src/store/display/use-all-menu-list.ts new file mode 100644 index 0000000000..e000e7077b --- /dev/null +++ b/apps/web/src/store/display/use-all-menu-list.ts @@ -0,0 +1,104 @@ +import { computed } from 'vue'; +import type { RawLocation, Route } from 'vue-router'; +import type VueRouter from 'vue-router'; + +import { i18n } from '@/translations'; + +import { makeAdminRouteName } from '@/router/helpers/route-helper'; + +import type { Menu, MenuId, MenuInfo } from '@/lib/menu/config'; +import { MENU_ID } from '@/lib/menu/config'; +import { MENU_INFO_MAP } from '@/lib/menu/menu-info'; + +import { useAppContextStore } from '../app-context/app-context-store'; +import { useUserWorkspaceStore } from '../app-context/workspace/user-workspace-store'; +import { useAccessControlStore } from '../derived/access-control-store'; +import { useMenuStore } from '../derived/menu-store'; +import type { DisplayMenu } from './type'; + +export const useAllMenuList = () => { + const appContextStore = useAppContextStore(); + const menuStore = useMenuStore(); + const accessControlStore = useAccessControlStore(); + const userWorkspaceStore = useUserWorkspaceStore(); + + const _isAdminMode = computed(() => appContextStore.getters.isAdminMode); + const _currentWorkspaceId = computed(() => userWorkspaceStore.getters.currentWorkspaceId); + + + const getAllMenuList = (route: Route, router: VueRouter): DisplayMenu[] => { + const isMyPage = route?.path.startsWith('/my-page'); + let _allGnbMenuList: DisplayMenu[]; + + _allGnbMenuList = _getDisplayMenuList(menuStore.getters.menuList, _isAdminMode.value, _currentWorkspaceId.value); + _allGnbMenuList = _filterMenuByRoute(_allGnbMenuList, router, _currentWorkspaceId.value); + if (!_isAdminMode.value) { + _allGnbMenuList = _filterMenuByAccessPermission(_allGnbMenuList, accessControlStore.getters.pageAccessPermissionList); + } + + if (!isMyPage) { + _allGnbMenuList.forEach((menu) => { + if (menu.id === MENU_ID.MY_PAGE) { + menu.hideOnGNB = true; + } + }); + } + + return _allGnbMenuList; + }; + + return { + getAllMenuList, + }; +}; + +const _filterMenuByRoute = (menuList: DisplayMenu[], router: VueRouter, targetWorkspaceId?: string): DisplayMenu[] => menuList.reduce((results, _menu) => { + const menu = { ..._menu }; + if (menu.subMenuList) { + menu.subMenuList = _filterMenuByRoute(menu.subMenuList, router, targetWorkspaceId); + if (menu.subMenuList.length) { + results.push(menu); + return results; + } + } + + const to: RawLocation = { + name: menu.to.name, + params: targetWorkspaceId ? { workspaceId: targetWorkspaceId } : {}, + }; + const link = router.resolve(to); + if (link?.href !== '/') results.push(menu); + + return results; +}, [] as DisplayMenu[]); +const _filterMenuByAccessPermission = (menuList: DisplayMenu[], pagePermissionList: MenuId[]): DisplayMenu[] => menuList.reduce((results, _menu) => { + const menu = { ..._menu }; + + if (menu.subMenuList) { + menu.subMenuList = _filterMenuByAccessPermission(menu.subMenuList, pagePermissionList); + } + + if (menu.subMenuList?.length) results.push(menu); + else { + const hasPermission = pagePermissionList.some((menuId) => menuId === menu.id); + if (hasPermission) results.push(menu); + } + + return results; +}, [] as DisplayMenu[]); + +const _getDisplayMenuList = (menuList: Menu[], isAdminMode?: boolean, currentWorkspaceId?: string): DisplayMenu[] => menuList.map((d) => { + const menuInfo: MenuInfo = MENU_INFO_MAP[d.id]; + const routeName = isAdminMode ? makeAdminRouteName(MENU_INFO_MAP[d.id].routeName) : MENU_INFO_MAP[d.id].routeName; + const label = d.label || i18n.t(menuInfo.translationId); + + return { + ...d, + id: d.id, + label, + icon: menuInfo.icon, + highlightTag: menuInfo.highlightTag, + to: { name: routeName, params: { workspaceId: currentWorkspaceId } }, + subMenuList: d.subMenuList ? _getDisplayMenuList(d.subMenuList, isAdminMode, currentWorkspaceId) : [], + } as DisplayMenu; +}); From 691c0363a4528f3e4a24a18879dd2de32309519b Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:30:27 +0900 Subject: [PATCH 05/51] refactor(user-store): remove authorization concern from user-store Signed-off-by: samuel.park --- apps/web/src/store/user/constant.ts | 10 - apps/web/src/store/user/type.ts | 37 ---- apps/web/src/store/user/user-store.ts | 304 ++------------------------ 3 files changed, 20 insertions(+), 331 deletions(-) diff --git a/apps/web/src/store/user/constant.ts b/apps/web/src/store/user/constant.ts index 57a9a28b93..bcf4400e9d 100644 --- a/apps/web/src/store/user/constant.ts +++ b/apps/web/src/store/user/constant.ts @@ -1,8 +1,5 @@ import { timeZonesNames } from '@vvo/tzdb'; -import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; - - export const USER_STORAGE_KEY = 'store/user'; @@ -13,10 +10,3 @@ export const languages = { ko: '한국어', ja: '日本語', }; - -export const MANAGED_ROLES = { - 'managed-system-admin': ROLE_TYPE.SYSTEM_ADMIN, - 'managed-domain-admin': ROLE_TYPE.DOMAIN_ADMIN, - 'managed-workspace-owner': ROLE_TYPE.WORKSPACE_OWNER, - 'managed-workspace-member': ROLE_TYPE.WORKSPACE_MEMBER, -} as const; diff --git a/apps/web/src/store/user/type.ts b/apps/web/src/store/user/type.ts index 1bf0e3b444..da5fb52954 100644 --- a/apps/web/src/store/user/type.ts +++ b/apps/web/src/store/user/type.ts @@ -1,30 +1,13 @@ import type { ComputedRef } from 'vue'; import type { RoleType } from '@/api-clients/identity/role/type'; -import type { GrantScope } from '@/api-clients/identity/token/schema/type'; import type { UserMfa } from '@/api-clients/identity/user/schema/model'; import type { AuthType, UserType } from '@/api-clients/identity/user/schema/type'; -import type { PageAccessMap } from '@/lib/access-control/config'; -import type { MenuId } from '@/lib/menu/config'; - - export type LanguageCode = 'ko' | 'en' | string; // export type Timezone = 'UTC' | 'Asia/Seoul' | string; -export interface RoleInfo { - roleType: RoleType; - roleId: string; - pageAccess: string[]; -} - -export interface GrantInfo { - scope: GrantScope; - workspaceId?: string; - pageAccess?: string[]; -} - export interface UserStoreState { isSessionExpired?: boolean; userId?: string; @@ -35,8 +18,6 @@ export interface UserStoreState { email?: string; language?: string; timezone?: string; - currentGrantInfo?: GrantInfo; - currentRoleInfo?: RoleInfo; requiredActions?: string[]; emailVerified?: boolean; isSignInLoading?: boolean; @@ -48,23 +29,5 @@ export interface UserStoreGetters { isSystemAdmin: ComputedRef; domainId: ComputedRef; languageLabel: ComputedRef; - isNoRoleUser: ComputedRef; hasAdminOrWorkspaceOwnerRole: ComputedRef; - hasPermission: ComputedRef; - pageAccessPermissionList: ComputedRef; - pageAccessPermissionMap: ComputedRef; } - -export interface SignInRequest { - credentials: Record; - authType: AuthType | 'SAML'; - timeout?: number; - refresh_count?: number; - verify_code?: string; - domainId: string; -} - -export interface JWTPayload { - rol: RoleType; -} - diff --git a/apps/web/src/store/user/user-store.ts b/apps/web/src/store/user/user-store.ts index 4a2687257c..60a491c848 100644 --- a/apps/web/src/store/user/user-store.ts +++ b/apps/web/src/store/user/user-store.ts @@ -1,122 +1,24 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck import { computed, reactive, watch } from 'vue'; -import { jwtDecode } from 'jwt-decode'; -import { isEmpty } from 'lodash'; import { defineStore } from 'pinia'; import { LocalStorageAccessor } from '@cloudforet/core-lib/local-storage-accessor'; import { SpaceConnector } from '@cloudforet/core-lib/space-connector'; -import { getCancellableFetcher } from '@cloudforet/core-lib/space-connector/cancellable-fetcher'; -import { isInstanceOfAPIError } from '@cloudforet/core-lib/space-connector/error'; import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; -import type { RoleGetParameters } from '@/api-clients/identity/role/schema/api-verbs/get'; -import type { RoleModel } from '@/api-clients/identity/role/schema/model'; -import type { RoleType } from '@/api-clients/identity/role/type'; -import type { TokenGrantParameters } from '@/api-clients/identity/token/schema/api-verbs/grant'; -import type { TokenIssueParameters } from '@/api-clients/identity/token/schema/api-verbs/issue'; -import type { - TokenGrantModel, - TokenIssueModel, -} from '@/api-clients/identity/token/schema/model'; import type { UserProfileUpdateParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/update'; import type { UserModel, UserMfa } from '@/api-clients/identity/user/schema/model'; import { setI18nLocale } from '@/translations'; -import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; import { useDomainStore } from '@/store/domain/domain-store'; -import { useErrorStore } from '@/store/error/error-store'; -import { useGlobalConfigSchemaStore } from '@/store/global-config-schema/global-config-schema-store'; -import { languages, MANAGED_ROLES, USER_STORAGE_KEY } from '@/store/user/constant'; +import { languages, USER_STORAGE_KEY } from '@/store/user/constant'; import type { - RoleInfo, - SignInRequest, - JWTPayload, UserStoreGetters, UserStoreState, } from '@/store/user/type'; -import type { PageAccessMap } from '@/lib/access-control/config'; -import { - checkAllMenuReadonly, - getDefaultPageAccessPermissionList, - getMinimalPageAccessPermissionList, - getPageAccessMapFromRawData, -} from '@/lib/access-control/page-access-helper'; -import type { Menu, MenuId } from '@/lib/menu/config'; -import { setCurrentAccessedWorkspaceId } from '@/lib/site-initializer/last-accessed-workspace'; - -const getUserInfo = async (): Promise> => { - const response = await SpaceConnector.clientV2.identity.userProfile.get< - undefined, - UserModel - >(); - return { - userId: response.user_id, - roleType: response.role_type, - authType: response.auth_type, - name: response.name, - email: response.email, - language: response.language, - timezone: response.timezone, - requiredActions: response.required_actions, - emailVerified: !!response.email_verified, - mfa: response.mfa, - }; -}; - -const getGrantedRole = async (roleId: string, currentRoleType: RoleType, baseRoleType?: RoleType): Promise => { - // DOMAIN_ADMIN -> enter workspace case - if (baseRoleType === ROLE_TYPE.DOMAIN_ADMIN && currentRoleType === ROLE_TYPE.WORKSPACE_OWNER) { - return { - roleType: ROLE_TYPE.WORKSPACE_OWNER, - roleId: 'managed-workspace-owner', - pageAccess: ['*'], - }; - } - // USER -> grant USER case - if (currentRoleType === ROLE_TYPE.USER) { - return undefined; - } - - // MANAGED_ROLE case - const MANAGED_ROLE_IDS: string[] = Object.keys(MANAGED_ROLES); - const isManagedRole = MANAGED_ROLE_IDS.includes(roleId); - if (isManagedRole) { - return { - roleType: MANAGED_ROLES[roleId], - roleId, - pageAccess: ['*'], - }; - } - - // normal case - try { - const response = await SpaceConnector.clientV2.identity.role.get({ - role_id: roleId, - }); - - return { - roleType: response.role_type, - roleId: response.role_id, - pageAccess: response.page_access, - }; - } catch (e) { - console.error(`Role Load Error: ${e}`); - return undefined; - } -}; -const getRoleTypeFromToken = (token: string): RoleType => { - const decodedToken = jwtDecode(token); - return decodedToken.rol; -}; - export const useUserStore = defineStore('user-store', () => { const domainStore = useDomainStore(); - const globalConfigSchemaStore = useGlobalConfigSchemaStore(); let storedUserState: Partial = {}; try { @@ -135,81 +37,23 @@ export const useUserStore = defineStore('user-store', () => { email: storedUserState.email, language: (storedUserState.language && languages[storedUserState.language]) ? storedUserState.language : 'en', timezone: storedUserState.timezone, - currentGrantInfo: undefined, // "currentGrantInfo" is for optimization of grant API call - currentRoleInfo: storedUserState.currentRoleInfo, requiredActions: storedUserState.requiredActions, emailVerified: storedUserState.emailVerified, isSignInLoading: false, mfa: storedUserState.mfa, }); - const _getters = reactive({ - menuList: computed(() => globalConfigSchemaStore.getters.menuList), - }); const getters = reactive({ isDomainAdmin: computed(() => state.roleType === ROLE_TYPE.DOMAIN_ADMIN), isSystemAdmin: computed(() => state.roleType === ROLE_TYPE.SYSTEM_ADMIN), domainId: computed(() => domainStore.state.domainId), languageLabel: computed(() => languages[state.language as string] || state.language), - isNoRoleUser: computed(() => !state.currentRoleInfo), hasAdminOrWorkspaceOwnerRole: computed(() => state.roleType === 'DOMAIN_ADMIN' || state.roleType === 'WORKSPACE_OWNER'), - hasPermission: computed(() => !!state.currentRoleInfo), - pageAccessPermissionList: computed(() => { - const roleType = state.currentRoleInfo?.roleType ?? 'USER'; - const roleBasePagePermissions = state.currentRoleInfo?.pageAccess ?? ['my_page.*']; - const pagePermissionMap = getPageAccessMapFromRawData({ - pageAccessPermissions: roleBasePagePermissions, - menuList: _getters.menuList, - }); - const minimalPagePermissionList = getMinimalPageAccessPermissionList(roleType); - const defaultPagePermissionList = getDefaultPageAccessPermissionList(roleType); - - Object.keys(pagePermissionMap).forEach((menuId) => { - if (minimalPagePermissionList.includes(menuId as MenuId)) pagePermissionMap[menuId] = { read: true, write: true, access: true }; - }); - let result = [...minimalPagePermissionList]; - Object.keys(pagePermissionMap).forEach((menuId) => { - const _menuId = menuId as MenuId; - if (defaultPagePermissionList.includes(_menuId) && !minimalPagePermissionList.includes(_menuId) && pagePermissionMap[_menuId].access) result = [...result, _menuId]; - }); - - return result; - }), - pageAccessPermissionMap: computed(() => { - const result: PageAccessMap = {}; - - const roleType = state.currentRoleInfo?.roleType ?? 'USER'; - const roleBasePagePermissions = state.currentRoleInfo?.pageAccess ?? ['my_page.*']; - const pagePermissionMap = getPageAccessMapFromRawData({ - pageAccessPermissions: roleBasePagePermissions, - menuList: _getters.menuList, - }); - const minimalPagePermissionList = getMinimalPageAccessPermissionList(roleType); - - const isAllReadOnly = checkAllMenuReadonly(roleBasePagePermissions); - - getters.pageAccessPermissionList?.forEach((menuId) => { - if (!result[menuId]) { - if (roleType === ROLE_TYPE.DOMAIN_ADMIN) { - result[menuId] = { - write: !isAllReadOnly, - }; - } else { - result[menuId] = { - write: minimalPagePermissionList.includes(menuId) ? true : pagePermissionMap[menuId]?.write, - }; - } - } - }); - return result; - }), }); /* Mutations */ const setIsSessionExpired = (val: boolean) => { state.isSessionExpired = val; }; const setLanguage = (val: string) => { state.language = val; }; const setTimezone = (val: string) => { state.timezone = val; }; - const setCurrentGrantInfo = (val: any) => { state.currentGrantInfo = val; }; - const setCurrentRoleInfo = (val: any) => { state.currentRoleInfo = val; }; const setIsSignInLoading = (val: boolean) => { state.isSignInLoading = val; }; const setEmailVerified = (val: boolean) => { state.emailVerified = val; }; const setMfa = (val: UserMfa) => { state.mfa = val; }; @@ -219,8 +63,6 @@ export const useUserStore = defineStore('user-store', () => { setIsSessionExpired, setLanguage, setTimezone, - setCurrentGrantInfo, - setCurrentRoleInfo, setIsSignInLoading, setEmailVerified, setMfa, @@ -240,133 +82,29 @@ export const useUserStore = defineStore('user-store', () => { state.requiredActions = userInfo.requiredActions; state.emailVerified = userInfo.emailVerified; state.mfa = userInfo.mfa; - state.currentRoleInfo = userInfo.currentRoleInfo; if (userInfo.language) { await setI18nLocale(userInfo.language); } }; - const signIn = async (signInRequest: SignInRequest): Promise => { - const domainId = signInRequest.domainId; - let response; - - if (signInRequest.authType === 'SAML') { - response = await SpaceConnector.clientV2.identity.token.grant({ - grant_type: 'REFRESH_TOKEN', - scope: 'USER', - token: signInRequest.credentials.refreshToken, - }, { skipAuthRefresh: true }); - SpaceConnector.setToken(response.access_token, signInRequest.credentials.refreshToken); - } else { - response = await SpaceConnector.clientV2.identity.token.issue({ - domain_id: domainId, - auth_type: signInRequest.authType, - credentials: signInRequest.credentials, - verify_code: signInRequest.verify_code, - }, { skipAuthRefresh: true }); - SpaceConnector.setToken(response.access_token, response.refresh_token); - } - - if (isEmpty(response)) { - throw new Error(); - } - - const userInfo = await getUserInfo(); - await setUserInfo(userInfo); - state.isSessionExpired = false; - }; - const signOut = () => { - SpaceConnector.flushToken(); - state.currentGrantInfo = undefined; - state.currentRoleInfo = undefined; - }; - /* - * @returns { Promise } isGranted - */ - const grantRole = async (grantRequest: Omit): Promise => { - const appContextStore = useAppContextStore(); - const userWorkspaceStore = useUserWorkspaceStore(); - const errorStore = useErrorStore(); - const fetcher = getCancellableFetcher(SpaceConnector.clientV2.identity.token.grant); - let isGranted = false; - - try { - // This is for not-triggered CASE, such as when user use browser back button. - appContextStore.setGlobalGrantLoading(true); - const { status, response } = await fetcher({ - grant_type: 'REFRESH_TOKEN', - scope: grantRequest.scope, - token: grantRequest.token, - workspace_id: grantRequest.workspace_id, - }, { skipAuthRefresh: true }); - if (status === 'succeed') { - SpaceConnector.setToken(response.access_token); - const currentRoleType = getRoleTypeFromToken(response.access_token); - - const rootRoleInfo = await getGrantedRole(response.role_id, currentRoleType); - state.currentGrantInfo = { - scope: grantRequest.scope, - workspaceId: grantRequest.workspace_id, - pageAccess: rootRoleInfo?.pageAccess || undefined, - }; - - const currentRoleInfo = await getGrantedRole(response.role_id, currentRoleType, response.role_type); - state.currentRoleInfo = currentRoleInfo; - - if (grantRequest.scope === 'DOMAIN') { - appContextStore.enterAdminMode(); - } else appContextStore.exitAdminMode(); - if (grantRequest.scope === 'WORKSPACE' && grantRequest.workspace_id) { - userWorkspaceStore.setCurrentWorkspace(grantRequest.workspace_id); - await setCurrentAccessedWorkspaceId(grantRequest.workspace_id); - } - if (grantRequest.scope === 'USER') userWorkspaceStore.setCurrentWorkspace(); - - if (currentRoleInfo) { - isGranted = true; - } - errorStore.setGrantAccessFailStatus(false); - } - } catch (error) { - console.error(error); - /* - * Unlike other cases where the ErrorHandler is used for error handling, - * in the grant logic scenario, there can be instances where the Router - * has not been initialized yet. Using the ErrorHandler in such situations - * can lead to issues since it internally relies on the router. - * Therefore, in this specific case, errors are simply logged to the console - * and not further processed, to avoid complications with uninitialized Router instances. - * */ - state.currentGrantInfo = undefined; - state.currentRoleInfo = undefined; - userWorkspaceStore.setCurrentWorkspace(); - appContextStore.exitAdminMode(); - - if (isInstanceOfAPIError(error)) { - if (error.code === 'ERROR_WORKSPACE_STATE') { - errorStore.setGrantAccessFailStatus(true); - } else if (error.code === 'ERROR_NOT_FOUND') { - errorStore.setGrantAccessFailStatus(true); - } else if (error.code === 'ERROR_PERMISSION_DENIED') { - errorStore.setGrantAccessFailStatus(true); - } else if (error.code === 'ERROR_AUTHENTICATE_FAILURE') { - SpaceConnector.flushToken(); - } else { - SpaceConnector.flushToken(); - } - } else { - SpaceConnector.flushToken(); - } - } finally { - /* - * Implemented a global loading with a minimum duration of 500 milliseconds - * during the grant process to prevent rendering of services until the process is complete. - * */ - setTimeout(() => { - appContextStore.setGlobalGrantLoading(false); - }, 500); - } - return isGranted; + const getUserInfo = async (): Promise => { + const response = await SpaceConnector.clientV2.identity.userProfile.get< + undefined, + UserModel + >(); + setUserInfo({ + userId: response.user_id, + roleType: response.role_type, + authType: response.auth_type, + name: response.name, + email: response.email, + language: response.language, + timezone: response.timezone, + requiredActions: response.required_actions, + emailVerified: !!response.email_verified, + mfa: response.mfa, + isSessionExpired: false, + }); }; const updateUser = async (userRequest: UserProfileUpdateParameters): Promise => { @@ -392,9 +130,7 @@ export const useUserStore = defineStore('user-store', () => { const actions = { setUserInfo, - signIn, - signOut, - grantRole, + getUserInfo, updateUser, }; From db213eb0a3f31b687727aae3612f03e32cf24d83 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:31:29 +0900 Subject: [PATCH 06/51] feat(authorization): create authorization store Signed-off-by: samuel.park --- .../authorization/authorization-store.ts | 292 ++++++++++++++++++ apps/web/src/store/authorization/constant.ts | 8 + apps/web/src/store/authorization/type.ts | 29 ++ 3 files changed, 329 insertions(+) create mode 100644 apps/web/src/store/authorization/authorization-store.ts create mode 100644 apps/web/src/store/authorization/constant.ts create mode 100644 apps/web/src/store/authorization/type.ts diff --git a/apps/web/src/store/authorization/authorization-store.ts b/apps/web/src/store/authorization/authorization-store.ts new file mode 100644 index 0000000000..66f9811dfb --- /dev/null +++ b/apps/web/src/store/authorization/authorization-store.ts @@ -0,0 +1,292 @@ +import { computed, reactive } from 'vue'; + +import { jwtDecode } from 'jwt-decode'; +import { isEmpty } from 'lodash'; +import { defineStore } from 'pinia'; + +import { SpaceConnector } from '@cloudforet/core-lib/space-connector'; +import { getCancellableFetcher } from '@cloudforet/core-lib/space-connector/cancellable-fetcher'; +import { isInstanceOfAPIError } from '@cloudforet/core-lib/space-connector/error'; + +import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; +import type { RoleGetParameters } from '@/api-clients/identity/role/schema/api-verbs/get'; +import type { RoleModel } from '@/api-clients/identity/role/schema/model'; +import type { RoleType } from '@/api-clients/identity/role/type'; +import type { TokenGrantParameters } from '@/api-clients/identity/token/schema/api-verbs/grant'; +import type { TokenIssueParameters } from '@/api-clients/identity/token/schema/api-verbs/issue'; +import type { TokenGrantModel, TokenIssueModel } from '@/api-clients/identity/token/schema/model'; + +import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; +import { MANAGED_ROLES } from '@/store/authorization/constant'; +import type { + RoleInfo, JWTPayload, GrantInfo, SignInRequest, +} from '@/store/authorization/type'; +import { useErrorStore } from '@/store/error/error-store'; +import { useMenuStore } from '@/store/menu/menu-store'; +import { pinia } from '@/store/pinia'; + +import type { PageAccessMap } from '@/lib/access-control/config'; +import { + checkAllMenuReadonly, + getDefaultPageAccessPermissionList, + getMinimalPageAccessPermissionList, + getPageAccessMapFromRawData, +} from '@/lib/access-control/page-access-helper'; +import type { MenuId } from '@/lib/menu/config'; +import { setCurrentAccessedWorkspaceId } from '@/lib/site-initializer/last-accessed-workspace'; + + +interface AuthorizationStoreState { + currentRoleInfo?: RoleInfo; + currentGrantInfo?: GrantInfo; +} + +export const useAuthorizationStore = defineStore('authorization', () => { + const appContextStore = useAppContextStore(pinia); + const userWorkspaceStore = useUserWorkspaceStore(pinia); + const errorStore = useErrorStore(pinia); + const menuStore = useMenuStore(pinia); + + const state = reactive({ + currentRoleInfo: undefined, + currentGrantInfo: undefined, + }); + + const getters = reactive({ + isNoRoleUser: computed(() => !state.currentRoleInfo), + hasPermission: computed(() => !!state.currentRoleInfo), + pageAccessPermissionList: computed(() => { + const roleType = state.currentRoleInfo?.roleType ?? 'USER'; + const roleBasePagePermissions = state.currentRoleInfo?.pageAccess ?? ['my_page.*']; + const pagePermissionMap = getPageAccessMapFromRawData({ + pageAccessPermissions: roleBasePagePermissions, + menuList: menuStore.getters.menuList, + }); + const minimalPagePermissionList = getMinimalPageAccessPermissionList(roleType); + const defaultPagePermissionList = getDefaultPageAccessPermissionList(roleType); + + Object.keys(pagePermissionMap).forEach((menuId) => { + if (minimalPagePermissionList.includes(menuId as MenuId)) pagePermissionMap[menuId] = { read: true, write: true, access: true }; + }); + let result = [...minimalPagePermissionList]; + Object.keys(pagePermissionMap).forEach((menuId) => { + const _menuId = menuId as MenuId; + if (defaultPagePermissionList.includes(_menuId) && !minimalPagePermissionList.includes(_menuId) && pagePermissionMap[_menuId].access) result = [...result, _menuId]; + }); + + return result; + }), + pageAccessPermissionMap: computed(() => { + const result: PageAccessMap = {}; + + const roleType = state.currentRoleInfo?.roleType ?? 'USER'; + const roleBasePagePermissions = state.currentRoleInfo?.pageAccess ?? ['my_page.*']; + const pagePermissionMap = getPageAccessMapFromRawData({ + pageAccessPermissions: roleBasePagePermissions, + menuList: menuStore.getters.menuList, + }); + const minimalPagePermissionList = getMinimalPageAccessPermissionList(roleType); + + const isAllReadOnly = checkAllMenuReadonly(roleBasePagePermissions); + + getters.pageAccessPermissionList.forEach((menuId) => { + if (!result[menuId]) { + if (roleType === ROLE_TYPE.DOMAIN_ADMIN) { + result[menuId] = { + write: !isAllReadOnly, + }; + } else { + result[menuId] = { + write: minimalPagePermissionList.includes(menuId) ? true : pagePermissionMap[menuId]?.write, + }; + } + } + }); + return result; + }), + }); + + const mutations = { + setCurrentRoleInfo: (roleInfo?: RoleInfo) => { + state.currentRoleInfo = roleInfo; + }, + setCurrentGrantInfo: (grantInfo?: GrantInfo) => { + state.currentGrantInfo = grantInfo; + }, + }; + + const signIn = async (signInRequest: SignInRequest): Promise => { + const domainId = signInRequest.domainId; + let response; + + if (signInRequest.authType === 'SAML') { + response = await SpaceConnector.clientV2.identity.token.grant({ + grant_type: 'REFRESH_TOKEN', + scope: 'USER', + token: signInRequest.credentials.refreshToken, + }, { skipAuthRefresh: true }); + SpaceConnector.setToken(response.access_token, signInRequest.credentials.refreshToken); + } else { + response = await SpaceConnector.clientV2.identity.token.issue({ + domain_id: domainId, + auth_type: signInRequest.authType, + credentials: signInRequest.credentials, + verify_code: signInRequest.verify_code, + }, { skipAuthRefresh: true }); + SpaceConnector.setToken(response.access_token, response.refresh_token); + } + + if (isEmpty(response)) { + throw new Error(); + } + }; + const signOut = () => { + SpaceConnector.flushToken(); + state.currentGrantInfo = undefined; + state.currentRoleInfo = undefined; + }; + + const grantRole = async (grantRequest: Omit): Promise => { + const fetcher = getCancellableFetcher(SpaceConnector.clientV2.identity.token.grant); + let isGranted = false; + + try { + // This is for not-triggered CASE, such as when user use browser back button. + appContextStore.setGlobalGrantLoading(true); + const { status, response } = await fetcher({ + grant_type: 'REFRESH_TOKEN', + scope: grantRequest.scope, + token: grantRequest.token, + workspace_id: grantRequest.workspace_id, + }, { skipAuthRefresh: true }); + if (status === 'succeed') { + SpaceConnector.setToken(response.access_token); + const currentRoleType = _getRoleTypeFromToken(response.access_token); + + const rootRoleInfo = await _getGrantedRole(response.role_id, currentRoleType); + state.currentGrantInfo = { + scope: grantRequest.scope, + workspaceId: grantRequest.workspace_id, + pageAccess: rootRoleInfo?.pageAccess || undefined, + }; + + const currentRoleInfo = await _getGrantedRole(response.role_id, currentRoleType, response.role_type); + state.currentRoleInfo = currentRoleInfo; + + if (grantRequest.scope === 'DOMAIN') { + appContextStore.enterAdminMode(); + } else appContextStore.exitAdminMode(); + if (grantRequest.scope === 'WORKSPACE' && grantRequest.workspace_id) { + userWorkspaceStore.setCurrentWorkspace(grantRequest.workspace_id); + await setCurrentAccessedWorkspaceId(grantRequest.workspace_id); + } + if (grantRequest.scope === 'USER') userWorkspaceStore.setCurrentWorkspace(); + + if (currentRoleInfo) { + isGranted = true; + } + errorStore.setGrantAccessFailStatus(false); + } + } catch (error) { + console.error(error); + /* + * Unlike other cases where the ErrorHandler is used for error handling, + * in the grant logic scenario, there can be instances where the Router + * has not been initialized yet. Using the ErrorHandler in such situations + * can lead to issues since it internally relies on the router. + * Therefore, in this specific case, errors are simply logged to the console + * and not further processed, to avoid complications with uninitialized Router instances. + * */ + state.currentGrantInfo = undefined; + state.currentRoleInfo = undefined; + userWorkspaceStore.setCurrentWorkspace(); + appContextStore.exitAdminMode(); + + if (isInstanceOfAPIError(error)) { + if (error.code === 'ERROR_WORKSPACE_STATE') { + errorStore.setGrantAccessFailStatus(true); + } else if (error.code === 'ERROR_NOT_FOUND') { + errorStore.setGrantAccessFailStatus(true); + } else if (error.code === 'ERROR_PERMISSION_DENIED') { + errorStore.setGrantAccessFailStatus(true); + } else if (error.code === 'ERROR_AUTHENTICATE_FAILURE') { + SpaceConnector.flushToken(); + } else { + SpaceConnector.flushToken(); + } + } else { + SpaceConnector.flushToken(); + } + } finally { + /* + * Implemented a global loading with a minimum duration of 500 milliseconds + * during the grant process to prevent rendering of services until the process is complete. + * */ + setTimeout(() => { + appContextStore.setGlobalGrantLoading(false); + }, 500); + } + return isGranted; + }; + + const actions = { + grantRole, + signIn, + signOut, + }; + + return { + state, + getters, + ...mutations, + ...actions, + }; +}); + + +const _getGrantedRole = async (roleId: string, currentRoleType: RoleType, baseRoleType?: RoleType): Promise => { + // DOMAIN_ADMIN -> enter workspace case + if (baseRoleType === ROLE_TYPE.DOMAIN_ADMIN && currentRoleType === ROLE_TYPE.WORKSPACE_OWNER) { + return { + roleType: ROLE_TYPE.WORKSPACE_OWNER, + roleId: 'managed-workspace-owner', + pageAccess: ['*'], + }; + } + // USER -> grant USER case + if (currentRoleType === ROLE_TYPE.USER) { + return undefined; + } + + // MANAGED_ROLE case + const MANAGED_ROLE_IDS: string[] = Object.keys(MANAGED_ROLES); + const isManagedRole = MANAGED_ROLE_IDS.includes(roleId); + if (isManagedRole) { + return { + roleType: MANAGED_ROLES[roleId], + roleId, + pageAccess: ['*'], + }; + } + + // normal case + try { + const response = await SpaceConnector.clientV2.identity.role.get({ + role_id: roleId, + }); + + return { + roleType: response.role_type, + roleId: response.role_id, + pageAccess: response.page_access, + }; + } catch (e) { + console.error(`Role Load Error: ${e}`); + return undefined; + } +}; +const _getRoleTypeFromToken = (token: string): RoleType => { + const decodedToken = jwtDecode(token); + return decodedToken.rol; +}; diff --git a/apps/web/src/store/authorization/constant.ts b/apps/web/src/store/authorization/constant.ts new file mode 100644 index 0000000000..4d1ffa504a --- /dev/null +++ b/apps/web/src/store/authorization/constant.ts @@ -0,0 +1,8 @@ +import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; + +export const MANAGED_ROLES = { + 'managed-system-admin': ROLE_TYPE.SYSTEM_ADMIN, + 'managed-domain-admin': ROLE_TYPE.DOMAIN_ADMIN, + 'managed-workspace-owner': ROLE_TYPE.WORKSPACE_OWNER, + 'managed-workspace-member': ROLE_TYPE.WORKSPACE_MEMBER, +} as const; diff --git a/apps/web/src/store/authorization/type.ts b/apps/web/src/store/authorization/type.ts new file mode 100644 index 0000000000..39fb7a4a26 --- /dev/null +++ b/apps/web/src/store/authorization/type.ts @@ -0,0 +1,29 @@ +import type { RoleType } from '@/api-clients/identity/role/type'; +import type { GrantScope } from '@/api-clients/identity/token/schema/type'; +import type { AuthType } from '@/api-clients/identity/user/schema/type'; + +export interface RoleInfo { + roleType: RoleType; + roleId: string; + pageAccess: string[]; +} + +export interface GrantInfo { + scope: GrantScope; + workspaceId?: string; + pageAccess?: string[]; +} + +export interface SignInRequest { + credentials: Record; + authType: AuthType | 'SAML'; + timeout?: number; + refresh_count?: number; + verify_code?: string; + domainId: string; +} + +export interface JWTPayload { + rol: RoleType; +} + From fb9b628717afd8c594da52fdf636d6df86b2c7f8 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:32:10 +0900 Subject: [PATCH 07/51] refactor(display-store): remove menu concern from display-store Signed-off-by: samuel.park --- apps/web/src/store/display/display-store.ts | 94 +-------------------- apps/web/src/store/display/type.ts | 14 --- 2 files changed, 1 insertion(+), 107 deletions(-) diff --git a/apps/web/src/store/display/display-store.ts b/apps/web/src/store/display/display-store.ts index add60f3192..18435d0374 100644 --- a/apps/web/src/store/display/display-store.ts +++ b/apps/web/src/store/display/display-store.ts @@ -1,6 +1,4 @@ import { computed, reactive } from 'vue'; -import type { RawLocation, Route } from 'vue-router'; -import type VueRouter from 'vue-router'; import type { CancelTokenSource } from 'axios'; import axios from 'axios'; @@ -14,82 +12,20 @@ import { SpaceConnector } from '@cloudforet/core-lib/space-connector'; import { ApiQueryHelper } from '@cloudforet/core-lib/space-connector/helper'; import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; -import { SpaceRouter } from '@/router'; import type { NotificationListParameters } from '@/schema/notification/notification/api-verbs/list'; import type { NotificationModel } from '@/schema/notification/notification/model'; -import { i18n } from '@/translations'; -import { makeAdminRouteName } from '@/router/helpers/route-helper'; - -import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; import { SIDEBAR_TYPE } from '@/store/display/constant'; import type { - DisplayMenu, DisplayStoreState, SidebarProps, SidebarType, + DisplayStoreState, SidebarProps, SidebarType, DisplayStoreGetters, } from '@/store/display/type'; -import { useGlobalConfigSchemaStore } from '@/store/global-config-schema/global-config-schema-store'; import { useUserStore } from '@/store/user/user-store'; -import type { Menu, MenuId, MenuInfo } from '@/lib/menu/config'; -import { MENU_ID } from '@/lib/menu/config'; -import { MENU_INFO_MAP } from '@/lib/menu/menu-info'; import ErrorHandler from '@/common/composables/error/errorHandler'; const verbose = false; -const filterMenuByRoute = (menuList: DisplayMenu[], router: VueRouter): DisplayMenu[] => menuList.reduce((results, _menu) => { - const userWorkspaceStore = useUserWorkspaceStore(); - const targetWorkspaceId = userWorkspaceStore.getters.currentWorkspaceId; - const menu = { ..._menu }; - if (menu.subMenuList) { - menu.subMenuList = filterMenuByRoute(menu.subMenuList, router); - if (menu.subMenuList.length) { - results.push(menu); - return results; - } - } - - const to: RawLocation = { - name: menu.to.name, - params: targetWorkspaceId ? { workspaceId: targetWorkspaceId } : {}, - }; - const link = router.resolve(to); - if (link?.href !== '/') results.push(menu); - - return results; -}, [] as DisplayMenu[]); -const filterMenuByAccessPermission = (menuList: DisplayMenu[], pagePermissionList: MenuId[]): DisplayMenu[] => menuList.reduce((results, _menu) => { - const menu = { ..._menu }; - - if (menu.subMenuList) { - menu.subMenuList = filterMenuByAccessPermission(menu.subMenuList, pagePermissionList); - } - - if (menu.subMenuList?.length) results.push(menu); - else { - const hasPermission = pagePermissionList.some((menuId) => menuId === menu.id); - if (hasPermission) results.push(menu); - } - - return results; -}, [] as DisplayMenu[]); - -const getDisplayMenuList = (menuList: Menu[], isAdminMode?: boolean, currentWorkspaceId?: string): DisplayMenu[] => menuList.map((d) => { - const menuInfo: MenuInfo = MENU_INFO_MAP[d.id]; - const routeName = isAdminMode ? makeAdminRouteName(MENU_INFO_MAP[d.id].routeName) : MENU_INFO_MAP[d.id].routeName; - const label = d.label || i18n.t(menuInfo.translationId); - - return { - ...d, - id: d.id, - label, - icon: menuInfo.icon, - highlightTag: menuInfo.highlightTag, - to: { name: routeName, params: { workspaceId: currentWorkspaceId } }, - subMenuList: d.subMenuList ? getDisplayMenuList(d.subMenuList, isAdminMode, currentWorkspaceId) : [], - } as DisplayMenu; -}); export const useDisplayStore = defineStore('display-store', () => { const userStore = useUserStore(); @@ -303,33 +239,6 @@ export const useDisplayStore = defineStore('display-store', () => { } }; - const getAllMenuList = (route?: Route): DisplayMenu[] => { - const isMyPage = route?.path.startsWith('/my-page'); - const appContextStore = useAppContextStore(); - const globalConfigSchemaStore = useGlobalConfigSchemaStore(); - const appContextState = appContextStore.$state; - const userWorkspaceStore = useUserWorkspaceStore(); - const isAdminMode = appContextState.getters.isAdminMode; - const currentWorkspaceId = userWorkspaceStore.getters.currentWorkspaceId; - const menuList = globalConfigSchemaStore.getters.menuList; - let _allGnbMenuList: DisplayMenu[]; - - _allGnbMenuList = getDisplayMenuList(menuList, isAdminMode, currentWorkspaceId); - _allGnbMenuList = filterMenuByRoute(_allGnbMenuList, SpaceRouter.router); - if (!isAdminMode) { - _allGnbMenuList = filterMenuByAccessPermission(_allGnbMenuList, userStore.getters.pageAccessPermissionList); - } - - if (!isMyPage) { - _allGnbMenuList.forEach((menu) => { - if (menu.id === MENU_ID.MY_PAGE) { - menu.hideOnGNB = true; - } - }); - } - - return _allGnbMenuList; - }; const actions = { showHandbook, @@ -339,7 +248,6 @@ export const useDisplayStore = defineStore('display-store', () => { stopCheckNotification, startCheckNotification, updateGnbNotificationLastReadTime, - getAllMenuList, }; return { diff --git a/apps/web/src/store/display/type.ts b/apps/web/src/store/display/type.ts index ac7722e93f..d238b125ec 100644 --- a/apps/web/src/store/display/type.ts +++ b/apps/web/src/store/display/type.ts @@ -1,26 +1,12 @@ import type { ComputedRef } from 'vue'; -import type { TranslateResult } from 'vue-i18n'; -import type { Location } from 'vue-router'; import type { SIDEBAR_TYPE, CURRENCY, CURRENCY_SYMBOL } from '@/store/display/constant'; -import type { Menu } from '@/lib/menu/config'; - export type SidebarType = typeof SIDEBAR_TYPE[keyof typeof SIDEBAR_TYPE]; -export type HighlightTagType = 'new' | 'beta' | 'update'; export type Currency = typeof CURRENCY[keyof typeof CURRENCY]; export type CurrencySymbol = typeof CURRENCY_SYMBOL[keyof typeof CURRENCY_SYMBOL]; -export interface DisplayMenu extends Menu { - show?: boolean; - label: TranslateResult; - icon?: string; - highlightTag?: HighlightTagType; - to: Location; - subMenuList?: DisplayMenu[]; - href?: string; -} export interface DisplayStoreState { visibleSidebar: boolean; From 6e56c63c7e1fa86008812240089c0b96665edf66 Mon Sep 17 00:00:00 2001 From: nayeongkim Date: Wed, 23 Apr 2025 16:32:21 +0900 Subject: [PATCH 08/51] feat: apply resource link parameter Signed-off-by: NaYeong,Kim --- .../v2/components/AlertDetailInfoTable.vue | 66 +++++++++++++++---- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/apps/web/src/services/alert-manager/v2/components/AlertDetailInfoTable.vue b/apps/web/src/services/alert-manager/v2/components/AlertDetailInfoTable.vue index 7bf1cbce01..fb675195b8 100644 --- a/apps/web/src/services/alert-manager/v2/components/AlertDetailInfoTable.vue +++ b/apps/web/src/services/alert-manager/v2/components/AlertDetailInfoTable.vue @@ -1,7 +1,9 @@ @@ -162,7 +200,7 @@ const handleRouteViewButton = async (id: string) => { {{ $t('ALERT_MANAGER.ALERTS.VIEW_RESOURCE') }} From b450113edccf2aa7936efe66fc10c2b580180b7e Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:32:45 +0900 Subject: [PATCH 09/51] refactor(global-config-schema-store): remove menu concern from gcs-store Signed-off-by: samuel.park --- .../global-config-schema-store.ts | 122 +----------------- 1 file changed, 1 insertion(+), 121 deletions(-) diff --git a/apps/web/src/store/global-config-schema/global-config-schema-store.ts b/apps/web/src/store/global-config-schema/global-config-schema-store.ts index 5b81e6dd7d..015e566f1c 100644 --- a/apps/web/src/store/global-config-schema/global-config-schema-store.ts +++ b/apps/web/src/store/global-config-schema/global-config-schema-store.ts @@ -1,22 +1,12 @@ -import { computed, reactive } from 'vue'; +import { reactive } from 'vue'; -import { orderBy } from 'lodash'; import { defineStore } from 'pinia'; -import { useAppContextStore } from '@/store/app-context/app-context-store'; -import type { DisplayMenu } from '@/store/display/type'; import type { GeneratedRouteSchema, GeneratedMenuSchema, GeneratedRouteMetadataSchema, GeneratedUiAffectSchema, } from '@/lib/config/global-config/types/type'; -import type { Menu, MenuId } from '@/lib/menu/config'; -import { MENU_ID } from '@/lib/menu/config'; -import { DEFAULT_MENU_LIST, DEFAULT_ADMIN_MENU_LIST } from '@/lib/menu/menu-architecture'; - -import { useTaskManagementTemplateStore } from '@/services/ops-flow/task-management-templates/stores/use-task-management-template-store'; - -export type FlattenedMenuMap = Partial>; interface GlobalConfigSchemaStoreState { uiAffectsSchema: GeneratedUiAffectSchema; @@ -25,32 +15,7 @@ interface GlobalConfigSchemaStoreState { routeSchema: GeneratedRouteSchema; } -interface MenuTransformer { - menuId: MenuId; - transform: (menu: DisplayMenu, getters: any) => Partial; - shouldInclude?: (getters: any) => boolean; -} - -const MENU_TRANSFORMERS_MAP = new Map([ - [MENU_ID.OPS_FLOW_LANDING, { - menuId: MENU_ID.OPS_FLOW_LANDING, - transform: (menu: DisplayMenu, getters: any) => ({ - label: getters.templateName, - }), - shouldInclude: (getters: any) => getters.templateId !== 'default' && getters.enableLanding, - }], - [MENU_ID.TASK_BOARD, { - menuId: MENU_ID.TASK_BOARD, - transform: (menu: DisplayMenu, getters: any) => ({ - label: getters.taskBoardName, - }), - }], -]); - export const useGlobalConfigSchemaStore = defineStore('global-config-schema-store', () => { - const appContextStore = useAppContextStore(); - const taskManagementTemplateStore = useTaskManagementTemplateStore(); - const state = reactive({ uiAffectsSchema: {} as GeneratedUiAffectSchema, menuSchema: {} as GeneratedMenuSchema, @@ -58,90 +23,6 @@ export const useGlobalConfigSchemaStore = defineStore('global-config-schema-stor routeSchema: {} as GeneratedRouteSchema, }); - const _getters = reactive({ - isAdminMode: computed(() => appContextStore.getters.isAdminMode), - templateName: computed(() => taskManagementTemplateStore.templates.TemplateName), - taskBoardName: computed(() => taskManagementTemplateStore.templates.TaskBoard), - templateId: computed(() => taskManagementTemplateStore.state.templateId), - enableLanding: computed(() => taskManagementTemplateStore.state.enableLanding), - }); - - const baseMenuList = computed(() => { - const menuList: Menu[] = _getters.isAdminMode ? [] : [...DEFAULT_MENU_LIST]; - - Object.values(state.menuSchema).forEach((featureSetting) => { - if (featureSetting) { - const menu = _getters.isAdminMode ? featureSetting.adminMenu : featureSetting.menu; - if (menu && !menuList.some((existingMenu) => existingMenu.id === menu.id)) { - menuList.push(menu); - } - } - }); - - if (_getters.isAdminMode) { - menuList.push(...DEFAULT_ADMIN_MENU_LIST); - } - - return menuList; - }); - - const transformSubMenu = (subMenu: DisplayMenu): DisplayMenu | null => { - const transformer = MENU_TRANSFORMERS_MAP.get(subMenu.id); - if (!transformer) return subMenu; - if (transformer.shouldInclude && !transformer.shouldInclude(_getters)) return null; - - return { - ...subMenu, - ...transformer.transform(subMenu, _getters), - }; - }; - - const transformMenu = (menu: DisplayMenu): DisplayMenu => { - if (!menu.subMenuList) return menu; - - const transformedSubMenus = menu.subMenuList - .map(transformSubMenu) - .filter((subMenu): subMenu is DisplayMenu => subMenu !== null); - - return { - ...menu, - subMenuList: transformedSubMenus, - }; - }; - - const getters = reactive({ - menuList: computed(() => { - const refinedMenuList = baseMenuList.value - .map((menu) => transformMenu(menu as DisplayMenu)); - - const orderedMenus = refinedMenuList.filter((menu) => menu.order !== undefined); - const unorderedMenus = refinedMenuList.filter((menu) => menu.order === undefined); - - return [...orderBy(orderedMenus, ['order'], ['asc']), ...unorderedMenus]; - }), - generateFlattenedMenuMap: computed(() => { - const map: FlattenedMenuMap = {}; - - const getSubMenuIdsToMap = (menu: Menu, flattenedMenuMap: FlattenedMenuMap) => { - let results: MenuId[] = []; - const subMenuList = menu.subMenuList; - if (subMenuList) { - results = subMenuList.map((d) => d.id); - subMenuList.forEach((subMenu) => { - getSubMenuIdsToMap(subMenu, flattenedMenuMap); - }); - } - flattenedMenuMap[menu.id] = results; - }; - - getters.menuList.forEach((menu) => { - getSubMenuIdsToMap(menu, map); - }); - - return map; - }), - }); - const actions = { setMenuSchema(menuSchema: GeneratedMenuSchema) { state.menuSchema = menuSchema; @@ -159,7 +40,6 @@ export const useGlobalConfigSchemaStore = defineStore('global-config-schema-stor return { state, - getters, ...actions, }; }); From d1baf58c2a0f1f2782c820c9de71e662c7c3a655 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:33:13 +0900 Subject: [PATCH 10/51] chore: refactor menu-store Signed-off-by: samuel.park --- apps/web/src/store/{derived => menu}/menu-store.ts | 13 +++++++------ apps/web/src/store/menu/type.ts | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) rename apps/web/src/store/{derived => menu}/menu-store.ts (94%) create mode 100644 apps/web/src/store/menu/type.ts diff --git a/apps/web/src/store/derived/menu-store.ts b/apps/web/src/store/menu/menu-store.ts similarity index 94% rename from apps/web/src/store/derived/menu-store.ts rename to apps/web/src/store/menu/menu-store.ts index 646872f5a6..28cd2d4be2 100644 --- a/apps/web/src/store/derived/menu-store.ts +++ b/apps/web/src/store/menu/menu-store.ts @@ -3,16 +3,17 @@ import { computed, reactive } from 'vue'; import { orderBy } from 'lodash'; import { defineStore } from 'pinia'; -import type { Menu, MenuId } from '@/lib/menu/config'; +import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useGlobalConfigSchemaStore } from '@/store/global-config-schema/global-config-schema-store'; +import type { DisplayMenu } from '@/store/menu/type'; +import { pinia } from '@/store/pinia'; + import { MENU_ID } from '@/lib/menu/config'; +import type { Menu, MenuId } from '@/lib/menu/config'; import { DEFAULT_ADMIN_MENU_LIST, DEFAULT_MENU_LIST } from '@/lib/menu/menu-architecture'; import { useTaskManagementTemplateStore } from '@/services/ops-flow/task-management-templates/stores/use-task-management-template-store'; -import { useAppContextStore } from '../app-context/app-context-store'; -import type { DisplayMenu } from '../display/type'; -import { useGlobalConfigSchemaStore } from '../global-config-schema/global-config-schema-store'; - export type FlattenedMenuMap = Partial>; interface MenuContext { @@ -41,11 +42,11 @@ const MENU_CUSTOMIZATIONS = new Map([ export const useMenuStore = defineStore('derived-menu', () => { const globalConfigSchemaStore = useGlobalConfigSchemaStore(); - const taskManagementTemplateStore = useTaskManagementTemplateStore(); const appContextStore = useAppContextStore(); const _isAdminMode = computed(() => appContextStore.getters.isAdminMode); + const taskManagementTemplateStore = useTaskManagementTemplateStore(pinia); const _menuContext = computed(() => ({ templateName: taskManagementTemplateStore.templates.TemplateName, taskBoardName: taskManagementTemplateStore.templates.TaskBoard, diff --git a/apps/web/src/store/menu/type.ts b/apps/web/src/store/menu/type.ts new file mode 100644 index 0000000000..668be40945 --- /dev/null +++ b/apps/web/src/store/menu/type.ts @@ -0,0 +1,14 @@ +import type { TranslateResult } from 'vue-i18n'; + +import type { Menu } from '@/lib/menu/config'; + +export interface DisplayMenu extends Menu { + show?: boolean; + label: TranslateResult; + icon?: string; + highlightTag?: HighlightTagType; + to: Location; + subMenuList?: DisplayMenu[]; + href?: string; +} +export type HighlightTagType = 'new' | 'beta' | 'update'; From 42e7e2ee77ef72dac7dcd36315169f9f6eb0aab2 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:34:04 +0900 Subject: [PATCH 11/51] chore: apply changed store Signed-off-by: samuel.park --- apps/web/src/App.vue | 5 ++++- .../composables/contents-accessibility/index.ts | 6 +++--- .../common/composables/grant-scope-guard/index.ts | 8 ++++---- .../composables/page-editable-status/index.ts | 14 +++----------- apps/web/src/lib/site-initializer/api-client.ts | 7 ++++--- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/apps/web/src/App.vue b/apps/web/src/App.vue index 95d5dde6b7..09800b3104 100755 --- a/apps/web/src/App.vue +++ b/apps/web/src/App.vue @@ -15,6 +15,7 @@ import { EXTERNAL_PAGE_ROUTE } from '@/router/constant'; import { getRouteScope } from '@/router/helpers/route-helper'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { SIDEBAR_TYPE } from '@/store/display/constant'; import { useDisplayStore } from '@/store/display/display-store'; import { useErrorStore } from '@/store/error/error-store'; @@ -37,6 +38,7 @@ import MobileGuideModal from '@/services/auth/components/MobileGuideModal.vue'; import { AUTH_ROUTE } from '@/services/auth/routes/route-constant'; import { LANDING_ROUTE } from '@/services/landing/routes/route-constant'; + const router = useRouter(); const route = useRoute(); @@ -56,6 +58,7 @@ const state = reactive({ }); const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const appContextStore = useAppContextStore(); const errorStore = useErrorStore(); const globalUIStore = useGlobalUIStore(); @@ -74,7 +77,7 @@ const goToSignIn = async () => { name: AUTH_ROUTE.SIGN_OUT._NAME, query: { previousPath: route.fullPath }, }; - userStore.setCurrentGrantInfo(undefined); + authorizationStore.setCurrentGrantInfo(undefined); errorStore.setVisibleSessionExpiredError(false); await router.push(to); diff --git a/apps/web/src/common/composables/contents-accessibility/index.ts b/apps/web/src/common/composables/contents-accessibility/index.ts index 7581899961..09228415af 100644 --- a/apps/web/src/common/composables/contents-accessibility/index.ts +++ b/apps/web/src/common/composables/contents-accessibility/index.ts @@ -1,7 +1,7 @@ import type { Ref } from 'vue'; import { computed, reactive, toRef } from 'vue'; -import { useGlobalConfigSchemaStore } from '@/store/global-config-schema/global-config-schema-store'; +import { useMenuStore } from '@/store/menu/menu-store'; import type { MenuId } from '@/lib/menu/config'; @@ -10,10 +10,10 @@ interface UseContentsAccessibilityReturnType { } export const useContentsAccessibility = (menuId: MenuId): UseContentsAccessibilityReturnType => { - const globalConfigSchemaStore = useGlobalConfigSchemaStore(); + const menuStore = useMenuStore(); const state = reactive({ - visibleContents: computed(() => globalConfigSchemaStore.getters.menuList.findIndex((menu) => menu.id === menuId) !== -1), + visibleContents: computed(() => menuStore.getters.menuList.findIndex((menu) => menu.id === menuId) !== -1), }); return { diff --git a/apps/web/src/common/composables/grant-scope-guard/index.ts b/apps/web/src/common/composables/grant-scope-guard/index.ts index ad4d72cf34..66d41b1897 100644 --- a/apps/web/src/common/composables/grant-scope-guard/index.ts +++ b/apps/web/src/common/composables/grant-scope-guard/index.ts @@ -3,8 +3,8 @@ import { computed, reactive, watch } from 'vue'; import type { GrantScope } from '@/api-clients/identity/token/schema/type'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import type { GrantInfo } from '@/store/user/type'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; +import type { GrantInfo } from '@/store/authorization/type'; import ErrorHandler from '@/common/composables/error/errorHandler'; @@ -14,10 +14,10 @@ interface GrantScopeGuardReturnType { } export const useGrantScopeGuard = (requiredScopes: GrantScope[], apiFunction: () => Promise): GrantScopeGuardReturnType => { const appContextStore = useAppContextStore(); - const userStore = useUserStore(); + const authorizationStore = useAuthorizationStore(); const state = reactive({ - currentGrantInfo: computed(() => userStore.state.currentGrantInfo || { scope: 'USER' }), + currentGrantInfo: computed(() => authorizationStore.state.currentGrantInfo || { scope: 'USER' }), isLoading: computed(() => appContextStore.getters.globalGrantLoading), isValidScope: computed(() => requiredScopes.includes(state.currentGrantInfo.scope)), }); diff --git a/apps/web/src/common/composables/page-editable-status/index.ts b/apps/web/src/common/composables/page-editable-status/index.ts index a77d60f18c..b5a3eb803c 100644 --- a/apps/web/src/common/composables/page-editable-status/index.ts +++ b/apps/web/src/common/composables/page-editable-status/index.ts @@ -1,9 +1,7 @@ import type { Ref } from 'vue'; import { computed, reactive, toRef } from 'vue'; -import { useUserStore } from '@/store/user/user-store'; - -import type { PageAccessMap } from '@/lib/access-control/config'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useCurrentMenuId } from '@/common/composables/current-menu-id'; @@ -12,17 +10,11 @@ interface UsePageEditableStatusReturnType { } export const usePageEditableStatus = (): UsePageEditableStatusReturnType => { - const userStore = useUserStore(); - const userGetters = userStore.getters; - + const authorizationStore = useAuthorizationStore(); const { currentMenuId } = useCurrentMenuId(); - const storeState = reactive({ - pageAccessPermissionMap: computed(() => userGetters.pageAccessPermissionMap), - }); - const state = reactive({ - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[currentMenuId.value]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[currentMenuId.value]?.write), }); return { diff --git a/apps/web/src/lib/site-initializer/api-client.ts b/apps/web/src/lib/site-initializer/api-client.ts index 97198e2e26..cab04c00ee 100644 --- a/apps/web/src/lib/site-initializer/api-client.ts +++ b/apps/web/src/lib/site-initializer/api-client.ts @@ -5,6 +5,7 @@ import type { DevConfig, MockConfig, AuthConfig } from '@cloudforet/core-lib/spa import type { TokenGrantParameters } from '@/api-clients/identity/token/schema/api-verbs/grant'; import type { TokenGrantModel } from '@/api-clients/identity/token/schema/model'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useErrorStore } from '@/store/error/error-store'; import { pinia } from '@/store/pinia'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; @@ -415,7 +416,7 @@ const getAuthConfig = (config): AuthConfig => ({ }); export const initApiClient = async (config) => { - const userStore = useUserStore(pinia); + const authorizationStore = useAuthorizationStore(pinia); const endpoints = getApiEndpoints(config); const tokenApi = new TokenAPI(endpoints[1], getSessionTimeoutCallback()); const apiSettings = getApiSettings(config); @@ -443,8 +444,8 @@ export const initApiClient = async (config) => { }; const response = await SpaceConnector.clientV2.identity.token.grant(grantRequest); SpaceConnector.setToken(response.access_token); - userStore.setCurrentGrantInfo({ scope: 'USER' }); - userStore.setCurrentRoleInfo(undefined); + authorizationStore.setCurrentGrantInfo({ scope: 'USER' }); + authorizationStore.setCurrentRoleInfo(undefined); } catch (e) { console.error(e); } From 878a70a54e85fb780aec48a1d53309eefbe72083 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:34:44 +0900 Subject: [PATCH 12/51] feat(all-menu): create all menu list composable (from display-store) Signed-off-by: samuel.park --- .../display => lib/menu}/use-all-menu-list.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) rename apps/web/src/{store/display => lib/menu}/use-all-menu-list.ts (88%) diff --git a/apps/web/src/store/display/use-all-menu-list.ts b/apps/web/src/lib/menu/use-all-menu-list.ts similarity index 88% rename from apps/web/src/store/display/use-all-menu-list.ts rename to apps/web/src/lib/menu/use-all-menu-list.ts index e000e7077b..33904eb9d8 100644 --- a/apps/web/src/store/display/use-all-menu-list.ts +++ b/apps/web/src/lib/menu/use-all-menu-list.ts @@ -6,20 +6,22 @@ import { i18n } from '@/translations'; import { makeAdminRouteName } from '@/router/helpers/route-helper'; -import type { Menu, MenuId, MenuInfo } from '@/lib/menu/config'; + +import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; +import { useMenuStore } from '@/store/menu/menu-store'; +import type { DisplayMenu } from '@/store/menu/type'; + import { MENU_ID } from '@/lib/menu/config'; +import type { Menu, MenuId, MenuInfo } from '@/lib/menu/config'; import { MENU_INFO_MAP } from '@/lib/menu/menu-info'; -import { useAppContextStore } from '../app-context/app-context-store'; -import { useUserWorkspaceStore } from '../app-context/workspace/user-workspace-store'; -import { useAccessControlStore } from '../derived/access-control-store'; -import { useMenuStore } from '../derived/menu-store'; -import type { DisplayMenu } from './type'; export const useAllMenuList = () => { const appContextStore = useAppContextStore(); const menuStore = useMenuStore(); - const accessControlStore = useAccessControlStore(); + const authorizationStore = useAuthorizationStore(); const userWorkspaceStore = useUserWorkspaceStore(); const _isAdminMode = computed(() => appContextStore.getters.isAdminMode); @@ -33,7 +35,7 @@ export const useAllMenuList = () => { _allGnbMenuList = _getDisplayMenuList(menuStore.getters.menuList, _isAdminMode.value, _currentWorkspaceId.value); _allGnbMenuList = _filterMenuByRoute(_allGnbMenuList, router, _currentWorkspaceId.value); if (!_isAdminMode.value) { - _allGnbMenuList = _filterMenuByAccessPermission(_allGnbMenuList, accessControlStore.getters.pageAccessPermissionList); + _allGnbMenuList = _filterMenuByAccessPermission(_allGnbMenuList, authorizationStore.getters.pageAccessPermissionList); } if (!isMyPage) { From da51df99d32c994e59bc17be36d6d846b99b7e63 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:36:53 +0900 Subject: [PATCH 13/51] chore: apply all menu list composable Signed-off-by: samuel.park --- .../favorite-button/FavoriteButton.vue | 10 ++++--- .../navigations/gnb/GNBNavigationRail.vue | 8 +++--- .../modules/TopBarSearchServiceTab.vue | 15 +++++----- .../modules/TopBarFavoriteContextMenu.vue | 28 +++++++++---------- .../components/UserConfigRecent.vue | 9 +++--- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/apps/web/src/common/modules/favorites/favorite-button/FavoriteButton.vue b/apps/web/src/common/modules/favorites/favorite-button/FavoriteButton.vue index 0644fc399b..4263d60b7a 100644 --- a/apps/web/src/common/modules/favorites/favorite-button/FavoriteButton.vue +++ b/apps/web/src/common/modules/favorites/favorite-button/FavoriteButton.vue @@ -2,7 +2,7 @@ import { computed, reactive, } from 'vue'; -import { useRoute } from 'vue-router/composables'; +import { useRoute, useRouter } from 'vue-router/composables'; import { PI } from '@cloudforet/mirinae'; @@ -12,7 +12,6 @@ import type { MetricExampleModel } from '@/schema/inventory/metric-example/model import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useDisplayStore } from '@/store/display/display-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { CloudServiceTypeReferenceMap } from '@/store/reference/cloud-service-type-reference-store'; import type { CostDataSourceReferenceMap } from '@/store/reference/cost-data-source-reference-store'; @@ -34,6 +33,7 @@ import { convertWorkspaceConfigToReferenceData, convertServiceConfigToReferenceData, } from '@/lib/helper/config-data-helper'; +import { useAllMenuList } from '@/lib/menu/use-all-menu-list'; import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; import type { FavoriteType, FavoriteConfig } from '@/common/modules/favorites/favorite-button/type'; @@ -57,6 +57,7 @@ const props = withDefaults(defineProps(), { }); const route = useRoute(); +const router = useRouter(); const allReferenceStore = useAllReferenceStore(); const userWorkspaceStore = useUserWorkspaceStore(); @@ -66,7 +67,8 @@ const favoriteStore = useFavoriteStore(); const favoriteStoreGetters = favoriteStore.getters; const gnbStore = useGnbStore(); const gnbStoreGetters = gnbStore.getters; -const displayStore = useDisplayStore(); +const { getAllMenuList } = useAllMenuList(); + /* Query */ const { @@ -152,7 +154,7 @@ const convertFavoriteToReferenceData = (favoriteConfig: FavoriteConfig): Referen if (itemType === FAVORITE_TYPE.SERVICE) { return convertServiceConfigToReferenceData([favoriteConfig], storeState.service)[0]; } - const allMenuList = displayStore.getAllMenuList(route); + const allMenuList = getAllMenuList(route, router); return convertMenuConfigToReferenceData([favoriteConfig], allMenuList)[0]; }; diff --git a/apps/web/src/common/modules/navigations/gnb/GNBNavigationRail.vue b/apps/web/src/common/modules/navigations/gnb/GNBNavigationRail.vue index f4593f0749..16e010e23b 100644 --- a/apps/web/src/common/modules/navigations/gnb/GNBNavigationRail.vue +++ b/apps/web/src/common/modules/navigations/gnb/GNBNavigationRail.vue @@ -14,13 +14,13 @@ import type { ContextMenuType } from '@cloudforet/mirinae/types/controls/context import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useDisplayStore } from '@/store/display/display-store'; -import type { DisplayMenu } from '@/store/display/type'; +import type { DisplayMenu } from '@/store/menu/type'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { CostDataSourceReferenceMap } from '@/store/reference/cost-data-source-reference-store'; import type { MenuId } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; +import { useAllMenuList } from '@/lib/menu/use-all-menu-list'; import BetaMark from '@/common/components/marks/BetaMark.vue'; import NewMark from '@/common/components/marks/NewMark.vue'; @@ -44,7 +44,7 @@ const gnbStore = useGnbStore(); const gnbGetters = gnbStore.getters; const userWorkspaceStore = useUserWorkspaceStore(); const userWorkspaceGetters = userWorkspaceStore.getters; -const displayStore = useDisplayStore(); +const { getAllMenuList } = useAllMenuList(); const route = useRoute(); const router = useRouter(); @@ -69,7 +69,7 @@ const state = reactive({ isMenuDescription: undefined as boolean | undefined, gnbMenuList: computed(() => { let results = [] as GNBMenuType[]; - const allMenuList = displayStore.getAllMenuList(route); + const allMenuList = getAllMenuList(route, router); const menuList = allMenuList.filter((d) => !d.hideOnGNB); if (state.isInit && isEmpty(storeState.costDataSource)) { results = removeCostExplorerFromMenuList(menuList); diff --git a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-search/modules/top-bar-search-dropdown/modules/TopBarSearchServiceTab.vue b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-search/modules/top-bar-search-dropdown/modules/TopBarSearchServiceTab.vue index 4ec074ee72..8ae2bc60ae 100644 --- a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-search/modules/top-bar-search-dropdown/modules/TopBarSearchServiceTab.vue +++ b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-search/modules/top-bar-search-dropdown/modules/TopBarSearchServiceTab.vue @@ -12,15 +12,15 @@ import { getTextHighlightRegex, PDataLoader, PDivider } from '@cloudforet/mirina import { i18n } from '@/translations'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useDisplayStore } from '@/store/display/display-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; + -import type { PageAccessMap } from '@/lib/access-control/config'; import type { SuggestionMenu } from '@/lib/helper/menu-suggestion-helper'; import { getAllSuggestionMenuList } from '@/lib/helper/menu-suggestion-helper'; import type { MenuInfo } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; import { MENU_INFO_MAP } from '@/lib/menu/menu-info'; +import { useAllMenuList } from '@/lib/menu/use-all-menu-list'; import { useProxyValue } from '@/common/composables/proxy-state'; import { useRecentStore } from '@/common/modules/navigations/stores/recent-store'; @@ -49,8 +49,8 @@ const props = withDefaults(defineProps(), { const userWorkspaceStore = useUserWorkspaceStore(); const topBarSearchStore = useTopBarSearchStore(); const recentStore = useRecentStore(); -const userStore = useUserStore(); -const displayStore = useDisplayStore(); +const authorizationStore = useAuthorizationStore(); +const { getAllMenuList } = useAllMenuList(); const route = useRoute(); const router = useRouter(); @@ -66,12 +66,11 @@ const storeState = reactive({ currentWorkspaceId: computed(() => userWorkspaceStore.getters.currentWorkspaceId), inputText: computed(() => topBarSearchStore.getters.inputText), trimmedInputText: computed(() => topBarSearchStore.getters.trimmedInputText), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); const state = reactive({ allMenuList: computed(() => { - const allMenuList = displayStore.getAllMenuList(route); + const allMenuList = getAllMenuList(route, router); return getAllSuggestionMenuList(allMenuList); }), allMenuMap: computed(() => { @@ -99,7 +98,7 @@ const state = reactive({ recentMenuList: computed(() => { const _recentMenuList: RecentItem[] = []; recentStore.state.recentMenuList.forEach((i) => { - if (storeState.pageAccessPermissionMap[i.data.id]) { + if (authorizationStore.getters.pageAccessPermissionMap[i.data.id]) { _recentMenuList.push(i); } }); diff --git a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-favorite/modules/TopBarFavoriteContextMenu.vue b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-favorite/modules/TopBarFavoriteContextMenu.vue index 07c40ad50a..23245274e4 100644 --- a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-favorite/modules/TopBarFavoriteContextMenu.vue +++ b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-favorite/modules/TopBarFavoriteContextMenu.vue @@ -19,17 +19,15 @@ import { i18n } from '@/translations'; import { useReferenceRouter } from '@/router/composables/use-reference-router'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useDisplayStore } from '@/store/display/display-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { CostDataSourceReferenceMap } from '@/store/reference/cost-data-source-reference-store'; import type { MetricReferenceMap } from '@/store/reference/metric-reference-store'; import type { ProjectGroupReferenceMap } from '@/store/reference/project-group-reference-store'; import type { ProjectReferenceMap } from '@/store/reference/project-reference-store'; import type { ServiceReferenceMap } from '@/store/reference/service-reference-store'; -import { useUserStore } from '@/store/user/user-store'; import { isUserAccessibleToMenu } from '@/lib/access-control'; -import type { PageAccessMap } from '@/lib/access-control/config'; import type { ReferenceData } from '@/lib/helper/config-data-helper'; import { convertCostAnalysisConfigToReferenceData, @@ -39,9 +37,10 @@ import { convertProjectGroupConfigToReferenceData, convertServiceConfigToReferenceData, getParsedKeysWithManagedCostQueryFavoriteKey, } from '@/lib/helper/config-data-helper'; -import type { MenuInfo } from '@/lib/menu/config'; +import type { MenuId, MenuInfo } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; import { MENU_INFO_MAP } from '@/lib/menu/menu-info'; +import { useAllMenuList } from '@/lib/menu/use-all-menu-list'; import { useGrantScopeGuard } from '@/common/composables/grant-scope-guard'; import { useFavoriteStore } from '@/common/modules/favorites/favorite-button/store/favorite-store'; @@ -78,9 +77,8 @@ const favoriteStore = useFavoriteStore(); const favoriteGetters = favoriteStore.getters; const gnbStore = useGnbStore(); const gnbStoreGetters = gnbStore.getters; -const userStore = useUserStore(); -const displayStore = useDisplayStore(); - +const authorizationStore = useAuthorizationStore(); +const { getAllMenuList } = useAllMenuList(); const { getReferenceLocation } = useReferenceRouter(); /* Query */ @@ -102,13 +100,13 @@ const storeState = reactive({ projectGroups: computed(() => allReferenceStore.getters.projectGroup), service: computed(() => allReferenceStore.getters.service), costQuerySets: computed(() => gnbStoreGetters.costQuerySets), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), + pageAccessPermissionList: computed(() => authorizationStore.getters.pageAccessPermissionList), }); const state = reactive({ loading: true, showAll: false, showAllType: undefined as undefined|FavoriteType, - accessProject: computed(() => !isEmpty(storeState.pageAccessPermissionMap[MENU_ID.PROJECT])), + accessProject: computed(() => !isEmpty(authorizationStore.getters.pageAccessPermissionMap[MENU_ID.PROJECT])), items: computed(() => { const results: FavoriteMenuItem[] = []; if (state.favoriteMenuItems.length) { @@ -186,14 +184,14 @@ const state = reactive({ }), // favoriteMenuItems: computed(() => { - const allMenuList = displayStore.getAllMenuList(route); + const allMenuList = getAllMenuList(route, router); return convertMenuConfigToReferenceData( favoriteGetters.menuItems ?? [], allMenuList, ); }), favoriteCostAnalysisItems: computed(() => { - const isUserAccessible = isUserAccessibleToMenu(MENU_ID.COST_ANALYSIS, userStore.getters.pageAccessPermissionList); + const isUserAccessible = isUserAccessibleToMenu(MENU_ID.COST_ANALYSIS, storeState.pageAccessPermissionList); return isUserAccessible ? convertCostAnalysisConfigToReferenceData( favoriteGetters.costAnalysisItems ?? [], @@ -203,7 +201,7 @@ const state = reactive({ : []; }), favoriteDashboardItems: computed(() => { - const isUserAccessibleToDashboards = isUserAccessibleToMenu(MENU_ID.DASHBOARDS, userStore.getters.pageAccessPermissionList); + const isUserAccessibleToDashboards = isUserAccessibleToMenu(MENU_ID.DASHBOARDS, storeState.pageAccessPermissionList); if (!isUserAccessibleToDashboards) return []; return convertDashboardConfigToReferenceData( favoriteGetters.dashboardItems ?? [], @@ -211,7 +209,7 @@ const state = reactive({ ); }), favoriteMetricItems: computed(() => { - const isUserAccessible = isUserAccessibleToMenu(MENU_ID.METRIC_EXPLORER, userStore.getters.pageAccessPermissionList); + const isUserAccessible = isUserAccessibleToMenu(MENU_ID.METRIC_EXPLORER, storeState.pageAccessPermissionList); if (!isUserAccessible) return []; const favoriteMetricItems = convertMetricConfigToReferenceData(favoriteGetters.metricItems ?? [], storeState.metrics); const favoriteMetricExampleItems = convertMetricExampleConfigToReferenceData(favoriteGetters.metricExampleItems ?? [], storeState.metricExamples); @@ -221,14 +219,14 @@ const state = reactive({ ]; }), favoriteProjects: computed(() => { - const isUserAccessible = isUserAccessibleToMenu(MENU_ID.PROJECT, userStore.getters.pageAccessPermissionList); + const isUserAccessible = isUserAccessibleToMenu(MENU_ID.PROJECT, storeState.pageAccessPermissionList); if (!isUserAccessible) return []; const favoriteProjectItems = convertProjectConfigToReferenceData(favoriteGetters.projectItems ?? [], storeState.projects); const favoriteProjectGroupItems = convertProjectGroupConfigToReferenceData(favoriteGetters.projectGroupItems ?? [], storeState.projectGroups); return [...favoriteProjectGroupItems, ...favoriteProjectItems]; }), favoriteServiceItems: computed(() => { - const isUserAccessible = isUserAccessibleToMenu(MENU_ID.SERVICE, userStore.getters.pageAccessPermissionList); + const isUserAccessible = isUserAccessibleToMenu(MENU_ID.SERVICE, storeState.pageAccessPermissionList); return isUserAccessible ? convertServiceConfigToReferenceData(favoriteGetters.serviceItems ?? [], storeState.service) : []; }), }); diff --git a/apps/web/src/services/workspace-home/components/UserConfigRecent.vue b/apps/web/src/services/workspace-home/components/UserConfigRecent.vue index 829775d414..f6ccde6b31 100644 --- a/apps/web/src/services/workspace-home/components/UserConfigRecent.vue +++ b/apps/web/src/services/workspace-home/components/UserConfigRecent.vue @@ -1,6 +1,6 @@ From dbeb94a5a5b516a9c6c0386c7dbaf6c90a8a76a7 Mon Sep 17 00:00:00 2001 From: Yongtae Park Date: Wed, 23 Apr 2025 16:40:00 +0900 Subject: [PATCH 14/51] chore: apply authorization store Signed-off-by: samuel.park --- .../bookmark/BookmarkFolderFormModal.vue | 6 +- .../bookmark/BookmarkLinkFormModal.vue | 6 +- .../TopBarSearchDropdown.vue | 8 +- .../modules/top-bar-profile/TopBarProfile.vue | 5 +- .../modules/popup/notice/NoticePopup.vue | 6 +- .../_internal/use-dashboard-manageable.ts | 7 +- .../DashboardCreateScopeForm.vue | 6 +- .../DashboardCreateStep2BundleCase.vue | 4 +- .../v1/components/AlertMainDataTable.vue | 6 +- .../v1/components/EscalationPolicyForm.vue | 7 +- .../CloudServiceHistoryDetailNoteTab.vue | 9 +-- .../components/CloudServiceLSB.vue | 6 +- .../components/CollectorMainContents.vue | 7 +- .../components/MetricExplorerHeader.vue | 8 +- .../components/MetricExplorerLSBMetric.vue | 8 +- .../pages/CloudServicePage.vue | 10 +-- .../pages/CollectorDetailPage.vue | 8 +- .../asset-inventory/pages/SecurityPage.vue | 10 +-- .../pages/admin/AdminCollectorDetailPage.vue | 11 +-- .../src/services/auth/authenticator/index.ts | 9 ++- .../components/BudgetDetailHeading.vue | 7 +- .../cost-explorer/pages/BudgetMainPage.vue | 6 +- .../src/services/dashboards/DashboardsLSB.vue | 6 +- .../dashboard-detail/DashboardCloneModal.vue | 10 +-- .../DashboardFolderFormModal.vue | 6 +- .../DashboardFolderTreeItem.vue | 6 +- .../DashboardBundleCloneModal.vue | 4 +- .../dashboard-main/DashboardLSBTree.vue | 6 +- .../composables/use-dashboard-search-query.ts | 6 +- .../dashboards/pages/DashboardsMainPage.vue | 8 +- .../src/services/iam/pages/UserMainPage.vue | 6 +- .../src/services/iam/store/user-page-store.ts | 6 +- .../components/DomainLandingStartBanner.vue | 6 -- .../my-page/pages/UserAccountPage.vue | 5 +- .../project/v1/stores/project-page-store.ts | 7 +- .../ProjectGroupMemberManagementModal.vue | 7 +- .../pages/ServiceAccountDetailPage.vue | 10 +-- .../pages/ServiceAccountPage.vue | 8 +- .../components/BookmarkBoard.vue | 6 +- .../components/WorkspaceInfo.vue | 8 +- .../pages/WorkspaceHomePage.vue | 14 ++-- .../shared/components/AccountSummary.vue | 10 +-- .../shared/components/AssetSummary.vue | 6 +- .../AssetSummaryDailyUpdateItem.vue | 10 +-- .../components/AssetSummaryProviderItem.vue | 11 +-- .../shared/components/CostSummary.vue | 29 ++++--- .../shared/components/EmptySummaryData.vue | 8 +- .../src/store/derived/access-control-store.ts | 78 ------------------- .../store/reference/app-reference-store.ts | 7 +- .../cloud-service-type-reference-store.ts | 7 +- ...cloue-service-query-set-reference-store.ts | 7 +- .../reference/collector-reference-store.ts | 6 +- .../cost-data-source-reference-store.ts | 6 +- .../escalation-policy-reference-store.ts | 10 ++- .../reference/namespace-reference-store.ts | 6 +- .../project-group-reference-store.ts | 8 +- .../reference/project-reference-store.ts | 7 +- .../reference/protocol-reference-store.ts | 6 +- .../reference/provider-reference-store.ts | 7 +- .../public-dashboard-reference-store.ts | 7 +- .../public-folder-reference-store.ts | 7 +- .../store/reference/region-reference-store.ts | 7 +- .../store/reference/secret-reference-store.ts | 7 +- .../service-account-reference-store.ts | 7 +- .../reference/service-reference-store.ts | 6 +- .../trusted-account-reference-store.ts | 6 +- .../reference/user-group-reference-store.ts | 6 +- .../store/reference/user-reference-store.ts | 7 +- .../reference/webhook-reference-store.ts | 6 +- .../workspace-group-reference-store.ts | 7 +- .../reference/workspace-reference-store.ts | 6 +- 71 files changed, 245 insertions(+), 358 deletions(-) delete mode 100644 apps/web/src/store/derived/access-control-store.ts diff --git a/apps/web/src/common/components/bookmark/BookmarkFolderFormModal.vue b/apps/web/src/common/components/bookmark/BookmarkFolderFormModal.vue index 6b4b6e90f6..58679a5c34 100644 --- a/apps/web/src/common/components/bookmark/BookmarkFolderFormModal.vue +++ b/apps/web/src/common/components/bookmark/BookmarkFolderFormModal.vue @@ -10,7 +10,7 @@ import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { BOOKMARK_MODAL_TYPE } from '@/common/components/bookmark/constant/constant'; import { useBookmarkStore } from '@/common/components/bookmark/store/bookmark-store'; @@ -38,13 +38,13 @@ const bookmarkStore = useBookmarkStore(); const bookmarkState = bookmarkStore.state; const appContextStore = useAppContextStore(); const appContextGetters = appContextStore.getters; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const emit = defineEmits<{(e: 'confirm', isEdit?: boolean, name?: string): void; }>(); const storeState = reactive({ isAdminMode: computed(() => appContextGetters.isAdminMode), - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), modal: computed(() => bookmarkState.modal), bookmarkType: computed(() => bookmarkState.bookmarkType), diff --git a/apps/web/src/common/components/bookmark/BookmarkLinkFormModal.vue b/apps/web/src/common/components/bookmark/BookmarkLinkFormModal.vue index fb6063854a..99b12f4346 100644 --- a/apps/web/src/common/components/bookmark/BookmarkLinkFormModal.vue +++ b/apps/web/src/common/components/bookmark/BookmarkLinkFormModal.vue @@ -15,7 +15,7 @@ import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; @@ -47,14 +47,14 @@ const userWorkspaceStore = useUserWorkspaceStore(); const userWorkspaceStoreGetters = userWorkspaceStore.getters; const appContextStore = useAppContextStore(); const appContextGetters = appContextStore.getters; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const emit = defineEmits<{(e: 'confirm', selectedFolder?: BookmarkItem, scope?: BookmarkType): void; }>(); const storeState = reactive({ isAdminMode: computed(() => appContextGetters.isAdminMode), currentWorkspaceId: computed(() => userWorkspaceStoreGetters.currentWorkspaceId), - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), modal: computed(() => bookmarkState.modal), selectedBookmark: computed(() => bookmarkState.selectedBookmark), diff --git a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-search/modules/top-bar-search-dropdown/TopBarSearchDropdown.vue b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-search/modules/top-bar-search-dropdown/TopBarSearchDropdown.vue index d6f8b76d00..b2662c3d76 100644 --- a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-search/modules/top-bar-search-dropdown/TopBarSearchDropdown.vue +++ b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-search/modules/top-bar-search-dropdown/TopBarSearchDropdown.vue @@ -13,10 +13,9 @@ import type { ValueItem } from '@cloudforet/mirinae/types/controls/search/query- import { useReferenceRouter } from '@/router/composables/use-reference-router'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; -import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { MENU_ID } from '@/lib/menu/config'; import { useRecentStore } from '@/common/modules/navigations/stores/recent-store'; @@ -54,8 +53,8 @@ const BOTTOM_MARGIN = 5.5 * 16; const topBarSearchStore = useTopBarSearchStore(); const recentStore = useRecentStore(); -const userStore = useUserStore(); const windowSize = useWindowSize(); +const authorizationStore = useAuthorizationStore(); const { getReferenceLocation } = useReferenceRouter(); @@ -73,7 +72,6 @@ const getTabHeaderHeight = () => { const storeState = reactive({ activeTab: computed(() => topBarSearchStore.state.activeTab), cloudServiceTypeMap: computed(() => allReferenceStore.getters.cloudServiceType), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); const state = reactive({ @@ -86,7 +84,7 @@ const state = reactive({ tabs: computed(() => { const accessMenuList: ValueItem[] = []; state.defaultServiceTabs.forEach((i) => { - if (storeState.pageAccessPermissionMap[i.id]) { + if (authorizationStore.getters.pageAccessPermissionMap[i.id]) { accessMenuList.push({ label: i.label, name: i.name }); } }); diff --git a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-profile/TopBarProfile.vue b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-profile/TopBarProfile.vue index c6de3c4c1b..cb60b9da4b 100644 --- a/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-profile/TopBarProfile.vue +++ b/apps/web/src/common/modules/navigations/top-bar/modules/top-bar-toolset/modules/top-bar-profile/TopBarProfile.vue @@ -23,6 +23,7 @@ import WorkspaceOwnerImage from '@/assets/images/role/img_avatar_workspace-owner import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useDomainStore } from '@/store/domain/domain-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { RoleReferenceMap } from '@/store/reference/role-reference-store'; @@ -38,7 +39,6 @@ import { AUTH_ROUTE } from '@/services/auth/routes/route-constant'; import { LANDING_ROUTE } from '@/services/landing/routes/route-constant'; import { MY_PAGE_ROUTE } from '@/services/my-page/routes/route-constant'; - interface Props { visible: boolean } @@ -49,6 +49,7 @@ const props = withDefaults(defineProps(), { const appContextStore = useAppContextStore(); const domainStore = useDomainStore(); const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const emit = defineEmits<{(e: 'update:visible', visible: boolean): void; }>(); @@ -69,7 +70,7 @@ const state = reactive({ return UserImage; }), baseRoleType: computed(() => userStore.state.roleType), - currentRoleType: computed(() => userStore.state.currentRoleInfo?.roleType), + currentRoleType: computed(() => authorizationStore.state.currentRoleInfo?.roleType), visibleRoleType: computed(() => { if (state.baseRoleType === ROLE_TYPE.DOMAIN_ADMIN) return 'Admin'; if (state.currentRoleType === ROLE_TYPE.WORKSPACE_OWNER) return 'Workspace Owner'; diff --git a/apps/web/src/common/modules/popup/notice/NoticePopup.vue b/apps/web/src/common/modules/popup/notice/NoticePopup.vue index 97e8efef27..e3a537bbb6 100644 --- a/apps/web/src/common/modules/popup/notice/NoticePopup.vue +++ b/apps/web/src/common/modules/popup/notice/NoticePopup.vue @@ -15,6 +15,7 @@ import type { PostModel } from '@/schema/board/post/model'; import type { NoticeConfigData } from '@/schema/board/post/type'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useUserStore } from '@/store/user/user-store'; import ErrorHandler from '@/common/composables/error/errorHandler'; @@ -22,9 +23,10 @@ import NoticePopupItem from '@/common/modules/popup/notice/modules/NoticePopupIt const appContextStore = useAppContextStore(); const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const state = reactive({ isSessionExpired: computed(() => !!userStore.state.isSessionExpired), - isNoRoleUser: computed(() => userStore.getters.isNoRoleUser), + isNoRoleUser: computed(() => authorizationStore.getters.isNoRoleUser), popupList: [] as PostModel[], hasLoaded: false, }); @@ -79,7 +81,7 @@ watch([ () => state.isSessionExpired, () => state.isNoRoleUser, () => appContextStore.getters.globalGrantLoading, - () => userStore.state.currentGrantInfo, + () => authorizationStore.state.currentGrantInfo, ], async ([hasLoaded, isSessionExpired, isNoRoleUser, globalGrantLoading, grantInfo]) => { if (hasLoaded) return; if (isNoRoleUser || isSessionExpired) { diff --git a/apps/web/src/services/_shared/dashboard/core/composables/_internal/use-dashboard-manageable.ts b/apps/web/src/services/_shared/dashboard/core/composables/_internal/use-dashboard-manageable.ts index 01c058e077..2179f37da9 100644 --- a/apps/web/src/services/_shared/dashboard/core/composables/_internal/use-dashboard-manageable.ts +++ b/apps/web/src/services/_shared/dashboard/core/composables/_internal/use-dashboard-manageable.ts @@ -8,21 +8,20 @@ import type { PublicFolderModel } from '@/api-clients/dashboard/public-folder/sc import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useDashboardSharedContext } from '@/services/_shared/dashboard/core/composables/_internal/use-dashboard-shared-context'; import type { DashboardSharedEntryPoint } from '@/services/_shared/dashboard/core/types/dashboard-shared-type'; - export const useDashboardManageable = () => { - const userStore = useUserStore(); + const authorizationStore = useAuthorizationStore(); const appContextStore = useAppContextStore(); const { entryPoint } = useDashboardSharedContext(); const isAdminMode = computed(() => appContextStore.getters.isAdminMode); const storeState = reactive({ - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), }); const _isManageable = ( diff --git a/apps/web/src/services/_shared/dashboard/dashboard-create/contextual-components/DashboardCreateScopeForm.vue b/apps/web/src/services/_shared/dashboard/dashboard-create/contextual-components/DashboardCreateScopeForm.vue index 3c5e0874f0..ae2785cc07 100644 --- a/apps/web/src/services/_shared/dashboard/dashboard-create/contextual-components/DashboardCreateScopeForm.vue +++ b/apps/web/src/services/_shared/dashboard/dashboard-create/contextual-components/DashboardCreateScopeForm.vue @@ -14,7 +14,7 @@ import type { WorkspaceModel } from '@/api-clients/identity/workspace/schema/mod import { i18n } from '@/translations'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import WorkspaceLogoIcon from '@/common/modules/navigations/top-bar/modules/top-bar-header/WorkspaceLogoIcon.vue'; @@ -34,9 +34,9 @@ const dashboardCreatePageStore = useDashboardCreatePageStore(); const dashboardCreatePageState = dashboardCreatePageStore.state; const userWorkspaceStore = useUserWorkspaceStore(); const userWorkspaceState = userWorkspaceStore.$state; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const storeState = reactive({ - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), selectedWorkspace: computed(() => userWorkspaceState.getters.currentWorkspace), }); diff --git a/apps/web/src/services/_shared/dashboard/dashboard-create/contextual-components/DashboardCreateStep2BundleCase.vue b/apps/web/src/services/_shared/dashboard/dashboard-create/contextual-components/DashboardCreateStep2BundleCase.vue index 56df784c06..2d16f1a196 100644 --- a/apps/web/src/services/_shared/dashboard/dashboard-create/contextual-components/DashboardCreateStep2BundleCase.vue +++ b/apps/web/src/services/_shared/dashboard/dashboard-create/contextual-components/DashboardCreateStep2BundleCase.vue @@ -21,6 +21,7 @@ import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { useServiceQueryKey } from '@/query/query-key/use-service-query-key'; import { i18n } from '@/translations'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useUserStore } from '@/store/user/user-store'; import { showErrorMessage, showSuccessMessage } from '@/lib/helper/notice-alert-helper'; @@ -53,6 +54,7 @@ const dashboardCreatePageStore = useDashboardCreatePageStore(); const dashboardCreatePageState = dashboardCreatePageStore.state; const dashboardTreeControlStore = useDashboardTreeControlStore(); const dashboardTreeControlState = dashboardTreeControlStore.state; +const authorizationStore = useAuthorizationStore(); const userStore = useUserStore(); const router = useRouter(); const { @@ -60,7 +62,7 @@ const { } = useDashboardSharedContext(); const storeState = reactive({ - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), }); const state = reactive({ loading: false, diff --git a/apps/web/src/services/alert-manager/v1/components/AlertMainDataTable.vue b/apps/web/src/services/alert-manager/v1/components/AlertMainDataTable.vue index efbf69a62a..61e8eb16b7 100644 --- a/apps/web/src/services/alert-manager/v1/components/AlertMainDataTable.vue +++ b/apps/web/src/services/alert-manager/v1/components/AlertMainDataTable.vue @@ -25,13 +25,13 @@ import { ALERT_STATE, ALERT_URGENCY } from '@/schema/monitoring/alert/constants' import { useReferenceRouter } from '@/router/composables/use-reference-router'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { ProjectReferenceMap } from '@/store/reference/project-reference-store'; import type { UserReferenceMap } from '@/store/reference/user-reference-store'; import type { WebhookReferenceMap } from '@/store/reference/webhook-reference-store'; import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { FILE_NAME_PREFIX } from '@/lib/excel-export/constant'; import { downloadExcel } from '@/lib/helper/file-download-helper'; import type { MenuId } from '@/lib/menu/config'; @@ -94,12 +94,12 @@ const emit = defineEmits<{(event: 'update', filters: Partial(() => userStore.state.timezone || ''), projects: computed(() => allReferenceStore.getters.project), users: computed(() => allReferenceStore.getters.user), webhooks: computed(() => allReferenceStore.getters.webhook), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); /* Search Tags */ @@ -213,7 +213,7 @@ const state = reactive({ } return targetMenuId; }), - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[state.selectedMenuId]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[state.selectedMenuId]?.write), }); /* formatters & autocomplete handlers */ diff --git a/apps/web/src/services/alert-manager/v1/components/EscalationPolicyForm.vue b/apps/web/src/services/alert-manager/v1/components/EscalationPolicyForm.vue index 7d0af44909..2ccf19a949 100644 --- a/apps/web/src/services/alert-manager/v1/components/EscalationPolicyForm.vue +++ b/apps/web/src/services/alert-manager/v1/components/EscalationPolicyForm.vue @@ -18,8 +18,8 @@ import { i18n } from '@/translations'; import { useReferenceRouter } from '@/router/composables/use-reference-router'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; -import { useUserStore } from '@/store/user/user-store'; import { useFormValidator } from '@/common/composables/form-validator'; import type { ProjectTreeNodeData } from '@/common/modules/project/project-tree-type'; @@ -45,8 +45,7 @@ const allReferenceStore = useAllReferenceStore(); const userWorkspaceStore = useUserWorkspaceStore(); const escalationPolicyFormStore = useEscalationPolicyFormStore(); const escalationPolicyFormState = escalationPolicyFormStore.$state; -const userStore = useUserStore(); - +const authorizationStore = useAuthorizationStore(); const { getReferenceLocation } = useReferenceRouter(); const state = reactive({ @@ -57,7 +56,7 @@ const state = reactive({ PROJECT: i18n.t('MONITORING.ALERT.ESCALATION_POLICY.FORM.PROJECT'), })), resourceGroups: computed<{label: TranslateResult; value: EscalationPolicyModel['resource_group']}[]>(() => { - const currentRoleType = userStore.state.currentRoleInfo?.roleType; + const currentRoleType = authorizationStore.state.currentRoleInfo?.roleType; const resGroup: {label: TranslateResult; value: EscalationPolicyModel['resource_group']}[] = [ { label: i18n.t('MONITORING.ALERT.ESCALATION_POLICY.FORM.PROJECT'), value: 'PROJECT' }, ]; diff --git a/apps/web/src/services/asset-inventory/components/CloudServiceHistoryDetailNoteTab.vue b/apps/web/src/services/asset-inventory/components/CloudServiceHistoryDetailNoteTab.vue index 2a8343b746..cee4506c79 100644 --- a/apps/web/src/services/asset-inventory/components/CloudServiceHistoryDetailNoteTab.vue +++ b/apps/web/src/services/asset-inventory/components/CloudServiceHistoryDetailNoteTab.vue @@ -20,9 +20,9 @@ import type { NoteListParameters } from '@/schema/inventory/note/api-verbs/list' import type { NoteModel } from '@/schema/inventory/note/model'; import { i18n } from '@/translations'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import type { MenuId } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; @@ -31,7 +31,6 @@ import ErrorHandler from '@/common/composables/error/errorHandler'; import { COST_EXPLORER_ROUTE } from '@/services/cost-explorer/routes/route-constant'; - interface Props { recordId: string; manageDisabled: boolean; @@ -47,9 +46,7 @@ const emit = defineEmits<{(e: 'refresh-note-count'): void}>(); const route = useRoute(); const userStore = useUserStore(); -const storeState = reactive({ - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), -}); +const authorizationStore = useAuthorizationStore(); const state = reactive({ id: '', noteInput: '', @@ -72,7 +69,7 @@ const state = reactive({ } return targetMenuId; }), - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[state.selectedMenuId]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[state.selectedMenuId]?.write), }); const handleChangeNoteInput = (e) => { diff --git a/apps/web/src/services/asset-inventory/components/CloudServiceLSB.vue b/apps/web/src/services/asset-inventory/components/CloudServiceLSB.vue index 1f6f6d0171..38a3393a3c 100644 --- a/apps/web/src/services/asset-inventory/components/CloudServiceLSB.vue +++ b/apps/web/src/services/asset-inventory/components/CloudServiceLSB.vue @@ -13,9 +13,9 @@ import { import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { ProviderReferenceMap } from '@/store/reference/provider-reference-store'; -import { useUserStore } from '@/store/user/user-store'; import LSB from '@/common/modules/navigations/lsb/LSB.vue'; import type { @@ -47,14 +47,14 @@ const cloudServicePageState = cloudServicePageStore.$state; const cloudServiceDetailPageStore = useCloudServiceDetailPageStore(); const cloudServiceDetailPageState = cloudServiceDetailPageStore.$state; const allReferenceStore = useAllReferenceStore(); -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const route = useRoute(); const router = useRouter(); const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), - currentGrantInfo: computed(() => userStore.state.currentGrantInfo), + currentGrantInfo: computed(() => authorizationStore.state.currentGrantInfo), providers: computed(() => allReferenceStore.getters.provider), }); const state = reactive({ diff --git a/apps/web/src/services/asset-inventory/components/CollectorMainContents.vue b/apps/web/src/services/asset-inventory/components/CollectorMainContents.vue index 00b801433a..aae4c0ddd6 100644 --- a/apps/web/src/services/asset-inventory/components/CollectorMainContents.vue +++ b/apps/web/src/services/asset-inventory/components/CollectorMainContents.vue @@ -21,11 +21,11 @@ import type { ToolboxOptions } from '@cloudforet/mirinae/types/controls/toolbox/ import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { PluginReferenceMap } from '@/store/reference/plugin-reference-store'; import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { FILE_NAME_PREFIX } from '@/lib/excel-export/constant'; import { downloadExcel } from '@/lib/helper/file-download-helper'; import type { ExcelDataField } from '@/lib/helper/file-download-helper/type'; @@ -46,7 +46,6 @@ import { useCollectorPageStore } from '@/services/asset-inventory/stores/collect import type { CollectorItemInfo } from '@/services/asset-inventory/types/collector-main-page-type'; import { COST_EXPLORER_ROUTE } from '@/services/cost-explorer/routes/route-constant'; - /** * @function * @name makePluginReferenceValueHandler * @param distinct @@ -82,6 +81,7 @@ const collectorPageStore = useCollectorPageStore(); const collectorPageState = collectorPageStore.state; const allReferenceStore = useAllReferenceStore(); const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const appContextStore = useAppContextStore(); const router = useRouter(); const route = useRoute(); @@ -90,7 +90,6 @@ const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), plugins: computed(() => allReferenceStore.getters.plugin), timezone: computed(() => userStore.state.timezone ?? 'UTC'), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); const keyItemSets: KeyItemSet[] = [{ @@ -137,7 +136,7 @@ const state = reactive({ } return targetMenuId; }), - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[state.selectedMenuId]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[state.selectedMenuId]?.write), searchTags: computed(() => { const tags = searchQueryHelper.setFilters(collectorPageState.searchFilters).queryTags; return tags.reduce((r: QueryItem[], d: any): QueryItem[] => { diff --git a/apps/web/src/services/asset-inventory/components/MetricExplorerHeader.vue b/apps/web/src/services/asset-inventory/components/MetricExplorerHeader.vue index 18dca0dd3f..b6381c4878 100644 --- a/apps/web/src/services/asset-inventory/components/MetricExplorerHeader.vue +++ b/apps/web/src/services/asset-inventory/components/MetricExplorerHeader.vue @@ -23,10 +23,9 @@ import type { MetricModel } from '@/schema/inventory/metric/model'; import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; -import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; import type { MenuId } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; @@ -53,7 +52,7 @@ const metricExplorerPageState = metricExplorerPageStore.state; const metricExplorerPageGetters = metricExplorerPageStore.getters; const allReferenceStore = useAllReferenceStore(); const appContextStore = useAppContextStore(); -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const router = useRouter(); const route = useRoute(); @@ -62,7 +61,6 @@ const storeState = reactive({ namespaces: computed(() => allReferenceStore.getters.namespace), currentMetric: computed(() => metricExplorerPageState.metric), isAdminMode: computed(() => appContextStore.getters.isAdminMode), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); const state = reactive({ selectedMenuId: computed(() => { @@ -74,7 +72,7 @@ const state = reactive({ } return targetMenuId; }), - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[state.selectedMenuId]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[state.selectedMenuId]?.write), currentMetricId: computed(() => route.params.metricId), isDuplicateEnabled: computed(() => Object.values(storeState.namespaces).find((d) => d.key === storeState.currentMetric?.namespace_id)?.data.group !== 'common'), currentMetricExampleId: computed(() => route.params.metricExampleId), diff --git a/apps/web/src/services/asset-inventory/components/MetricExplorerLSBMetric.vue b/apps/web/src/services/asset-inventory/components/MetricExplorerLSBMetric.vue index d94f478bf2..95c10a143b 100644 --- a/apps/web/src/services/asset-inventory/components/MetricExplorerLSBMetric.vue +++ b/apps/web/src/services/asset-inventory/components/MetricExplorerLSBMetric.vue @@ -12,15 +12,14 @@ import { import type { TreeDisplayMap, TreeNode } from '@cloudforet/mirinae/types/data-display/tree/tree-view/type'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { CloudServiceTypeItem, CloudServiceTypeReferenceMap, } from '@/store/reference/cloud-service-type-reference-store'; import type { MetricReferenceItem } from '@/store/reference/metric-reference-store'; -import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import type { MenuId } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; @@ -46,7 +45,7 @@ const allReferenceStore = useAllReferenceStore(); const appContextStore = useAppContextStore(); const metricExplorerPageStore = useMetricExplorerPageStore(); const metricExplorerPageState = metricExplorerPageStore.state; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const route = useRoute(); const storeState = reactive({ @@ -60,7 +59,6 @@ const storeState = reactive({ return res; }), selectedNamespace: computed(() => metricExplorerPageState.selectedNamespace), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); const state = reactive({ selectedMenuId: computed(() => { @@ -72,7 +70,7 @@ const state = reactive({ } return targetMenuId; }), - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[state.selectedMenuId]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[state.selectedMenuId]?.write), selectedId: computed(() => { const routeName = { name: storeState.isAdminMode ? ADMIN_ASSET_INVENTORY_ROUTE.METRIC_EXPLORER.DETAIL._NAME : ASSET_INVENTORY_ROUTE.METRIC_EXPLORER.DETAIL._NAME }.name; if (!props.isDetailPage) return undefined; diff --git a/apps/web/src/services/asset-inventory/pages/CloudServicePage.vue b/apps/web/src/services/asset-inventory/pages/CloudServicePage.vue index 45ff1fdc53..18b0e32d58 100644 --- a/apps/web/src/services/asset-inventory/pages/CloudServicePage.vue +++ b/apps/web/src/services/asset-inventory/pages/CloudServicePage.vue @@ -23,14 +23,13 @@ import { SpaceRouter } from '@/router'; import type { CloudServiceAnalyzeParameters } from '@/schema/inventory/cloud-service/api-verbs/analyze'; import { i18n } from '@/translations'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { CollectorReferenceMap } from '@/store/reference/collector-reference-store'; import type { ProjectGroupReferenceMap } from '@/store/reference/project-group-reference-store'; import type { ProviderReferenceMap } from '@/store/reference/provider-reference-store'; import type { ServiceAccountReferenceMap } from '@/store/reference/service-account-reference-store'; -import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { MENU_ID } from '@/lib/menu/config'; import { arrayToQueryString, @@ -69,7 +68,7 @@ const allReferenceStore = useAllReferenceStore(); const cloudServicePageStore = useCloudServicePageStore(); const cloudServicePageState = cloudServicePageStore.$state; const cloudServiceLSBStore = useCloudServiceLSBStore(); -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const storeState = reactive({ projects: computed(() => allReferenceStore.getters.project), @@ -77,7 +76,6 @@ const storeState = reactive({ serviceAccounts: computed(() => allReferenceStore.getters.serviceAccount), providers: computed(() => allReferenceStore.getters.provider), collectors: computed(() => allReferenceStore.getters.collector), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); const handlerState = reactive({ keyItemSets: computed(() => [{ @@ -150,8 +148,8 @@ const state = reactive({ } return result; }), - writableServiceAccount: computed(() => storeState.pageAccessPermissionMap[MENU_ID.SERVICE_ACCOUNT].write), - writableCollector: computed(() => storeState.pageAccessPermissionMap[MENU_ID.COLLECTOR].write), + writableServiceAccount: computed(() => authorizationStore.getters.pageAccessPermissionMap[MENU_ID.SERVICE_ACCOUNT]?.write), + writableCollector: computed(() => authorizationStore.getters.pageAccessPermissionMap[MENU_ID.COLLECTOR]?.write), }); /* api */ diff --git a/apps/web/src/services/asset-inventory/pages/CollectorDetailPage.vue b/apps/web/src/services/asset-inventory/pages/CollectorDetailPage.vue index fa73b5e487..c4db18f4ee 100644 --- a/apps/web/src/services/asset-inventory/pages/CollectorDetailPage.vue +++ b/apps/web/src/services/asset-inventory/pages/CollectorDetailPage.vue @@ -41,9 +41,9 @@ import type { CollectorGetParameters } from '@/schema/inventory/collector/api-ve import type { CollectorModel } from '@/schema/inventory/collector/model'; import { i18n } from '@/translations'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; import type { MenuId } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; @@ -87,6 +87,7 @@ const collectorJobState = collectorJobStore.$state; const collectorDataModalStore = useCollectorDataModalStore(); const collectorDetailPageStore = useCollectorDetailPageStore(); const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const route = useRoute(); @@ -100,9 +101,6 @@ watch(() => collectorFormState.originCollector, async (collector) => { const queryHelper = new QueryHelper(); -const storeState = reactive({ - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), -}); const state = reactive({ selectedMenuId: computed(() => { const reversedMatched = clone(route.matched).reverse(); @@ -113,7 +111,7 @@ const state = reactive({ } return targetMenuId; }), - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[state.selectedMenuId]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[state.selectedMenuId]?.write), isNotiVisible: computed(() => !collectorDetailPageStore.getters.isEditableCollector), isDomainAdmin: computed(() => userStore.getters.isDomainAdmin), loading: true, diff --git a/apps/web/src/services/asset-inventory/pages/SecurityPage.vue b/apps/web/src/services/asset-inventory/pages/SecurityPage.vue index 4157969357..e44048c72e 100644 --- a/apps/web/src/services/asset-inventory/pages/SecurityPage.vue +++ b/apps/web/src/services/asset-inventory/pages/SecurityPage.vue @@ -10,12 +10,11 @@ import { import { i18n } from '@/translations'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { CollectorReferenceMap } from '@/store/reference/collector-reference-store'; import type { ServiceAccountReferenceMap } from '@/store/reference/service-account-reference-store'; -import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { MENU_ID } from '@/lib/menu/config'; import { useGrantScopeGuard } from '@/common/composables/grant-scope-guard'; @@ -31,7 +30,7 @@ import { SERVICE_ACCOUNT_ROUTE } from '@/services/service-account/routes/route-c const allReferenceStore = useAllReferenceStore(); const securityPageStore = useSecurityPageStore(); const securityPageGetters = securityPageStore.getters; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const route = useRoute(); @@ -41,7 +40,6 @@ const storeState = reactive({ selectedCloudServiceType: computed(() => securityPageGetters.selectedCloudServiceType), serviceAccounts: computed(() => allReferenceStore.getters.serviceAccount), collectors: computed(() => allReferenceStore.getters.collector), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); const state = reactive({ pageParams: computed(() => route.params as unknown as CloudServiceDetailPageParams), @@ -70,8 +68,8 @@ const state = reactive({ } return result; }), - writableServiceAccount: computed(() => storeState.pageAccessPermissionMap[MENU_ID.SERVICE_ACCOUNT].write), - writableCollector: computed(() => storeState.pageAccessPermissionMap[MENU_ID.COLLECTOR].write), + writableServiceAccount: computed(() => authorizationStore.getters.pageAccessPermissionMap[MENU_ID.SERVICE_ACCOUNT]?.write), + writableCollector: computed(() => authorizationStore.getters.pageAccessPermissionMap[MENU_ID.COLLECTOR]?.write), }); const initData = async () => { diff --git a/apps/web/src/services/asset-inventory/pages/admin/AdminCollectorDetailPage.vue b/apps/web/src/services/asset-inventory/pages/admin/AdminCollectorDetailPage.vue index a0a6ab872a..cb51702a9e 100644 --- a/apps/web/src/services/asset-inventory/pages/admin/AdminCollectorDetailPage.vue +++ b/apps/web/src/services/asset-inventory/pages/admin/AdminCollectorDetailPage.vue @@ -121,9 +121,9 @@ import type { CollectorGetParameters } from '@/schema/inventory/collector/api-ve import type { CollectorModel } from '@/schema/inventory/collector/model'; import { i18n } from '@/translations'; -import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; + import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; import type { MenuId } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; @@ -168,7 +168,7 @@ const collectorJobState = collectorJobStore.$state; const collectorDataModalStore = useCollectorDataModalStore(); const collectorDetailPageStore = useCollectorDetailPageStore(); -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const route = useRoute(); @@ -182,9 +182,6 @@ watch(() => collectorFormState.originCollector, async (collector) => { const queryHelper = new QueryHelper(); -const storeState = reactive({ - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), -}); const state = reactive({ selectedMenuId: computed(() => { const reversedMatched = clone(route.matched).reverse(); @@ -195,7 +192,7 @@ const state = reactive({ } return targetMenuId; }), - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[state.selectedMenuId]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[state.selectedMenuId]?.write), isNotiVisible: computed(() => !collectorDetailPageStore.getters.isEditableCollector), loading: true, collector: computed(() => collectorFormState.originCollector), diff --git a/apps/web/src/services/auth/authenticator/index.ts b/apps/web/src/services/auth/authenticator/index.ts index 321277a92f..4917181454 100644 --- a/apps/web/src/services/auth/authenticator/index.ts +++ b/apps/web/src/services/auth/authenticator/index.ts @@ -4,6 +4,7 @@ import type { AuthType } from '@/api-clients/identity/user/schema/type'; import { SpaceRouter } from '@/router'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useDisplayStore } from '@/store/display/display-store'; import { useDomainStore } from '@/store/domain/domain-store'; import { useErrorStore } from '@/store/error/error-store'; @@ -17,13 +18,15 @@ abstract class Authenticator { const errorStore = useErrorStore(); const domainStore = useDomainStore(); const userStore = useUserStore(); + const authorizationStore = useAuthorizationStore(); const displayStore = useDisplayStore(pinia); - await userStore.signIn({ + await authorizationStore.signIn({ domainId: domainStore.state.domainId, credentials, authType, verify_code: verifyCode, }); + await userStore.getUserInfo(); await userWorkspaceStore.load(); displayStore.setIsSignInFailed(false); errorStore.reset(); @@ -31,11 +34,11 @@ abstract class Authenticator { static async signOut(): Promise { const errorStore = useErrorStore(); - const userStore = useUserStore(); + const authorizationStore = useAuthorizationStore(); const displayStore = useDisplayStore(pinia); try { if (SpaceRouter.router) { - userStore.signOut(); + authorizationStore.signOut(); const userWorkspaceStore = useUserWorkspaceStore(); userWorkspaceStore.reset(); displayStore.setIsSignInFailed(false); diff --git a/apps/web/src/services/cost-explorer/components/BudgetDetailHeading.vue b/apps/web/src/services/cost-explorer/components/BudgetDetailHeading.vue index 0b7226e2ac..b86e7e3583 100644 --- a/apps/web/src/services/cost-explorer/components/BudgetDetailHeading.vue +++ b/apps/web/src/services/cost-explorer/components/BudgetDetailHeading.vue @@ -11,9 +11,9 @@ import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { CostDataSourceReferenceMap } from '@/store/reference/cost-data-source-reference-store'; -import { useUserStore } from '@/store/user/user-store'; import { usePageEditableStatus } from '@/common/composables/page-editable-status'; @@ -34,15 +34,14 @@ const appContextStore = useAppContextStore(); const budgetPageStore = useBudgetDetailPageStore(); const budgetPageState = budgetPageStore.$state; const allReferenceStore = useAllReferenceStore(); -const userStore = useUserStore(); - +const authorizationStore = useAuthorizationStore(); const { hasReadWriteAccess } = usePageEditableStatus(); const router = useRouter(); const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), dataSourceMap: computed(() => allReferenceStore.getters.costDataSource), }); const state = reactive({ diff --git a/apps/web/src/services/cost-explorer/pages/BudgetMainPage.vue b/apps/web/src/services/cost-explorer/pages/BudgetMainPage.vue index d88d7a97b8..eec8a1d4d6 100644 --- a/apps/web/src/services/cost-explorer/pages/BudgetMainPage.vue +++ b/apps/web/src/services/cost-explorer/pages/BudgetMainPage.vue @@ -8,7 +8,7 @@ import { import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { SpaceRouter } from '@/router'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { usePageEditableStatus } from '@/common/composables/page-editable-status'; @@ -16,12 +16,12 @@ import BudgetListPeriodCustomModal from '@/services/cost-explorer/components/Bud import BudgetMainList from '@/services/cost-explorer/components/BudgetMainList.vue'; import { COST_EXPLORER_ROUTE } from '@/services/cost-explorer/routes/route-constant'; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const { hasReadWriteAccess } = usePageEditableStatus(); const storeState = reactive({ - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), }); const state = reactive({ diff --git a/apps/web/src/services/dashboards/DashboardsLSB.vue b/apps/web/src/services/dashboards/DashboardsLSB.vue index 5fa2365968..7f2ac62643 100644 --- a/apps/web/src/services/dashboards/DashboardsLSB.vue +++ b/apps/web/src/services/dashboards/DashboardsLSB.vue @@ -14,7 +14,7 @@ import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { MENU_ID } from '@/lib/menu/config'; @@ -40,7 +40,7 @@ import { DASHBOARDS_ROUTE } from '@/services/dashboards/routes/route-constant'; const appContextStore = useAppContextStore(); const favoriteStore = useFavoriteStore(); const favoriteGetters = favoriteStore.getters; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const { hasReadWriteAccess } = usePageEditableStatus(); @@ -58,7 +58,7 @@ const { } = useDashboardFolderQuery(); const storeState = reactive({ - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), favoriteItems: computed(() => favoriteGetters.dashboardItems), isAdminMode: computed(() => appContextStore.getters.isAdminMode), }); diff --git a/apps/web/src/services/dashboards/components/dashboard-detail/DashboardCloneModal.vue b/apps/web/src/services/dashboards/components/dashboard-detail/DashboardCloneModal.vue index 481d787e72..c22b70bcef 100644 --- a/apps/web/src/services/dashboards/components/dashboard-detail/DashboardCloneModal.vue +++ b/apps/web/src/services/dashboards/components/dashboard-detail/DashboardCloneModal.vue @@ -16,7 +16,7 @@ import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { showErrorMessage } from '@/lib/helper/notice-alert-helper'; @@ -29,7 +29,6 @@ import { ADMIN_DASHBOARDS_ROUTE } from '@/services/dashboards/routes/admin/route import { DASHBOARDS_ROUTE } from '@/services/dashboards/routes/route-constant'; - interface Props { visible: boolean; dashboardId: string; @@ -51,7 +50,8 @@ const queryClient = useQueryClient(); const router = useRouter(); const appContextStore = useAppContextStore(); -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); + const { forms: { name, @@ -74,8 +74,8 @@ const { }); const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), }); const state = reactive({ proxyVisible: useProxyValue('visible', props, emit), diff --git a/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderFormModal.vue b/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderFormModal.vue index 18b939028b..fb690b8048 100644 --- a/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderFormModal.vue +++ b/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderFormModal.vue @@ -19,6 +19,7 @@ import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useUserStore } from '@/store/user/user-store'; import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; @@ -31,7 +32,6 @@ import { useDashboardFolderQuery } from '@/services/dashboards/composables/use-d import { useDashboardPageControlStore } from '@/services/dashboards/stores/dashboard-page-control-store'; import { useDashboardTreeControlStore } from '@/services/dashboards/stores/dashboard-tree-control-store'; - interface Props { visible: boolean; folderId?: string; @@ -44,7 +44,9 @@ const emit = defineEmits<{(e: 'update:visible', visible: boolean): void; }>(); const appContextStore = useAppContextStore(); +const authorizationStore = useAuthorizationStore(); const userStore = useUserStore(); + const dashboardPageControlStore = useDashboardPageControlStore(); const dashboardPageControlState = dashboardPageControlStore.state; const dashboardTreeControlStore = useDashboardTreeControlStore(); @@ -62,7 +64,7 @@ const { const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), }); const state = reactive({ proxyVisible: useProxyValue('visible', props, emit), diff --git a/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderTreeItem.vue b/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderTreeItem.vue index d10aac68e3..b2987bf646 100644 --- a/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderTreeItem.vue +++ b/apps/web/src/services/dashboards/components/dashboard-folder/DashboardFolderTreeItem.vue @@ -17,7 +17,7 @@ import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import NewMark from '@/common/components/marks/NewMark.vue'; import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; @@ -56,7 +56,7 @@ const appContextStore = useAppContextStore(); const userWorkspaceStore = useUserWorkspaceStore(); const dashboardTreeControlStore = useDashboardTreeControlStore(); const dashboardTreeControlState = dashboardTreeControlStore.state; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const { getDashboardManageable, getFolderManageable } = useDashboardManageable(); const { getControlDashboardMenuItems, getControlFolderMenuItems } = useDashboardControlMenuHelper(); @@ -64,7 +64,7 @@ const { getControlDashboardMenuItems, getControlFolderMenuItems } = useDashboard const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), }); const state = reactive({ slicedLabels: computed(() => props.treeData.data?.labels?.slice(0, LABELS_LIMIT) || []), diff --git a/apps/web/src/services/dashboards/components/dashboard-main/DashboardBundleCloneModal.vue b/apps/web/src/services/dashboards/components/dashboard-main/DashboardBundleCloneModal.vue index 057d8f4a81..722f13a81c 100644 --- a/apps/web/src/services/dashboards/components/dashboard-main/DashboardBundleCloneModal.vue +++ b/apps/web/src/services/dashboards/components/dashboard-main/DashboardBundleCloneModal.vue @@ -30,6 +30,7 @@ import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { CostDataSourceReferenceMap } from '@/store/reference/cost-data-source-reference-store'; import { useUserStore } from '@/store/user/user-store'; @@ -89,6 +90,7 @@ const dashboardPageControlState = dashboardPageControlStore.state; const dashboardTreeControlStore = useDashboardTreeControlStore(); const dashboardTreeControlState = dashboardTreeControlStore.state; const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const allReferenceStore = useAllReferenceStore(); const { privateWidgetAPI } = usePrivateWidgetApi(); const { publicWidgetAPI } = usePublicWidgetApi(); @@ -109,7 +111,7 @@ const queryClient = useQueryClient(); const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), costDataSource: computed(() => allReferenceStore.getters.costDataSource), userId: computed(() => userStore.state.userId), }); diff --git a/apps/web/src/services/dashboards/components/dashboard-main/DashboardLSBTree.vue b/apps/web/src/services/dashboards/components/dashboard-main/DashboardLSBTree.vue index 705a089852..72a751db81 100644 --- a/apps/web/src/services/dashboards/components/dashboard-main/DashboardLSBTree.vue +++ b/apps/web/src/services/dashboards/components/dashboard-main/DashboardLSBTree.vue @@ -15,7 +15,7 @@ import type { PublicFolderModel } from '@/api-clients/dashboard/public-folder/sc import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; import { FAVORITE_TYPE } from '@/common/modules/favorites/favorite-button/type'; @@ -43,11 +43,11 @@ const route = useRoute(); const router = useRouter(); const dashboardPageControlStore = useDashboardPageControlStore(); const appContextStore = useAppContextStore(); -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), }); const { getControlDashboardMenuItems, getControlFolderMenuItems } = useDashboardControlMenuHelper(); diff --git a/apps/web/src/services/dashboards/composables/use-dashboard-search-query.ts b/apps/web/src/services/dashboards/composables/use-dashboard-search-query.ts index 237b0b8741..20ed7414cd 100644 --- a/apps/web/src/services/dashboards/composables/use-dashboard-search-query.ts +++ b/apps/web/src/services/dashboards/composables/use-dashboard-search-query.ts @@ -19,7 +19,7 @@ import { useScopedQuery } from '@/query/composables/use-scoped-query'; import { useServiceQueryKey } from '@/query/query-key/use-service-query-key'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; const DEFAULT_LIST_DATA = { results: [] }; @@ -35,12 +35,12 @@ export const useDashboardSearchQuery = ({ searchFilters }: UseDashboardSearchQue const { privateDashboardAPI } = usePrivateDashboardApi(); const appContextStore = useAppContextStore(); - const userStore = useUserStore(); + const authorizationStore = useAuthorizationStore(); const searchApiQueryHelper = new ApiQueryHelper(); const _state = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), }); const searchQuery = ref({}); diff --git a/apps/web/src/services/dashboards/pages/DashboardsMainPage.vue b/apps/web/src/services/dashboards/pages/DashboardsMainPage.vue index e03045eb2f..a9f901dcf7 100644 --- a/apps/web/src/services/dashboards/pages/DashboardsMainPage.vue +++ b/apps/web/src/services/dashboards/pages/DashboardsMainPage.vue @@ -23,7 +23,7 @@ import { SpaceRouter } from '@/router'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { replaceUrlQuery } from '@/lib/router-query-string'; @@ -53,7 +53,7 @@ const appContextStore = useAppContextStore(); const dashboardPageControlStore = useDashboardPageControlStore(); const dashboardTreeControlStore = useDashboardTreeControlStore(); const dashboardTreeControlState = dashboardTreeControlStore.state; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const { hasReadWriteAccess } = usePageEditableStatus(); @@ -80,8 +80,8 @@ const { } = useDashboardFolderQuery(); const storeState = reactive({ - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), isAdminMode: computed(() => appContextStore.getters.isAdminMode), }); diff --git a/apps/web/src/services/iam/pages/UserMainPage.vue b/apps/web/src/services/iam/pages/UserMainPage.vue index a1dcd64288..4d0b978897 100644 --- a/apps/web/src/services/iam/pages/UserMainPage.vue +++ b/apps/web/src/services/iam/pages/UserMainPage.vue @@ -7,7 +7,7 @@ import { ApiQueryHelper } from '@cloudforet/core-lib/space-connector/helper'; import { PHorizontalLayout } from '@cloudforet/mirinae'; import { useAppContextStore } from '@/store/app-context/app-context-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useGrantScopeGuard } from '@/common/composables/grant-scope-guard'; import { usePageEditableStatus } from '@/common/composables/page-editable-status'; @@ -28,14 +28,14 @@ import { useUserPageStore } from '@/services/iam/store/user-page-store'; const appContextStore = useAppContextStore(); const userPageStore = useUserPageStore(); const userPageState = userPageStore.state; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const { hasReadWriteAccess } = usePageEditableStatus(); const storeState = reactive({ isAdminMode: computed(() => appContextStore.getters.isAdminMode), globalGrantLoading: computed(() => appContextStore.getters.globalGrantLoading), - grantInfo: computed(() => userStore.state.currentGrantInfo), + grantInfo: computed(() => authorizationStore.state.currentGrantInfo), }); const userListApiQueryHelper = new ApiQueryHelper() diff --git a/apps/web/src/services/iam/store/user-page-store.ts b/apps/web/src/services/iam/store/user-page-store.ts index eaf2add9a6..75eed15da5 100644 --- a/apps/web/src/services/iam/store/user-page-store.ts +++ b/apps/web/src/services/iam/store/user-page-store.ts @@ -22,14 +22,14 @@ import type { WorkspaceUserGetParameters } from '@/api-clients/identity/workspac import type { WorkspaceUserListParameters } from '@/api-clients/identity/workspace-user/schema/api-verbs/list'; import type { WorkspaceUserModel, SummaryWorkspaceUserModel } from '@/api-clients/identity/workspace-user/schema/model'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import ErrorHandler from '@/common/composables/error/errorHandler'; import type { UserListItemType, ModalSettingState, ModalState } from '@/services/iam/types/user-type'; export const useUserPageStore = defineStore('page-user', () => { - const userStore = useUserStore(); + const authorizationStore = useAuthorizationStore(); const state = reactive({ isAdminMode: false, @@ -53,7 +53,7 @@ export const useUserPageStore = defineStore('page-user', () => { } as ModalState, }); const getters = reactive({ - isWorkspaceOwner: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), + isWorkspaceOwner: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_OWNER), selectedUsers: computed(():UserListItemType[] => { if (state.selectedIndices.length === 1 && !isEmpty(state.selectedUser)) return [state.selectedUser]; const users: UserListItemType[] = []; diff --git a/apps/web/src/services/landing/components/DomainLandingStartBanner.vue b/apps/web/src/services/landing/components/DomainLandingStartBanner.vue index fad60f0b05..435b4f806b 100644 --- a/apps/web/src/services/landing/components/DomainLandingStartBanner.vue +++ b/apps/web/src/services/landing/components/DomainLandingStartBanner.vue @@ -8,22 +8,16 @@ import { screens, PTextButton } from '@cloudforet/mirinae'; import type { WorkspaceModel } from '@/api-clients/identity/workspace/schema/model'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useUserStore } from '@/store/user/user-store'; - -import type { PageAccessMap } from '@/lib/access-control/config'; import { LANDING_ROUTE } from '@/services/landing/routes/route-constant'; const userWorkspaceStore = useUserWorkspaceStore(); const userWorkspaceGetters = userWorkspaceStore.getters; -const userStore = useUserStore(); - const router = useRouter(); const { width } = useWindowSize(); const storeState = reactive({ - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), workspaceList: computed(() => userWorkspaceGetters.workspaceList), }); const state = reactive({ diff --git a/apps/web/src/services/my-page/pages/UserAccountPage.vue b/apps/web/src/services/my-page/pages/UserAccountPage.vue index 268aecf9bd..542beaae41 100644 --- a/apps/web/src/services/my-page/pages/UserAccountPage.vue +++ b/apps/web/src/services/my-page/pages/UserAccountPage.vue @@ -19,6 +19,7 @@ import WorkspaceMemberImage from '@/assets/images/role/img_avatar_workspace-memb import WorkspaceOwnerImage from '@/assets/images/role/img_avatar_workspace-owner.png'; import { i18n } from '@/translations'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useDomainStore } from '@/store/domain/domain-store'; import { useUserStore } from '@/store/user/user-store'; @@ -32,13 +33,13 @@ import UserAccountNotificationEmail from '@/services/my-page/components/UserAcco - const domainStore = useDomainStore(); const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const storeState = reactive({ authType: computed(() => userStore.state.authType), baseRoleType: computed(() => userStore.state.roleType), - currentRoleType: computed(() => userStore.state.currentRoleInfo?.roleType), + currentRoleType: computed(() => authorizationStore.state.currentRoleInfo?.roleType), userId: computed(() => userStore.state.userId), }); const state = reactive({ diff --git a/apps/web/src/services/project/v1/stores/project-page-store.ts b/apps/web/src/services/project/v1/stores/project-page-store.ts index e9315e43f7..0774c56f7f 100644 --- a/apps/web/src/services/project/v1/stores/project-page-store.ts +++ b/apps/web/src/services/project/v1/stores/project-page-store.ts @@ -14,7 +14,7 @@ import type { ProjectUpdateParameters } from '@/api-clients/identity/project/sch import type { ProjectUpdateProjectTypeParameters } from '@/api-clients/identity/project/schema/api-verbs/update-project-type'; import type { ProjectModel } from '@/api-clients/identity/project/schema/model'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import getRandomId from '@/lib/random-id-generator'; @@ -24,13 +24,12 @@ import type { } from '@/common/modules/project/project-tree-type'; import { useProjectTree } from '@/common/modules/project/use-project-tree'; - const projectTreeHelper = useProjectTree(); export const useProjectPageStore = defineStore('page-project', () => { - const userStore = useUserStore(); + const authorizationStore = useAuthorizationStore(); const _state = reactive({ - currentRoleType: computed(() => userStore.state.currentRoleInfo?.roleType), + currentRoleType: computed(() => authorizationStore.state.currentRoleInfo?.roleType), }); const state = reactive({ projectTreeKey: getRandomId(), diff --git a/apps/web/src/services/project/v2/components/ProjectGroupMemberManagementModal.vue b/apps/web/src/services/project/v2/components/ProjectGroupMemberManagementModal.vue index 0aa0d15051..d86555394b 100644 --- a/apps/web/src/services/project/v2/components/ProjectGroupMemberManagementModal.vue +++ b/apps/web/src/services/project/v2/components/ProjectGroupMemberManagementModal.vue @@ -13,10 +13,11 @@ import type { SelectDropdownMenuItem } from '@cloudforet/mirinae/types/controls/ import { useProjectGroupApi } from '@/api-clients/identity/project-group/composables/use-project-group-api'; import type { ProjectGroupRemoveUsersParameters } from '@/api-clients/identity/project-group/schema/api-verbs/remove-users'; +import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useUserReferenceStore } from '@/store/reference/user-reference-store'; -import { useUserStore } from '@/store/user/user-store'; import { showSuccessMessage } from '@/lib/helper/notice-alert-helper'; @@ -31,8 +32,8 @@ const visible = computed(() => projectPageModalStore.state.projectGroupMemberMod const targetId = computed(() => projectPageModalStore.state.targetId); /* mode */ -const userStore = useUserStore(); -const readonlyMode = computed(() => userStore.state.currentRoleInfo?.roleType !== 'WORKSPACE_OWNER'); +const authorizationStore = useAuthorizationStore(); +const readonlyMode = computed(() => authorizationStore.state.currentRoleInfo?.roleType !== ROLE_TYPE.WORKSPACE_OWNER); /* project group users */ const { projectGroupAPI } = useProjectGroupApi(); diff --git a/apps/web/src/services/service-account/pages/ServiceAccountDetailPage.vue b/apps/web/src/services/service-account/pages/ServiceAccountDetailPage.vue index be0610068c..19317e2daa 100644 --- a/apps/web/src/services/service-account/pages/ServiceAccountDetailPage.vue +++ b/apps/web/src/services/service-account/pages/ServiceAccountDetailPage.vue @@ -20,12 +20,11 @@ import type { TrustedAccountGetParameters } from '@/api-clients/identity/trusted import type { TrustedAccountModel } from '@/api-clients/identity/trusted-account/schema/model'; import { useAppContextStore } from '@/store/app-context/app-context-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { ProjectReferenceMap } from '@/store/reference/project-reference-store'; import type { ProviderReferenceMap } from '@/store/reference/provider-reference-store'; -import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import type { MenuId } from '@/lib/menu/config'; import { MENU_ID } from '@/lib/menu/config'; @@ -58,7 +57,7 @@ const serviceAccountSchemaStore = useServiceAccountSchemaStore(); const serviceAccountPageStore = useServiceAccountPageStore(); const allReferenceStore = useAllReferenceStore(); const appContextStore = useAppContextStore(); -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const route = useRoute(); @@ -69,8 +68,7 @@ const storeState = reactive({ ? serviceAccountSchemaStore.getters.trustedAccountSchema?.options?.external_link : serviceAccountSchemaStore.getters.generalAccountSchema?.options?.external_link)), isAdminMode: computed(() => appContextStore.getters.isAdminMode), - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), }); const state = reactive({ loading: true, @@ -83,7 +81,7 @@ const state = reactive({ } return targetMenuId; }), - hasReadWriteAccess: computed(() => storeState.pageAccessPermissionMap[state.selectedMenuId]?.write), + hasReadWriteAccess: computed(() => authorizationStore.getters.pageAccessPermissionMap[state.selectedMenuId]?.write), originServiceAccountItem: computed(() => serviceAccountPageStore.state.originServiceAccountItem), serviceAccountType: computed(() => serviceAccountPageStore.state.serviceAccountType), isTrustedAccount: computed(() => state.serviceAccountType === ACCOUNT_TYPE.TRUSTED), diff --git a/apps/web/src/services/service-account/pages/ServiceAccountPage.vue b/apps/web/src/services/service-account/pages/ServiceAccountPage.vue index 263aff2a68..b7fb65daf7 100644 --- a/apps/web/src/services/service-account/pages/ServiceAccountPage.vue +++ b/apps/web/src/services/service-account/pages/ServiceAccountPage.vue @@ -33,13 +33,13 @@ import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { CURRENCY_SYMBOL } from '@/store/display/constant'; import type { Currency } from '@/store/display/type'; import { useAllReferenceStore } from '@/store/reference/all-reference-store'; import type { ProviderReferenceMap, ProviderItem } from '@/store/reference/provider-reference-store'; import { useUserStore } from '@/store/user/user-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { dynamicFieldsToExcelDataFields } from '@/lib/excel-export'; import { FILE_NAME_PREFIX } from '@/lib/excel-export/constant'; import { downloadExcel } from '@/lib/helper/file-download-helper'; @@ -83,6 +83,7 @@ const userWorkspaceStore = useUserWorkspaceStore(); const appContextStore = useAppContextStore(); const allReferenceStore = useAllReferenceStore(); const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const { hasReadWriteAccess } = usePageEditableStatus(); @@ -90,7 +91,6 @@ const { referenceFieldFormatter } = useReferenceFieldFormatter(); const storeState = reactive({ - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), currency: computed(() => serviceAccountPageGetters.currency), }); const state = reactive({ @@ -107,7 +107,7 @@ const state = reactive({ selectedProviderName: computed(() => state.providers[state.selectedProvider]?.label), timezone: computed(() => userStore.state.timezone || 'UTC'), grantLoading: computed(() => appContextStore.getters.globalGrantLoading), - currentGrantInfo: computed(() => userStore.state.currentGrantInfo), + currentGrantInfo: computed(() => authorizationStore.state.currentGrantInfo), isAgentModeAccount: computed(() => state.selectedProvider === 'kubernetes'), }); @@ -130,7 +130,7 @@ const typeOptionState = reactive({ }); const tableState = reactive({ - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), items: [] as ServiceAccountModel[] | TrustedAccountModel[], schema: computed(() => (tableState.isTrustedAccount ? serviceAccountSchemaState.trustedAccountTableSchema : serviceAccountSchemaState.generalAccountTableSchema)), diff --git a/apps/web/src/services/workspace-home/components/BookmarkBoard.vue b/apps/web/src/services/workspace-home/components/BookmarkBoard.vue index 780465e3ee..df56488e71 100644 --- a/apps/web/src/services/workspace-home/components/BookmarkBoard.vue +++ b/apps/web/src/services/workspace-home/components/BookmarkBoard.vue @@ -13,7 +13,7 @@ import type { UserConfigModel } from '@/api-clients/config/user-config/schema/mo import { ROLE_TYPE } from '@/api-clients/identity/role/constant'; import { i18n } from '@/translations'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; import { assetUrlConverter } from '@/lib/helper/asset-helper'; @@ -46,10 +46,10 @@ const bookmarkState = bookmarkStore.state; const workspaceHomePageStore = useWorkspaceHomePageStore(); const workspaceHomePageState = workspaceHomePageStore.state; const workspaceHomePageGetters = workspaceHomePageStore.getters; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const storeState = reactive({ - isWorkspaceMember: computed(() => userStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), + isWorkspaceMember: computed(() => authorizationStore.state.currentRoleInfo?.roleType === ROLE_TYPE.WORKSPACE_MEMBER), selectedBookmarks: computed(() => bookmarkState.selectedBookmarks), bookmarkType: computed(() => bookmarkState.bookmarkType), diff --git a/apps/web/src/services/workspace-home/components/WorkspaceInfo.vue b/apps/web/src/services/workspace-home/components/WorkspaceInfo.vue index b2c8a4ff1c..8d8e95d990 100644 --- a/apps/web/src/services/workspace-home/components/WorkspaceInfo.vue +++ b/apps/web/src/services/workspace-home/components/WorkspaceInfo.vue @@ -15,9 +15,8 @@ import { i18n } from '@/translations'; import { useAppContextStore } from '@/store/app-context/app-context-store'; import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store'; -import { useUserStore } from '@/store/user/user-store'; +import { useAuthorizationStore } from '@/store/authorization/authorization-store'; -import type { PageAccessMap } from '@/lib/access-control/config'; import { MENU_ID } from '@/lib/menu/config'; import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; @@ -51,7 +50,7 @@ const appPageStore = useAppPageStore(); const appContextStore = useAppContextStore(); const workspaceHomePageStore = useWorkspaceHomePageStore(); const workspaceHomePageState = workspaceHomePageStore.state; -const userStore = useUserStore(); +const authorizationStore = useAuthorizationStore(); const router = useRouter(); @@ -61,7 +60,6 @@ const storeState = reactive({ workspaceList: computed(() => userWorkspaceGetters.workspaceList), workspaceUserTotalCount: computed(() => workspaceHomePageState.workspaceUserTotalCount), appsTotalCount: computed(() => workspaceHomePageState.appsTotalCount), - pageAccessPermissionMap: computed(() => userStore.getters.pageAccessPermissionMap), }); const state = reactive({ selectedWorkspace: computed(() => storeState.workspaceList.find((workspace) => workspace.workspace_id === storeState.currentWorkspace?.workspace_id) || {} as WorkspaceModel), @@ -183,7 +181,7 @@ const routerToWorkspaceUser = (isOpenModal: boolean) => { - diff --git a/apps/web/src/services/alert-manager/v2/stores/service-list-page-store.ts b/apps/web/src/services/alert-manager/v2/stores/service-list-page-store.ts new file mode 100644 index 0000000000..b63fda6296 --- /dev/null +++ b/apps/web/src/services/alert-manager/v2/stores/service-list-page-store.ts @@ -0,0 +1,18 @@ +import { defineStore } from 'pinia'; + +export const useServiceListPageStore = defineStore('serviceListPage', { + state: () => ({ + unhealthyThisPage: 1, + unhealthyPageSize: 10, + healthyThisPage: 1, + healthyPageSize: 6, + }), + actions: { + setUnhealthyPage(page: number) { + this.unhealthyThisPage = page; + }, + setHealthyPage(page: number) { + this.healthyThisPage = page; + }, + }, +}); From c2d68c47d2d5de52f28aea0a265c444e12066041 Mon Sep 17 00:00:00 2001 From: nayeongkim Date: Fri, 9 May 2025 11:08:01 +0900 Subject: [PATCH 49/51] feat: provide escalation policy link for notification dependent on the escalation policy Signed-off-by: NaYeong,Kim --- .../ServiceDetailTabsNotifications.vue | 29 +--- ...viceDetailTabsNotificationsDeleteModal.vue | 129 ++++++++++++++++-- .../v2/composables/refined-table-data.ts | 25 ++++ .../console-translation-2.8.babel | 84 ++++++++++++ packages/language-pack/en.json | 4 + packages/language-pack/ja.json | 4 + packages/language-pack/ko.json | 4 + 7 files changed, 246 insertions(+), 33 deletions(-) diff --git a/apps/web/src/services/alert-manager/v2/components/ServiceDetailTabsNotifications.vue b/apps/web/src/services/alert-manager/v2/components/ServiceDetailTabsNotifications.vue index ce8f7b56f3..48324091be 100644 --- a/apps/web/src/services/alert-manager/v2/components/ServiceDetailTabsNotifications.vue +++ b/apps/web/src/services/alert-manager/v2/components/ServiceDetailTabsNotifications.vue @@ -23,12 +23,10 @@ import type { ValueHandlerMap } from '@cloudforet/mirinae/types/controls/search/ import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; import type { ServiceChannelListParameters } from '@/schema/alert-manager/service-channel/api-verbs/list'; import { - SERVICE_CHANNEL_FORWARD_TYPE, SERVICE_CHANNEL_STATE, SERVICE_CHANNEL_TYPE, } from '@/schema/alert-manager/service-channel/constants'; import type { ServiceChannelModel } from '@/schema/alert-manager/service-channel/model'; -import type { ServiceChannelDataType } from '@/schema/alert-manager/service-channel/type'; import type { ServiceModel } from '@/schema/alert-manager/service/model'; import { i18n } from '@/translations'; @@ -46,7 +44,7 @@ import ServiceDetailTabsNotificationsTableModal from '@/services/alert-manager/v2/components/ServiceDetailTabsNotificationsTableModal.vue'; import ServiceDetailTabsNotificationsUpdateModal from '@/services/alert-manager/v2/components/ServiceDetailTabsNotificationsUpdateModal.vue'; -import { alertManagerStateFormatter } from '@/services/alert-manager/v2/composables/refined-table-data'; +import { alertManagerStateFormatter, getProtocolInfo } from '@/services/alert-manager/v2/composables/refined-table-data'; import { SERVICE_TAB_HEIGHT } from '@/services/alert-manager/v2/constants/common-constant'; import { ALERT_EXCEL_FIELDS, @@ -56,7 +54,7 @@ import { import { ALERT_MANAGER_ROUTE } from '@/services/alert-manager/v2/routes/route-constant'; import { useServiceCreateFormStore } from '@/services/alert-manager/v2/stores/service-create-form-store'; import { useServiceDetailPageStore } from '@/services/alert-manager/v2/stores/service-detail-page-store'; -import type { ProtocolInfo, NotificationsModalType, ProtocolCardItemType } from '@/services/alert-manager/v2/types/alert-manager-type'; +import type { NotificationsModalType, ProtocolCardItemType } from '@/services/alert-manager/v2/types/alert-manager-type'; interface Props { tableHeight: number; @@ -140,25 +138,6 @@ const notificationsListApiQueryHelper = new ApiQueryHelper().setSort('created_at const queryTagHelper = useQueryTags({ keyItemSets: NOTIFICATION_MANAGEMENT_TABLE_KEY_ITEMS_SETS }); const { queryTags } = queryTagHelper; -const getProtocolInfo = (id: string, data?: ServiceChannelDataType): ProtocolInfo => { - if (id === 'forward') { - if (data?.FORWARD_TYPE === SERVICE_CHANNEL_FORWARD_TYPE.ALL_MEMBER) { - return { name: i18n.t('ALERT_MANAGER.NOTIFICATIONS.NOTIFY_TO_ALL_MEMBER') }; - } - if (data?.FORWARD_TYPE === SERVICE_CHANNEL_FORWARD_TYPE.USER_GROUP) { - return { name: i18n.t('ALERT_MANAGER.NOTIFICATIONS.NOTIFY_TO_USER_GROUP') }; - } - if (data?.FORWARD_TYPE === SERVICE_CHANNEL_FORWARD_TYPE.USER) { - return { name: i18n.t('ALERT_MANAGER.NOTIFICATIONS.NOTIFY_TO_USER') }; - } - return { name: i18n.t('ALERT_MANAGER.NOTIFICATIONS.ASSOCIATED_MEMBER') }; - } - const protocol = storeState.notificationProtocolList.find((item) => item.protocol_id === id); - return { - name: protocol?.name || '', - icon: protocol?.icon || '', - }; -}; const handleCloseModal = () => { state.selectIndex = undefined; fetchNotificationList(); @@ -298,11 +277,11 @@ onUnmounted(() => { height="1rem" /> - {{ getProtocolInfo(value, item.data).name }} + {{ getProtocolInfo(value, storeState.notificationProtocolList, item.data).name }} diff --git a/apps/web/src/services/alert-manager/v2/components/ServiceDetailTabsNotificationsDeleteModal.vue b/apps/web/src/services/alert-manager/v2/components/ServiceDetailTabsNotificationsDeleteModal.vue index 112aae553a..aadccceda3 100644 --- a/apps/web/src/services/alert-manager/v2/components/ServiceDetailTabsNotificationsDeleteModal.vue +++ b/apps/web/src/services/alert-manager/v2/components/ServiceDetailTabsNotificationsDeleteModal.vue @@ -1,18 +1,32 @@ diff --git a/apps/web/src/services/alert-manager/v2/composables/refined-table-data.ts b/apps/web/src/services/alert-manager/v2/composables/refined-table-data.ts index c97000f82e..2900647bdb 100644 --- a/apps/web/src/services/alert-manager/v2/composables/refined-table-data.ts +++ b/apps/web/src/services/alert-manager/v2/composables/refined-table-data.ts @@ -1,4 +1,9 @@ +import { SERVICE_CHANNEL_FORWARD_TYPE } from '@/schema/alert-manager/service-channel/constants'; +import type { ServiceChannelDataType } from '@/schema/alert-manager/service-channel/type'; +import { i18n } from '@/translations'; + import { ALERT_MANAGER_STATE_COLOR } from '@/services/alert-manager/v2/constants/common-constant'; +import type { ProtocolCardItemType, ProtocolInfo } from '@/services/alert-manager/v2/types/alert-manager-type'; const colorBindFactory = (colorMapping, textFnc) => (value) => ({ text: textFnc(value), @@ -6,3 +11,23 @@ const colorBindFactory = (colorMapping, textFnc) => (value) => ({ }); export const alertManagerStateFormatter = colorBindFactory(ALERT_MANAGER_STATE_COLOR, (value) => value.toLowerCase()); + +export const getProtocolInfo = (id: string, list: ProtocolCardItemType[], data?: ServiceChannelDataType): ProtocolInfo => { + if (id === 'forward') { + if (data?.FORWARD_TYPE === SERVICE_CHANNEL_FORWARD_TYPE.ALL_MEMBER) { + return { name: i18n.t('ALERT_MANAGER.NOTIFICATIONS.NOTIFY_TO_ALL_MEMBER') }; + } + if (data?.FORWARD_TYPE === SERVICE_CHANNEL_FORWARD_TYPE.USER_GROUP) { + return { name: i18n.t('ALERT_MANAGER.NOTIFICATIONS.NOTIFY_TO_USER_GROUP') }; + } + if (data?.FORWARD_TYPE === SERVICE_CHANNEL_FORWARD_TYPE.USER) { + return { name: i18n.t('ALERT_MANAGER.NOTIFICATIONS.NOTIFY_TO_USER') }; + } + return { name: i18n.t('ALERT_MANAGER.NOTIFICATIONS.ASSOCIATED_MEMBER') }; + } + const protocol = list.find((item) => item.protocol_id === id); + return { + name: protocol?.name || '', + icon: protocol?.icon || '', + }; +}; diff --git a/packages/language-pack/console-translation-2.8.babel b/packages/language-pack/console-translation-2.8.babel index 8e9e6979ae..e6222317ab 100644 --- a/packages/language-pack/console-translation-2.8.babel +++ b/packages/language-pack/console-translation-2.8.babel @@ -5757,6 +5757,90 @@ + + DELETE_NOTIFICATION_DESC + false + + + + + + en-US + false + + + ja-JP + false + + + ko-KR + false + + + + + DELETE_NOTIFICATION_DESC_BOLD + false + + + + + + en-US + false + + + ja-JP + false + + + ko-KR + false + + + + + DELETE_NOTIFICATION_TITLE + false + + + + + + en-US + false + + + ja-JP + false + + + ko-KR + false + + + + + ESCALATION_LINK_TEXT + false + + + + + + en-US + false + + + ja-JP + false + + + ko-KR + false + + + NOTIFICATION_LINK_TEXT false diff --git a/packages/language-pack/en.json b/packages/language-pack/en.json index c2b5936c82..56a35adf1f 100644 --- a/packages/language-pack/en.json +++ b/packages/language-pack/en.json @@ -282,6 +282,10 @@ "DELETE_MEMBER_DESC": "You must first disconnect each connected 'Notification' before deleting the [{name}] Member.", "DELETE_MEMBER_DESC_BOLD": "Please remove all [{name}] Members from the 'Notification' and try again.", "DELETE_MEMBER_TITLE": "Remove Service Member", + "DELETE_NOTIFICATION_DESC": "You must first disconnect each connected 'Escalation Policy' before deleting the [{name}] Notification.", + "DELETE_NOTIFICATION_DESC_BOLD": "Please remove all [{name}] Notifications from the 'Escalation Policy' and try again.", + "DELETE_NOTIFICATION_TITLE": "Delete Notification Channel", + "ESCALATION_LINK_TEXT": "Go to Escalation Policy", "NOTIFICATION_LINK_TEXT": "Go to Connected Notifications", "REMOVE": "Remove" }, diff --git a/packages/language-pack/ja.json b/packages/language-pack/ja.json index e1c73aa7ae..51830c3efe 100644 --- a/packages/language-pack/ja.json +++ b/packages/language-pack/ja.json @@ -282,6 +282,10 @@ "DELETE_MEMBER_DESC": "[{name}]メンバーを削除する前に、接続されている各「通知」の接続を解除する必要があります。", "DELETE_MEMBER_DESC_BOLD": "Please remove all [{name}] Members from the 'Notification' and try again.\" → \"「通知」から[{name}]メンバーをすべて削除してから、もう一度お試しください。", "DELETE_MEMBER_TITLE": "サービスメンバー削除", + "DELETE_NOTIFICATION_DESC": "[{name}]通知を削除する前に、接続されている各「エスカレーションポリシー」の接続を解除する必要があります。", + "DELETE_NOTIFICATION_DESC_BOLD": "「エスカレーションポリシー」から[{name}]通知をすべて削除してから、もう一度お試しください。", + "DELETE_NOTIFICATION_TITLE": "通知チャネル削除", + "ESCALATION_LINK_TEXT": "エスカレーションポリシーへ移動", "NOTIFICATION_LINK_TEXT": "接続された通知へ移動", "REMOVE": "削除" }, diff --git a/packages/language-pack/ko.json b/packages/language-pack/ko.json index 342796e548..ab92a79b0a 100644 --- a/packages/language-pack/ko.json +++ b/packages/language-pack/ko.json @@ -282,6 +282,10 @@ "DELETE_MEMBER_DESC": "[{name}] 멤버를 삭제하기 전에 연결된 각 '알림'의 연결을 해제해야 합니다.", "DELETE_MEMBER_DESC_BOLD": "'알림'에서 [{name}] 멤버를 모두 제거한 후 다시 시도해주세요.", "DELETE_MEMBER_TITLE": "서비스 멤버 제거", + "DELETE_NOTIFICATION_DESC": "[{name}] 알림을 삭제하기 전에 연결된 각 '에스컬레이션 정책'의 연결을 해제해야 합니다.", + "DELETE_NOTIFICATION_DESC_BOLD": "에스컬레이션 정책'에서 [{name}] 알림을 모두 제거한 후 다시 시도해주세요.", + "DELETE_NOTIFICATION_TITLE": "알림 채널 삭제", + "ESCALATION_LINK_TEXT": "에스컬레이션 정책으로 이동", "NOTIFICATION_LINK_TEXT": "연결된 알림으로 이동", "REMOVE": "제거" }, From b960d59aee043a920b2341f8bd8dd87fb2565ec4 Mon Sep 17 00:00:00 2001 From: nayeongkim Date: Fri, 9 May 2025 11:22:45 +0900 Subject: [PATCH 50/51] fix: fixed fetch service channel bug Signed-off-by: NaYeong,Kim --- .../v2/components/ServiceDetailMemberModal.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/web/src/services/alert-manager/v2/components/ServiceDetailMemberModal.vue b/apps/web/src/services/alert-manager/v2/components/ServiceDetailMemberModal.vue index ab59b1464e..87aa42d81d 100644 --- a/apps/web/src/services/alert-manager/v2/components/ServiceDetailMemberModal.vue +++ b/apps/web/src/services/alert-manager/v2/components/ServiceDetailMemberModal.vue @@ -1,6 +1,6 @@