Files
habit-tracker/app/markdown_utils.py
T
shinalokandClaude Sonnet 5 bdf9d0bae7 journal: real markdown editor (EasyMDE) with live preview toggle, fix default template
- Replace the plain textarea with EasyMDE (vendored locally, no CDN) for
  markdown authoring: syntax highlighting, smart list continuation, and a
  custom text-based toolbar (built-in EasyMDE toolbar icons require Font
  Awesome from a CDN, which this app doesn't use). unorderedListStyle is set
  to "-" to match the app's own template convention.
- Add a preview/edit toggle button that swaps the editor for the exact same
  server-rendered markdown (via /journal/preview) shown after saving, instead
  of always showing both.
- Fix create/edit entry routes to verify the submitted category_id actually
  belongs to the current user before inserting -- every other write path in
  this app already checked ownership; this one didn't (found while manually
  testing the new editor with a typo'd category id that happened to belong to
  someone else's category, which surfaced as an IntegrityError 500 instead of
  a clean 404-equivalent).
- Fix the default "일상" category template: bare "-" bullet lines don't parse
  as list items in the markdown renderer (they need a trailing space), and
  the content_template validator was silently stripping that trailing space
  off on every save. Backfill migration updates any category still holding
  the old, broken template text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 12:24:18 +09:00

24 lines
1.1 KiB
Python

import bleach
import markdown
from markupsafe import Markup
# nl2br: 빈 줄 없이 그냥 엔터만 쳐도 줄바꿈되게 한다 — 지금까지 백엔드가 순수 텍스트를
# white-space: pre-wrap으로 보여주던 것과 체감이 최대한 비슷하도록.
_MARKDOWN_EXTENSIONS = ["nl2br", "sane_lists"]
_ALLOWED_TAGS = [
"p", "br", "strong", "em", "del",
"h1", "h2", "h3", "h4",
"ul", "ol", "li",
"blockquote", "code", "pre", "hr", "a",
]
_ALLOWED_ATTRS = {"a": ["href", "title"]}
def render_markdown(text: str) -> Markup:
"""저널 기록 내용을 마크다운 HTML로 렌더링한다. markdown 라이브러리는 기본적으로 원본 HTML을
그대로 통과시키므로(<script> 등 포함) bleach로 허용 태그만 남기고 나머지는 전부 지운다 —
이 함수가 반환하는 Markup만 템플릿에서 이스케이프 없이(그대로 안전하게) 렌더링해야 한다."""
html = markdown.markdown(text, extensions=_MARKDOWN_EXTENSIONS)
return Markup(bleach.clean(html, tags=_ALLOWED_TAGS, attributes=_ALLOWED_ATTRS, strip=True))