Skip to content

Refactor profile routes for Supabase lookups#45

Open
numbpill3d wants to merge 1 commit intomainfrom
codex/update-profile.js-with-supabase-queries
Open

Refactor profile routes for Supabase lookups#45
numbpill3d wants to merge 1 commit intomainfrom
codex/update-profile.js-with-supabase-queries

Conversation

@numbpill3d
Copy link
Collaborator

@numbpill3d numbpill3d commented Jun 8, 2025

Summary

  • query streetpass visitors through Supabase
  • record visits using Supabase when viewing profiles
  • update emotes via Supabase
  • persist profile HTML/CSS using findByIdAndUpdate

Testing

  • npm test (fails: Cannot find module '../../../server/models/Item' and missing Supabase env vars)

https://chatgpt.com/codex/tasks/task_e_68450e66e9b4832fb9d2cbcb719e0875

Summary by Sourcery

Refactor profile routes to use Supabase for streetpass visitor management and streamline profile content updates

Enhancements:

  • Replace MongoDB-based streetpass visitor lookups and recordings with Supabase queries and abstractions
  • Centralize emote updates and visit rate limiting through Supabase via the Streetpass model
  • Simplify profile HTML/CSS persistence by using atomic findByIdAndUpdate calls

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jun 8, 2025

Reviewer's Guide

Refactors profile route handlers to integrate Supabase for streetpass visitor queries, visit recording and emote updates, and streamlines profile HTML/CSS persistence via findByIdAndUpdate.

Sequence Diagram: Fetching Own Profile's Visitors via Supabase

sequenceDiagram
    actor Client
    participant App as Express App (/profile)
    participant User_Model as User Model
    participant Streetpass_Model as Streetpass Model
    participant DB_Supabase as Supabase

    Client->>+App: GET /profile (own)
    App->>App: ensureAuthenticated (verifies req.user)
    App->>+User_Model: findById(req.user.id)
    User_Model-->>-App: User object
    App->>+Streetpass_Model: getVisitors(user.id, 10)
    Streetpass_Model->>+DB_Supabase: Query streetpass_visits table (SELECT ... WHERE profile_id = user.id)
    DB_Supabase-->>-Streetpass_Model: Visitor data
    Streetpass_Model-->>-App: Formatted visitors list
    App->>Client: Render profile page with visitors
Loading

ER Diagram: User and Streetpass Visits (Supabase Table)

erDiagram
    User {
        int id PK "User ID"
        string username
        string profileHtml
        string profileCss
        bool streetpassEnabled
    }

    streetpass_visits {
        int id PK "Visit ID"
        int visitor_id FK "Visitor (User ID)"
        int profile_id FK "Visited Profile (User ID)"
        datetime visited_at
        string emote
    }

    User ||--o{ streetpass_visits : "visited_by (visitor_id)"
    User ||--o{ streetpass_visits : "has_visitors (profile_id)"
Loading

Class Diagram: User and Streetpass Model Changes

classDiagram
    class User {
        +id: string
        +username: string
        +profileHtml: string
        +profileCss: string
        +streetpassEnabled: boolean
        +findByIdAndUpdate(id, data): Promise~User~
        <<Mongoose Model>>
        Note: Streetpass visitor data now handled by Streetpass model and Supabase
    }

    class Streetpass {
        <<Model>>
        +getVisitors(profileId: string, limit: int): Promise~Array~Visitor~
        +recordVisit(visitorId: string, profileId: string, emote: string): Promise~void~
        +updateEmote(visitId: string, emote: string): Promise~void~
        Note: Interacts with Supabase 'streetpass_visits' table
    }

    User ..> Streetpass : Uses for streetpass logic
Loading

File-Level Changes

Change Details Files
Integrate Supabase for streetpass visitor retrieval and recording
  • Imported Streetpass model and supabase client
  • Replaced Mongoose population with Streetpass.getVisitors
  • Queried Supabase for existing visits before recording
  • Used Streetpass.recordVisit instead of manual push/save
  • Added streetpassEnabled check before recording visits
server/routes/profile.js
Handle emote updates through Supabase
  • Removed in-memory emote update logic
  • Queried Supabase for the visit record ID
  • Invoked Streetpass.updateEmote with the visit ID and new emote
server/routes/profile.js
Streamline profile HTML/CSS persistence
  • Replaced findById+save with findByIdAndUpdate for HTML
  • Applied same findByIdAndUpdate approach for CSS
server/routes/profile.js
Standardize user identifier usage
  • Replaced req.user._id/profileUser._id with req.user.id/profileUser.id in comparisons
server/routes/profile.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Comment on lines 18 to 28
// User's own profile page
router.get('/', ensureAuthenticated, async (req, res) => {
try {
// Find the user with their streetpass visitors populated
const user = await User.findById(req.user._id)
.populate({
path: 'streetpassVisitors.user',
select: 'username customGlyph avatar'
});

// Get recent visitors
const visitors = user.streetpassVisitors
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, 10);
// Find the user by ID
const user = await User.findById(req.user.id);

// Get recent visitors from Supabase
const visitors = await Streetpass.getVisitors(user.id, 10);

// User's items in the Scrapyard
const userItems = await ScrapyardItem.find({ creator: user._id })
Copy link
Contributor

Choose a reason for hiding this comment

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

Error Handling Improvement

The asynchronous operations within the profile page route handler lack comprehensive error handling for each individual database query. This could lead to unhandled promise rejections if one of the queries fails.

Recommendation:
Wrap each asynchronous operation (User.findById, Streetpass.getVisitors, ScrapyardItem.find) in separate try-catch blocks or chain them with proper error handling to ensure that all errors are caught and handled appropriately, thus improving the robustness of the application.

Comment on lines 254 to 282
try {
const { emote } = req.body;
const profileUser = await User.findOne({ username: req.params.username });

if (!profileUser) {
return res.status(404).json({ error: 'User not found' });
}

// Find the visitor entry and update the emote
const visitorIndex = profileUser.streetpassVisitors.findIndex(
v => v.user.toString() === req.user._id.toString()
);

if (visitorIndex !== -1) {
profileUser.streetpassVisitors[visitorIndex].emote = emote;
await profileUser.save();
return res.json({ success: true });
} else {

// Look up the visit record in Supabase
const { data: visit, error } = await supabase
.from('streetpass_visits')
.select('id')
.eq('visitor_id', req.user.id)
.eq('profile_id', profileUser.id)
.single();

if (error && error.code !== 'PGRST116') {
throw error;
}

if (!visit) {
return res.status(400).json({ error: 'No visit found' });
}

await Streetpass.updateEmote(visit.id, emote);
return res.json({ success: true });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Server error' });
Copy link
Contributor

Choose a reason for hiding this comment

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

Security and Error Handling Concerns

The route handling the update of the 'streetpass emote' lacks input validation, which could lead to security vulnerabilities such as injection attacks. Additionally, the error handling could be more descriptive to aid in debugging and user feedback.

Recommendation:

  1. Validate the emote input to ensure it meets expected criteria (e.g., length, character type) before processing it.
  2. Enhance error messages to provide more context about the failure, helping in quicker resolution and better user experience.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @numbpill3d - I've reviewed your changes - here's some feedback:

  • Extract the duplicated visitor-fetching logic in both profile routes into a single helper in Streetpass to reduce code repetition and improve maintainability.
  • Ensure all Supabase queries are properly handled with try/catch or explicit error checks to avoid unhandled rejections, ideally centralizing error handling for clarity.
  • Consider using a single upsert or transaction for recording a visit and updating its emote in Supabase to guarantee atomicity and prevent race conditions between separate calls.
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

});

await profileUser.save();
const { data: existingVisit, error } = await supabase
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Handle and rename the Supabase error

Consider checking and logging the error value to avoid silently ignoring Supabase errors. Using .maybeSingle() can help handle 'no rows found' without throwing, and renaming error to fetchError improves clarity.

.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, 10);
// Find the user by ID
const user = await User.findById(req.user.id);
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: Standardize on one ID property

Choose either req.user.id or req.user._id consistently to avoid confusion.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant