-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: db migration #97
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 1 Skipped Deployment
|
WalkthroughThis pull request introduces significant changes to the blog application's architecture, focusing on post retrieval, rendering, and database schema. The modifications shift from file-based post management to a Supabase-driven approach, updating how posts are fetched, displayed, and metadata is generated. Key changes include introducing new API functions for post retrieval, modifying component props, and updating the database schema to support more flexible post categorization. Changes
Sequence DiagramsequenceDiagram
participant Client
participant PostAPI
participant Supabase
Client->>PostAPI: Request posts
PostAPI->>Supabase: Query posts
Supabase-->>PostAPI: Return post data
PostAPI-->>Client: Filtered and sorted posts
Client->>PostAPI: Request specific post
PostAPI->>Supabase: Query post by slug
Supabase-->>PostAPI: Return post details
PostAPI-->>Client: Post metadata and content
Possibly related PRs
Suggested Labels
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (6)
apps/blog/src/entities/post/api/getAllPosts.ts (1)
9-11
: Enhance error handling with contextThe current error handling loses context about what operation failed.
if (error) { - throw error; + throw new Error(`Failed to fetch posts: ${error.message}`); }apps/blog/app/sitemap.ts (1)
11-14
: Use actual lastModified datesThe sitemap uses the current date for all entries instead of actual post modification dates.
const postUrls = posts.map((post) => ({ url: `${BASE_SITE_URL}${ROUTES.postDetail([post.category, post.slug])}`, - lastModified: new Date(), + lastModified: new Date(post.updated_at || post.created_at), }));packages/database/src/server.ts (2)
8-8
: Document the implications of missing cookieStoreThe function now accepts an optional cookieStore, but the implications of it being undefined are not documented.
+ /** + * Creates a Supabase client for server-side operations. + * @param cookieStore - Optional cookie store. If not provided, cookie-based operations will be no-ops. + * @param admin - Whether to create an admin client with elevated privileges. + * @returns A configured Supabase client instance + */ export const createServerSupabaseClient = async (cookieStore?: Awaited<ReturnType<typeof cookies>>, admin = false) => {
Line range hint
18-25
: Improve error handling in cookie operationsThe current implementation silently catches errors in cookie operations. Consider logging these errors for debugging purposes.
set(name: string, value: string, options: CookieOptions) { try { cookieStore?.set({ name, value, ...options }); } catch (error) { - // The `set` method was called from a Server Component. - // This can be ignored if you have middleware refreshing - // user sessions. + console.debug( + 'Cookie set operation failed (expected in Server Components):', + error instanceof Error ? error.message : 'Unknown error' + ); } }, remove(name: string, options: CookieOptions) { try { cookieStore?.set({ name, value: "", ...options }); } catch (error) { - // The `delete` method was called from a Server Component. - // This can be ignored if you have middleware refreshing - // user sessions. + console.debug( + 'Cookie remove operation failed (expected in Server Components):', + error instanceof Error ? error.message : 'Unknown error' + ); } },Also applies to: 27-34
apps/blog/app/page.tsx (1)
36-44
: Consistent date formatting across the application.The date formatting uses a specific format (
yyyy.MM.dd. HH:mm
). Consider extracting this to a constant or utility function for consistency.+const POST_DATE_FORMAT = "yyyy.MM.dd. HH:mm"; +const formatPostDate = (date: string) => format(parseISO(date), POST_DATE_FORMAT); <PostCard key={post.title} title={post.title} category={post.category} description={post.description} href={ROUTES.postDetail([post.category, post.slug])} authorNickname={AUTHOR_NICKNAME} - date={format(parseISO(post.release_date), "yyyy.MM.dd. HH:mm")} + date={formatPostDate(post.release_date)} />apps/blog/app/(blog)/posts/[...slug]/page.tsx (1)
71-71
: URL structure change requires redirect strategy.The URL structure has changed to include categories. Consider:
- Adding redirects for old URL format
- Updating sitemap generation
- Implementing URL migration strategy
Also applies to: 79-79
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
apps/blog/app/(blog)/posts/[...slug]/page.tsx
(3 hunks)apps/blog/app/page.tsx
(2 hunks)apps/blog/app/sitemap.ts
(1 hunks)apps/blog/package.json
(2 hunks)apps/blog/posts/frontend/single-flight.mdx
(1 hunks)apps/blog/src/entities/post/api/getAllPosts.ts
(1 hunks)apps/blog/src/entities/post/api/getPostBySlug.ts
(1 hunks)apps/blog/src/entities/post/model/post-service.test.ts
(0 hunks)apps/blog/src/entities/post/model/post.model.ts
(0 hunks)apps/blog/src/entities/post/model/post.service.ts
(0 hunks)apps/blog/src/entities/post/ui/post/PostCard.tsx
(2 hunks)apps/blog/src/widgets/PostRecommend.tsx
(3 hunks)packages/database/src/server.ts
(2 hunks)packages/database/src/typesDb.ts
(2 hunks)
💤 Files with no reviewable changes (3)
- apps/blog/src/entities/post/model/post-service.test.ts
- apps/blog/src/entities/post/model/post.service.ts
- apps/blog/src/entities/post/model/post.model.ts
✅ Files skipped from review due to trivial changes (1)
- apps/blog/posts/frontend/single-flight.mdx
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Build and Test
🔇 Additional comments (8)
apps/blog/src/widgets/PostRecommend.tsx (2)
22-22
: Improved type safety with generic constraints.Good improvements:
- URL construction now uses slug instead of filePath, aligning with database-driven approach
- Generic type constraint ensures type safety while maintaining flexibility
Also applies to: 36-38
4-5
: Cookie handling implementation needs verification.The addition of cookie-based post retrieval suggests potential user-specific content filtering. Verify if this is intentional and if proper security measures are in place.
Also applies to: 10-11
✅ Verification successful
Cookie implementation verified as secure and appropriate
The cookie usage is part of Supabase's authentication system for maintaining server-side sessions. It follows security best practices and doesn't involve any sensitive content filtering.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for cookie usage patterns and security measures rg -A 3 "cookies\(\)" . ast-grep --pattern 'getAllPosts($cookieStore)'Length of output: 953
Script:
#!/bin/bash # Check getAllPosts implementation and related security measures ast-grep --pattern 'export $_ getAllPosts($_) { $$$ }' # Check for any security-related utilities used with cookies rg -A 5 "createServerSupabaseClient|createServerSupabaseAdminClient" .Length of output: 3783
apps/blog/src/entities/post/ui/post/PostCard.tsx (2)
5-10
: Props restructuring improves component reusability.The shift from a single post object to individual props:
- Provides better type safety
- Makes the component more reusable
- Aligns with database schema
14-16
: Verify prop usage consistency across the application.The component now uses granular props instead of a post object. Ensure all component instances are updated accordingly.
Also applies to: 24-24, 34-34, 37-37, 40-40
✅ Verification successful
Props usage is consistent across the application
The PostCard component's granular props pattern is consistently implemented. The only usage in
apps/blog/app/page.tsx
correctly provides all required props individually, with no instances of passing post objects directly.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Find all PostCard usages and verify prop pattern ast-grep --pattern 'PostCard $props'Length of output: 38
Script:
#!/bin/bash # Find PostCard usages with various patterns echo "=== Direct PostCard references ===" rg -l "PostCard" --type tsx --type jsx echo -e "\n=== PostCard prop patterns ===" rg "<PostCard.*?>" -U --type tsx --type jsx echo -e "\n=== Looking for potential post object spread ===" rg "<PostCard.*?\{\.\.\..*?\}" -U --type tsx --type jsxLength of output: 495
Script:
#!/bin/bash echo "=== Finding PostCard component definition and usages ===" cd apps/blog && find . -type f -exec grep -l "PostCard" {} \; echo -e "\n=== Checking PostCard usage patterns ===" cd apps/blog && find . -type f -exec grep -A 2 "<PostCard" {} \; echo -e "\n=== Looking for spread operator usage ===" cd apps/blog && find . -type f -exec grep -A 2 "<PostCard.*{....*}" {} \;Length of output: 692
Script:
#!/bin/bash echo "=== Checking PostCard usage in page.tsx ===" cat apps/blog/app/page.tsx echo -e "\n=== Checking PostCard component definition ===" cat apps/blog/src/entities/post/ui/post/PostCard.tsxLength of output: 3997
apps/blog/app/page.tsx (1)
13-16
: Review post filtering logic for edge cases.The post filtering logic includes:
- Authority check (
viewer
)- Date comparison
Consider handling edge cases:
- Invalid dates
- Missing authority values
✅ Verification successful
Post filtering implementation is properly typed and safe ✅
The current implementation is correct as:
- Database schema enforces non-null constraints for both
authority
andrelease_date
authority
is restricted to a valid enum type ("admin" | "editor" | "viewer")release_date
is guaranteed to be a valid ISO string🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for null/undefined handling in post filtering ast-grep --pattern 'post.authority' ast-grep --pattern 'parseISO($date)'Length of output: 203
Script:
#!/bin/bash # Find type definitions and implementations ast-grep --pattern 'type Post' ast-grep --pattern 'interface Post' fd getAllPosts --exec cat {} rg "release_date.*null|authority.*null" -A 2Length of output: 566
Script:
#!/bin/bash # Look for type definitions and schema fd -e ts -e tsx -e sql | xargs rg -l "Post|posts" --ignore-case rg "create table.*posts" -A 10 rg "from.*posts.*where" -A 2Length of output: 891
Script:
#!/bin/bash # Check database types and post filtering implementations cat packages/database/src/typesDb.ts rg "post\.authority.*===.*viewer" -A 2 rg "parseISO.*release_date" -A 2Length of output: 9534
apps/blog/app/(blog)/posts/[...slug]/page.tsx (1)
25-27
: Verify slug handling for all possible scenarios.The new slug handling:
- Uses
last()
with fallback- Retrieves post by slug
Consider handling:
- Invalid slug formats
- Non-existent slugs
✅ Verification successful
Slug handling implementation is robust and secure
The implementation properly handles all scenarios:
- Valid slugs are statically generated with [category, slug] structure
- Invalid formats fall back to empty string via
last()
- Non-existent slugs trigger proper error handling with redirect
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check slug handling patterns rg -A 3 "getPostBySlug" . ast-grep --pattern 'last($slug)'Length of output: 1634
Script:
#!/bin/bash # Check static params and last() implementation rg -A 5 "generateStaticParams" ./apps/blog/app/(blog)/posts/ ast-grep --pattern 'function last' rg -l "from.*es-toolkit"Length of output: 243
Script:
#!/bin/bash # Search with properly escaped paths and broader patterns rg -A 5 "generateStaticParams" "apps/blog/app" rg "import.*last.*from" . rg "export.*function.*last" .Length of output: 626
packages/database/src/typesDb.ts (1)
21-58
: Consider adding indices for performance optimization.The comment table schema looks well-structured with appropriate fields and relationships. However, consider adding indices for frequently queried fields to optimize performance.
Recommended indices:
post_id
(already covered by foreign key)created_at
(for chronological sorting)(post_id, created_at)
(for fetching latest comments per post)Also verify:
- Do we need a composite index on
(post_id, is_deleted)
for efficient filtering of non-deleted comments?- Should we add a constraint to prevent empty content?
apps/blog/package.json (1)
44-45
: Verify compatibility of updated dependencies.The changes look reasonable, but please verify:
next
update from 15.1.3 to 15.1.6: Check changelog for breaking changes- New
shiki
dependency: Ensure it's the recommended version for syntax highlightingAlso applies to: 73-73, 81-81
✅ Verification successful
Dependencies update verified and safe to proceed
The Next.js update (15.1.3 to 15.1.6) contains only bug fixes with no breaking changes. Shiki is properly integrated for syntax highlighting in the MDX components.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for Next.js version compatibility curl -s https://api.github.com/repos/vercel/next.js/releases | jq -r '.[] | select(.tag_name | contains("v15.1")) | {tag_name, body}' # Verify if shiki is used in the codebase rg -A 5 "shiki|highlight"Length of output: 10202
|
||
export const getAllPosts = async (cookieStore?: Awaited<ReturnType<typeof cookies>>) => { | ||
const supabase = await createServerSupabaseClient(cookieStore); | ||
const { data, error } = await supabase.from("posts").select("*"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding pagination and column selection
The current implementation fetches all posts and all columns at once, which could lead to performance issues as the dataset grows.
Consider:
- Adding pagination parameters
- Explicitly selecting only needed columns
- const { data, error } = await supabase.from("posts").select("*");
+ const { data, error } = await supabase
+ .from("posts")
+ .select('id, title, slug, category, created_at, updated_at')
+ .order('created_at', { ascending: false })
+ .range(0, 9);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const { data, error } = await supabase.from("posts").select("*"); | |
const { data, error } = await supabase | |
.from("posts") | |
.select('id, title, slug, category, created_at, updated_at') | |
.order('created_at', { ascending: false }) | |
.range(0, 9); |
export const getPostBySlug = async (slug: string, cookieStore?: Awaited<ReturnType<typeof cookies>>) => { | ||
const supabase = await createServerSupabaseClient(cookieStore); | ||
const { data, error } = await supabase.from("posts").select("*").eq("slug", slug).single(); | ||
|
||
if (error) { | ||
throw error; | ||
} | ||
|
||
return data; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add type safety and input validation
The function lacks type safety for the returned data and input validation for the slug parameter.
+ import { type Post } from './types';
+
+ const isValidSlug = (slug: string) => {
+ return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug);
+ };
+
- export const getPostBySlug = async (slug: string, cookieStore?: Awaited<ReturnType<typeof cookies>>) => {
+ export const getPostBySlug = async (
+ slug: string,
+ cookieStore?: Awaited<ReturnType<typeof cookies>>
+ ): Promise<Post> => {
+ if (!isValidSlug(slug)) {
+ throw new Error('Invalid slug format');
+ }
+
const supabase = await createServerSupabaseClient(cookieStore);
- const { data, error } = await supabase.from("posts").select("*").eq("slug", slug).single();
+ const { data, error } = await supabase
+ .from("posts")
+ .select('id, title, slug, category, content, created_at, updated_at')
+ .eq("slug", slug)
+ .single();
if (error) {
- throw error;
+ throw new Error(`Failed to fetch post with slug ${slug}: ${error.message}`);
}
return data;
};
Committable suggestion skipped: line range outside the PR's diff.
const cookieStore = await cookies(); | ||
const posts = await getAllPosts(cookieStore); | ||
|
||
const postUrls = posts.map((post) => ({ | ||
url: `${BASE_SITE_URL}${ROUTES.postDetail(post.filePath)}`, | ||
url: `${BASE_SITE_URL}${ROUTES.postDetail([post.category, post.slug])}`, | ||
lastModified: new Date(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling and caching strategy
The sitemap generation lacks error handling and could benefit from caching to improve performance.
+ import { cache } from 'react';
+
+ const CACHE_REVALIDATE_SECONDS = 3600; // 1 hour
+
+ const getCachedPosts = cache(async (cookieStore: Awaited<ReturnType<typeof cookies>>) => {
+ try {
+ return await getAllPosts(cookieStore);
+ } catch (error) {
+ console.error('Failed to generate sitemap:', error);
+ return [];
+ }
+ });
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const cookieStore = await cookies();
- const posts = await getAllPosts(cookieStore);
+ const posts = await getCachedPosts(cookieStore);
Committable suggestion skipped: line range outside the PR's diff.
@@ -83,34 +121,37 @@ export type Database = { | |||
posts: { | |||
Row: { | |||
authority: Database["public"]["Enums"]["role"]; | |||
can_view: boolean; | |||
category: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolve schema inconsistency between direct category and post_categories table.
There appears to be a conflict in the database design:
- Posts table now has a direct
category
field - Existing
post_categories
table suggests a many-to-many relationship
This could lead to data inconsistencies. Consider either:
- Removing the
post_categories
table if multiple categories are no longer needed - Removing the direct
category
field if multiple categories should be maintained
Also applies to: 136-136, 148-148
💡 Codebase verification
Make the slug
field required to prevent URL generation issues
The slug
field is used extensively in URL generation, routing, and sitemap creation. Making it optional could lead to broken links. The category
field usage is appropriate, and removing can_view
appears safe as it's not referenced in the codebase.
🔗 Analysis chain
Verify the impact of schema changes on existing functionality.
Several concerns regarding the schema changes:
- Removing
can_view
might break existing access control. How is visibility now managed? category
as an unconstrained string could lead to inconsistencies. Consider using an enum or reference table.slug
should probably be required and unique for URL generation.
Let's verify the impact:
Also applies to: 130-130, 136-136, 142-142, 148-148, 154-154
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for existing usage of can_view
rg -A 5 "can_view"
# Check if slugs are used in routing
rg -A 5 "params.*slug|slug.*params"
# Check category usage patterns
rg -A 5 "post.*category|category.*post"
Length of output: 3115
🥷 제목
구현한 내용
Summary by CodeRabbit
Release Notes
New Features
Improvements
Dependency Updates
Database Changes