Skip to content
This repository was archived by the owner on Oct 8, 2025. It is now read-only.

supabase ghost integration#182

Open
losndu wants to merge 1 commit into
mainfrom
add-ghost-member
Open

supabase ghost integration#182
losndu wants to merge 1 commit into
mainfrom
add-ghost-member

Conversation

@losndu

@losndu losndu commented Mar 21, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Introduced backend support for comprehensive membership management, enabling creation, retrieval, updating, and deletion of members.
    • Added a dedicated API endpoint to process membership operations based on user actions.
    • Integrated membership synchronization into the OAuth callback, sign-in, and sign-up flows, ensuring seamless member management during authentication.
  • Bug Fixes

    • Corrected a typographical error in user-facing error messages.

@vercel

vercel Bot commented Mar 21, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
research ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 21, 2025 4:48am

@coderabbitai

coderabbitai Bot commented Mar 21, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This 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

File(s) Change Summary
src/.../ghost.service.ts Added async functions: createGhostMember, getGhostMember, updateGhostMember, and deleteGhostMember that interact with the Ghost API and include error handling.
src/.../api/ghost/+server.ts Introduced a new API endpoint with a POST handler to manage Ghost member CRUD operations using TypeScript interfaces and a switch-case action dispatcher.
src/routes/callback/+server.ts Added a GET handler for OAuth callbacks that exchanges auth codes for sessions, checks for existing ghost members, and creates new ones if absent, followed by redirection.
src/routes/signin/+page.svelte
src/routes/signup/+page.svelte
Integrated ghost membership synchronization in signin and signup flows with logic to retrieve or create ghost members and updated error message text.

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
Loading
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
Loading

Possibly related PRs

  • Search #167: Implements ghost member functions similar to createGhostMember and getGhostMember, showing a strong code-level connection.
  • update share #159: Involves management of ghost members with creation and retrieval functionalities that align with this PR’s updates.
  • fixed issue #169: Modifies the ghost service by adding functions for member (and article) management, directly connecting with the changes here.

Poem

I'm a rabbit, hopping with glee,
Ghost members now dance merrily.
CRUD functions sprout in the code,
API flows lighten our load.
OAuth redirects with a joyful spin,
Signing in and up, let the magic begin!
🐇🌟

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/routes/api/ghost/+server.ts

Oops! Something went wrong! :(

ESLint: 9.12.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@eslint/js' imported from /eslint.config.js
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

src/lib/services/ghost.service.ts

Oops! Something went wrong! :(

ESLint: 9.12.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@eslint/js' imported from /eslint.config.js
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

src/routes/callback/+server.ts

Oops! Something went wrong! :(

ESLint: 9.12.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@eslint/js' imported from /eslint.config.js
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

  • 2 others
✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4fecdf and 8983c57.

📒 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 null if 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 updateGhostMember function 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
fi

Length of output: 1487


API Endpoint Verification Complete

  • Confirmed that the API endpoint exists at /api/ghost in src/routes/api/ghost/+server.ts.
  • Verified that it handles all required actions: createMember, getMember, updateMember, and deleteMember.

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)

Comment thread src/routes/signin/+page.svelte
Comment on lines +6 to +37
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider optional chaining and confirm user consistency.

  1. 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) {
  2. 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.

Suggested change
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)

Comment thread src/routes/signup/+page.svelte
Comment thread src/routes/api/ghost/+server.ts
Comment thread src/routes/api/ghost/+server.ts
Comment thread src/lib/services/ghost.service.ts
Comment thread src/lib/services/ghost.service.ts
Comment thread src/lib/services/ghost.service.ts
Comment thread src/lib/services/ghost.service.ts
Comment thread src/lib/services/ghost.service.ts
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant