Add published collections: public directory, homepage discovery, password bypass - #26
Conversation
…word bypass Collections can now be marked published independently of shared, making them listable on a new /published-collections search page and sampled on the guest homepage, with password protection bypassed while published. The collection form's password field now toggles visibility based on shared/published state, and owners can see the share link and a "Published" badge for published-only collections too. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 39 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThis change adds published collections with database fields, validated CRUD support, public access rules, listing and search APIs, random selection, collection cards, form controls, homepage integration, and localized navigation. ChangesPublished collections
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant Guest
participant Homepage
participant usePublishedCollections
participant PublishedAPI
participant Database
Guest->>Homepage: Open homepage
Homepage->>usePublishedCollections: Load published collections
usePublishedCollections->>PublishedAPI: GET /api/shared
PublishedAPI->>Database: Query published collections
Database-->>PublishedAPI: Return public metadata
PublishedAPI-->>usePublishedCollections: Return collection list
usePublishedCollections-->>Homepage: Select and render random cards
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
server/db/migrations/0002_needy_calypso.sql (1)
1-2: 🗄️ Data Integrity & Integration | 🔵 TrivialApply this migration before deploying the new API contract.
The schema and API routes now read and write
collections.publishedandcollections.image_url. If0002_needy_calypsois not applied in an environment, collection requests will fail with missing-column errors. Apply and verify the migration in shared development and every deployment target before enabling the new code.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/db/migrations/0002_needy_calypso.sql` around lines 1 - 2, Apply and verify the 0002_needy_calypso migration, ensuring the collections table contains the published and image_url columns, before deploying or enabling the API changes in shared development and every deployment target.app/shared/api/use-published-collections.ts (1)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winImport
PublishedCollectionthrough the shared API barrel.Replace the relative import with
~/shared/api. This keeps shared API imports on the public segment boundary.Proposed fix
-import type { PublishedCollection } from "./shared-collections"; +import type { PublishedCollection } from "~/shared/api";As per coding guidelines, “Import shared modules through their segment barrels (
~/shared/api,~/shared/ui, or~/shared/lib) rather than deep paths.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/shared/api/use-published-collections.ts` at line 1, Update the PublishedCollection import in use-published-collections.ts to use the shared API barrel at ~/shared/api instead of the relative shared-collections path, preserving the existing type-only import.Source: Coding guidelines
app/features/published-collections/model/use-random-published-collections.ts (1)
4-7: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winReplace the sort-based shuffle with Fisher-Yates.
shuffled.sort(() => Math.random() - 0.5)does not produce a uniform random shuffle. The bias depends on the sort algorithm's comparison pattern, so some items are more likely than others to land in the firstcountslots thatsliceselects. This affects which collections actually get shown, not just their order.Use Fisher-Yates for a correct, unbiased shuffle.
♻️ Proposed fix: Fisher-Yates shuffle
function pickRandom<T>(list: T[], count: number): T[] { - const shuffled = [...list].sort(() => Math.random() - 0.5); + const shuffled = [...list]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } return shuffled.slice(0, count); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/features/published-collections/model/use-random-published-collections.ts` around lines 4 - 7, Replace the sort-based shuffle in pickRandom with an in-place Fisher-Yates shuffle on the copied list, selecting a random index from each remaining range before swapping. Preserve the existing behavior of returning up to count items from the shuffled copy without mutating the input list.app/pages/index.vue (1)
24-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove the guest-only fetch/pick logic into a feature composable.
This branch decides whether to fetch published collections, and computes the random pick, directly in the page. This is feature logic, not page composition.
Extract this into a composable in the
published-collectionsfeature model (for exampleuseGuestHomepageCollections()), exported through the feature'sindex.ts. Have the page call that composable and bind its result in the template.As per coding guidelines, "Keep route pages thin: compose exported feature components rather than containing feature logic."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/pages/index.vue` around lines 24 - 38, Extract the guest-only fetch and random selection from the page’s randomPublishedCollections block into a published-collections feature composable such as useGuestHomepageCollections(), preserving the awaited source loading and unauthenticated-only behavior. Export the composable through the feature index.ts, then have the page call it and bind the returned collections to the existing template rendering.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/api/collections/`[id].patch.ts:
- Around line 73-74: Update the partial update payload around
parsed.data.imageUrl so an omitted imageUrl remains undefined and does not
overwrite the stored value; preserve explicit null as a deliberate clear, and
only include the imageUrl column when the property is present.
- Around line 17-23: Align the PATCH contract across the validation schema,
CollectionInput, updateCollection, and the PATCH handler: allow partial payloads
without published, and ensure imageUrl is set to null only when the request
explicitly includes that field, preserving the existing value when omitted.
---
Nitpick comments:
In
`@app/features/published-collections/model/use-random-published-collections.ts`:
- Around line 4-7: Replace the sort-based shuffle in pickRandom with an in-place
Fisher-Yates shuffle on the copied list, selecting a random index from each
remaining range before swapping. Preserve the existing behavior of returning up
to count items from the shuffled copy without mutating the input list.
In `@app/pages/index.vue`:
- Around line 24-38: Extract the guest-only fetch and random selection from the
page’s randomPublishedCollections block into a published-collections feature
composable such as useGuestHomepageCollections(), preserving the awaited source
loading and unauthenticated-only behavior. Export the composable through the
feature index.ts, then have the page call it and bind the returned collections
to the existing template rendering.
In `@app/shared/api/use-published-collections.ts`:
- Line 1: Update the PublishedCollection import in use-published-collections.ts
to use the shared API barrel at ~/shared/api instead of the relative
shared-collections path, preserving the existing type-only import.
In `@server/db/migrations/0002_needy_calypso.sql`:
- Around line 1-2: Apply and verify the 0002_needy_calypso migration, ensuring
the collections table contains the published and image_url columns, before
deploying or enabling the API changes in shared development and every deployment
target.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 72dde8ca-8478-475c-a165-4d66deceabe9
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (33)
app/features/collection-form/model/__tests__/use-collection-form.test.tsapp/features/collection-form/model/use-collection-form.tsapp/features/collection-form/ui/collection-form.vueapp/features/collections/ui/collections-list.vueapp/features/published-collections/index.tsapp/features/published-collections/model/__tests__/use-published-collections-search.test.tsapp/features/published-collections/model/__tests__/use-random-published-collections.test.tsapp/features/published-collections/model/use-published-collections-search.tsapp/features/published-collections/model/use-random-published-collections.tsapp/features/published-collections/ui/published-collection-card.vueapp/pages/collections/[id]/index.vueapp/pages/index.vueapp/pages/published-collections.vueapp/shared/api/collections.tsapp/shared/api/index.tsapp/shared/api/shared-collections.tsapp/shared/api/use-published-collections.tsapp/shared/testing/mocks/use-state.tsapp/ui/app-header/app-header.vuei18n/locales/en.jsoni18n/locales/ru.jsonpackage.jsonserver/api/collections/[id].patch.tsserver/api/collections/index.post.tsserver/api/shared/[slug].get.tsserver/api/shared/[slug]/links/index.get.tsserver/api/shared/[slug]/unlock.post.tsserver/api/shared/index.get.tsserver/db/migrations/0002_needy_calypso.sqlserver/db/migrations/meta/0002_snapshot.jsonserver/db/migrations/meta/_journal.jsonserver/db/schema.tsserver/utils/shared-collection-links.ts
| password: z.string().trim().max(255).optional().nullable(), | ||
| published: z.boolean(), | ||
| imageUrl: z | ||
| .url({ protocol: /^https?$/ }) | ||
| .max(2048) | ||
| .optional() | ||
| .nullable() |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 5 'PATCH|/api/collections|published|imageUrl' \
app server \
--glob '*.ts' --glob '*.tsx' --glob '*.vue'Repository: ebulgakov/linkfolio
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- candidate files ---\n'
git ls-files | rg '(^app/shared/api/collections.ts$|^app/features/collections/|^app/shared/(components|testing)/|^server/api/collections/\[id\]\.patch\.ts$|^server/api/collections/index\.post\.ts$)' || true
printf '\n--- server PATCH ---\n'
cat -n server/api/collections/[id].patch.ts | sed -n '1,130p'
printf '\n--- collections api shared ---\n'
cat -n app/shared/api/collections.ts | sed -n '1,120p'
printf '\n--- likely collection form files ---\n'
git ls-files app/features/collections | sortRepository: ebulgakov/linkfolio
Length of output: 248
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^app/shared/api/collections.ts$|^app/features/collections/|^app/shared/(components|testing)/|^server/api/collections/\[id\]\.patch\.ts$|^server/api/collections/index\.post\.ts$)' || true
printf '%s\n' ''
printf '%s\n' '--- server PATCH ---'
cat -n server/api/collections/[id].patch.ts | sed -n '1,140p'
printf '%s\n' ''
printf '%s\n' '--- collections api shared ---'
cat -n app/shared/api/collections.ts | sed -n '1,120p'
printf '%s\n' ''
printf '%s\n' '--- likely collection form files ---'
git ls-files app/features/collections | sortRepository: ebulgakov/linkfolio
Length of output: 9265
Align PATCH validation with the update contract.
updateCollection() and the PATCH handler are documented/implemented as PATCH routes, but published is required in both CollectionInput and server/api/collections/[id].patch.ts. Partial payloads without published fail before update. Make imageUrl optional in PATCH validation and only null it when the field is sent; if published remains PATCH-required, make it required in CollectionInput too.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@server/api/collections/`[id].patch.ts around lines 17 - 23, Align the PATCH
contract across the validation schema, CollectionInput, updateCollection, and
the PATCH handler: allow partial payloads without published, and ensure imageUrl
is set to null only when the request explicitly includes that field, preserving
the existing value when omitted.
- Preserve imageUrl on partial PATCH instead of clobbering it to null when omitted, matching the existing password preserve-if-omitted contract. - Replace the biased sort-based shuffle in pickRandom with Fisher-Yates. - Extract the guest homepage's published-collections fetch/pick into a useHomepagePublishedCollections() feature composable, keeping the page thin. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
published(boolean) andimageUrlfields to collections.publishedis independent ofshared: public access gate becomesshared OR publishedeverywhere a collection's reachability is checked, and password protection is bypassed whenever a collection is published (hasPassword = password !== null && !published)./published-collectionslisting all published collections with fuse.js-powered search over name/description; new public listing endpointGET /api/shared.useState) with a link to the full directory; nav link added to the app header.Publishedtoggle andImage URLfield; the password field's visibility is now tri-state (hidden by default → shown onceSharedis on → hidden again oncePublishedis on), and the stored password is preserved (not cleared) across toggles.Test plan
pnpm type-check,pnpm lint,pnpm testall green (171/171 tests, 15 new)/shared/:slugbypasses a stored password oncepublished=true, and re-enforces it once unpublished/published-collectionslists published collections, search filters by name and by description, empty-state renders for no matches0002_needy_calypsohas been applied to the shared dev database (additive, defaults tofalse/NULL, safe againstmain)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests