Skip to content

Commit

Permalink
- feat(web) adds gzip compression optimization to the cookie-based se…
Browse files Browse the repository at this point in the history
…ssion storage scheme, greatly alleviating the 4KB request header limitation in Serverless scenarios, while also reducing the request header transfer burden

- feat(grommet) Optimize the LocaleMenu component
- feat(grommet) update Cellbang icon
- feat(fact) adds `@Icon()` decorator for injecting custom icon components
- feat(cli) provides the default site icon, the template no longer provides favicon.ico files, developers can override the default by placing a custom favicon.ico file at the root of the project
- feat(security + oauth2-client) provides authentication success custom redirect URL capability for OIDC authentication
  • Loading branch information
muxiangqiu committed Nov 19, 2020
1 parent 2810fd4 commit 44a970a
Show file tree
Hide file tree
Showing 25 changed files with 131 additions and 60 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Change Log

## v1.6.0

- feat(web) adds gzip compression optimization to the cookie-based session storage scheme, greatly alleviating the 4KB request header limitation in Serverless scenarios, while also reducing the request header transfer burden
- feat(grommet) Optimize the LocaleMenu component
- feat(grommet) update Cellbang icon
- feat(fact) adds `@Icon()` decorator for injecting custom icon components
- feat(cli) provides the default site icon, the template no longer provides favicon.ico files, developers can override the default by placing a custom favicon.ico file at the root of the project
- feat(security + oauth2-client) provides authentication success custom redirect URL capability for OIDC authentication
- feat(web) 基于 Cookie 的 Session 存储方案添加 gzip 压缩优化,极大缓解在 Serverless 场景下的请求头 4KB 限制,同时也减轻请求头传输负担
- feat(grommet) 优化 LocaleMenu 组件
- feat(grommet) 更新 Cellbang 图标
- feat(react) 添加 `@Icon()` 装饰器,用于注入自定义的图标组件
- feat(cli) 提供默认网站图标,模板中不再提供 favicon.ico 文件,开发者可以通过在项目根下放置自定义的 favicon.ico 文件覆盖默认
- feat(security + oauth2-client) 为 OIDC 认证提供认证成功自定义重定向 URL 能力


## v1.5.1
- feat (cli) supports static module configuration `staticModules` to avoid repeated packaging of public modules for dynamic modules
- feat (cli) built-in es6 to es5 capability, specify the module to be converted through attribute configuration
Expand Down
40 changes: 13 additions & 27 deletions dev-packages/cli/src/webpack/config/base-config-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,19 @@ export class BaseConfigFactory {
name: target,
mode: webpackMode,
optimization: {
minimize: !dev
minimize: !dev,
minimizer: [
new TerserPlugin({
terserOptions: {
output: {
comments: false,
},
keep_classnames: true,
keep_fnames: true
},
extractComments: false
})
]
},
devtool: dev ? 'source-map' : false,
stats: 'minimal',
Expand Down Expand Up @@ -61,20 +73,6 @@ export class BaseConfigFactory {
__dirname: false,
__filename: false
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
output: {
comments: false,
},
keep_classnames: true,
keep_fnames: true
},
extractComments: false
})
]
},
devtool: 'source-map',
});
} else {
Expand All @@ -89,18 +87,6 @@ export class BaseConfigFactory {
performance: {
hints: false
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
output: {
comments: false,
}
},
extractComments: false
})
]
},
module: {
rules: [
{
Expand Down
4 changes: 3 additions & 1 deletion dev-packages/cli/src/webpack/config/plugin-config-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,16 @@ export class HtmlWebpackPluginConfigFactory {
const templateExists = existsSync(templatePath);
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BaseHrefWebpackPlugin } = require('base-href-webpack-plugin');
const templatePathBase = path.join(__dirname, '..', '..', '..', 'templates');

const c = getMalaguConfig(cfg, FRONTEND_TARGET);
const baseHref = c.server?.path;
return {
plugins: [
new HtmlWebpackPlugin({
title: 'Malagu App',
template: templateExists ? undefined : path.join(__dirname, '..', '..', '..', 'templates', 'index.html'),
template: templateExists ? undefined : path.join(templatePathBase, 'index.html'),
favicon: faviconExists ? undefined : path.join(templatePathBase, 'favicon.ico'),
templateParameters: getConfig(cfg, FRONTEND_TARGET),
...getWebpackConfig(cfg, FRONTEND_TARGET).htmlWebpackPlugin || {},
...(templateExists ? { template: templatePath } : {}),
Expand Down
Binary file removed dev-packages/cli/templates/admin-app/favicon.ico
Binary file not shown.
Binary file added dev-packages/cli/templates/favicon.ico
Binary file not shown.
Binary file not shown.
Binary file removed dev-packages/cli/templates/sample-app/favicon.ico
Binary file not shown.
22 changes: 22 additions & 0 deletions packages/grommet/src/browser/icon/cellbang.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as React from 'react';
import { Blank, IconProps } from 'grommet-icons';
import { Icon } from '@malagu/react';

export function Cellbang(props: IconProps) {
return (
<Blank {...props}>
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 182 182">
<g>
<rect x="0" y="20" width="30" height="138" stroke="none"/>
<rect x="0" y="0" width="180" height="40" rx="20" ry="20" stroke="none"/>
<rect x="0" y="140" width="180" height="40" rx="20" ry="20" stroke="none"/>
<rect x="150" y="20" width="30" height="138" stroke="none"/>
<rect x="90" y="80" width="80" height="40" stroke="none"/>
</g>
</svg>
</Blank>
);
}

@Icon(Cellbang)
export default class {};
17 changes: 12 additions & 5 deletions packages/grommet/src/browser/icon/icon-resolver.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import * as React from 'react';
import { Component } from '@malagu/core';
import { Component, Autowired, PostConstruct } from '@malagu/core';
import * as icons from 'grommet-icons';
import { IconProps } from './icon-protocol';
import { IconResolver } from '@malagu/react';
import { Cellbang } from './icon';

(icons as any).Cellbang = Cellbang;
import { ICON, IconResolver } from '@malagu/react';

@Component(IconResolver)
export class IconResolverImpl implements IconResolver<IconProps> {

@Autowired(ICON)
protected readonly extendedIcons: React.ComponentType<any>[];

@PostConstruct()
protected init() {
for (const icon of this.extendedIcons) {
(icons as any)[icon.name] = icon;
}
}

async resolve({ icon, ...rest }: IconProps): Promise<React.ReactNode> {
if (!icon) {
return <></>;
Expand Down
13 changes: 1 addition & 12 deletions packages/grommet/src/browser/icon/icon.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
import * as React from 'react';
import { IconProps } from './icon-protocol';
import { Icon as RawIcon } from '@malagu/react';
import { Blank, IconProps as RawIconProps } from 'grommet-icons';
import { Icon as RawIcon } from '@malagu/react/lib/browser/icon/icon';

export function Icon(iconProps: IconProps) {
return (<RawIcon {...iconProps}/>);
}

export function Cellbang(props: RawIconProps) {
return (
<Blank {...props}>
<svg viewBox="0 0 24 14.832" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="14.832" />
</svg>
</Blank>
);
}
1 change: 1 addition & 0 deletions packages/grommet/src/browser/icon/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './icon';
export * from './cellbang';
export * from './icon-protocol';
export * from './icon-resolver';
12 changes: 7 additions & 5 deletions packages/grommet/src/browser/locale-menu/locale-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import * as React from 'react';
import { Menu, MenuProps, ButtonType } from 'grommet';
import { Menu, MenuProps, ButtonType, Text } from 'grommet';
import { ContainerUtil } from '@malagu/core';
import { LocaleManager, Locale } from '@malagu/widget';
import { useIntl, IntlShape } from 'react-intl';
import { Down } from 'grommet-icons';

function parseLabel(locale: Locale, intl: IntlShape) {
return locale.label ? intl.formatMessage({ id: locale.label }) : intl.formatDisplayName(locale.lang, { type: 'language' } );
function parseLabel(intl: IntlShape, locale?: Locale) {
if (locale) {
return locale.label ? intl.formatMessage({ id: locale.label }) : intl.formatDisplayName(locale.lang, { type: 'language' } );
}
}

export function LocaleMenu(props: MenuProps & Omit<ButtonType, 'icon'>) {
Expand All @@ -20,8 +22,8 @@ export function LocaleMenu(props: MenuProps & Omit<ButtonType, 'icon'>) {
return () => subscription.unsubscribe();
}, []);
return (
<Menu size="medium" hoverIndicator icon={<Down/>} {...props} label={parseLabel(current!, intl)}
items={locales.map(l => ({ label: parseLabel(l, intl), onClick: () => localeManager.currentSubject.next(l) }))}
<Menu size="medium" hoverIndicator icon={<Down size={props.size}/>} {...props} label={parseLabel(intl, current)}
items={locales.map(l => ({ label: <Text size={props.size}>{parseLabel(intl, l)}</Text>, onClick: () => localeManager.currentSubject.next(l) }))}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { AUTHORIZATION_REQUEST_BASE_URI } from '../constants';
import { AuthorizationRequestResolver, AuthorizationRequestManager, AUTHORIZATION_REQUEST_REDIRECT_MIDDLEWARE_PRIORITY } from './authorization-protocol';
import { AuthorizationGrantType, AuthorizationRequest } from '@malagu/oauth2-core';
import { RedirectStrategy } from '@malagu/web/lib/node';
import { HttpStatus } from '@malagu/web';
import { HttpMethod, HttpStatus } from '@malagu/web';
import { ClientAuthorizationError } from '../error';
import { RequestCache } from '@malagu/security';

Expand All @@ -29,6 +29,9 @@ export class AuthorizationRequestRedirectMiddleware implements Middleware {
@Autowired(RequestCache)
protected readonly requestCache: RequestCache;

@Value('malagu.security.targetUrlParameter')
protected readonly targetUrlParameter: string;

@Autowired(Logger)
protected readonly logger: Logger;

Expand Down Expand Up @@ -71,6 +74,16 @@ export class AuthorizationRequestRedirectMiddleware implements Middleware {
if (AuthorizationGrantType.AuthorizationCode === authorizationRequest.authorizationGrantType) {
await this.authorizationRequestManager.save(authorizationRequest);
}
if (this.targetUrlParameter) {
const redirectUrl = Context.getRequest().query[this.targetUrlParameter];
if (redirectUrl) {
await this.requestCache.save({
redirectUrl,
method: HttpMethod.GET,
query: {}
});
}
}
await this.redirectStrategy.send(authorizationRequest.authorizationRequestUri);
}

Expand Down
10 changes: 10 additions & 0 deletions packages/react/src/browser/annotation/icon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ReactComponent } from './react-component';
import { ICON } from '../icon/icon-protocol';
import * as React from 'react';

export const Icon =
function (component?: React.ComponentType<any>, rebind: boolean = false): (target: any) => any {
return (t: any) => {
ReactComponent(ICON, component || t, rebind)(t);
};
};
1 change: 1 addition & 0 deletions packages/react/src/browser/annotation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export * from './route';
export * from './view';
export * from './router';
export * from './app';
export * from './icon';
export * from './context';
export * from './default-layout';
2 changes: 2 additions & 0 deletions packages/react/src/browser/icon/icon-protocol.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as React from 'react';

export const ICON = Symbol('Icon');

export const IconResolver = Symbol('IconResolver');

export interface IconResolver<T> {
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/browser/icon/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './icon';
export * from './icon-protocol';
1 change: 1 addition & 0 deletions packages/security/malagu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ malagu:
logoutSuccessUrl: /login
alwaysUseDefaultTargetUrl: false
useReferer: false
targetUrlParameter: redirect
backend:
malagu:
security:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class DefaultAuthenticationSuccessHandler implements AuthenticationSucces
@Value('malagu.security.loginSuccessUrl')
protected readonly loginSuccessUrl: string;

@Value('malagu.security.loginSuccessUrl')
@Value('malagu.security.alwaysUseLoginSuccessUrl')
protected readonly alwaysUseLoginSuccessUrl: boolean;

@Value('malagu.security.useReferer')
Expand Down
2 changes: 1 addition & 1 deletion packages/security/src/node/cache/cache-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface SavedRequest {
}

export interface RequestCache {
save(): Promise<void>;
save(savedRequest?: SavedRequest): Promise<void>;
get(): Promise<SavedRequest | undefined>;
remove(): Promise<void>;

Expand Down
4 changes: 2 additions & 2 deletions packages/security/src/node/cache/request-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export class HttpSessionRequestCache implements RequestCache {
@Autowired(PathResolver)
protected readonly pathResolver: PathResolver;

async save(): Promise<void> {
async save(savedRequest?: SavedRequest): Promise<void> {
if (Context.getSession()) {
const request = Context.getRequest();
Context.setAttr(SAVED_REQUEST, {
Context.setAttr(SAVED_REQUEST, savedRequest || {
redirectUrl: await this.pathResolver.resolve(this.endpoint, request.path, qs.stringify(request.query)),
method: request.method.toUpperCase(),
query: { ...request.query }
Expand Down
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"cors": "^2.8.5",
"crc": "^3.8.0",
"express-http-context": "^1.2.3",
"node-gzip": "^1.1.2",
"qs": "^6.9.4",
"url-pattern": "^1.0.3",
"uuid": "^3.4.0"
Expand Down
5 changes: 4 additions & 1 deletion packages/web/src/node/session/session-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export class SessionManagerImpl implements SessionManager {
@Value('malagu.session.sessionIdKey')
protected readonly sessionIdKey: string;

@Value('malagu.session')
protected readonly sessionOptions: any;

@Autowired(SessionStrategy)
protected readonly sessionStrategy: SessionStrategy;

Expand Down Expand Up @@ -50,7 +53,7 @@ export class SessionManagerImpl implements SessionManager {
}
if (await this.sessionStrategy.shouldSaveSession(session)) {
await this.sessionStore.set(session);
Context.getCookies().set(this.sessionIdKey, session.id);
Context.getCookies().set(this.sessionIdKey, session.id, this.sessionOptions);
}
}

Expand Down
17 changes: 14 additions & 3 deletions packages/web/src/node/session/session-store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Session, SessionStrategy, SessionStore, COOKIE_EXP_DATE } from './session-protocol';
import { Autowired, Value, Component } from '@malagu/core';
import { Autowired, Value, Component, Logger } from '@malagu/core';
import { Context } from '../context';
const { gzip, ungzip } = require('node-gzip');

@Component(SessionStore)
export class CookieSessionStore implements SessionStore {
Expand All @@ -13,15 +14,25 @@ export class CookieSessionStore implements SessionStore {
@Autowired(SessionStrategy)
protected readonly sessionStrategy: SessionStrategy;

@Autowired(Logger)
protected readonly logger: Logger;

async get(id: string): Promise<Session | undefined> {
const value = Context.getCookies().get(this.sessionOptions.sessionKey, this.sessionOptions);
if (value) {
return this.sessionStrategy.create(JSON.parse(Buffer.from(value, 'base64').toString('utf8')));
try {
const decompressed = await ungzip(Buffer.from(value, 'base64'));
return this.sessionStrategy.create(JSON.parse(decompressed.toString()));
} catch (error) {
this.logger.error(error);
}

}
}

async set(session: Session): Promise<void> {
Context.getCookies().set(this.sessionOptions.sessionKey, Buffer.from(JSON.stringify(session.toJSON())).toString('base64'), this.sessionOptions);
const compressed = await gzip(JSON.stringify(session.toJSON()));
Context.getCookies().set(this.sessionOptions.sessionKey, compressed.toString('base64'), this.sessionOptions);
}

async remove(id: string): Promise<void> {
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9623,6 +9623,11 @@ node-gyp-build@~3.7.0:
resolved "https://registry.npm.taobao.org/node-gyp-build/download/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d"
integrity sha1-2qd6T1R7mu0+Kqx3nq8VGv1g7I0=

node-gzip@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/node-gzip/-/node-gzip-1.1.2.tgz#245bd171b31ce7c7f50fc4cd0ca7195534359afb"
integrity sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw==

node-libs-browser@^2.2.1:
version "2.2.1"
resolved "https://registry.npm.taobao.org/node-libs-browser/download/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
Expand Down

0 comments on commit 44a970a

Please sign in to comment.