Samme indhold, URL'er og udseende, bygget af en enkelt zola-binær og Tailwind standalone-CLI uden Node. Kanonisk domæne er asger.weirsoe.dk. Blogindlæg ligger som page bundles med frontmatter Zola forstår, så de gamle stempler er ugyldige, indtil scripts/omstempl.py er kørt med PUBLISH_KEY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.0 KiB
Python
Executable File
62 lines
2.0 KiB
Python
Executable File
"""Godkendelsesstemplet på blogindlæg: HMAC-SHA256 over sti, indhold og billeder.
|
|
|
|
canonical() skal matche godkendelsestjenesten (service/app.py i agw/pw-drafts) byte for byte.
|
|
"""
|
|
import hashlib
|
|
import hmac
|
|
import os
|
|
import re
|
|
|
|
BLOG = "content/blog"
|
|
|
|
|
|
def canonical(rel, text, assets=()):
|
|
"""assets: [(sti, sha256-hex)] for billederne i bundlen, sorteret efter sti."""
|
|
lines = text.replace("\r\n", "\n").split("\n")
|
|
if lines and lines[0] == "---" and "---" in lines[1:]:
|
|
end = lines.index("---", 1)
|
|
lines = [l for i, l in enumerate(lines) if not (0 < i < end and re.match(r"\s*godkendt:", l))]
|
|
return f"{rel}\n" + "\n".join(lines) + "".join(f"\n@asset {p} {h}" for p, h in assets)
|
|
|
|
|
|
def sign(key, rel, text, assets=()):
|
|
return "v1:" + hmac.new(key, canonical(rel, text, assets).encode(), hashlib.sha256).hexdigest()
|
|
|
|
|
|
def sha256_file(path):
|
|
with open(path, "rb") as f:
|
|
return hashlib.sha256(f.read()).hexdigest()
|
|
|
|
|
|
def assets_for(slug):
|
|
"""Alt i bundlen ud over index.md er billeder, og de ligger fladt."""
|
|
d = f"{BLOG}/{slug}"
|
|
out = []
|
|
for name in os.listdir(d):
|
|
if name == "index.md":
|
|
continue
|
|
p = f"{d}/{name}"
|
|
if not os.path.isfile(p):
|
|
raise ValueError(f"{p}: undermapper understøttes ikke")
|
|
if name.endswith(".md"):
|
|
raise ValueError(f"{p}: kun index.md må være markdown i en bundle")
|
|
out.append((p, sha256_file(p)))
|
|
return sorted(out)
|
|
|
|
|
|
def read_text(path):
|
|
with open(path, "rb") as f:
|
|
return f.read().decode()
|
|
|
|
|
|
def stamp_of(text):
|
|
"""(stempel eller None, om indlægget er markeret draft: true)"""
|
|
lines = text.replace("\r\n", "\n").split("\n")
|
|
end = lines.index("---", 1) if lines[0] == "---" and "---" in lines[1:] else -1
|
|
if end < 0:
|
|
return None, False
|
|
fm = lines[1:end]
|
|
g = next((l for l in fm if re.match(r"\s*godkendt:", l)), None)
|
|
draft = any(re.fullmatch(r"draft:\s*true\s*", l) for l in fm)
|
|
return (re.sub(r"^\s*godkendt:\s*", "", g).strip("'\"") if g else None), draft
|