update
This commit is contained in:
parent
86e689f453
commit
96ef2cf88c
|
|
@ -0,0 +1,33 @@
|
||||||
|
{% extends "sqladmin/layout.html" %}
|
||||||
|
{% from 'sqladmin/_macros.html' import render_form_fields %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
### 编辑{{ model_view.name }}
|
||||||
|
|
||||||
|
{% if error %}
|
||||||
|
|
||||||
|
{{ error }}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{{ render_form_fields(form, form_opts=form_opts) }}
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label class="form-label col-sm-2 col-form-label">密文配置(secret_cfg)</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea name="secret_cfg" class="form-control" rows="8" placeholder='留空表示不修改;填写将覆盖并加密保存。示例:{"token":"xxx"}'></textarea>
|
||||||
|
<div class="form-text">
|
||||||
|
出于安全考虑,编辑页不回显历史密文。留空表示不修改;填写 JSON 对象将覆盖原值并重新加密保存。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3 d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary">保存</button>
|
||||||
|
<a class="btn btn-secondary" href="{{ url_for('admin:list', identity=model_view.identity) }}">取消</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
@ -69,9 +69,15 @@ class JobAdmin(ModelView, model=Job):
|
||||||
# 为 Job 详情页指定模板(用于调整按钮间距)
|
# 为 Job 详情页指定模板(用于调整按钮间距)
|
||||||
details_template = "job_details.html"
|
details_template = "job_details.html"
|
||||||
|
|
||||||
|
# 编辑页:secret_cfg 只写不读(不回显密文;留空表示不更新)
|
||||||
|
edit_template = "job_edit.html"
|
||||||
|
|
||||||
# 列表页模板:加入每行 Run Now
|
# 列表页模板:加入每行 Run Now
|
||||||
list_template = "job_list.html"
|
list_template = "job_list.html"
|
||||||
|
|
||||||
|
# 编辑页排除 secret_cfg,避免回显密文;由自定义模板额外渲染一个空输入框
|
||||||
|
form_edit_rules = [Job.id, Job.enabled, Job.cron_expr, Job.handler_path, Job.public_cfg]
|
||||||
|
|
||||||
column_labels = {
|
column_labels = {
|
||||||
"id": "任务ID",
|
"id": "任务ID",
|
||||||
"enabled": "启用",
|
"enabled": "启用",
|
||||||
|
|
@ -141,7 +147,10 @@ class JobAdmin(ModelView, model=Job):
|
||||||
raise ValueError("public_cfg must be a JSON object")
|
raise ValueError("public_cfg must be a JSON object")
|
||||||
data["public_cfg"] = pcfg
|
data["public_cfg"] = pcfg
|
||||||
|
|
||||||
# secret_cfg:必须是合法 JSON 对象(dict),并且保存时必须加密落库
|
# secret_cfg:
|
||||||
|
# - 创建:必须是合法 JSON 对象(dict),并且保存时必须加密落库
|
||||||
|
# - 编辑:出于安全考虑不回显密文;若留空则保留原密文不更新;若填写则按 JSON 校验并加密覆盖
|
||||||
|
if is_created:
|
||||||
scfg = data.get("secret_cfg")
|
scfg = data.get("secret_cfg")
|
||||||
if isinstance(scfg, str):
|
if isinstance(scfg, str):
|
||||||
try:
|
try:
|
||||||
|
|
@ -151,6 +160,25 @@ class JobAdmin(ModelView, model=Job):
|
||||||
if not isinstance(scfg, dict):
|
if not isinstance(scfg, dict):
|
||||||
raise ValueError("secret_cfg must be a JSON object")
|
raise ValueError("secret_cfg must be a JSON object")
|
||||||
data["secret_cfg"] = encrypt_json(scfg)
|
data["secret_cfg"] = encrypt_json(scfg)
|
||||||
|
else:
|
||||||
|
# 自定义编辑页会以 textarea 传回 secret_cfg(可能不存在或为空)
|
||||||
|
try:
|
||||||
|
form = await request.form()
|
||||||
|
raw = form.get("secret_cfg")
|
||||||
|
except Exception:
|
||||||
|
raw = None
|
||||||
|
raw_s = str(raw).strip() if raw is not None else ""
|
||||||
|
if not raw_s:
|
||||||
|
# 留空:不更新密文字段
|
||||||
|
data.pop("secret_cfg", None)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
scfg2 = json.loads(raw_s)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise ValueError("secret_cfg must be a JSON object") from e
|
||||||
|
if not isinstance(scfg2, dict):
|
||||||
|
raise ValueError("secret_cfg must be a JSON object")
|
||||||
|
data["secret_cfg"] = encrypt_json(scfg2)
|
||||||
|
|
||||||
|
|
||||||
class JobLogAdmin(ModelView, model=JobLog):
|
class JobLogAdmin(ModelView, model=JobLog):
|
||||||
|
|
@ -208,6 +236,11 @@ class JobLogAdmin(ModelView, model=JobLog):
|
||||||
column_formatters_detail = {
|
column_formatters_detail = {
|
||||||
JobLog.started_at: lambda m, a: _fmt_dt_seconds(m.started_at),
|
JobLog.started_at: lambda m, a: _fmt_dt_seconds(m.started_at),
|
||||||
JobLog.finished_at: lambda m, a: _fmt_dt_seconds(m.finished_at),
|
JobLog.finished_at: lambda m, a: _fmt_dt_seconds(m.finished_at),
|
||||||
|
JobLog.message: lambda m, a: Markup(
|
||||||
|
"<pre style='max-height:240px;overflow:auto;white-space:pre-wrap'>"
|
||||||
|
+ (m.message or "")
|
||||||
|
+ "</pre>"
|
||||||
|
),
|
||||||
JobLog.traceback: lambda m, a: Markup(f"<pre style='white-space:pre-wrap'>{m.traceback or ''}</pre>"),
|
JobLog.traceback: lambda m, a: Markup(f"<pre style='white-space:pre-wrap'>{m.traceback or ''}</pre>"),
|
||||||
JobLog.run_log: lambda m, a: Markup(
|
JobLog.run_log: lambda m, a: Markup(
|
||||||
"<pre style='max-height:480px;overflow:auto;white-space:pre-wrap'>"
|
"<pre style='max-height:480px;overflow:auto;white-space:pre-wrap'>"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue