feat: public end-user API#47
Merged
Merged
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds cursor-keyset paginated public post list filtered to public boards and non-deleted/non-merged posts, with sort=newest|votes support. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ity + rename Rewrites the relational db.query.* call to db.select().from(posts).innerJoin(boards,...) so boards.isPublic and boards.deletedAt filters are applied against the correct table. Renames listPublicPosts → listPublicPostFeed and aligns type names to PublicPostFeedSummary. Tests are reworked to mock and assert the core query builder chain. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Anonymous-read routes for published changelog entries: GET /api/public/v1/changelog (hardcoded status:'published', cursor+limit) and GET /api/public/v1/changelog/:entryId (404 for missing or non-published). 16 vitest tests all passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…jection test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ead routes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1c96915 to
37f036e
Compare
There was a problem hiding this comment.
Pull request overview
Adds a new “public end-user” API surface under /api/public/v1 for the widget / first-party clients, including endpoints for posts (feed/detail/comments/vote), changelog, config, help-center, and an OpenAPI spec.
Changes:
- Introduces new public v1 routes (posts, votes, comments, boards, changelog, config, help-center, openapi).
- Adds portal-session auth helper for
Authorization: Bearer <token>and wires it into authenticated public endpoints. - Adds a new cursor/keyset-based public post feed query (
listPublicPostFeed) and updates comment querying to supportpublicOnlyfiltering.
Reviewed changes
Copilot reviewed 28 out of 29 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
| bun.lock | Lockfile metadata updated (configVersion). |
| apps/web/src/routes/api/public/v1/posts/index.ts | Public posts feed + authenticated post submission. |
| apps/web/src/routes/api/public/v1/posts/$postId.ts | Public single-post read with visibility guard + hasVoted. |
| apps/web/src/routes/api/public/v1/posts/$postId.comments.ts | Public comments list + authenticated comment create. |
| apps/web/src/routes/api/public/v1/posts/$postId.vote.ts | Authenticated vote toggle endpoint. |
| apps/web/src/routes/api/public/v1/posts/tests/vote.test.ts | Tests for vote endpoint behavior. |
| apps/web/src/routes/api/public/v1/posts/tests/submit.test.ts | Tests for post submission behavior. |
| apps/web/src/routes/api/public/v1/posts/tests/index.test.ts | Tests for posts feed (and boards route). |
| apps/web/src/routes/api/public/v1/posts/tests/detail.test.ts | Tests for post detail + comments listing. |
| apps/web/src/routes/api/public/v1/posts/tests/comment-create.test.ts | Tests for comment creation behavior. |
| apps/web/src/routes/api/public/v1/openapi.json.ts | Route serving generated OpenAPI document. |
| apps/web/src/routes/api/public/v1/help/search.ts | Public help-center search endpoint. |
| apps/web/src/routes/api/public/v1/help/categories/index.ts | Public help-center categories endpoint. |
| apps/web/src/routes/api/public/v1/help/articles/$slug.ts | Public help-center article endpoint (refactor). |
| apps/web/src/routes/api/public/v1/help/tests/help.test.ts | Tests for new help-center public endpoints. |
| apps/web/src/routes/api/public/v1/config.ts | Public widget config endpoint. |
| apps/web/src/routes/api/public/v1/changelog/index.ts | Public changelog list endpoint (published-only). |
| apps/web/src/routes/api/public/v1/changelog/$entryId.ts | Public changelog detail endpoint (published-only). |
| apps/web/src/routes/api/public/v1/changelog/tests/index.test.ts | Tests for changelog list + detail routes. |
| apps/web/src/routes/api/public/v1/boards/index.ts | Public boards list endpoint (filters public boards). |
| apps/web/src/routes/api/public/v1/tests/openapi.test.ts | Tests for OpenAPI JSON route + document basics. |
| apps/web/src/routes/api/public/v1/tests/config.test.ts | Tests for public config route. |
| apps/web/src/lib/server/domains/posts/post.types.ts | Adds board.isPublic to PostWithDetails. |
| apps/web/src/lib/server/domains/posts/post.query.ts | Propagates board.isPublic; changes comments query to publicOnly option and pruning. |
| apps/web/src/lib/server/domains/posts/post.public-list.ts | New keyset/cursor public post feed query. |
| apps/web/src/lib/server/domains/posts/tests/post.public-list.test.ts | Unit tests for new public post feed query builder. |
| apps/web/src/lib/server/domains/api/public-openapi.ts | New OpenAPI document builder for public v1 API. |
| apps/web/src/lib/server/domains/api/portal-auth.ts | New portal-session resolver + “require” wrapper. |
| apps/web/src/lib/server/domains/api/tests/portal-auth.test.ts | Tests for portal-auth session handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+13
to
+16
| const createCommentSchema = z.object({ | ||
| content: z.string().min(1, 'Content is required').max(10000), | ||
| parentId: z.string().optional(), | ||
| }) |
Comment on lines
1
to
5
| import { createFileRoute } from '@tanstack/react-router' | ||
| import { successResponse, handleDomainError } from '@/lib/server/domains/api/responses' | ||
| import { listChangelogs } from '@/lib/server/domains/changelog/changelog.query' | ||
|
|
||
| export const Route = createFileRoute('/api/public/v1/changelog/')({ |
| import { createFileRoute } from '@tanstack/react-router' | ||
| import { config } from '@/lib/server/config' | ||
|
|
||
| export const Route = createFileRoute('/api/public/v1/openapi/json')({ |
Comment on lines
+82
to
+91
| const body = await request.json() | ||
| const parsed = submitPostSchema.safeParse(body) | ||
| if (!parsed.success) { | ||
| return badRequestResponse('Invalid request body', { | ||
| errors: parsed.error.flatten().fieldErrors, | ||
| }) | ||
| } | ||
|
|
||
| const { getBoardById } = await import('@/lib/server/domains/boards/board.service') | ||
| const board = await getBoardById(parsed.data.boardId as BoardId) |
Comment on lines
170
to
174
| /** | ||
| * Get comments with nested replies and reactions for a post | ||
| * | ||
| * @param postId - Post ID to fetch comments for | ||
| * @param principalId - Principal ID to check for reactions (optional) |
Comment on lines
+35
to
+39
| const boardIdParam = url.searchParams.get('boardId') ?? undefined | ||
| const cursor = url.searchParams.get('cursor') ?? undefined | ||
|
|
||
| const { isValidTypeId } = await import('@opencoven-feedback/ids') | ||
| const boardId = |
Comment on lines
+31
to
+35
| let hasVoted = false | ||
| if (session) { | ||
| const { getAllUserVotedPostIds } = | ||
| await import('@/lib/server/domains/posts/post.public') | ||
| const voted = await getAllUserVotedPostIds(session.principal.id) |
Comment on lines
+84
to
+88
| const { createComment } = await import('@/lib/server/domains/comments/comment.service') | ||
|
|
||
| const result = await createComment( | ||
| { | ||
| postId, |
Comment on lines
+34
to
+38
| if (!principalRecord) { | ||
| const [created] = await db | ||
| .insert(principal) | ||
| .values({ | ||
| id: generateId('principal'), |
Comment on lines
+13
to
17
| const { listBoardsWithDetails } = | ||
| await import('@/lib/server/domains/boards/board.service') | ||
|
|
||
| const boards = await listBoardsWithDetails() | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a public-facing end-user API surface.