mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
refactor(core,ui): fix cache issue when sign in to live preview (#3495)
This commit is contained in:
parent
c17fb35298
commit
5a2f3c3461
8 changed files with 65 additions and 18 deletions
|
@ -9,7 +9,6 @@ import i18next from 'i18next';
|
||||||
import Provider, { errors, ResourceServer } from 'oidc-provider';
|
import Provider, { errors, ResourceServer } from 'oidc-provider';
|
||||||
import snakecaseKeys from 'snakecase-keys';
|
import snakecaseKeys from 'snakecase-keys';
|
||||||
|
|
||||||
import { wellKnownCache } from '#src/caches/well-known.js';
|
|
||||||
import type { EnvSet } from '#src/env-set/index.js';
|
import type { EnvSet } from '#src/env-set/index.js';
|
||||||
import { addOidcEventListeners } from '#src/event-listeners/index.js';
|
import { addOidcEventListeners } from '#src/event-listeners/index.js';
|
||||||
import koaAuditLog from '#src/middleware/koa-audit-log.js';
|
import koaAuditLog from '#src/middleware/koa-audit-log.js';
|
||||||
|
@ -151,16 +150,11 @@ export default function initOidc(
|
||||||
|
|
||||||
const appendParameters = (path: string) => {
|
const appendParameters = (path: string) => {
|
||||||
// `notification` is for showing a text banner on the homepage
|
// `notification` is for showing a text banner on the homepage
|
||||||
return isDemoApp ? path + `?notification=demo_app.notification` : path;
|
return isDemoApp ? path + `?notification=demo_app.notification&no_cache` : path;
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (interaction.prompt.name) {
|
switch (interaction.prompt.name) {
|
||||||
case 'login': {
|
case 'login': {
|
||||||
// Always fetch the latest sign-in experience config for demo app (live preview)
|
|
||||||
if (isDemoApp) {
|
|
||||||
wellKnownCache.invalidate(tenantId, ['sie', 'sie-full']);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSignUp =
|
const isSignUp =
|
||||||
ctx.oidc.params?.[OIDCExtraParametersKey.InteractionMode] === InteractionMode.signUp;
|
ctx.oidc.params?.[OIDCExtraParametersKey.InteractionMode] === InteractionMode.signUp;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default function koaInteractionSie<StateT, ContextT, ResponseT>(
|
||||||
tenantId: string
|
tenantId: string
|
||||||
): MiddlewareType<StateT, WithInteractionSieContext<ContextT>, ResponseT> {
|
): MiddlewareType<StateT, WithInteractionSieContext<ContextT>, ResponseT> {
|
||||||
return async (ctx, next) => {
|
return async (ctx, next) => {
|
||||||
if (noCache(ctx.headers)) {
|
if (noCache(ctx.request)) {
|
||||||
wellKnownCache.invalidate(tenantId, ['sie']);
|
wellKnownCache.invalidate(tenantId, ['sie']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,9 @@ export default function wellKnownRoutes<T extends AnonymousRouter>(
|
||||||
'/.well-known/sign-in-exp',
|
'/.well-known/sign-in-exp',
|
||||||
koaGuard({ response: guardFullSignInExperience, status: 200 }),
|
koaGuard({ response: guardFullSignInExperience, status: 200 }),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
if (noCache(ctx.headers)) {
|
if (noCache(ctx.request)) {
|
||||||
wellKnownCache.invalidate(tenantId, ['sie', 'sie-full']);
|
wellKnownCache.invalidate(tenantId, ['sie', 'sie-full']);
|
||||||
|
console.log('invalidated');
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = await getFullSignInExperience();
|
ctx.body = await getFullSignInExperience();
|
||||||
|
@ -59,7 +60,7 @@ export default function wellKnownRoutes<T extends AnonymousRouter>(
|
||||||
status: 200,
|
status: 200,
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
if (noCache(ctx.headers)) {
|
if (noCache(ctx.request)) {
|
||||||
wellKnownCache.invalidate(tenantId, ['sie', 'phrases-lng-tags', 'phrases']);
|
wellKnownCache.invalidate(tenantId, ['sie', 'phrases-lng-tags', 'phrases']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import type { IncomingHttpHeaders } from 'http';
|
import type { Request } from 'koa';
|
||||||
|
|
||||||
export const noCache = (headers: IncomingHttpHeaders): boolean =>
|
export const noCache = (request: Request): boolean =>
|
||||||
headers['cache-control']?.split(',').some((value) => value.trim().toLowerCase() === 'no-cache') ??
|
Boolean(
|
||||||
false;
|
request.headers['cache-control']
|
||||||
|
?.split(',')
|
||||||
|
.some((value) => ['no-cache', 'no-store'].includes(value.trim().toLowerCase()))
|
||||||
|
) || request.URL.searchParams.get('no_cache') !== null;
|
||||||
|
|
|
@ -23,10 +23,13 @@ import SocialLinkAccount from './pages/SocialLinkAccount';
|
||||||
import SocialSignIn from './pages/SocialSignInCallback';
|
import SocialSignIn from './pages/SocialSignInCallback';
|
||||||
import Springboard from './pages/Springboard';
|
import Springboard from './pages/Springboard';
|
||||||
import VerificationCode from './pages/VerificationCode';
|
import VerificationCode from './pages/VerificationCode';
|
||||||
|
import { handleSearchParametersData } from './utils/search-parameters';
|
||||||
import { getSignInExperienceSettings, setFavIcon } from './utils/sign-in-experience';
|
import { getSignInExperienceSettings, setFavIcon } from './utils/sign-in-experience';
|
||||||
|
|
||||||
import './scss/normalized.scss';
|
import './scss/normalized.scss';
|
||||||
|
|
||||||
|
handleSearchParametersData();
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const { context, Provider } = usePageContext();
|
const { context, Provider } = usePageContext();
|
||||||
const { experienceSettings, setLoading, setExperienceSettings } = context;
|
const { experienceSettings, setLoading, setExperienceSettings } = context;
|
||||||
|
|
|
@ -3,13 +3,29 @@
|
||||||
* The API will be deprecated in the future once SSR is implemented.
|
* The API will be deprecated in the future once SSR is implemented.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { conditionalString } from '@silverhand/essentials';
|
import type { Nullable, Optional } from '@silverhand/essentials';
|
||||||
|
import { conditional } from '@silverhand/essentials';
|
||||||
import ky from 'ky';
|
import ky from 'ky';
|
||||||
|
|
||||||
import type { SignInExperienceResponse } from '@/types';
|
import type { SignInExperienceResponse } from '@/types';
|
||||||
|
import { searchKeys } from '@/utils/search-parameters';
|
||||||
|
|
||||||
|
const buildSearchParameters = (record: Record<string, Nullable<Optional<string>>>) => {
|
||||||
|
const entries = Object.entries(record).filter((entry): entry is [string, string] =>
|
||||||
|
Boolean(entry[0] && entry[1])
|
||||||
|
);
|
||||||
|
|
||||||
|
return conditional(entries.length > 0 && entries);
|
||||||
|
};
|
||||||
|
|
||||||
export const getSignInExperience = async <T extends SignInExperienceResponse>(): Promise<T> => {
|
export const getSignInExperience = async <T extends SignInExperienceResponse>(): Promise<T> => {
|
||||||
return ky.get('/api/.well-known/sign-in-exp').json<T>();
|
return ky
|
||||||
|
.get('/api/.well-known/sign-in-exp', {
|
||||||
|
searchParams: buildSearchParameters({
|
||||||
|
[searchKeys.noCache]: sessionStorage.getItem(searchKeys.noCache),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.json<T>();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPhrases = async ({
|
export const getPhrases = async ({
|
||||||
|
@ -31,4 +47,9 @@ export const getPhrases = async ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.get(`/api/.well-known/phrases${conditionalString(language && `?lng=${language}`)}`);
|
.get('/api/.well-known/phrases', {
|
||||||
|
searchParams: buildSearchParameters({
|
||||||
|
[searchKeys.noCache]: sessionStorage.getItem(searchKeys.noCache),
|
||||||
|
lng: language,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { AppNotification as Notification } from '@/components/Notification';
|
import { AppNotification as Notification } from '@/components/Notification';
|
||||||
import usePlatform from '@/hooks/use-platform';
|
import usePlatform from '@/hooks/use-platform';
|
||||||
|
import { searchKeys } from '@/utils/search-parameters';
|
||||||
|
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ const AppNotification = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNotification(new URLSearchParams(window.location.search).get('notification'));
|
setNotification(sessionStorage.getItem(searchKeys.notification));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
24
packages/ui/src/utils/search-parameters.ts
Normal file
24
packages/ui/src/utils/search-parameters.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
export const searchKeys = Object.freeze({
|
||||||
|
notification: 'notification',
|
||||||
|
noCache: 'no_cache',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const handleSearchParametersData = () => {
|
||||||
|
const { search } = window.location;
|
||||||
|
|
||||||
|
if (!search) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameters = new URLSearchParams(search);
|
||||||
|
|
||||||
|
const notification = parameters.get(searchKeys.notification);
|
||||||
|
|
||||||
|
if (notification) {
|
||||||
|
sessionStorage.setItem(searchKeys.notification, notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.get(searchKeys.noCache) !== null) {
|
||||||
|
sessionStorage.setItem(searchKeys.noCache, 'true');
|
||||||
|
}
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue