Files
shinalokandClaude Sonnet 5 00c66f9df8 journal: paste image from clipboard, cap embedded image size, fix media persistence
- Paste-to-embed: pasting an image into the markdown editor uploads it and
  inserts ![](url) at the cursor. Unlike gallery attachments these aren't
  tied to a journal_entry (the entry may not exist yet while composing), so
  they're stored per-user under app/media/journal/{user_id}/pasted/ with no
  DB row, served through an ownership-scoped route, and never cleaned up
  automatically when an entry is deleted -- an accepted tradeoff at this
  app's personal scale.
- The markdown sanitizer was stripping all <img> tags (not on the bleach
  allowlist), which would have silently deleted every pasted image on save;
  added img/src/alt/title while keeping event-handler attributes blocked.
- Cap embedded image width in both the editor pane and the rendered preview
  so a large pasted photo can't overflow its card.
- Fix real data loss risk found while testing this: docker-compose.yml had
  no volume for app/media, so every container recreate during a deploy wiped
  uploaded photos, and deploy_sftp.py was syncing app/media/ (runtime user
  data, not source) into the remote build context. Added the volume mount
  and excluded media/ from the sync script. Recovered and relocated the
  real attachments that had already landed in the wrong place on the NAS
  during earlier deploys this session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 14:27:30 +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", "img",
]
_ALLOWED_ATTRS = {"a": ["href", "title"], "img": ["src", "alt", "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))