@cdmbase/wiki-browser 12.0.18-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/lib/components/Logo.d.ts +4 -0
- package/lib/components/Logo.d.ts.map +1 -0
- package/lib/components/Logo.js +16 -0
- package/lib/components/Logo.js.map +1 -0
- package/lib/components/help/SidebarSearch.d.ts +8 -0
- package/lib/components/help/SidebarSearch.d.ts.map +1 -0
- package/lib/components/help/SidebarSearch.js +111 -0
- package/lib/components/help/SidebarSearch.js.map +1 -0
- package/lib/components/help/index.d.ts +2 -0
- package/lib/components/help/index.d.ts.map +1 -0
- package/lib/components/landing/FeatureCard.d.ts +13 -0
- package/lib/components/landing/FeatureCard.d.ts.map +1 -0
- package/lib/components/landing/FeatureCard.js +85 -0
- package/lib/components/landing/FeatureCard.js.map +1 -0
- package/lib/components/landing/QuickLinkCard.d.ts +8 -0
- package/lib/components/landing/QuickLinkCard.d.ts.map +1 -0
- package/lib/components/landing/QuickLinkCard.js +26 -0
- package/lib/components/landing/QuickLinkCard.js.map +1 -0
- package/lib/components/landing/SearchInput.d.ts +10 -0
- package/lib/components/landing/SearchInput.d.ts.map +1 -0
- package/lib/components/landing/SearchInput.js +223 -0
- package/lib/components/landing/SearchInput.js.map +1 -0
- package/lib/components/landing/index.d.ts +4 -0
- package/lib/components/landing/index.d.ts.map +1 -0
- package/lib/components/welcome.d.ts +3 -0
- package/lib/components/welcome.d.ts.map +1 -0
- package/lib/compute.d.ts +4 -0
- package/lib/compute.d.ts.map +1 -0
- package/lib/compute.js +96 -0
- package/lib/compute.js.map +1 -0
- package/lib/config/env-config.d.ts +4 -0
- package/lib/config/env-config.d.ts.map +1 -0
- package/lib/config/env-config.js +7 -0
- package/lib/config/env-config.js.map +1 -0
- package/lib/docs.config.d.ts +48 -0
- package/lib/docs.config.d.ts.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/loaders/search.d.ts +1 -0
- package/lib/loaders/search.d.ts.map +1 -0
- package/lib/module.d.ts +4 -0
- package/lib/module.d.ts.map +1 -0
- package/lib/module.js +11 -0
- package/lib/module.js.map +1 -0
- package/lib/pages/ArticlePage/ArticlePage.d.ts +4 -0
- package/lib/pages/ArticlePage/ArticlePage.d.ts.map +1 -0
- package/lib/pages/ArticlePage/ArticlePage.js +222 -0
- package/lib/pages/ArticlePage/ArticlePage.js.map +1 -0
- package/lib/pages/ArticlePage/index.d.ts +3 -0
- package/lib/pages/ArticlePage/index.d.ts.map +1 -0
- package/lib/pages/ArticlePage/index.js +3 -0
- package/lib/pages/ArticlePage/index.js.map +1 -0
- package/lib/pages/CategoryCollection/CategoryCollection.d.ts +4 -0
- package/lib/pages/CategoryCollection/CategoryCollection.d.ts.map +1 -0
- package/lib/pages/CategoryCollection/CategoryCollection.js +103 -0
- package/lib/pages/CategoryCollection/CategoryCollection.js.map +1 -0
- package/lib/pages/CategoryCollection/index.d.ts +3 -0
- package/lib/pages/CategoryCollection/index.d.ts.map +1 -0
- package/lib/pages/CategoryCollection/index.js +3 -0
- package/lib/pages/CategoryCollection/index.js.map +1 -0
- package/lib/pages/Help/HelpIndex.d.ts +4 -0
- package/lib/pages/Help/HelpIndex.d.ts.map +1 -0
- package/lib/pages/Help/HelpIndex.js +44 -0
- package/lib/pages/Help/HelpIndex.js.map +1 -0
- package/lib/pages/Help/index.d.ts +4 -0
- package/lib/pages/Help/index.d.ts.map +1 -0
- package/lib/pages/Help/index.js +226 -0
- package/lib/pages/Help/index.js.map +1 -0
- package/lib/pages/Landing/index.d.ts +3 -0
- package/lib/pages/Landing/index.d.ts.map +1 -0
- package/lib/pages/Landing/index.js +281 -0
- package/lib/pages/Landing/index.js.map +1 -0
- package/lib/routes.json +2533 -0
- package/lib/seo.d.ts +22 -0
- package/lib/seo.d.ts.map +1 -0
- package/lib/slot-fill/FooterFill.d.ts +3 -0
- package/lib/slot-fill/FooterFill.d.ts.map +1 -0
- package/lib/slot-fill/FooterFill.js +18 -0
- package/lib/slot-fill/FooterFill.js.map +1 -0
- package/lib/slot-fill/LogoFill.d.ts +5 -0
- package/lib/slot-fill/LogoFill.d.ts.map +1 -0
- package/lib/slot-fill/LogoFill.js +74 -0
- package/lib/slot-fill/LogoFill.js.map +1 -0
- package/lib/slot-fill/consts.d.ts +5 -0
- package/lib/slot-fill/consts.d.ts.map +1 -0
- package/lib/slot-fill/consts.js +1 -0
- package/lib/slot-fill/consts.js.map +1 -0
- package/lib/slot-fill/index.d.ts +4 -0
- package/lib/slot-fill/index.d.ts.map +1 -0
- package/lib/templates/assets/images/add-link-frontend.png +0 -0
- package/lib/templates/assets/images/add-package-backend.png +0 -0
- package/lib/templates/assets/images/add-to-backend-module.png +0 -0
- package/lib/templates/assets/images/add-upload-client-frontend.png +0 -0
- package/lib/templates/assets/images/additional-parameters.png +0 -0
- package/lib/templates/assets/images/aeh-implementation.png +0 -0
- package/lib/templates/assets/images/aeh-usage.png +0 -0
- package/lib/templates/assets/images/apollo-client/recommendation_cache_mgmt.png +0 -0
- package/lib/templates/assets/images/app-deploy-new-version/jenkins1.PNG +0 -0
- package/lib/templates/assets/images/app-deploy-new-version/jenkins2.PNG +0 -0
- package/lib/templates/assets/images/auth-wrapper-code.png +0 -0
- package/lib/templates/assets/images/cdebase.png +0 -0
- package/lib/templates/assets/images/cdm-locales-directory.png +0 -0
- package/lib/templates/assets/images/client-settings.png +0 -0
- package/lib/templates/assets/images/codegen_file_update.png +0 -0
- package/lib/templates/assets/images/configuration.png +0 -0
- package/lib/templates/assets/images/copy-plugin.png +0 -0
- package/lib/templates/assets/images/docusaurus.png +0 -0
- package/lib/templates/assets/images/error-link.png +0 -0
- package/lib/templates/assets/images/error-sample.png +0 -0
- package/lib/templates/assets/images/extension copy.png +0 -0
- package/lib/templates/assets/images/extension.png +0 -0
- package/lib/templates/assets/images/graphql/graphql-folder-backend.png +0 -0
- package/lib/templates/assets/images/graphql/graphql-folder-with-gql.png +0 -0
- package/lib/templates/assets/images/i18n-config.png +0 -0
- package/lib/templates/assets/images/image.png +0 -0
- package/lib/templates/assets/images/logo.svg +10 -0
- package/lib/templates/assets/images/logo1.svg +1 -0
- package/lib/templates/assets/images/modify-upload-false-server.png +0 -0
- package/lib/templates/assets/images/navigation-auth-enabled.png +0 -0
- package/lib/templates/assets/images/org-dashboard-navigation.png +0 -0
- package/lib/templates/assets/images/org-navigation.png +0 -0
- package/lib/templates/assets/images/preferences_graphql_type.png +0 -0
- package/lib/templates/assets/images/provider.png +0 -0
- package/lib/templates/assets/images/route-config.png +0 -0
- package/lib/templates/assets/images/service-accounts.png +0 -0
- package/lib/templates/assets/images/source-code/source-code-environments.png +0 -0
- package/lib/templates/assets/images/source-code/source-code-organization.png +0 -0
- package/lib/templates/assets/images/spin-clone-develop-deployment/jenkins-changes.png +0 -0
- package/lib/templates/assets/images/spin-clone-develop-deployment/lerna-changes.png +0 -0
- package/lib/templates/assets/images/spin-clone-develop-deployment/root-package-json-changes.png +0 -0
- package/lib/templates/assets/images/spin-clone-develop-deployment/values-dev-changes.png +0 -0
- package/lib/templates/assets/images/sso-mappers.png +0 -0
- package/lib/templates/assets/images/sso-picture-mapper.png +0 -0
- package/lib/templates/assets/images/sso-settings.png +0 -0
- package/lib/templates/assets/images/timesheet_apollo_cache.png +0 -0
- package/lib/templates/assets/images/timesheet_query.png +0 -0
- package/lib/templates/assets/images/tutorial/docsVersionDropdown.png +0 -0
- package/lib/templates/assets/images/tutorial/localeDropdown.png +0 -0
- package/lib/templates/assets/images/unauthenticated.png +0 -0
- package/lib/templates/assets/images/undraw_docusaurus_mountain.svg +170 -0
- package/lib/templates/assets/images/undraw_docusaurus_react.svg +169 -0
- package/lib/templates/assets/images/undraw_docusaurus_tree.svg +1 -0
- package/lib/templates/assets/images/vite-plugin-config.png +0 -0
- package/lib/templates/content/docs/Generators/Project/generate-fullproject.md +12 -0
- package/lib/templates/content/docs/LLM/Logger.llm.md +194 -0
- package/lib/templates/content/docs/LLM/backend-proxies-services-llm.md +2687 -0
- package/lib/templates/content/docs/LLM/backend-service-llm.md +3384 -0
- package/lib/templates/content/docs/LLM/db_migration_llm.md +954 -0
- package/lib/templates/content/docs/LLM/frontend/REMIX-15.3-upgrade-llm.md +1245 -0
- package/lib/templates/content/docs/LLM/inngest/INNGEST_FUNCTION_DEVELOPMENT_GUIDE_LLM.md +1241 -0
- package/lib/templates/content/docs/LLM/inngest/INNGEST_NAMESPACE_LLM.md +384 -0
- package/lib/templates/content/docs/LLM/llm_workflow_namespace.md +384 -0
- package/lib/templates/content/docs/LLM/organization-components-form-llm.md +1395 -0
- package/lib/templates/content/docs/LLM/page-component-llm.md +173 -0
- package/lib/templates/content/docs/LLM/preferences-settings-llm.md +2781 -0
- package/lib/templates/content/docs/LLM/tailwind-css-llm.md +502 -0
- package/lib/templates/content/docs/UI/SchemaBasedUI.md +334 -0
- package/lib/templates/content/docs/UI/SlotFillComponent.md +334 -0
- package/lib/templates/content/docs/adminide-modules/account/auth0-login.md +31 -0
- package/lib/templates/content/docs/adminide-modules/account/index.md +14 -0
- package/lib/templates/content/docs/adminide-modules/account/keycloak-remix-setup.md +86 -0
- package/lib/templates/content/docs/adminide-modules/account/remix-auth-setup.md +79 -0
- package/lib/templates/content/docs/adminide-modules/account/various-auth-qatest.md +157 -0
- package/lib/templates/content/docs/adminide-modules/api-builders/graphql.md +906 -0
- package/lib/templates/content/docs/adminide-modules/billing/payments/index.md +14 -0
- package/lib/templates/content/docs/adminide-modules/billing/payments/stripe/index.md +14 -0
- package/lib/templates/content/docs/adminide-modules/billing/payments/stripe/settingup-stripe-locally.md +25 -0
- package/lib/templates/content/docs/adminide-modules/billing/tier-config.md +293 -0
- package/lib/templates/content/docs/adminide-modules/connectors/Connector.md +207 -0
- package/lib/templates/content/docs/adminide-modules/file-upload/index.md +16 -0
- package/lib/templates/content/docs/adminide-modules/file-upload/setup.md +435 -0
- package/lib/templates/content/docs/adminide-modules/file-upload/upload-file-using-signed-url.md +161 -0
- package/lib/templates/content/docs/adminide-modules/preferences/AddAdditionalPermissions.md +151 -0
- package/lib/templates/content/docs/adminide-modules/preferences/Configuration.md +241 -0
- package/lib/templates/content/docs/adminide-modules/preferences/Policy-Configuration.md +61 -0
- package/lib/templates/content/docs/adminide-modules/preferences/UI-components/ResourceSettingsLoader.md +319 -0
- package/lib/templates/content/docs/adminide-modules/preferences/contribute_scope_target.md +280 -0
- package/lib/templates/content/docs/adminide-modules/preferences/generate-urii.md +94 -0
- package/lib/templates/content/docs/adminide-modules/preferences/index.md +28 -0
- package/lib/templates/content/docs/adminide-modules/preferences/machine-configuration.md +157 -0
- package/lib/templates/content/docs/adminide-modules/preferences/pageSettings/generateCdecodeUri.md +1289 -0
- package/lib/templates/content/docs/adminide-modules/preferences/pageSettings/migratingFromUseSettings.md +215 -0
- package/lib/templates/content/docs/adminide-modules/preferences/permissions/Roles-Permissions.md +72 -0
- package/lib/templates/content/docs/adminide-modules/preferences/permissions/settingUserPermissions.md +139 -0
- package/lib/templates/content/docs/adminide-modules/preferences/preference-dependency.md +138 -0
- package/lib/templates/content/docs/adminide-modules/preferences/route-based-configuration.md +41 -0
- package/lib/templates/content/docs/adminide-modules/preferences/schema-configuration.md +71 -0
- package/lib/templates/content/docs/adminide-modules/preferences/supported.md +24 -0
- package/lib/templates/content/docs/adminide-modules/preferences/useSettingsLoader.md +248 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/auth-providers.md +1317 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/keycloak-guide.md +543 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenant-based-authentication.md +846 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenant-management.md +708 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenants.md +1117 -0
- package/lib/templates/content/docs/chrome-extension/index.md +14 -0
- package/lib/templates/content/docs/chrome-extension/setup.md +30 -0
- package/lib/templates/content/docs/contributing/adding-package.md +23 -0
- package/lib/templates/content/docs/contributing/adding_new_modules.md +99 -0
- package/lib/templates/content/docs/contributing/architecture-updates.md +19 -0
- package/lib/templates/content/docs/contributing/avoid-using-promises-ui.md +116 -0
- package/lib/templates/content/docs/contributing/coding-guidelines.md +111 -0
- package/lib/templates/content/docs/contributing/do-and-dont.md +42 -0
- package/lib/templates/content/docs/contributing/faq.md +22 -0
- package/lib/templates/content/docs/contributing/folder-setup/browser.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/config.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/containers-server.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/core.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/graphql.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/index.md +30 -0
- package/lib/templates/content/docs/contributing/folder-setup/module.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/server.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/services.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/store.md +12 -0
- package/lib/templates/content/docs/contributing/frontend-coding.md +30 -0
- package/lib/templates/content/docs/contributing/git-subtree-sharing.md +73 -0
- package/lib/templates/content/docs/contributing/graphql-subscriptions.md +69 -0
- package/lib/templates/content/docs/contributing/how-to-contribute.md +30 -0
- package/lib/templates/content/docs/contributing/how_to_check_pure_esm.md +29 -0
- package/lib/templates/content/docs/contributing/index.md +60 -0
- package/lib/templates/content/docs/contributing/installation-issues.md +23 -0
- package/lib/templates/content/docs/contributing/keyboard-shortcut.md +131 -0
- package/lib/templates/content/docs/contributing/language/locale-support.md +12 -0
- package/lib/templates/content/docs/contributing/lerna-build-tools.md +516 -0
- package/lib/templates/content/docs/contributing/lerna-yarn-workspaces.md +95 -0
- package/lib/templates/content/docs/contributing/lint-and-formatter.md +20 -0
- package/lib/templates/content/docs/contributing/mobile-setup.md +16 -0
- package/lib/templates/content/docs/contributing/project-setup.md +233 -0
- package/lib/templates/content/docs/contributing/react/index.md +14 -0
- package/lib/templates/content/docs/contributing/react/lazy-component.md +70 -0
- package/lib/templates/content/docs/contributing/run-various-options.md +124 -0
- package/lib/templates/content/docs/contributing/schema-first-graphql-types.md +37 -0
- package/lib/templates/content/docs/contributing/source-code-organization.md +57 -0
- package/lib/templates/content/docs/contributing/staging-docker.md +88 -0
- package/lib/templates/content/docs/contributing/third-party/apollo-client-v3-tutorials.md +28 -0
- package/lib/templates/content/docs/contributing/third-party/index.md +18 -0
- package/lib/templates/content/docs/contributing/typescript-contribution.md +16 -0
- package/lib/templates/content/docs/devops/app-deploy-new-version.md +30 -0
- package/lib/templates/content/docs/devops/index.md +14 -0
- package/lib/templates/content/docs/devops/mobile-jenkins-build.md +40 -0
- package/lib/templates/content/docs/devops/versioning-the-project.md +128 -0
- package/lib/templates/content/docs/error-handler/application-error-handler.md +40 -0
- package/lib/templates/content/docs/error-handler/error-handling.md +26 -0
- package/lib/templates/content/docs/error-handler/index.md +16 -0
- package/lib/templates/content/docs/error-handler/logging-errors.md +14 -0
- package/lib/templates/content/docs/feature-api/copy-operation.md +427 -0
- package/lib/templates/content/docs/feature-api/feature-browser/assets.md +46 -0
- package/lib/templates/content/docs/feature-api/feature-browser/auth-permissions.md +12 -0
- package/lib/templates/content/docs/feature-api/feature-browser/feature.md +131 -0
- package/lib/templates/content/docs/feature-api/feature-browser/index.md +22 -0
- package/lib/templates/content/docs/feature-api/feature-browser/routes-menu.md +110 -0
- package/lib/templates/content/docs/feature-api/feature-browser/routing-convention.md +124 -0
- package/lib/templates/content/docs/feature-api/feature-browser/routing.md +338 -0
- package/lib/templates/content/docs/feature-api/feature-mobile/auth-permissions.md +20 -0
- package/lib/templates/content/docs/feature-api/feature-mobile/feature.md +130 -0
- package/lib/templates/content/docs/feature-api/feature-mobile/index.md +18 -0
- package/lib/templates/content/docs/feature-api/feature-mobile/navigation.md +187 -0
- package/lib/templates/content/docs/feature-api/feature-server/Scheduling.md +44 -0
- package/lib/templates/content/docs/feature-api/feature-server/dataloader.md +320 -0
- package/lib/templates/content/docs/feature-api/feature-server/dependency-injection.md +81 -0
- package/lib/templates/content/docs/feature-api/feature-server/feature.md +65 -0
- package/lib/templates/content/docs/feature-api/feature-server/generic-dataloader.md +135 -0
- package/lib/templates/content/docs/feature-api/feature-server/index.md +40 -0
- package/lib/templates/content/docs/feature-api/feature-server/migration.md +127 -0
- package/lib/templates/content/docs/feature-api/feature-server/mongo-model.md +72 -0
- package/lib/templates/content/docs/feature-api/feature-server/permissions.md +12 -0
- package/lib/templates/content/docs/feature-api/feature-server/policies.md +57 -0
- package/lib/templates/content/docs/feature-api/feature-server/preferences.md +57 -0
- package/lib/templates/content/docs/feature-api/feature-server/repositories.md +114 -0
- package/lib/templates/content/docs/feature-api/feature-server/resolvers.md +126 -0
- package/lib/templates/content/docs/feature-api/feature-server/rules.md +132 -0
- package/lib/templates/content/docs/feature-api/feature-server/schema.md +12 -0
- package/lib/templates/content/docs/feature-api/feature-server/services.md +102 -0
- package/lib/templates/content/docs/feature-api/feature-server/setup-resource-crud.md +359 -0
- package/lib/templates/content/docs/feature-api/index.md +18 -0
- package/lib/templates/content/docs/graphql/apolloClient-mutation.md +94 -0
- package/lib/templates/content/docs/graphql/index.md +14 -0
- package/lib/templates/content/docs/graphql/scalars.md +15 -0
- package/lib/templates/content/docs/help/index.md +14 -0
- package/lib/templates/content/docs/help/intro.md +16 -0
- package/lib/templates/content/docs/intl/ant-design-menu-translation.md +74 -0
- package/lib/templates/content/docs/intl/intl-namespace.md +129 -0
- package/lib/templates/content/docs/intl/vite-plugin-intl.md +87 -0
- package/lib/templates/content/docs/intl/webpack-plugin-intl.md +12 -0
- package/lib/templates/content/docs/intro.md +18 -0
- package/lib/templates/content/docs/knowledge/basic-fullstack.md +238 -0
- package/lib/templates/content/docs/mailing/index.md +14 -0
- package/lib/templates/content/docs/mailing/mailing-template.md +148 -0
- package/lib/templates/content/docs/mobile/App-navigation-generator.md +410 -0
- package/lib/templates/content/docs/mobile/MobileTestCases.md +264 -0
- package/lib/templates/content/docs/mobile/eas-profile-build.md +107 -0
- package/lib/templates/content/docs/mobile/expo-push-notification-setup.md +216 -0
- package/lib/templates/content/docs/mobile/index.md +14 -0
- package/lib/templates/content/docs/mobile/routes.md +83 -0
- package/lib/templates/content/docs/organization/adding-account-context.md +116 -0
- package/lib/templates/content/docs/organization/adding-org-mobile-navigation.md +22 -0
- package/lib/templates/content/docs/organization/adding-org-web-navigation.md +12 -0
- package/lib/templates/content/docs/organization/index.md +20 -0
- package/lib/templates/content/docs/organization/initialization.md +20 -0
- package/lib/templates/content/docs/organization/organization-resource-vs-resource.md +112 -0
- package/lib/templates/content/docs/remix/configuration/component-structure-best-practices.md +152 -0
- package/lib/templates/content/docs/remix/configuration/configurations.md +218 -0
- package/lib/templates/content/docs/remix/configuration/css-import-and-stylesheets.md +142 -0
- package/lib/templates/content/docs/remix/configuration/dont-subcomponent-network.md +166 -0
- package/lib/templates/content/docs/remix/configuration/generated-data-loaders.md +122 -0
- package/lib/templates/content/docs/remix/configuration/generated-resource-loaders.md +257 -0
- package/lib/templates/content/docs/remix/configuration/query-params-generator.md +216 -0
- package/lib/templates/content/docs/remix/configuration/routes-extra-icons.md +103 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-advanced.md +86 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-auth.md +113 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-best-practices.md +55 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-fields.md +79 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-graphql.md +79 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-index.md +112 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-loaders.md +165 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-middleware.md +196 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-overview.md +53 -0
- package/lib/templates/content/docs/remix/data-loaders.md +43 -0
- package/lib/templates/content/docs/remix/devtools/remix-devtools.md +58 -0
- package/lib/templates/content/docs/remix/examples/changes-using-servercode.md +79 -0
- package/lib/templates/content/docs/remix/extra-icons.md +62 -0
- package/lib/templates/content/docs/remix/extra-links.md +65 -0
- package/lib/templates/content/docs/remix/generated-data-loaders.md +114 -0
- package/lib/templates/content/docs/remix/queryParamsGenerator.md +89 -0
- package/lib/templates/content/docs/remix/resources.md +16 -0
- package/lib/templates/content/docs/remix/styles.md +132 -0
- package/lib/templates/content/docs/remix/wiki.md +12 -0
- package/lib/templates/content/docs/security/auth-wrapper/auth-wrapper.md +24 -0
- package/lib/templates/content/docs/security/index.md +18 -0
- package/lib/templates/content/docs/security/secure-button-mobilenative.md +88 -0
- package/lib/templates/content/docs/security/secure-button-web.md +89 -0
- package/lib/templates/content/docs/server-side/account-customization.md +82 -0
- package/lib/templates/content/docs/server-side/apollo/caching.md +164 -0
- package/lib/templates/content/docs/server-side/backend-architecture/FINAL-DECISION.md +209 -0
- package/lib/templates/content/docs/server-side/backend-architecture/TRUE-FINAL-ARCHITECTURE.md +603 -0
- package/lib/templates/content/docs/server-side/backend-architecture/index1.md +0 -0
- package/lib/templates/content/docs/server-side/backend-coding.md +839 -0
- package/lib/templates/content/docs/server-side/e2b/manageing-template.md +197 -0
- package/lib/templates/content/docs/server-side/index.md +14 -0
- package/lib/templates/content/docs/server-side/inngest-functions-module.md +309 -0
- package/lib/templates/content/docs/server-side/listen-stripe-events.md +43 -0
- package/lib/templates/content/docs/server-side/slug-service.md +323 -0
- package/lib/templates/content/docs/tests/index.md +18 -0
- package/lib/templates/content/docs/tests/jest-test-debug-vscode.md +40 -0
- package/lib/templates/content/docs/tests/known-errors.md +116 -0
- package/lib/templates/content/docs/tests/service-test-template.md +118 -0
- package/lib/templates/content/docs/tests/test-setup.md +93 -0
- package/lib/templates/content/docs/xstate.md +23 -0
- package/lib/types.d.ts +37 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/utils/docsNavigation.d.ts +9 -0
- package/lib/utils/docsNavigation.d.ts.map +1 -0
- package/lib/utils/docsNavigation.js +37 -0
- package/lib/utils/docsNavigation.js.map +1 -0
- package/lib/utils/helpCenterUtils.d.ts +26 -0
- package/lib/utils/helpCenterUtils.d.ts.map +1 -0
- package/lib/utils/index.d.ts +3 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +3 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/markdownLoader.d.ts +36 -0
- package/lib/utils/markdownLoader.d.ts.map +1 -0
- package/lib/utils/markdownLoader.js +2242 -0
- package/lib/utils/markdownLoader.js.map +1 -0
- package/package.json +71 -0
package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenants.md
ADDED
|
@@ -0,0 +1,1117 @@
|
|
|
1
|
+
# Tenant Management System - Comprehensive Documentation
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [Overview](#overview)
|
|
6
|
+
2. [What This Functionality Is About](#what-this-functionality-is-about)
|
|
7
|
+
3. [How to Execute the Functionality](#how-to-execute-the-functionality)
|
|
8
|
+
4. [Implementation Details](#implementation-details)
|
|
9
|
+
5. [Component Architecture](#component-architecture)
|
|
10
|
+
6. [State Management](#state-management)
|
|
11
|
+
7. [GraphQL Integration](#graphql-integration)
|
|
12
|
+
8. [Backend Architecture & Inngest Integration](#backend-architecture--inngest-integration)
|
|
13
|
+
9. [Important Notes and Best Practices](#important-notes-and-best-practices)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
|
|
19
|
+
The Tenant Management System is a comprehensive multi-tenant management solution built with React, TypeScript, XState, and GraphQL. It provides a complete CRUD interface for managing tenants within an organization, including tenant creation, editing, deletion, secret key management, and lifecycle operations.
|
|
20
|
+
|
|
21
|
+
**Primary Entry Point:** `TenantDashboard.tsx`
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## What This Functionality Is About
|
|
26
|
+
|
|
27
|
+
### Core Purpose
|
|
28
|
+
|
|
29
|
+
The Tenant Management System enables organizations to:
|
|
30
|
+
|
|
31
|
+
1. **Manage Multiple Tenants**: Create, view, update, and delete tenant configurations within an organization
|
|
32
|
+
2. **Environment-Based Organization**: Associate tenants with specific environments (development, staging, production) and projects
|
|
33
|
+
3. **Secret Key Management**: Generate, view, and revoke API secret keys for tenant authentication
|
|
34
|
+
4. **Lifecycle Management**: Handle tenant creation workflows, retry failed operations, and monitor tenant status
|
|
35
|
+
5. **Multi-Project Support**: Link tenants to specific projects within an organization
|
|
36
|
+
|
|
37
|
+
### Key Features
|
|
38
|
+
|
|
39
|
+
- ✅ **Full CRUD Operations**: Create, Read, Update, Delete tenants
|
|
40
|
+
- ✅ **Secret API Key Management**: Generate and manage client configurations with secret keys
|
|
41
|
+
- ✅ **Status Tracking**: Monitor tenant status (active, pending, error, disabled, completed)
|
|
42
|
+
- ✅ **Error Handling**: Retry failed tenant creation operations
|
|
43
|
+
- ✅ **Project Integration**: Associate tenants with projects and environment tags
|
|
44
|
+
- ✅ **Real-time Updates**: Automatic refetching and cache management
|
|
45
|
+
- ✅ **Toast Notifications**: User-friendly success/error feedback
|
|
46
|
+
- ✅ **State Machine Management**: Predictable state transitions using XState
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## How to Execute the Functionality
|
|
51
|
+
|
|
52
|
+
### Accessing the Tenant Dashboard
|
|
53
|
+
|
|
54
|
+
1. **Navigate to the Route**: The Tenant Dashboard is accessible via a route that includes the organization name parameter (`orgName`)
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Route pattern: /organizations/:orgName/tenants
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
2. **Prerequisites**:
|
|
61
|
+
- User must be authenticated
|
|
62
|
+
- User must have appropriate permissions for the organization
|
|
63
|
+
- Organization must exist and be accessible
|
|
64
|
+
|
|
65
|
+
### Creating a New Tenant
|
|
66
|
+
|
|
67
|
+
1. **Click "Create Tenant" Button**: Located in the header of the tenant list or in the empty state
|
|
68
|
+
2. **Fill Required Fields**:
|
|
69
|
+
- **Name**: Tenant name (minimum 3 characters)
|
|
70
|
+
- **Description**: Detailed description of the tenant
|
|
71
|
+
- **Project**: Select a project from the dropdown (optional)
|
|
72
|
+
- **Environment**: Select an environment tag (automatically populated based on selected project)
|
|
73
|
+
- **Secret API Keys**: Add at least one secret key with name and optional description
|
|
74
|
+
3. **Submit**: Click "Create Tenant" button
|
|
75
|
+
4. **Result**:
|
|
76
|
+
- Success: Tenant is created, modal closes, list refreshes, toast notification appears
|
|
77
|
+
- Error: Error message displayed in the form, toast notification shows error details
|
|
78
|
+
|
|
79
|
+
### Viewing Tenant Details
|
|
80
|
+
|
|
81
|
+
1. **Click Tenant Name**: Click on any tenant name in the list to view details
|
|
82
|
+
2. **Actions Menu**: Click "Actions" dropdown and select "View Details"
|
|
83
|
+
3. **Details Modal Shows**:
|
|
84
|
+
- Basic information (name, description, app name, environment, status, auth type)
|
|
85
|
+
- Creation and update timestamps
|
|
86
|
+
- Error messages (if any)
|
|
87
|
+
- Secret API Keys section with reveal/revoke functionality
|
|
88
|
+
|
|
89
|
+
### Editing a Tenant
|
|
90
|
+
|
|
91
|
+
1. **Open Actions Menu**: Click "Actions" button next to the tenant
|
|
92
|
+
2. **Select "Edit"**: Opens the edit modal
|
|
93
|
+
3. **Modify Fields**: Update name, description, environment, or project
|
|
94
|
+
4. **Add New Secret Keys**: Add additional secret API keys (existing keys are preserved)
|
|
95
|
+
5. **Submit**: Click "Update Tenant" button
|
|
96
|
+
6. **Result**: Tenant is updated, modal closes, list refreshes, selected tenant is cleared
|
|
97
|
+
|
|
98
|
+
### Deleting a Tenant
|
|
99
|
+
|
|
100
|
+
1. **Open Actions Menu**: Click "Actions" button next to the tenant
|
|
101
|
+
2. **Select "Delete"**: Confirmation dialog appears
|
|
102
|
+
3. **Confirm**: Tenant is permanently deleted
|
|
103
|
+
4. **Result**: Tenant removed from list, toast notification confirms deletion
|
|
104
|
+
|
|
105
|
+
### Managing Secret API Keys
|
|
106
|
+
|
|
107
|
+
#### Viewing Secret Keys
|
|
108
|
+
|
|
109
|
+
- **In Tenant Details**: All secret keys are listed with their metadata
|
|
110
|
+
- **Reveal Key**: Click "Reveal" button to view the actual secret value
|
|
111
|
+
- **Copy Key**: After revealing, click "Copy" to copy to clipboard
|
|
112
|
+
- **Hide Key**: Click "Hide" to conceal the secret value again
|
|
113
|
+
|
|
114
|
+
#### Revoking Secret Keys
|
|
115
|
+
|
|
116
|
+
1. **In Tenant Details**: Click "Revoke" button next to a secret key
|
|
117
|
+
2. **Confirm**: Confirm the revocation in the modal
|
|
118
|
+
3. **Result**: Key is permanently revoked and removed from the list
|
|
119
|
+
|
|
120
|
+
#### Adding New Secret Keys
|
|
121
|
+
|
|
122
|
+
- **During Creation**: Add keys in the create tenant form
|
|
123
|
+
- **During Edit**: Add new keys in the edit form (existing keys remain unchanged)
|
|
124
|
+
|
|
125
|
+
### Retrying Failed Tenant Creation
|
|
126
|
+
|
|
127
|
+
1. **Identify Failed Tenant**: Look for tenants with status "error" (red badge)
|
|
128
|
+
2. **Open Actions Menu**: Click "Actions" button
|
|
129
|
+
3. **Select "Retry Creation"**: Only visible for tenants with error status
|
|
130
|
+
4. **Result**: Retry process initiated, status updates accordingly
|
|
131
|
+
|
|
132
|
+
### Regenerating Tenant Secret
|
|
133
|
+
|
|
134
|
+
**Note**: This feature is currently commented out in the UI but available in the backend.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Implementation Details
|
|
139
|
+
|
|
140
|
+
### Component Hierarchy
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
TenantDashboard (Main Container)
|
|
144
|
+
├── TenantList (Table View)
|
|
145
|
+
│ └── Action Dropdown Menu
|
|
146
|
+
├── TenantDetails (Modal - View Only)
|
|
147
|
+
│ └── SecretKeyList (Secret Key Management)
|
|
148
|
+
├── CreateTenantModal (Modal)
|
|
149
|
+
│ └── TenantForm
|
|
150
|
+
│ ├── SecretKeyModal (Add New Keys)
|
|
151
|
+
│ └── SecretKeyList (Preview New Keys)
|
|
152
|
+
├── EditTenantModal (Modal)
|
|
153
|
+
│ └── TenantForm
|
|
154
|
+
│ ├── SecretKeyList (Existing Keys)
|
|
155
|
+
│ ├── SecretKeyList (New Keys to Add)
|
|
156
|
+
│ └── SecretKeyModal (Add New Keys)
|
|
157
|
+
└── Toast (Notifications)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Key Components
|
|
161
|
+
|
|
162
|
+
#### 1. TenantDashboard (`TenantDashboard.tsx`)
|
|
163
|
+
|
|
164
|
+
**Purpose**: Main orchestrator component that manages the entire tenant management workflow.
|
|
165
|
+
|
|
166
|
+
**Responsibilities**:
|
|
167
|
+
|
|
168
|
+
- State machine initialization and management
|
|
169
|
+
- GraphQL query/mutation orchestration
|
|
170
|
+
- Event handling and state synchronization
|
|
171
|
+
- Modal visibility management
|
|
172
|
+
- Toast notification management
|
|
173
|
+
|
|
174
|
+
**Key Features**:
|
|
175
|
+
|
|
176
|
+
- Uses XState machine (`tenantMachine`) for predictable state management
|
|
177
|
+
- Integrates with Apollo Client for GraphQL operations
|
|
178
|
+
- Manages multiple modals (create, edit, details) with proper state isolation
|
|
179
|
+
- Handles organization context via URL parameter (`orgName`)
|
|
180
|
+
|
|
181
|
+
**State Management Flow**:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// Initial load
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
send({ type: 'LOAD_TENANTS' });
|
|
187
|
+
await loadTenants({ variables: { orgName } });
|
|
188
|
+
}, []);
|
|
189
|
+
|
|
190
|
+
// State updates via machine events
|
|
191
|
+
send({ type: 'SUCCESS', data: tenants });
|
|
192
|
+
send({ type: 'ERROR', error: errorMessage });
|
|
193
|
+
send({ type: 'SELECT_TENANT', tenant });
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### 2. TenantList (`TenantList.tsx`)
|
|
197
|
+
|
|
198
|
+
**Purpose**: Displays tenants in a tabular format with action capabilities.
|
|
199
|
+
|
|
200
|
+
**Features**:
|
|
201
|
+
|
|
202
|
+
- Responsive table layout
|
|
203
|
+
- Status badges with color coding
|
|
204
|
+
- Environment tag badges
|
|
205
|
+
- Action dropdown menu per tenant
|
|
206
|
+
- Empty state with call-to-action
|
|
207
|
+
- Loading state with spinner
|
|
208
|
+
|
|
209
|
+
**Status Colors**:
|
|
210
|
+
|
|
211
|
+
- `active`: Green
|
|
212
|
+
- `pending`: Yellow
|
|
213
|
+
- `error`: Red
|
|
214
|
+
- `disabled`: Gray
|
|
215
|
+
- `completed`: Blue
|
|
216
|
+
|
|
217
|
+
**Environment Colors**:
|
|
218
|
+
|
|
219
|
+
- `development`: Cyan
|
|
220
|
+
- `staging`: Orange
|
|
221
|
+
- `production`: Red
|
|
222
|
+
|
|
223
|
+
**Action Menu Options**:
|
|
224
|
+
|
|
225
|
+
- View Details
|
|
226
|
+
- Edit
|
|
227
|
+
- Retry Creation (only for error status)
|
|
228
|
+
- Delete
|
|
229
|
+
|
|
230
|
+
#### 3. TenantDetails (`TenantDetails.tsx`)
|
|
231
|
+
|
|
232
|
+
**Purpose**: Modal component displaying comprehensive tenant information.
|
|
233
|
+
|
|
234
|
+
**Displays**:
|
|
235
|
+
|
|
236
|
+
- Basic information (name, description, app name)
|
|
237
|
+
- Environment tag
|
|
238
|
+
- Status badge
|
|
239
|
+
- Auth type
|
|
240
|
+
- Timestamps (created/updated)
|
|
241
|
+
- Error messages (if applicable)
|
|
242
|
+
- Secret API Keys section
|
|
243
|
+
|
|
244
|
+
**Secret Key Integration**:
|
|
245
|
+
|
|
246
|
+
- Embeds `SecretKeyList` component
|
|
247
|
+
- Passes `send` function for state machine updates
|
|
248
|
+
- Enables reveal/revoke actions
|
|
249
|
+
|
|
250
|
+
#### 4. CreateTenantModal (`CreateTenantModal.tsx`)
|
|
251
|
+
|
|
252
|
+
**Purpose**: Modal wrapper for tenant creation form.
|
|
253
|
+
|
|
254
|
+
**Features**:
|
|
255
|
+
|
|
256
|
+
- Full-screen overlay modal
|
|
257
|
+
- Click-outside-to-close functionality
|
|
258
|
+
- Responsive design (max-width: 4xl)
|
|
259
|
+
- Integrates with `TenantForm` component
|
|
260
|
+
|
|
261
|
+
#### 5. EditTenantModal (`EditTenantModal.tsx`)
|
|
262
|
+
|
|
263
|
+
**Purpose**: Modal wrapper for tenant editing form.
|
|
264
|
+
|
|
265
|
+
**Features**:
|
|
266
|
+
|
|
267
|
+
- Similar to CreateTenantModal
|
|
268
|
+
- Pre-populates form with existing tenant data
|
|
269
|
+
- Handles update-specific logic
|
|
270
|
+
|
|
271
|
+
#### 6. TenantForm (`TenantForm/TenantForm.tsx`)
|
|
272
|
+
|
|
273
|
+
**Purpose**: Reusable form component for both create and edit operations.
|
|
274
|
+
|
|
275
|
+
**Form Fields**:
|
|
276
|
+
|
|
277
|
+
1. **Name** (required, min 3 chars)
|
|
278
|
+
2. **Description** (required)
|
|
279
|
+
3. **Project** (optional dropdown)
|
|
280
|
+
4. **Environment** (required, auto-populated from project)
|
|
281
|
+
5. **Secret API Keys** (required for creation, optional for edit)
|
|
282
|
+
|
|
283
|
+
**Key Behaviors**:
|
|
284
|
+
|
|
285
|
+
- **Project Selection**: Automatically fetches environment tags when project is selected
|
|
286
|
+
- **Environment Auto-selection**: Sets first environment tag as default
|
|
287
|
+
- **Validation**: Real-time validation with error messages
|
|
288
|
+
- **Secret Key Management**:
|
|
289
|
+
- Create mode: Requires at least one key
|
|
290
|
+
- Edit mode: Shows existing keys separately from new keys
|
|
291
|
+
- New keys can be added via modal
|
|
292
|
+
- Existing keys cannot be modified (only revoked via details view)
|
|
293
|
+
|
|
294
|
+
**State Synchronization**:
|
|
295
|
+
|
|
296
|
+
- Updates XState machine on field changes
|
|
297
|
+
- Validates form before submission
|
|
298
|
+
- Handles loading and error states
|
|
299
|
+
|
|
300
|
+
#### 7. SecretKeyList (`SecretKeyList/SecretKeyList.tsx`)
|
|
301
|
+
|
|
302
|
+
**Purpose**: Displays and manages secret API keys.
|
|
303
|
+
|
|
304
|
+
**Features**:
|
|
305
|
+
|
|
306
|
+
- Table view of secret keys
|
|
307
|
+
- Reveal/Hide functionality for secret values
|
|
308
|
+
- Copy to clipboard
|
|
309
|
+
- Revoke functionality with confirmation
|
|
310
|
+
- Security warning banner
|
|
311
|
+
- Loading states for async operations
|
|
312
|
+
|
|
313
|
+
**Key Operations**:
|
|
314
|
+
|
|
315
|
+
- `revealKey(keyId)`: Fetches and displays secret value
|
|
316
|
+
- `revokeKey(keyId)`: Permanently deletes secret key
|
|
317
|
+
- Uses `useSecretKeys` hook for API operations
|
|
318
|
+
|
|
319
|
+
**Security Considerations**:
|
|
320
|
+
|
|
321
|
+
- Secrets are hidden by default
|
|
322
|
+
- One-time reveal (must click reveal for each key)
|
|
323
|
+
- Confirmation required for revocation
|
|
324
|
+
- Security best practices displayed in warning banner
|
|
325
|
+
|
|
326
|
+
#### 8. Toast (`Toast/Toast.tsx`)
|
|
327
|
+
|
|
328
|
+
**Purpose**: User notification system.
|
|
329
|
+
|
|
330
|
+
**Types**:
|
|
331
|
+
|
|
332
|
+
- `success`: Green background
|
|
333
|
+
- `error`: Red background
|
|
334
|
+
- `info`: Blue background
|
|
335
|
+
- `warning`: Yellow background
|
|
336
|
+
|
|
337
|
+
**Features**:
|
|
338
|
+
|
|
339
|
+
- Auto-dismiss after 5 seconds (configurable)
|
|
340
|
+
- Manual close button
|
|
341
|
+
- Icon indicators per type
|
|
342
|
+
- Fixed position (top-right)
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## State Management
|
|
349
|
+
|
|
350
|
+
### XState Machine (`tenantMachine.ts`)
|
|
351
|
+
|
|
352
|
+
The tenant management system uses XState for predictable state management.
|
|
353
|
+
|
|
354
|
+
#### Machine Context
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
interface TenantMachineContext {
|
|
358
|
+
tenant: Partial<ITenant>;
|
|
359
|
+
tenants: ITenant[];
|
|
360
|
+
selectedTenant?: ITenant;
|
|
361
|
+
formData: TenantFormData;
|
|
362
|
+
loading: boolean;
|
|
363
|
+
error?: string;
|
|
364
|
+
success?: string;
|
|
365
|
+
validationErrors: Record<string, string>;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### Machine States
|
|
370
|
+
|
|
371
|
+
1. **idle**: Initial state, ready for actions
|
|
372
|
+
2. **loadingTenants**: Fetching tenant list
|
|
373
|
+
3. **loadingTenant**: Fetching single tenant details
|
|
374
|
+
4. **creatingTenant**: Creating new tenant
|
|
375
|
+
5. **updatingTenant**: Updating existing tenant
|
|
376
|
+
6. **deletingTenant**: Deleting tenant
|
|
377
|
+
7. **regeneratingSecret**: Regenerating tenant secret
|
|
378
|
+
8. **retryingCreation**: Retrying failed tenant creation
|
|
379
|
+
|
|
380
|
+
#### Key Events
|
|
381
|
+
|
|
382
|
+
- `LOAD_TENANTS`: Initialize tenant list loading
|
|
383
|
+
- `SELECT_TENANT`: Select a tenant for viewing/editing
|
|
384
|
+
- `CREATE_TENANT`: Initiate tenant creation
|
|
385
|
+
- `UPDATE_TENANT`: Initiate tenant update
|
|
386
|
+
- `DELETE_TENANT`: Initiate tenant deletion
|
|
387
|
+
- `SUCCESS`: Handle successful operation
|
|
388
|
+
- `ERROR`: Handle error condition
|
|
389
|
+
- `RESET`: Clear selected tenant and errors
|
|
390
|
+
- `UPDATE_FORM`: Update form field in context
|
|
391
|
+
- `REVOKE_SECRET_KEY`: Revoke a secret key
|
|
392
|
+
|
|
393
|
+
#### Actions
|
|
394
|
+
|
|
395
|
+
- `setTenants`: Update tenants array
|
|
396
|
+
- `setSelectedTenant`: Update selected tenant
|
|
397
|
+
- `addTenant`: Add new tenant to list
|
|
398
|
+
- `updateTenantInList`: Update tenant in list
|
|
399
|
+
- `removeTenant`: Remove tenant from list
|
|
400
|
+
- `updateForm`: Update form data
|
|
401
|
+
- `validateForm`: Validate form fields
|
|
402
|
+
- `resetForm`: Clear form data
|
|
403
|
+
- `setError`: Set error message
|
|
404
|
+
- `setSuccess`: Set success message
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## GraphQL Integration
|
|
409
|
+
|
|
410
|
+
### Queries
|
|
411
|
+
|
|
412
|
+
#### GetTenants
|
|
413
|
+
|
|
414
|
+
```graphql
|
|
415
|
+
query GetTenants($orgName: String!) {
|
|
416
|
+
tenants(orgName: $orgName) {
|
|
417
|
+
id
|
|
418
|
+
tenantId
|
|
419
|
+
name
|
|
420
|
+
description
|
|
421
|
+
appName
|
|
422
|
+
status
|
|
423
|
+
environmentTag {
|
|
424
|
+
id
|
|
425
|
+
name
|
|
426
|
+
}
|
|
427
|
+
project {
|
|
428
|
+
id
|
|
429
|
+
name
|
|
430
|
+
}
|
|
431
|
+
clientConfigurations {
|
|
432
|
+
id
|
|
433
|
+
name
|
|
434
|
+
description
|
|
435
|
+
createdAt
|
|
436
|
+
}
|
|
437
|
+
errorMessage
|
|
438
|
+
createdAt
|
|
439
|
+
updatedAt
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
#### GetTenant
|
|
445
|
+
|
|
446
|
+
```graphql
|
|
447
|
+
query GetTenant($id: ID!) {
|
|
448
|
+
tenant(id: $id) {
|
|
449
|
+
# Same fields as GetTenants
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
#### GetProjectsNameAndId
|
|
455
|
+
|
|
456
|
+
```graphql
|
|
457
|
+
query GetProjectsNameAndId($orgName: String!) {
|
|
458
|
+
getProjects(orgName: $orgName) {
|
|
459
|
+
data {
|
|
460
|
+
id
|
|
461
|
+
name
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
#### GetEnvironmentTags
|
|
468
|
+
|
|
469
|
+
```graphql
|
|
470
|
+
query GetEnvironmentTags($projectId: ID!) {
|
|
471
|
+
getEnvironmentTags(projectId: $projectId) {
|
|
472
|
+
id
|
|
473
|
+
name
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Mutations
|
|
479
|
+
|
|
480
|
+
#### CreateTenant
|
|
481
|
+
|
|
482
|
+
```graphql
|
|
483
|
+
mutation CreateTenant($input: TenantInput!) {
|
|
484
|
+
createTenant(input: $input) {
|
|
485
|
+
tenant {
|
|
486
|
+
id
|
|
487
|
+
tenantId
|
|
488
|
+
name
|
|
489
|
+
# ... other fields
|
|
490
|
+
}
|
|
491
|
+
message
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Input Structure**:
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
{
|
|
500
|
+
name: string;
|
|
501
|
+
description: string;
|
|
502
|
+
environmentTag: string; // Environment tag ID
|
|
503
|
+
authType: AuthType.ClientSecret;
|
|
504
|
+
clientConfigurations: Array<{
|
|
505
|
+
name: string;
|
|
506
|
+
description?: string;
|
|
507
|
+
}>;
|
|
508
|
+
vault: string; // Project ID
|
|
509
|
+
organization: string; // Organization name/slug
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
#### UpdateTenant
|
|
514
|
+
|
|
515
|
+
```graphql
|
|
516
|
+
mutation UpdateTenant($id: ID!, $input: TenantUpdateInput!) {
|
|
517
|
+
updateTenant(id: $id, input: $input) {
|
|
518
|
+
id
|
|
519
|
+
tenantId
|
|
520
|
+
name
|
|
521
|
+
# ... updated fields
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**Update Input**: Only includes changed fields (partial update)
|
|
527
|
+
|
|
528
|
+
#### DeleteTenant
|
|
529
|
+
|
|
530
|
+
```graphql
|
|
531
|
+
mutation DeleteTenant($id: ID!) {
|
|
532
|
+
deleteTenant(id: $id)
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
#### RegenerateTenantSecret
|
|
537
|
+
|
|
538
|
+
```graphql
|
|
539
|
+
mutation RegenerateTenantSecret($id: ID!) {
|
|
540
|
+
regenerateTenantSecret(id: $id) {
|
|
541
|
+
id
|
|
542
|
+
# ... tenant fields
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
#### RetryTenantCreation
|
|
548
|
+
|
|
549
|
+
```graphql
|
|
550
|
+
mutation RetryTenantCreation($tenantId: ID!) {
|
|
551
|
+
retryTenantCreation(tenantId: $tenantId)
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Apollo Client Configuration
|
|
556
|
+
|
|
557
|
+
- **Fetch Policy**: `cache-and-network` for queries (ensures fresh data)
|
|
558
|
+
- **Fetch Policy**: `network-only` for tenant details (always fetch latest)
|
|
559
|
+
- **Refetch Queries**: Automatic refetch after mutations
|
|
560
|
+
- **Error Handling**: Centralized via `onError` callbacks
|
|
561
|
+
- **Loading States**: Managed via Apollo hooks
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Backend Architecture & Inngest Integration
|
|
566
|
+
|
|
567
|
+
### Overview
|
|
568
|
+
|
|
569
|
+
The Tenant Management System uses an **event-driven architecture** with **Inngest** for asynchronous background processing. The backend service handles immediate database operations, while Inngest functions handle external integrations (primarily Keycloak client management) and long-running workflows.
|
|
570
|
+
|
|
571
|
+
### Architecture Pattern
|
|
572
|
+
|
|
573
|
+
The system follows a **separation of concerns** pattern:
|
|
574
|
+
|
|
575
|
+
1. **Service Layer** (`TenantService`): Handles immediate database operations and triggers Inngest events
|
|
576
|
+
2. **Inngest Functions**: Handle external integrations (Keycloak) and async workflows
|
|
577
|
+
3. **No Infinite Loops**: Inngest functions perform different operations than the service methods that trigger them
|
|
578
|
+
|
|
579
|
+
### Backend Service Layer
|
|
580
|
+
|
|
581
|
+
#### TenantService (`tenant-service.ts`)
|
|
582
|
+
|
|
583
|
+
**Purpose**: Core service for tenant CRUD operations and business logic.
|
|
584
|
+
|
|
585
|
+
**Key Responsibilities**:
|
|
586
|
+
|
|
587
|
+
- Database operations (create, read, update, delete)
|
|
588
|
+
- Tenant ID resolution and mapping
|
|
589
|
+
- Secret key generation and management
|
|
590
|
+
- Inngest event triggering for async operations
|
|
591
|
+
- Status management (pending → active → error)
|
|
592
|
+
|
|
593
|
+
**Key Methods**:
|
|
594
|
+
|
|
595
|
+
##### `createTenant(input: ITenantInput)`
|
|
596
|
+
|
|
597
|
+
**Flow**:
|
|
598
|
+
|
|
599
|
+
1. Generates unique `jobId` for tracking
|
|
600
|
+
2. Creates tenant record in MongoDB with status `Pending`
|
|
601
|
+
3. Generates `connectionId` (secret for tenant connection)
|
|
602
|
+
4. Creates `appName` from tenant name (lowercase, hyphenated)
|
|
603
|
+
5. Maps `vault` input to `project` (MongoDB ObjectId)
|
|
604
|
+
6. Maps `organization` input to ObjectId
|
|
605
|
+
7. **Triggers Inngest Event**: `TENANT_MGMT_CREATE_CLIENT_EVENT` for Keycloak client creation
|
|
606
|
+
8. Returns tenant with `jobId` and success message
|
|
607
|
+
|
|
608
|
+
**Inngest Event Payload**:
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
{
|
|
612
|
+
name: WorkflowNamespace.TENANT_MGMT_CREATE_CLIENT_EVENT,
|
|
613
|
+
data: {
|
|
614
|
+
tenantId: tenant.id,
|
|
615
|
+
id: tenant._id, // MongoDB ObjectId
|
|
616
|
+
clientConfigurations: input.clientConfigurations
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
##### `updateTenant(id: string, input: Partial<ITenantInput>)`
|
|
622
|
+
|
|
623
|
+
**Flow**:
|
|
624
|
+
|
|
625
|
+
1. Finds existing tenant by `tenantId`
|
|
626
|
+
2. Updates basic fields (name, description, environmentTag, project)
|
|
627
|
+
3. **Preserves existing client configurations**
|
|
628
|
+
4. Merges new configurations with existing ones
|
|
629
|
+
5. **Triggers Inngest Event**: `TENANT_MGMT_CREATE_CLIENT_EVENT` if new keys are added
|
|
630
|
+
6. Returns updated tenant
|
|
631
|
+
|
|
632
|
+
**Important**: Only new client configurations trigger Inngest events. Existing keys remain unchanged.
|
|
633
|
+
|
|
634
|
+
##### `deleteTenant(id: string)`
|
|
635
|
+
|
|
636
|
+
**Flow**:
|
|
637
|
+
|
|
638
|
+
1. Finds tenant by `tenantId`
|
|
639
|
+
2. Deletes tenant from MongoDB
|
|
640
|
+
3. **Triggers Inngest Event**: `TENANT_MGMT_DELETE_TENANT_EVENT` with client configurations
|
|
641
|
+
4. Returns success boolean
|
|
642
|
+
|
|
643
|
+
**Inngest Event Payload**:
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
{
|
|
647
|
+
name: WorkflowNamespace.TENANT_MGMT_DELETE_TENANT_EVENT,
|
|
648
|
+
data: {
|
|
649
|
+
clientConfiguraiton: tenant.clientConfigurations // Note: typo in original code
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
##### `getSecretKeyValue(tenantId: string, keyId: string)`
|
|
655
|
+
|
|
656
|
+
**Purpose**: Reveals secret key value on-demand (deterministic generation).
|
|
657
|
+
|
|
658
|
+
**Flow**:
|
|
659
|
+
|
|
660
|
+
1. Finds tenant by `tenantId`
|
|
661
|
+
2. Locates client configuration by `keyId`
|
|
662
|
+
3. **Generates secret deterministically** using `generateSecretFromName()`
|
|
663
|
+
4. Creates base64-encoded secret: `clientId:secret`
|
|
664
|
+
5. Returns secret value
|
|
665
|
+
|
|
666
|
+
**Security**: Secrets are not stored; they are regenerated deterministically when needed.
|
|
667
|
+
|
|
668
|
+
##### `revokeSecretKey(tenantId: string, keyId: string)`
|
|
669
|
+
|
|
670
|
+
**Flow**:
|
|
671
|
+
|
|
672
|
+
1. Finds tenant and secret key
|
|
673
|
+
2. Removes key from `clientConfigurations` array
|
|
674
|
+
3. Updates tenant in database
|
|
675
|
+
4. **Triggers Inngest Event**: `TENANT_MGMT_DELETE_TENANT_EVENT` to delete Keycloak client
|
|
676
|
+
5. Returns revoked key ID
|
|
677
|
+
|
|
678
|
+
##### `retryTenantCreation(tenantId: string)`
|
|
679
|
+
|
|
680
|
+
**Flow**:
|
|
681
|
+
|
|
682
|
+
1. Validates tenant exists and has `error` status
|
|
683
|
+
2. Increments retry count
|
|
684
|
+
3. Resets status to `Pending`
|
|
685
|
+
4. Generates new `jobId`
|
|
686
|
+
5. Updates tenant in database
|
|
687
|
+
6. Returns success boolean
|
|
688
|
+
|
|
689
|
+
**Note**: This only resets the status. The actual retry is handled by background processes monitoring pending tenants.
|
|
690
|
+
|
|
691
|
+
#### Tenant ID Resolution
|
|
692
|
+
|
|
693
|
+
**Method**: `resolveTenantId(tenantId: string)`
|
|
694
|
+
|
|
695
|
+
**Purpose**: Maps special tenant IDs to actual IDs.
|
|
696
|
+
|
|
697
|
+
**Implementation**:
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
private resolveTenantId(tenantId: string): string {
|
|
701
|
+
return defaultTenantIdMapper(tenantId);
|
|
702
|
+
}
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
**Mapping Logic**:
|
|
706
|
+
|
|
707
|
+
- `'default'` → `config.STATIC_TENANT_ID` (from environment)
|
|
708
|
+
- All other IDs → Returned as-is
|
|
709
|
+
|
|
710
|
+
**Usage**: All tenant lookups use this method to ensure consistent ID resolution.
|
|
711
|
+
|
|
712
|
+
### Inngest Event-Driven Workflow
|
|
713
|
+
|
|
714
|
+
#### Event Namespace
|
|
715
|
+
|
|
716
|
+
All tenant management events use the `WorkflowNamespace` constants:
|
|
717
|
+
|
|
718
|
+
**Trigger Events** (what functions listen for):
|
|
719
|
+
|
|
720
|
+
- `TENANT_MGMT_CREATE_CLIENT_EVENT`: Create Keycloak clients
|
|
721
|
+
- `TENANT_MGMT_DELETE_TENANT_EVENT`: Delete Keycloak clients
|
|
722
|
+
|
|
723
|
+
**Function IDs**:
|
|
724
|
+
|
|
725
|
+
- `TENANT_MGMT_CREATE_CLIENT`: Function that creates clients
|
|
726
|
+
- `TENANT_MGMT_DELETE_TENANT`: Function that deletes clients
|
|
727
|
+
|
|
728
|
+
#### Inngest Functions
|
|
729
|
+
|
|
730
|
+
##### 1. `createTenantClientFunction`
|
|
731
|
+
|
|
732
|
+
**Purpose**: Creates Keycloak clients for tenant secret keys.
|
|
733
|
+
|
|
734
|
+
**Trigger**: `TENANT_MGMT_CREATE_CLIENT_EVENT`
|
|
735
|
+
|
|
736
|
+
**Event Data**:
|
|
737
|
+
|
|
738
|
+
```typescript
|
|
739
|
+
{
|
|
740
|
+
tenantId: string;
|
|
741
|
+
id: string; // MongoDB ObjectId
|
|
742
|
+
clientConfigurations: Array<{
|
|
743
|
+
name: string;
|
|
744
|
+
description?: string;
|
|
745
|
+
}>;
|
|
746
|
+
}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
**Workflow Steps**:
|
|
750
|
+
|
|
751
|
+
1. **Step: `create-secret-keys`**
|
|
752
|
+
- Iterates through `clientConfigurations`
|
|
753
|
+
- For each key without `clientId`:
|
|
754
|
+
- Generates deterministic secret using `generateSecretFromName(keyName, tenantId)`
|
|
755
|
+
- Creates unique `clientId`: `tenant-${tenantId}-key-${timestamp}`
|
|
756
|
+
- Calls `KeycloakInternalAdminService.Instance.createClientWithClientSecretAuthType(clientId, clientSecret)`
|
|
757
|
+
- Creates key object with metadata
|
|
758
|
+
- Returns array of created keys with `clientId` and metadata
|
|
759
|
+
|
|
760
|
+
2. **Step: `update-tenant-with-keys`**
|
|
761
|
+
- Calls `tenantService.update()` with:
|
|
762
|
+
- Updated `clientConfigurations` array (with `clientId` values)
|
|
763
|
+
- Status set to `Active`
|
|
764
|
+
- Returns success confirmation
|
|
765
|
+
|
|
766
|
+
**Result**: Tenant status changes from `Pending` → `Active`, and Keycloak clients are created.
|
|
767
|
+
|
|
768
|
+
**Error Handling**: If Keycloak client creation fails, tenant status remains `Pending` or changes to `Error`.
|
|
769
|
+
|
|
770
|
+
##### 2. `deleteTenantClientFunction`
|
|
771
|
+
|
|
772
|
+
**Purpose**: Deletes Keycloak clients when tenant or secret keys are revoked.
|
|
773
|
+
|
|
774
|
+
**Trigger**: `TENANT_MGMT_DELETE_TENANT_EVENT`
|
|
775
|
+
|
|
776
|
+
**Event Data**:
|
|
777
|
+
|
|
778
|
+
```typescript
|
|
779
|
+
{
|
|
780
|
+
clientConfiguration: IClientConfigurations[] // Array of client configs to delete
|
|
781
|
+
}
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
**Workflow Steps**:
|
|
785
|
+
|
|
786
|
+
1. **Step: `delete-tenant`**
|
|
787
|
+
- Iterates through `clientConfiguration` array
|
|
788
|
+
- For each config with `clientId`:
|
|
789
|
+
- Calls `keycloakAdminService.deleteClient(clientId)`
|
|
790
|
+
- Continues even if individual deletions fail
|
|
791
|
+
- Returns success
|
|
792
|
+
|
|
793
|
+
**Error Handling**: Individual client deletion failures are logged but don't stop the process.
|
|
794
|
+
|
|
795
|
+
##### 3. `createTenantFunction` (Legacy/Alternative)
|
|
796
|
+
|
|
797
|
+
**Purpose**: Alternative tenant creation workflow (used in some contexts).
|
|
798
|
+
|
|
799
|
+
**Trigger**: `TENANT_MGMT_CREATE_TENANT_EVENT`
|
|
800
|
+
|
|
801
|
+
**Note**: This function creates a tenant from project data, typically used in project creation workflows rather than direct tenant creation.
|
|
802
|
+
|
|
803
|
+
### Secret Key Generation
|
|
804
|
+
|
|
805
|
+
#### Deterministic Secret Generation
|
|
806
|
+
|
|
807
|
+
**Function**: `generateSecretFromName(keyName: string, tenantId: string)`
|
|
808
|
+
|
|
809
|
+
**Purpose**: Generate reproducible secrets for API keys.
|
|
810
|
+
|
|
811
|
+
**Algorithm**:
|
|
812
|
+
|
|
813
|
+
1. Uses salt from environment: `process.env.SECRET_KEY_SALT` or default
|
|
814
|
+
2. Creates data string: `${keyName}:${tenantId}:${salt}`
|
|
815
|
+
3. Generates SHA-256 hash
|
|
816
|
+
4. Encodes as base64
|
|
817
|
+
5. Removes non-alphanumeric characters
|
|
818
|
+
6. Limits to 32 characters (Keycloak requirement)
|
|
819
|
+
|
|
820
|
+
**Benefits**:
|
|
821
|
+
|
|
822
|
+
- Secrets can be regenerated on-demand
|
|
823
|
+
- No need to store secrets in database
|
|
824
|
+
- Consistent across regenerations
|
|
825
|
+
|
|
826
|
+
**Usage**:
|
|
827
|
+
|
|
828
|
+
- When revealing secret keys (on-demand generation)
|
|
829
|
+
- When creating new Keycloak clients
|
|
830
|
+
|
|
831
|
+
#### Secret Format
|
|
832
|
+
|
|
833
|
+
**For API Usage**: Secrets are base64-encoded as `clientId:secret`
|
|
834
|
+
|
|
835
|
+
**Example**:
|
|
836
|
+
|
|
837
|
+
```typescript
|
|
838
|
+
const secret = generateSecretFromName('ACCESS_KEY', 'tenant-123');
|
|
839
|
+
const clientSecret = Buffer.from(`${clientId}:${secret}`).toString('base64');
|
|
840
|
+
// Returns: "Y2xpZW50SWQ6c2VjcmV0VmFsdWU="
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Complete Data Flow
|
|
844
|
+
|
|
845
|
+
#### Creating a Tenant
|
|
846
|
+
|
|
847
|
+
```
|
|
848
|
+
1. Frontend: User submits create tenant form
|
|
849
|
+
↓
|
|
850
|
+
2. GraphQL Mutation: createTenant(input)
|
|
851
|
+
↓
|
|
852
|
+
3. TenantService.createTenant()
|
|
853
|
+
├── Creates tenant in MongoDB (status: Pending)
|
|
854
|
+
├── Generates jobId, connectionId, appName
|
|
855
|
+
└── Triggers Inngest Event: TENANT_MGMT_CREATE_CLIENT_EVENT
|
|
856
|
+
↓
|
|
857
|
+
4. Returns tenant to frontend (status: Pending)
|
|
858
|
+
↓
|
|
859
|
+
5. Inngest Function: createTenantClientFunction
|
|
860
|
+
├── Step 1: Create Keycloak clients
|
|
861
|
+
│ ├── Generate secrets deterministically
|
|
862
|
+
│ ├── Create clientId for each key
|
|
863
|
+
│ └── Call Keycloak API to create clients
|
|
864
|
+
└── Step 2: Update tenant
|
|
865
|
+
├── Add clientId to each configuration
|
|
866
|
+
└── Set status: Active
|
|
867
|
+
↓
|
|
868
|
+
6. Tenant status: Pending → Active
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
#### Updating a Tenant (Adding New Keys)
|
|
872
|
+
|
|
873
|
+
```
|
|
874
|
+
1. Frontend: User adds new secret keys in edit form
|
|
875
|
+
↓
|
|
876
|
+
2. GraphQL Mutation: updateTenant(id, input)
|
|
877
|
+
↓
|
|
878
|
+
3. TenantService.updateTenant()
|
|
879
|
+
├── Preserves existing clientConfigurations
|
|
880
|
+
├── Merges new configurations
|
|
881
|
+
└── Triggers Inngest Event: TENANT_MGMT_CREATE_CLIENT_EVENT
|
|
882
|
+
↓
|
|
883
|
+
4. Returns updated tenant to frontend
|
|
884
|
+
↓
|
|
885
|
+
5. Inngest Function: createTenantClientFunction
|
|
886
|
+
├── Step 1: Create Keycloak clients for NEW keys only
|
|
887
|
+
└── Step 2: Update tenant with all keys (existing + new)
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
#### Revoking a Secret Key
|
|
891
|
+
|
|
892
|
+
```
|
|
893
|
+
1. Frontend: User clicks "Revoke" on a secret key
|
|
894
|
+
↓
|
|
895
|
+
2. GraphQL Mutation: revokeSecretKey(tenantId, keyId)
|
|
896
|
+
↓
|
|
897
|
+
3. TenantService.revokeSecretKey()
|
|
898
|
+
├── Removes key from clientConfigurations array
|
|
899
|
+
├── Updates tenant in database
|
|
900
|
+
└── Triggers Inngest Event: TENANT_MGMT_DELETE_TENANT_EVENT
|
|
901
|
+
↓
|
|
902
|
+
4. Returns success to frontend
|
|
903
|
+
↓
|
|
904
|
+
5. Inngest Function: deleteTenantClientFunction
|
|
905
|
+
└── Step: Delete Keycloak client by clientId
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
#### Deleting a Tenant
|
|
909
|
+
|
|
910
|
+
```
|
|
911
|
+
1. Frontend: User confirms tenant deletion
|
|
912
|
+
↓
|
|
913
|
+
2. GraphQL Mutation: deleteTenant(id)
|
|
914
|
+
↓
|
|
915
|
+
3. TenantService.deleteTenant()
|
|
916
|
+
├── Deletes tenant from MongoDB
|
|
917
|
+
└── Triggers Inngest Event: TENANT_MGMT_DELETE_TENANT_EVENT
|
|
918
|
+
↓
|
|
919
|
+
4. Returns success to frontend
|
|
920
|
+
↓
|
|
921
|
+
5. Inngest Function: deleteTenantClientFunction
|
|
922
|
+
└── Step: Delete all Keycloak clients for tenant
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
### Keycloak Integration
|
|
926
|
+
|
|
927
|
+
#### Client Creation
|
|
928
|
+
|
|
929
|
+
**Service**: `KeycloakInternalAdminService.Instance.createClientWithClientSecretAuthType()`
|
|
930
|
+
|
|
931
|
+
**Parameters**:
|
|
932
|
+
|
|
933
|
+
- `clientId`: Unique identifier (format: `tenant-{tenantId}-key-{timestamp}`)
|
|
934
|
+
- `clientSecret`: Deterministically generated secret (32 chars, alphanumeric)
|
|
935
|
+
|
|
936
|
+
**Configuration**:
|
|
937
|
+
|
|
938
|
+
- Authentication type: Client Secret
|
|
939
|
+
- Client credentials flow enabled
|
|
940
|
+
- Scopes and permissions configured per tenant requirements
|
|
941
|
+
|
|
942
|
+
#### Client Deletion
|
|
943
|
+
|
|
944
|
+
**Service**: `keycloakAdminService.deleteClient(clientId)`
|
|
945
|
+
|
|
946
|
+
**Behavior**:
|
|
947
|
+
|
|
948
|
+
- Permanently removes client from Keycloak
|
|
949
|
+
- All associated tokens become invalid
|
|
950
|
+
- Cannot be undone
|
|
951
|
+
|
|
952
|
+
### Status Lifecycle
|
|
953
|
+
|
|
954
|
+
```
|
|
955
|
+
Pending → Active (when Keycloak clients created successfully)
|
|
956
|
+
Pending → Error (when Keycloak client creation fails)
|
|
957
|
+
Error → Pending (when retry is initiated)
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
**Status Transitions**:
|
|
961
|
+
|
|
962
|
+
- **Pending**: Initial state, waiting for Keycloak client creation
|
|
963
|
+
- **Active**: All Keycloak clients created successfully
|
|
964
|
+
- **Error**: Keycloak client creation failed (error message stored)
|
|
965
|
+
- **Disabled**: Manually disabled (not used in current flow)
|
|
966
|
+
- **Completed**: Legacy status (not commonly used)
|
|
967
|
+
|
|
968
|
+
### Error Handling
|
|
969
|
+
|
|
970
|
+
#### Service Layer Errors
|
|
971
|
+
|
|
972
|
+
- **Database Errors**: Thrown immediately, caught by GraphQL resolver
|
|
973
|
+
- **Validation Errors**: Thrown before database operations
|
|
974
|
+
- **Inngest Trigger Errors**: Logged but don't fail the operation (event is queued)
|
|
975
|
+
|
|
976
|
+
#### Inngest Function Errors
|
|
977
|
+
|
|
978
|
+
- **Keycloak API Errors**: Logged, tenant status may remain `Pending` or change to `Error`
|
|
979
|
+
- **Partial Failures**: Individual client creation failures don't stop the entire process
|
|
980
|
+
- **Retry Mechanism**: Failed operations can be retried via `retryTenantCreation()`
|
|
981
|
+
|
|
982
|
+
### Performance Considerations
|
|
983
|
+
|
|
984
|
+
#### Async Processing
|
|
985
|
+
|
|
986
|
+
- **Immediate Response**: Service methods return immediately after triggering Inngest events
|
|
987
|
+
- **Background Processing**: Keycloak operations happen asynchronously
|
|
988
|
+
- **Status Updates**: Frontend can poll or use subscriptions to track status changes
|
|
989
|
+
|
|
990
|
+
#### Database Operations
|
|
991
|
+
|
|
992
|
+
- **Optimistic Updates**: Frontend updates immediately, backend confirms asynchronously
|
|
993
|
+
- **Idempotency**: Inngest functions are designed to be safely retried
|
|
994
|
+
- **Transaction Safety**: Database updates are atomic, but Keycloak operations are separate
|
|
995
|
+
|
|
996
|
+
### Monitoring & Debugging
|
|
997
|
+
|
|
998
|
+
#### Logging
|
|
999
|
+
|
|
1000
|
+
- **Service Layer**: Uses structured logging with tenant context
|
|
1001
|
+
- **Inngest Functions**: Console logs for debugging (can be enhanced with proper logging)
|
|
1002
|
+
- **Keycloak Operations**: Errors logged with clientId and tenantId
|
|
1003
|
+
|
|
1004
|
+
#### Tracking
|
|
1005
|
+
|
|
1006
|
+
- **Job IDs**: Each tenant creation gets a unique `jobId` for tracking
|
|
1007
|
+
- **Retry Count**: Failed operations track retry attempts
|
|
1008
|
+
- **Error Messages**: Stored in `tenant.errorMessage` for debugging
|
|
1009
|
+
|
|
1010
|
+
### Best Practices
|
|
1011
|
+
|
|
1012
|
+
1. **Always Check Status**: Before operations, verify tenant status
|
|
1013
|
+
2. **Handle Async Nature**: Keycloak operations are async; don't expect immediate completion
|
|
1014
|
+
3. **Idempotent Operations**: Inngest functions can be safely retried
|
|
1015
|
+
4. **Error Recovery**: Use `retryTenantCreation()` for failed operations
|
|
1016
|
+
5. **Secret Security**: Never log or expose secret values
|
|
1017
|
+
6. **Client ID Uniqueness**: Ensure clientIds are unique across all tenants
|
|
1018
|
+
|
|
1019
|
+
---
|
|
1020
|
+
|
|
1021
|
+
## Important Notes and Best Practices
|
|
1022
|
+
|
|
1023
|
+
### 1. Tenant ID vs ID
|
|
1024
|
+
|
|
1025
|
+
**Critical Distinction**:
|
|
1026
|
+
|
|
1027
|
+
- `id`: MongoDB ObjectId (used internally)
|
|
1028
|
+
- `tenantId`: String identifier (used in API calls and display)
|
|
1029
|
+
|
|
1030
|
+
**Usage**:
|
|
1031
|
+
|
|
1032
|
+
```typescript
|
|
1033
|
+
// For GraphQL mutations/queries
|
|
1034
|
+
variables: {
|
|
1035
|
+
id: tenant.tenantId;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// For display and internal references
|
|
1039
|
+
tenant.id; // MongoDB ID
|
|
1040
|
+
tenant.tenantId; // Tenant identifier
|
|
1041
|
+
```
|
|
1042
|
+
|
|
1043
|
+
### 2. Secret Key Management
|
|
1044
|
+
|
|
1045
|
+
**Important Behaviors**:
|
|
1046
|
+
|
|
1047
|
+
- **Creation**: At least one secret key is required when creating a tenant
|
|
1048
|
+
- **Editing**: Existing keys are preserved; only new keys can be added
|
|
1049
|
+
- **Revealing**: Secret values are fetched on-demand (not stored in state)
|
|
1050
|
+
- **Revocation**: Permanent action; cannot be undone
|
|
1051
|
+
- **Security**: Secrets are hidden by default; must explicitly reveal
|
|
1052
|
+
|
|
1053
|
+
**Best Practices**:
|
|
1054
|
+
|
|
1055
|
+
- Never log secret values
|
|
1056
|
+
- Use one-time reveal pattern
|
|
1057
|
+
- Always confirm before revocation
|
|
1058
|
+
- Rotate keys regularly
|
|
1059
|
+
|
|
1060
|
+
### 3. Form State Management
|
|
1061
|
+
|
|
1062
|
+
**Key Points**:
|
|
1063
|
+
|
|
1064
|
+
- Form state is managed in XState machine context
|
|
1065
|
+
- Field updates trigger machine events
|
|
1066
|
+
- Validation happens before submission
|
|
1067
|
+
- Form resets after successful operations
|
|
1068
|
+
|
|
1069
|
+
**Form Data Structure**:
|
|
1070
|
+
|
|
1071
|
+
```typescript
|
|
1072
|
+
{
|
|
1073
|
+
name: string;
|
|
1074
|
+
description: string;
|
|
1075
|
+
environment: string | undefined; // Environment tag ID
|
|
1076
|
+
projectId?: string; // Project ID
|
|
1077
|
+
clientConfigurations: Array<{
|
|
1078
|
+
name: string;
|
|
1079
|
+
description?: string;
|
|
1080
|
+
}>;
|
|
1081
|
+
}
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
### 4. Project and Environment Relationship
|
|
1085
|
+
|
|
1086
|
+
**Workflow**:
|
|
1087
|
+
|
|
1088
|
+
1. User selects a project
|
|
1089
|
+
2. System automatically fetches environment tags for that project
|
|
1090
|
+
3. First environment tag is auto-selected
|
|
1091
|
+
4. User can change environment if needed
|
|
1092
|
+
|
|
1093
|
+
**Important**: Environment tags are project-specific. If no project is selected, environment selection may be limited.
|
|
1094
|
+
|
|
1095
|
+
## Summary
|
|
1096
|
+
|
|
1097
|
+
The Tenant Management System provides a comprehensive, user-friendly interface for managing multi-tenant configurations. It leverages modern React patterns, state management with XState, GraphQL for efficient data operations, and Inngest for asynchronous background processing. The system is designed with type safety, error handling, and user experience as top priorities.
|
|
1098
|
+
|
|
1099
|
+
**Key Takeaways**:
|
|
1100
|
+
|
|
1101
|
+
**Frontend**:
|
|
1102
|
+
|
|
1103
|
+
- ✅ Full CRUD operations with proper state management
|
|
1104
|
+
- ✅ Secret key management with security best practices
|
|
1105
|
+
- ✅ Project and environment integration
|
|
1106
|
+
- ✅ Comprehensive error handling and user feedback
|
|
1107
|
+
- ✅ Type-safe implementation with TypeScript
|
|
1108
|
+
- ✅ Scalable architecture with XState and GraphQL
|
|
1109
|
+
|
|
1110
|
+
**Backend**:
|
|
1111
|
+
|
|
1112
|
+
- ✅ Event-driven architecture with Inngest for async operations
|
|
1113
|
+
- ✅ Keycloak integration for client management
|
|
1114
|
+
- ✅ Deterministic secret generation (no secret storage)
|
|
1115
|
+
- ✅ Status lifecycle management (Pending → Active/Error)
|
|
1116
|
+
- ✅ Retry mechanism for failed operations
|
|
1117
|
+
- ✅ Separation of concerns (Service handles DB, Inngest handles external APIs)
|