Skip to content

feat(profile): add user profile management endpoints#72

Merged
respp merged 5 commits intoStellar-Rent:mainfrom
bernardev254:profile-management
Jun 10, 2025
Merged

feat(profile): add user profile management endpoints#72
respp merged 5 commits intoStellar-Rent:mainfrom
bernardev254:profile-management

Conversation

@bernardev254
Copy link
Contributor

@bernardev254 bernardev254 commented Jun 8, 2025

StellarRent Logo

Pull Request | StellarRent

📝 Summary

This PR implements the user profile management system, enabling users to view, update, and delete their profile, as well as upload a profile avatar. It ensures type safety and improves error handling when interacting with the Supabase backend.

🔗 Related Issues

Closes #44.

🔄 Changes Made

  1. Added profile.controller.ts with routes for
  • getProfile
  • updateProfile
  • deleteAccount
  • uploadAvatar
  1. Implemented profile.service.ts functions for:
  • Fetching user profiles from Supabase
  • Updating profiles with strict types (UserProfile)
  • Deleting accounts
  • Uploading avatar images to Supabase storage
  1. Integrated Supabase public URL retrieval after avatar upload
  2. Fixed type issues flagged by Biome and enforced interface usage for profile data

🖼️ Current Output

Provide visual evidence of the changes:

  • For small changes: Screenshots.
  • For large changes: Video or Loom link.

🧪 Testing

If applicable, describe the tests performed. Include screenshots, test outputs, or any resources that help reviewers understand how the changes were tested.

✅ Testing Checklist

  • Unit tests added/modified
  • Integration tests performed
  • Manual tests executed
  • All tests pass in CI/CD

⚠️ Potential Risks

List any possible issues that might arise with this change.

🚀 Next Steps & Improvements

This change lays a solid foundation for further optimizations. Some areas that could benefit from future improvements include:

  • 🔹 Performance optimization
  • 🔹 Increased test coverage
  • 🔹 Potential user experience enhancements

💬 Comments

Any additional context, questions, or considerations for reviewers.

Summary by CodeRabbit

  • New Features

    • Introduced user profile management, allowing users to view, update, and delete their profiles.
    • Added support for uploading and managing user avatar images.
    • Extended API with new profile-related endpoints, secured for authenticated users.
    • Integrated Supabase authentication for user registration and login.
  • Bug Fixes

    • Improved error handling and validation for authentication and profile operations.
  • Documentation

    • Updated database and setup documentation to reflect new profile features and storage changes.
  • Tests

    • Added automated tests for all new profile endpoints and avatar upload functionality.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 8, 2025

Walkthrough

This update introduces a comprehensive user profile system to the backend. It adds a new profiles table, storage bucket, and related API endpoints for profile management, including viewing, updating, deleting profiles, and avatar uploads. Middleware, validation, service, and type definitions are extended to support these features, with Supabase integration for authentication and storage.

Changes

File(s) Change Summary
database/README.md, database/setup.sql Added profiles table, storage bucket for avatars, updated documentation and reset scripts, and sample data.
src/controllers/profile.controller.ts, src/routes/profile.route.ts Added controller and routes for profile management (GET, PATCH, DELETE, POST avatar).
src/services/profile.service.ts Added service functions for profile CRUD and avatar upload using Supabase.
src/middleware/auth.middleware.ts Refactored to use Supabase for JWT validation; updated request typing.
src/middleware/multer.ts Introduced multer middleware for image uploads with memory storage and file type/size limits.
src/validators/profile.validators.ts Added Zod schema for profile update validation.
src/types/userProfile.ts, src/types/auth.types.ts, src/types/express/index.d.ts Added/updated TypeScript interfaces for user profile, authentication, and Express request augmentation.
src/services/auth.service.ts Refactored registration/login to use Supabase auth and profile upsert.
src/controllers/authController.ts Swapped and refactored login/register logic to align with new auth service.
src/index.ts Registered new /profiles route group.
test_endpoints.sh Added tests for new profile endpoints (GET, PATCH, DELETE, POST avatar).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AuthMiddleware
    participant ProfileController
    participant ProfileService
    participant Supabase

    Client->>AuthMiddleware: Request (GET/PATCH/DELETE /profiles, POST /profiles/avatar)
    AuthMiddleware->>Supabase: Validate JWT, fetch user
    Supabase-->>AuthMiddleware: User info or error
    AuthMiddleware-->>ProfileController: Attach user to req or reject

    alt GET /profiles
        ProfileController->>ProfileService: getUserProfile(userId)
        ProfileService->>Supabase: Query profiles table
        Supabase-->>ProfileService: Profile data
        ProfileService-->>ProfileController: Profile data
        ProfileController-->>Client: Respond with profile JSON
    else PATCH /profiles
        ProfileController->>ProfileService: updateUserProfile(userId, updates)
        ProfileService->>Supabase: Update profiles table
        Supabase-->>ProfileService: Updated profile
        ProfileService-->>ProfileController: Updated profile
        ProfileController-->>Client: Respond with updated profile
    else DELETE /profiles
        ProfileController->>ProfileService: deleteUserAccount(userId)
        ProfileService->>Supabase: Delete profile
        Supabase-->>ProfileService: Success/failure
        ProfileService-->>ProfileController: Confirmation
        ProfileController-->>Client: Respond with confirmation
    else POST /profiles/avatar
        ProfileController->>ProfileService: uploadUserAvatar(userId, req)
        ProfileService->>Supabase: Upload file to storage
        Supabase-->>ProfileService: File URL
        ProfileService->>Supabase: Update profile avatar_url
        Supabase-->>ProfileService: Updated profile
        ProfileService-->>ProfileController: Avatar URL
        ProfileController-->>Client: Respond with avatar URL
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Profile Management: GET, PATCH, DELETE, POST avatar endpoints (#44)
Profile Fields Extension: All specified fields in UserProfile interface (#44)
Security Enhancements: Email/phone verification, 2FA, session mgmt, activity logging (#44) Only basic session/auth handled; no explicit flows for verification, 2FA, or logging present.
Advanced Features: Profile completion %, badges, last active, login history, device mgmt (#44) Only last_active is present; other advanced features are not implemented in this change.
Integration Points: Listings, reviews, booking, payment, saved properties (#44) No integration with these modules is present in the code changes.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Refactor of login/register logic to use Supabase auth in src/services/auth.service.ts and authController.ts This refactor is not strictly required for profile management; it changes core authentication implementation.
Addition of endpoint tests in test_endpoints.sh These are support/testing changes, not directly tied to the implementation objectives of the linked issue.

Possibly related PRs

  • Users integration #14: Initial integration of user authentication with Supabase and JWT handling; related as foundational auth changes preceding profile management.

Poem

In burrows deep, a profile grew,
With fields and forms both fresh and new.
Now rabbits hop with avatar flair,
Update, delete, or show—they’re there!
Multer guards the image gate,
Supabase keeps profiles straight.
🐰 Cheers to code that’s up-to-date!

✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate sequence diagram to generate a sequence diagram of the changes in 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.

@bernardev254
Copy link
Contributor Author

@Benjtalkshow requesting for a review

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 19

🧹 Nitpick comments (10)
apps/backend/src/middleware/multer.ts (1)

3-8: Consider disk storage for production scalability.

While memory storage works for development, it could cause memory issues in production with multiple concurrent uploads or high traffic. Consider using disk storage with proper cleanup for production environments.

-const storage = multer.memoryStorage();
+const storage = process.env.NODE_ENV === 'production' 
+  ? multer.diskStorage({
+      destination: '/tmp/uploads',
+      filename: (req, file, cb) => {
+        cb(null, `${Date.now()}-${file.originalname}`);
+      }
+    })
+  : multer.memoryStorage();
apps/backend/src/validators/profile.validators.ts (1)

4-29: Enhance validation with phone format and language constraints.

The schema structure is solid, but consider adding format validation for phone numbers and constraining language to supported values.

export const updateProfileSchema = z.object({
  name: z.string().optional(),
-  phone: z.string().optional(),
+  phone: z.string().regex(/^\+?[\d\s\-\(\)]+$/, 'Invalid phone format').optional(),
  address: z
    .object({
      street: z.string(),
      city: z.string(),
      country: z.string(),
      postal_code: z.string(),
    })
    .optional(),
  preferences: z
    .object({
      notifications: z.boolean(),
      newsletter: z.boolean(),
-      language: z.string(),
+      language: z.enum(['en', 'es', 'fr']), // Add supported languages
    })
    .optional(),
  social_links: z
    .object({
      facebook: z.string().url().optional(),
      twitter: z.string().url().optional(),
      instagram: z.string().url().optional(),
    })
    .optional(),
});
apps/backend/test_endpoints.sh (2)

29-36: Fix duplicate test numbering.

Both tests are numbered "7" which creates confusion. The DELETE test should be numbered "8".

Apply this diff:

-echo -e "\n7. 🔐 Testing DELETE /profiles (authenticated)"
+echo -e "\n8. 🔐 Testing DELETE /profiles (authenticated)"

38-60: Update test numbering for consistency.

This test should be numbered "9" to maintain sequential order.

Apply this diff:

-echo -e "\n8. ✏️ Testing PATCH /profiles (update basic profile)"
+echo -e "\n9. ✏️ Testing PATCH /profiles (update basic profile)"
apps/backend/src/types/userProfile.ts (1)

23-23: Consider string type for date serialization.

The last_active field is typed as Date, but in JSON APIs, dates are typically serialized as ISO strings. This could cause serialization/deserialization issues.

Consider using string type instead:

-  last_active?: Date;
+  last_active?: string; // ISO date string

Or create a utility type for handling dates consistently across the API.

apps/backend/src/middleware/auth.middleware.ts (1)

9-9: Consider using consistent language for error messages.

The error messages are in Spanish ("Token no proporcionado", "Token inválido o expirado") which may not be consistent with the rest of the codebase.

Consider using English for consistency:

-  if (!token) return res.status(401).json({ error: 'Token no proporcionado' });
+  if (!token) return res.status(401).json({ error: 'Token not provided' });
-  if (error || !user) return res.status(403).json({ error: 'Token inválido o expirado' });
+  if (error || !user) return res.status(403).json({ error: 'Invalid or expired token' });

Also applies to: 16-16

apps/backend/database/README.md (1)

72-72: Fix markdown formatting issue.

Remove the extra space after the double asterisk to fix the markdown emphasis.

-### ** Table `profiles`**
+### **Table `profiles`**
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

72-72: Spaces inside emphasis markers
null

(MD037, no-space-in-emphasis)

apps/backend/src/controllers/profile.controller.ts (1)

11-11: Consider internationalizing error messages.

The authentication error messages are hardcoded in Spanish ("Usuario no autenticado"). Consider using an internationalization system or configuration for error messages to support multiple languages consistently across the application.

Example approach:

-  if (!req.user) return res.status(401).json({ error: 'Usuario no autenticado' });
+  if (!req.user) return res.status(401).json({ error: 'Unauthorized user' });

Also applies to: 20-20, 34-34, 43-43

apps/backend/src/types/auth.types.ts (2)

1-1: Import alias creates confusion.

The import uses User as user which creates a lowercase alias that doesn't follow TypeScript naming conventions and could be confused with a variable.

-import type { User as user } from '@supabase/supabase-js';
+import type { User } from '@supabase/supabase-js';

Then update line 6:

-  user?: user;
+  user?: User;

25-27: Complex type intersection may cause confusion.

The RegisterInput type combines the login schema with profile data, which could lead to confusion about required vs optional fields during registration.

Consider creating a more explicit registration interface:

export interface RegisterInput extends LoginInput {
  name: string;
  profile?: Partial<Omit<PublicProfile, 'verification_status' | 'last_active'>>;
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 62d2491 and 4cd6ca2.

📒 Files selected for processing (15)
  • apps/backend/database/README.md (4 hunks)
  • apps/backend/database/setup.sql (9 hunks)
  • apps/backend/src/controllers/authController.ts (1 hunks)
  • apps/backend/src/controllers/profile.controller.ts (1 hunks)
  • apps/backend/src/index.ts (2 hunks)
  • apps/backend/src/middleware/auth.middleware.ts (1 hunks)
  • apps/backend/src/middleware/multer.ts (1 hunks)
  • apps/backend/src/routes/profile.route.ts (1 hunks)
  • apps/backend/src/services/auth.service.ts (1 hunks)
  • apps/backend/src/services/profile.service.ts (1 hunks)
  • apps/backend/src/types/auth.types.ts (2 hunks)
  • apps/backend/src/types/express/index.d.ts (1 hunks)
  • apps/backend/src/types/userProfile.ts (1 hunks)
  • apps/backend/src/validators/profile.validators.ts (1 hunks)
  • apps/backend/test_endpoints.sh (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
apps/backend/src/types/express/index.d.ts (1)
apps/backend/src/types/auth.types.ts (1)
  • User (9-13)
apps/backend/src/routes/profile.route.ts (3)
apps/backend/src/middleware/auth.middleware.ts (1)
  • authenticateToken (5-20)
apps/backend/src/controllers/profile.controller.ts (4)
  • getProfile (10-17)
  • updateProfile (19-31)
  • deleteAccount (33-40)
  • uploadAvatar (42-49)
apps/backend/src/middleware/multer.ts (1)
  • upload (4-18)
apps/backend/src/middleware/auth.middleware.ts (1)
apps/backend/src/config/supabase.ts (1)
  • supabase (91-103)
apps/backend/src/controllers/authController.ts (1)
apps/backend/src/services/auth.service.ts (2)
  • registerUser (5-60)
  • loginUser (62-81)
apps/backend/src/services/profile.service.ts (2)
apps/backend/src/config/supabase.ts (1)
  • supabase (91-103)
apps/backend/src/types/userProfile.ts (1)
  • UserProfileUpdate (1-24)
apps/backend/src/controllers/profile.controller.ts (2)
apps/backend/src/services/profile.service.ts (4)
  • getUserProfile (5-16)
  • updateUserProfile (18-28)
  • deleteUserAccount (30-33)
  • uploadUserAvatar (35-59)
apps/backend/src/validators/profile.validators.ts (1)
  • updateProfileSchema (4-29)
🪛 Shellcheck (0.10.0)
apps/backend/test_endpoints.sh

[warning] 26-26: AUTH1_TOKEN appears unused. Verify use (or export if used externally).

(SC2034)

🪛 markdownlint-cli2 (0.17.2)
apps/backend/database/README.md

72-72: Spaces inside emphasis markers
null

(MD037, no-space-in-emphasis)

🔇 Additional comments (15)
apps/backend/src/index.ts (2)

7-7: LGTM: Clean import following established patterns.

The import statement follows the same pattern as other route imports in the file.


44-44: LGTM: Router registration follows established patterns.

The profile router is registered consistently with the existing auth and properties routes.

apps/backend/src/middleware/multer.ts (1)

10-17: LGTM: Proper file validation and error handling.

The file filter correctly validates image MIME types and provides clear error messages for invalid file types.

apps/backend/src/types/express/index.d.ts (2)

4-9: LGTM: Proper Express Request interface extension.

The module augmentation correctly extends the Express Request interface with optional properties for user authentication and file uploads.


2-2:

Details

✅ Verification successful

Verify no type conflicts with local User interface.

There's a local User interface defined in apps/backend/src/types/auth.types.ts that might conflict with the Supabase User type. Ensure consistent typing across the application.


🏁 Script executed:

#!/bin/bash
# Description: Check for potential User type conflicts in the codebase
# Expected: Clear distinction between local and Supabase User types

# Search for User type definitions and imports
echo "=== User type definitions ==="
ast-grep --pattern $'interface User {
  $$$
}'

echo -e "\n=== User type imports ==="
rg "import.*User.*from" -A 2

echo -e "\n=== User type usage in auth middleware ==="
rg "req\.user" -A 3 -B 3

Length of output: 7191


🏁 Script executed:

#!/bin/bash
# Display the backend auth.types.ts file to inspect the local User interface usage
cat apps/backend/src/types/auth.types.ts

Length of output: 1432


No Type Conflicts Detected

I verified the type definitions and imports:

  • apps/backend/src/types/express/index.d.ts imports User directly from @supabase/supabase-js and uses it for Request.user.
  • apps/backend/src/types/auth.types.ts imports the same Supabase User under the alias user, then declares its own local User interface—this is scoped to that file and isn’t imported elsewhere.

Because each User type lives in its own module scope (and the Supabase type is aliased where needed), there’s no collision. Everything behaves as intended.

apps/backend/src/routes/profile.route.ts (1)

1-19: Well-structured Express router implementation.

The router implementation follows Express.js best practices with:

  • Clean import organization
  • Proper authentication middleware on all routes
  • Appropriate use of HTTP methods (GET, PATCH, DELETE, POST)
  • Correct file upload middleware configuration for avatar uploads

The route structure is logical and consistent with RESTful API design.

apps/backend/src/types/userProfile.ts (1)

1-24: Well-designed interface for profile updates.

The interface structure is excellent with:

  • Optional fields appropriate for partial updates
  • Logical grouping of related fields (address, preferences, social_links)
  • Proper TypeScript typing with union types for constrained values
  • Comprehensive coverage of user profile attributes
apps/backend/src/middleware/auth.middleware.ts (2)

5-20: Clean Supabase authentication integration.

The middleware implementation is well-structured with:

  • Proper async/await usage for Supabase auth.getUser()
  • Appropriate HTTP status codes (401 for missing token, 403 for invalid token)
  • Clean error handling without exposing internal details
  • Correct assignment of user data to request object

The transition from manual JWT verification to Supabase's built-in auth is a good architectural decision.


18-18:

Details

✅ Verification successful

Verify Express Request type augmentation for user property.

The middleware assigns req.user = user but we should ensure the Express Request type has been properly augmented to include the user property.

Run this script to verify the type augmentation exists:


🏁 Script executed:

#!/bin/bash
# Check for Express Request type augmentation
echo "Searching for Express Request type augmentation..."
rg -A 10 -B 5 "declare.*Request.*user|interface.*Request.*user" --type ts

Length of output: 186


🏁 Script executed:

#!/bin/bash
echo "Searching for Express Request augmentation via namespace Express..."
rg -C5 "declare namespace Express" -t ts
echo "Searching for Express Request augmentation via express-serve-static-core module..."
rg -C5 "declare module ['\"]express-serve-static-core['\"]" -t ts
echo "Searching for direct interface Request declarations..."
rg -C5 "export interface Request" -t ts

Length of output: 1175


Express Request user augmentation verified

Type augmentation for Request.user is present in your custom declarations:

• apps/backend/src/types/express/index.d.ts
• declare module 'express-serve-static-core' {
interface Request {
user?: User;
file?: Express.Multer.File;
}
}

No further action needed.

apps/backend/src/controllers/profile.controller.ts (1)

22-25: Excellent validation implementation.

The use of Zod schema validation with safeParse provides robust input validation and proper error formatting. This approach ensures type safety and clear error messages for the client.

apps/backend/src/types/auth.types.ts (1)

28-50: Well-structured profile interface.

The PublicProfile interface is comprehensive and well-structured with proper nested objects for address, preferences, and social links. The verification status constraint using union types is excellent for type safety.

apps/backend/src/services/profile.service.ts (2)

5-16: Efficient profile retrieval implementation.

The use of maybeSingle() is appropriate for fetching a single profile record, and the error handling properly distinguishes between database errors and missing records.


18-28: Automatic timestamp update is good practice.

Automatically updating the last_active timestamp during profile updates is a good approach for tracking user activity. The use of spread operator preserves all provided updates.

apps/backend/database/setup.sql (2)

35-45: Well-designed profiles table structure.

The profiles table design is excellent with:

  • Proper foreign key with CASCADE delete
  • JSONB columns for flexible nested data
  • Check constraint for verification status
  • Default values where appropriate

289-289: Verification queries are comprehensive.

The verification section properly checks for both table creation and storage bucket setup, which helps ensure the script executed successfully.

Also applies to: 293-293

Comment on lines +61 to +65
echo -e "\n9. 📤 Testing POST /profiles/avatar (upload image)"
curl -s -X POST "$BASE_URL/profiles/avatar" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-F "avatar=@./tests/assets/avatar.png" | jq '.avatar_url // .error'

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify test asset file exists and update numbering.

The test references ./tests/assets/avatar.png which may not exist, and the numbering should be "10".

Apply this diff for numbering:

-echo -e "\n9. 📤 Testing POST /profiles/avatar (upload image)"
+echo -e "\n10. 📤 Testing POST /profiles/avatar (upload image)"

Run this script to verify the test asset exists:


🏁 Script executed:

#!/bin/bash
# Check if the test asset file exists
if [ -f "./tests/assets/avatar.png" ]; then
    echo "✅ Test asset file exists"
    file ./tests/assets/avatar.png
else
    echo "❌ Test asset file not found: ./tests/assets/avatar.png"
    echo "Available files in tests directory:"
    find ./tests -name "*.png" -o -name "*.jpg" -o -name "*.jpeg" 2>/dev/null || echo "No test assets found"
fi

Length of output: 353


Missing test asset and update test numbering

The test at apps/backend/test_endpoints.sh (lines 61–65) references ./tests/assets/avatar.png, but that file doesn’t exist (no image files found under tests/assets). This will cause the upload test to fail. Additionally, the step number should be incremented from 9 to 10.

Please:

  • Add the avatar.png fixture under tests/assets/ (or update the path to point to an existing image).
  • Update the printed step number.

Suggested diff:

--- a/apps/backend/test_endpoints.sh
+++ b/apps/backend/test_endpoints.sh
@@ -61,7 +61,7 @@
 
-echo -e "\n9. 📤 Testing POST /profiles/avatar (upload image)"
+echo -e "\n10. 📤 Testing POST /profiles/avatar (upload image)"
 curl -s -X POST "$BASE_URL/profiles/avatar" \
   -H "Authorization: Bearer $AUTH_TOKEN" \
   -F "avatar=@./tests/assets/avatar.png" | jq '.avatar_url // .error'
📝 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
echo -e "\n9. 📤 Testing POST /profiles/avatar (upload image)"
curl -s -X POST "$BASE_URL/profiles/avatar" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-F "avatar=@./tests/assets/avatar.png" | jq '.avatar_url // .error'
echo -e "\n10. 📤 Testing POST /profiles/avatar (upload image)"
curl -s -X POST "$BASE_URL/profiles/avatar" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-F "avatar=@./tests/assets/avatar.png" | jq '.avatar_url // .error'
🤖 Prompt for AI Agents
In apps/backend/test_endpoints.sh around lines 61 to 65, the test references a
missing image file ./tests/assets/avatar.png which will cause the upload test to
fail, and the step number should be updated from 9 to 10. Fix this by adding the
avatar.png image fixture to the tests/assets directory or update the path to
point to an existing image file, and change the printed step number in the echo
statement from 9 to 10.

Comment on lines 25 to 26
# 🔐 Replace with your actual token or load from environment
AUTH1_TOKEN="eyJhbGciOiJIUzI1NiIsImtpZCI6IjBJMjBNZlg1TGlXSDg4eXEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2dtcHBrbHl3b3Bna2Jkdmpvb2ZnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiJlNjRmNzJkZS01OGFiLTQ3N2QtYTIxMi0xMmNiNGUwMWFlMTEiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzQ5Mzg2NDEwLCJpYXQiOjE3NDkzODI4MTAsImVtYWlsIjoidGVzdDExQGV4YW1wbGUuY29tIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InRlc3QxMUBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaG9uZV92ZXJpZmllZCI6ZmFsc2UsInN1YiI6ImU2NGY3MmRlLTU4YWItNDc3ZC1hMjEyLTEyY2I0ZTAxYWUxMSJ9LCJyb2xlIjoiYXV0aGVudGljYXRlZCIsImFhbCI6ImFhbDEiLCJhbXIiOlt7Im1ldGhvZCI6InBhc3N3b3JkIiwidGltZXN0YW1wIjoxNzQ5MzgyODEwfV0sInNlc3Npb25faWQiOiIzZmFkZmE3Mi1mZDNmLTQyN2UtYmNmMS0wMGM0OTQwOTAxMjkiLCJpc19hbm9ueW1vdXMiOmZhbHNlfQ.vZnnQm7BOkxglxbyvkklRB5xBx__DzF6wEIi_2u-N1o"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix variable name inconsistency and security issue.

Two issues identified:

  1. Variable AUTH1_TOKEN is defined but AUTH_TOKEN is used in the tests (lines 31, 35, 40, 63)
  2. Hardcoded JWT tokens should not be committed to version control

Apply this diff to fix the variable name:

-AUTH1_TOKEN="eyJhbGciOiJIUzI1NiIsImtpZCI6IjBJMjBNZlg1TGlXSDg4eXEiLCJ0eXAiOiJKV1QifQ..."
+AUTH_TOKEN="eyJhbGciOiJIUzI1NiIsImtpZCI6IjBJMjBNZlg1TGlXSDg4eXEiLCJ0eXAiOiJKV1QifQ..."

However, consider loading the token from environment variables instead:

-AUTH1_TOKEN="eyJhbGciOiJIUzI1NiIsImtpZCI6IjBJMjBNZlg1TGlXSDg4eXEiLCJ0eXAiOiJKV1QifQ..."
+AUTH_TOKEN="${AUTH_TOKEN:-"your-test-token-here"}"
📝 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
# 🔐 Replace with your actual token or load from environment
AUTH1_TOKEN="eyJhbGciOiJIUzI1NiIsImtpZCI6IjBJMjBNZlg1TGlXSDg4eXEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2dtcHBrbHl3b3Bna2Jkdmpvb2ZnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiJlNjRmNzJkZS01OGFiLTQ3N2QtYTIxMi0xMmNiNGUwMWFlMTEiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzQ5Mzg2NDEwLCJpYXQiOjE3NDkzODI4MTAsImVtYWlsIjoidGVzdDExQGV4YW1wbGUuY29tIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InRlc3QxMUBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaG9uZV92ZXJpZmllZCI6ZmFsc2UsInN1YiI6ImU2NGY3MmRlLTU4YWItNDc3ZC1hMjEyLTEyY2I0ZTAxYWUxMSJ9LCJyb2xlIjoiYXV0aGVudGljYXRlZCIsImFhbCI6ImFhbDEiLCJhbXIiOlt7Im1ldGhvZCI6InBhc3N3b3JkIiwidGltZXN0YW1wIjoxNzQ5MzgyODEwfV0sInNlc3Npb25faWQiOiIzZmFkZmE3Mi1mZDNmLTQyN2UtYmNmMS0wMGM0OTQwOTAxMjkiLCJpc19hbm9ueW1vdXMiOmZhbHNlfQ.vZnnQm7BOkxglxbyvkklRB5xBx__DzF6wEIi_2u-N1o"
# 🔐 Replace with your actual token or load from environment
-AUTH1_TOKEN="eyJhbGciOiJIUzI1NiIsImtpZCI6IjBJMjBNZlg1TGlXSDg4eXEiLCJ0eXAiOiJKV1QifQ..."
+AUTH_TOKEN="${AUTH_TOKEN:-"your-test-token-here"}"
🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 26-26: AUTH1_TOKEN appears unused. Verify use (or export if used externally).

(SC2034)

🤖 Prompt for AI Agents
In apps/backend/test_endpoints.sh around lines 25 to 26, the variable is named
AUTH1_TOKEN but the tests use AUTH_TOKEN, causing inconsistency. Rename
AUTH1_TOKEN to AUTH_TOKEN to match usage. Additionally, remove the hardcoded JWT
token and instead load the token securely from an environment variable to avoid
committing sensitive data to version control.

DROP TABLE IF EXISTS public.users CASCADE;
DROP TABLE IF EXISTS public.profiles CASCADE;
DELETE FROM storage.buckets WHERE name = 'property-images';
DELETE FROM storage.buckets WHERE name = 'profile-avaters';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in bucket name.

The bucket name has a typo: "profile-avaters" should be "profile-avatars" to match the bucket name used elsewhere.

-DELETE FROM storage.buckets WHERE name = 'profile-avaters';
+DELETE FROM storage.buckets WHERE name = 'profile-avatars';
📝 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
DELETE FROM storage.buckets WHERE name = 'profile-avaters';
DELETE FROM storage.buckets WHERE name = 'profile-avatars';
🤖 Prompt for AI Agents
In apps/backend/database/README.md at line 133, the bucket name in the DELETE
statement contains a typo: "profile-avaters" should be corrected to
"profile-avatars" to match the correct bucket name used elsewhere. Update the
SQL command to use the correct bucket name.


### **Storage**
- **Bucket `property-images`** for property images
- **Bucket `profile-avatars`** for property images
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect description for profile-avatars bucket.

The description says "for property images" but should be "for profile avatars".

-- **Bucket `profile-avatars`** for property images
+- **Bucket `profile-avatars`** for profile avatars
📝 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
- **Bucket `profile-avatars`** for property images
- **Bucket `profile-avatars`** for profile avatars
🤖 Prompt for AI Agents
In apps/backend/database/README.md at line 34, the description for the bucket
named "profile-avatars" is incorrect as it says "for property images." Update
this description to correctly state "for profile avatars" to accurately reflect
the bucket's purpose.

Comment on lines +19 to 51
export const register = async (req: Request, res: Response) => {
try {
const { token, user } = await loginUser(req.body);
res.status(200).json({ token, user });
const { user, token } = await registerUser(req.body);

res.status(201).json({
user,
token, // send token, not session
});
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : 'Error desconocido';
console.error('Register error:', error);
const message = error instanceof Error ? error.message : 'Error desconocido';

if (
errorMessage === 'Usuario no encontrado' ||
errorMessage === 'Contraseña incorrecta'
) {
res.status(401).json({ error: 'Credenciales inválidas' });
} else if (errorMessage.includes('validation')) {
res.status(400).json({ error: 'Datos de entrada inválidos' });
} else {
console.error('Error en login:', error);
res.status(500).json({ error: 'Error interno del servidor' });
if (message.includes('registrado')) {
return res.status(409).json({ error: message });
}

res.status(400).json({ error: message });
}
};

export const register = async (req: Request, res: Response) => {
export const login = async (req: Request, res: Response) => {
try {
// Log registration attempt without PII in production
if (process.env.NODE_ENV !== 'production') {
console.log('Iniciando registro con datos:', {
email: req.body.email,
name: req.body.name,
});
} else {
console.log('Iniciando intento de registro');
}

const { token, user } = await registerUser(req.body);
res.status(201).json({ token, user });
const { user, session } = await loginUser(req.body);
res.status(200).json({ user, session });
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : 'Error desconocido';
console.error('Error en registro:', errorMessage);

if (errorMessage === 'El email ya está registrado') {
res.status(409).json({ error: 'El email ya está registrado' });
} else if (errorMessage.includes('validation')) {
res.status(400).json({ error: 'Datos de entrada inválidos' });
} else {
res.status(500).json({ error: 'Error al registrar usuario' });
const message = error instanceof Error ? error.message : 'Error desconocido';
if (message === 'Credenciales inválidas') {
return res.status(401).json({ error: message });
}
console.error('Login error:', error);
res.status(500).json({ error: message });
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider consistent response format between register and login endpoints.

The register endpoint returns { user, token } while the login endpoint returns { user, session }. This inconsistency could confuse API consumers. Consider standardizing the response format.

For consistency, both endpoints should return the same structure. Either:

  1. Both return { user, token } (extract token from session in login), or
  2. Both return { user, session } (change register to match login)

Additionally, ensure error messages are consistently handled in the same language throughout the application.

🤖 Prompt for AI Agents
In apps/backend/src/controllers/authController.ts between lines 19 and 51, the
register endpoint returns { user, token } while the login endpoint returns {
user, session }, causing inconsistency in API responses. To fix this,
standardize both endpoints to return the same structure, either both returning {
user, token } by extracting the token from the session in login, or both
returning { user, session } by modifying register accordingly. Also, review and
unify error messages to use consistent language throughout the application.

Comment on lines +26 to +27
if (error) return { error };
return data;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Inconsistent return structure.

The function returns either { error } or just data, creating inconsistent response structures across the service.

-  if (error) return { error };
-  return data;
+  if (error) return { error };
+  return { data };
🤖 Prompt for AI Agents
In apps/backend/src/services/profile.service.ts around lines 26 to 27, the
function returns inconsistent structures: an object with an error property when
there is an error, but just data otherwise. To fix this, always return an object
with consistent keys, such as { data, error }, where one of the keys can be null
or undefined depending on the outcome, ensuring uniform response structure.

Comment on lines +43 to +44
const { error: uploadError } = await supabase.storage
.from('avatars')
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Verify storage bucket configuration.

The code references storage bucket 'avatars', but the database setup creates 'profiles-avatars'. This mismatch will cause upload failures.

-  const { error: uploadError } = await supabase.storage
-    .from('avatars')
+  const { error: uploadError } = await supabase.storage
+    .from('profiles-avatars')

Also update line 51:

-  const { data } = supabase.storage.from('avatars').getPublicUrl(filePath);
+  const { data } = supabase.storage.from('profiles-avatars').getPublicUrl(filePath);
📝 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
const { error: uploadError } = await supabase.storage
.from('avatars')
const { error: uploadError } = await supabase.storage
.from('profiles-avatars')
// … other code …
const { data } = supabase.storage
.from('profiles-avatars')
.getPublicUrl(filePath);
🤖 Prompt for AI Agents
In apps/backend/src/services/profile.service.ts around lines 43 to 44, the code
references the storage bucket 'avatars', but the correct bucket name according
to the database setup is 'profiles-avatars'. Update the bucket name in the
supabase.storage.from() call to 'profiles-avatars' to match the configured
bucket and prevent upload failures. Also, ensure any other references to the
bucket name, such as on line 51, are updated accordingly.

-- ===============================================

CREATE TABLE IF NOT EXISTS public.profiles (
user_id uuid PRIMARY KEY REFERENCES public.users(id) ON DELETE CASCADE,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing RLS policies for profiles table.

The profiles table is created but RLS is not enabled, and no security policies are defined. This creates a significant security vulnerability where any authenticated user could potentially access or modify any profile.

Add these policies after the existing RLS section:

-- Enable RLS for profiles table
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;

-- Profiles policies
CREATE POLICY "Users can view own profile" ON public.profiles
    FOR SELECT USING (auth.uid() = user_id);

CREATE POLICY "Users can update own profile" ON public.profiles
    FOR UPDATE USING (auth.uid() = user_id);

CREATE POLICY "Users can insert own profile" ON public.profiles
    FOR INSERT WITH CHECK (auth.uid() = user_id);

CREATE POLICY "Users can delete own profile" ON public.profiles
    FOR DELETE USING (auth.uid() = user_id);
🤖 Prompt for AI Agents
In apps/backend/database/setup.sql at line 36, the profiles table lacks Row
Level Security (RLS) and associated policies, exposing a security risk. To fix
this, enable RLS on the profiles table using ALTER TABLE to enable row level
security, then create policies that restrict SELECT, UPDATE, INSERT, and DELETE
operations to only the authenticated user matching the user_id. Add these policy
definitions immediately after the existing RLS section to ensure proper access
control.

Comment on lines +177 to +201
-- Policy to read profile avatars (public)
CREATE POLICY "Anyone can view profile avatars" ON storage.objects
FOR SELECT USING (bucket_id = 'profiles-avatars');

-- Policy to upload avatars (authenticated users only)
CREATE POLICY "Authenticated users can upload profile avatars" ON storage.objects
FOR INSERT WITH CHECK (
bucket_id = 'profiles-avatars'
AND auth.role() = 'authenticated'
);

-- Policy to update avatars (authenticated users only)
CREATE POLICY "Users can update their profile avatars" ON storage.objects
FOR UPDATE USING (
bucket_id = 'profiles-avatars'
AND auth.role() = 'authenticated'
);

-- Policy to delete avatars (authenticated users only)
CREATE POLICY "Users can delete their profile avatars" ON storage.objects
FOR DELETE USING (
bucket_id = 'profiles-avatars'
AND auth.role() = 'authenticated'
);

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Storage policies need refinement.

The storage policies for profile avatars are too permissive. Users should only be able to modify their own avatar files, not all avatar files.

-- Policy to update avatars (user's own avatars only)
CREATE POLICY "Users can update their profile avatars" ON storage.objects
    FOR UPDATE USING (
        bucket_id = 'profiles-avatars'
        AND auth.role() = 'authenticated'
+        AND (storage.foldername(name))[1] = auth.uid()::text
    );

-- Policy to delete avatars (user's own avatars only)
CREATE POLICY "Users can delete their profile avatars" ON storage.objects
    FOR DELETE USING (
        bucket_id = 'profiles-avatars'
        AND auth.role() = 'authenticated'
+        AND (storage.foldername(name))[1] = auth.uid()::text
    );
🤖 Prompt for AI Agents
In apps/backend/database/setup.sql between lines 177 and 201, the storage
policies for profile avatars currently allow any authenticated user to insert,
update, or delete any file in the 'profiles-avatars' bucket. To fix this, modify
the policies to restrict these actions so users can only modify their own avatar
files. This typically involves adding a condition that the object's owner or
user ID matches the authenticated user's ID, ensuring users cannot affect other
users' avatar files.

bernardev254 and others added 3 commits June 8, 2025 18:32
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
apps/backend/database/README.md (2)

35-35: Fix description for profile avatars bucket

The description still reads "for property images" instead of "for profile avatars".

- **Bucket `profile-avatars`** for property images
+ **Bucket `profile-avatars`** for profile avatars

149-149: Fix typo in bucket reset command

The bucket name is misspelled as profile-avaters.

- DELETE FROM storage.buckets WHERE name = 'profile-avaters';
+ DELETE FROM storage.buckets WHERE name = 'profile-avatars';
🧹 Nitpick comments (1)
apps/backend/database/README.md (1)

73-84: Refine table section formatting and indentation

The header has extra spaces inside the emphasis markers, and several fields are over-indented. Also normalize "json" → "JSON".

- ### ** Table `profiles`**
+ ### **Table `profiles`**

- -  address (JSON object)
+ - address (JSON object)
- -  preferences (json object)
+ - preferences (JSON object)
- -  social_links (json object)
+ - social_links (JSON object)
- -  verification_status (text)
+ - verification_status (text)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

73-73: Spaces inside emphasis markers
null

(MD037, no-space-in-emphasis)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7d7183e and a3376af.

📒 Files selected for processing (3)
  • apps/backend/database/README.md (4 hunks)
  • apps/backend/database/setup.sql (11 hunks)
  • apps/backend/src/index.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/backend/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/backend/database/setup.sql
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
apps/backend/database/README.md

73-73: Spaces inside emphasis markers
null

(MD037, no-space-in-emphasis)

🔇 Additional comments (2)
apps/backend/database/README.md (2)

24-24: Approve addition of profiles table to overview

The new profiles entry correctly documents the addition of the user profile table.


147-147: Approve reset script drop of profiles table

The DROP TABLE IF EXISTS public.profiles CASCADE; command aligns with the addition of the table and ensures proper cleanup.

@bernardev254
Copy link
Contributor Author

@Benjtalkshow request for review

@respp respp merged commit a3376af into Stellar-Rent:main Jun 10, 2025
5 of 8 checks passed
@respp
Copy link
Contributor

respp commented Jun 10, 2025

Amazing job — everything looks great! Thanks so much for contributing to StellarRent, really appreciate your work!

@coderabbitai coderabbitai bot mentioned this pull request Jun 30, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants