feat: auth by default, theme toggle, CoderPad startup, review fixes

- Require a session on every route; public routes opt out with @allow_anonymous
- Split password hashing and pepper loading into passwords.py
- Add a system/light/dark theme toggle backed by light-dark() colors
- Ignore stale 401s from an earlier session, PATCH only changed book fields,
  and block overlapping journal-entry saves
- Add bin/start and CoderPad Vite server settings for the pad's start/restart
- Rewrite README as a mise onboarding guide; expand .gitignore
- Include review-round fixes and tests
This commit is contained in:
2026-10-02 16:49:14 -05:00
parent 9d7b0e805c
commit 7f5d034a1f
46 changed files with 1797 additions and 497 deletions
+38 -7
View File
@@ -79,13 +79,7 @@ describe("BookForm", () => {
expect(calls).toContainEqual({
method: "PATCH",
path: "/api/books/7",
body: {
title: "Dune Messiah",
author: "Frank Herbert",
genre_id: 5,
total_pages: 380,
current_page: 142,
},
body: { title: "Dune Messiah" },
});
});
@@ -104,4 +98,41 @@ describe("BookForm", () => {
await user.click(screen.getByRole("button", { name: "Retry" }));
expect(await screen.findByLabelText("Title")).toHaveValue("Dune");
});
it("saving without changes returns to the book without a request", async () => {
const calls = signedIn({
"GET /api/books/7": () => [200, dune],
"GET /api/books/7/notes": () => [200, []],
});
const user = renderApp("/books/7/edit");
await screen.findByDisplayValue("Dune");
await user.click(screen.getByRole("button", { name: "Save changes" }));
await screen.findByRole("heading", { name: "Dune", level: 1 });
expect(calls.some((call) => call.method === "PATCH")).toBe(false);
});
it("preserves progress saved in another session when only the title changes", async () => {
let saved = { ...dune };
signedIn({
"GET /api/books/7": () => [200, { ...saved }],
"GET /api/books/7/notes": () => [200, []],
"PATCH /api/books/7": ({ body }) => {
saved = { ...saved, ...(body as object) };
return [200, saved];
},
});
const user = renderApp("/books/7/edit");
const title = await screen.findByDisplayValue("Dune");
await screen.findByRole("option", { name: "Science Fiction" });
// Another session saves progress after this form loads its snapshot.
saved = { ...saved, current_page: 300 };
await user.clear(title);
await user.type(title, "Dune Messiah");
await user.click(screen.getByRole("button", { name: "Save changes" }));
await screen.findByRole("heading", { name: "Dune Messiah", level: 1 });
expect(screen.getByRole("progressbar")).toHaveAttribute("value", "300");
});
});