| 80 | |
| 81 | @dataclass |
| 82 | class Post: |
| 83 | path: Path |
| 84 | slug: str |
| 85 | title: str |
| 86 | date: dt.date |
| 87 | front_matter: dict[str, Any] |
| 88 | body: str |
| 89 | |
| 90 | @property |
| 91 | def canonical_url(self) -> str: |
| 92 | url_field = self.front_matter.get("url") |
| 93 | if isinstance(url_field, str) and url_field.startswith("/"): |
| 94 | return f"{SITE_BASE_URL}{url_field}" |
| 95 | return f"{SITE_BASE_URL}/blog/{self.slug}/" |
| 96 | |
| 97 | @property |
| 98 | def cover_image(self) -> str | None: |
| 99 | match = re.search(r"!\[[^\]]*\]\((/blog/[^)\s]+)\)", self.body) |
| 100 | if match: |
| 101 | return f"{SITE_BASE_URL}{match.group(1)}" |
| 102 | return None |
| 103 | |
| 104 | |
| 105 | @dataclass |