fix(admin): protect cornerstone .md files from bulk delete + fix PDF 500
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2142,7 +2142,7 @@ async def scenario_preview(scenario_id: int):
|
||||
async def scenario_pdf(scenario_id: int):
|
||||
"""Generate and immediately download a business plan PDF for a published scenario."""
|
||||
from ..businessplan import get_plan_sections
|
||||
from ..planner.calculator import validate_state
|
||||
from ..planner.calculator import calc, validate_state
|
||||
|
||||
scenario = await fetch_one("SELECT * FROM published_scenarios WHERE id = ?", (scenario_id,))
|
||||
if not scenario:
|
||||
@@ -2153,7 +2153,7 @@ async def scenario_pdf(scenario_id: int):
|
||||
lang = "en"
|
||||
|
||||
state = validate_state(json.loads(scenario["state_json"]))
|
||||
d = json.loads(scenario["calc_json"])
|
||||
d = calc(state)
|
||||
sections = get_plan_sections(state, d, lang)
|
||||
sections["scenario_name"] = scenario["title"]
|
||||
sections["location"] = scenario.get("location", "")
|
||||
@@ -2274,6 +2274,18 @@ async def _sync_static_articles() -> None:
|
||||
template_slug, group_key, now_iso, now_iso),
|
||||
)
|
||||
|
||||
# Build HTML so the article is immediately servable (cornerstones have no template)
|
||||
if template_slug is None:
|
||||
from ..content.routes import BUILD_DIR, bake_product_cards, bake_scenario_cards
|
||||
|
||||
body = raw[m.end():]
|
||||
body_html = mistune.html(body)
|
||||
body_html = await bake_scenario_cards(body_html, lang=language)
|
||||
body_html = await bake_product_cards(body_html, lang=language)
|
||||
build_dir = BUILD_DIR / language
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
(build_dir / f"{slug}.html").write_text(body_html)
|
||||
|
||||
|
||||
def _build_article_where(
|
||||
status: str = None,
|
||||
@@ -2613,15 +2625,18 @@ async def articles_bulk():
|
||||
from ..content.routes import BUILD_DIR
|
||||
|
||||
rows = await fetch_all(
|
||||
f"SELECT id, slug FROM articles WHERE {where} LIMIT 5000", tuple(where_params)
|
||||
f"SELECT id, slug, template_slug FROM articles WHERE {where} LIMIT 5000",
|
||||
tuple(where_params),
|
||||
)
|
||||
for a in rows:
|
||||
build_path = BUILD_DIR / f"{a['slug']}.html"
|
||||
if build_path.exists():
|
||||
build_path.unlink()
|
||||
md_path = Path("data/content/articles") / f"{a['slug']}.md"
|
||||
if md_path.exists():
|
||||
md_path.unlink()
|
||||
# Only remove source .md for generated articles; cornerstones have no template
|
||||
if a["template_slug"] is not None:
|
||||
md_path = Path("data/content/articles") / f"{a['slug']}.md"
|
||||
if md_path.exists():
|
||||
md_path.unlink()
|
||||
await execute(f"DELETE FROM articles WHERE {where}", tuple(where_params))
|
||||
from ..sitemap import invalidate_sitemap_cache
|
||||
invalidate_sitemap_cache()
|
||||
@@ -2667,16 +2682,18 @@ async def articles_bulk():
|
||||
from ..content.routes import BUILD_DIR
|
||||
|
||||
articles_rows = await fetch_all(
|
||||
f"SELECT id, slug FROM articles WHERE id IN ({placeholders})",
|
||||
f"SELECT id, slug, template_slug FROM articles WHERE id IN ({placeholders})",
|
||||
tuple(article_ids),
|
||||
)
|
||||
for a in articles_rows:
|
||||
build_path = BUILD_DIR / f"{a['slug']}.html"
|
||||
if build_path.exists():
|
||||
build_path.unlink()
|
||||
md_path = Path("data/content/articles") / f"{a['slug']}.md"
|
||||
if md_path.exists():
|
||||
md_path.unlink()
|
||||
# Only remove source .md for generated articles; cornerstones have no template
|
||||
if a["template_slug"] is not None:
|
||||
md_path = Path("data/content/articles") / f"{a['slug']}.md"
|
||||
if md_path.exists():
|
||||
md_path.unlink()
|
||||
await execute(
|
||||
f"DELETE FROM articles WHERE id IN ({placeholders})",
|
||||
tuple(article_ids),
|
||||
|
||||
Reference in New Issue
Block a user