@cloud-ru/uikit-product-claudia 1.11.1 → 1.12.0
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/CHANGELOG.md +11 -0
- package/dist/cjs/components/SshField/SshField.js +33 -46
- package/dist/cjs/components/SshField/components/MobileFieldAi/MobileFieldAi.d.ts +3 -0
- package/dist/cjs/components/SshField/components/MobileFieldAi/MobileFieldAi.js +2 -2
- package/dist/cjs/components/SshField/helperComponents/CheckItem/CheckItem.d.ts +6 -0
- package/dist/cjs/components/SshField/helperComponents/CheckItem/CheckItem.js +13 -0
- package/dist/cjs/components/SshField/helperComponents/CheckItem/index.d.ts +1 -0
- package/dist/cjs/components/SshField/helperComponents/CheckItem/index.js +17 -0
- package/dist/cjs/components/SshField/helperComponents/CheckItem/styles.module.css +25 -0
- package/dist/cjs/components/SshField/helperComponents/DropZoneContent/styles.module.css +1 -1
- package/dist/cjs/components/SshField/helperComponents/SshValidation/SshValidation.d.ts +6 -0
- package/dist/cjs/components/SshField/helperComponents/SshValidation/SshValidation.js +16 -0
- package/dist/cjs/components/SshField/helperComponents/SshValidation/index.d.ts +1 -0
- package/dist/cjs/components/SshField/helperComponents/SshValidation/index.js +17 -0
- package/dist/cjs/components/SshField/helperComponents/SshValidation/styles.module.css +35 -0
- package/dist/cjs/components/SshField/helperComponents/WithSshValidation/WithSshValidation.d.ts +8 -0
- package/dist/cjs/components/SshField/helperComponents/WithSshValidation/WithSshValidation.js +19 -0
- package/dist/cjs/components/SshField/helperComponents/WithSshValidation/index.d.ts +1 -0
- package/dist/cjs/components/SshField/helperComponents/WithSshValidation/index.js +17 -0
- package/dist/cjs/components/SshField/helperComponents/WithSshValidation/styles.module.css +5 -0
- package/dist/cjs/components/SshField/types.d.ts +12 -0
- package/dist/cjs/components/SshField/types.js +2 -0
- package/dist/cjs/components/SshField/utils/readFileContent.d.ts +6 -1
- package/dist/cjs/components/SshField/utils/readFileContent.js +6 -11
- package/dist/cjs/components/SshField/utils/validateSSHKey.d.ts +9 -3
- package/dist/cjs/components/SshField/utils/validateSSHKey.js +56 -59
- package/dist/esm/components/SshField/SshField.js +34 -47
- package/dist/esm/components/SshField/components/MobileFieldAi/MobileFieldAi.d.ts +3 -0
- package/dist/esm/components/SshField/components/MobileFieldAi/MobileFieldAi.js +2 -2
- package/dist/esm/components/SshField/helperComponents/CheckItem/CheckItem.d.ts +6 -0
- package/dist/esm/components/SshField/helperComponents/CheckItem/CheckItem.js +7 -0
- package/dist/esm/components/SshField/helperComponents/CheckItem/index.d.ts +1 -0
- package/dist/esm/components/SshField/helperComponents/CheckItem/index.js +1 -0
- package/dist/esm/components/SshField/helperComponents/CheckItem/styles.module.css +25 -0
- package/dist/esm/components/SshField/helperComponents/DropZoneContent/styles.module.css +1 -1
- package/dist/esm/components/SshField/helperComponents/SshValidation/SshValidation.d.ts +6 -0
- package/dist/esm/components/SshField/helperComponents/SshValidation/SshValidation.js +10 -0
- package/dist/esm/components/SshField/helperComponents/SshValidation/index.d.ts +1 -0
- package/dist/esm/components/SshField/helperComponents/SshValidation/index.js +1 -0
- package/dist/esm/components/SshField/helperComponents/SshValidation/styles.module.css +35 -0
- package/dist/esm/components/SshField/helperComponents/WithSshValidation/WithSshValidation.d.ts +8 -0
- package/dist/esm/components/SshField/helperComponents/WithSshValidation/WithSshValidation.js +13 -0
- package/dist/esm/components/SshField/helperComponents/WithSshValidation/index.d.ts +1 -0
- package/dist/esm/components/SshField/helperComponents/WithSshValidation/index.js +1 -0
- package/dist/esm/components/SshField/helperComponents/WithSshValidation/styles.module.css +5 -0
- package/dist/esm/components/SshField/types.d.ts +12 -0
- package/dist/esm/components/SshField/types.js +1 -0
- package/dist/esm/components/SshField/utils/readFileContent.d.ts +6 -1
- package/dist/esm/components/SshField/utils/readFileContent.js +6 -11
- package/dist/esm/components/SshField/utils/validateSSHKey.d.ts +9 -3
- package/dist/esm/components/SshField/utils/validateSSHKey.js +53 -55
- package/package.json +3 -2
- package/src/components/SshField/SshField.tsx +120 -121
- package/src/components/SshField/components/MobileFieldAi/MobileFieldAi.tsx +6 -5
- package/src/components/SshField/helperComponents/CheckItem/CheckItem.tsx +23 -0
- package/src/components/SshField/helperComponents/CheckItem/index.ts +1 -0
- package/src/components/SshField/helperComponents/CheckItem/styles.module.scss +31 -0
- package/src/components/SshField/helperComponents/DropZoneContent/styles.module.scss +1 -1
- package/src/components/SshField/helperComponents/SshValidation/SshValidation.tsx +36 -0
- package/src/components/SshField/helperComponents/SshValidation/index.ts +1 -0
- package/src/components/SshField/helperComponents/SshValidation/styles.module.scss +31 -0
- package/src/components/SshField/helperComponents/WithSshValidation/WithSshValidation.tsx +43 -0
- package/src/components/SshField/helperComponents/WithSshValidation/index.ts +1 -0
- package/src/components/SshField/helperComponents/WithSshValidation/styles.module.scss +7 -0
- package/src/components/SshField/types.ts +13 -0
- package/src/components/SshField/utils/readFileContent.ts +12 -11
- package/src/components/SshField/utils/validateSSHKey.ts +60 -68
- package/dist/cjs/components/SshField/utils/handleFileError.d.ts +0 -2
- package/dist/cjs/components/SshField/utils/handleFileError.js +0 -32
- package/dist/esm/components/SshField/utils/handleFileError.d.ts +0 -2
- package/dist/esm/components/SshField/utils/handleFileError.js +0 -28
- package/src/components/SshField/utils/handleFileError.ts +0 -41
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 1.12.0 (2025-12-17)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **AINFR-4587:** add new ssh validation rules ([cf6ad31](https://gitverse.ru/cloud-ru-tech/uikit-product/commits/cf6ad31b9549c73de2346a63fcd489fa2f39068f))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## 1.11.1 (2025-12-12)
|
|
7
18
|
|
|
8
19
|
### Only dependencies have been changed
|
|
@@ -39,8 +39,8 @@ const MobileFieldAi_1 = require("./components/MobileFieldAi");
|
|
|
39
39
|
const DropZoneContent_1 = require("./helperComponents/DropZoneContent");
|
|
40
40
|
const FieldSubmitButton_1 = require("./helperComponents/FieldSubmitButton");
|
|
41
41
|
const TextAreaActionsFooter_1 = require("./helperComponents/TextAreaActionsFooter");
|
|
42
|
+
const WithSshValidation_1 = require("./helperComponents/WithSshValidation");
|
|
42
43
|
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
43
|
-
const handleFileError_1 = require("./utils/handleFileError");
|
|
44
44
|
const isTouchDevice_1 = require("./utils/isTouchDevice");
|
|
45
45
|
const readFileContent_1 = require("./utils/readFileContent");
|
|
46
46
|
const validateSSHKey_1 = require("./utils/validateSSHKey");
|
|
@@ -52,36 +52,19 @@ exports.SshField = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
52
52
|
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
53
53
|
const [isDragOver, setIsDragOver] = (0, react_1.useState)(false);
|
|
54
54
|
const [isValueHidden, setIsValueHidden] = (0, react_1.useState)(true);
|
|
55
|
-
const [
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const getErrorMessage = (errorType) => {
|
|
59
|
-
switch (errorType) {
|
|
60
|
-
case 'EMPTY_FILE':
|
|
61
|
-
return t('SshField.errors.emptyFile');
|
|
62
|
-
case 'BINARY_DATA':
|
|
63
|
-
return t('SshField.errors.binaryData');
|
|
64
|
-
case 'INVALID_SSH_KEY':
|
|
65
|
-
return t('SshField.errors.invalidSSHKey');
|
|
66
|
-
case 'INVALID_EXTENSION':
|
|
67
|
-
case 'INVALID_MIME_TYPE':
|
|
68
|
-
case 'INVALID_FILE_TYPE':
|
|
69
|
-
return t('SshField.errors.invalidFileExtension');
|
|
70
|
-
case 'FILE_TOO_LARGE':
|
|
71
|
-
return t('SshField.errors.fileTooLarge');
|
|
72
|
-
case 'READ_ERROR':
|
|
73
|
-
return t('SshField.errors.readError');
|
|
74
|
-
case 'UNKNOWN_ERROR':
|
|
75
|
-
default:
|
|
76
|
-
return t('SshField.errors.unknownError');
|
|
77
|
-
}
|
|
78
|
-
};
|
|
55
|
+
const [sshValidation, setSshValidation] = (0, react_1.useState)(null);
|
|
56
|
+
const isSshValid = !sshValidation || Object.values(sshValidation).every(value => !value);
|
|
57
|
+
const isValueValid = isSshValid && typeof value === 'string' && value.trim().length > 0;
|
|
79
58
|
const handleChange = (newValue) => {
|
|
80
|
-
if (
|
|
81
|
-
|
|
59
|
+
if (sshValidation) {
|
|
60
|
+
setSshValidation(null);
|
|
82
61
|
}
|
|
83
|
-
if (onChange)
|
|
84
|
-
|
|
62
|
+
if (!onChange)
|
|
63
|
+
return;
|
|
64
|
+
onChange(newValue);
|
|
65
|
+
if (newValue) {
|
|
66
|
+
const sshValidationState = (0, validateSSHKey_1.validateSshKeyErrors)(newValue);
|
|
67
|
+
setSshValidation(sshValidationState);
|
|
85
68
|
}
|
|
86
69
|
};
|
|
87
70
|
const handleDragOver = (event) => {
|
|
@@ -112,18 +95,22 @@ exports.SshField = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
112
95
|
const onFileUpload = (file) => __awaiter(void 0, void 0, void 0, function* () {
|
|
113
96
|
try {
|
|
114
97
|
setIsLoading(true);
|
|
115
|
-
|
|
116
|
-
(0, validateSSHKey_1.
|
|
117
|
-
(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (onChange) {
|
|
121
|
-
onChange(fileContent);
|
|
98
|
+
setSshValidation(null);
|
|
99
|
+
const fileValidationErrorState = (0, validateSSHKey_1.validateFileErrors)(file);
|
|
100
|
+
if (fileValidationErrorState.fileType) {
|
|
101
|
+
setSshValidation(fileValidationErrorState);
|
|
102
|
+
return;
|
|
122
103
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
104
|
+
const { error, fileContent } = yield (0, readFileContent_1.readFileContent)(file);
|
|
105
|
+
if (error || typeof fileContent !== 'string') {
|
|
106
|
+
setSshValidation(Object.assign(Object.assign({}, fileValidationErrorState), { readError: true }));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const sshValidationState = (0, validateSSHKey_1.validateSshKeyErrors)(fileContent);
|
|
110
|
+
setSshValidation(Object.assign(Object.assign({}, fileValidationErrorState), sshValidationState));
|
|
111
|
+
if (!onChange)
|
|
112
|
+
return;
|
|
113
|
+
onChange(fileContent);
|
|
127
114
|
}
|
|
128
115
|
finally {
|
|
129
116
|
setIsLoading(false);
|
|
@@ -131,11 +118,11 @@ exports.SshField = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
131
118
|
}
|
|
132
119
|
});
|
|
133
120
|
if (isTouchDevice) {
|
|
134
|
-
return ((0, jsx_runtime_1.jsx)(MobileFieldAi_1.MobileFieldAi, Object.assign({}, props, (0, uikit_product_mobile_fields_1.getAdaptiveFieldProps)(props), { onSubmit: handleSubmit, submitEnabled: isValueValid && !disabled, ref: ref, value: value })));
|
|
121
|
+
return ((0, jsx_runtime_1.jsx)(WithSshValidation_1.WithSshValidation, { layoutType: layoutType, sshValidation: sshValidation, children: (0, jsx_runtime_1.jsx)(MobileFieldAi_1.MobileFieldAi, Object.assign({}, props, { onChange: handleChange, onFileUpload: onFileUpload }, (0, uikit_product_mobile_fields_1.getAdaptiveFieldProps)(props), { onSubmit: handleSubmit, submitEnabled: isValueValid && !disabled, ref: ref, value: value })) }));
|
|
135
122
|
}
|
|
136
|
-
return ((0, jsx_runtime_1.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
123
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.wrapper, className), onDragOver: handleDragOver, onDragLeave: handleDragLeave, children: (0, jsx_runtime_1.jsxs)(WithSshValidation_1.WithSshValidation, { layoutType: layoutType, sshValidation: sshValidation, children: [(0, jsx_runtime_1.jsx)(ChatStatusAnnouncement_1.ChatStatusAnnouncement, { className: styles_module_scss_1.default.chatStatus, layoutType: layoutType, icon: (0, jsx_runtime_1.jsx)(uikit_product_icons_1.PasswordLockSVG, { size: 16, color: figma_tokens_1.themeVars.sys.neutral.textSupport }), content: [
|
|
124
|
+
{ content: t('SshField.chatStatusAnnouncement.content.option1') },
|
|
125
|
+
{ content: t('SshField.chatStatusAnnouncement.content.option2'), shouldFocusOnHover: true },
|
|
126
|
+
{ content: t('SshField.chatStatusAnnouncement.content.option3') },
|
|
127
|
+
], actionLabel: t('SshField.chatStatusAnnouncement.cancel'), onActionClick: onCancel }), isDragOver ? ((0, jsx_runtime_1.jsx)(drop_zone_1.DropZone, { description: (0, jsx_runtime_1.jsx)(DropZoneContent_1.DropZoneContent, {}), className: styles_module_scss_1.default.dropZone, mode: 'single', onFilesUpload: (files) => onFileUpload(files[0]) })) : ((0, jsx_runtime_1.jsx)(uikit_product_mobile_fields_1.AdaptiveFieldTextArea, Object.assign({}, props, { ref: ref, value: value, onChange: handleChange, size: 'm', disabled: isLoading, minRows: 2, maxRows: 4, placeholder: t('SshField.placeholder'), className: isValueHidden ? styles_module_scss_1.default.secured : undefined, onKeyDown: handleKeyDown, validationState: isSshValid ? validationState : 'error', hint: props.hint, footer: (0, jsx_runtime_1.jsx)(TextAreaActionsFooter_1.TextAreaActionsFooter, { left: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, { size: 'xs', icon: isValueHidden ? (0, jsx_runtime_1.jsx)(uikit_product_icons_1.EyeSVG, {}) : (0, jsx_runtime_1.jsx)(uikit_product_icons_1.EyeClosedSVG, {}), onClick: () => setIsValueHidden(prev => !prev), disabled: isLoading }), right: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { tip: t('SshField.attachFileTooltip'), hoverDelayOpen: 600, triggerClassName: styles_module_scss_1.default.uploadTooltip, open: isTouchDevice ? false : undefined, children: (0, jsx_runtime_1.jsx)(drop_zone_1.FileUpload, { mode: 'single', onFilesUpload: (files) => onFileUpload(files[0]), children: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, { disabled: isLoading, size: isTouchDevice ? 's' : 'xs', icon: (0, jsx_runtime_1.jsx)(uikit_product_icons_1.AttachmentSVG, {}) }) }) }), (0, jsx_runtime_1.jsx)(FieldSubmitButton_1.FieldSubmitButton, { disabled: isLoading, showTooltip: !isTouchDevice, className: isTouchDevice ? styles_module_scss_1.default.mobileSubmitButton : undefined, active: isValueValid && !disabled, handleClick: handleSubmit, size: isTouchDevice ? 's' : 'xs' })] }) }) })))] }) }));
|
|
141
128
|
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { FieldTextAreaProps } from '@cloud-ru/uikit-product-mobile-fields';
|
|
2
2
|
export declare const MobileFieldAi: import("react").ForwardRefExoticComponent<Omit<FieldTextAreaProps, "label" | "size" | "placeholder" | "spellCheck" | "labelTooltip" | "required" | "footer"> & {
|
|
3
|
+
layoutType: import("@cloud-ru/uikit-product-utils/.").LayoutType;
|
|
4
|
+
} & {
|
|
3
5
|
onSubmit(): void;
|
|
4
6
|
submitEnabled: boolean;
|
|
7
|
+
onFileUpload(file: File): void;
|
|
5
8
|
} & import("react").RefAttributes<HTMLTextAreaElement>>;
|
|
@@ -29,7 +29,7 @@ const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
|
29
29
|
const MIN_ROWS = 1;
|
|
30
30
|
const MAX_ROWS = 6;
|
|
31
31
|
exports.MobileFieldAi = (0, react_1.forwardRef)((_a, ref) => {
|
|
32
|
-
var { onSubmit, value, submitEnabled } = _a, props = __rest(_a, ["onSubmit", "value", "submitEnabled"]);
|
|
32
|
+
var { onSubmit, value, submitEnabled, onFileUpload } = _a, props = __rest(_a, ["onSubmit", "value", "submitEnabled", "onFileUpload"]);
|
|
33
33
|
const { t } = (0, uikit_product_locale_1.useLocale)('Claudia');
|
|
34
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.mobileInputWrapper, style: { '--max-rows': MAX_ROWS, '--min-rows': MIN_ROWS }, "data-size": 'm', children: [(0, jsx_runtime_1.jsx)(scroll_1.Scroll, { className: styles_module_scss_1.default.scrollContainer, size: 's', barHideStrategy: 'never', children: (0, jsx_runtime_1.jsx)(TextArea_1.TextArea, Object.assign({}, props, { className: styles_module_scss_1.default.textarea, ref: ref, value: value, minRows: MIN_ROWS, placeholder: t('SshField.placeholder'), spellCheck: true })) }), (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.mobileSubmitButtonWrapper, children: [(0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { disableSpanWrapper: true, tip: t('SshField.attachFileTooltip'), hoverDelayOpen: 600, triggerClassName: styles_module_scss_1.default.uploadTooltip, children: (0, jsx_runtime_1.jsx)(drop_zone_1.FileUpload, { mode: 'multiple', onFilesUpload: () =>
|
|
34
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.mobileInputWrapper, style: { '--max-rows': MAX_ROWS, '--min-rows': MIN_ROWS }, "data-size": 'm', children: [(0, jsx_runtime_1.jsx)(scroll_1.Scroll, { className: styles_module_scss_1.default.scrollContainer, size: 's', barHideStrategy: 'never', children: (0, jsx_runtime_1.jsx)(TextArea_1.TextArea, Object.assign({}, props, { className: styles_module_scss_1.default.textarea, ref: ref, value: value, minRows: MIN_ROWS, placeholder: t('SshField.placeholder'), spellCheck: true })) }), (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.mobileSubmitButtonWrapper, children: [(0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { disableSpanWrapper: true, tip: t('SshField.attachFileTooltip'), hoverDelayOpen: 600, triggerClassName: styles_module_scss_1.default.uploadTooltip, children: (0, jsx_runtime_1.jsx)(drop_zone_1.FileUpload, { mode: 'multiple', onFilesUpload: (files) => onFileUpload(files[0]), children: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, { size: 's', icon: (0, jsx_runtime_1.jsx)(uikit_product_icons_1.AttachmentSVG, {}) }) }) }), (0, jsx_runtime_1.jsx)(FieldSubmitButton_1.FieldSubmitButton, { showTooltip: false, className: styles_module_scss_1.default.mobileSubmitButton, fullWidth: true, active: submitEnabled, handleClick: onSubmit, size: 's' })] })] }));
|
|
35
35
|
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CheckItem = CheckItem;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const uikit_product_icons_1 = require("@cloud-ru/uikit-product-icons");
|
|
9
|
+
const typography_1 = require("@snack-uikit/typography");
|
|
10
|
+
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
11
|
+
function CheckItem({ label, layoutType }) {
|
|
12
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.checkItem, "data-layout-type": layoutType, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.iconWrapper, children: (0, jsx_runtime_1.jsx)(uikit_product_icons_1.CrossFilledSVG, { size: 16, className: styles_module_scss_1.default.icon }) }), (0, jsx_runtime_1.jsx)(typography_1.Typography.SansBodyM, { "data-layout-type": layoutType, className: styles_module_scss_1.default.label, children: label })] }));
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CheckItem';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./CheckItem"), exports);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.checkItem{
|
|
2
|
+
display:flex;
|
|
3
|
+
flex-direction:row;
|
|
4
|
+
gap:var(--dimension-050m, 4px);
|
|
5
|
+
}
|
|
6
|
+
.checkItem[data-layout-type=mobile], .checkItem[data-layout-type=tablet]{
|
|
7
|
+
gap:var(--dimension-1m, 8px);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.iconWrapper{
|
|
11
|
+
width:var(--dimension-2m, 16px);
|
|
12
|
+
height:var(--dimension-2m, 16px);
|
|
13
|
+
transform:translateY(var(--dimension-025m, 2px));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.icon{
|
|
17
|
+
color:var(--sys-red-accent-default, #cb3f3e);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.label{
|
|
21
|
+
color:var(--sys-red-decor-default, #fdd6cd);
|
|
22
|
+
}
|
|
23
|
+
.label[data-layout-type=mobile], .label[data-layout-type=tablet]{
|
|
24
|
+
color:var(--sys-red-text-main, #7a2d2d);
|
|
25
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
|
|
2
|
+
import { ValidationState } from '../../types';
|
|
3
|
+
export type WithPasswordTooltipProps = WithLayoutType<{
|
|
4
|
+
sshValidation: ValidationState | null;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function SshValidation({ sshValidation, layoutType }: WithPasswordTooltipProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SshValidation = SshValidation;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const uikit_product_locale_1 = require("@cloud-ru/uikit-product-locale");
|
|
9
|
+
const CheckItem_1 = require("../CheckItem");
|
|
10
|
+
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
11
|
+
function SshValidation({ sshValidation, layoutType }) {
|
|
12
|
+
const { t } = (0, uikit_product_locale_1.useLocale)('Claudia');
|
|
13
|
+
if (!sshValidation)
|
|
14
|
+
return null;
|
|
15
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.validationItemsContainer, "data-layout-type": layoutType, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.validationList, children: [sshValidation.fileSize && (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { label: t('SshField.errors.fileSize'), layoutType: layoutType }), sshValidation.fileType && (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { label: t('SshField.errors.fileType'), layoutType: layoutType }), sshValidation.binaryData && (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { label: t('SshField.errors.binaryData'), layoutType: layoutType }), sshValidation.emptyFile && (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { label: t('SshField.errors.emptyFile'), layoutType: layoutType }), sshValidation.invalidSSHKey && ((0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { label: t('SshField.errors.invalidSSHKey'), layoutType: layoutType })), sshValidation.readError && (0, jsx_runtime_1.jsx)(CheckItem_1.CheckItem, { label: t('SshField.errors.readError'), layoutType: layoutType })] }) }));
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SshValidation';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./SshValidation"), exports);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.tooltipText{
|
|
2
|
+
display:flex;
|
|
3
|
+
flex-direction:column;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.validationList{
|
|
7
|
+
display:flex;
|
|
8
|
+
flex-direction:column;
|
|
9
|
+
gap:var(--dimension-1m, 8px);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.validationItemsContainer{
|
|
13
|
+
font-family:var(--sans-body-m-font-family, SB Sans Interface);
|
|
14
|
+
font-weight:var(--sans-body-m-font-weight, Regular);
|
|
15
|
+
line-height:var(--sans-body-m-line-height, 20px);
|
|
16
|
+
font-size:var(--sans-body-m-font-size, 14px);
|
|
17
|
+
letter-spacing:var(--sans-body-m-letter-spacing, 0.1px);
|
|
18
|
+
paragraph-spacing:var(--sans-body-m-paragraph-spacing, 7.7px);
|
|
19
|
+
color:var(--sys-neutral-text-main, #41424e);
|
|
20
|
+
display:flex;
|
|
21
|
+
flex-direction:column;
|
|
22
|
+
gap:var(--dimension-1m, 8px);
|
|
23
|
+
}
|
|
24
|
+
.validationItemsContainer[data-layout-type=mobile], .validationItemsContainer[data-layout-type=tablet]{
|
|
25
|
+
gap:var(--dimension-1m, 8px);
|
|
26
|
+
font-family:var(--sans-body-s-font-family, SB Sans Interface);
|
|
27
|
+
font-weight:var(--sans-body-s-font-weight, Regular);
|
|
28
|
+
line-height:var(--sans-body-s-line-height, 16px);
|
|
29
|
+
font-size:var(--sans-body-s-font-size, 12px);
|
|
30
|
+
letter-spacing:var(--sans-body-s-letter-spacing, 0.1px);
|
|
31
|
+
paragraph-spacing:var(--sans-body-s-paragraph-spacing, 6.6px);
|
|
32
|
+
}
|
|
33
|
+
.validationItemsContainer[data-layout-type=mobile] .validationList, .validationItemsContainer[data-layout-type=tablet] .validationList{
|
|
34
|
+
gap:var(--dimension-025m, 2px);
|
|
35
|
+
}
|
package/dist/cjs/components/SshField/helperComponents/WithSshValidation/WithSshValidation.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
|
|
3
|
+
import { ValidationState } from '../../types';
|
|
4
|
+
export type WithSshValidationProps = WithLayoutType<{
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
sshValidation: ValidationState | null;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function WithSshValidation({ sshValidation, layoutType, children }: WithSshValidationProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WithSshValidation = WithSshValidation;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const uikit_product_mobile_tooltip_1 = require("@cloud-ru/uikit-product-mobile-tooltip");
|
|
10
|
+
const isTouchDevice_1 = require("../../utils/isTouchDevice");
|
|
11
|
+
const SshValidation_1 = require("../SshValidation");
|
|
12
|
+
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
13
|
+
function WithSshValidation({ sshValidation, layoutType, children }) {
|
|
14
|
+
const isSshValidationError = (0, react_1.useMemo)(() => (sshValidation ? Object.values(sshValidation).some(item => item) : false), [sshValidation]);
|
|
15
|
+
if ((0, isTouchDevice_1.isTouchDevice)(layoutType)) {
|
|
16
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.validationContainer, children: [(0, jsx_runtime_1.jsx)(SshValidation_1.SshValidation, { sshValidation: sshValidation, layoutType: layoutType }), children] }));
|
|
17
|
+
}
|
|
18
|
+
return ((0, jsx_runtime_1.jsx)(uikit_product_mobile_tooltip_1.AdaptiveTooltip, { placement: 'left-end', layoutType: layoutType, tip: (0, jsx_runtime_1.jsx)(SshValidation_1.SshValidation, { sshValidation: sshValidation, layoutType: layoutType }), open: isSshValidationError, offset: 8, children: children }));
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './WithSshValidation';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./WithSshValidation"), exports);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type FileError = {
|
|
2
|
+
fileType: boolean;
|
|
3
|
+
fileSize: boolean;
|
|
4
|
+
readError: boolean;
|
|
5
|
+
};
|
|
6
|
+
type FileContentError = {
|
|
7
|
+
binaryData: boolean;
|
|
8
|
+
emptyFile: boolean;
|
|
9
|
+
invalidSSHKey: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type ValidationState = Partial<FileError & FileContentError>;
|
|
12
|
+
export {};
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.readFileContent = void 0;
|
|
4
|
-
const readFileContent = (file) => new Promise(
|
|
4
|
+
const readFileContent = (file) => new Promise(resolve => {
|
|
5
5
|
const reader = new FileReader();
|
|
6
6
|
reader.onload = (e) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
resolve(e.target.result);
|
|
7
|
+
if (e.target && typeof e.target.result === 'string') {
|
|
8
|
+
resolve({ error: false, fileContent: e.target.result });
|
|
10
9
|
}
|
|
11
10
|
else {
|
|
12
|
-
|
|
11
|
+
resolve({ error: true });
|
|
13
12
|
}
|
|
14
13
|
};
|
|
15
|
-
reader.onerror = () => {
|
|
16
|
-
|
|
17
|
-
};
|
|
18
|
-
reader.onabort = () => {
|
|
19
|
-
reject(new Error('READ_ERROR: Чтение файла было прервано'));
|
|
20
|
-
};
|
|
14
|
+
reader.onerror = () => resolve({ error: true });
|
|
15
|
+
reader.onabort = () => resolve({ error: true });
|
|
21
16
|
// Читаем как текст
|
|
22
17
|
reader.readAsText(file);
|
|
23
18
|
});
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export declare const validateFileErrors: (file: File) => {
|
|
2
|
+
fileSize: boolean;
|
|
3
|
+
fileType: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare const validateSshKeyErrors: (value: string) => {
|
|
6
|
+
binaryChars: boolean;
|
|
7
|
+
emptyFile: boolean;
|
|
8
|
+
invalidSSHKey: boolean;
|
|
9
|
+
};
|
|
@@ -1,66 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const validateSSHKeyContent = (content) => {
|
|
5
|
-
const trimmedContent = content.trim();
|
|
6
|
-
// Проверка на пустой файл
|
|
7
|
-
if (trimmedContent.length === 0) {
|
|
8
|
-
throw new Error('INVALID_SSH_KEY: Файл пустой');
|
|
9
|
-
}
|
|
10
|
-
// Проверка на бинарный файл (простейшая проверка)
|
|
11
|
-
const binaryChars = content
|
|
12
|
-
.split('')
|
|
13
|
-
.filter(char => char.charCodeAt(0) < 32 && char !== '\n' && char !== '\r' && char !== '\t').length;
|
|
14
|
-
if (binaryChars > content.length * 0.1) {
|
|
15
|
-
// Если больше 10% бинарных символов
|
|
16
|
-
throw new Error('INVALID_SSH_KEY: Файл содержит бинарные данные, а не текстовый SSH ключ');
|
|
17
|
-
}
|
|
18
|
-
// Базовая проверка на формат SSH ключа
|
|
19
|
-
const sshKeyPatterns = [
|
|
20
|
-
/^-----BEGIN (?:RSA|DSA|EC|OPENSSH) PRIVATE KEY-----/,
|
|
21
|
-
/^ssh-(rsa|dsa|ecdsa|ed25519)/,
|
|
22
|
-
/^ecdsa-sha2-nistp/,
|
|
23
|
-
/^-----BEGIN.*PRIVATE KEY-----/,
|
|
24
|
-
/^-----BEGIN.*CERTIFICATE-----/,
|
|
25
|
-
];
|
|
26
|
-
const isValidSSHKey = sshKeyPatterns.some(pattern => pattern.test(trimmedContent));
|
|
27
|
-
if (!isValidSSHKey) {
|
|
28
|
-
throw new Error('INVALID_SSH_KEY: Файл не содержит валидный SSH ключ. Поддерживаются: RSA, DSA, ECDSA, Ed25519 ключи и сертификаты');
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
exports.validateSSHKeyContent = validateSSHKeyContent;
|
|
32
|
-
const DEFAULT_ALLOWED_MIME_TYPES = ['text/plain'];
|
|
3
|
+
exports.validateSshKeyErrors = exports.validateFileErrors = void 0;
|
|
33
4
|
const MAX_FILE_SIZE = 10 * 1024; // 10KB по умолчанию
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
5
|
+
const SSH_KEY_BEGIN_PATTERN = [
|
|
6
|
+
/^-----BEGIN (?:RSA|DSA|EC|OPENSSH) PRIVATE KEY-----/,
|
|
7
|
+
/^-----BEGIN.*PRIVATE KEY-----/,
|
|
8
|
+
/^-----BEGIN.*CERTIFICATE-----/,
|
|
9
|
+
];
|
|
10
|
+
const SSH_KEY_END_PATTERN = [
|
|
11
|
+
/-----END (?:RSA|DSA|EC|OPENSSH) PRIVATE KEY-----/,
|
|
12
|
+
/-----END.*PRIVATE KEY-----/,
|
|
13
|
+
/-----END.*CERTIFICATE-----/,
|
|
14
|
+
];
|
|
15
|
+
const checkIsFileSizeError = (file) => file.size > MAX_FILE_SIZE;
|
|
16
|
+
function getFileExtension(filename) {
|
|
17
|
+
return filename.includes('.') ? filename.split('.').pop() : '';
|
|
18
|
+
}
|
|
19
|
+
const checkIsFileTypeError = (file) => Boolean(getFileExtension(file.name));
|
|
20
|
+
const validateFileErrors = (file) => ({
|
|
21
|
+
fileSize: checkIsFileSizeError(file),
|
|
22
|
+
fileType: checkIsFileTypeError(file),
|
|
23
|
+
});
|
|
24
|
+
exports.validateFileErrors = validateFileErrors;
|
|
25
|
+
const checkIsEmptyContent = (value) => !value.trim().length;
|
|
26
|
+
const checkIsBinaryCharsInContent = (value) => {
|
|
27
|
+
const binaryChars = value
|
|
28
|
+
.split('')
|
|
29
|
+
.filter(char => char.charCodeAt(0) < 32 && !['\n', '\r', '\t'].includes(char)).length;
|
|
30
|
+
return binaryChars > value.length * 0.1;
|
|
41
31
|
};
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
32
|
+
const removePEMBoundaries = (sshFileContent) => {
|
|
33
|
+
let replacedContent = sshFileContent.trim().replaceAll('\n', '');
|
|
34
|
+
let isBeginPartExist = false;
|
|
35
|
+
let isEndPartExist = false;
|
|
36
|
+
SSH_KEY_BEGIN_PATTERN.forEach(pattern => {
|
|
37
|
+
if (pattern.test(replacedContent)) {
|
|
38
|
+
isBeginPartExist = true;
|
|
39
|
+
replacedContent = replacedContent.replace(new RegExp(pattern, 'g'), '');
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
SSH_KEY_END_PATTERN.forEach(pattern => {
|
|
43
|
+
if (pattern.test(replacedContent)) {
|
|
44
|
+
isEndPartExist = true;
|
|
45
|
+
replacedContent = replacedContent.replace(new RegExp(pattern, 'g'), '');
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return { isError: !(isBeginPartExist && isEndPartExist), content: replacedContent };
|
|
53
49
|
};
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
`максимальный: ${MAX_FILE_SIZE / 1024}KB`);
|
|
59
|
-
}
|
|
60
|
-
// Минимальный размер для SSH ключа (примерно 100 байт)
|
|
61
|
-
const minFileSize = 100;
|
|
62
|
-
if (file.size < minFileSize) {
|
|
63
|
-
throw new Error(`INVALID_FILE_TYPE: Файл слишком маленький для SSH ключа. Минимальный размер: ${minFileSize} байт`);
|
|
50
|
+
const checkIsSshKeyError = (value) => {
|
|
51
|
+
const { isError, content: base64content } = removePEMBoundaries(value);
|
|
52
|
+
if (isError) {
|
|
53
|
+
return true;
|
|
64
54
|
}
|
|
55
|
+
const base64Regexp = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[+/=])[A-Za-z0-9+/=\r\n]+$/;
|
|
56
|
+
return !base64Regexp.test(base64content);
|
|
65
57
|
};
|
|
66
|
-
|
|
58
|
+
const validateSshKeyErrors = (value) => ({
|
|
59
|
+
binaryChars: checkIsBinaryCharsInContent(value),
|
|
60
|
+
emptyFile: checkIsEmptyContent(value),
|
|
61
|
+
invalidSSHKey: checkIsSshKeyError(value),
|
|
62
|
+
});
|
|
63
|
+
exports.validateSshKeyErrors = validateSshKeyErrors;
|