No updates detected yet. GitHub releases will appear here.
Kinnect v2: a reading-centered social app (Next.js, Clerk, Supabase, Open Library)
翻訳待ち
Loading the full README.
Read the full README ↗Live: https://kinnect-2.vercel.app
A small social app for keeping up with your friends: short text posts from people you've accepted as friends, newest first, with no algorithm and no promoted posts. It has a reading tool built in: a private library of books from Open Library that you can mention in a post.
This is version 2 of my Assignment 3 prototype, which saved everything in the browser. Version 2 has real accounts (Clerk), a real database with row-level security (Supabase), a real API (Open Library), and an end-to-end test that proves two people who aren't friends can't see each other's posts or books.
Browser ──► Next.js on Vercel ──► Supabase (Postgres + RLS)
│ │ ▲
│ └── Clerk session ──┘ (Supabase trusts Clerk's token)
└──► Open Library search API
src/lib/data.ts) and Server Actions for writes (src/lib/actions.ts). Pages never talk to Supabase directly.src/components/PasskeySignIn.tsx).src/lib/supabase.ts), so the database knows who is asking and applies row-level security.auth() first, and RLS decides which rows come back. src/proxy.ts only redirects signed-out visitors as a convenience.Open Library is called from the server, through /api/books?q= (src/app/api/books/route.ts), never from the browser. That gives one place to:
The page shows four states: loading (with "Still searching…" after 2.5 s, since searches took up to 6 s in testing), nothing found, unavailable (with retry, while your saved library still shows from Supabase), and an empty library. Only book covers load straight from Open Library in the browser.
RLS is on for every table. Signed-out visitors (anon) have no access at all, and every table also requires that the token was issued by kinnect's Clerk app.
| Table | Rule |
|---|---|
profiles | Any signed-in person can read names and avatars (People needs them). You can only change your own. |
library_items | Only you can read, add, change or remove your books. Private even from friends. |
friendships | Only the two people involved can see a request. Only the requester can send it, only the other person can accept, either can remove it. |
posts | You can read your own posts and posts by people you have an accepted friendship with. A book on a post must match a book in your own library exactly. |
post_likes, post_comments | You can read and add them only on posts you can already see. You can remove your own, and a post's author can remove any comment on it. |
The app's queries deliberately don't filter by user, so RLS is the only thing deciding what you see. That's what makes the test below meaningful.
tests/privacy.spec.ts (Playwright with @clerk/testing): two test users who are not friends. Reader A saves a book and posts about it. Reader B checks their feed, A's profile page and their own library, and must see neither A's post nor A's book.
To prove the test can fail, I loosened the posts read policy to using (true) and ran it:
| Run | Posts read policy | Result |
|---|---|---|
| 1 | using (true) (break.sql) | FAIL: B must not see A's post, expected 0, received 1 (output) |
| 2 | restored (restore.sql) | PASS (output) |
After restoring, Postgres confirmed the policy matched the original migration character for character. A second test, tests/friends.spec.ts, makes the two readers friends for a minute and checks the friendly path: request badge, posting, saving a book from a post, likes, comments and reading status.
I ran /security-review on the full-stack branch and fixed everything it found before merging:
This is the beginning of the README. Read the rest at the original. Read the full README ↗