From ff9873d0f7ce57f82e1ff996771a78c7f26cf54a Mon Sep 17 00:00:00 2001 From: jingrow Date: Mon, 28 Jul 2025 18:39:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=87=8D=E7=BD=AE=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E6=8C=89=E9=92=AE=E5=8F=8AAPI=E7=AB=AF=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcloud/api/aliyun_server_light.py | 58 +++++++++++++++++ .../pagetype/jsite_server/jsite_server.js | 62 +++++++++++++++++++ .../pagetype/jsite_server/jsite_server.json | 9 ++- .../pagetype/jsite_server/jsite_server.py | 1 + 4 files changed, 129 insertions(+), 1 deletion(-) diff --git a/jcloud/api/aliyun_server_light.py b/jcloud/api/aliyun_server_light.py index 5902c4c..07a6b0b 100644 --- a/jcloud/api/aliyun_server_light.py +++ b/jcloud/api/aliyun_server_light.py @@ -217,6 +217,22 @@ class AliyunLightServerManager: jingrow.log_error("重置系统失败", f"重置实例 {instance_id} 系统时发生错误: {str(e)}") return {'success': False, 'error': str(e), 'message': '系统重置失败'} + def update_instance_password(self, instance_id, password, region_id='cn-shanghai'): + """更新实例密码""" + client = self._get_client(region_id) + try: + request = swas__open20200601_models.UpdateInstanceAttributeRequest( + region_id=region_id, + instance_id=instance_id, + password=password + ) + runtime = util_models.RuntimeOptions() + response = client.update_instance_attribute_with_options(request, runtime) + return {'success': True, 'data': self._convert_response_to_dict(response.body), 'message': '密码更新成功'} + except Exception as e: + jingrow.log_error("更新实例密码失败", f"更新实例 {instance_id} 密码时发生错误: {str(e)}") + return {'success': False, 'error': str(e), 'message': '密码更新失败'} + def list_instances(self, page_number=1, page_size=20, region_id='cn-shanghai'): @@ -508,6 +524,48 @@ def reset_aliyun_instance_system(instance_id, image_id, password=None, region_id manager = _get_manager() return manager.reset_system(instance_id, image_id, password, region_id) +@jingrow.whitelist() +def update_aliyun_instance_password(instance_id, password, region_id='cn-shanghai'): + """更新实例密码""" + manager = _get_manager() + return manager.update_instance_password(instance_id, password, region_id) + +@jingrow.whitelist() +def reset_aliyun_instance_password(instance_id, password, region_id='cn-shanghai'): + """重置实例密码并更新Jsite Server记录""" + try: + # 1. 查找对应的Jsite Server记录 + server = jingrow.get_pg("Jsite Server", {"instance_id": instance_id}) + if not server: + return {"success": False, "message": "找不到对应的服务器记录"} + + # 2. 调用阿里云API更新密码 + manager = _get_manager() + result = manager.update_instance_password(instance_id, password, region_id) + + if not result or not result.get('success'): + error_msg = f"阿里云密码更新失败: {result.get('message', '未知错误')}" + jingrow.log_error("重置密码失败", error_msg) + return {"success": False, "message": error_msg} + + # 3. 更新Jsite Server记录中的password字段 + server.password = password + server.save(ignore_permissions=True) + jingrow.db.commit() + + # 4. 自动重启实例 + reboot_aliyun_instance(instance_id, region_id) + + return { + "success": True, + "message": "密码重置成功", + "server_name": server.name, + "instance_id": instance_id + } + + except Exception as e: + jingrow.log_error("重置密码失败", f"重置实例 {instance_id} 密码时发生错误: {str(e)}") + return {"success": False, "error": str(e), "message": "密码重置失败"} @jingrow.whitelist() diff --git a/jcloud/jcloud/pagetype/jsite_server/jsite_server.js b/jcloud/jcloud/pagetype/jsite_server/jsite_server.js index 4ec50d9..1113c7c 100644 --- a/jcloud/jcloud/pagetype/jsite_server/jsite_server.js +++ b/jcloud/jcloud/pagetype/jsite_server/jsite_server.js @@ -13,6 +13,31 @@ jingrow.ui.form.on("Jsite Server", { } ); }); + + frm.add_custom_button(__('重置密码'), function() { + // 弹出密码输入对话框 + jingrow.prompt( + { + fieldtype: 'Password', + label: __('服务器密码'), + fieldname: 'new_password', + reqd: 1, + description: __('长度为 8 至 30 个字符,必须同时包含大小写英文字母、数字和特殊符号。') + }, + function(values) { + if (values.new_password) { + reset_password(frm, values.new_password); + } else { + jingrow.msgprint({ + title: __('错误'), + message: __('请输入新密码'), + indicator: 'red' + }); + } + }, + __('重置服务器密码') + ); + }); } } }); @@ -51,3 +76,40 @@ function restart_server(frm) { } }); } + +function reset_password(frm, new_password) { + jingrow.call({ + method: 'jcloud.api.aliyun_server_light.reset_aliyun_instance_password', + args: { + instance_id: frm.pg.instance_id, + password: new_password, + region_id: frm.pg.region || 'cn-shanghai' + }, + callback: function(r) { + if (r.success) { + jingrow.msgprint({ + title: __('成功'), + message: __('密码重置成功,新密码已保存到服务器记录中'), + indicator: 'green' + }); + // 刷新页面以显示新密码 + setTimeout(function() { + frm.reload_pg(); + }, 2000); + } else { + jingrow.msgprint({ + title: __('重置失败'), + message: r.message || r.error || __('未知错误'), + indicator: 'red' + }); + } + }, + error: function(err) { + jingrow.msgprint({ + title: __('重置失败'), + message: err.message, + indicator: 'red' + }); + } + }); +} diff --git a/jcloud/jcloud/pagetype/jsite_server/jsite_server.json b/jcloud/jcloud/pagetype/jsite_server/jsite_server.json index b8ccb00..618a6db 100644 --- a/jcloud/jcloud/pagetype/jsite_server/jsite_server.json +++ b/jcloud/jcloud/pagetype/jsite_server/jsite_server.json @@ -27,6 +27,7 @@ "ssh_section", "ssh_user", "ssh_port", + "password", "column_break_20", "private_key" ], @@ -180,11 +181,17 @@ "fieldtype": "Data", "label": "系统", "read_only": 1 + }, + { + "default": "22", + "fieldname": "password", + "fieldtype": "Password", + "label": "服务器密码" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2025-07-28 16:21:14.934079", + "modified": "2025-07-28 17:53:25.476573", "modified_by": "Administrator", "module": "Jcloud", "name": "Jsite Server", diff --git a/jcloud/jcloud/pagetype/jsite_server/jsite_server.py b/jcloud/jcloud/pagetype/jsite_server/jsite_server.py index f2d3bf2..bcae718 100644 --- a/jcloud/jcloud/pagetype/jsite_server/jsite_server.py +++ b/jcloud/jcloud/pagetype/jsite_server/jsite_server.py @@ -22,6 +22,7 @@ class JsiteServer(Document): instance_id: DF.Data | None memory: DF.Data | None order_id: DF.Data | None + password: DF.Password | None period: DF.Int planid: DF.Data | None private_key: DF.Text | None