published() ->latest('published_at') ->limit(200) ->get(); $lines = [ '# '.$siteName, '', '> '.$siteDescription, '', '## Feeds', '', '- [RSS]('.route('feed.rss').')', '- [Sitemap]('.route('feed.sitemap').')', '- [llms.txt]('.route('feed.llms').')', '', '## Articles', '', ]; foreach ($posts as $post) { if ($this->isRestricted($post)) { continue; } $summary = Str::limit(trim(preg_replace('/\s+/', ' ', strip_tags($post->excerpt_or_fallback)) ?? ''), 120); $markdownUrl = $this->markdownUrl($post); $htmlUrl = $post->url; $title = $post->title; $lines[] = $markdownUrl ? '- ['.$title.']('.$markdownUrl.') — '.$summary.' ([html]('.$htmlUrl.'))' : '- ['.$title.']('.$htmlUrl.') — '.$summary; } return response(implode("\n", $lines)."\n", 200, [ 'Content-Type' => 'text/plain; charset=utf-8', 'Cache-Control' => 'no-store', ]); } /** * /posts/{slug}.md — full markdown for a published public post. */ public function postMarkdown(string $slug) { $post = Post::query() ->where('slug', $slug) ->orWhere('id', (int) $slug) ->first(); abort_unless($post && $post->status === 'published' && ! $this->isRestricted($post), 404); $body = $post->content_format === 'markdown' ? (string) $post->content : $this->htmlToMarkdown((string) $post->content); $out = '# '.$post->title."\n\n"; if ($post->excerpt) { $out .= '> '.$post->excerpt."\n\n"; } $out .= $body."\n"; return response($out, 200, [ 'Content-Type' => 'text/markdown; charset=utf-8', 'Cache-Control' => 'no-store', ]); } private function isRestricted(Post $post): bool { if (filled($post->read_password)) { return true; } return ! empty(($post->meta ?? [])['members_only']); } private function markdownUrl(Post $post): ?string { $slug = trim((string) $post->slug); if ($slug === '') { return route('geo.post.markdown', ['slug' => (string) $post->id]); } return route('geo.post.markdown', ['slug' => $slug]); } private function htmlToMarkdown(string $html): string { $converter = new HtmlConverter([ 'strip_tags' => false, 'hard_break' => true, ]); return $converter->convert($html); } }