Support non-Markdown files in public share and add raw download endpoint

This commit is contained in:
Bruno Charest 2026-05-26 22:34:45 -04:00
parent 7c4f2964eb
commit ff06d89eda

View File

@ -2836,7 +2836,11 @@ async def public_share_pdf_download(token: str):
record_access(token) record_access(token)
raw = redact_file_content(raw, str(file_path)) raw = redact_file_content(raw, str(file_path))
post = parse_markdown_file(raw) post = parse_markdown_file(raw)
html = _render_markdown(post.content, share["vault"], file_path) ext = file_path.suffix.lower()
if ext == ".md":
html = _render_markdown(post.content, share["vault"], file_path)
else:
html = f'<pre style="font-family:monospace;font-size:12px;line-height:1.6;white-space:pre-wrap">{html_mod.escape(raw)}</pre>'
title = post.metadata.get("title", file_path.stem) title = post.metadata.get("title", file_path.stem)
pdf_html = build_pdf_html(html, str(title)) pdf_html = build_pdf_html(html, str(title))
pdf_bytes = generate_pdf(pdf_html, str(title)) pdf_bytes = generate_pdf(pdf_html, str(title))
@ -2844,6 +2848,23 @@ async def public_share_pdf_download(token: str):
return Response(content=pdf_bytes, media_type="application/pdf", headers={"Content-Disposition": f'attachment; filename="{safe_name}.pdf"'}) return Response(content=pdf_bytes, media_type="application/pdf", headers={"Content-Disposition": f'attachment; filename="{safe_name}.pdf"'})
@app.get("/s/{token}/raw")
async def public_share_raw(token: str):
"""Download the raw (original) shared document."""
share = get_share_by_token(token)
if not share:
raise HTTPException(404, "Share not found or expired")
vault_data = get_vault_data(share["vault"])
if not vault_data:
raise HTTPException(404, "Vault not found")
vault_root = Path(vault_data["path"])
file_path = _resolve_safe_path(vault_root, share["path"])
if not file_path.exists():
raise HTTPException(404, "File not found")
record_access(token)
return FileResponse(path=str(file_path), filename=file_path.name, media_type="application/octet-stream")
@app.get("/s/{token}") @app.get("/s/{token}")
async def public_share_view(token: str): async def public_share_view(token: str):
"""Public share view — no authentication required.""" """Public share view — no authentication required."""
@ -2864,10 +2885,19 @@ async def public_share_view(token: str):
record_access(token) record_access(token)
raw = redact_file_content(raw, str(file_path)) raw = redact_file_content(raw, str(file_path))
post = parse_markdown_file(raw) post = parse_markdown_file(raw)
html = _render_markdown(post.content, share["vault"], file_path) ext = file_path.suffix.lower()
if ext == ".md":
html = _render_markdown(post.content, share["vault"], file_path)
else:
escaped = html_mod.escape(raw)
html = f'<pre style="background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:16px;overflow-x:auto;font-size:0.85rem;line-height:1.6"><code>{escaped}</code></pre>'
title = post.metadata.get("title", file_path.stem) title = post.metadata.get("title", file_path.stem)
# Build frontmatter section HTML # JSON-escape raw content for embedding in HTML
import json as _json
raw_json = _json.dumps(raw)
fm_html = "" fm_html = ""
if post.metadata: if post.metadata:
fm_items = [] fm_items = []
@ -2943,10 +2973,11 @@ body{{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:
</button> </button>
</div> </div>
<div class="content" id="content">{fm_html}{html}</div> <div class="content" id="content">{fm_html}{html}</div>
<script id="raw-content" type="text/plain" style="display:none">{raw_json}</script>
<script> <script>
function toggleTheme(){{var t=document.documentElement;var isDark=t.dataset.theme==="dark";t.dataset.theme=isDark?"light":"dark";document.getElementById("theme-icon-dark").style.display=isDark?"none":"";document.getElementById("theme-icon-light").style.display=isDark?"":"none";localStorage.setItem("obsigate-share-theme",t.dataset.theme)}} function toggleTheme(){{var t=document.documentElement;var isDark=t.dataset.theme==="dark";t.dataset.theme=isDark?"light":"dark";document.getElementById("theme-icon-dark").style.display=isDark?"none":"";document.getElementById("theme-icon-light").style.display=isDark?"":"none";localStorage.setItem("obsigate-share-theme",t.dataset.theme)}}
(function(){{var s=localStorage.getItem("obsigate-share-theme");if(!s)s="dark";document.documentElement.dataset.theme=s;var isDark=s==="dark";document.getElementById("theme-icon-dark").style.display=isDark?"":"none";document.getElementById("theme-icon-light").style.display=isDark?"none":""}})(); (function(){{var s=localStorage.getItem("obsigate-share-theme");if(!s)s="dark";document.documentElement.dataset.theme=s;var isDark=s==="dark";document.getElementById("theme-icon-dark").style.display=isDark?"":"none";document.getElementById("theme-icon-light").style.display=isDark?"none":""}})();
function exportMD(){{var t=document.getElementById("content").innerText;var b=new Blob([t],{{type:"text/markdown"}});var a=document.createElement("a");a.href=URL.createObjectURL(b);a.download="{title}.md";a.click()}} function exportMD(){{var raw=JSON.parse(document.getElementById("raw-content").textContent);var b=new Blob([raw],{{type:"text/markdown"}});var a=document.createElement("a");a.href=URL.createObjectURL(b);a.download="{title}.md";a.click()}}
</script></body></html>""") </script></body></html>""")