feat: 支持批量发送文件

pull/12300/head
wangruidong 2023-12-05 19:09:17 +08:00 committed by Bryan
parent e3ac26e377
commit d0b0c87d3c
11 changed files with 186 additions and 91 deletions

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5d443763c06877304dca8dac76131271287acc6594df665bdf9445455c5187f1 oid sha256:a00c0d53df7fa88fc2fe69adda31fd9ab581b5a0362a01b8191924f74fab800d
size 167791 size 167820

View File

@ -8,8 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-12-08 14:51+0800\n" "POT-Creation-Date: 2023-12-11 14:54+0800\n"
"POT-Creation-Date: 2023-12-08 15:33+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -88,7 +87,7 @@ msgstr "集めました"
msgid "Template" msgid "Template"
msgstr "テンプレート" msgstr "テンプレート"
#: accounts/const/account.py:31 ops/const.py:45 #: accounts/const/account.py:31 ops/const.py:46
msgid "Skip" msgid "Skip"
msgstr "スキップ" msgstr "スキップ"
@ -100,7 +99,7 @@ msgstr "更新"
#: accounts/const/account.py:33 #: accounts/const/account.py:33
#: accounts/serializers/automations/change_secret.py:150 audits/const.py:62 #: accounts/serializers/automations/change_secret.py:150 audits/const.py:62
#: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19
#: ops/const.py:74 terminal/const.py:79 xpack/plugins/cloud/const.py:46 #: ops/const.py:75 terminal/const.py:79 xpack/plugins/cloud/const.py:46
msgid "Failed" msgid "Failed"
msgstr "失敗しました" msgstr "失敗しました"
@ -208,7 +207,6 @@ msgstr "作成のみ"
msgid "Email" msgid "Email"
msgstr "メール" msgstr "メール"
#: accounts/const/automation.py:104 terminal/const.py:87
#: accounts/const/automation.py:105 terminal/const.py:87 #: accounts/const/automation.py:105 terminal/const.py:87
msgid "SFTP" msgid "SFTP"
msgstr "SFTP" msgstr "SFTP"
@ -265,7 +263,7 @@ msgstr "資産"
#: accounts/models/account.py:53 accounts/models/template.py:16 #: accounts/models/account.py:53 accounts/models/template.py:16
#: accounts/serializers/account/account.py:220 #: accounts/serializers/account/account.py:220
#: accounts/serializers/account/account.py:268 #: accounts/serializers/account/account.py:268
#: accounts/serializers/account/template.py:25 #: accounts/serializers/account/template.py:27
#: authentication/serializers/connect_token_secret.py:50 #: authentication/serializers/connect_token_secret.py:50
msgid "Su from" msgid "Su from"
msgstr "から切り替え" msgstr "から切り替え"
@ -397,7 +395,7 @@ msgstr "理由"
#: accounts/models/automations/backup_account.py:135 #: accounts/models/automations/backup_account.py:135
#: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:105
#: accounts/serializers/automations/change_secret.py:128 #: accounts/serializers/automations/change_secret.py:128
#: ops/serializers/job.py:55 terminal/serializers/session.py:49 #: ops/serializers/job.py:65 terminal/serializers/session.py:49
msgid "Is success" msgid "Is success"
msgstr "成功は" msgstr "成功は"
@ -585,7 +583,7 @@ msgstr "ひみつ"
msgid "Secret strategy" msgid "Secret strategy"
msgstr "鍵ポリシー" msgstr "鍵ポリシー"
#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 #: accounts/models/base.py:44 accounts/serializers/account/template.py:24
#: accounts/serializers/automations/change_secret.py:44 #: accounts/serializers/automations/change_secret.py:44
msgid "Password rules" msgid "Password rules"
msgstr "パスワードルール" msgstr "パスワードルール"
@ -902,19 +900,19 @@ msgstr "数値#スウスウ#"
msgid "Special symbol" msgid "Special symbol"
msgstr "特殊記号" msgstr "特殊記号"
#: accounts/serializers/account/template.py:18 #: accounts/serializers/account/template.py:19
msgid "Exclude symbol" msgid "Exclude symbol"
msgstr "除外文字" msgstr "除外文字"
#: accounts/serializers/account/template.py:36 #: accounts/serializers/account/template.py:38
msgid "Secret generation strategy for account creation" msgid "Secret generation strategy for account creation"
msgstr "账号创建时,密文生成策略" msgstr "账号创建时,密文生成策略"
#: accounts/serializers/account/template.py:37 #: accounts/serializers/account/template.py:39
msgid "Whether to automatically push the account to the asset" msgid "Whether to automatically push the account to the asset"
msgstr "是否自动推送账号到资产" msgstr "是否自动推送账号到资产"
#: accounts/serializers/account/template.py:40 #: accounts/serializers/account/template.py:42
msgid "" msgid ""
"Associated platform, you can configure push parameters. If not associated, " "Associated platform, you can configure push parameters. If not associated, "
"default parameters will be used" "default parameters will be used"
@ -980,7 +978,7 @@ msgstr "自動タスク実行履歴"
#: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61
#: audits/models.py:64 audits/signal_handlers/activity_log.py:33 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33
#: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:40
#: terminal/const.py:78 terminal/models/session/sharing.py:121 #: terminal/const.py:78 terminal/models/session/sharing.py:121
#: tickets/views/approve.py:117 #: tickets/views/approve.py:117
msgid "Success" msgid "Success"
@ -1118,7 +1116,7 @@ msgid "Accounts"
msgstr "アカウント" msgstr "アカウント"
#: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60
#: ops/serializers/job.py:54 terminal/const.py:86 #: ops/serializers/job.py:64 terminal/const.py:86
#: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18
#: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_alert.html:12
#: terminal/templates/terminal/_msg_command_execute_alert.html:10 #: terminal/templates/terminal/_msg_command_execute_alert.html:10
@ -1668,7 +1666,7 @@ msgstr "プロトコル"
msgid "Sudo" msgid "Sudo"
msgstr "すど" msgstr "すど"
#: assets/models/_user.py:55 ops/const.py:49 ops/const.py:59 #: assets/models/_user.py:55 ops/const.py:50 ops/const.py:60
msgid "Shell" msgid "Shell"
msgstr "シェル" msgstr "シェル"
@ -3639,7 +3637,7 @@ msgstr "の準備を"
msgid "Pending" msgid "Pending"
msgstr "未定" msgstr "未定"
#: common/const/choices.py:17 ops/const.py:71 #: common/const/choices.py:17 ops/const.py:72
msgid "Running" msgid "Running"
msgstr "ランニング" msgstr "ランニング"
@ -4053,6 +4051,10 @@ msgstr "タスクは存在しません"
msgid "Task {} args or kwargs error" msgid "Task {} args or kwargs error"
msgstr "タスク実行パラメータエラー" msgstr "タスク実行パラメータエラー"
#: ops/api/job.py:128
msgid "Duplicate file exists"
msgstr "重複したファイルが存在する"
#: ops/api/playbook.py:39 #: ops/api/playbook.py:39
msgid "Currently playbook is being used in a job" msgid "Currently playbook is being used in a job"
msgstr "現在プレイブックは1つのジョブで使用されています" msgstr "現在プレイブックは1つのジョブで使用されています"
@ -4125,47 +4127,53 @@ msgstr "コマンド#コマンド#"
msgid "Playbook" msgid "Playbook"
msgstr "Playbook" msgstr "Playbook"
#: ops/const.py:43 #: ops/const.py:40
#, fuzzy
#| msgid "Upload"
msgid "Upload File"
msgstr "アップロード"
#: ops/const.py:44
msgid "Privileged Only" msgid "Privileged Only"
msgstr "特権アカウントのみ" msgstr "特権アカウントのみ"
#: ops/const.py:44 #: ops/const.py:45
msgid "Privileged First" msgid "Privileged First"
msgstr "特権アカウント優先" msgstr "特権アカウント優先"
#: ops/const.py:50 ops/const.py:60 #: ops/const.py:51 ops/const.py:61
msgid "Powershell" msgid "Powershell"
msgstr "PowerShell" msgstr "PowerShell"
#: ops/const.py:51 ops/const.py:61 #: ops/const.py:52 ops/const.py:62
msgid "Python" msgid "Python"
msgstr "Python" msgstr "Python"
#: ops/const.py:52 ops/const.py:62 #: ops/const.py:53 ops/const.py:63
msgid "MySQL" msgid "MySQL"
msgstr "MySQL" msgstr "MySQL"
#: ops/const.py:53 ops/const.py:64 #: ops/const.py:54 ops/const.py:65
msgid "PostgreSQL" msgid "PostgreSQL"
msgstr "PostgreSQL" msgstr "PostgreSQL"
#: ops/const.py:54 ops/const.py:65 #: ops/const.py:55 ops/const.py:66
msgid "SQLServer" msgid "SQLServer"
msgstr "SQLServer" msgstr "SQLServer"
#: ops/const.py:55 ops/const.py:67 #: ops/const.py:56 ops/const.py:68
msgid "Raw" msgid "Raw"
msgstr "" msgstr ""
#: ops/const.py:63 #: ops/const.py:64
msgid "MariaDB" msgid "MariaDB"
msgstr "MariaDB" msgstr "MariaDB"
#: ops/const.py:66 #: ops/const.py:67
msgid "Oracle" msgid "Oracle"
msgstr "Oracle" msgstr "Oracle"
#: ops/const.py:73 #: ops/const.py:74
msgid "Timeout" msgid "Timeout"
msgstr "タイムアウト" msgstr "タイムアウト"
@ -4299,7 +4307,7 @@ msgstr "Material"
msgid "Material Type" msgid "Material Type"
msgstr "Material を選択してオプションを設定します。" msgstr "Material を選択してオプションを設定します。"
#: ops/models/job.py:557 #: ops/models/job.py:565
msgid "Job Execution" msgid "Job Execution"
msgstr "ジョブ実行" msgstr "ジョブ実行"
@ -4343,15 +4351,15 @@ msgstr "{max_threshold} を超えるCPUロード: => {value}"
msgid "Run after save" msgid "Run after save"
msgstr "保存後に実行" msgstr "保存後に実行"
#: ops/serializers/job.py:53 #: ops/serializers/job.py:63
msgid "Job type" msgid "Job type"
msgstr "タスクの種類" msgstr "タスクの種類"
#: ops/serializers/job.py:56 terminal/serializers/session.py:53 #: ops/serializers/job.py:66 terminal/serializers/session.py:53
msgid "Is finished" msgid "Is finished"
msgstr "終了しました" msgstr "終了しました"
#: ops/serializers/job.py:57 #: ops/serializers/job.py:67
msgid "Time cost" msgid "Time cost"
msgstr "時を過ごす" msgstr "時を過ごす"
@ -4567,7 +4575,8 @@ msgstr "認定アカウント"
msgid "today" msgid "today"
msgstr "今日" msgstr "今日"
#: perms/notifications.py:12 settings/serializers/feature.py:106 #: perms/notifications.py:12 perms/notifications.py:44
#: settings/serializers/feature.py:106
msgid "day" msgid "day"
msgstr "日" msgstr "日"
@ -4595,8 +4604,9 @@ msgstr "アセット認証ルールの有効期限が切れていることを確
msgid "Send asset permission expired notification" msgid "Send asset permission expired notification"
msgstr "アセット許可の有効期限通知を送信する" msgstr "アセット許可の有効期限通知を送信する"
#: perms/templates/perms/_msg_item_permissions_expire.html:7
#: perms/templates/perms/_msg_permed_items_expire.html:7 #: perms/templates/perms/_msg_permed_items_expire.html:7
#, python-format
msgid "" msgid ""
"\n" "\n"
" The following %(item_type)s will expire in %(count)s\n" " The following %(item_type)s will expire in %(count)s\n"

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:8976c6b41e2c0ce591b2b257fd0352b4ed7517661f7df83ba74b302b8cf94b00 oid sha256:d4a9a61bf1b247d3843001737ebfa6d5f8580f2d9c1ae0fc76649ecc535f5d96
size 137479 size 137563

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-12-08 15:33+0800\n" "POT-Creation-Date: 2023-12-11 14:54+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -86,7 +86,7 @@ msgstr "收集"
msgid "Template" msgid "Template"
msgstr "模板" msgstr "模板"
#: accounts/const/account.py:31 ops/const.py:45 #: accounts/const/account.py:31 ops/const.py:46
msgid "Skip" msgid "Skip"
msgstr "跳过" msgstr "跳过"
@ -98,7 +98,7 @@ msgstr "更新"
#: accounts/const/account.py:33 #: accounts/const/account.py:33
#: accounts/serializers/automations/change_secret.py:150 audits/const.py:62 #: accounts/serializers/automations/change_secret.py:150 audits/const.py:62
#: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19
#: ops/const.py:74 terminal/const.py:79 xpack/plugins/cloud/const.py:46 #: ops/const.py:75 terminal/const.py:79 xpack/plugins/cloud/const.py:46
msgid "Failed" msgid "Failed"
msgstr "失败" msgstr "失败"
@ -262,7 +262,7 @@ msgstr "资产"
#: accounts/models/account.py:53 accounts/models/template.py:16 #: accounts/models/account.py:53 accounts/models/template.py:16
#: accounts/serializers/account/account.py:220 #: accounts/serializers/account/account.py:220
#: accounts/serializers/account/account.py:268 #: accounts/serializers/account/account.py:268
#: accounts/serializers/account/template.py:25 #: accounts/serializers/account/template.py:27
#: authentication/serializers/connect_token_secret.py:50 #: authentication/serializers/connect_token_secret.py:50
msgid "Su from" msgid "Su from"
msgstr "切换自" msgstr "切换自"
@ -394,7 +394,7 @@ msgstr "原因"
#: accounts/models/automations/backup_account.py:135 #: accounts/models/automations/backup_account.py:135
#: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:105
#: accounts/serializers/automations/change_secret.py:128 #: accounts/serializers/automations/change_secret.py:128
#: ops/serializers/job.py:55 terminal/serializers/session.py:49 #: ops/serializers/job.py:65 terminal/serializers/session.py:49
msgid "Is success" msgid "Is success"
msgstr "是否成功" msgstr "是否成功"
@ -582,7 +582,7 @@ msgstr "密钥"
msgid "Secret strategy" msgid "Secret strategy"
msgstr "密文策略" msgstr "密文策略"
#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 #: accounts/models/base.py:44 accounts/serializers/account/template.py:24
#: accounts/serializers/automations/change_secret.py:44 #: accounts/serializers/automations/change_secret.py:44
msgid "Password rules" msgid "Password rules"
msgstr "密码规则" msgstr "密码规则"
@ -898,19 +898,19 @@ msgstr "数字"
msgid "Special symbol" msgid "Special symbol"
msgstr "特殊字符" msgstr "特殊字符"
#: accounts/serializers/account/template.py:18 #: accounts/serializers/account/template.py:19
msgid "Exclude symbol" msgid "Exclude symbol"
msgstr "排除字符" msgstr "排除字符"
#: accounts/serializers/account/template.py:36 #: accounts/serializers/account/template.py:38
msgid "Secret generation strategy for account creation" msgid "Secret generation strategy for account creation"
msgstr "密码生成策略,用于账号创建时,设置密码" msgstr "密码生成策略,用于账号创建时,设置密码"
#: accounts/serializers/account/template.py:37 #: accounts/serializers/account/template.py:39
msgid "Whether to automatically push the account to the asset" msgid "Whether to automatically push the account to the asset"
msgstr "是否自动推送账号到资产" msgstr "是否自动推送账号到资产"
#: accounts/serializers/account/template.py:40 #: accounts/serializers/account/template.py:42
msgid "" msgid ""
"Associated platform, you can configure push parameters. If not associated, " "Associated platform, you can configure push parameters. If not associated, "
"default parameters will be used" "default parameters will be used"
@ -975,7 +975,7 @@ msgstr "自动化任务执行历史"
#: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61
#: audits/models.py:64 audits/signal_handlers/activity_log.py:33 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33
#: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:40
#: terminal/const.py:78 terminal/models/session/sharing.py:121 #: terminal/const.py:78 terminal/models/session/sharing.py:121
#: tickets/views/approve.py:117 #: tickets/views/approve.py:117
msgid "Success" msgid "Success"
@ -1113,7 +1113,7 @@ msgid "Accounts"
msgstr "账号管理" msgstr "账号管理"
#: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60
#: ops/serializers/job.py:54 terminal/const.py:86 #: ops/serializers/job.py:64 terminal/const.py:86
#: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18
#: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_alert.html:12
#: terminal/templates/terminal/_msg_command_execute_alert.html:10 #: terminal/templates/terminal/_msg_command_execute_alert.html:10
@ -1659,7 +1659,7 @@ msgstr "协议"
msgid "Sudo" msgid "Sudo"
msgstr "Sudo" msgstr "Sudo"
#: assets/models/_user.py:55 ops/const.py:49 ops/const.py:59 #: assets/models/_user.py:55 ops/const.py:50 ops/const.py:60
msgid "Shell" msgid "Shell"
msgstr "Shell" msgstr "Shell"
@ -3594,7 +3594,7 @@ msgstr "准备"
msgid "Pending" msgid "Pending"
msgstr "待定的" msgstr "待定的"
#: common/const/choices.py:17 ops/const.py:71 #: common/const/choices.py:17 ops/const.py:72
msgid "Running" msgid "Running"
msgstr "运行中" msgstr "运行中"
@ -4003,6 +4003,10 @@ msgstr "任务 {} 不存在"
msgid "Task {} args or kwargs error" msgid "Task {} args or kwargs error"
msgstr "任务 {} 执行参数错误" msgstr "任务 {} 执行参数错误"
#: ops/api/job.py:128
msgid "Duplicate file exists"
msgstr "存在同名文件"
#: ops/api/playbook.py:39 #: ops/api/playbook.py:39
msgid "Currently playbook is being used in a job" msgid "Currently playbook is being used in a job"
msgstr "当前 playbook 正在作业中使用" msgstr "当前 playbook 正在作业中使用"
@ -4075,47 +4079,51 @@ msgstr "命令"
msgid "Playbook" msgid "Playbook"
msgstr "Playbook" msgstr "Playbook"
#: ops/const.py:43 #: ops/const.py:40
msgid "Upload File"
msgstr "上传"
#: ops/const.py:44
msgid "Privileged Only" msgid "Privileged Only"
msgstr "仅限特权账号" msgstr "仅限特权账号"
#: ops/const.py:44 #: ops/const.py:45
msgid "Privileged First" msgid "Privileged First"
msgstr "特权账号优先" msgstr "特权账号优先"
#: ops/const.py:50 ops/const.py:60 #: ops/const.py:51 ops/const.py:61
msgid "Powershell" msgid "Powershell"
msgstr "PowerShell" msgstr "PowerShell"
#: ops/const.py:51 ops/const.py:61 #: ops/const.py:52 ops/const.py:62
msgid "Python" msgid "Python"
msgstr "Python" msgstr "Python"
#: ops/const.py:52 ops/const.py:62 #: ops/const.py:53 ops/const.py:63
msgid "MySQL" msgid "MySQL"
msgstr "MySQL" msgstr "MySQL"
#: ops/const.py:53 ops/const.py:64 #: ops/const.py:54 ops/const.py:65
msgid "PostgreSQL" msgid "PostgreSQL"
msgstr "PostgreSQL" msgstr "PostgreSQL"
#: ops/const.py:54 ops/const.py:65 #: ops/const.py:55 ops/const.py:66
msgid "SQLServer" msgid "SQLServer"
msgstr "SQLServer" msgstr "SQLServer"
#: ops/const.py:55 ops/const.py:67 #: ops/const.py:56 ops/const.py:68
msgid "Raw" msgid "Raw"
msgstr "Raw" msgstr "Raw"
#: ops/const.py:63 #: ops/const.py:64
msgid "MariaDB" msgid "MariaDB"
msgstr "MariaDB" msgstr "MariaDB"
#: ops/const.py:66 #: ops/const.py:67
msgid "Oracle" msgid "Oracle"
msgstr "Oracle" msgstr "Oracle"
#: ops/const.py:73 #: ops/const.py:74
msgid "Timeout" msgid "Timeout"
msgstr "超时" msgstr "超时"
@ -4249,7 +4257,7 @@ msgstr "Material"
msgid "Material Type" msgid "Material Type"
msgstr "Material 类型" msgstr "Material 类型"
#: ops/models/job.py:557 #: ops/models/job.py:565
msgid "Job Execution" msgid "Job Execution"
msgstr "作业执行" msgstr "作业执行"
@ -4293,15 +4301,15 @@ msgstr "CPU 使用率超过 {max_threshold}: => {value}"
msgid "Run after save" msgid "Run after save"
msgstr "保存后执行" msgstr "保存后执行"
#: ops/serializers/job.py:53 #: ops/serializers/job.py:63
msgid "Job type" msgid "Job type"
msgstr "任务类型" msgstr "任务类型"
#: ops/serializers/job.py:56 terminal/serializers/session.py:53 #: ops/serializers/job.py:66 terminal/serializers/session.py:53
msgid "Is finished" msgid "Is finished"
msgstr "是否完成" msgstr "是否完成"
#: ops/serializers/job.py:57 #: ops/serializers/job.py:67
msgid "Time cost" msgid "Time cost"
msgstr "花费时间" msgstr "花费时间"
@ -4516,7 +4524,8 @@ msgstr "授权账号"
msgid "today" msgid "today"
msgstr "今天" msgstr "今天"
#: perms/notifications.py:12 settings/serializers/feature.py:106 #: perms/notifications.py:12 perms/notifications.py:44
#: settings/serializers/feature.py:106
msgid "day" msgid "day"
msgstr "天" msgstr "天"
@ -4545,20 +4554,6 @@ msgid "Send asset permission expired notification"
msgstr "发送资产权限过期通知" msgstr "发送资产权限过期通知"
#: perms/templates/perms/_msg_item_permissions_expire.html:7 #: perms/templates/perms/_msg_item_permissions_expire.html:7
#, fuzzy, python-format
#| msgid ""
#| "\n"
#| " The following %(item_type)s will expire in %(count)s\n"
#| " "
msgid ""
"\n"
" The following %(item_type)s will expire in %(count)s days\n"
" "
msgstr ""
"\n"
" 以下 %(item_type)s 即将在 %(count)s 后过期\n"
" "
#: perms/templates/perms/_msg_permed_items_expire.html:7 #: perms/templates/perms/_msg_permed_items_expire.html:7
#, python-format #, python-format
msgid "" msgid ""
@ -8801,6 +8796,7 @@ msgstr "企业专业版"
msgid "Ultimate edition" msgid "Ultimate edition"
msgstr "企业旗舰版" msgstr "企业旗舰版"
#~ msgid "FeiShu query user failed" #~ msgid "FeiShu query user failed"
#~ msgstr "飞书查询用户失败" #~ msgstr "飞书查询用户失败"

View File

@ -1,8 +1,9 @@
import os import os
import uuid import uuid
import shutil
import ansible_runner import ansible_runner
from django.conf import settings from django.conf import settings
from django.utils._os import safe_join
from .callback import DefaultCallback from .callback import DefaultCallback
from ..utils import get_ansible_log_verbosity from ..utils import get_ansible_log_verbosity
@ -85,6 +86,34 @@ class PlaybookRunner:
return self.cb return self.cb
class UploadFileRunner:
def __init__(self, inventory, job_id, dest_path, callback=None):
self.id = uuid.uuid4()
self.inventory = inventory
self.cb = DefaultCallback()
upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file')
self.src_paths = safe_join(upload_file_dir, str(job_id))
self.dest_path = dest_path
def run(self, verbosity=0, **kwargs):
verbosity = get_ansible_log_verbosity(verbosity)
ansible_runner.run(
host_pattern="*",
inventory=self.inventory,
module='copy',
module_args=f"src={self.src_paths}/ dest={self.dest_path}",
verbosity=verbosity,
event_handler=self.cb.event_handler,
status_handler=self.cb.status_handler,
**kwargs
)
try:
shutil.rmtree(self.src_paths)
except OSError as e:
print(f"del upload tmp dir {self.src_paths} failed! {e}")
return self.cb
class CommandRunner(AdHocRunner): class CommandRunner(AdHocRunner):
def __init__(self, inventory, command, pattern='*', project_dir='/tmp/'): def __init__(self, inventory, command, pattern='*', project_dir='/tmp/'):
super().__init__(inventory, 'shell', command, pattern, project_dir) super().__init__(inventory, 'shell', command, pattern, project_dir)

View File

@ -1,16 +1,22 @@
import json
import os
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction
from django.db.models import Count from django.db.models import Count
from django.db.transaction import atomic
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils._os import safe_join
from django.utils.translation import gettext_lazy as _
from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from assets.models import Asset from assets.models import Asset
from common.const.http import POST
from common.permissions import IsValidUser from common.permissions import IsValidUser
from ops.const import Types from ops.const import Types
from ops.models import Job, JobExecution from ops.models import Job, JobExecution
from ops.serializers.job import JobSerializer, JobExecutionSerializer from ops.serializers.job import JobSerializer, JobExecutionSerializer, FileSerializer
__all__ = [ __all__ = [
'JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', 'JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView',
@ -24,6 +30,7 @@ from orgs.utils import tmp_to_org, get_current_org
from accounts.models import Account from accounts.models import Account
from perms.models import PermNode from perms.models import PermNode
from perms.utils import UserPermAssetUtil from perms.utils import UserPermAssetUtil
from jumpserver.settings import get_file_md5
def set_task_to_serializer_data(serializer, task_id): def set_task_to_serializer_data(serializer, task_id):
@ -91,6 +98,40 @@ class JobViewSet(OrgBulkModelViewSet):
transaction.on_commit( transaction.on_commit(
lambda: run_ops_job_execution.apply_async((str(execution.id),), task_id=str(execution.id))) lambda: run_ops_job_execution.apply_async((str(execution.id),), task_id=str(execution.id)))
@action(methods=[POST], detail=False, serializer_class=FileSerializer, permission_classes=[IsValidUser, ],
url_path='upload')
def upload(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
msg = 'Upload data invalid: {}'.format(serializer.errors)
return Response({'msg': msg}, status=400)
uploaded_files = request.FILES.getlist('files')
job_id = request.data.get('job_id', '')
job = get_object_or_404(Job, pk=job_id)
job_args = json.loads(job.args)
src_path_info = []
filename_set = set()
same_filenames = []
upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file')
for uploaded_file in uploaded_files:
filename = uploaded_file.name
saved_path = safe_join(upload_file_dir, f'{job_id}/{filename}')
os.makedirs(os.path.dirname(saved_path), exist_ok=True)
with open(saved_path, 'wb+') as destination:
for chunk in uploaded_file.chunks():
destination.write(chunk)
if filename in filename_set:
same_filenames.append(filename)
filename_set.add(filename)
src_path_info.append({'filename': filename, 'md5': get_file_md5(saved_path)})
if same_filenames:
return Response({'msg': _("Duplicate file exists")}, status=400)
job_args['src_path_info'] = src_path_info
job.args = json.dumps(job_args)
job.save()
self.run_job(job, serializer)
return Response({'task_id': serializer.data.get('task_id')}, status=201)
class JobExecutionViewSet(OrgBulkModelViewSet): class JobExecutionViewSet(OrgBulkModelViewSet):
serializer_class = JobExecutionSerializer serializer_class = JobExecutionSerializer

View File

@ -37,6 +37,7 @@ class CreateMethods(models.TextChoices):
class Types(models.TextChoices): class Types(models.TextChoices):
adhoc = 'adhoc', _('Adhoc') adhoc = 'adhoc', _('Adhoc')
playbook = 'playbook', _('Playbook') playbook = 'playbook', _('Playbook')
upload_file = 'upload_file', _('Upload File')
class RunasPolicies(models.TextChoices): class RunasPolicies(models.TextChoices):

View File

@ -23,7 +23,7 @@ from assets.models import Asset
from assets.automations.base.manager import SSHTunnelManager from assets.automations.base.manager import SSHTunnelManager
from common.db.encoder import ModelJSONFieldEncoder from common.db.encoder import ModelJSONFieldEncoder
from labels.mixins import LabeledMixin from labels.mixins import LabeledMixin
from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException, UploadFileRunner
from ops.mixin import PeriodTaskModelMixin from ops.mixin import PeriodTaskModelMixin
from ops.variables import * from ops.variables import *
from ops.const import Types, RunasPolicies, JobStatus, JobModules from ops.const import Types, RunasPolicies, JobStatus, JobModules
@ -362,7 +362,7 @@ class JobExecution(JMSOrgBaseModel):
static_variables = self.gather_static_variables() static_variables = self.gather_static_variables()
extra_vars.update(static_variables) extra_vars.update(static_variables)
if self.current_job.type == 'adhoc': if self.current_job.type == Types.adhoc:
module, args = self.compile_shell() module, args = self.compile_shell()
runner = AdHocRunner( runner = AdHocRunner(
@ -374,10 +374,18 @@ class JobExecution(JMSOrgBaseModel):
project_dir=self.private_dir, project_dir=self.private_dir,
extra_vars=extra_vars, extra_vars=extra_vars,
) )
elif self.current_job.type == 'playbook': elif self.current_job.type == Types.playbook:
runner = PlaybookRunner( runner = PlaybookRunner(
self.inventory_path, self.current_job.playbook.entry self.inventory_path, self.current_job.playbook.entry
) )
elif self.current_job.type == Types.upload_file:
job_id = self.current_job.id
args = json.loads(self.current_job.args)
dst_path = args.get('dst_path')
if dst_path:
runner = UploadFileRunner(self.inventory_path, job_id, dst_path)
else:
raise ValueError("dst_path is null")
else: else:
raise Exception("unsupported job type") raise Exception("unsupported job type")
return runner return runner

View File

@ -21,9 +21,12 @@ class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodT
def to_internal_value(self, data): def to_internal_value(self, data):
instant = data.get('instant', False) instant = data.get('instant', False)
job_type = data.get('type', '')
_uid = str(uuid.uuid4()).split('-')[-1]
if instant: if instant:
_uid = str(uuid.uuid4()).split('-')[-1]
data['name'] = f'job-{_uid}' data['name'] = f'job-{_uid}'
if job_type == 'upload_file':
data['name'] = f'upload_file-{_uid}'
return super().to_internal_value(data) return super().to_internal_value(data)
def get_request_user(self): def get_request_user(self):
@ -44,10 +47,17 @@ class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodT
"use_parameter_define", "parameters_define", "use_parameter_define", "parameters_define",
"timeout", "chdir", "comment", "summary", "timeout", "chdir", "comment", "summary",
"is_periodic", "interval", "crontab", "nodes", "is_periodic", "interval", "crontab", "nodes",
"run_after_save", "run_after_save"
] ]
class FileSerializer(serializers.Serializer):
files = serializers.FileField(allow_empty_file=True)
class Meta:
ref_name = "JobFileSerializer"
class JobExecutionSerializer(BulkOrgResourceModelSerializer): class JobExecutionSerializer(BulkOrgResourceModelSerializer):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
job_type = serializers.ReadOnlyField(label=_("Job type")) job_type = serializers.ReadOnlyField(label=_("Job type"))

View File

@ -41,7 +41,7 @@ class AssetPermsWillExpireForOrgAdminMsg(UserMessage):
super().__init__(user) super().__init__(user)
self.perms = perms self.perms = perms
self.org = org self.org = org
self.day_count = _('today') if day_count == 0 else day_count self.day_count = _('today') if day_count == 0 else day_count + _('day')
def get_items_with_url(self): def get_items_with_url(self):
items_with_url = [] items_with_url = []

View File

@ -5,7 +5,7 @@
<p> <p>
{% blocktranslate %} {% blocktranslate %}
The following {{ item_type }} will expire in {{ count }} days The following {{ item_type }} will expire in {{ count }}
{% endblocktranslate %} {% endblocktranslate %}
</p> </p>