From 30f24e137ee2d1acec11a0da61e134f512ee02dd Mon Sep 17 00:00:00 2001
From: yyamashita <yyamashita@hetzner.yyamashita.com>
Date: Fri, 19 Jun 2026 15:18:55 +0900
Subject: Initial implementation of microblog

Markdown-based personal microblog with REST API.
XSS protection via sanitize-html on server-side markdown rendering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
 app/routes/api.posts.tsx | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 app/routes/api.posts.tsx

(limited to 'app/routes/api.posts.tsx')

diff --git a/app/routes/api.posts.tsx b/app/routes/api.posts.tsx
new file mode 100644
index 0000000..2d5637c
--- /dev/null
+++ b/app/routes/api.posts.tsx
@@ -0,0 +1,42 @@
+import type { Route } from "./+types/api.posts";
+import { createPost, listPosts } from "~/lib/db.server";
+import { renderMarkdown } from "~/lib/md.server";
+
+export async function loader() {
+  const posts = listPosts();
+  return Response.json(
+    posts.map((p) => ({ ...p, html: renderMarkdown(p.content) }))
+  );
+}
+
+export async function action({ request }: Route.ActionArgs) {
+  if (request.method !== "POST") {
+    return Response.json({ error: "Method not allowed" }, { status: 405 });
+  }
+
+  let content: string;
+  const ct = request.headers.get("content-type") ?? "";
+  if (ct.includes("application/json")) {
+    const body = (await request.json()) as { content?: unknown };
+    content = String(body.content ?? "").trim();
+  } else {
+    const form = await request.formData();
+    content = String(form.get("content") ?? "").trim();
+  }
+
+  if (!content) {
+    return Response.json({ error: "content is required" }, { status: 400 });
+  }
+  if (content.length > 10000) {
+    return Response.json(
+      { error: "content too long (max 10000 chars)" },
+      { status: 400 }
+    );
+  }
+
+  const post = createPost({ id: crypto.randomUUID(), content });
+  return Response.json(
+    { ...post, html: renderMarkdown(post.content) },
+    { status: 201 }
+  );
+}
-- 
cgit v1.2.3

