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

refactor: remove demo app db entity (#3165)

This commit is contained in:
Gao Sun 2023-02-21 11:45:06 +08:00 committed by GitHub
parent dfa17ba555
commit e3f88f5250
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 104 additions and 92 deletions

View file

@ -4,7 +4,6 @@ import path from 'path';
import {
defaultSignInExperience,
createDefaultAdminConsoleConfig,
createDemoAppApplication,
defaultTenantId,
adminTenantId,
defaultManagementApi,
@ -125,8 +124,6 @@ export const seedTables = async (
await Promise.all([
connection.query(insertInto(createDefaultAdminConsoleConfig(defaultTenantId), 'logto_configs')),
connection.query(insertInto(defaultSignInExperience, 'sign_in_experiences')),
// TODO: @gao remove demo app
connection.query(insertInto(createDemoAppApplication(defaultTenantId), 'applications')),
updateDatabaseTimestamp(connection, latestTimestamp),
]);
};

View file

@ -1,9 +1,7 @@
import type { AdminConsoleKey } from '@logto/phrases';
import type { Application } from '@logto/schemas';
import { AppearanceMode, demoAppApplicationId } from '@logto/schemas';
import { AppearanceMode } from '@logto/schemas';
import { useContext, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import useSWR from 'swr';
import CheckDemoDark from '@/assets/images/check-demo-dark.svg';
import CheckDemo from '@/assets/images/check-demo.svg';
@ -19,7 +17,6 @@ import SocialDark from '@/assets/images/social-dark.svg';
import Social from '@/assets/images/social.svg';
import { ConnectorsTabs } from '@/consts/page-tabs';
import { AppEndpointsContext } from '@/containers/AppEndpointsProvider';
import { RequestError } from '@/hooks/use-api';
import useConfigs from '@/hooks/use-configs';
import useDocumentationUrl from '@/hooks/use-documentation-url';
import { useTheme } from '@/hooks/use-theme';
@ -41,21 +38,7 @@ const useGetStartedMetadata = () => {
const { userEndpoint } = useContext(AppEndpointsContext);
const theme = useTheme();
const isLightMode = theme === AppearanceMode.LightMode;
const { data: demoApp, error } = useSWR<Application, RequestError>(
`api/applications/${demoAppApplicationId}`,
{
shouldRetryOnError: (error: unknown) => {
if (error instanceof RequestError) {
return error.status !== 404;
}
return true;
},
}
);
const navigate = useNavigate();
const isLoadingDemoApp = !demoApp && !error;
const hideDemo = error?.status === 404;
const data = useMemo(() => {
const metadataItems: GetStartedMetadata[] = [
@ -66,7 +49,6 @@ const useGetStartedMetadata = () => {
icon: isLightMode ? CheckDemo : CheckDemoDark,
buttonText: 'general.check_out',
isComplete: configs?.demoChecked,
isHidden: hideDemo,
onClick: async () => {
void updateConfigs({ demoChecked: true });
window.open(new URL('/demo-app', userEndpoint), '_blank');
@ -142,7 +124,6 @@ const useGetStartedMetadata = () => {
configs?.passwordlessConfigured,
configs?.socialSignInConfigured,
configs?.furtherReadingsChecked,
hideDemo,
updateConfigs,
userEndpoint,
navigate,
@ -153,7 +134,7 @@ const useGetStartedMetadata = () => {
data,
completedCount: data.filter(({ isComplete }) => isComplete).length,
totalCount: data.length,
isLoading: isLoadingDemoApp,
isLoading: false,
};
};

View file

@ -1,5 +1,6 @@
import { adminTenantId } from '@logto/schemas';
import { trySafe } from '@silverhand/essentials';
import type { Optional } from '@silverhand/essentials';
import { deduplicate, trySafe } from '@silverhand/essentials';
import type GlobalValues from './GlobalValues.js';
@ -23,3 +24,29 @@ export const getTenantEndpoint = (
return tenantUrl;
};
export const getTenantLocalhost = (
id: string,
{ urlSet, adminUrlSet, isDomainBasedMultiTenancy }: GlobalValues
): Optional<URL> => {
const adminUrl = trySafe(() => adminUrlSet.localhostUrl);
if (adminUrl && id === adminTenantId) {
return adminUrl;
}
if (!isDomainBasedMultiTenancy) {
return trySafe(() => urlSet.localhostUrl);
}
};
export const getTenantUrls = (id: string, globalValues: GlobalValues): URL[] => {
const endpoint = getTenantEndpoint(id, globalValues);
const localhost = getTenantLocalhost(id, globalValues);
return deduplicate(
[endpoint.toString(), localhost?.toString()].filter(
(value): value is string => typeof value === 'string'
)
).map((element) => new URL(element));
};

View file

@ -1,22 +0,0 @@
import { demoAppApplicationId } from '@logto/schemas';
import type { MiddlewareType } from 'koa';
import type Queries from '#src/tenants/Queries.js';
export default function koaCheckDemoApp<StateT, ContextT, ResponseBodyT>(
queries: Queries
): MiddlewareType<StateT, ContextT, ResponseBodyT> {
const { findApplicationById } = queries.applications;
return async (ctx, next) => {
try {
await findApplicationById(demoAppApplicationId);
await next();
return;
} catch {
ctx.throw(404);
}
};
}

View file

@ -1,13 +1,13 @@
import type { CreateApplication, OidcClientMetadata } from '@logto/schemas';
import type { CreateApplication } from '@logto/schemas';
import { ApplicationType, adminConsoleApplicationId, demoAppApplicationId } from '@logto/schemas';
import { tryThat } from '@logto/shared';
import { deduplicate } from '@silverhand/essentials';
import { addSeconds } from 'date-fns';
import type { AdapterFactory, AllClientMetadata } from 'oidc-provider';
import { errors } from 'oidc-provider';
import snakecaseKeys from 'snakecase-keys';
import { EnvSet, UserApps } from '#src/env-set/index.js';
import { EnvSet } from '#src/env-set/index.js';
import { getTenantUrls } from '#src/env-set/utils.js';
import type Queries from '#src/tenants/Queries.js';
import { appendPath } from '#src/utils/url.js';
@ -28,18 +28,18 @@ const buildAdminConsoleClientMetadata = (envSet: EnvSet): AllClientMetadata => {
};
};
const buildDemoAppUris = (
oidcClientMetadata: OidcClientMetadata
): Pick<OidcClientMetadata, 'redirectUris' | 'postLogoutRedirectUris'> => {
const { urlSet } = EnvSet.values;
const urls = urlSet.deduplicated().map((url) => appendPath(url, UserApps.DemoApp).toString());
const buildDemoAppClientMetadata = (envSet: EnvSet): AllClientMetadata => {
const urls = getTenantUrls(envSet.tenantId, EnvSet.values).map((url) =>
appendPath(url, '/demo-app').toString()
);
const data = {
redirectUris: deduplicate([...urls, ...oidcClientMetadata.redirectUris]),
postLogoutRedirectUris: deduplicate([...urls, ...oidcClientMetadata.postLogoutRedirectUris]),
return {
...getConstantClientMetadata(envSet, ApplicationType.SPA),
client_id: demoAppApplicationId,
client_name: 'Demo App',
redirect_uris: urls,
post_logout_redirect_uris: urls,
};
return data;
};
export default function postgresAdapter(
@ -76,8 +76,6 @@ export default function postgresAdapter(
client_name,
...getConstantClientMetadata(envSet, type),
...snakecaseKeys(oidcClientMetadata),
...(client_id === demoAppApplicationId &&
snakecaseKeys(buildDemoAppUris(oidcClientMetadata))),
// `node-oidc-provider` won't camelCase custom parameter keys, so we need to keep the keys camelCased
...customClientMetadata,
});
@ -90,6 +88,10 @@ export default function postgresAdapter(
return buildAdminConsoleClientMetadata(envSet);
}
if (id === demoAppApplicationId) {
return buildDemoAppClientMetadata(envSet);
}
return transpileClient(
await tryThat(findApplicationById(id), new errors.InvalidClient(`invalid client ${id}`))
);

View file

@ -25,7 +25,6 @@ const middlewareList = Object.freeze(
'oidc-error-handler',
'slonik-error-handler',
'spa-proxy',
'check-demo-app',
'console-redirect-proxy',
].map((name) => [name, buildMockMiddleware(name)] as const)
);

View file

@ -7,7 +7,6 @@ import mount from 'koa-mount';
import type Provider from 'oidc-provider';
import { AdminApps, EnvSet, UserApps } from '#src/env-set/index.js';
import koaCheckDemoApp from '#src/middleware/koa-check-demo-app.js';
import koaConnectorErrorHandler from '#src/middleware/koa-connector-error-handler.js';
import koaConsoleRedirectProxy from '#src/middleware/koa-console-redirect-proxy.js';
import koaErrorHandler from '#src/middleware/koa-error-handler.js';
@ -97,10 +96,7 @@ export default class Tenant implements TenantContext {
app.use(
mount(
'/' + UserApps.DemoApp,
compose([
koaCheckDemoApp(this.queries),
koaSpaProxy(mountedApps, UserApps.DemoApp, 5003, UserApps.DemoApp),
])
koaSpaProxy(mountedApps, UserApps.DemoApp, 5003, UserApps.DemoApp)
)
);
}

View file

@ -1,4 +1,4 @@
import { ApplicationType, demoAppApplicationId } from '@logto/schemas';
import { ApplicationType } from '@logto/schemas';
import { HTTPError } from 'got';
import {
@ -9,12 +9,6 @@ import {
} from '#src/api/index.js';
describe('admin console application', () => {
it('should get demo app details successfully', async () => {
const demoApp = await getApplication(demoAppApplicationId);
expect(demoApp.id).toBe(demoAppApplicationId);
});
it('should create application successfully', async () => {
const applicationName = 'test-create-app';
const applicationType = ApplicationType.SPA;

View file

@ -0,0 +1,52 @@
import { generateStandardId } from '@logto/core-kit';
import chalk from 'chalk';
import inquirer from 'inquirer';
import { sql } from 'slonik';
import type { AlterationScript } from '../lib/types/alteration.js';
const defaultTenantId = 'default';
const alteration: AlterationScript = {
up: async (pool) => {
const isCi = process.env.CI;
const { confirm } = await inquirer.prompt<{ confirm: boolean }>({
type: 'confirm',
name: 'confirm',
message: String(
chalk.bold(chalk.yellow('***CAUTION***')) +
'\n' +
'The application `demo-app` will be removed from your database.\n' +
'Usually this is harmless since the demo app will be still functional with predefined data.\n' +
'Are you sure to continue?'
),
default: false,
when: !isCi,
});
if (!isCi && !confirm) {
throw new Error('User cancelled alteration.');
}
await pool.query(sql`
delete from applications where id = 'demo-app';
`);
},
down: async (pool) => {
await pool.query(sql`
insert into applications
(tenant_id, id, secret, name, description, type, oidc_client_metadata)
values (
'default',
'demo-app',
${generateStandardId()},
'Demo App',
'Logto demo app.',
'SPA',
'{ "redirectUris": [], "postLogoutRedirectUris": [] }'::jsonb
)
`);
},
};
export default alteration;

View file

@ -48,6 +48,7 @@
"@types/node": "^18.11.18",
"@types/pluralize": "^0.0.29",
"camelcase": "^7.0.0",
"chalk": "^5.0.0",
"eslint": "^8.34.0",
"jest": "^29.1.2",
"lint-staged": "^13.0.0",

View file

@ -1,8 +1,3 @@
import { generateStandardId } from '@logto/core-kit';
import type { CreateApplication } from '../db-entries/index.js';
import { ApplicationType } from '../db-entries/index.js';
/**
* The fixed application ID for Admin Console.
*
@ -11,14 +6,3 @@ import { ApplicationType } from '../db-entries/index.js';
export const adminConsoleApplicationId = 'admin-console';
export const demoAppApplicationId = 'demo-app';
/** @deprecated Demo app database entity will be removed soon. */
export const createDemoAppApplication = (forTenantId: string): Readonly<CreateApplication> => ({
tenantId: forTenantId,
id: demoAppApplicationId,
secret: generateStandardId(),
name: 'Demo App',
description: 'Logto demo app.',
type: ApplicationType.SPA,
oidcClientMetadata: { redirectUris: [], postLogoutRedirectUris: [] },
});

View file

@ -653,6 +653,7 @@ importers:
'@types/pluralize': ^0.0.29
'@withtyped/server': ^0.8.0
camelcase: ^7.0.0
chalk: ^5.0.0
eslint: ^8.34.0
jest: ^29.1.2
lint-staged: ^13.0.0
@ -680,6 +681,7 @@ importers:
'@types/node': 18.11.18
'@types/pluralize': 0.0.29
camelcase: 7.0.0
chalk: 5.1.2
eslint: 8.34.0
jest: 29.1.2_@types+node@18.11.18
lint-staged: 13.0.0
@ -5283,7 +5285,6 @@ packages:
/chalk/5.1.2:
resolution: {integrity: sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
dev: false
/char-regex/1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}