docs(examples): add the CMS proof-of-concept dogfooding artifact
Some checks failed
CI / Rust — fmt, clippy, test (push) Failing after 39m53s
CI / Dashboard — check (push) Successful in 10m15s

A WordPress-inspired CMS whose entire backend is PiCloud Rhai scripts
deployed with pic apply, plus a SvelteKit frontend. Built to dogfood the
project tool, CLI, and SDK; exercises docs/kv/files/users, docs+queue+cron
+pubsub triggers, the transactional-outbox notification chain, a docs
before-interceptor, set_if CAS, SSE, per-app CORS, env overlays, and a
durable Workflow (validate -> enrich || seo -> publish -> finalize) started
with workflow::start and polled with workflow::run_status, visualized live
with Svelte Flow.

FINDINGS.md / SECURITY.md capture every gap, surprise, and security issue
found (each tagged [PiCloud] vs [CMS]); the platform fixes for the
actionable ones ship in the preceding commit.

Also folds in the read-only `pic` allowlist entries added to
.claude/settings.json during the session (fewer-permission-prompts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-19 20:15:29 +02:00
parent 5682be366c
commit 813bc46640
68 changed files with 4623 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
// POST /cms/posts/:id/comments — submit a comment (public, rate-limited).
// The docs `before` interceptor (comment_sanitize) escapes the body and sets
// the moderation status; this endpoint just validates + writes.
let post_id = ctx.request.params.id;
let b = ctx.request.body;
if b == () || b.body == () || b.author_name == () {
return #{ statusCode: 400, body: #{ error: "author_name and body required" } };
}
// the post must exist and be published
let posts = docs::collection("posts");
let pd = posts.get(post_id);
if pd == () || pd.data.status != "published" {
return #{ statusCode: 404, body: #{ error: "post not found" } };
}
// rate limit: max 10 comments / hour / IP
let ip = ctx.request.headers["x-forwarded-for"];
if ip == () { ip = "local"; }
let rl = kv::collection("ratelimit");
let bk = "comment:" + ip;
let cur = rl.get(bk);
let cnt = if cur == () { 0 } else { cur.count };
if cnt >= 10 { return #{ statusCode: 429, body: #{ error: "slow down" } }; }
rl.set(bk, #{ count: cnt + 1, at: time::now() });
let comments = docs::collection("comments");
let data = #{
post_id: post_id,
post_slug: pd.data.slug,
post_title: pd.data.title,
author_name: b.author_name,
author_email: if b.author_email == () { "" } else { b.author_email },
body: b.body,
status: "pending", // interceptor re-affirms/overrides
created_at: time::now(),
};
let id = comments.create(data); // <- interceptor sanitizes + sets status here
#{ statusCode: 201, body: #{ id: id, status: "submitted" } }