diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..a6f7ac4007 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ + + +**Checklist:** (if applicable) + +- [ ] Mention to the original issue +- [ ] Documentation +- [ ] Minium required manager version +- [ ] Specific setting for review (eg., KB link, endpoint or how to setup) +- [ ] Minimum requirements to check during review +- [ ] Test case(s) to demonstrate the difference of before/after diff --git a/CHANGELOG.md b/CHANGELOG.md index 823ac5228c..6f57795c15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,18 @@ # Changelog +## v23.09.1 (24/10/2023) +## What's Changed +* update: change label on terms of service dialog's close button by @rapsealk in https://github.com/lablup/backend.ai-webui/pull/1957 +* feat: Neo session launcher (Step 3&4) by @yomybaby in https://github.com/lablup/backend.ai-webui/pull/1896 +* fix: remove warnings/errors and unused logs in the browser console by @yomybaby in https://github.com/lablup/backend.ai-webui/pull/1961 +* style: widen control column to show rescan icon by @agatha197 in https://github.com/lablup/backend.ai-webui/pull/1963 +* feature: hide sftp resource group by @agatha197 in https://github.com/lablup/backend.ai-webui/pull/1966 +* feature: Enable create session with bootstrap script by @fregataa in https://github.com/lablup/backend.ai-webui/pull/1944 +* fix: remove `max_vfolder_count` from keypair resource policy by @agatha197 in https://github.com/lablup/backend.ai-webui/pull/1972 +* fix: empty filtered scaling groups generates error by @inureyes in https://github.com/lablup/backend.ai-webui/pull/1982 +* add: disable hostkey checking option for sftp session by @lizable in https://github.com/lablup/backend.ai-webui/pull/1984 + +**Full Changelog**: https://github.com/lablup/backend.ai-webui/compare/v23.09.0...v23.09.1 + ## v23.09.0 (27/09/2023) ## What's Changed diff --git a/react/data/schema.graphql b/react/data/schema.graphql index c27c672290..41eb9ed0d2 100644 --- a/react/data/schema.graphql +++ b/react/data/schema.graphql @@ -22,6 +22,7 @@ type Agent implements Item { hardware_metadata: JSONString auto_terminate_abusing_kernel: Boolean local_config: JSONString + container_count: Int mem_slots: Int cpu_slots: Float gpu_slots: Float @@ -176,6 +177,34 @@ type ComputeSessionList implements PaginatedList { total_count: Int! } +type ContainerRegistry implements Node { + id: ID! + hostname: String + config: ContainerRegistryConfig +} + +type ContainerRegistryConfig { + url: String! + type: String! + project: [String] + username: String + password: String + ssl_verify: Boolean +} + +type CreateContainerRegistry { + container_registry: ContainerRegistry +} + +input CreateContainerRegistryInput { + url: String! + type: String! + project: [String] + username: String + password: String + ssl_verify: Boolean +} + type CreateDomain { ok: Boolean msg: String @@ -207,6 +236,8 @@ input CreateKeyPairResourcePolicyInput { max_concurrent_sessions: Int! max_containers_per_session: Int! idle_timeout: BigInt! + max_vfolder_count: Int! + max_vfolder_size: BigInt! allowed_vfolder_hosts: JSONString } @@ -217,8 +248,7 @@ type CreateProjectResourcePolicy { } input CreateProjectResourcePolicyInput { - max_vfolder_count: BigInt! - max_quota_scope_size: BigInt! + max_quota_scope_size: BigInt } type CreateResourcePreset { @@ -264,8 +294,7 @@ type CreateUserResourcePolicy { } input CreateUserResourcePolicyInput { - max_vfolder_count: BigInt! - max_quota_scope_size: BigInt! + max_quota_scope_size: BigInt } scalar DateTime @@ -275,6 +304,10 @@ type DealiasImage { msg: String } +type DeleteContainerRegistry { + container_registry: ContainerRegistry +} + type DeleteDomain { ok: Boolean msg: String @@ -392,10 +425,11 @@ type Endpoint implements Item { cluster_size: Int open_to_public: Boolean created_at: DateTime! - terminated_at: DateTime + destroyed_at: DateTime routings: [Routing] retries: Int status: String + lifecycle_stage: String errors: [InferenceSessionError!]! } @@ -470,7 +504,7 @@ type Image { } type InferenceSessionError { - session_id: UUID! + session_id: UUID errors: [InferenceSessionErrorInfo!]! } @@ -516,10 +550,7 @@ type KeyPair implements Item { compute_sessions(status: String): [ComputeSession] concurrency_used: Int user_info: UserInfo - concurrency_limit: Int - @deprecated( - reason: "Moved to KeyPairResourcePolicy object as the max_concurrent_sessions field." - ) + concurrency_limit: Int @deprecated(reason: "Moved to KeyPairResourcePolicy object as the max_concurrent_sessions field.") } input KeyPairInput { @@ -544,6 +575,8 @@ type KeyPairResourcePolicy { max_concurrent_sessions: Int max_containers_per_session: Int idle_timeout: BigInt + max_vfolder_count: Int + max_vfolder_size: BigInt allowed_vfolder_hosts: JSONString } @@ -614,6 +647,19 @@ input ModifyAgentInput { scaling_group: String } +type ModifyContainerRegistry { + container_registry: ContainerRegistry +} + +input ModifyContainerRegistryInput { + url: String! + type: String! + project: [String] + username: String + password: String + ssl_verify: Boolean +} + type ModifyDomain { ok: Boolean msg: String @@ -694,6 +740,8 @@ input ModifyKeyPairResourcePolicyInput { max_concurrent_sessions: Int max_containers_per_session: Int idle_timeout: BigInt + max_vfolder_count: Int + max_vfolder_size: BigInt allowed_vfolder_hosts: JSONString } @@ -703,8 +751,7 @@ type ModifyProjectResourcePolicy { } input ModifyProjectResourcePolicyInput { - max_vfolder_count: BigInt! - max_quota_scope_size: BigInt! + max_quota_scope_size: BigInt } type ModifyResourcePreset { @@ -755,6 +802,7 @@ input ModifyUserInput { allowed_client_ip: [String] totp_activated: Boolean resource_policy: String + sudo_session_enabled: Boolean } type ModifyUserResourcePolicy { @@ -763,8 +811,7 @@ type ModifyUserResourcePolicy { } input ModifyUserResourcePolicyInput { - max_vfolder_count: BigInt! - max_quota_scope_size: BigInt! + max_quota_scope_size: BigInt } type Mutations { @@ -787,106 +834,43 @@ type Mutations { rescan_images(registry: String): RescanImages preload_image(references: [String]!, target_agents: [String]!): PreloadImage unload_image(references: [String]!, target_agents: [String]!): UnloadImage - modify_image( - architecture: String = "aarch64" - props: ModifyImageInput! - target: String! - ): ModifyImage - forget_image( - architecture: String = "aarch64" - reference: String! - ): ForgetImage - alias_image( - alias: String! - architecture: String = "aarch64" - target: String! - ): AliasImage + modify_image(architecture: String = "aarch64", props: ModifyImageInput!, target: String!): ModifyImage + forget_image(architecture: String = "aarch64", reference: String!): ForgetImage + alias_image(alias: String!, architecture: String = "aarch64", target: String!): AliasImage dealias_image(alias: String!): DealiasImage clear_images(registry: String): ClearImages - create_keypair_resource_policy( - name: String! - props: CreateKeyPairResourcePolicyInput! - ): CreateKeyPairResourcePolicy - modify_keypair_resource_policy( - name: String! - props: ModifyKeyPairResourcePolicyInput! - ): ModifyKeyPairResourcePolicy + create_keypair_resource_policy(name: String!, props: CreateKeyPairResourcePolicyInput!): CreateKeyPairResourcePolicy + modify_keypair_resource_policy(name: String!, props: ModifyKeyPairResourcePolicyInput!): ModifyKeyPairResourcePolicy delete_keypair_resource_policy(name: String!): DeleteKeyPairResourcePolicy - create_user_resource_policy( - name: String! - props: CreateUserResourcePolicyInput! - ): CreateUserResourcePolicy - modify_user_resource_policy( - name: String! - props: ModifyUserResourcePolicyInput! - ): ModifyUserResourcePolicy + create_user_resource_policy(name: String!, props: CreateUserResourcePolicyInput!): CreateUserResourcePolicy + modify_user_resource_policy(name: String!, props: ModifyUserResourcePolicyInput!): ModifyUserResourcePolicy delete_user_resource_policy(name: String!): DeleteUserResourcePolicy - create_project_resource_policy( - name: String! - props: CreateProjectResourcePolicyInput! - ): CreateProjectResourcePolicy - modify_project_resource_policy( - name: String! - props: ModifyProjectResourcePolicyInput! - ): ModifyProjectResourcePolicy + create_project_resource_policy(name: String!, props: CreateProjectResourcePolicyInput!): CreateProjectResourcePolicy + modify_project_resource_policy(name: String!, props: ModifyProjectResourcePolicyInput!): ModifyProjectResourcePolicy delete_project_resource_policy(name: String!): DeleteProjectResourcePolicy - create_resource_preset( - name: String! - props: CreateResourcePresetInput! - ): CreateResourcePreset - modify_resource_preset( - name: String! - props: ModifyResourcePresetInput! - ): ModifyResourcePreset + create_resource_preset(name: String!, props: CreateResourcePresetInput!): CreateResourcePreset + modify_resource_preset(name: String!, props: ModifyResourcePresetInput!): ModifyResourcePreset delete_resource_preset(name: String!): DeleteResourcePreset - create_scaling_group( - name: String! - props: CreateScalingGroupInput! - ): CreateScalingGroup - modify_scaling_group( - name: String! - props: ModifyScalingGroupInput! - ): ModifyScalingGroup + create_scaling_group(name: String!, props: CreateScalingGroupInput!): CreateScalingGroup + modify_scaling_group(name: String!, props: ModifyScalingGroupInput!): ModifyScalingGroup delete_scaling_group(name: String!): DeleteScalingGroup - associate_scaling_group_with_domain( - domain: String! - scaling_group: String! - ): AssociateScalingGroupWithDomain - associate_scaling_group_with_user_group( - scaling_group: String! - user_group: UUID! - ): AssociateScalingGroupWithUserGroup - associate_scaling_group_with_keypair( - access_key: String! - scaling_group: String! - ): AssociateScalingGroupWithKeyPair - disassociate_scaling_group_with_domain( - domain: String! - scaling_group: String! - ): DisassociateScalingGroupWithDomain - disassociate_scaling_group_with_user_group( - scaling_group: String! - user_group: UUID! - ): DisassociateScalingGroupWithUserGroup - disassociate_scaling_group_with_keypair( - access_key: String! - scaling_group: String! - ): DisassociateScalingGroupWithKeyPair - disassociate_all_scaling_groups_with_domain( - domain: String! - ): DisassociateAllScalingGroupsWithDomain - disassociate_all_scaling_groups_with_group( - user_group: UUID! - ): DisassociateAllScalingGroupsWithGroup - set_quota_scope( - props: QuotaScopeInput! - quota_scope_id: String! - storage_host_name: String! - ): SetQuotaScope - unset_quota_scope( - quota_scope_id: String! - storage_host_name: String! - ): UnsetQuotaScope + associate_scaling_group_with_domain(domain: String!, scaling_group: String!): AssociateScalingGroupWithDomain + associate_scaling_group_with_user_group(scaling_group: String!, user_group: UUID!): AssociateScalingGroupWithUserGroup + associate_scaling_group_with_keypair(access_key: String!, scaling_group: String!): AssociateScalingGroupWithKeyPair + disassociate_scaling_group_with_domain(domain: String!, scaling_group: String!): DisassociateScalingGroupWithDomain + disassociate_scaling_group_with_user_group(scaling_group: String!, user_group: UUID!): DisassociateScalingGroupWithUserGroup + disassociate_scaling_group_with_keypair(access_key: String!, scaling_group: String!): DisassociateScalingGroupWithKeyPair + disassociate_all_scaling_groups_with_domain(domain: String!): DisassociateAllScalingGroupsWithDomain + disassociate_all_scaling_groups_with_group(user_group: UUID!): DisassociateAllScalingGroupsWithGroup + set_quota_scope(props: QuotaScopeInput!, quota_scope_id: String!, storage_host_name: String!): SetQuotaScope + unset_quota_scope(quota_scope_id: String!, storage_host_name: String!): UnsetQuotaScope + create_container_registry(hostname: String!, props: CreateContainerRegistryInput!): CreateContainerRegistry + modify_container_registry(hostname: String!, props: ModifyContainerRegistryInput!): ModifyContainerRegistry + delete_container_registry(hostname: String!): DeleteContainerRegistry +} + +interface Node { + id: ID! } interface PaginatedList { @@ -908,8 +892,7 @@ type ProjectResourcePolicy { id: ID! name: String! created_at: DateTime! - max_vfolder_count: BigInt - max_vfolder_size: BigInt # aliased field + max_vfolder_size: BigInt @deprecated(reason: "Deprecated since 23.09.1") max_quota_scope_size: BigInt } @@ -933,25 +916,12 @@ input PurgeUserInput { } type Queries { + node(id: ID!): Node agent(agent_id: String!): Agent - agent_list( - limit: Int! - offset: Int! - filter: String - order: String - scaling_group: String - status: String - ): AgentList + agent_list(limit: Int!, offset: Int!, filter: String, order: String, scaling_group: String, status: String): AgentList agents(scaling_group: String, status: String): [Agent] agent_summary(agent_id: String!): AgentSummary - agent_summary_list( - limit: Int! - offset: Int! - filter: String - order: String - scaling_group: String - status: String - ): AgentSummaryList + agent_summary_list(limit: Int!, offset: Int!, filter: String, order: String, scaling_group: String, status: String): AgentSummaryList domain(name: String): Domain domains(is_active: Boolean): [Domain] group(id: UUID!, domain_name: String): Group @@ -961,33 +931,11 @@ type Queries { images(is_installed: Boolean, is_operation: Boolean): [Image] user(domain_name: String, email: String): User user_from_uuid(domain_name: String, user_id: ID): User - users( - domain_name: String - group_id: UUID - is_active: Boolean - status: String - ): [User] - user_list( - limit: Int! - offset: Int! - filter: String - order: String - domain_name: String - group_id: UUID - is_active: Boolean - status: String - ): UserList + users(domain_name: String, group_id: UUID, is_active: Boolean, status: String): [User] + user_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, group_id: UUID, is_active: Boolean, status: String): UserList keypair(domain_name: String, access_key: String): KeyPair keypairs(domain_name: String, email: String, is_active: Boolean): [KeyPair] - keypair_list( - limit: Int! - offset: Int! - filter: String - order: String - domain_name: String - email: String - is_active: Boolean - ): KeyPairList + keypair_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, email: String, is_active: Boolean): KeyPairList keypair_resource_policy(name: String): KeyPairResourcePolicy user_resource_policy(name: String): UserResourcePolicy project_resource_policy(name: String!): ProjectResourcePolicy @@ -999,129 +947,33 @@ type Queries { scaling_group(name: String): ScalingGroup scaling_groups(name: String, is_active: Boolean): [ScalingGroup] scaling_groups_for_domain(domain: String!, is_active: Boolean): [ScalingGroup] - scaling_groups_for_user_group( - user_group: String! - is_active: Boolean - ): [ScalingGroup] - scaling_groups_for_keypair( - access_key: String! - is_active: Boolean - ): [ScalingGroup] + scaling_groups_for_user_group(user_group: String!, is_active: Boolean): [ScalingGroup] + scaling_groups_for_keypair(access_key: String!, is_active: Boolean): [ScalingGroup] storage_volume(id: String): StorageVolume - storage_volume_list( - limit: Int! - offset: Int! - filter: String - order: String - ): StorageVolumeList - vfolder_list( - limit: Int! - offset: Int! - filter: String - order: String - domain_name: String - group_id: UUID - access_key: String - ): VirtualFolderList - vfolder_permission_list( - limit: Int! - offset: Int! - filter: String - order: String - ): VirtualFolderPermissionList - vfolder_own_list( - limit: Int! - offset: Int! - filter: String - order: String - domain_name: String - access_key: String - ): VirtualFolderList - vfolder_invited_list( - limit: Int! - offset: Int! - filter: String - order: String - domain_name: String - access_key: String - ): VirtualFolderList - vfolder_project_list( - limit: Int! - offset: Int! - filter: String - order: String - domain_name: String - access_key: String - ): VirtualFolderList - vfolders( - domain_name: String - group_id: String - access_key: String - ): [VirtualFolder] + storage_volume_list(limit: Int!, offset: Int!, filter: String, order: String): StorageVolumeList + vfolder(id: String): VirtualFolder + vfolder_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, group_id: UUID, access_key: String): VirtualFolderList + vfolder_permission_list(limit: Int!, offset: Int!, filter: String, order: String): VirtualFolderPermissionList + vfolder_own_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, access_key: String): VirtualFolderList + vfolder_invited_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, access_key: String): VirtualFolderList + vfolder_project_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, access_key: String): VirtualFolderList + vfolders(domain_name: String, group_id: String, access_key: String): [VirtualFolder] compute_session(id: UUID!): ComputeSession compute_container(id: UUID!): ComputeContainer - compute_session_list( - limit: Int! - offset: Int! - filter: String - order: String - domain_name: String - group_id: String - access_key: String - status: String - ): ComputeSessionList - compute_container_list( - limit: Int! - offset: Int! - filter: String - order: String - session_id: ID! - role: String - ): ComputeContainerList - legacy_compute_session_list( - limit: Int! - offset: Int! - order_key: String - order_asc: Boolean - domain_name: String - group_id: String - access_key: String - status: String - ): LegacyComputeSessionList - legacy_compute_session( - sess_id: String! - domain_name: String - access_key: String - ): LegacyComputeSession + compute_session_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, group_id: String, access_key: String, status: String): ComputeSessionList + compute_container_list(limit: Int!, offset: Int!, filter: String, order: String, session_id: ID!, role: String): ComputeContainerList + legacy_compute_session_list(limit: Int!, offset: Int!, order_key: String, order_asc: Boolean, domain_name: String, group_id: String, access_key: String, status: String): LegacyComputeSessionList + legacy_compute_session(sess_id: String!, domain_name: String, access_key: String): LegacyComputeSession vfolder_host_permissions: PredefinedAtomicPermission endpoint(endpoint_id: UUID!): Endpoint - endpoint_list( - limit: Int! - offset: Int! - filter: String - order: String - domain_name: String - group_id: String - access_key: String - project: UUID - ): EndpointList + endpoint_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, group_id: String, access_key: String, project: UUID): EndpointList routing(routing_id: UUID!): Routing - routing_list( - limit: Int! - offset: Int! - filter: String - order: String - endpoint_id: UUID - ): RoutingList + routing_list(limit: Int!, offset: Int!, filter: String, order: String, endpoint_id: UUID): RoutingList endpoint_token(token: String!): EndpointToken - endpoint_token_list( - limit: Int! - offset: Int! - filter: String - order: String - endpoint_id: UUID - ): EndpointTokenList + endpoint_token_list(limit: Int!, offset: Int!, filter: String, order: String, endpoint_id: UUID): EndpointTokenList quota_scope(storage_host_name: String!, quota_scope_id: String!): QuotaScope + container_registry(hostname: String!): ContainerRegistry + container_registries: [ContainerRegistry] } type QuotaDetails { @@ -1173,6 +1025,7 @@ type Routing implements Item { status: String traffic_ratio: Float created_at: DateTime + error_data: JSONString } type RoutingList implements PaginatedList { @@ -1246,6 +1099,7 @@ type User implements Item { allowed_client_ip: [String] totp_activated: Boolean totp_activated_at: DateTime + sudo_session_enabled: Boolean groups: [UserGroup] } @@ -1273,6 +1127,7 @@ input UserInput { allowed_client_ip: [String] totp_activated: Boolean resource_policy: String + sudo_session_enabled: Boolean } type UserList implements PaginatedList { @@ -1284,8 +1139,7 @@ type UserResourcePolicy { id: ID! name: String! created_at: DateTime! - max_vfolder_count: BigInt - max_vfolder_size: BigInt # aliased field + max_vfolder_size: BigInt @deprecated(reason: "Deprecated since 23.09.1") max_quota_scope_size: BigInt } @@ -1331,3 +1185,4 @@ type VirtualFolderPermissionList implements PaginatedList { items: [VirtualFolderPermission]! total_count: Int! } + diff --git a/react/package-lock.json b/react/package-lock.json index c6a5ac4be7..cb3249b656 100644 --- a/react/package-lock.json +++ b/react/package-lock.json @@ -17,7 +17,7 @@ "@types/react": "^18.2.23", "@types/react-dom": "^18.2.8", "ahooks": "^3.7.8", - "antd": "^5.9.3", + "antd": "^5.10.1", "dayjs": "^1.11.10", "i18next": "^23.5.1", "i18next-http-backend": "^2.2.2", @@ -94,9 +94,9 @@ } }, "node_modules/@ant-design/cssinjs": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.17.0.tgz", - "integrity": "sha512-MgGCZ6sfD3yQB0XW0hN4jgixMxApTlDYyct+pc7fRZNO4CaqWWm/9iXkkljNR27lyWLZmm+XiDfcIOo1bnrnMA==", + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.17.2.tgz", + "integrity": "sha512-vu7lnfEx4Mf8MPzZxn506Zen3Nt4fRr2uutwvdCuTCN5IiU0lDdQ0tiJ24/rmB8+pefwjluYsbyzbQSbgfJy+A==", "dependencies": { "@babel/runtime": "^7.11.1", "@emotion/hash": "^0.8.0", @@ -5278,56 +5278,56 @@ } }, "node_modules/antd": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/antd/-/antd-5.9.3.tgz", - "integrity": "sha512-a7gY6hfsjoDLOENHKzjXZgmAxi1hDdsuIvYm6YMTctb08EhTEXCZoeFOekwz9S0vrTcdSpUMblRWsiwuYRdPYg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.10.1.tgz", + "integrity": "sha512-alcBmeH4oAdmEdBs6EORH3onRFRjGYRkWtVjPyJxlTIfLILb/+S5Y+ZqisV3AobC8mlj6T3RV8aKG9ic6PgtzQ==", "dependencies": { "@ant-design/colors": "^7.0.0", - "@ant-design/cssinjs": "^1.16.0", - "@ant-design/icons": "^5.2.2", - "@ant-design/react-slick": "~1.0.0", + "@ant-design/cssinjs": "^1.17.2", + "@ant-design/icons": "^5.2.6", + "@ant-design/react-slick": "~1.0.2", "@babel/runtime": "^7.18.3", - "@ctrl/tinycolor": "^3.6.0", + "@ctrl/tinycolor": "^3.6.1", "@rc-component/color-picker": "~1.4.1", "@rc-component/mutate-observer": "^1.1.0", "@rc-component/tour": "~1.10.0", - "@rc-component/trigger": "^1.16.0", + "@rc-component/trigger": "^1.17.0", "classnames": "^2.2.6", "copy-to-clipboard": "^3.2.0", "dayjs": "^1.11.1", "qrcode.react": "^3.1.0", - "rc-cascader": "~3.17.0", + "rc-cascader": "~3.18.1", "rc-checkbox": "~3.1.0", "rc-collapse": "~3.7.1", - "rc-dialog": "~9.2.0", - "rc-drawer": "~6.4.1", + "rc-dialog": "~9.3.3", + "rc-drawer": "~6.5.2", "rc-dropdown": "~4.1.0", - "rc-field-form": "~1.38.1", - "rc-image": "~7.2.0", + "rc-field-form": "~1.39.0", + "rc-image": "~7.3.1", "rc-input": "~1.2.1", "rc-input-number": "~8.1.0", "rc-mentions": "~2.8.0", - "rc-menu": "~9.12.0", + "rc-menu": "~9.12.2", "rc-motion": "^2.9.0", - "rc-notification": "~5.1.1", + "rc-notification": "~5.2.0", "rc-pagination": "~3.6.1", - "rc-picker": "~3.14.1", + "rc-picker": "~3.14.5", "rc-progress": "~3.5.1", "rc-rate": "~2.12.0", "rc-resize-observer": "^1.3.1", "rc-segmented": "~2.2.2", - "rc-select": "~14.9.0", - "rc-slider": "~10.2.1", + "rc-select": "~14.9.1", + "rc-slider": "~10.3.0", "rc-steps": "~6.0.1", "rc-switch": "~4.1.0", - "rc-table": "~7.34.0", + "rc-table": "~7.34.4", "rc-tabs": "~12.12.1", "rc-textarea": "~1.4.0", - "rc-tooltip": "~6.0.1", + "rc-tooltip": "~6.1.1", "rc-tree": "~5.7.12", "rc-tree-select": "~5.13.0", - "rc-upload": "~4.3.4", - "rc-util": "^5.37.0", + "rc-upload": "~4.3.5", + "rc-util": "^5.38.0", "scroll-into-view-if-needed": "^3.0.3", "throttle-debounce": "^5.0.0" }, @@ -15678,9 +15678,9 @@ } }, "node_modules/rc-cascader": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.17.0.tgz", - "integrity": "sha512-8O5Eq/NteRuBaaUIb+ZsTEkNKM3BwWKizsFlSpukCVa2ELqrdMyslbe/OdxtuFlyJIqGyWF5rS2Q+fd0Rpvmgw==", + "version": "3.18.1", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.18.1.tgz", + "integrity": "sha512-M7Xr5Fs/E87ZGustfObtBYQjsvBCET0UX2JYXB2GmOP+2fsZgjaRGXK+CJBmmWXQ6o4OFinpBQBXG4wJOQ5MEg==", "dependencies": { "@babel/runtime": "^7.12.5", "array-tree-filter": "^2.1.0", @@ -15724,9 +15724,9 @@ } }, "node_modules/rc-dialog": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.2.0.tgz", - "integrity": "sha512-dL2tklMou/QfK77+0CTH3FTnKCvIiYv9Df7PfFfg8YVXhYAGmuIkV4ooQYHAIR4juL3Ywcm5oQflF2vDDuGlUg==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.3.3.tgz", + "integrity": "sha512-OpgzE0wq55ebN8TL/ZPc+MLY6qXswEuZg2/3uX3+lqjxUnVaH78PyntpJwqY+3BJdQkDj28XeXYRVY6gXQ8fNg==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/portal": "^1.0.0-8", @@ -15740,9 +15740,9 @@ } }, "node_modules/rc-drawer": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-6.4.1.tgz", - "integrity": "sha512-QIbNMjiZy322o9uEpJHsSZ5rS/zuxqam3lYVPDzjztoqsoDzTNNxWN77QVpOfQ0UC9/87+qu25zocJ+O9bK2Tg==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-6.5.2.tgz", + "integrity": "sha512-QckxAnQNdhh4vtmKN0ZwDf3iakO83W9eZcSKWYYTDv4qcD2fHhRAZJJ/OE6v2ZlQ2kSqCJX5gYssF4HJFvsEPQ==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/portal": "^1.1.1", @@ -15771,9 +15771,9 @@ } }, "node_modules/rc-field-form": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.38.2.tgz", - "integrity": "sha512-O83Oi1qPyEv31Sg+Jwvsj6pXc8uQI2BtIAkURr5lvEYHVggXJhdU/nynK8wY1gbw0qR48k731sN5ON4egRCROA==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.39.0.tgz", + "integrity": "sha512-V7Wk7uji1jBsUGGgP788H9rpFy55HLiD4lywTlktUGjK7EgW5dt+mq1MPbtCpPRMzs83vZBW4SOChOmCACz4WA==", "dependencies": { "@babel/runtime": "^7.18.0", "async-validator": "^4.1.0", @@ -15788,14 +15788,14 @@ } }, "node_modules/rc-image": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.2.0.tgz", - "integrity": "sha512-5Ug2hCVl6VcT0osR5XaZQ4zclTMEWPnbn3b4/TS/MR1QjRpEACLNFUzBGwr5mbAVhzvLWX5YZf4vO10xUA5IUA==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.3.1.tgz", + "integrity": "sha512-Tu3vcUyMHa6zxTiQRzHt1glbGwuNWzeQBG9O6qIdy/+1ue0Qb70it+jUct1YPVNkJa/QfaTfUhmsNsqrw7mgsg==", "dependencies": { "@babel/runtime": "^7.11.2", "@rc-component/portal": "^1.0.2", "classnames": "^2.2.6", - "rc-dialog": "~9.2.0", + "rc-dialog": "~9.3.0", "rc-motion": "^2.6.2", "rc-util": "^5.34.1" }, @@ -15853,12 +15853,12 @@ } }, "node_modules/rc-menu": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.12.0.tgz", - "integrity": "sha512-Apr/fRf5EcqWJ4nphHV6dTGZcLPaPzwY44q9hAtLJysY4rkC9Eg+ekj3uFx6opPWVruV2sJNWq/Po+HHtO48CA==", + "version": "9.12.2", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.12.2.tgz", + "integrity": "sha512-NzloFH2pRUYmQ3S/YbJAvRkgCZaLvq0sRa5rgJtuIHLfPPprNHNyepeSlT64+dbVqI4qRWL44VN0lUCldCbbfg==", "dependencies": { "@babel/runtime": "^7.10.1", - "@rc-component/trigger": "^1.6.2", + "@rc-component/trigger": "^1.17.0", "classnames": "2.x", "rc-motion": "^2.4.3", "rc-overflow": "^1.3.1", @@ -15884,13 +15884,13 @@ } }, "node_modules/rc-notification": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.1.1.tgz", - "integrity": "sha512-BPnded/WmWFE57ubqhVCgRSuedfQQNeSOYqdwppyr2B/Wt909gYFKyWAkFJVXuppAjsOGop05a93UaxjmUFdkg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.2.0.tgz", + "integrity": "sha512-HwUSypEW4mfOpiakJ7dm6TAKf+3zuSR2xm0I0XMes493rtA3n4EVMvQyldrp23hUwCE3RFj8oncyU1E8iNC4ag==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", - "rc-motion": "^2.6.0", + "rc-motion": "^2.9.0", "rc-util": "^5.20.1" }, "engines": { @@ -15931,9 +15931,9 @@ } }, "node_modules/rc-picker": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-3.14.1.tgz", - "integrity": "sha512-U3XKgD1f3OXwRD6aKyuEcZozTPPSuAzmTZO9F5F4ywrcFzSMdq2JmDzhaHH3jjD7WY8w3SaMlLBV01KmHOr5hA==", + "version": "3.14.5", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-3.14.5.tgz", + "integrity": "sha512-h0O8b5AYfWwHSRUUH/9F2oBXB5gZerHIyGG6z2r5rn/kfSQodyCXEO4GNqrG30iUC1qkvLFIOn/JqI4XaO0+2A==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^1.5.0", @@ -16028,9 +16028,9 @@ } }, "node_modules/rc-select": { - "version": "14.9.0", - "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.9.0.tgz", - "integrity": "sha512-vbIhK1MBA12MRdxXbiylSCTPKsWV8WmeN7OyATk9I0LsuIVwe/kBAUNH02am1ryjoylbK+AH309a6X1AflGRSw==", + "version": "14.9.2", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.9.2.tgz", + "integrity": "sha512-VQ15sRFgPURHb8ZcZNSDtb2rAw3+C9xlL0nDziwNHTEW1KvEpZ8y+0v5w24X/Bpl9b3cW1BOyW1F5UqSAq+7Dg==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^1.5.0", @@ -16049,9 +16049,9 @@ } }, "node_modules/rc-slider": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.2.1.tgz", - "integrity": "sha512-l355C/65iV4UFp7mXq5xBTNX2/tF2g74VWiTVlTpNp+6vjE/xaHHNiQq5Af+Uu28uUiqCuH/QXs5HfADL9KJ/A==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.3.1.tgz", + "integrity": "sha512-XszsZLkbjcG9ogQy/zUC0n2kndoKUAnY/Vnk1Go5Gx+JJQBz0Tl15d5IfSiglwBUZPS9vsUJZkfCmkIZSqWbcA==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.2.5", @@ -16097,16 +16097,16 @@ } }, "node_modules/rc-table": { - "version": "7.34.0", - "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.34.0.tgz", - "integrity": "sha512-ueh5AQilBLk0i75FPQj0LekxYbYE6pUKHRNT14wpW0wSbTXfG7QD/LFQqUjdSz5UyAflC8gedmsehTuWI9bp0Q==", + "version": "7.34.4", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.34.4.tgz", + "integrity": "sha512-os+i88Y2AO/6dNkOgJkKSHgXYaZZGnuOEEe+nyaq5IRgvAQNhLysUjXt2objtBeFDEZR8TqXrajwBNRUwunmdw==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/context": "^1.4.0", "classnames": "^2.2.5", "rc-resize-observer": "^1.1.0", "rc-util": "^5.36.0", - "rc-virtual-list": "^3.10.7" + "rc-virtual-list": "^3.11.1" }, "engines": { "node": ">=8.x" @@ -16154,12 +16154,12 @@ } }, "node_modules/rc-tooltip": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.0.1.tgz", - "integrity": "sha512-MdvPlsD1fDSxKp9+HjXrc/CxLmA/s11QYIh1R7aExxfodKP7CZA++DG1AjrW80F8IUdHYcR43HAm0Y2BYPelHA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.1.1.tgz", + "integrity": "sha512-YoxL0Ev4htsX37qgN23eKr0L5PIRpZaLVL9GX6aJ4x6UEnwgXZYUNCAEHfKlKT3eD1felDq3ob4+Cn9lprLDBw==", "dependencies": { "@babel/runtime": "^7.11.2", - "@rc-component/trigger": "^1.0.4", + "@rc-component/trigger": "^1.17.0", "classnames": "^2.3.1" }, "peerDependencies": { @@ -16203,9 +16203,9 @@ } }, "node_modules/rc-upload": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.3.4.tgz", - "integrity": "sha512-uVbtHFGNjHG/RyAfm9fluXB6pvArAGyAx8z7XzXXyorEgVIWj6mOlriuDm0XowDHYz4ycNK0nE0oP3cbFnzxiQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.3.5.tgz", + "integrity": "sha512-EHlKJbhkgFSQHliTj9v/2K5aEuFwfUQgZARzD7AmAPOneZEPiCNF3n6PEWIuqz9h7oq6FuXgdR67sC5BWFxJbA==", "dependencies": { "@babel/runtime": "^7.18.3", "classnames": "^2.2.5", @@ -16217,12 +16217,12 @@ } }, "node_modules/rc-util": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.37.0.tgz", - "integrity": "sha512-cPMV8DzaHI1KDaS7XPRXAf4J7mtBqjvjikLpQieaeOO7+cEbqY2j7Kso/T0R0OiEZTNcLS/8Zl9YrlXiO9UbjQ==", + "version": "5.38.0", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.38.0.tgz", + "integrity": "sha512-yV/YBNdFn+edyBpBdCqkPE29Su0jWcHNgwx2dJbRqMrMfrUcMJUjCRV+ZPhcvWyKFJ63GzEerPrz9JIVo0zXmA==", "dependencies": { "@babel/runtime": "^7.18.3", - "react-is": "^16.12.0" + "react-is": "^18.2.0" }, "peerDependencies": { "react": ">=16.9.0", @@ -16230,14 +16230,14 @@ } }, "node_modules/rc-util/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/rc-virtual-list": { - "version": "3.10.7", - "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.10.7.tgz", - "integrity": "sha512-NZl8nsNV2kf0k9o4SHt6u/2kFBhE0SRTh71msUZkUB/rfNTBRKnhLXf8bD2jayT7TkprnbHBCPQbPqwJo2BSJw==", + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.11.2.tgz", + "integrity": "sha512-MTFLL2LOHr3+/+r+WjTIs6j8XmJE6EqdOsJvCH8SWig7qyik3aljCEImUtw5tdWR0tQhXUfbv7P7nZaLY91XPg==", "dependencies": { "@babel/runtime": "^7.20.0", "classnames": "^2.2.6", diff --git a/react/package.json b/react/package.json index 8723f2ce0d..6a0bd4c103 100644 --- a/react/package.json +++ b/react/package.json @@ -12,7 +12,7 @@ "@types/react": "^18.2.23", "@types/react-dom": "^18.2.8", "ahooks": "^3.7.8", - "antd": "^5.9.3", + "antd": "^5.10.1", "dayjs": "^1.11.10", "i18next": "^23.5.1", "i18next-http-backend": "^2.2.2", @@ -59,11 +59,6 @@ }, "devDependencies": { "@craco/craco": "^7.1.0", - "@testing-library/user-event": "^14.5.0", - "@types/react-relay": "^14.1.5", - "@types/react-test-renderer": "^18.0.1", - "@types/relay-runtime": "^14.1.13", - "@types/relay-test-utils": "^14.1.0", "@testing-library/user-event": "^14.5.1", "@types/react-relay": "^14.1.6", "@types/react-syntax-highlighter": "^15.5.7", diff --git a/react/src/components/ContainerRegistryEditorModal.tsx b/react/src/components/ContainerRegistryEditorModal.tsx new file mode 100644 index 0000000000..a4a1377da5 --- /dev/null +++ b/react/src/components/ContainerRegistryEditorModal.tsx @@ -0,0 +1,385 @@ +import BAIModal, { BAIModalProps } from './BAIModal'; +import { ContainerRegistryEditorModalCreateMutation } from './__generated__/ContainerRegistryEditorModalCreateMutation.graphql'; +import { ContainerRegistryEditorModalFragment$key } from './__generated__/ContainerRegistryEditorModalFragment.graphql'; +import { ContainerRegistryEditorModalModifyMutation } from './__generated__/ContainerRegistryEditorModalModifyMutation.graphql'; +import { message, Form, Input, Select, Modal, Checkbox } from 'antd'; +import graphql from 'babel-plugin-relay/macro'; +import _ from 'lodash'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useFragment, useMutation } from 'react-relay'; + +interface ContainerRegistryEditorModalProps + extends Omit { + existingHostnames?: string[]; + onOk: (type: 'create' | 'modify') => void; + containerRegistryFrgmt?: ContainerRegistryEditorModalFragment$key | null; +} +const ContainerRegistryEditorModal: React.FC< + ContainerRegistryEditorModalProps +> = ({ + existingHostnames, + containerRegistryFrgmt = null, + onOk, + ...modalProps +}) => { + const { t } = useTranslation(); + const [form] = Form.useForm(); + + const [messageAPI, contextHolder] = message.useMessage(); + const [modal, modalContextHolder] = Modal.useModal(); + + const containerRegistry = useFragment( + graphql` + fragment ContainerRegistryEditorModalFragment on ContainerRegistry { + id + hostname + config { + url + type + project + username + ssl_verify + } + } + `, + containerRegistryFrgmt, + ); + const [commitCreateRegistry, isInflightCreateRegistry] = + useMutation(graphql` + mutation ContainerRegistryEditorModalCreateMutation( + $hostname: String! + $props: CreateContainerRegistryInput! + ) { + create_container_registry(hostname: $hostname, props: $props) { + container_registry { + id + hostname + config { + url + type + project + username + ssl_verify + } + } + } + } + `); + + const [commitModifyRegistry, isInflightModifyRegistry] = + useMutation(graphql` + mutation ContainerRegistryEditorModalModifyMutation( + $hostname: String! + $props: ModifyContainerRegistryInput! + ) { + modify_container_registry(hostname: $hostname, props: $props) { + container_registry { + id + hostname + config { + url + type + project + username + ssl_verify + } + } + } + } + `); + + const handleSave = async () => { + return form + .validateFields() + .then((values) => { + const mutationVariables = { + hostname: values.hostname, + props: { + url: values.config.url, + type: values.config.type, + project: + values.config.project === 'docker' + ? undefined + : values.config.project, + username: _.isEmpty(values.config.username) + ? null + : values.config.username, + password: _.isEmpty(values.config.password) + ? null + : values.config.password, + }, + }; + if (containerRegistry) { + if (!values.isChangedPassword) { + delete mutationVariables.props.password; + } + commitModifyRegistry({ + variables: mutationVariables, + onCompleted: (res, error) => { + if (error) { + messageAPI.error(t('dialog.ErrorOccurred')); + } else { + onOk && onOk('modify'); + } + }, + onError: (error) => { + messageAPI.error(t('dialog.ErrorOccurred')); + }, + }); + } else { + commitCreateRegistry({ + variables: mutationVariables, + onCompleted: (res, error) => { + if (error) { + messageAPI.error(t('dialog.ErrorOccurred')); + } else { + onOk && onOk('create'); + } + }, + onError(error) { + messageAPI.error(t('dialog.ErrorOccurred')); + }, + }); + } + }) + .catch((error) => {}); + }; + return ( + { + form + .validateFields() + .then((values) => { + if (_.includes(values.config?.type, 'harbor')) { + modal.confirm({ + title: t('button.Confirm'), + content: t('registry.ConfirmNoUserName'), + onOk: () => { + handleSave(); + }, + }); + } else { + handleSave(); + } + }) + .catch(() => {}); + }} + {...modalProps} + destroyOnClose + > + {contextHolder} + {modalContextHolder} +
+ { + if (!containerRegistry && existingHostnames?.includes(value)) { + return Promise.reject( + t('registry.RegistryHostnameAlreadyExists'), + ); + } + return Promise.resolve(); + }, + }, + ]} + > + + + { + if ( + value && + !value.startsWith('http://') && + !value.startsWith('https://') + ) { + return Promise.reject(t('registry.DescURLStartString')); + } + return Promise.resolve(); + }, + }, + ]} + > + + + + + _.isEmpty(prev.config?.password) !== + _.isEmpty(next.config?.password) + } + > + {() => { + form.validateFields([['config', 'username']]); + return ( + + + + ); + }} + + + + + prev.isChangedPassword !== next.isChangedPassword + } + > + {() => ( + + + + )} + + {!_.isEmpty(containerRegistry) && ( + + { + if (!e.target.checked) { + form.setFieldValue(['config', 'password'], ''); + } + }} + > + {t('webui.menu.ChangePassword')} + + + )} + + + + + + prev?.config?.type !== next?.config?.type + } + noStyle + > + {() => { + return ( + form.getFieldValue(['config', 'type']) !== 'docker' && ( + + {/* */} + setDeletingConfirmText(e.target.value)} + /> + + + +
+ + ); +}; + +export default ContainerRegistryList; diff --git a/react/src/components/DefaultProviders.tsx b/react/src/components/DefaultProviders.tsx index 498de4be60..efaeffd896 100644 --- a/react/src/components/DefaultProviders.tsx +++ b/react/src/components/DefaultProviders.tsx @@ -4,7 +4,7 @@ import rawFixAntCss from '../fix_antd.css?raw'; import { useCustomThemeConfig } from '../helper/customThemeConfig'; import { ReactWebComponentProps } from '../helper/react-to-webcomponent'; import { StyleProvider, createCache } from '@ant-design/cssinjs'; -import { ConfigProvider } from 'antd'; +import { App, ConfigProvider } from 'antd'; import en_US from 'antd/locale/en_US'; import ko_KR from 'antd/locale/ko_KR'; import dayjs from 'dayjs'; @@ -161,24 +161,26 @@ const DefaultProviders: React.FC = ({ locale={'ko' === lang ? ko_KR : en_US} theme={themeConfig} > - - - - + + + + - - {children} - - - - + > + + {children} + + + + + diff --git a/react/src/components/PopConfirmWithInput.tsx b/react/src/components/PopConfirmWithInput.tsx new file mode 100644 index 0000000000..da04355217 --- /dev/null +++ b/react/src/components/PopConfirmWithInput.tsx @@ -0,0 +1,102 @@ +import Flex from './Flex'; +import { ExclamationCircleFilled } from '@ant-design/icons'; +import { Form, Input, Popconfirm, PopconfirmProps, Typography } from 'antd'; +import _ from 'lodash'; +import React from 'react'; + +const { Text } = Typography; + +interface Props extends PopconfirmProps { + confirmText: string; + content: React.ReactNode; + title: React.ReactNode; +} +const PopConfirmWithInput: React.FC = ({ + confirmText, + children, + onConfirm, + onCancel, + title, + icon, + content, + ...props +}) => { + const [form] = Form.useForm(); + const typedText = Form.useWatch('confirmText', form); + + return ( + { + alert(e); + e.preventDefault(); + e.stopPropagation(); + }} + > + + {icon ?? ( + + )} + {title} + + {content} + {/* + {' '} + Please type {confirmText} to confirm. + */} +
+ { + if (value === confirmText) { + return Promise.resolve(); + } + return Promise.reject(); + }, + }, + ]} + > + { + e.preventDefault(); + e.stopPropagation(); + }} + /> + +
+ + } + icon={null} + okButtonProps={{ disabled: confirmText !== typedText, danger: true }} + onConfirm={(e) => { + form.resetFields(); + _.isFunction(onConfirm) && onConfirm(e); + }} + onCancel={(e) => { + form.resetFields(); + _.isFunction(onCancel) && onCancel(e); + }} + okText="Delete" + cancelText="No" + {...props} + > + {children} +
+ ); +}; + +export default PopConfirmWithInput; diff --git a/react/src/components/ProjectResourcePolicySettingModal.tsx b/react/src/components/ProjectResourcePolicySettingModal.tsx index c626e156bf..4f9cb6341c 100644 --- a/react/src/components/ProjectResourcePolicySettingModal.tsx +++ b/react/src/components/ProjectResourcePolicySettingModal.tsx @@ -78,7 +78,7 @@ const ProjectResourcePolicySettingModal: React.FC = ({ variables: { name: projectResourcePolicyInfo?.name, props: { - max_vfolder_count: values?.max_vfolder_count, + // max_vfolder_count: values?.max_vfolder_count, max_quota_scope_size: GBToBytes(values?.max_quota_scope_size), }, }, diff --git a/react/src/components/ResourcePolicyCard.tsx b/react/src/components/ResourcePolicyCard.tsx index a2973770df..dba51fa93d 100644 --- a/react/src/components/ResourcePolicyCard.tsx +++ b/react/src/components/ResourcePolicyCard.tsx @@ -116,7 +116,7 @@ const ResourcePolicyCard: React.FC = ({ variables: { name: project_resource_policy.name, props: { - max_vfolder_count: 0, + // max_vfolder_count: 0, max_quota_scope_size: -1, }, }, @@ -139,7 +139,7 @@ const ResourcePolicyCard: React.FC = ({ variables: { name: user_resource_policy.name, props: { - max_vfolder_count: 0, + // max_vfolder_count: 0, max_quota_scope_size: -1, }, }, diff --git a/react/src/components/StorageStatusPanel.tsx b/react/src/components/StorageStatusPanel.tsx index 6d4982b741..bfbca02c0c 100644 --- a/react/src/components/StorageStatusPanel.tsx +++ b/react/src/components/StorageStatusPanel.tsx @@ -68,12 +68,17 @@ const StorageStatusPanel: React.FC<{ ).length; // TODO: Add resolver to enable subquery and modify to call useLazyLoadQuery only once. - const { user } = useLazyLoadQuery( + // const { user } = useLazyLoadQuery( + const { keypair, user } = useLazyLoadQuery( graphql` query StorageStatusPanelKeypairQuery( $domain_name: String $email: String + $access_key: String ) { + keypair(domain_name: $domain_name, access_key: $access_key) { + resource_policy + } user(domain_name: $domain_name, email: $email) { id } @@ -82,20 +87,26 @@ const StorageStatusPanel: React.FC<{ { domain_name: useCurrentDomainValue(), email: baiClient?.email, + access_key: baiClient?._config.accessKey, }, ); - const { user_resource_policy, project_quota_scope, user_quota_scope } = + // const { user_resource_policy, project_quota_scope, user_quota_scope } = + const { keypair_resource_policy, project_quota_scope, user_quota_scope } = useLazyLoadQuery( graphql` query StorageStatusPanelQuery( - $name: String + # $name: String + $keypair_resource_policy_name: String $project_quota_scope_id: String! $user_quota_scope_id: String! $storage_host_name: String! $skipQuotaScope: Boolean! ) { - user_resource_policy(name: $name) { + # user_resource_policy(name: $name) { + # max_vfolder_count + # } + keypair_resource_policy(name: $keypair_resource_policy_name) { max_vfolder_count } project_quota_scope: quota_scope( @@ -113,6 +124,7 @@ const StorageStatusPanel: React.FC<{ } `, { + keypair_resource_policy_name: keypair?.resource_policy, project_quota_scope_id: addQuotaScopeTypePrefix( 'project', currentProject?.id, @@ -126,7 +138,8 @@ const StorageStatusPanel: React.FC<{ }, ); - const maxVfolderCount = user_resource_policy?.max_vfolder_count || 0; + const maxVfolderCount = keypair_resource_policy?.max_vfolder_count || 0; + // const maxVfolderCount = user_resource_policy?.max_vfolder_count || 0; const numberOfFolderPercent = ( maxVfolderCount > 0 ? ((createdCount / maxVfolderCount) * 100)?.toFixed(2) diff --git a/react/src/components/UserResourcePolicySettingModal.tsx b/react/src/components/UserResourcePolicySettingModal.tsx index 34fdc57fbb..7560167076 100644 --- a/react/src/components/UserResourcePolicySettingModal.tsx +++ b/react/src/components/UserResourcePolicySettingModal.tsx @@ -30,7 +30,7 @@ const UserResourcePolicySettingModal: React.FC = ({ id name created_at - max_vfolder_count + # max_vfolder_count max_quota_scope_size } `, @@ -78,7 +78,7 @@ const UserResourcePolicySettingModal: React.FC = ({ variables: { name: userResourcePolicyInfo?.name, props: { - max_vfolder_count: values?.max_vfolder_count, + // max_vfolder_count: values?.max_vfolder_count, max_quota_scope_size: GBToBytes(values?.max_quota_scope_size), }, }, diff --git a/react/src/components/__snapshots__/DomainSelector.test.tsx.snap b/react/src/components/__snapshots__/DomainSelector.test.tsx.snap index 7ea81a3007..38b944f0a4 100644 --- a/react/src/components/__snapshots__/DomainSelector.test.tsx.snap +++ b/react/src/components/__snapshots__/DomainSelector.test.tsx.snap @@ -3,7 +3,7 @@ exports[`DomainSelect default render 1`] = `
{ + const { t } = useTranslation(); + const relieve = (msg: string) => { + if (typeof msg === 'undefined') { + if ( + // @ts-ignore + globalThis.backendaiclient === undefined || + // @ts-ignore + globalThis.backendaiclient === null + ) { + return '_DISCONNECTED'; + } else { + return 'Problem occurred.'; + } + } + // @ts-ignore + if (globalThis.backendaiwebui.debug === true) { + return msg; + } + if ({}.hasOwnProperty.call(errorMessageTable, msg)) { + return t(errorMessageTable[msg]); + } else { + for (const regex of Object.keys(regexTable)) { + if (RegExp(regex).test(msg)) { + return t(regexTable[regex]); + } + } + return msg; // Bypass message. It will log on log panel + } + }; + return { relieve }; +}; diff --git a/react/src/index.tsx b/react/src/index.tsx index d199b34539..9d1b7f58f6 100644 --- a/react/src/index.tsx +++ b/react/src/index.tsx @@ -1,5 +1,6 @@ import BAIErrorBoundary from './components/BAIErrorBoundary'; import Flex from './components/Flex'; +import FlexActivityIndicator from './components/FlexActivityIndicator'; import ResourceGroupSelect from './components/ResourceGroupSelect'; import { loadCustomThemeConfig } from './helper/customThemeConfig'; import reactToWebComponent from './helper/react-to-webcomponent'; @@ -39,7 +40,6 @@ const UserInfoModal = React.lazy(() => import('./components/UserInfoModal')); const UserSettingsModal = React.lazy( () => import('./components/UserSettingModal'), ); - const ManageAppsModal = React.lazy( () => import('./components/ManageAppsModal'), ); @@ -49,10 +49,12 @@ const UserDropdownMenu = React.lazy( const UserProfileSettingModal = React.lazy( () => import('./components/UserProfileSettingModal'), ); - const SessionLauncherPage = React.lazy( () => import('./pages/SessionLauncherPage'), ); +const ContainerRegistryList = React.lazy( + () => import('./components/ContainerRegistryList'), +); customElements.define( 'backend-ai-react-information', @@ -230,3 +232,16 @@ customElements.define( ); }), ); + +customElements.define( + 'backend-ai-react-container-registry-list', + reactToWebComponent((props) => { + return ( + + }> + + + + ); + }), +); diff --git a/resources/i18n/de.json b/resources/i18n/de.json index 331be5727f..2fe3904bfb 100644 --- a/resources/i18n/de.json +++ b/resources/i18n/de.json @@ -233,10 +233,11 @@ "PortsTitleWithRange": "Port-Wert (zwischen 1024 ~ 65535)", "PrePortConfigWillDisappear": "Alle nicht gespeicherten, bereits geöffneten Ports werden gelöscht.", "StartModelServing": "Start Model Serving", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Geben Sie mehrere Werte ein, getrennt durch ein Komma (,) oder ein Leerzeichen.", + "preopen": "Voreröffnet", + "Network": "Netzwerk", + "Enable": "Aktivieren", + "FolderAliasInvalid": "Es sind nur alphanumerische Zeichen, Unterstrich (_), Schrägstrich (/) und Bindestrich (-) zulässig." }, "Preparing": "Vorbereitung...", "PreparingSession": "Sitzung vorbereiten...", @@ -293,8 +294,8 @@ "SessionNameRequired": "Sitzungsname ist erforderlich", "SluggedStrings": "Geschliffene Zeichenfolgen (>3 Zeichen)", "SessionNameAlreadyExist": "Sitzungsname existiert bereits", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Bitte geben Sie maximal 64 Zeichen ein.", + "PleaseFollowSessionNameRule": "Bitte geben Sie eine Kombination aus mindestens 3 Buchstaben, Zahlen, „.“, „_“ und „-“ ein." }, "Interactive": "Interaktiv", "Batch": "Stapel", @@ -670,7 +671,7 @@ "Pipeline": "Pipeline-Ordner", "DialogDataFolder": "Pipeline-spezifische Ordner, die bei der Erstellung von Pipelines in FastTrack automatisch erstellt werden.", "DialogModelFolder": "Sie können Modelle bedienen und verwalten.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Suche mit Name" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Bitte wählen Sie eine Option.", "RegistrySuccessfullyModified": "Registry erfolgreich geändert.", "NoRegistryToDisplay": "Keine Registrierungen anzeigen", - "ModifyRegistry": "Registry ändern" + "ModifyRegistry": "Registry ändern", + "ConfirmNoUserName": "Benutzer und Passwort nicht angegeben, möchten Sie speichern?" }, "resourceGroup": { "ResourceGroups": "Ressourcengruppen", diff --git a/resources/i18n/el.json b/resources/i18n/el.json index c2fb3bb354..f245c0205f 100644 --- a/resources/i18n/el.json +++ b/resources/i18n/el.json @@ -233,10 +233,11 @@ "PortsTitleWithRange": "Τιμή θύρας (μεταξύ 1024 ~ 65535)", "PrePortConfigWillDisappear": "Τυχόν μη αποθηκευμένες προ-ανοιχτές θύρες θα εξαφανιστούν.", "StartModelServing": "Έναρξη μοντέλου σερβιρίσματος", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Εισαγάγετε πολλές τιμές διαχωρισμένες είτε με κόμμα (,) είτε με κενό.", + "preopen": "προανοίγει", + "Network": "Δίκτυο", + "Enable": "επιτρέπω", + "FolderAliasInvalid": "Επιτρέπονται μόνο αλφαριθμητικοί χαρακτήρες, κάτω παύλα (_), κάθετος (/) και παύλα (-)." }, "Preparing": "Προετοιμασία ...", "PreparingSession": "Προετοιμασία συνεδρίας ...", @@ -293,8 +294,8 @@ "SessionNameRequired": "Το όνομα συνεδρίας είναι υποχρεωτικό", "SluggedStrings": "Στιγμιότυπα αλφαριθμητικά (>3 χαρακτήρες)", "SessionNameAlreadyExist": "Το όνομα συνεδρίας υπάρχει ήδη", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Εισαγάγετε 64 χαρακτήρες ή λιγότερους.", + "PleaseFollowSessionNameRule": "Εισαγάγετε έναν συνδυασμό τουλάχιστον 3 γραμμάτων, αριθμών, '.', '_' και '-'." }, "Interactive": "Διαδραστικό", "Batch": "Παρτίδα", @@ -670,7 +671,7 @@ "Pipeline": "Φάκελοι αγωγών", "DialogDataFolder": "Ειδικοί φάκελοι αγωγών που δημιουργούνται αυτόματα κατά τη δημιουργία αγωγών στο FastTrack.", "DialogModelFolder": "Μπορείτε να εξυπηρετείτε και να διαχειρίζεστε μοντέλα.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Αναζήτηση με όνομα" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Επιλέξτε μία από τις επιλογές.", "RegistrySuccessfullyModified": "Το μητρώο τροποποιήθηκε επιτυχώς.", "NoRegistryToDisplay": "Δεν εμφανίζονται μητρώα", - "ModifyRegistry": "Τροποποίηση μητρώου" + "ModifyRegistry": "Τροποποίηση μητρώου", + "ConfirmNoUserName": "Ο χρήστης και ο κωδικός πρόσβασης δεν έχουν καθοριστεί, θέλετε να αποθηκεύσετε;" }, "resourceGroup": { "ResourceGroups": "Ομάδες πόρων", diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 2f17794b4d..739f871b0f 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -281,7 +281,7 @@ "AllowedClientIps": "Allowed client IPs", "CommaSeparated": "comma-separated", "TryPreferredPort": "Try preferred port", - "SFTPDescription": "You can upload files quickly and securely through an SSH/SFTP client. If you haven't uploaded your SSH key pair beforehand, please click the \"SSH Key Download\" button to save your SSH key first. You can then use that key to execute commands such as SFTP, SCP, and Rsync. On the \"Session - Upload Session\" page, you can manage the list of sessions for file uploads.", + "SFTPDescription": "You can upload files quickly and securely through an SSH/SFTP client. If you haven't uploaded your SSH key pair beforehand, please click the \"DOWNLOAD SSH KEY\" button to save your SSH key first. You can then use that key to execute commands such as SFTP, SCP, and Rsync. On the \"Session - Upload Session\" page, you can manage the list of sessions for file uploads.", "SFTPExtraNotification": "It is recommended to delete sessions after they have been used, as there is a limit on the number of sessions. Sessions that are not used for uploading files within a certain period of time may be automatically deleted.", "Readmore": "Read More...", "Readless": "Read less", @@ -990,7 +990,8 @@ "RescanFailed": "Rescan Failed.", "NoRegistryToDisplay": "No Registries to display", "RegistrySuccessfullyModified": "Registry successfully modified.", - "PleaseSelectOption": "Please Select Option" + "PleaseSelectOption": "Please Select Option", + "ConfirmNoUserName": "User and password not specified, would you like to save?" }, "resourceGroup": { "ResourceGroups": "Resource Groups", diff --git a/resources/i18n/es.json b/resources/i18n/es.json index effa67c3a4..51086e401f 100644 --- a/resources/i18n/es.json +++ b/resources/i18n/es.json @@ -369,7 +369,8 @@ "Status": "Estado", "StatusOfSelectedHost": "Estado del host seleccionado", "used": "usado" - } + }, + "SearchByName": "Buscar por nombre" }, "dialog": { "ErrorOccurred": "Se ha producido un error", @@ -782,7 +783,8 @@ "TypeRegistryNameToDelete": "Escriba el nombre de host del registro que desea eliminar", "UpdatingRegistryInfo": "Actualización de la información del registro...", "Username": "Nombre de usuario", - "UsernameOptional": "Nombre de usuario (opcional)" + "UsernameOptional": "Nombre de usuario (opcional)", + "ConfirmNoUserName": "Usuario y contraseña no especificados, ¿desea guardar?" }, "resourceGroup": { "Active": "Activo", @@ -963,7 +965,9 @@ "EnterValidSessionName": "Introduzca un nombre de sesión válido", "SessionNameAlreadyExist": "El nombre de la sesión ya existe", "SessionNameRequired": "El nombre de la sesión es obligatorio", - "SluggedStrings": "Cadenas babosas (>3 caracteres)" + "SluggedStrings": "Cadenas babosas (>3 caracteres)", + "SessionNameTooLong64": "Introduzca 64 caracteres o menos.", + "PleaseFollowSessionNameRule": "Ingrese una combinación de al menos 3 letras, números, '.', '_' y '-'." }, "XRDPconnection": "Conexión RDP", "applauncher": { @@ -1096,7 +1100,12 @@ "TotalAllocation": "Asignación total", "UserResourceLimit": "Límite de recursos de usuario", "Version": "Versión", - "sessionStillPreparing": "La sesión aún está en preparación. Una vez iniciada la sesión, pulse el icono de la aplicación para iniciarla." + "sessionStillPreparing": "La sesión aún está en preparación. Una vez iniciada la sesión, pulse el icono de la aplicación para iniciarla.", + "PreOpenPortRangeGuide": "Introduzca varios valores separados por una coma (,) o un espacio.", + "preopen": "preabierto", + "FolderAliasInvalid": "Sólo se permiten caracteres alfanuméricos, guión bajo (_), barra (/) y guión (-).", + "Network": "Red", + "Enable": "Permitir" }, "ExpiresAfter": "Tiempo restante", "CPU": "CPU", diff --git a/resources/i18n/fi.json b/resources/i18n/fi.json index e2a3f4a909..7715ff2279 100644 --- a/resources/i18n/fi.json +++ b/resources/i18n/fi.json @@ -369,7 +369,8 @@ "Status": "Tila", "StatusOfSelectedHost": "Valitun isännän tila", "used": "käytetty" - } + }, + "SearchByName": "Hae nimellä" }, "dialog": { "ErrorOccurred": "Tapahtunut virhe", @@ -782,7 +783,8 @@ "TypeRegistryNameToDelete": "Kirjoita rekisterin isäntänimi, jonka haluat poistaa", "UpdatingRegistryInfo": "Rekisteritietojen päivittäminen...", "Username": "Käyttäjätunnus", - "UsernameOptional": "Käyttäjätunnus (valinnainen)" + "UsernameOptional": "Käyttäjätunnus (valinnainen)", + "ConfirmNoUserName": "Käyttäjää ja salasanaa ei ole määritetty, haluatko tallentaa?" }, "resourceGroup": { "Active": "Aktiivinen", @@ -963,7 +965,9 @@ "EnterValidSessionName": "Syötä kelvollinen istunnon nimi", "SessionNameAlreadyExist": "Istunnon nimi on jo olemassa", "SessionNameRequired": "Istunnon nimi vaaditaan", - "SluggedStrings": "Slugged-merkkijonot (>3 merkkiä)" + "SluggedStrings": "Slugged-merkkijonot (>3 merkkiä)", + "SessionNameTooLong64": "Anna enintään 64 merkkiä.", + "PleaseFollowSessionNameRule": "Anna vähintään kolmen kirjaimen, numeron, '.', '_' ja '-' yhdistelmä." }, "XRDPconnection": "RDP-yhteys", "applauncher": { @@ -1096,7 +1100,12 @@ "TotalAllocation": "Kokonaismääräraha", "UserResourceLimit": "Käyttäjän resurssirajoitus", "Version": "Versio", - "sessionStillPreparing": "Istuntoa valmistellaan edelleen. Kun istunto on alkanut, käynnistä sovellus napauttamalla sovelluskuvaketta." + "sessionStillPreparing": "Istuntoa valmistellaan edelleen. Kun istunto on alkanut, käynnistä sovellus napauttamalla sovelluskuvaketta.", + "PreOpenPortRangeGuide": "Syötä useita arvoja joko pilkulla (,) tai välilyönnillä erotettuina.", + "preopen": "avataan etukäteen", + "FolderAliasInvalid": "Vain aakkosnumeeriset merkit, alaviiva (_), kauttaviiva (/) ja viiva (-) ovat sallittuja.", + "Network": "Verkko", + "Enable": "ota käyttöön" }, "ExpiresAfter": "Jäljellä oleva aika", "CPU": "CPU", diff --git a/resources/i18n/fr.json b/resources/i18n/fr.json index f12fbbe1a0..286912529e 100644 --- a/resources/i18n/fr.json +++ b/resources/i18n/fr.json @@ -233,10 +233,11 @@ "PreOpenPortRange": "Les ports préouverts sont uniquement disponibles entre 1024 et 65535.", "MinMemory": "La capacité de mémoire minimale pour l'environnement d'exécution actuellement sélectionné est de {{taille}}iB.", "ModelStorage": "Stockage de modèles", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Entrez plusieurs valeurs séparées par une virgule (,) ou un espace.", + "preopen": "préouverture", + "Network": "Réseau", + "Enable": "Activer", + "FolderAliasInvalid": "Seuls les caractères alphanumériques, le trait de soulignement (_), la barre oblique (/) et le tiret (-) sont autorisés." }, "Preparing": "En train de préparer...", "PreparingSession": "Séance de préparation...", @@ -303,8 +304,8 @@ "EnterValidSessionName": "Saisir un nom de session valide", "SessionNameAlreadyExist": "Le nom de la session existe déjà", "SluggedStrings": "Chaînes de caractères tronquées (>3 chars)", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Veuillez saisir 64 caractères ou moins.", + "PleaseFollowSessionNameRule": "Veuillez saisir une combinaison d'au moins 3 lettres, chiffres, '.', '_' et '-'." }, "AlreadyTerminatingSession": "La session est déjà terminée.", "NoSessionToDisplay": "Aucune session à afficher", @@ -670,7 +671,7 @@ "Used": "Utilisé", "Pipeline": "Dossiers du pipeline", "DialogDataFolder": "Dossiers dédiés aux pipelines qui sont automatiquement créés lors de la création de pipelines dans FastTrack.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Rechercher par nom" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Veuillez sélectionner une option.", "RegistrySuccessfullyModified": "Registre modifié avec succès.", "ModifyRegistry": "Modifier le registre", - "NoRegistryToDisplay": "Aucun registre à afficher" + "NoRegistryToDisplay": "Aucun registre à afficher", + "ConfirmNoUserName": "L'utilisateur et le mot de passe ne sont pas spécifiés, voulez-vous sauvegarder ?" }, "resourceGroup": { "ResourceGroups": "Groupes de ressources", diff --git a/resources/i18n/id.json b/resources/i18n/id.json index 05ee46ea72..b5b6a22961 100644 --- a/resources/i18n/id.json +++ b/resources/i18n/id.json @@ -233,10 +233,11 @@ "PreOpenPortRange": "Port yang sudah dibuka sebelumnya hanya tersedia dari 1024 hingga 65535.", "MinMemory": "Kapasitas memori minimum untuk lingkungan runtime yang saat ini dipilih adalah {{size}}iB.", "ModelStorage": "Penyimpanan Model", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Masukkan beberapa nilai yang dipisahkan dengan koma (,) atau spasi.", + "preopen": "pra-buka", + "Network": "Jaringan", + "Enable": "Memungkinkan", + "FolderAliasInvalid": "Hanya karakter alfanumerik, garis bawah (_), garis miring (/), dan tanda hubung (-) yang diperbolehkan." }, "Preparing": "Mempersiapkan...", "PreparingSession": "Mempersiapkan sesi...", @@ -303,8 +304,8 @@ "EnterValidSessionName": "Masukkan nama sesi yang valid", "SessionNameAlreadyExist": "Nama sesi sudah ada", "SluggedStrings": "String yang disumbat (>3 karakter)", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Silakan masukkan 64 karakter atau kurang.", + "PleaseFollowSessionNameRule": "Silakan masukkan kombinasi minimal 3 huruf, angka, '.', '_', dan '-'." }, "AlreadyTerminatingSession": "Sudah mengakhiri sesi.", "NoSessionToDisplay": "Tidak ada sesi yang ditampilkan", @@ -670,7 +671,7 @@ "Used": "Digunakan", "Pipeline": "Folder Pipa", "DialogDataFolder": "Folder khusus pipeline yang secara otomatis dibuat saat membuat pipeline di FastTrack.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Cari berdasarkan nama" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Silakan pilih salah satu opsi.", "RegistrySuccessfullyModified": "Registry berhasil dimodifikasi.", "ModifyRegistry": "Memodifikasi Registri", - "NoRegistryToDisplay": "Tidak ada Pendaftaran untuk ditampilkan" + "NoRegistryToDisplay": "Tidak ada Pendaftaran untuk ditampilkan", + "ConfirmNoUserName": "Pengguna dan kata sandi tidak ditentukan, apakah Anda ingin menyimpan?" }, "resourceGroup": { "ResourceGroups": "Grup Sumber Daya", diff --git a/resources/i18n/it.json b/resources/i18n/it.json index 720339c914..64628a10ea 100644 --- a/resources/i18n/it.json +++ b/resources/i18n/it.json @@ -233,10 +233,11 @@ "PortsTitleWithRange": "Valore della porta (tra 1024 e 65535)", "PrePortConfigWillDisappear": "Tutte le porte preaperture non salvate scompariranno.", "StartModelServing": "Avvio del modello di servizio", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Inserisci più valori separati da una virgola (,) o da uno spazio.", + "preopen": "preaperto", + "Network": "Rete", + "Enable": "Abilitare", + "FolderAliasInvalid": "Sono consentiti solo caratteri alfanumerici, carattere di sottolineatura (_), barra (/) e trattino (-)." }, "Preparing": "Preparazione...", "PreparingSession": "Preparazione della sessione...", @@ -293,8 +294,8 @@ "SessionNameRequired": "Il nome della sessione è obbligatorio", "SluggedStrings": "Stringhe con slugged (>3 caratteri)", "SessionNameAlreadyExist": "Il nome della sessione esiste già", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Inserisci 64 caratteri o meno.", + "PleaseFollowSessionNameRule": "Inserisci una combinazione di almeno 3 lettere, numeri, \".\", \"_\" e \"-\"." }, "Interactive": "Interattivo", "Batch": "Lotto", @@ -670,7 +671,7 @@ "Pipeline": "Cartelle della pipeline", "DialogDataFolder": "Cartelle dedicate alle pipeline che vengono create automaticamente quando si creano le pipeline in FastTrack.", "DialogModelFolder": "È possibile servire e gestire i modelli.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Ricerca per nome" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Seleziona un'opzione.", "RegistrySuccessfullyModified": "Registro modificato con successo.", "NoRegistryToDisplay": "Nessun registro da visualizzare", - "ModifyRegistry": "Modificare il registro" + "ModifyRegistry": "Modificare il registro", + "ConfirmNoUserName": "Utente e password non specificati, si desidera salvare?" }, "resourceGroup": { "ResourceGroups": "Gruppi di risorse", diff --git a/resources/i18n/ja.json b/resources/i18n/ja.json index c07ad867d5..335f198959 100644 --- a/resources/i18n/ja.json +++ b/resources/i18n/ja.json @@ -233,10 +233,11 @@ "PrePortConfigWillDisappear": "保存されていない事前開放ポート値は消えます。", "StartModelServing": "モデルサービス開始", "ModelStorage": "モデルストレージフォルダ", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "複数の値をカンマ(、)またはスペースで区切って入力できます。", + "preopen": "事前開放ポート", + "Network": "ネットワーク", + "Enable": "活性化", + "FolderAliasInvalid": "英語の大文字と小文字、数字、アンダースコア(_)、スラッシュ(/)、ダッシュ(-)のみを入力できます。" }, "Preparing": "準備...", "PreparingSession": "セッションの準備...", @@ -293,8 +294,8 @@ "SessionNameRequired": "セッション名を入力してください", "SluggedStrings": "最低4文字以上の固有の文字列で入力してください。", "SessionNameAlreadyExist": "同じ名前のセッションが既にあります", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "64文字以下で入力してください。", + "PleaseFollowSessionNameRule": "少なくとも3文字以上の文字、数字、「.」、「_」、「-」の組み合わせで入力してください。" }, "Interactive": "インタラクティブ", "Batch": "バッチ", @@ -670,7 +671,7 @@ "Pipeline": "パイプラインフォルダ", "DialogDataFolder": "FastTrackでパイプライン作成時に自動的に生成されるパイプライン専用フォルダ。", "DialogModelFolder": "モデルを選択して実行したり、モデルを管理することができます。", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "名前で検索" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "オプションを1つ選択してください。", "RegistrySuccessfullyModified": "レジストリが正常に修正されました。", "NoRegistryToDisplay": "レジストリ情報がありません。", - "ModifyRegistry": "レジストリの修正" + "ModifyRegistry": "レジストリの修正", + "ConfirmNoUserName": "ユーザーとパスワードが指定されていません。" }, "resourceGroup": { "ResourceGroups": "リソースグループ", diff --git a/resources/i18n/ko.json b/resources/i18n/ko.json index bb13efe058..5f2b0ef604 100644 --- a/resources/i18n/ko.json +++ b/resources/i18n/ko.json @@ -264,7 +264,7 @@ "AllowedClientIps": "허용된 클라이언트 IP", "CommaSeparated": "콤마로 구분", "TryPreferredPort": "선호 포트", - "SFTPDescription": "SSH/SFTP 클라이언트를 통해 빠르고 안정적으로 파일을 업로드할 수 있습니다. 미리 SSH 키페어를 업로드하지 않으셨다면, \"SSH키 다운로드\" 버튼을 클릭하여 SSH 키를 먼저 저장하십시오. 그 키를 이용해서 sftp, scp, rsync 등의 명령을 사용할 수 있습니다. 세션 - 업로드 세션페이지에서 파일 업로드를 위한 세션 리스트를 관리할 수 있습니다.", + "SFTPDescription": "SSH/SFTP 클라이언트를 통해 빠르고 안정적으로 파일을 업로드할 수 있습니다. 미리 SSH 키페어를 업로드하지 않으셨다면, \"SSH 키 다운로드\" 버튼을 클릭하여 SSH 키를 먼저 저장하십시오. 그 키를 이용해서 sftp, scp, rsync 등의 명령을 사용할 수 있습니다. 세션 - 업로드 세션페이지에서 파일 업로드를 위한 세션 리스트를 관리할 수 있습니다.", "ConnectionInformation": "연결 정보", "VNCconnection": "VNC 연결", "CheckAgainDialog": "이 선택은 취소할 수 없습니다. 정말 진행하시겠습니까?", @@ -979,7 +979,8 @@ "RescanFailed": "리스캔 작업이 실패하였습니다.", "NoRegistryToDisplay": "레지스트리 정보가 없습니다.", "RegistrySuccessfullyModified": "레지스트리가 성공적으로 수정되었습니다.", - "PleaseSelectOption": "옵션을 선택하세요" + "PleaseSelectOption": "옵션을 선택하세요", + "ConfirmNoUserName": "사용자 및 비밀번호가 지정되지 않았습니다. 저장하시겠습니까?" }, "resourceGroup": { "ResourceGroups": "자원 그룹", diff --git a/resources/i18n/mn.json b/resources/i18n/mn.json index 6ac7c35772..8bc5d8b1ca 100644 --- a/resources/i18n/mn.json +++ b/resources/i18n/mn.json @@ -233,10 +233,11 @@ "PreOpenPortRange": "Урьдчилан нээх портуудыг зөвхөн 1024-ээс 65535 хүртэл ашиглах боломжтой.", "MinMemory": "Одоогоор сонгогдсон ажиллах орчны орчны хамгийн бага санах ойн багтаамж нь {{size}}iB байна.", "ModelStorage": "Загвар хадгалах", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Таслал (,) эсвэл хоосон зайгаар тусгаарласан олон утгыг оруулна уу.", + "preopen": "урьдчилан нээх", + "Network": "Сүлжээ", + "Enable": "Идэвхжүүлэх", + "FolderAliasInvalid": "Зөвхөн үсэг, тоон тэмдэгт, доогуур зураас (_), ташуу зураас (/), зураас (-) ашиглахыг зөвшөөрнө." }, "Preparing": "Бэлтгэж байна ...", "PreparingSession": "Session бэлтгэж байна ...", @@ -303,8 +304,8 @@ "EnterValidSessionName": "Хүчинтэй сессийн нэрийг оруулна уу", "SessionNameAlreadyExist": "Сешн нэр аль хэдийн байна", "SluggedStrings": "Залгисан мөрүүд (>3 тэмдэгт)", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "64 ба түүнээс бага тэмдэгт оруулна уу.", + "PleaseFollowSessionNameRule": "Дор хаяж 3 үсэг, тоо, '.', '_', '-' гэсэн хослолыг оруулна уу." }, "AlreadyTerminatingSession": "Сессийг аль хэдийн дуусгаж байна.", "NoSessionToDisplay": "Харуулах сесс алга", @@ -670,7 +671,7 @@ "Used": "Ашигласан", "Pipeline": "Дамжуулах хоолойн хавтас", "DialogDataFolder": "FastTrack-д дамжуулах шугам үүсгэх үед автоматаар үүсгэгддэг дамжуулах хоолойд зориулагдсан фолдерууд.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Нэрээр нь хайх" }, "dialog": { "warning": { diff --git a/resources/i18n/ms.json b/resources/i18n/ms.json index d0c3ca11c8..539c12e7c4 100644 --- a/resources/i18n/ms.json +++ b/resources/i18n/ms.json @@ -233,10 +233,11 @@ "PortsTitleWithRange": "Nilai port (antara 1024 ~ 65535)", "PrePortConfigWillDisappear": "Mana-mana port prabuka yang belum disimpan akan hilang.", "StartModelServing": "Mulakan Penyajian Model", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Masukkan berbilang nilai yang dipisahkan dengan sama ada koma (,) atau ruang.", + "preopen": "prabuka", + "Network": "Rangkaian", + "Enable": "Dayakan", + "FolderAliasInvalid": "Hanya aksara abjad angka, garis bawah (_), garis miring (/), dan sempang (-) dibenarkan." }, "Preparing": "Menyiapkan ...", "PreparingSession": "Menyiapkan sesi ...", @@ -293,8 +294,8 @@ "SessionNameRequired": "Nama sesi diperlukan", "SluggedStrings": "Rentetan slugged (>3 aksara)", "SessionNameAlreadyExist": "Nama sesi sudah wujud", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Sila masukkan 64 aksara atau kurang.", + "PleaseFollowSessionNameRule": "Sila masukkan gabungan sekurang-kurangnya 3 huruf, nombor, '.', '_' dan '-'." }, "Interactive": "Interaktif", "Batch": "Kumpulan", @@ -670,7 +671,7 @@ "Pipeline": "Folder Saluran Paip", "DialogDataFolder": "Folder khusus saluran paip yang dibuat secara automatik apabila membuat saluran paip dalam FastTrack.", "DialogModelFolder": "Anda boleh berkhidmat dan mengurus model.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Cari mengikut nama" }, "dialog": { "warning": { diff --git a/resources/i18n/pl.json b/resources/i18n/pl.json index f506ab946d..8da2435420 100644 --- a/resources/i18n/pl.json +++ b/resources/i18n/pl.json @@ -233,10 +233,11 @@ "PortsTitleWithRange": "Wartość portu (między 1024 ~ 65535)", "PrePortConfigWillDisappear": "Wszystkie niezapisane wcześniej otwarte porty zostaną usunięte.", "StartModelServing": "Rozpoczęcie obsługi modelu", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Wprowadź wiele wartości oddzielonych przecinkiem (,) lub spacją.", + "preopen": "wstępnie otwarte", + "Network": "Sieć", + "Enable": "Włączać", + "FolderAliasInvalid": "Dozwolone są tylko znaki alfanumeryczne, podkreślenie (_), ukośnik (/) i myślnik (-)." }, "Preparing": "Przygotowuję...", "PreparingSession": "Przygotowuję sesję...", @@ -293,8 +294,8 @@ "SessionNameRequired": "Nazwa sesji jest wymagana", "SluggedStrings": "Ucięte ciągi znaków (>3 znaki)", "SessionNameAlreadyExist": "Nazwa sesji już istnieje", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Wprowadź maksymalnie 64 znaki.", + "PleaseFollowSessionNameRule": "Wprowadź kombinację co najmniej 3 liter, cyfr, „.”, „_” i „-”." }, "Interactive": "Interaktywny", "Batch": "Partia", @@ -670,7 +671,7 @@ "Pipeline": "Foldery rurociągów", "DialogDataFolder": "Dedykowane potokom foldery, które są automatycznie tworzone podczas tworzenia potoków w FastTrack.", "DialogModelFolder": "Możesz obsługiwać modele i zarządzać nimi.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Szukaj według nazwy" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Proszę wybrać jedną opcję.", "RegistrySuccessfullyModified": "Rejestr został pomyślnie zmodyfikowany.", "NoRegistryToDisplay": "Brak rejestrów do wyświetlenia", - "ModifyRegistry": "Modyfikacja rejestru" + "ModifyRegistry": "Modyfikacja rejestru", + "ConfirmNoUserName": "Użytkownik i hasło nie zostały określone, czy chcesz zapisać?" }, "resourceGroup": { "ResourceGroups": "Grupy zasobów", diff --git a/resources/i18n/pt-BR.json b/resources/i18n/pt-BR.json index 3462a848b2..b8fdef7901 100644 --- a/resources/i18n/pt-BR.json +++ b/resources/i18n/pt-BR.json @@ -233,10 +233,11 @@ "PortsTitleWithRange": "Valor da porta (entre 1024 ~ 65535)", "PrePortConfigWillDisappear": "Quaisquer portas pré-abertas não guardadas desaparecerão.", "StartModelServing": "Iniciar modelo de serviço", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Insira vários valores separados por vírgula (,) ou espaço.", + "preopen": "pré-abrir", + "Network": "Rede", + "Enable": "Habilitar", + "FolderAliasInvalid": "Somente caracteres alfanuméricos, sublinhado (_), barra (/) e traço (-) são permitidos." }, "Preparing": "Preparando...", "PreparingSession": "Preparando sessão ...", @@ -293,8 +294,8 @@ "SessionNameRequired": "O nome da sessão é obrigatório", "SluggedStrings": "Cadeias de caracteres com slugged (>3 caracteres)", "SessionNameAlreadyExist": "O nome da sessão já existe", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Insira 64 caracteres ou menos.", + "PleaseFollowSessionNameRule": "Insira uma combinação de pelo menos 3 letras, números, '.', '_' e '-'." }, "Interactive": "Interativo", "Batch": "Lote", @@ -670,7 +671,7 @@ "Pipeline": "Pastas de pipelines", "DialogDataFolder": "Pastas dedicadas ao pipeline que são criadas automaticamente ao criar pipelines no FastTrack.", "DialogModelFolder": "Pode servir e gerir modelos.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Procura por nome" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Por favor selecione uma opção.", "RegistrySuccessfullyModified": "Registo modificado com sucesso.", "NoRegistryToDisplay": "Não há registos a apresentar", - "ModifyRegistry": "Modificar o registo" + "ModifyRegistry": "Modificar o registo", + "ConfirmNoUserName": "Utilizador e palavra-passe não especificados, pretende guardar?" }, "resourceGroup": { "ResourceGroups": "Grupos de Recursos", diff --git a/resources/i18n/pt.json b/resources/i18n/pt.json index 2e2173d1eb..5e04811ff9 100644 --- a/resources/i18n/pt.json +++ b/resources/i18n/pt.json @@ -233,10 +233,11 @@ "PrePortConfigWillDisappear": "Quaisquer portas pré-abertas não guardadas desaparecerão.", "StartModelServing": "Iniciar modelo de serviço", "ModelStorage": "Modelo de armazenamento", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Insira vários valores separados por vírgula (,) ou espaço.", + "preopen": "pré-abrir", + "Network": "Rede", + "Enable": "Habilitar", + "FolderAliasInvalid": "Somente caracteres alfanuméricos, sublinhado (_), barra (/) e traço (-) são permitidos." }, "Preparing": "Preparando...", "PreparingSession": "Preparando sessão ...", @@ -293,8 +294,8 @@ "SessionNameRequired": "O nome da sessão é obrigatório", "SluggedStrings": "Cadeias de caracteres com slugged (>3 caracteres)", "SessionNameAlreadyExist": "O nome da sessão já existe", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Insira 64 caracteres ou menos.", + "PleaseFollowSessionNameRule": "Insira uma combinação de pelo menos 3 letras, números, '.', '_' e '-'." }, "Interactive": "Interativo", "Batch": "Lote", @@ -670,7 +671,7 @@ "Pipeline": "Pastas de pipelines", "DialogDataFolder": "Pastas dedicadas ao pipeline que são criadas automaticamente ao criar pipelines no FastTrack.", "DialogModelFolder": "Pode servir e gerir modelos.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Procura por nome" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Por favor selecione uma opção.", "RegistrySuccessfullyModified": "Registo modificado com sucesso.", "NoRegistryToDisplay": "Não há registos a apresentar", - "ModifyRegistry": "Modificar o registo" + "ModifyRegistry": "Modificar o registo", + "ConfirmNoUserName": "Utilizador e palavra-passe não especificados, pretende guardar?" }, "resourceGroup": { "ResourceGroups": "Grupos de Recursos", diff --git a/resources/i18n/ru.json b/resources/i18n/ru.json index 18c8b0325c..69befe8d90 100644 --- a/resources/i18n/ru.json +++ b/resources/i18n/ru.json @@ -233,10 +233,11 @@ "PreOpenPortRange": "Предварительно открытые порты доступны только в диапазоне от 1024 до 65535.", "MinMemory": "Минимальный объем памяти для выбранной в данный момент среды выполнения составляет {{size}}iB.", "ModelStorage": "Модельное хранилище", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Введите несколько значений, разделенных запятой (,) или пробелом.", + "preopen": "предварительное открытие", + "Network": "Сеть", + "Enable": "Давать возможность", + "FolderAliasInvalid": "Разрешены только буквенно-цифровые символы, подчеркивание (_), косая черта (/) и тире (-)." }, "Preparing": "Подготовка ...", "PreparingSession": "Подготовка сеанса ...", @@ -303,8 +304,8 @@ "EnterValidSessionName": "Введите действительное имя сессии", "SessionNameAlreadyExist": "Имя сессии уже существует", "SluggedStrings": "Строки с засечками (>3 символов)", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Введите не более 64 символов.", + "PleaseFollowSessionNameRule": "Введите комбинацию как минимум из 3 букв, цифр, «.», «_» и «-»." }, "AlreadyTerminatingSession": "Уже завершаю сессию.", "NoSessionToDisplay": "Нет сеансов для отображения", @@ -670,7 +671,7 @@ "Used": "Используется", "Pipeline": "Трубопроводные папки", "DialogDataFolder": "Специальные папки для трубопроводов, которые автоматически создаются при создании трубопроводов в FastTrack.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Поиск по имени" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Пожалуйста, выберите один вариант.", "RegistrySuccessfullyModified": "Реестр успешно модифицирован.", "ModifyRegistry": "Изменение реестра", - "NoRegistryToDisplay": "Отсутствие отображаемых реестров" + "NoRegistryToDisplay": "Отсутствие отображаемых реестров", + "ConfirmNoUserName": "Пользователь и пароль не указаны, хотите сохранить?" }, "resourceGroup": { "ResourceGroups": "Группы ресурсов", diff --git a/resources/i18n/tr.json b/resources/i18n/tr.json index 7d7069af79..e16908840d 100644 --- a/resources/i18n/tr.json +++ b/resources/i18n/tr.json @@ -233,10 +233,11 @@ "PortsTitleWithRange": "Port değeri (1024 ~ 65535 arasında)", "PrePortConfigWillDisappear": "Kaydedilmemiş tüm önceden açık portlar kaybolacaktır.", "StartModelServing": "Model Servisini Başlat", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Virgülle (,) veya boşlukla ayırarak birden fazla değer girin.", + "preopen": "önceden açılmak", + "Network": "Ağ", + "Enable": "Olanak vermek", + "FolderAliasInvalid": "Yalnızca alfasayısal karakterlere, alt çizgiye (_), eğik çizgiye (/) ve kısa çizgiye (-) izin verilir." }, "Preparing": "hazırlanıyor...", "PreparingSession": "Oturum hazırlanıyor...", @@ -293,8 +294,8 @@ "SessionNameRequired": "Oturum adı gereklidir", "SluggedStrings": "Kesikli dizeler (>3 karakter)", "SessionNameAlreadyExist": "Oturum adı zaten mevcut", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Lütfen 64 veya daha az karakter girin.", + "PleaseFollowSessionNameRule": "Lütfen en az 3 harf, rakam, '.', '_' ve '-' kombinasyonunu girin." }, "Interactive": "İnteraktif", "Batch": "Toplu İş", @@ -670,7 +671,7 @@ "Pipeline": "Boru Hattı Klasörleri", "DialogDataFolder": "FastTrack'te boru hatları oluşturulurken otomatik olarak oluşturulan boru hattına özel klasörler.", "DialogModelFolder": "Modellere hizmet verebilir ve yönetebilirsiniz.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Ada göre ara" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "Lütfen bir seçeneği seçin.", "RegistrySuccessfullyModified": "Kayıt defteri başarıyla değiştirildi.", "NoRegistryToDisplay": "Görüntülenecek Kayıt Yok", - "ModifyRegistry": "Kayıt Defterini Değiştir" + "ModifyRegistry": "Kayıt Defterini Değiştir", + "ConfirmNoUserName": "Kullanıcı ve şifre belirtilmemiş, kaydetmek ister misiniz?" }, "resourceGroup": { "ResourceGroups": "Kaynak Grupları", diff --git a/resources/i18n/vi.json b/resources/i18n/vi.json index 4ed97ac0da..194120a067 100644 --- a/resources/i18n/vi.json +++ b/resources/i18n/vi.json @@ -233,10 +233,11 @@ "PortsTitleWithRange": "Giá trị cổng (trong khoảng 1024 ~ 65535)", "PrePortConfigWillDisappear": "Mọi cổng mở trước chưa được lưu sẽ biến mất.", "StartModelServing": "Bắt đầu phân phối mẫu", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "Nhập nhiều giá trị được phân tách bằng dấu phẩy (,) hoặc dấu cách.", + "preopen": "mở trước", + "Network": "Mạng", + "Enable": "Cho phép", + "FolderAliasInvalid": "Chỉ cho phép các ký tự chữ và số, dấu gạch dưới (_), dấu gạch chéo (/) và dấu gạch ngang (-)." }, "Preparing": "Đang chuẩn bị ...", "PreparingSession": "Đang chuẩn bị phiên ...", @@ -293,8 +294,8 @@ "SessionNameRequired": "Tên phiên là bắt buộc", "SluggedStrings": "Chuỗi bị trượt (>3 ký tự)", "SessionNameAlreadyExist": "Tên phiên đã tồn tại", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "Vui lòng nhập 64 ký tự trở xuống.", + "PleaseFollowSessionNameRule": "Vui lòng nhập kết hợp ít nhất 3 chữ cái, số, '.', '_' và '-'." }, "Interactive": "Tương tác", "Batch": "Lô hàng", @@ -670,7 +671,7 @@ "Pipeline": "Thư mục đường ống", "DialogDataFolder": "Các thư mục dành riêng cho đường dẫn được tạo tự động khi tạo đường dẫn trong FastTrack.", "DialogModelFolder": "Bạn có thể phục vụ và quản lý các mô hình.", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "Tìm kiếm theo tên" }, "dialog": { "warning": { diff --git a/resources/i18n/zh-CN.json b/resources/i18n/zh-CN.json index 3f77ec05de..2e1ea71a2b 100644 --- a/resources/i18n/zh-CN.json +++ b/resources/i18n/zh-CN.json @@ -233,10 +233,11 @@ "PrePortConfigWillDisappear": "任何未保存的预打开端口都将消失。", "StartModelServing": "开始模型服务", "ModelStorage": "模型存储", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "您可以输入多个值,以逗号(,)或空格分隔。", + "preopen": "预开放端口", + "Network": "网络", + "Enable": "启用", + "FolderAliasInvalid": "只能输入大小写字母、数字、下划线 (_)、斜线 (/) 和破折号 (-)。" }, "Preparing": "正在准备...", "PreparingSession": "正在准备会议...", @@ -293,8 +294,8 @@ "SessionNameRequired": "会话名称为必填项", "SluggedStrings": "多字符串(>3 字符)", "SessionNameAlreadyExist": "会话名称已存在", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "请输入不超过 64 个字符。", + "PleaseFollowSessionNameRule": "请输入至少 3 个字母、数字、“.”、“_”和“-”的组合字符。" }, "Interactive": "交互式", "Batch": "批次", @@ -670,7 +671,7 @@ "Pipeline": "管道文件夹", "DialogDataFolder": "在 FastTrack 中创建管道时自动创建的管道专用文件夹。", "DialogModelFolder": "您可以服务和管理模型。", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "按名称搜索" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "请选择一个选项。", "RegistrySuccessfullyModified": "注册表已成功修改。", "NoRegistryToDisplay": "无注册表显示", - "ModifyRegistry": "修改注册表" + "ModifyRegistry": "修改注册表", + "ConfirmNoUserName": "未指定用户和密码,要保存吗?" }, "resourceGroup": { "ResourceGroups": "资源组", diff --git a/resources/i18n/zh-TW.json b/resources/i18n/zh-TW.json index 983316ba00..137d4ec633 100644 --- a/resources/i18n/zh-TW.json +++ b/resources/i18n/zh-TW.json @@ -233,10 +233,11 @@ "PrePortConfigWillDisappear": "任何未保存的预打开端口都将消失。", "StartModelServing": "开始模型服务", "ModelStorage": "模型存储", - "PreOpenPortRangeGuide": "__NOT_TRANSLATED__", - "preopen": "__NOT_TRANSLATED__", - "Network": "__NOT_TRANSLATED__", - "Enable": "__NOT_TRANSLATED__" + "PreOpenPortRangeGuide": "您可以輸入多個值,以逗號(,)或空格分隔。", + "preopen": "預開放埠", + "Network": "網路", + "Enable": "啟用", + "FolderAliasInvalid": "只能輸入大小寫字母、數字、底線 (_)、斜線 (/) 和破折號 (-)。" }, "Preparing": "正在準備...", "PreparingSession": "正在準備會議...", @@ -293,8 +294,8 @@ "SessionNameRequired": "会话名称为必填项", "SluggedStrings": "多字符串(>3 字符)", "SessionNameAlreadyExist": "会话名称已存在", - "SessionNameTooLong64": "__NOT_TRANSLATED__", - "PleaseFollowSessionNameRule": "__NOT_TRANSLATED__" + "SessionNameTooLong64": "請輸入不超過 64 個字元。", + "PleaseFollowSessionNameRule": "請輸入至少 3 個字母、數字、「.」、「_」和「-」的組合字元。" }, "Interactive": "交互式", "Batch": "批次", @@ -670,7 +671,7 @@ "Pipeline": "管道文件夹", "DialogDataFolder": "在 FastTrack 中创建管道时自动创建的管道专用文件夹。", "DialogModelFolder": "您可以服务和管理模型。", - "SearchByName": "__NOT_TRANSLATED__" + "SearchByName": "按名稱搜尋" }, "dialog": { "warning": { @@ -897,7 +898,8 @@ "PleaseSelectOption": "請選擇一個選項。", "RegistrySuccessfullyModified": "注册表已成功修改。", "NoRegistryToDisplay": "无注册表显示", - "ModifyRegistry": "修改注册表" + "ModifyRegistry": "修改注册表", + "ConfirmNoUserName": "未指定用户和密码,要保存吗?" }, "resourceGroup": { "ResourceGroups": "資源組", diff --git a/src/components/backend-ai-app-launcher.ts b/src/components/backend-ai-app-launcher.ts index a44bd0bd6d..e49f5c653a 100644 --- a/src/components/backend-ai-app-launcher.ts +++ b/src/components/backend-ai-app-launcher.ts @@ -56,6 +56,7 @@ export default class BackendAiAppLauncher extends BackendAIPage { @property({ type: String }) sshPort = ''; @property({ type: Number }) vncPort = 0; @property({ type: Number }) xrdpPort = 0; + @property({ type: String }) mountedVfolderName = ''; @property({ type: Number }) vscodeDesktopPort = 0; @property({ type: String }) tensorboardPath = ''; @property({ type: String }) endpointURL = ''; @@ -128,6 +129,10 @@ export default class BackendAiAppLauncher extends BackendAIPage { color: var(--general-button-background-color); } + mwc-icon-button.sftp-session-connection-copy { + --mdc-icon-size: 20px; + } + #ssh-dialog { --component-width: 375px; } @@ -265,13 +270,10 @@ export default class BackendAiAppLauncher extends BackendAIPage { .ssh-connection-example { display: flex; - } - - #current-ssh-connection-example { - color: #ffffff; - background-color: #242424; - padding: 15px; - margin: 0 5px 0 0; + background-color: rgba(230, 230, 230, 1); + padding: 10px; + border-radius: 5px; + margin-bottom: 5px; } @media screen and (max-width: 810px) { @@ -316,6 +318,7 @@ export default class BackendAiAppLauncher extends BackendAIPage { this._readSSHKey(e.detail.sessionUuid); this.sshPort = e.detail.port; this.sshHost = e.detail.host; + this.mountedVfolderName = e.detail.mounted; this._openSSHDialog(); } }, @@ -1748,27 +1751,44 @@ export default class BackendAiAppLauncher extends BackendAIPage {
Port: ${this.sshPort}

${_t('session.ConnectionExample')}

-
-
- sftp -i ./id_container -P ${this.sshPort} work@${ - this.sshHost - } -o StringHostKeyChecking=no -o UserKnownHostsFile=/dev/null
- scp -i ./id_container -P ${ - this.sshPort - } -rp /path/to/source work@${this.sshHost}:~/
- rsync -av -e "ssh -i ./id_container" /path/to/source/ work@${ - this.sshHost - }:~//
-
- - -
+
+ + sftp -i ./id_container -P ${this.sshPort} work@${ + this.sshHost + } -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
+
+ + +
+
+ + scp -i ./id_container -o StrictHostKeyChecking=no UserKnownHostsFile=/dev/null -P ${ + this.sshPort + } -rp /path/to/source work@${this.sshHost}:~/${ + this.mountedVfolderName + }
+
+ + +
+
+ + rsync -av -e "ssh -i ./id_container -o StrictHostKeyChecking=no UserKnownHostsFile=/dev/null" /path/to/source/ work@${ + this.sshHost + }:~/${this.mountedVfolderName}/
+
+ + +
diff --git a/src/components/backend-ai-credential-list.ts b/src/components/backend-ai-credential-list.ts index e78d4d74d7..7951fbd364 100644 --- a/src/components/backend-ai-credential-list.ts +++ b/src/components/backend-ai-credential-list.ts @@ -848,6 +848,11 @@ export default class BackendAICredentialList extends BackendAIPage { GB
+
+ folder + ${rowData.item.max_vfolder_count} + ${_t('general.Folders')} +
`, root, ); diff --git a/src/components/backend-ai-credential-view.ts b/src/components/backend-ai-credential-view.ts index 675111ae25..765a2130f6 100644 --- a/src/components/backend-ai-credential-view.ts +++ b/src/components/backend-ai-credential-view.ts @@ -64,6 +64,7 @@ export default class BackendAICredentialView extends BackendAIPage { @property({ type: Object }) idle_timeout = {}; @property({ type: Object }) session_lifetime = {}; @property({ type: Object }) container_per_session_limit = {}; + @property({ type: Object }) vfolder_max_limit = {}; @property({ type: Array }) rate_metric = [ 1000, 2000, 3000, 4000, 5000, 10000, 50000, ]; @@ -314,6 +315,7 @@ export default class BackendAICredentialView extends BackendAIPage { if (this.enableSessionLifetime) { this._updateInputStatus(this.session_lifetime); } + this.vfolder_max_limit['value'] = 10; this._defaultFileName = this._getDefaultCSVFileName(); await this._runAction(); } @@ -568,6 +570,7 @@ export default class BackendAICredentialView extends BackendAIPage { this._validateUserInput(this.concurrency_limit); this._validateUserInput(this.idle_timeout); this._validateUserInput(this.container_per_session_limit); + this._validateUserInput(this.vfolder_max_limit); total_resource_slots['cpu'] = this.cpu_resource['value']; total_resource_slots['mem'] = this.ram_resource['value'] + 'g'; @@ -594,12 +597,19 @@ export default class BackendAICredentialView extends BackendAIPage { delete total_resource_slots[resource]; } }); + this.vfolder_max_limit['value'] = + this.vfolder_max_limit['value'] === '' + ? 0 + : parseInt(this.vfolder_max_limit['value']); const input = { default_for_unspecified: 'UNLIMITED', total_resource_slots: JSON.stringify(total_resource_slots), max_concurrent_sessions: this.concurrency_limit['value'], max_containers_per_session: this.container_per_session_limit['value'], idle_timeout: this.idle_timeout['value'], + max_vfolder_count: this.vfolder_max_limit['value'], + // TODO: remove this after fix graphql schema + max_vfolder_size: -1, allowed_vfolder_hosts: vfolder_hosts, }; if (this.enableSessionLifetime) { @@ -1018,6 +1028,9 @@ export default class BackendAICredentialView extends BackendAIPage { this.container_per_session_limit = this.shadowRoot?.querySelector( '#container-per-session-limit', ) as TextField; + this.vfolder_max_limit = this.shadowRoot?.querySelector( + '#vfolder-count-limit', + ) as TextField; if (this.enableSessionLifetime) { this.session_lifetime = this.shadowRoot?.querySelector( '#session-lifetime', @@ -1462,6 +1475,15 @@ export default class BackendAICredentialView extends BackendAIPage { +
+
+ +
+
diff --git a/src/components/backend-ai-environment-view.ts b/src/components/backend-ai-environment-view.ts index d85a3c563b..2108756cc8 100644 --- a/src/components/backend-ai-environment-view.ts +++ b/src/components/backend-ai-environment-view.ts @@ -181,9 +181,18 @@ export default class BackendAIEnvironmentView extends BackendAIPage { ?active="${this._activeTab === 'resource-template-lists'}" >
- + ${globalThis.backendaiclient.supports('container-registry-gql') && + this._activeTab === 'registry-lists' + ? html` +
+ +
+ ` + : html` + + `}
diff --git a/src/components/backend-ai-resource-policy-list.ts b/src/components/backend-ai-resource-policy-list.ts index 49946a96ea..d87f40475b 100644 --- a/src/components/backend-ai-resource-policy-list.ts +++ b/src/components/backend-ai-resource-policy-list.ts @@ -63,6 +63,7 @@ export default class BackendAIResourcePolicyList extends BackendAIPage { @property({ type: Object }) _boundStorageNodesRenderer = this.storageNodesRenderer.bind(this); @query('#dropdown-area') dropdownArea!: HTMLDivElement; + @query('#vfolder-count-limit') vfolderCountLimitInput!: TextField; @query('#cpu-resource') cpuResource!: TextField; @query('#ram-resource') ramResource!: TextField; @query('#gpu-resource') gpuResource!: TextField; @@ -444,6 +445,17 @@ export default class BackendAIResourcePolicyList extends BackendAIPage { label="${_t('resourcePolicy.AllowedHosts')}" style="width:100%;" > +
+ +
+
+
+ folder + + ${this._displayResourcesByResourceUnit( + rowData.item.max_vfolder_count, + false, + 'max_vfolder_count', + )} + + Folders +
+
`, root, ); @@ -872,6 +898,8 @@ export default class BackendAIResourcePolicyList extends BackendAIPage { this._updateInputStatus(this.idleTimeout); this._updateInputStatus(this.containerPerSessionLimit); + this.vfolderCountLimitInput.value = resourcePolicy.max_vfolder_count; + this.allowed_vfolder_hosts = allowedStorageHosts; } @@ -1018,6 +1046,7 @@ export default class BackendAIResourcePolicyList extends BackendAIPage { this._validateUserInput(this.concurrencyLimit); this._validateUserInput(this.idleTimeout); this._validateUserInput(this.containerPerSessionLimit); + this._validateUserInput(this.vfolderCountLimitInput); total_resource_slots['cpu'] = this.cpuResource.value; total_resource_slots['mem'] = this.ramResource.value + 'g'; @@ -1034,6 +1063,10 @@ export default class BackendAIResourcePolicyList extends BackendAIPage { ? BigNumber.getValue().toString() : this.containerPerSessionLimit.value; + this.vfolderCountLimitInput.value = + this.vfolderCountLimitInput.value === '' + ? '0' + : this.vfolderCountLimitInput.value; Object.keys(total_resource_slots).map((resource) => { if (isNaN(parseFloat(total_resource_slots[resource]))) { delete total_resource_slots[resource]; @@ -1046,6 +1079,7 @@ export default class BackendAIResourcePolicyList extends BackendAIPage { max_concurrent_sessions: this.concurrencyLimit.value, max_containers_per_session: this.containerPerSessionLimit.value, idle_timeout: this.idleTimeout.value, + max_vfolder_count: this.vfolderCountLimitInput.value, allowed_vfolder_hosts: vfolder_hosts, }; diff --git a/src/components/backend-ai-session-list.ts b/src/components/backend-ai-session-list.ts index 357c503a2f..320d54f0f8 100644 --- a/src/components/backend-ai-session-list.ts +++ b/src/components/backend-ai-session-list.ts @@ -2413,13 +2413,21 @@ ${item.traceback} 0 + ? rowData.item.mounts.filter( + (vfolder: string) => !vfolder.startsWith('.'), + )[0] + : '', + )}" > diff --git a/src/components/backend-ai-storage-list.ts b/src/components/backend-ai-storage-list.ts index 6b54bcdce5..415556bc9f 100644 --- a/src/components/backend-ai-storage-list.ts +++ b/src/components/backend-ai-storage-list.ts @@ -3876,6 +3876,7 @@ export default class BackendAiStorageList extends BackendAIPage { sessionUuid: sessionResponse.sessionId, host: host, port: port, + mounted: this.explorer.id, }, }); document.dispatchEvent(event); diff --git a/src/lib/backend.ai-client-esm.ts b/src/lib/backend.ai-client-esm.ts index 690d1d96d7..89b0061260 100644 --- a/src/lib/backend.ai-client-esm.ts +++ b/src/lib/backend.ai-client-esm.ts @@ -660,6 +660,9 @@ class Client { if (this.isManagerVersionCompatibleWith('23.03.11')) { this._features['model-serving'] = true; } + if (this.isManagerVersionCompatibleWith('23.09.2')) { + this._features['container-registry-gql'] = true; + } } /** @@ -3164,6 +3167,7 @@ class ResourcePolicy { 'total_resource_slots', 'max_concurrent_sessions', 'max_containers_per_session', + 'max_vfolder_count', 'allowed_vfolder_hosts', 'idle_timeout', ], @@ -3209,6 +3213,7 @@ class ResourcePolicy { 'total_resource_slots', 'max_concurrent_sessions', 'max_containers_per_session', + 'max_vfolder_count', 'allowed_vfolder_hosts', 'idle_timeout', ]; @@ -3243,6 +3248,7 @@ class ResourcePolicy { * {int} 'max_concurrent_sessions': concurrency_limit, * {int} 'max_containers_per_session': containers_per_session_limit, * {bigint} 'idle_timeout': idle_timeout, + * {int} 'max_vfolder_count': vfolder_count_limit, * {[string]} 'allowed_vfolder_hosts': vfolder_hosts, * {int} 'max_session_lifetime': max_session_lifetime * };