feat: add /admin/templates/preview endpoint; add preview button to template editor
This commit is contained in:
parent
74b8b8e8ed
commit
0def77dc30
28
app/admin.py
28
app/admin.py
@ -1,4 +1,4 @@
|
||||
from fastapi import APIRouter, Request, Form, Depends, HTTPException
|
||||
from fastapi import APIRouter, Request, Form, Depends, HTTPException, Body
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from sqlalchemy.orm import Session, joinedload, aliased
|
||||
@ -7,6 +7,7 @@ import json
|
||||
from app.db import SessionLocal, Target, NotificationChannel, MessageTemplate, WebhookEndpoint, RequestLog, DeliveryLog, ProcessingRule, RuleAction
|
||||
from app.services.stats import stats_service
|
||||
from app.services.engine import engine
|
||||
from app.templates import safe_render
|
||||
|
||||
router = APIRouter(prefix="/admin", tags=["admin"])
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
@ -424,6 +425,31 @@ async def delete_template(id: int = Form(...), db: Session = Depends(get_db)):
|
||||
db.commit()
|
||||
return RedirectResponse(url="/admin/templates", status_code=303)
|
||||
|
||||
|
||||
@router.post("/templates/preview")
|
||||
async def preview_template(data: dict = Body(...), db: Session = Depends(get_db)):
|
||||
"""
|
||||
Preview a template with an optional sample payload.
|
||||
Request JSON: { "template_content": "...", "sample_payload": {...}, "vars": {...} }
|
||||
"""
|
||||
template_content = data.get("template_content")
|
||||
if not template_content:
|
||||
return JSONResponse({"error": "template_content is required"}, status_code=400)
|
||||
|
||||
sample_payload = data.get("sample_payload") or {}
|
||||
# Build render context using engine's flatten helper if available
|
||||
try:
|
||||
render_context = engine._flatten_payload(sample_payload) if hasattr(engine, "_flatten_payload") else dict(sample_payload)
|
||||
# Merge any provided template vars
|
||||
extra_vars = data.get("vars") or {}
|
||||
if isinstance(extra_vars, dict):
|
||||
render_context.update(extra_vars)
|
||||
|
||||
rendered = safe_render(template_content, render_context)
|
||||
return JSONResponse({"rendered": rendered})
|
||||
except Exception as e:
|
||||
return JSONResponse({"error": str(e)}, status_code=400)
|
||||
|
||||
# --- Logs ---
|
||||
@router.get("/logs", response_class=HTMLResponse)
|
||||
async def list_logs(request: Request, db: Session = Depends(get_db)):
|
||||
|
||||
@ -67,9 +67,14 @@
|
||||
<label class="form-label">模板内容</label>
|
||||
<textarea class="form-control" name="template_content" id="templateContent" rows="3" required placeholder="收到{trans_amt}元"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">示例 Payload (JSON, 可选,用于预览)</label>
|
||||
<textarea class="form-control font-monospace" id="templateSample" rows="4" placeholder='{"trans_amt": 100, "trans_order_info": {"remark": "abc"}}'></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="previewTemplate()">预览</button>
|
||||
<button type="submit" class="btn btn-primary">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -88,6 +93,7 @@
|
||||
document.getElementById('templateId').value = "";
|
||||
document.getElementById('templateName').value = "";
|
||||
document.getElementById('templateContent').value = "";
|
||||
document.getElementById('templateSample').value = '';
|
||||
modal.show();
|
||||
}
|
||||
|
||||
@ -97,7 +103,38 @@
|
||||
document.getElementById('templateId').value = id;
|
||||
document.getElementById('templateName').value = name;
|
||||
document.getElementById('templateContent').value = content;
|
||||
document.getElementById('templateSample').value = '';
|
||||
modal.show();
|
||||
}
|
||||
|
||||
async function previewTemplate() {
|
||||
const content = document.getElementById('templateContent').value;
|
||||
let sample = {};
|
||||
try {
|
||||
const sampleText = document.getElementById('templateSample').value;
|
||||
if (sampleText && sampleText.trim()) {
|
||||
sample = JSON.parse(sampleText);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('示例 payload JSON 格式错误: ' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/templates/preview', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ template_content: content, sample_payload: sample })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
alert('渲染结果:\\n' + data.rendered);
|
||||
} else {
|
||||
alert('预览失败: ' + (data.error || '未知错误'));
|
||||
}
|
||||
} catch (e) {
|
||||
alert('请求错误: ' + e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user