@alepha/react 0.6.0 → 0.6.2
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/dist/index.browser.cjs +1 -1
- package/dist/index.browser.js +2 -2
- package/dist/index.cjs +127 -98
- package/dist/index.d.ts +71 -45
- package/dist/index.js +129 -100
- package/dist/{useAuth-DOVx2kqa.cjs → useAuth-B9ypF48n.cjs} +112 -44
- package/dist/{useAuth-i7wbKVrt.js → useAuth-Ps01oe8e.js} +113 -45
- package/package.json +13 -12
- package/src/components/Link.tsx +0 -22
- package/src/components/NestedView.tsx +0 -36
- package/src/constants/SSID.ts +0 -1
- package/src/contexts/RouterContext.ts +0 -15
- package/src/contexts/RouterLayerContext.ts +0 -10
- package/src/descriptors/$auth.ts +0 -28
- package/src/descriptors/$page.ts +0 -144
- package/src/errors/RedirectionError.ts +0 -7
- package/src/hooks/RouterHookApi.ts +0 -154
- package/src/hooks/useActive.ts +0 -57
- package/src/hooks/useAuth.ts +0 -29
- package/src/hooks/useClient.ts +0 -6
- package/src/hooks/useInject.ts +0 -12
- package/src/hooks/useQueryParams.ts +0 -59
- package/src/hooks/useRouter.ts +0 -28
- package/src/hooks/useRouterEvents.ts +0 -43
- package/src/hooks/useRouterState.ts +0 -23
- package/src/index.browser.ts +0 -21
- package/src/index.shared.ts +0 -28
- package/src/index.ts +0 -46
- package/src/providers/PageDescriptorProvider.ts +0 -52
- package/src/providers/ReactAuthProvider.ts +0 -410
- package/src/providers/ReactBrowserProvider.ts +0 -250
- package/src/providers/ReactServerProvider.ts +0 -301
- package/src/services/Auth.ts +0 -45
- package/src/services/Router.ts +0 -855
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as _alepha_core from '@alepha/core';
|
|
2
|
-
import { EventEmitter, Alepha,
|
|
2
|
+
import { EventEmitter, Alepha, Static as Static$1, TSchema as TSchema$1, KIND, Async, Class, TObject as TObject$1 } from '@alepha/core';
|
|
3
3
|
import * as react from 'react';
|
|
4
|
-
import react__default, { ReactNode,
|
|
4
|
+
import react__default, { ReactNode, FC, AnchorHTMLAttributes } from 'react';
|
|
5
5
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
6
|
import { UserAccountToken } from '@alepha/security';
|
|
7
7
|
import * as _alepha_server from '@alepha/server';
|
|
8
|
-
import {
|
|
8
|
+
import { HttpLink, ServerCookieProvider, CookieManager, HttpClient, ServerProvider, ServeDescriptorOptions, RouteObject } from '@alepha/server';
|
|
9
9
|
import { Root } from 'react-dom/client';
|
|
10
10
|
import { MatchFunction, ParamData } from 'path-to-regexp';
|
|
11
11
|
import { Configuration } from 'openid-client';
|
|
12
|
+
import { CheerioAPI } from 'cheerio';
|
|
12
13
|
|
|
13
14
|
/** Symbol key applied to readonly types */
|
|
14
15
|
declare const ReadonlyKind: unique symbol;
|
|
@@ -149,13 +150,7 @@ interface NestedViewProps {
|
|
|
149
150
|
* @param props
|
|
150
151
|
* @constructor
|
|
151
152
|
*/
|
|
152
|
-
declare const NestedView: (props: NestedViewProps) => string | number | boolean | react.ReactElement<any, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null;
|
|
153
|
-
|
|
154
|
-
interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
155
|
-
to: string;
|
|
156
|
-
children?: react__default.ReactNode;
|
|
157
|
-
}
|
|
158
|
-
declare const Link: (props: LinkProps) => react_jsx_runtime.JSX.Element;
|
|
153
|
+
declare const NestedView: (props: NestedViewProps) => string | number | bigint | boolean | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null;
|
|
159
154
|
|
|
160
155
|
declare class Router extends EventEmitter<RouterEvents> {
|
|
161
156
|
protected readonly log: _alepha_core.Logger;
|
|
@@ -348,6 +343,10 @@ interface Layer {
|
|
|
348
343
|
*
|
|
349
344
|
*/
|
|
350
345
|
props?: Record<string, any>;
|
|
346
|
+
/**
|
|
347
|
+
*
|
|
348
|
+
*/
|
|
349
|
+
error?: Error;
|
|
351
350
|
/**
|
|
352
351
|
*
|
|
353
352
|
*/
|
|
@@ -430,7 +429,7 @@ interface RouterRenderContext {
|
|
|
430
429
|
/**
|
|
431
430
|
*
|
|
432
431
|
*/
|
|
433
|
-
|
|
432
|
+
head?: RouterRenderHeadContext;
|
|
434
433
|
}
|
|
435
434
|
interface RouterRenderOptions extends RouterMatchOptions {
|
|
436
435
|
/**
|
|
@@ -456,7 +455,7 @@ interface RouterStackItem {
|
|
|
456
455
|
*/
|
|
457
456
|
error?: Error;
|
|
458
457
|
}
|
|
459
|
-
interface
|
|
458
|
+
interface RouterRenderHeadContext {
|
|
460
459
|
/**
|
|
461
460
|
*
|
|
462
461
|
*/
|
|
@@ -464,15 +463,15 @@ interface RouterRenderHelmetContext {
|
|
|
464
463
|
/**
|
|
465
464
|
*
|
|
466
465
|
*/
|
|
467
|
-
|
|
468
|
-
attributes?: Record<string, string>;
|
|
469
|
-
};
|
|
466
|
+
titleSeparator?: string;
|
|
470
467
|
/**
|
|
471
|
-
*
|
|
468
|
+
* Add html attributes to the <html> tag.
|
|
472
469
|
*/
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
470
|
+
htmlAttributes?: Record<string, string>;
|
|
471
|
+
/**
|
|
472
|
+
* Add html attributes to the <body> tag.
|
|
473
|
+
*/
|
|
474
|
+
bodyAttributes?: Record<string, string>;
|
|
476
475
|
/**
|
|
477
476
|
*
|
|
478
477
|
*/
|
|
@@ -503,7 +502,7 @@ interface RouterRenderResult {
|
|
|
503
502
|
declare class ReactAuthProvider {
|
|
504
503
|
protected readonly log: _alepha_core.Logger;
|
|
505
504
|
protected readonly alepha: Alepha;
|
|
506
|
-
protected readonly
|
|
505
|
+
protected readonly serverCookieProvider: ServerCookieProvider;
|
|
507
506
|
protected authProviders: AuthProvider[];
|
|
508
507
|
protected readonly authorizationCode: _alepha_server.CookieDescriptor<TObject<{
|
|
509
508
|
codeVerifier: TOptional<TString>;
|
|
@@ -517,7 +516,7 @@ declare class ReactAuthProvider {
|
|
|
517
516
|
scope: TOptional<TString>;
|
|
518
517
|
issued_at: TOptional<TNumber>;
|
|
519
518
|
}>>;
|
|
520
|
-
|
|
519
|
+
readonly user: _alepha_server.CookieDescriptor<TObject<{
|
|
521
520
|
id: TString;
|
|
522
521
|
name: TOptional<TString>;
|
|
523
522
|
email: TOptional<TString>;
|
|
@@ -526,7 +525,7 @@ declare class ReactAuthProvider {
|
|
|
526
525
|
/**
|
|
527
526
|
* Configure Fastify to forward Session Access Token to Header Authorization.
|
|
528
527
|
*/
|
|
529
|
-
protected readonly
|
|
528
|
+
protected readonly onRequest: _alepha_core.HookDescriptor<"server:onRequest">;
|
|
530
529
|
/**
|
|
531
530
|
*
|
|
532
531
|
* @param cookies
|
|
@@ -611,12 +610,23 @@ interface ReactHydrationState {
|
|
|
611
610
|
user?: ReactUser;
|
|
612
611
|
auth?: "server" | "client";
|
|
613
612
|
layers?: PreviousLayerData[];
|
|
613
|
+
links?: HttpLink[];
|
|
614
614
|
}
|
|
615
615
|
|
|
616
|
+
declare const envSchema$2: _alepha_core.TObject<{
|
|
617
|
+
REACT_ROOT_ID: TString;
|
|
618
|
+
}>;
|
|
619
|
+
declare module "@alepha/core" {
|
|
620
|
+
interface Env extends Partial<Static$1<typeof envSchema$2>> {
|
|
621
|
+
}
|
|
622
|
+
}
|
|
616
623
|
declare class ReactBrowserProvider {
|
|
617
624
|
protected readonly log: _alepha_core.Logger;
|
|
618
625
|
protected readonly client: HttpClient;
|
|
619
626
|
protected readonly router: Router;
|
|
627
|
+
protected readonly env: {
|
|
628
|
+
REACT_ROOT_ID: string;
|
|
629
|
+
};
|
|
620
630
|
protected root: Root;
|
|
621
631
|
transitioning?: {
|
|
622
632
|
to: string;
|
|
@@ -655,8 +665,15 @@ declare class ReactBrowserProvider {
|
|
|
655
665
|
previous?: PreviousLayerData[];
|
|
656
666
|
}): Promise<{
|
|
657
667
|
url: string;
|
|
668
|
+
context: RouterRenderContext;
|
|
658
669
|
}>;
|
|
659
|
-
|
|
670
|
+
/**
|
|
671
|
+
* Render the helmet context.
|
|
672
|
+
*
|
|
673
|
+
* @param ctx
|
|
674
|
+
* @protected
|
|
675
|
+
*/
|
|
676
|
+
protected renderHeadContext(ctx: RouterRenderHeadContext): void;
|
|
660
677
|
/**
|
|
661
678
|
* Get embedded layers from the server.
|
|
662
679
|
*
|
|
@@ -674,11 +691,6 @@ declare class ReactBrowserProvider {
|
|
|
674
691
|
* @protected
|
|
675
692
|
*/
|
|
676
693
|
protected ready: _alepha_core.HookDescriptor<"ready">;
|
|
677
|
-
/**
|
|
678
|
-
*
|
|
679
|
-
* @protected
|
|
680
|
-
*/
|
|
681
|
-
protected stop: _alepha_core.HookDescriptor<"stop">;
|
|
682
694
|
}
|
|
683
695
|
/**
|
|
684
696
|
*
|
|
@@ -815,12 +827,16 @@ interface PageDescriptorOptions<TConfig extends PageDescriptorConfigSchema = Pag
|
|
|
815
827
|
/**
|
|
816
828
|
*
|
|
817
829
|
*/
|
|
818
|
-
|
|
830
|
+
can?: () => boolean;
|
|
831
|
+
/**
|
|
832
|
+
*
|
|
833
|
+
*/
|
|
834
|
+
head?: RouterRenderHeadContext | ((props: TProps, previous?: RouterRenderHeadContext) => RouterRenderHeadContext);
|
|
819
835
|
/**
|
|
820
836
|
*
|
|
821
837
|
*/
|
|
822
838
|
notFoundHandler?: FC<{
|
|
823
|
-
|
|
839
|
+
url: string;
|
|
824
840
|
}>;
|
|
825
841
|
/**
|
|
826
842
|
*
|
|
@@ -833,6 +849,7 @@ interface PageDescriptorOptions<TConfig extends PageDescriptorConfigSchema = Pag
|
|
|
833
849
|
interface PageContext {
|
|
834
850
|
user?: UserAccountToken;
|
|
835
851
|
cookies?: CookieManager;
|
|
852
|
+
links?: HttpLink[];
|
|
836
853
|
}
|
|
837
854
|
interface PageDescriptorConfigValue<TConfig extends PageDescriptorConfigSchema = PageDescriptorConfigSchema> {
|
|
838
855
|
query: TConfig["query"] extends TSchema$1 ? Static$1<TConfig["query"]> : Record<string, string>;
|
|
@@ -857,6 +874,12 @@ declare const $page: {
|
|
|
857
874
|
[KIND]: string;
|
|
858
875
|
};
|
|
859
876
|
|
|
877
|
+
interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
878
|
+
to: string | PageDescriptor;
|
|
879
|
+
children?: react__default.ReactNode;
|
|
880
|
+
}
|
|
881
|
+
declare const Link: (props: LinkProps) => react_jsx_runtime.JSX.Element | null;
|
|
882
|
+
|
|
860
883
|
interface RouterContextValue {
|
|
861
884
|
router: Router;
|
|
862
885
|
alepha: Alepha;
|
|
@@ -875,7 +898,10 @@ declare class Auth {
|
|
|
875
898
|
alepha: Alepha;
|
|
876
899
|
log: _alepha_core.Logger;
|
|
877
900
|
client: HttpClient;
|
|
878
|
-
|
|
901
|
+
slugs: {
|
|
902
|
+
login: string;
|
|
903
|
+
logout: string;
|
|
904
|
+
};
|
|
879
905
|
start: _alepha_core.HookDescriptor<"start">;
|
|
880
906
|
login: (provider?: string) => void;
|
|
881
907
|
logout: () => void;
|
|
@@ -894,6 +920,7 @@ interface AuthDescriptorOptions {
|
|
|
894
920
|
interface AuthDescriptor {
|
|
895
921
|
[KIND]: typeof KEY;
|
|
896
922
|
options: AuthDescriptorOptions;
|
|
923
|
+
jwks: () => string;
|
|
897
924
|
}
|
|
898
925
|
declare const $auth: {
|
|
899
926
|
(options: AuthDescriptorOptions): AuthDescriptor;
|
|
@@ -962,47 +989,46 @@ declare const envSchema$1: TObject<{
|
|
|
962
989
|
REACT_SERVER_DIST: TString;
|
|
963
990
|
REACT_SERVER_PREFIX: TString;
|
|
964
991
|
REACT_SSR_ENABLED: TBoolean;
|
|
965
|
-
|
|
992
|
+
REACT_ROOT_ID: TString;
|
|
966
993
|
}>;
|
|
967
994
|
declare module "@alepha/core" {
|
|
968
995
|
interface Env extends Partial<Static$1<typeof envSchema$1>> {
|
|
969
996
|
}
|
|
970
997
|
interface State {
|
|
971
998
|
"ReactServerProvider.template"?: string;
|
|
999
|
+
"ReactServerProvider.ssr"?: boolean;
|
|
972
1000
|
}
|
|
973
1001
|
}
|
|
974
1002
|
declare class ReactServerProvider {
|
|
975
1003
|
protected readonly log: _alepha_core.Logger;
|
|
976
1004
|
protected readonly alepha: Alepha;
|
|
977
1005
|
protected readonly router: Router;
|
|
978
|
-
protected readonly
|
|
1006
|
+
protected readonly serverProvider: ServerProvider;
|
|
979
1007
|
protected readonly env: {
|
|
1008
|
+
REACT_ROOT_ID: string;
|
|
980
1009
|
REACT_SERVER_DIST: string;
|
|
981
1010
|
REACT_SERVER_PREFIX: string;
|
|
982
1011
|
REACT_SSR_ENABLED: boolean;
|
|
983
|
-
REACT_SSR_OUTLET: string;
|
|
984
1012
|
};
|
|
985
1013
|
protected readonly configure: _alepha_core.HookDescriptor<"configure">;
|
|
1014
|
+
id: string;
|
|
986
1015
|
protected configureRoutes(): Promise<void>;
|
|
987
1016
|
/**
|
|
988
|
-
* Check if the template contains the outlet.
|
|
989
1017
|
*
|
|
990
|
-
* @param
|
|
1018
|
+
* @param root
|
|
991
1019
|
* @protected
|
|
992
1020
|
*/
|
|
993
|
-
protected
|
|
1021
|
+
protected createStaticHandler(root: string): ServeDescriptorOptions;
|
|
994
1022
|
/**
|
|
995
1023
|
*
|
|
996
|
-
* @param
|
|
1024
|
+
* @param templateLoader
|
|
997
1025
|
* @protected
|
|
998
1026
|
*/
|
|
999
|
-
protected
|
|
1027
|
+
protected createHandler(templateLoader: () => Promise<string | undefined>): RouteObject;
|
|
1000
1028
|
/**
|
|
1001
1029
|
*
|
|
1002
|
-
* @param templateLoader
|
|
1003
1030
|
* @protected
|
|
1004
1031
|
*/
|
|
1005
|
-
protected createHandler(templateLoader: () => Promise<string | undefined>): CreateRoute;
|
|
1006
1032
|
protected processDescriptors(): void;
|
|
1007
1033
|
/**
|
|
1008
1034
|
*
|
|
@@ -1014,10 +1040,10 @@ declare class ReactServerProvider {
|
|
|
1014
1040
|
*
|
|
1015
1041
|
* @param url
|
|
1016
1042
|
* @param template
|
|
1017
|
-
* @param
|
|
1043
|
+
* @param args
|
|
1018
1044
|
*/
|
|
1019
|
-
ssr(url: URL, template
|
|
1020
|
-
protected
|
|
1045
|
+
ssr(url: URL, template: string, args?: PageContext): Promise<Response>;
|
|
1046
|
+
protected renderHeadContext($: CheerioAPI, headContext: RouterRenderHeadContext): void;
|
|
1021
1047
|
}
|
|
1022
1048
|
|
|
1023
1049
|
declare class RedirectionError extends Error {
|
|
@@ -1040,4 +1066,4 @@ declare class ReactModule {
|
|
|
1040
1066
|
constructor();
|
|
1041
1067
|
}
|
|
1042
1068
|
|
|
1043
|
-
export { $auth, $page, type AnchorProps, Auth, type AuthDescriptor, type AuthDescriptorOptions, type AuthHook, type AuthProvider, type HrefLike, type Layer, Link, NestedView, type PageContext, type PageDescriptor, type PageDescriptorConfigSchema, type PageDescriptorConfigValue, type PageDescriptorOptions, PageDescriptorProvider, type PageRoute, type PageRouteEntry, type PreviousLayerData, ReactAuthProvider, ReactBrowserProvider, type ReactHydrationState, ReactModule, ReactServerProvider, type ReactUser, RedirectionError, Router, RouterContext, type RouterContextValue, type RouterEvents, type RouterGoOptions, RouterHookApi, RouterLayerContext, type RouterLayerContextValue, type RouterMatchOptions, type RouterRenderContext, type
|
|
1069
|
+
export { $auth, $page, type AnchorProps, Auth, type AuthDescriptor, type AuthDescriptorOptions, type AuthHook, type AuthProvider, type HrefLike, type Layer, Link, NestedView, type PageContext, type PageDescriptor, type PageDescriptorConfigSchema, type PageDescriptorConfigValue, type PageDescriptorOptions, PageDescriptorProvider, type PageRoute, type PageRouteEntry, type PreviousLayerData, ReactAuthProvider, ReactBrowserProvider, type ReactHydrationState, ReactModule, ReactServerProvider, type ReactUser, RedirectionError, Router, RouterContext, type RouterContextValue, type RouterEvents, type RouterGoOptions, RouterHookApi, RouterLayerContext, type RouterLayerContextValue, type RouterMatchOptions, type RouterRenderContext, type RouterRenderHeadContext, type RouterRenderOptions, type RouterRenderResult, type RouterStackItem, type RouterState, type SessionAuthorizationCode, type SessionTokens, type TPropsDefault, type TPropsParentDefault, type UseActiveHook, type UseQueryParamsHookOptions, envSchema$1 as envSchema, pageDescriptorKey, useActive, useAuth, useClient, useInject, useQueryParams, useRouter, useRouterEvents, useRouterState };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { $logger, $inject, Alepha, t, $hook, autoInject } from '@alepha/core';
|
|
2
|
-
import {
|
|
3
|
-
import { $ as $auth, R as Router, a as $page, A as Auth, P as PageDescriptorProvider } from './useAuth-
|
|
4
|
-
export { L as Link, N as NestedView, l as ReactBrowserProvider, m as RedirectionError, b as RouterContext, d as RouterHookApi, c as RouterLayerContext, p as pageDescriptorKey, j as useActive, k as useAuth, e as useClient, u as useInject, f as useQueryParams, g as useRouter, h as useRouterEvents, i as useRouterState } from './useAuth-
|
|
2
|
+
import { ServerCookieProvider, $cookie, $route, BadRequestError, ServerProvider, ServerLinksProvider, ServerModule } from '@alepha/server';
|
|
3
|
+
import { $ as $auth, R as Router, a as $page, A as Auth, P as PageDescriptorProvider } from './useAuth-Ps01oe8e.js';
|
|
4
|
+
export { L as Link, N as NestedView, l as ReactBrowserProvider, m as RedirectionError, b as RouterContext, d as RouterHookApi, c as RouterLayerContext, p as pageDescriptorKey, j as useActive, k as useAuth, e as useClient, u as useInject, f as useQueryParams, g as useRouter, h as useRouterEvents, i as useRouterState } from './useAuth-Ps01oe8e.js';
|
|
5
5
|
import { discovery, allowInsecureRequests, refreshTokenGrant, randomPKCECodeVerifier, calculatePKCECodeChallenge, buildAuthorizationUrl, authorizationCodeGrant, buildEndSessionUrl } from 'openid-client';
|
|
6
6
|
import { existsSync } from 'node:fs';
|
|
7
7
|
import { readFile } from 'node:fs/promises';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
|
+
import { load } from 'cheerio';
|
|
9
10
|
import { renderToString } from 'react-dom/server';
|
|
10
11
|
import 'react/jsx-runtime';
|
|
11
12
|
import 'react';
|
|
@@ -15,7 +16,7 @@ import 'path-to-regexp';
|
|
|
15
16
|
class ReactAuthProvider {
|
|
16
17
|
log = $logger();
|
|
17
18
|
alepha = $inject(Alepha);
|
|
18
|
-
|
|
19
|
+
serverCookieProvider = $inject(ServerCookieProvider);
|
|
19
20
|
authProviders = [];
|
|
20
21
|
authorizationCode = $cookie({
|
|
21
22
|
name: "authorizationCode",
|
|
@@ -53,23 +54,30 @@ class ReactAuthProvider {
|
|
|
53
54
|
name: "configure",
|
|
54
55
|
handler: async () => {
|
|
55
56
|
const auths = this.alepha.getDescriptorValues($auth);
|
|
56
|
-
for (const
|
|
57
|
-
const options =
|
|
57
|
+
for (const { value, key, instance } of auths) {
|
|
58
|
+
const options = value.options;
|
|
58
59
|
if (options.oidc) {
|
|
60
|
+
this.log.debug(
|
|
61
|
+
`Discover OIDC auth provider -> ${options.oidc.issuer}`
|
|
62
|
+
);
|
|
63
|
+
const client = await discovery(
|
|
64
|
+
new URL(options.oidc.issuer),
|
|
65
|
+
options.oidc.clientId,
|
|
66
|
+
{
|
|
67
|
+
client_secret: options.oidc.clientSecret
|
|
68
|
+
},
|
|
69
|
+
void 0,
|
|
70
|
+
{
|
|
71
|
+
execute: [allowInsecureRequests]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
instance[key].jwks = () => {
|
|
75
|
+
return client.serverMetadata().jwks_uri;
|
|
76
|
+
};
|
|
59
77
|
this.authProviders.push({
|
|
60
|
-
name: options.name ??
|
|
78
|
+
name: options.name ?? key,
|
|
61
79
|
redirectUri: options.oidc.redirectUri ?? "/api/_oauth/callback",
|
|
62
|
-
client
|
|
63
|
-
new URL(options.oidc.issuer),
|
|
64
|
-
options.oidc.clientId,
|
|
65
|
-
{
|
|
66
|
-
client_secret: options.oidc.clientSecret
|
|
67
|
-
},
|
|
68
|
-
void 0,
|
|
69
|
-
{
|
|
70
|
-
execute: [allowInsecureRequests]
|
|
71
|
-
}
|
|
72
|
-
)
|
|
80
|
+
client
|
|
73
81
|
});
|
|
74
82
|
}
|
|
75
83
|
}
|
|
@@ -78,21 +86,19 @@ class ReactAuthProvider {
|
|
|
78
86
|
/**
|
|
79
87
|
* Configure Fastify to forward Session Access Token to Header Authorization.
|
|
80
88
|
*/
|
|
81
|
-
|
|
82
|
-
name: "
|
|
83
|
-
after: this.
|
|
84
|
-
handler: async (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
req.headers.authorization = `Bearer ${tokens.access_token}`;
|
|
90
|
-
}
|
|
91
|
-
if (this.user.get(req.cookies) && !this.tokens.get(req.cookies)) {
|
|
92
|
-
this.user.del(req.cookies);
|
|
93
|
-
}
|
|
89
|
+
onRequest = $hook({
|
|
90
|
+
name: "server:onRequest",
|
|
91
|
+
after: this.serverCookieProvider,
|
|
92
|
+
handler: async ({ request }) => {
|
|
93
|
+
if (request.cookies && !this.isViteFile(request.url.pathname) && !!this.authProviders.length) {
|
|
94
|
+
const tokens = await this.refresh(request.cookies);
|
|
95
|
+
if (tokens) {
|
|
96
|
+
request.headers.rep("authorization", `Bearer ${tokens.access_token}`);
|
|
94
97
|
}
|
|
95
|
-
|
|
98
|
+
if (this.user.get(request.cookies) && !this.tokens.get(request.cookies)) {
|
|
99
|
+
this.user.del(request.cookies);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
96
102
|
}
|
|
97
103
|
});
|
|
98
104
|
/**
|
|
@@ -121,7 +127,9 @@ class ReactAuthProvider {
|
|
|
121
127
|
});
|
|
122
128
|
return newTokens;
|
|
123
129
|
} catch (e) {
|
|
124
|
-
|
|
130
|
+
if (e instanceof Error) {
|
|
131
|
+
this.log.warn("Failed to refresh token", e.message);
|
|
132
|
+
}
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
this.tokens.del(cookies);
|
|
@@ -141,7 +149,7 @@ class ReactAuthProvider {
|
|
|
141
149
|
*/
|
|
142
150
|
login = $route({
|
|
143
151
|
security: false,
|
|
144
|
-
|
|
152
|
+
internal: true,
|
|
145
153
|
url: "/_oauth/login",
|
|
146
154
|
group: "auth",
|
|
147
155
|
method: "GET",
|
|
@@ -152,13 +160,13 @@ class ReactAuthProvider {
|
|
|
152
160
|
})
|
|
153
161
|
},
|
|
154
162
|
handler: async ({ query, cookies, url }) => {
|
|
155
|
-
const { client } = this.provider(query.provider);
|
|
163
|
+
const { client, redirectUri } = this.provider(query.provider);
|
|
156
164
|
const codeVerifier = randomPKCECodeVerifier();
|
|
157
165
|
const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
|
|
158
166
|
const scope = "openid profile email";
|
|
159
|
-
let redirect_uri =
|
|
167
|
+
let redirect_uri = redirectUri;
|
|
160
168
|
if (redirect_uri.startsWith("/")) {
|
|
161
|
-
redirect_uri = `${url.protocol}
|
|
169
|
+
redirect_uri = `${url.protocol}//${url.host}${redirect_uri}`;
|
|
162
170
|
}
|
|
163
171
|
const parameters = {
|
|
164
172
|
redirect_uri,
|
|
@@ -183,7 +191,7 @@ class ReactAuthProvider {
|
|
|
183
191
|
*/
|
|
184
192
|
callback = $route({
|
|
185
193
|
security: false,
|
|
186
|
-
|
|
194
|
+
internal: true,
|
|
187
195
|
url: "/_oauth/callback",
|
|
188
196
|
group: "auth",
|
|
189
197
|
method: "GET",
|
|
@@ -245,7 +253,7 @@ class ReactAuthProvider {
|
|
|
245
253
|
*/
|
|
246
254
|
logout = $route({
|
|
247
255
|
security: false,
|
|
248
|
-
|
|
256
|
+
internal: true,
|
|
249
257
|
url: "/_oauth/logout",
|
|
250
258
|
group: "auth",
|
|
251
259
|
method: "GET",
|
|
@@ -315,13 +323,13 @@ const envSchema$1 = t.object({
|
|
|
315
323
|
REACT_SERVER_DIST: t.string({ default: "client" }),
|
|
316
324
|
REACT_SERVER_PREFIX: t.string({ default: "" }),
|
|
317
325
|
REACT_SSR_ENABLED: t.boolean({ default: false }),
|
|
318
|
-
|
|
326
|
+
REACT_ROOT_ID: t.string({ default: "root" })
|
|
319
327
|
});
|
|
320
328
|
class ReactServerProvider {
|
|
321
329
|
log = $logger();
|
|
322
330
|
alepha = $inject(Alepha);
|
|
323
331
|
router = $inject(Router);
|
|
324
|
-
|
|
332
|
+
serverProvider = $inject(ServerProvider);
|
|
325
333
|
env = $inject(envSchema$1);
|
|
326
334
|
configure = $hook({
|
|
327
335
|
name: "configure",
|
|
@@ -329,7 +337,9 @@ class ReactServerProvider {
|
|
|
329
337
|
await this.configureRoutes();
|
|
330
338
|
}
|
|
331
339
|
});
|
|
340
|
+
id = Math.random().toString(36).substring(2, 7);
|
|
332
341
|
async configureRoutes() {
|
|
342
|
+
this.alepha.state("ReactServerProvider.ssr", false);
|
|
333
343
|
if (this.alepha.isTest()) {
|
|
334
344
|
this.processDescriptors();
|
|
335
345
|
}
|
|
@@ -337,14 +347,15 @@ class ReactServerProvider {
|
|
|
337
347
|
return;
|
|
338
348
|
}
|
|
339
349
|
if (process.env.VITE_ALEPHA_DEV === "true") {
|
|
350
|
+
const url = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}`;
|
|
340
351
|
this.log.info("SSR (vite) OK");
|
|
341
|
-
|
|
342
|
-
|
|
352
|
+
this.alepha.state("ReactServerProvider.ssr", true);
|
|
353
|
+
const templateUrl = `${url}/index.html`;
|
|
343
354
|
const route2 = this.createHandler(
|
|
344
|
-
() => fetch(templateUrl).then((it) => it.text()).catch(() => void 0)
|
|
355
|
+
() => fetch(templateUrl).then((it) => it.text()).catch(() => void 0)
|
|
345
356
|
);
|
|
346
|
-
await this.
|
|
347
|
-
await this.
|
|
357
|
+
await this.serverProvider.route(route2);
|
|
358
|
+
await this.serverProvider.route({
|
|
348
359
|
...route2,
|
|
349
360
|
url: "*"
|
|
350
361
|
});
|
|
@@ -366,37 +377,16 @@ class ReactServerProvider {
|
|
|
366
377
|
this.log.warn("Missing static files, SSR will be disabled");
|
|
367
378
|
return;
|
|
368
379
|
}
|
|
369
|
-
await this.
|
|
380
|
+
await this.serverProvider.serve(this.createStaticHandler(root));
|
|
370
381
|
}
|
|
371
|
-
const template = this.
|
|
372
|
-
this.alepha.state("ReactServerProvider.template") ?? await readFile(join(root, "index.html"), "utf-8")
|
|
373
|
-
);
|
|
382
|
+
const template = this.alepha.state("ReactServerProvider.template") ?? await readFile(join(root, "index.html"), "utf-8");
|
|
374
383
|
const route = this.createHandler(async () => template);
|
|
375
|
-
await this.
|
|
376
|
-
await this.
|
|
384
|
+
await this.serverProvider.route(route);
|
|
385
|
+
await this.serverProvider.route({
|
|
377
386
|
...route,
|
|
378
387
|
url: "*"
|
|
379
388
|
});
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Check if the template contains the outlet.
|
|
383
|
-
*
|
|
384
|
-
* @param template
|
|
385
|
-
* @protected
|
|
386
|
-
*/
|
|
387
|
-
checkTemplate(template) {
|
|
388
|
-
if (!template.includes(this.env.REACT_SSR_OUTLET)) {
|
|
389
|
-
if (!template.includes('<div id="root"></div>')) {
|
|
390
|
-
throw new Error(
|
|
391
|
-
`Missing React SSR outlet in index.html, please add ${this.env.REACT_SSR_OUTLET} to the index.html file`
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
return template.replace(
|
|
395
|
-
`<div id="root"></div>`,
|
|
396
|
-
`<div id="root">${this.env.REACT_SSR_OUTLET}</div>`
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
return template;
|
|
389
|
+
this.alepha.state("ReactServerProvider.ssr", true);
|
|
400
390
|
}
|
|
401
391
|
/**
|
|
402
392
|
*
|
|
@@ -433,13 +423,14 @@ class ReactServerProvider {
|
|
|
433
423
|
if (response) {
|
|
434
424
|
return response;
|
|
435
425
|
}
|
|
436
|
-
return await this.ssr(ctx.url, template,
|
|
437
|
-
user: ctx.user,
|
|
438
|
-
cookies: ctx.cookies
|
|
439
|
-
});
|
|
426
|
+
return await this.ssr(ctx.url, template, ctx);
|
|
440
427
|
}
|
|
441
428
|
};
|
|
442
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
*
|
|
432
|
+
* @protected
|
|
433
|
+
*/
|
|
443
434
|
processDescriptors() {
|
|
444
435
|
const pages = this.alepha.getDescriptorValues($page);
|
|
445
436
|
for (const { key, instance, value } of pages) {
|
|
@@ -478,13 +469,26 @@ class ReactServerProvider {
|
|
|
478
469
|
*
|
|
479
470
|
* @param url
|
|
480
471
|
* @param template
|
|
481
|
-
* @param
|
|
472
|
+
* @param args
|
|
482
473
|
*/
|
|
483
|
-
async ssr(url, template
|
|
474
|
+
async ssr(url, template, args = {}) {
|
|
475
|
+
const hasAuth = this.alepha.has(ReactAuthProvider);
|
|
476
|
+
if (!args.user && args.cookies && hasAuth) {
|
|
477
|
+
const auth = this.alepha.get(ReactAuthProvider);
|
|
478
|
+
args.user = auth.user.get(args.cookies);
|
|
479
|
+
if (args.user) {
|
|
480
|
+
args.user.roles = [];
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
if (this.alepha.has(ServerLinksProvider) && hasAuth) {
|
|
484
|
+
const srv = this.alepha.get(ServerLinksProvider);
|
|
485
|
+
args.links = await srv.links();
|
|
486
|
+
this.alepha.als.set("links", args.links);
|
|
487
|
+
}
|
|
484
488
|
const { element, layers, redirect, context } = await this.router.render(
|
|
485
489
|
url.pathname + url.search,
|
|
486
490
|
{
|
|
487
|
-
args
|
|
491
|
+
args
|
|
488
492
|
}
|
|
489
493
|
);
|
|
490
494
|
if (redirect) {
|
|
@@ -495,42 +499,67 @@ class ReactServerProvider {
|
|
|
495
499
|
}
|
|
496
500
|
});
|
|
497
501
|
}
|
|
498
|
-
const
|
|
502
|
+
const html = renderToString(element);
|
|
503
|
+
const $ = load(template);
|
|
499
504
|
const script = `<script>window.__ssr=${JSON.stringify({
|
|
505
|
+
links: args.links,
|
|
500
506
|
layers: layers.map((it) => ({
|
|
501
507
|
...it,
|
|
508
|
+
error: it.error ? {
|
|
509
|
+
...it.error,
|
|
510
|
+
name: it.error.name,
|
|
511
|
+
message: it.error.message,
|
|
512
|
+
stack: it.error.stack
|
|
513
|
+
// TODO: Hide stack in production ?
|
|
514
|
+
} : void 0,
|
|
502
515
|
index: void 0,
|
|
503
516
|
path: void 0,
|
|
504
517
|
element: void 0
|
|
505
518
|
}))
|
|
506
519
|
})}<\/script>`;
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
520
|
+
const body = $("body");
|
|
521
|
+
const root = body.find(`#${this.env.REACT_ROOT_ID}`);
|
|
522
|
+
if (root.length) {
|
|
523
|
+
root.html(html);
|
|
524
|
+
} else {
|
|
525
|
+
body.prepend(`<div id="${this.env.REACT_ROOT_ID}">${html}</div>`);
|
|
510
526
|
}
|
|
511
|
-
|
|
512
|
-
|
|
527
|
+
body.append(script);
|
|
528
|
+
if (context.head) {
|
|
529
|
+
this.renderHeadContext($, context.head);
|
|
513
530
|
}
|
|
514
|
-
|
|
515
|
-
return new Response(template, {
|
|
531
|
+
return new Response($.html(), {
|
|
516
532
|
headers: { "Content-Type": "text/html" }
|
|
517
533
|
});
|
|
518
534
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
535
|
+
renderHeadContext($, headContext) {
|
|
536
|
+
const head = $("head");
|
|
537
|
+
if (head) {
|
|
538
|
+
if (headContext.title) {
|
|
539
|
+
head.find("title").remove();
|
|
540
|
+
head.append(`<title>${headContext.title}</title>`);
|
|
541
|
+
}
|
|
542
|
+
if (headContext.meta) {
|
|
543
|
+
for (const it of headContext.meta) {
|
|
544
|
+
const meta = head.find(`meta[name="${it.name}"]`);
|
|
545
|
+
if (meta.length) {
|
|
546
|
+
meta.attr("content", it.content);
|
|
547
|
+
} else {
|
|
548
|
+
head.append(`<meta name="${it.name}" content="${it.content}" />`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (headContext.htmlAttributes) {
|
|
554
|
+
for (const [key, value] of Object.entries(headContext.htmlAttributes)) {
|
|
555
|
+
$("html").attr(key, value);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (headContext.bodyAttributes) {
|
|
559
|
+
for (const [key, value] of Object.entries(headContext.bodyAttributes)) {
|
|
560
|
+
$("body").attr(key, value);
|
|
531
561
|
}
|
|
532
562
|
}
|
|
533
|
-
return template;
|
|
534
563
|
}
|
|
535
564
|
}
|
|
536
565
|
|