@cloud-ru/uikit-product-claudia 1.11.1 → 1.12.1

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cjs/components/SshField/SshField.d.ts +1 -1
  3. package/dist/cjs/components/SshField/SshField.js +33 -46
  4. package/dist/cjs/components/SshField/components/MobileFieldAi/MobileFieldAi.d.ts +3 -0
  5. package/dist/cjs/components/SshField/components/MobileFieldAi/MobileFieldAi.js +2 -2
  6. package/dist/cjs/components/SshField/helperComponents/CheckItem/CheckItem.d.ts +6 -0
  7. package/dist/cjs/components/SshField/helperComponents/CheckItem/CheckItem.js +13 -0
  8. package/dist/cjs/components/SshField/helperComponents/CheckItem/index.d.ts +1 -0
  9. package/dist/cjs/components/SshField/helperComponents/CheckItem/index.js +17 -0
  10. package/dist/cjs/components/SshField/helperComponents/CheckItem/styles.module.css +25 -0
  11. package/dist/cjs/components/SshField/helperComponents/DropZoneContent/styles.module.css +1 -1
  12. package/dist/cjs/components/SshField/helperComponents/SshValidation/SshValidation.d.ts +6 -0
  13. package/dist/cjs/components/SshField/helperComponents/SshValidation/SshValidation.js +16 -0
  14. package/dist/cjs/components/SshField/helperComponents/SshValidation/index.d.ts +1 -0
  15. package/dist/cjs/components/SshField/helperComponents/SshValidation/index.js +17 -0
  16. package/dist/cjs/components/SshField/helperComponents/SshValidation/styles.module.css +35 -0
  17. package/dist/cjs/components/SshField/helperComponents/WithSshValidation/WithSshValidation.d.ts +8 -0
  18. package/dist/cjs/components/SshField/helperComponents/WithSshValidation/WithSshValidation.js +19 -0
  19. package/dist/cjs/components/SshField/helperComponents/WithSshValidation/index.d.ts +1 -0
  20. package/dist/cjs/components/SshField/helperComponents/WithSshValidation/index.js +17 -0
  21. package/dist/cjs/components/SshField/helperComponents/WithSshValidation/styles.module.css +5 -0
  22. package/dist/cjs/components/SshField/types.d.ts +12 -0
  23. package/dist/cjs/components/SshField/types.js +2 -0
  24. package/dist/cjs/components/SshField/utils/readFileContent.d.ts +6 -1
  25. package/dist/cjs/components/SshField/utils/readFileContent.js +6 -11
  26. package/dist/cjs/components/SshField/utils/validateSSHKey.d.ts +9 -3
  27. package/dist/cjs/components/SshField/utils/validateSSHKey.js +56 -59
  28. package/dist/esm/components/SshField/SshField.d.ts +1 -1
  29. package/dist/esm/components/SshField/SshField.js +34 -47
  30. package/dist/esm/components/SshField/components/MobileFieldAi/MobileFieldAi.d.ts +3 -0
  31. package/dist/esm/components/SshField/components/MobileFieldAi/MobileFieldAi.js +2 -2
  32. package/dist/esm/components/SshField/helperComponents/CheckItem/CheckItem.d.ts +6 -0
  33. package/dist/esm/components/SshField/helperComponents/CheckItem/CheckItem.js +7 -0
  34. package/dist/esm/components/SshField/helperComponents/CheckItem/index.d.ts +1 -0
  35. package/dist/esm/components/SshField/helperComponents/CheckItem/index.js +1 -0
  36. package/dist/esm/components/SshField/helperComponents/CheckItem/styles.module.css +25 -0
  37. package/dist/esm/components/SshField/helperComponents/DropZoneContent/styles.module.css +1 -1
  38. package/dist/esm/components/SshField/helperComponents/SshValidation/SshValidation.d.ts +6 -0
  39. package/dist/esm/components/SshField/helperComponents/SshValidation/SshValidation.js +10 -0
  40. package/dist/esm/components/SshField/helperComponents/SshValidation/index.d.ts +1 -0
  41. package/dist/esm/components/SshField/helperComponents/SshValidation/index.js +1 -0
  42. package/dist/esm/components/SshField/helperComponents/SshValidation/styles.module.css +35 -0
  43. package/dist/esm/components/SshField/helperComponents/WithSshValidation/WithSshValidation.d.ts +8 -0
  44. package/dist/esm/components/SshField/helperComponents/WithSshValidation/WithSshValidation.js +13 -0
  45. package/dist/esm/components/SshField/helperComponents/WithSshValidation/index.d.ts +1 -0
  46. package/dist/esm/components/SshField/helperComponents/WithSshValidation/index.js +1 -0
  47. package/dist/esm/components/SshField/helperComponents/WithSshValidation/styles.module.css +5 -0
  48. package/dist/esm/components/SshField/types.d.ts +12 -0
  49. package/dist/esm/components/SshField/types.js +1 -0
  50. package/dist/esm/components/SshField/utils/readFileContent.d.ts +6 -1
  51. package/dist/esm/components/SshField/utils/readFileContent.js +6 -11
  52. package/dist/esm/components/SshField/utils/validateSSHKey.d.ts +9 -3
  53. package/dist/esm/components/SshField/utils/validateSSHKey.js +53 -55
  54. package/package.json +7 -6
  55. package/src/components/SshField/SshField.tsx +120 -121
  56. package/src/components/SshField/components/MobileFieldAi/MobileFieldAi.tsx +6 -5
  57. package/src/components/SshField/helperComponents/CheckItem/CheckItem.tsx +23 -0
  58. package/src/components/SshField/helperComponents/CheckItem/index.ts +1 -0
  59. package/src/components/SshField/helperComponents/CheckItem/styles.module.scss +31 -0
  60. package/src/components/SshField/helperComponents/DropZoneContent/styles.module.scss +1 -1
  61. package/src/components/SshField/helperComponents/SshValidation/SshValidation.tsx +36 -0
  62. package/src/components/SshField/helperComponents/SshValidation/index.ts +1 -0
  63. package/src/components/SshField/helperComponents/SshValidation/styles.module.scss +31 -0
  64. package/src/components/SshField/helperComponents/WithSshValidation/WithSshValidation.tsx +43 -0
  65. package/src/components/SshField/helperComponents/WithSshValidation/index.ts +1 -0
  66. package/src/components/SshField/helperComponents/WithSshValidation/styles.module.scss +7 -0
  67. package/src/components/SshField/types.ts +13 -0
  68. package/src/components/SshField/utils/readFileContent.ts +12 -11
  69. package/src/components/SshField/utils/validateSSHKey.ts +60 -68
  70. package/dist/cjs/components/SshField/utils/handleFileError.d.ts +0 -2
  71. package/dist/cjs/components/SshField/utils/handleFileError.js +0 -32
  72. package/dist/esm/components/SshField/utils/handleFileError.d.ts +0 -2
  73. package/dist/esm/components/SshField/utils/handleFileError.js +0 -28
  74. package/src/components/SshField/utils/handleFileError.ts +0 -41
package/CHANGELOG.md CHANGED
@@ -3,6 +3,30 @@
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.1 (2025-12-17)
7
+
8
+ ### Only dependencies have been changed
9
+ * [@cloud-ru/uikit-product-icons@16.1.1](https://gitverse.ru/cloud-ru-tech/uikit-product/-/blob/master/packages/icons/CHANGELOG.md)
10
+ * [@cloud-ru/uikit-product-mobile-dropdown@0.9.31](https://gitverse.ru/cloud-ru-tech/uikit-product/-/blob/master/packages/mobile-dropdown/CHANGELOG.md)
11
+ * [@cloud-ru/uikit-product-mobile-fields@0.12.3](https://gitverse.ru/cloud-ru-tech/uikit-product/-/blob/master/packages/mobile-fields/CHANGELOG.md)
12
+ * [@cloud-ru/uikit-product-mobile-tooltip@0.5.4](https://gitverse.ru/cloud-ru-tech/uikit-product/-/blob/master/packages/mobile-tooltip/CHANGELOG.md)
13
+ * [@cloud-ru/uikit-product-utils@8.1.0](https://gitverse.ru/cloud-ru-tech/uikit-product/-/blob/master/packages/utils/CHANGELOG.md)
14
+
15
+
16
+
17
+
18
+
19
+ # 1.12.0 (2025-12-17)
20
+
21
+
22
+ ### Features
23
+
24
+ * **AINFR-4587:** add new ssh validation rules ([cf6ad31](https://gitverse.ru/cloud-ru-tech/uikit-product/commits/cf6ad31b9549c73de2346a63fcd489fa2f39068f))
25
+
26
+
27
+
28
+
29
+
6
30
  ## 1.11.1 (2025-12-12)
7
31
 
8
32
  ### Only dependencies have been changed
@@ -12,5 +12,5 @@ export declare const SshField: import("react").ForwardRefExoticComponent<Omit<Fi
12
12
  /** Колбек отмены действия */
13
13
  onCancel(): void;
14
14
  } & {
15
- layoutType: import("@cloud-ru/uikit-product-utils/.").LayoutType;
15
+ layoutType: import("@cloud-ru/uikit-product-utils").LayoutType;
16
16
  } & import("react").RefAttributes<HTMLTextAreaElement>>;
@@ -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 [fileErrorType, setFileErrorType] = (0, react_1.useState)(null);
56
- const isValueValid = typeof value === 'string' && value.trim().length > 0;
57
- const showFileError = Boolean(fileErrorType);
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 (fileErrorType) {
81
- setFileErrorType(null);
59
+ if (sshValidation) {
60
+ setSshValidation(null);
82
61
  }
83
- if (onChange) {
84
- onChange(newValue);
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
- setFileErrorType(null);
116
- (0, validateSSHKey_1.validateFileType)(file);
117
- (0, validateSSHKey_1.validateFileSize)(file);
118
- const fileContent = yield (0, readFileContent_1.readFileContent)(file);
119
- (0, validateSSHKey_1.validateSSHKeyContent)(fileContent);
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
- catch (err) {
125
- const errorType = (0, handleFileError_1.getFileErrorType)(err);
126
- setFileErrorType(errorType);
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.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.wrapper, className), onDragOver: handleDragOver, onDragLeave: handleDragLeave, 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: [
137
- { content: t('SshField.chatStatusAnnouncement.content.option1') },
138
- { content: t('SshField.chatStatusAnnouncement.content.option2'), shouldFocusOnHover: true },
139
- { content: t('SshField.chatStatusAnnouncement.content.option3') },
140
- ], 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: showFileError ? 'error' : validationState, hint: showFileError && fileErrorType ? getErrorMessage(fileErrorType) : 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' })] }) }) })))] }));
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: () => { }, 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' })] })] }));
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,6 @@
1
+ import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
2
+ type CheckItemProps = WithLayoutType<{
3
+ label: string;
4
+ }>;
5
+ export declare function CheckItem({ label, layoutType }: CheckItemProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -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
+ }
@@ -3,7 +3,7 @@
3
3
  flex-direction:column;
4
4
  justify-content:center;
5
5
  align-items:center;
6
- gap:4px;
6
+ gap:var(--dimension-050m, 4px);
7
7
  }
8
8
 
9
9
  .defaultText{
@@ -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
+ }
@@ -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,5 @@
1
+ .validationContainer{
2
+ display:flex;
3
+ flex-direction:column;
4
+ gap:var(--dimension-1m, 8px);
5
+ }
@@ -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 {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1 +1,6 @@
1
- export declare const readFileContent: (file: File) => Promise<string>;
1
+ type ReadFileContentResult = {
2
+ error: boolean;
3
+ fileContent?: string;
4
+ };
5
+ export declare const readFileContent: (file: File) => Promise<ReadFileContentResult>;
6
+ 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((resolve, reject) => {
4
+ const readFileContent = (file) => new Promise(resolve => {
5
5
  const reader = new FileReader();
6
6
  reader.onload = (e) => {
7
- var _a;
8
- if (((_a = e.target) === null || _a === void 0 ? void 0 : _a.result) && typeof e.target.result === 'string') {
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
- reject(new Error('READ_ERROR: Не удалось прочитать содержимое файла'));
11
+ resolve({ error: true });
13
12
  }
14
13
  };
15
- reader.onerror = () => {
16
- reject(new Error('READ_ERROR: Не удалось прочитать файл'));
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 validateSSHKeyContent: (content: string) => void;
2
- export declare const validateFileType: (file: File) => void;
3
- export declare const validateFileSize: (file: File) => void;
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.validateFileSize = exports.validateFileType = exports.validateSSHKeyContent = void 0;
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 getFileExtension = (filename) => {
35
- const lastDotIndex = filename.lastIndexOf('.');
36
- // Если точка не найдена или это первая точка в имени файла (скрытые файлы в Unix)
37
- if (lastDotIndex <= 0) {
38
- return '';
39
- }
40
- return filename.toLowerCase().slice(lastDotIndex);
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 getFileMimeType = (file) => file.type;
43
- const validateFileType = (file) => {
44
- const fileExtension = getFileExtension(file.name);
45
- const mimeType = getFileMimeType(file);
46
- // Проверка по MIME type (более надежный способ)
47
- const isValidMimeType = DEFAULT_ALLOWED_MIME_TYPES.some(allowedMime => mimeType.includes(allowedMime) || allowedMime.includes(mimeType));
48
- // Файл должен пройти хотя бы одну проверку
49
- if (!isValidMimeType) {
50
- throw new Error(`INVALID_MIME_TYPE: Неподходящий тип файла. MIME: ${mimeType}, расширение: ${fileExtension || 'нет'}. ` +
51
- `Разрешенные MIME types: ${DEFAULT_ALLOWED_MIME_TYPES.join(', ')}, `);
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
- exports.validateFileType = validateFileType;
55
- const validateFileSize = (file) => {
56
- if (file.size > MAX_FILE_SIZE) {
57
- throw new Error(`FILE_TOO_LARGE: Файл слишком большой. Размер: ${(file.size / 1024).toFixed(2)}KB, ` +
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
- exports.validateFileSize = validateFileSize;
58
+ const validateSshKeyErrors = (value) => ({
59
+ binaryChars: checkIsBinaryCharsInContent(value),
60
+ emptyFile: checkIsEmptyContent(value),
61
+ invalidSSHKey: checkIsSshKeyError(value),
62
+ });
63
+ exports.validateSshKeyErrors = validateSshKeyErrors;
@@ -12,5 +12,5 @@ export declare const SshField: import("react").ForwardRefExoticComponent<Omit<Fi
12
12
  /** Колбек отмены действия */
13
13
  onCancel(): void;
14
14
  } & {
15
- layoutType: import("@cloud-ru/uikit-product-utils/.").LayoutType;
15
+ layoutType: import("@cloud-ru/uikit-product-utils").LayoutType;
16
16
  } & import("react").RefAttributes<HTMLTextAreaElement>>;