supabase ghost integration#182
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis PR adds comprehensive ghost member management across the application. New asynchronous functions for creating, retrieving, updating, and deleting Ghost members are introduced in the ghost service. A dedicated API endpoint has been built to handle CRUD operations for Ghost members, and OAuth callback processing has been implemented to integrate Ghost member creation when handling authentication. Additionally, both signin and signup pages now include logic to synchronize Ghost membership, along with minor corrections and enhanced error handling. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as /api/ghost
participant Ghost as Ghost Service
Client->>API: POST request with action & payload
API->>Ghost: Invoke create/get/update/delete function
Ghost-->>API: Return result or error
API-->>Client: Send response with status and data
sequenceDiagram
participant User
participant Callback as /callback
participant Supabase as Supabase Auth
participant Ghost as Ghost Service
User->>Callback: GET request with code & next URL
Callback->>Supabase: Exchange code for session
Supabase-->>Callback: Return session or error
Callback->>Ghost: getGhostMember(email)
alt Member not found
Callback->>Ghost: createGhostMember(email, name, subscribedToEmails)
end
Callback-->>User: Redirect to next URL or /reports
Possibly related PRs
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure. 🔧 ESLint
src/routes/api/ghost/+server.tsOops! Something went wrong! :( ESLint: 9.12.0 Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@eslint/js' imported from /eslint.config.js src/lib/services/ghost.service.tsOops! Something went wrong! :( ESLint: 9.12.0 Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@eslint/js' imported from /eslint.config.js src/routes/callback/+server.tsOops! Something went wrong! :( ESLint: 9.12.0 Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@eslint/js' imported from /eslint.config.js
✨ Finishing Touches
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. 🪧 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.
Actionable comments posted: 11
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
src/lib/services/ghost.service.ts(1 hunks)src/routes/api/ghost/+server.ts(1 hunks)src/routes/callback/+server.ts(1 hunks)src/routes/signin/+page.svelte(3 hunks)src/routes/signup/+page.svelte(2 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/routes/callback/+server.ts
[error] 18-18: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
src/lib/services/ghost.service.ts
[error] 94-94: This type annotation is trivially inferred from its initialization.
Safe fix: Remove the type annotation.
(lint/style/noInferrableTypes)
[error] 94-94: This type annotation is trivially inferred from its initialization.
Safe fix: Remove the type annotation.
(lint/style/noInferrableTypes)
src/routes/api/ghost/+server.ts
[error] 81-81: This type annotation is trivially inferred from its initialization.
Safe fix: Remove the type annotation.
(lint/style/noInferrableTypes)
[error] 82-82: This type annotation is trivially inferred from its initialization.
Safe fix: Remove the type annotation.
(lint/style/noInferrableTypes)
🔇 Additional comments (12)
src/routes/signin/+page.svelte (4)
13-13: New import for Ghost membership functions.This import is needed to synchronize membership status. Looks good and follows best practices for modularizing Ghost membership logic.
63-65: Check for synchronization failures.You already have a try/catch for
syncGhostMembership, which logs errors to the console. If membership sync is critical, consider handling potential failures more robustly (e.g., surfacing an error message to the user or retrying).
67-67: Redirect after membership sync.Awaiting
goto(redirectURL)ensures membership synchronization completes first. This is a solid approach, preventing race conditions or partial user states.
81-81: Spelling fix in error message.Thank you for correcting the typo from "occured" to "occurred." This improves clarity for end users.
src/routes/callback/+server.ts (1)
1-3: Import statements are organized well.The imports for Ghost integration and Supabase are straightforward and aligned with the new membership flow. No concerns here.
src/routes/signup/+page.svelte (2)
12-12: Import Ghost membership helpers.This import supports the creation and retrieval of Ghost members on user signup. The approach is consistent with the rest of the codebase.
111-111: Error message correction.Great job fixing the spelling to "occurred." This minor fix improves clarity for users.
src/routes/api/ghost/+server.ts (4)
1-3: Libraries and environment imports.The file correctly imports necessary modules and references the environment variables. No issues noted here.
5-36: Helpful interfaces for typed Ghost operations.Defining explicit interfaces (e.g.,
CreateMemberPayload,GetMemberPayload, etc.) increases clarity and reduces type confusion, especially when dealing with external services.
108-126: Graceful handling of "no member found."Your function returns
nullif no member is found, which is intuitive for callers. Ensure any usage in the rest of the codebase checks for null explicitly to avoid runtime errors.
128-150: Update function is concise.The
updateGhostMemberfunction updates the specified user data in Ghost. The code properly checks for HTTP errors and throws an exception if the request fails. Looks good.src/lib/services/ghost.service.ts (1)
94-204:Details
✅ Verification successful
Verify the API route for Ghost operations exists at '/api/ghost'.
All functions make requests to '/api/ghost', but without seeing the API implementation, it's important to ensure this endpoint exists and can handle all the specified actions.
🏁 Script executed:
#!/bin/bash # Check if the Ghost API endpoint exists fd --type file "+server.ts" src/routes/api/ghost # Check if the endpoint handles all required actions if [ -f "src/routes/api/ghost/+server.ts" ]; then echo "Checking API implementation for required actions..." rg -A 3 "case 'createMember'|case 'getMember'|case 'updateMember'|case 'deleteMember'" src/routes/api/ghost/+server.ts fiLength of output: 1487
API Endpoint Verification Complete
- Confirmed that the API endpoint exists at
/api/ghostinsrc/routes/api/ghost/+server.ts.- Verified that it handles all required actions:
createMember,getMember,updateMember, anddeleteMember.No further changes are necessary.
🧰 Tools
🪛 Biome (1.9.4)
[error] 94-94: This type annotation is trivially inferred from its initialization.
Safe fix: Remove the type annotation.
(lint/style/noInferrableTypes)
[error] 94-94: This type annotation is trivially inferred from its initialization.
Safe fix: Remove the type annotation.
(lint/style/noInferrableTypes)
| export const GET: RequestHandler = async ({ url, cookies }) => { | ||
| const code = url.searchParams.get('code'); | ||
| const next = url.searchParams.get('next') || '/reports'; | ||
|
|
||
| if (code) { | ||
| const { data, error } = await supabase.auth.exchangeCodeForSession(code); | ||
|
|
||
| if (error) { | ||
| console.error('Auth error:', error); | ||
| throw redirect(303, '/signin?error=auth'); | ||
| } | ||
|
|
||
| if (data.user && data.user.email) { | ||
| try { | ||
| const existingMember = await getGhostMember(data.user.email as string); | ||
|
|
||
| if (!existingMember) { | ||
| await createGhostMember( | ||
| data.user.email as string, | ||
| data.user.user_metadata?.full_name || '', | ||
| true | ||
| ); | ||
| console.log('Ghost member created for OAuth user'); | ||
| } | ||
| } catch (ghostError) { | ||
| console.error('Error with Ghost integration:', ghostError); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| throw redirect(303, next); | ||
| }; No newline at end of file |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Consider optional chaining and confirm user consistency.
- The check at line 18 uses
if (data.user && data.user.email). Using optional chaining can be more concise:- if (data.user && data.user.email) { + if (data.user?.email) {
- Confirm if the newly created Ghost member data should be returned or stored for additional post-creation actions. Presently, the code just logs the creation and proceeds.
- if (data.user && data.user.email) {
+ if (data.user?.email) {📝 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.
| export const GET: RequestHandler = async ({ url, cookies }) => { | |
| const code = url.searchParams.get('code'); | |
| const next = url.searchParams.get('next') || '/reports'; | |
| if (code) { | |
| const { data, error } = await supabase.auth.exchangeCodeForSession(code); | |
| if (error) { | |
| console.error('Auth error:', error); | |
| throw redirect(303, '/signin?error=auth'); | |
| } | |
| if (data.user && data.user.email) { | |
| try { | |
| const existingMember = await getGhostMember(data.user.email as string); | |
| if (!existingMember) { | |
| await createGhostMember( | |
| data.user.email as string, | |
| data.user.user_metadata?.full_name || '', | |
| true | |
| ); | |
| console.log('Ghost member created for OAuth user'); | |
| } | |
| } catch (ghostError) { | |
| console.error('Error with Ghost integration:', ghostError); | |
| } | |
| } | |
| } | |
| throw redirect(303, next); | |
| }; | |
| export const GET: RequestHandler = async ({ url, cookies }) => { | |
| const code = url.searchParams.get('code'); | |
| const next = url.searchParams.get('next') || '/reports'; | |
| if (code) { | |
| const { data, error } = await supabase.auth.exchangeCodeForSession(code); | |
| if (error) { | |
| console.error('Auth error:', error); | |
| throw redirect(303, '/signin?error=auth'); | |
| } | |
| - if (data.user && data.user.email) { | |
| + if (data.user?.email) { | |
| try { | |
| const existingMember = await getGhostMember(data.user.email as string); | |
| if (!existingMember) { | |
| await createGhostMember( | |
| data.user.email as string, | |
| data.user.user_metadata?.full_name || '', | |
| true | |
| ); | |
| console.log('Ghost member created for OAuth user'); | |
| } | |
| } catch (ghostError) { | |
| console.error('Error with Ghost integration:', ghostError); | |
| } | |
| } | |
| } | |
| throw redirect(303, next); | |
| }; |
🧰 Tools
🪛 Biome (1.9.4)
[error] 18-18: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
Summary by CodeRabbit
New Features
Bug Fixes