Skip to content

feat: public end-user API#47

Merged
BunsDev merged 17 commits into
mainfrom
feat/public-end-user-api
Jun 15, 2026
Merged

feat: public end-user API#47
BunsDev merged 17 commits into
mainfrom
feat/public-end-user-api

Conversation

@BunsDev

@BunsDev BunsDev commented Jun 15, 2026

Copy link
Copy Markdown
Member

Adds a public-facing end-user API surface.

Copilot AI review requested due to automatic review settings June 15, 2026 13:20
apple-techie and others added 17 commits June 15, 2026 08:21
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>
@BunsDev BunsDev force-pushed the feat/public-end-user-api branch from 1c96915 to 37f036e Compare June 15, 2026 13:22
@BunsDev BunsDev merged commit 995b569 into main Jun 15, 2026
@BunsDev BunsDev deleted the feat/public-end-user-api branch June 15, 2026 13:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 support publicOnly filtering.

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()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants