feat(profile): add user profile management endpoints#72
feat(profile): add user profile management endpoints#72respp merged 5 commits intoStellar-Rent:mainfrom
Conversation
WalkthroughThis update introduces a comprehensive user profile system to the backend. It adds a new Changes
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
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Poem
✨ 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:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@Benjtalkshow requesting for a review |
There was a problem hiding this comment.
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_activefield is typed asDate, 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 stringOr 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 userwhich 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
RegisterInputtype 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
📒 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
Userinterface defined inapps/backend/src/types/auth.types.tsthat might conflict with the SupabaseUsertype. 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 3Length 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.tsLength of output: 1432
No Type Conflicts Detected
I verified the type definitions and imports:
apps/backend/src/types/express/index.d.tsimportsUserdirectly from@supabase/supabase-jsand uses it forRequest.user.apps/backend/src/types/auth.types.tsimports the same SupabaseUserunder the aliasuser, then declares its own localUserinterface—this is scoped to that file and isn’t imported elsewhere.Because each
Usertype 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 = userbut we should ensure the Express Request type has been properly augmented to include theuserproperty.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 tsLength 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 tsLength of output: 1175
Express Request
useraugmentation verifiedType augmentation for
Request.useris 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
safeParseprovides 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
PublicProfileinterface 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_activetimestamp 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
| 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' | ||
|
|
There was a problem hiding this comment.
💡 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"
fiLength 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.pngfixture undertests/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.
| 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.
apps/backend/test_endpoints.sh
Outdated
| # 🔐 Replace with your actual token or load from environment | ||
| AUTH1_TOKEN="eyJhbGciOiJIUzI1NiIsImtpZCI6IjBJMjBNZlg1TGlXSDg4eXEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2dtcHBrbHl3b3Bna2Jkdmpvb2ZnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiJlNjRmNzJkZS01OGFiLTQ3N2QtYTIxMi0xMmNiNGUwMWFlMTEiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzQ5Mzg2NDEwLCJpYXQiOjE3NDkzODI4MTAsImVtYWlsIjoidGVzdDExQGV4YW1wbGUuY29tIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InRlc3QxMUBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaG9uZV92ZXJpZmllZCI6ZmFsc2UsInN1YiI6ImU2NGY3MmRlLTU4YWItNDc3ZC1hMjEyLTEyY2I0ZTAxYWUxMSJ9LCJyb2xlIjoiYXV0aGVudGljYXRlZCIsImFhbCI6ImFhbDEiLCJhbXIiOlt7Im1ldGhvZCI6InBhc3N3b3JkIiwidGltZXN0YW1wIjoxNzQ5MzgyODEwfV0sInNlc3Npb25faWQiOiIzZmFkZmE3Mi1mZDNmLTQyN2UtYmNmMS0wMGM0OTQwOTAxMjkiLCJpc19hbm9ueW1vdXMiOmZhbHNlfQ.vZnnQm7BOkxglxbyvkklRB5xBx__DzF6wEIi_2u-N1o" |
There was a problem hiding this comment.
Fix variable name inconsistency and security issue.
Two issues identified:
- Variable
AUTH1_TOKENis defined butAUTH_TOKENis used in the tests (lines 31, 35, 40, 63) - 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.
| # 🔐 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'; |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
| - **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.
| 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 }); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🛠️ 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:
- Both return
{ user, token }(extract token from session in login), or - 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.
| if (error) return { error }; | ||
| return data; |
There was a problem hiding this comment.
🛠️ 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.
| const { error: uploadError } = await supabase.storage | ||
| .from('avatars') |
There was a problem hiding this comment.
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.
| 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, |
There was a problem hiding this comment.
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.
| -- 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' | ||
| ); | ||
|
|
There was a problem hiding this comment.
🛠️ 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.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
apps/backend/database/README.md (2)
35-35: Fix description for profile avatars bucketThe 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 commandThe 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 indentationThe 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
📒 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 ofprofilestable to overviewThe new
profilesentry correctly documents the addition of the user profile table.
147-147: Approve reset script drop ofprofilestableThe
DROP TABLE IF EXISTS public.profiles CASCADE;command aligns with the addition of the table and ensures proper cleanup.
|
@Benjtalkshow request for review |
|
Amazing job — everything looks great! Thanks so much for contributing to StellarRent, really appreciate your work! |
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
🖼️ Current Output
Provide visual evidence of the changes:
🧪 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
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:
💬 Comments
Any additional context, questions, or considerations for reviewers.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests