From b13151b48068e04d8b1f0b17019b9008fe7d3067 Mon Sep 17 00:00:00 2001
From: JimmFly <447268514@qq.com>
Date: Fri, 31 May 2024 09:42:22 +0000
Subject: [PATCH] feat(core): add admin panel page (#7115)
The path is `/admin-panel`
---
.../admin-panel/admin-panel-header.tsx | 71 ++++++++
.../admin-panel/collapsible-item.tsx | 74 ++++++++
.../src/components/admin-panel/index.css.ts | 157 +++++++++++++++++
.../core/src/components/admin-panel/index.ts | 5 +
.../admin-panel/runtime-setting-row.tsx | 36 ++++
.../use-get-server-runtime-config.ts | 57 ++++++
.../use-update-server-runtime-config.ts | 41 +++++
.../core/src/components/admin-panel/utils.tsx | 61 +++++++
.../core/src/pages/admin-panel.css.ts | 59 +++++++
.../frontend/core/src/pages/admin-panel.tsx | 165 ++++++++++++++++++
packages/frontend/core/src/router.tsx | 4 +
.../src/graphql/get-server-runtime-config.gql | 11 ++
.../frontend/graphql/src/graphql/index.ts | 33 ++++
.../graphql/update-server-runtime-configs.gql | 6 +
packages/frontend/graphql/src/schema.ts | 41 +++++
15 files changed, 821 insertions(+)
create mode 100644 packages/frontend/core/src/components/admin-panel/admin-panel-header.tsx
create mode 100644 packages/frontend/core/src/components/admin-panel/collapsible-item.tsx
create mode 100644 packages/frontend/core/src/components/admin-panel/index.css.ts
create mode 100644 packages/frontend/core/src/components/admin-panel/index.ts
create mode 100644 packages/frontend/core/src/components/admin-panel/runtime-setting-row.tsx
create mode 100644 packages/frontend/core/src/components/admin-panel/use-get-server-runtime-config.ts
create mode 100644 packages/frontend/core/src/components/admin-panel/use-update-server-runtime-config.ts
create mode 100644 packages/frontend/core/src/components/admin-panel/utils.tsx
create mode 100644 packages/frontend/core/src/pages/admin-panel.css.ts
create mode 100644 packages/frontend/core/src/pages/admin-panel.tsx
create mode 100644 packages/frontend/graphql/src/graphql/get-server-runtime-config.gql
create mode 100644 packages/frontend/graphql/src/graphql/update-server-runtime-configs.gql
diff --git a/packages/frontend/core/src/components/admin-panel/admin-panel-header.tsx b/packages/frontend/core/src/components/admin-panel/admin-panel-header.tsx
new file mode 100644
index 0000000000..6e7c3b2b58
--- /dev/null
+++ b/packages/frontend/core/src/components/admin-panel/admin-panel-header.tsx
@@ -0,0 +1,71 @@
+import { Button, useConfirmModal } from '@affine/component';
+import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
+import { ArrowRightBigIcon, Logo1Icon } from '@blocksuite/icons';
+import { useCallback } from 'react';
+
+import * as styles from './index.css';
+import { formatValue } from './utils';
+
+export type ModifiedValues = {
+ id: string;
+ key: string;
+ expiredValue: any;
+ newValue: any;
+};
+
+export const AdminPanelHeader = ({
+ modifiedValues,
+ onConfirm,
+}: {
+ modifiedValues: ModifiedValues[];
+ onConfirm: () => void;
+}) => {
+ const { openConfirmModal } = useConfirmModal();
+ const { jumpToIndex } = useNavigateHelper();
+
+ const handleJumpToIndex = useCallback(() => jumpToIndex(), [jumpToIndex]);
+
+ return (
+
+
+
+
After editing, please click the Save button on the right.
+
+
+
+
+
+
+ );
+};
diff --git a/packages/frontend/core/src/components/admin-panel/collapsible-item.tsx b/packages/frontend/core/src/components/admin-panel/collapsible-item.tsx
new file mode 100644
index 0000000000..196bac76fb
--- /dev/null
+++ b/packages/frontend/core/src/components/admin-panel/collapsible-item.tsx
@@ -0,0 +1,74 @@
+import { ArrowDownSmallIcon } from '@blocksuite/icons';
+import * as Collapsible from '@radix-ui/react-collapsible';
+import { useCallback, useState } from 'react';
+
+import * as styles from './index.css';
+
+export const CollapsibleItem = ({
+ items,
+ initialOpen = false,
+ title,
+ currentModule,
+ changeModule,
+}: {
+ title: string;
+ items: string[];
+ initialOpen?: boolean;
+ currentModule?: string;
+ changeModule?: (module: string) => void;
+}) => {
+ const [open, setOpen] = useState(initialOpen);
+
+ const handleClick = useCallback(
+ (id: string, event?: React.MouseEvent) => {
+ event?.preventDefault();
+ const targetElement = document.getElementById(id);
+ if (targetElement) {
+ targetElement.scrollIntoView({
+ behavior: 'smooth',
+ block: 'center',
+ });
+ }
+ changeModule?.(title);
+ },
+ [changeModule, title]
+ );
+
+ return (
+
+
+
+
+ {items.map((item, index) => (
+
handleClick(item)}
+ >
+
+ {item}
+
+
+ ))}
+
+
+ );
+};
diff --git a/packages/frontend/core/src/components/admin-panel/index.css.ts b/packages/frontend/core/src/components/admin-panel/index.css.ts
new file mode 100644
index 0000000000..82d406fe52
--- /dev/null
+++ b/packages/frontend/core/src/components/admin-panel/index.css.ts
@@ -0,0 +1,157 @@
+import { cssVar } from '@toeverything/theme';
+import { style } from '@vanilla-extract/css';
+
+export const header = style({
+ width: '100%',
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: '12px 16px',
+ borderBottom: `1px solid ${cssVar('borderColor')}`,
+ gap: 8,
+ position: 'sticky',
+ backgroundColor: cssVar('backgroundPrimaryColor'),
+ zIndex: 2,
+});
+
+export const logo = style({
+ fontSize: 32,
+ cursor: 'pointer',
+});
+
+export const title = style({
+ fontSize: cssVar('fontH3'),
+ fontWeight: 'bold',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 8,
+ whiteSpace: 'pre-wrap',
+ textAlign: 'center',
+ justifyContent: 'center',
+});
+
+export const outLine = style({
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ flexWrap: 'wrap',
+});
+
+export const outLineHeader = style({
+ display: 'flex',
+ width: '100%',
+ cursor: 'pointer',
+ alignItems: 'center',
+ wordBreak: 'break-all',
+ wordWrap: 'break-word',
+ fontSize: cssVar('fontBase'),
+ fontWeight: 'bold',
+ textTransform: 'capitalize',
+ ':hover': {
+ backgroundColor: cssVar('hoverColor'),
+ },
+ selectors: {
+ '&[data-active=true]': {
+ background: cssVar('hoverColor'),
+ },
+ },
+ color: cssVar('textPrimaryColor'),
+ borderRadius: 4,
+ padding: 4,
+ marginBottom: 4,
+ ':visited': {
+ color: cssVar('textPrimaryColor'),
+ },
+});
+
+export const arrowIcon = style({
+ transform: 'rotate(-90deg)',
+ selectors: {
+ '&[data-open=true]': {
+ transform: 'rotate(0deg) translateY(3px) translateX(-3px)',
+ },
+ },
+ transition: 'transform 0.2s',
+});
+
+export const collapsibleContainer = style({
+ display: 'flex',
+ width: '100%',
+ flexDirection: 'column',
+ wordBreak: 'break-all',
+ wordWrap: 'break-word',
+});
+
+export const outLineContent = style({
+ fontSize: cssVar('fontSm'),
+ cursor: 'pointer',
+ borderRadius: 4,
+ padding: '4px 18px',
+ color: cssVar('textPrimaryColor'),
+ textTransform: 'capitalize',
+ ':hover': {
+ backgroundColor: cssVar('hoverColor'),
+ },
+});
+
+export const navText = style({
+ width: '100%',
+ color: cssVar('textPrimaryColor'),
+ padding: '4px 0px',
+ ':visited': {
+ color: cssVar('textPrimaryColor'),
+ },
+});
+
+export const settingItem = style({
+ maxWidth: '800px',
+ minHeight: '100px',
+ display: 'flex',
+ gap: 8,
+ padding: '16px 0',
+ borderBottom: `0.5px solid ${cssVar('borderColor')}`,
+});
+
+export const LeftItem = style({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 4,
+ width: '60%',
+});
+
+export const RightItem = style({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 8,
+ fontSize: cssVar('fontXs'),
+ color: cssVar('textPrimaryColor'),
+ width: '40%',
+ alignItems: 'flex-end',
+});
+
+export const settingItemTitle = style({
+ fontSize: cssVar('fontBase'),
+ fontWeight: 'bold',
+ wordBreak: 'break-all',
+ wordWrap: 'break-word',
+ marginBottom: 8,
+});
+
+export const settingItemDescription = style({
+ fontSize: cssVar('fontXs'),
+ color: cssVar('textSecondaryColor'),
+});
+
+export const changedValues = style({
+ background: cssVar('backgroundSecondaryColor'),
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 4,
+ minHeight: '64px',
+ borderRadius: 4,
+ padding: '12px 16px 16px 12px',
+ marginTop: 8,
+ overflow: 'hidden',
+ color: cssVar('textPrimaryColor'),
+ fontSize: cssVar('fontSm'),
+});
diff --git a/packages/frontend/core/src/components/admin-panel/index.ts b/packages/frontend/core/src/components/admin-panel/index.ts
new file mode 100644
index 0000000000..e85d9a3f4f
--- /dev/null
+++ b/packages/frontend/core/src/components/admin-panel/index.ts
@@ -0,0 +1,5 @@
+export * from './admin-panel-header';
+export * from './collapsible-item';
+export * from './runtime-setting-row';
+export * from './use-get-server-runtime-config';
+export * from './utils';
diff --git a/packages/frontend/core/src/components/admin-panel/runtime-setting-row.tsx b/packages/frontend/core/src/components/admin-panel/runtime-setting-row.tsx
new file mode 100644
index 0000000000..f298ea9a6c
--- /dev/null
+++ b/packages/frontend/core/src/components/admin-panel/runtime-setting-row.tsx
@@ -0,0 +1,36 @@
+import { type ReactNode } from 'react';
+
+import * as styles from './index.css';
+
+export const RuntimeSettingRow = ({
+ id,
+ title,
+ description,
+ lastUpdatedTime,
+ operation,
+ children,
+}: {
+ id: string;
+ title: string;
+ description: string;
+ lastUpdatedTime: string;
+ operation: ReactNode;
+ children: ReactNode;
+}) => {
+ const formatTime = new Date(lastUpdatedTime).toLocaleString();
+ return (
+
+
+
{title}
+
{description}
+
+ last updated at: {formatTime}
+
+
+
+ {operation}
+ {children}
+
+
+ );
+};
diff --git a/packages/frontend/core/src/components/admin-panel/use-get-server-runtime-config.ts b/packages/frontend/core/src/components/admin-panel/use-get-server-runtime-config.ts
new file mode 100644
index 0000000000..333d3604fe
--- /dev/null
+++ b/packages/frontend/core/src/components/admin-panel/use-get-server-runtime-config.ts
@@ -0,0 +1,57 @@
+import { useQuery } from '@affine/core/hooks/use-query';
+import { getServerRuntimeConfigQuery } from '@affine/graphql';
+import { useMemo } from 'react';
+
+export const useGetServerRuntimeConfig = () => {
+ const { data } = useQuery({
+ query: getServerRuntimeConfigQuery,
+ });
+
+ const serverRuntimeConfig = useMemo(
+ () =>
+ data?.serverRuntimeConfig.sort((a, b) => a.id.localeCompare(b.id)) ?? [],
+ [data]
+ );
+
+ // collect all the modules and config keys in each module
+ const moduleList = useMemo(() => {
+ const moduleMap: { [key: string]: string[] } = {};
+
+ serverRuntimeConfig.forEach(config => {
+ if (!moduleMap[config.module]) {
+ moduleMap[config.module] = [];
+ }
+ moduleMap[config.module].push(config.key);
+ });
+
+ return Object.keys(moduleMap)
+ .sort((a, b) => a.localeCompare(b))
+ .map(moduleName => ({
+ moduleName,
+ keys: moduleMap[moduleName].sort((a, b) => a.localeCompare(b)),
+ }));
+ }, [serverRuntimeConfig]);
+
+ // group config by module name
+ const configGroup = useMemo(() => {
+ const configMap = new Map();
+
+ serverRuntimeConfig.forEach(config => {
+ if (!configMap.has(config.module)) {
+ configMap.set(config.module, []);
+ }
+ configMap.get(config.module)?.push(config);
+ });
+
+ return Array.from(configMap.entries()).map(([moduleName, configs]) => ({
+ moduleName,
+ configs,
+ }));
+ }, [serverRuntimeConfig]);
+
+ return {
+ serverRuntimeConfig,
+ moduleList,
+ configGroup,
+ };
+};
diff --git a/packages/frontend/core/src/components/admin-panel/use-update-server-runtime-config.ts b/packages/frontend/core/src/components/admin-panel/use-update-server-runtime-config.ts
new file mode 100644
index 0000000000..ac3ee7f819
--- /dev/null
+++ b/packages/frontend/core/src/components/admin-panel/use-update-server-runtime-config.ts
@@ -0,0 +1,41 @@
+import { notify } from '@affine/component';
+import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
+import {
+ useMutateQueryResource,
+ useMutation,
+} from '@affine/core/hooks/use-mutation';
+import {
+ getServerRuntimeConfigQuery,
+ updateServerRuntimeConfigsMutation,
+} from '@affine/graphql';
+
+export const useUpdateServerRuntimeConfigs = () => {
+ const { trigger, isMutating } = useMutation({
+ mutation: updateServerRuntimeConfigsMutation,
+ });
+ const revalidate = useMutateQueryResource();
+
+ return {
+ trigger: useAsyncCallback(
+ async (values: any) => {
+ try {
+ await trigger(values);
+ await revalidate(getServerRuntimeConfigQuery);
+ notify.success({
+ title: 'Saved successfully',
+ message: 'Runtime configurations have been saved successfully.',
+ });
+ } catch (e) {
+ notify.error({
+ title: 'Failed to save',
+ message:
+ 'Failed to save runtime configurations, please try again later.',
+ });
+ console.error(e);
+ }
+ },
+ [revalidate, trigger]
+ ),
+ isMutating,
+ };
+};
diff --git a/packages/frontend/core/src/components/admin-panel/utils.tsx b/packages/frontend/core/src/components/admin-panel/utils.tsx
new file mode 100644
index 0000000000..67d8311845
--- /dev/null
+++ b/packages/frontend/core/src/components/admin-panel/utils.tsx
@@ -0,0 +1,61 @@
+import { Input, Switch } from '@affine/component';
+import type { RuntimeConfigType } from '@affine/graphql';
+
+export const renderInput = (
+ type: RuntimeConfigType,
+ value: any,
+ onChange: (value?: any) => void
+) => {
+ switch (type) {
+ case 'Boolean':
+ return ;
+ case 'String':
+ return (
+
+ );
+ case 'Number':
+ return (
+
+
+
+ );
+ //TODO: add more types
+ default:
+ return null;
+ }
+};
+
+export const isEqual = (a: any, b: any) => {
+ if (typeof a !== typeof b) return false;
+ if (typeof a === 'object') return JSON.stringify(a) === JSON.stringify(b);
+ return a === b;
+};
+
+export const formatValue = (value: any) => {
+ if (typeof value === 'object') return JSON.stringify(value);
+ return value.toString();
+};
+
+export const formatValueForInput = (value: any, type: RuntimeConfigType) => {
+ let newValue = null;
+ switch (type) {
+ case 'Boolean':
+ newValue = !!value;
+ break;
+ case 'String':
+ newValue = value;
+ break;
+ case 'Number':
+ newValue = Number(value);
+ break;
+ case 'Array':
+ newValue = value.split(',');
+ break;
+ case 'Object':
+ newValue = JSON.parse(value);
+ break;
+ default:
+ break;
+ }
+ return newValue;
+};
diff --git a/packages/frontend/core/src/pages/admin-panel.css.ts b/packages/frontend/core/src/pages/admin-panel.css.ts
new file mode 100644
index 0000000000..450168c4df
--- /dev/null
+++ b/packages/frontend/core/src/pages/admin-panel.css.ts
@@ -0,0 +1,59 @@
+import { cssVar } from '@toeverything/theme';
+import { style } from '@vanilla-extract/css';
+export const root = style({
+ height: '100vh',
+ width: '100vw',
+ display: 'flex',
+ flexDirection: 'column',
+ fontSize: cssVar('fontBase'),
+ position: 'relative',
+});
+
+export const container = style({
+ display: 'flex',
+ width: '100%',
+ height: '100%',
+});
+
+export const sideBar = style({
+ width: '300px',
+ display: 'flex',
+ flexDirection: 'column',
+ borderRight: `1px solid ${cssVar('borderColor')}`,
+ padding: '12px 8px',
+ height: '100%',
+ background: cssVar('backgroundPrimaryColor'),
+ zIndex: 3,
+});
+
+export const scrollArea = style({
+ padding: '24px 0 160px',
+ background: cssVar('backgroundPrimaryColor'),
+});
+
+export const main = style({
+ display: 'flex',
+ width: '100%',
+ flexDirection: 'column',
+ overflow: 'auto',
+ alignItems: 'center',
+});
+
+export const moduleContainer = style({
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ padding: '16px',
+ maxWidth: '800px',
+ margin: 'auto',
+ gap: 16,
+});
+
+export const module = style({
+ fontSize: cssVar('fontH5'),
+ fontWeight: 'bold',
+ marginBottom: 8,
+ textTransform: 'capitalize',
+ padding: '16px 0',
+ borderBottom: `0.5px solid ${cssVar('borderColor')}`,
+});
diff --git a/packages/frontend/core/src/pages/admin-panel.tsx b/packages/frontend/core/src/pages/admin-panel.tsx
new file mode 100644
index 0000000000..1ef87fb8f5
--- /dev/null
+++ b/packages/frontend/core/src/pages/admin-panel.tsx
@@ -0,0 +1,165 @@
+import { Scrollable } from '@affine/component';
+import type { RuntimeConfigType } from '@affine/graphql';
+import { FeatureType, getUserFeaturesQuery } from '@affine/graphql';
+import { useCallback, useMemo, useState } from 'react';
+
+import {
+ AdminPanelHeader,
+ CollapsibleItem,
+ formatValue,
+ formatValueForInput,
+ isEqual,
+ type ModifiedValues,
+ renderInput,
+ RuntimeSettingRow,
+ useGetServerRuntimeConfig,
+} from '../components/admin-panel';
+import { useUpdateServerRuntimeConfigs } from '../components/admin-panel/use-update-server-runtime-config';
+import { useNavigateHelper } from '../hooks/use-navigate-helper';
+import { useQuery } from '../hooks/use-query';
+import * as styles from './admin-panel.css';
+
+export const AdminPanel = () => {
+ const { serverRuntimeConfig, moduleList, configGroup } =
+ useGetServerRuntimeConfig();
+
+ const [currentModule, setCurrentModule] = useState(
+ moduleList[0].moduleName
+ );
+
+ const { trigger } = useUpdateServerRuntimeConfigs();
+
+ const [configValues, setConfigValues] = useState(
+ serverRuntimeConfig.reduce(
+ (acc, config) => {
+ acc[config.id] = config.value;
+ return acc;
+ },
+ {} as Record
+ )
+ );
+
+ const handleInputChange = useCallback(
+ (key: string, value: any, type: RuntimeConfigType) => {
+ const newValue = formatValueForInput(value, type);
+ setConfigValues(prevValues => ({
+ ...prevValues,
+ [key]: newValue,
+ }));
+ },
+ []
+ );
+
+ const modifiedValues: ModifiedValues[] = useMemo(() => {
+ return serverRuntimeConfig
+ .filter(config => !isEqual(config.value, configValues[config.id]))
+ .map(config => ({
+ id: config.id,
+ key: config.key,
+ expiredValue: config.value,
+ newValue: configValues[config.id],
+ }));
+ }, [configValues, serverRuntimeConfig]);
+
+ const handleSave = useCallback(() => {
+ // post value example: { "key1": "newValue1","key2": "newValue2"}
+ const updates: Record = {};
+
+ modifiedValues.forEach(item => {
+ if (item.id && item.newValue !== undefined) {
+ updates[item.id] = item.newValue;
+ }
+ });
+ trigger({ updates });
+ }, [modifiedValues, trigger]);
+
+ return (
+
+
+
+ {moduleList.map(module => (
+
+ ))}
+
+
+
+
+
+
+ {configGroup
+ .filter(group => group.moduleName === currentModule)
+ .map(group => {
+ const { moduleName, configs } = group;
+ return (
+
+
{moduleName}
+ {configs?.map(config => {
+ const { id, key, type, description, updatedAt } =
+ config;
+ const title = `${key} (${id})`;
+ const isValueEqual = isEqual(
+ config.value,
+ configValues[id]
+ );
+ const formatServerValue = formatValue(config.value);
+ const formatCurrentValue = formatValue(
+ configValues[id]
+ );
+ return (
+
handleInputChange(id, value, type)
+ )}
+ >
+
+ {formatServerValue} => {formatCurrentValue}
+
+
+ );
+ })}
+
+ );
+ })}
+
+
+
+
+
+
+
+ );
+};
+
+export const Component = () => {
+ const { data } = useQuery({
+ query: getUserFeaturesQuery,
+ });
+ const { jumpTo404 } = useNavigateHelper();
+ const userFeatures = data?.currentUser?.features;
+ if (!userFeatures || !userFeatures.includes(FeatureType.Admin)) {
+ jumpTo404();
+ return null;
+ }
+
+ return ;
+};
diff --git a/packages/frontend/core/src/router.tsx b/packages/frontend/core/src/router.tsx
index 04e38b6a48..d2a05d68da 100644
--- a/packages/frontend/core/src/router.tsx
+++ b/packages/frontend/core/src/router.tsx
@@ -48,6 +48,10 @@ export const topLevelRoutes = [
path: '/404',
lazy: () => import('./pages/404'),
},
+ {
+ path: '/admin-panel',
+ lazy: () => import('./pages/admin-panel'),
+ },
{
path: '/auth/:authType',
lazy: () => import('./pages/auth'),
diff --git a/packages/frontend/graphql/src/graphql/get-server-runtime-config.gql b/packages/frontend/graphql/src/graphql/get-server-runtime-config.gql
new file mode 100644
index 0000000000..9229b3c7e8
--- /dev/null
+++ b/packages/frontend/graphql/src/graphql/get-server-runtime-config.gql
@@ -0,0 +1,11 @@
+query getServerRuntimeConfig {
+ serverRuntimeConfig {
+ id
+ module
+ key
+ description
+ value
+ type
+ updatedAt
+ }
+}
diff --git a/packages/frontend/graphql/src/graphql/index.ts b/packages/frontend/graphql/src/graphql/index.ts
index f5a117cb5f..59781c32aa 100644
--- a/packages/frontend/graphql/src/graphql/index.ts
+++ b/packages/frontend/graphql/src/graphql/index.ts
@@ -385,6 +385,25 @@ query oauthProviders {
}`,
};
+export const getServerRuntimeConfigQuery = {
+ id: 'getServerRuntimeConfigQuery' as const,
+ operationName: 'getServerRuntimeConfig',
+ definitionName: 'serverRuntimeConfig',
+ containsFile: false,
+ query: `
+query getServerRuntimeConfig {
+ serverRuntimeConfig {
+ id
+ module
+ key
+ description
+ value
+ type
+ updatedAt
+ }
+}`,
+};
+
export const getUserFeaturesQuery = {
id: 'getUserFeaturesQuery' as const,
operationName: 'getUserFeatures',
@@ -818,6 +837,20 @@ query subscription {
}`,
};
+export const updateServerRuntimeConfigsMutation = {
+ id: 'updateServerRuntimeConfigsMutation' as const,
+ operationName: 'updateServerRuntimeConfigs',
+ definitionName: 'updateRuntimeConfigs',
+ containsFile: false,
+ query: `
+mutation updateServerRuntimeConfigs($updates: JSONObject!) {
+ updateRuntimeConfigs(updates: $updates) {
+ key
+ value
+ }
+}`,
+};
+
export const updateSubscriptionMutation = {
id: 'updateSubscriptionMutation' as const,
operationName: 'updateSubscription',
diff --git a/packages/frontend/graphql/src/graphql/update-server-runtime-configs.gql b/packages/frontend/graphql/src/graphql/update-server-runtime-configs.gql
new file mode 100644
index 0000000000..0ab081950c
--- /dev/null
+++ b/packages/frontend/graphql/src/graphql/update-server-runtime-configs.gql
@@ -0,0 +1,6 @@
+mutation updateServerRuntimeConfigs($updates: JSONObject!) {
+ updateRuntimeConfigs(updates: $updates) {
+ key
+ value
+ }
+}
diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts
index bfe9381f93..31ede1dcbc 100644
--- a/packages/frontend/graphql/src/schema.ts
+++ b/packages/frontend/graphql/src/schema.ts
@@ -533,6 +533,24 @@ export type OauthProvidersQuery = {
};
};
+export type GetServerRuntimeConfigQueryVariables = Exact<{
+ [key: string]: never;
+}>;
+
+export type GetServerRuntimeConfigQuery = {
+ __typename?: 'Query';
+ serverRuntimeConfig: Array<{
+ __typename?: 'ServerRuntimeConfigType';
+ id: string;
+ module: string;
+ key: string;
+ description: string;
+ value: Record;
+ type: RuntimeConfigType;
+ updatedAt: string;
+ }>;
+};
+
export type GetUserFeaturesQueryVariables = Exact<{ [key: string]: never }>;
export type GetUserFeaturesQuery = {
@@ -916,6 +934,19 @@ export type SubscriptionQuery = {
} | null;
};
+export type UpdateServerRuntimeConfigsMutationVariables = Exact<{
+ updates: Scalars['JSONObject']['input'];
+}>;
+
+export type UpdateServerRuntimeConfigsMutation = {
+ __typename?: 'Mutation';
+ updateRuntimeConfigs: Array<{
+ __typename?: 'ServerRuntimeConfigType';
+ key: string;
+ value: Record;
+ }>;
+};
+
export type UpdateSubscriptionMutationVariables = Exact<{
idempotencyKey: Scalars['String']['input'];
plan?: InputMaybe;
@@ -1139,6 +1170,11 @@ export type Queries =
variables: OauthProvidersQueryVariables;
response: OauthProvidersQuery;
}
+ | {
+ name: 'getServerRuntimeConfigQuery';
+ variables: GetServerRuntimeConfigQueryVariables;
+ response: GetServerRuntimeConfigQuery;
+ }
| {
name: 'getUserFeaturesQuery';
variables: GetUserFeaturesQueryVariables;
@@ -1371,6 +1407,11 @@ export type Mutations =
variables: SetWorkspacePublicByIdMutationVariables;
response: SetWorkspacePublicByIdMutation;
}
+ | {
+ name: 'updateServerRuntimeConfigsMutation';
+ variables: UpdateServerRuntimeConfigsMutationVariables;
+ response: UpdateServerRuntimeConfigsMutation;
+ }
| {
name: 'updateSubscriptionMutation';
variables: UpdateSubscriptionMutationVariables;