39:    return {"templates": out, "count": len(out)}
59:    return {"id": row.id, "name": row.name}
78:    return {"id": row.id, "name": row.name}
98:    return {"ok": True, "created": created, "count": len(created)}
108:    return {"id": t.id, "name": t.name, "subject_hint": t.subject_hint,
126:    return {"ok": True}
139:    return {"id": row.id}
150:    return {"ok": True}
async def list_templates(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
    rows = (await db.execute(select(EmailTemplate).where(EmailTemplate.user_id == user.id)
                             .order_by(EmailTemplate.id))).scalars().all()
    out = []
    for t in rows:
        analysis = analyze_template_variables(t.body_html)
        out.append({"id": t.id, "name": t.name, "subject_hint": t.subject_hint,
                    "source": t.source, "sequence_order": t.sequence_order,
                    "size_html": len(t.body_html), "variables": analysis["variables"],
                    "unknown_variables": analysis["unknown"],
                    "created_at": t.created_at.isoformat()})
    return {"templates": out, "count": len(out)}

=== attachments list ===
54:    return {
92:    return {"attachments": [_serialize(a) for a in rows],
167:    return {"id": att.id, **_serialize(att)}
213:    return {"id": att.id, **_serialize(att)}
251:        return {"previewable": True, "kind": "html",
257:        return {"previewable": False, "reason": "no_content"}
259:        return {"previewable": False, "reason": "too_large", "size": len(content)}
262:        return {"previewable": True, "kind": att.kind,
