0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat(console,schemas): add new db config to record organization creation status (#4785)

This commit is contained in:
Charles Zhao 2023-10-31 09:34:44 +08:00 committed by GitHub
parent 161f012bc0
commit cee5717423
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 1 deletions

View file

@ -13,6 +13,7 @@ import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
import TextInput from '@/ds-components/TextInput';
import TextLink from '@/ds-components/TextLink';
import useApi from '@/hooks/use-api';
import useConfigs from '@/hooks/use-configs';
import useTenantPathname from '@/hooks/use-tenant-pathname';
import useTheme from '@/hooks/use-theme';
import { trySubmitSafe } from '@/utils/form';
@ -30,6 +31,7 @@ function CreateOrganization() {
const Icon = theme === Theme.Light ? OrganizationFeature : OrganizationFeatureDark;
const { navigate } = useTenantPathname();
const api = useApi();
const { updateConfigs } = useConfigs();
const {
register,
@ -43,6 +45,7 @@ function CreateOrganization() {
const onSubmit = handleSubmit(
trySubmitSafe(async (json) => {
await api.post(`api/organizations`, { json });
void updateConfigs({ organizationCreated: true });
navigate(`/organizations`);
})
);

View file

@ -6,6 +6,7 @@ import PageMeta from '@/components/PageMeta';
import Button from '@/ds-components/Button';
import CardTitle from '@/ds-components/CardTitle';
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
import useConfigs from '@/hooks/use-configs';
import useTenantPathname from '@/hooks/use-tenant-pathname';
import * as pageLayout from '@/scss/page-layout.module.scss';
@ -16,6 +17,7 @@ import * as styles from './index.module.scss';
const organizationsPathname = '/organizations';
const createPathname = `${organizationsPathname}/create`;
const organizationGuidePathname = '/organization-guide';
const tabs = Object.freeze({
settings: 'settings',
@ -29,6 +31,7 @@ function Organizations({ tab }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { navigate, match } = useTenantPathname();
const isCreating = match(createPathname);
const { configs } = useConfigs();
return (
<div className={pageLayout.container}>
@ -47,7 +50,7 @@ function Organizations({ tab }: Props) {
size="large"
title="organizations.create_organization"
onClick={() => {
navigate('/organization-guide');
navigate(configs?.organizationCreated ? createPathname : organizationGuidePathname);
}}
/>
</div>

View file

@ -98,6 +98,7 @@ export const mockAdminUserRole3: Role = {
export const mockAdminConsoleData: AdminConsoleData = {
signInExperienceCustomized: false,
organizationCreated: false,
};
export const mockPrivateKeys: OidcConfigKey[] = [

View file

@ -15,6 +15,7 @@ import { expectRejects } from '#src/helpers/index.js';
const defaultAdminConsoleConfig: AdminConsoleData = {
signInExperienceCustomized: false,
organizationCreated: false,
};
describe('admin console sign-in experience', () => {

View file

@ -0,0 +1,75 @@
import type { DatabaseTransactionConnection } from 'slonik';
import { sql } from 'slonik';
import type { AlterationScript } from '../lib/types/alteration.js';
const adminConsoleConfigKey = 'adminConsole';
type OldAdminConsoleData = {
signInExperienceCustomized: boolean;
} & Record<string, unknown>;
type OldLogtoAdminConsoleConfig = {
tenantId: string;
value: OldAdminConsoleData;
};
type NewAdminConsoleData = {
signInExperienceCustomized: boolean;
organizationCreated: boolean;
} & Record<string, unknown>;
type NewLogtoAdminConsoleConfig = {
tenantId: string;
value: NewAdminConsoleData;
};
const alterAdminConsoleData = async (
logtoConfig: OldLogtoAdminConsoleConfig,
pool: DatabaseTransactionConnection
) => {
const { tenantId, value: oldAdminConsoleConfig } = logtoConfig;
const newAdminConsoleData: NewAdminConsoleData = {
...oldAdminConsoleConfig,
organizationCreated: false,
};
await pool.query(
sql`update logto_configs set value = ${JSON.stringify(
newAdminConsoleData
)} where tenant_id = ${tenantId} and key = ${adminConsoleConfigKey}`
);
};
const rollbackAdminConsoleData = async (
logtoConfig: NewLogtoAdminConsoleConfig,
pool: DatabaseTransactionConnection
) => {
const { tenantId, value: newAdminConsoleConfig } = logtoConfig;
const { organizationCreated, ...oldAdminConsoleData } = newAdminConsoleConfig;
await pool.query(
sql`update logto_configs set value = ${JSON.stringify(
oldAdminConsoleData
)} where tenant_id = ${tenantId} and key = ${adminConsoleConfigKey}`
);
};
const alteration: AlterationScript = {
up: async (pool) => {
const rows = await pool.many<OldLogtoAdminConsoleConfig>(
sql`select * from logto_configs where key = ${adminConsoleConfigKey}`
);
await Promise.all(rows.map(async (row) => alterAdminConsoleData(row, pool)));
},
down: async (pool) => {
const rows = await pool.many<NewLogtoAdminConsoleConfig>(
sql`select * from logto_configs where key = ${adminConsoleConfigKey}`
);
await Promise.all(rows.map(async (row) => rollbackAdminConsoleData(row, pool)));
},
};
export default alteration;

View file

@ -16,6 +16,7 @@ export const createDefaultAdminConsoleConfig = (
key: LogtoTenantConfigKey.AdminConsole,
value: {
signInExperienceCustomized: false,
organizationCreated: false,
},
} satisfies CreateLogtoConfig);

View file

@ -48,6 +48,7 @@ export const logtoOidcConfigGuard: Readonly<{
/* --- Logto tenant configs --- */
export const adminConsoleDataGuard = z.object({
signInExperienceCustomized: z.boolean(),
organizationCreated: z.boolean(),
});
export type AdminConsoleData = z.infer<typeof adminConsoleDataGuard>;