Add user management, API testing tools, and fix database connection issues

- Added user routes (CRUD operations) for managing users
- Created Postman collection for API testing
- Added HTML test page for interactive API testing
- Fixed UUID handling for species_id and breed_id
- Added user validation for listings and locations
- Improved error handling and validation
- Added static file serving for test page
- Updated server configuration with all routes
This commit is contained in:
Chandresh Kerkar 2025-12-20 23:47:27 +05:30
parent 9f6f7a0bab
commit b3899dc14d
8 changed files with 2155 additions and 14 deletions

View File

@ -0,0 +1,637 @@
{
"info": {
"_postman_id": "buysellservice-api-collection",
"name": "BuySellService API",
"description": "Complete API collection for Livestock Marketplace - BuySellService",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Users",
"item": [
{
"name": "Create User",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"id\": \"aaf295cb-a19e-4179-a2df-31c0c64ea9f4\",\n \"name\": \"Test User\",\n \"phone_number\": \"+919876543210\",\n \"avatar_url\": null,\n \"language\": \"en\",\n \"timezone\": \"Asia/Kolkata\",\n \"country_code\": \"+91\"\n}"
},
"url": {
"raw": "http://localhost:3200/users",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"users"
]
},
"description": "Create a new user. You can provide a specific UUID or let the system generate one."
}
},
{
"name": "Get All Users",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/users?limit=100&offset=0",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"users"
],
"query": [
{
"key": "limit",
"value": "100"
},
{
"key": "offset",
"value": "0"
}
]
},
"description": "Get a list of all users"
}
},
{
"name": "Get User by ID",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/users/:userId",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"users",
":userId"
],
"variable": [
{
"key": "userId",
"value": "aaf295cb-a19e-4179-a2df-31c0c64ea9f4"
}
]
},
"description": "Get a single user by UUID"
}
},
{
"name": "Update User",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"Updated User Name\",\n \"phone_number\": \"+919876543210\"\n}"
},
"url": {
"raw": "http://localhost:3200/users/:userId",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"users",
":userId"
],
"variable": [
{
"key": "userId",
"value": "aaf295cb-a19e-4179-a2df-31c0c64ea9f4"
}
]
},
"description": "Update user information"
}
}
]
},
{
"name": "Listings",
"item": [
{
"name": "Get All Listings",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/listings?status=active&limit=20&offset=0",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"listings"
],
"query": [
{
"key": "status",
"value": "active"
},
{
"key": "limit",
"value": "20"
},
{
"key": "offset",
"value": "0"
}
]
},
"description": "Get all active listings"
}
},
{
"name": "Get Listing by ID",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/listings/:listingId",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"listings",
":listingId"
],
"variable": [
{
"key": "listingId",
"value": "your-listing-uuid-here"
}
]
},
"description": "Get a single listing with full details"
}
},
{
"name": "Create Listing",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"seller_id\": \"aaf295cb-a19e-4179-a2df-31c0c64ea9f4\",\n \"title\": \"High-yield Gir cow for sale\",\n \"price\": 55000,\n \"currency\": \"INR\",\n \"is_negotiable\": true,\n \"listing_type\": \"sale\",\n \"animal\": {\n \"species_id\": \"your-species-uuid\",\n \"breed_id\": \"your-breed-uuid\",\n \"sex\": \"F\",\n \"age_months\": 36,\n \"weight_kg\": 450,\n \"color_markings\": \"Brown with white patches\",\n \"quantity\": 1,\n \"purpose\": \"dairy\",\n \"health_status\": \"healthy\",\n \"vaccinated\": true,\n \"dewormed\": true,\n \"pregnancy_status\": \"pregnant\",\n \"milk_yield_litre_per_day\": 15,\n \"ear_tag_no\": \"TAG-12345\",\n \"description\": \"Calm nature, easy to handle.\"\n },\n \"media\": [\n {\n \"media_url\": \"https://cdn.app.com/listings/abc1.jpg\",\n \"media_type\": \"image\",\n \"is_primary\": true,\n \"sort_order\": 1\n }\n ]\n}"
},
"url": {
"raw": "http://localhost:3200/listings",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"listings"
]
},
"description": "Create a new listing with animal details"
}
},
{
"name": "Update Listing",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"title\": \"Updated title\",\n \"price\": 52000,\n \"status\": \"active\"\n}"
},
"url": {
"raw": "http://localhost:3200/listings/:listingId",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"listings",
":listingId"
],
"variable": [
{
"key": "listingId",
"value": "your-listing-uuid-here"
}
]
},
"description": "Update listing information"
}
},
{
"name": "Search Listings",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/listings/search?q=cow&limit=20&offset=0",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"listings",
"search"
],
"query": [
{
"key": "q",
"value": "cow"
},
{
"key": "limit",
"value": "20"
},
{
"key": "offset",
"value": "0"
}
]
},
"description": "Search listings by text query"
}
},
{
"name": "Near Me Search",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/listings/near-me?lat=18.5204&lng=73.8567&radius_meters=100000&limit=20",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"listings",
"near-me"
],
"query": [
{
"key": "lat",
"value": "18.5204"
},
{
"key": "lng",
"value": "73.8567"
},
{
"key": "radius_meters",
"value": "100000"
},
{
"key": "limit",
"value": "20"
}
]
},
"description": "Find listings near a specific location"
}
},
{
"name": "Get Species",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/listings/species",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"listings",
"species"
]
},
"description": "Get all available species"
}
},
{
"name": "Get Breeds",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/listings/breeds?species_id=your-species-uuid",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"listings",
"breeds"
],
"query": [
{
"key": "species_id",
"value": "your-species-uuid",
"description": "Optional: Filter breeds by species"
}
]
},
"description": "Get all available breeds (optionally filtered by species)"
}
}
]
},
{
"name": "Locations",
"item": [
{
"name": "Create Location",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"user_id\": \"aaf295cb-a19e-4179-a2df-31c0c64ea9f4\",\n \"lat\": 18.15,\n \"lng\": 74.5833,\n \"country\": \"India\",\n \"state\": \"Maharashtra\",\n \"district\": \"Pune\",\n \"city_village\": \"Baramati\",\n \"pincode\": \"413102\",\n \"location_type\": \"farm\",\n \"is_saved_address\": true,\n \"source_type\": \"manual\",\n \"source_confidence\": \"high\"\n}"
},
"url": {
"raw": "http://localhost:3200/locations",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"locations"
]
},
"description": "Create a new location. user_id is optional for captured locations but required for saved addresses."
}
},
{
"name": "Get User Locations",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/locations/user/:userId",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"locations",
"user",
":userId"
],
"variable": [
{
"key": "userId",
"value": "aaf295cb-a19e-4179-a2df-31c0c64ea9f4"
}
]
},
"description": "Get all locations for a specific user"
}
},
{
"name": "Get Location by ID",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/locations/:locationId",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"locations",
":locationId"
],
"variable": [
{
"key": "locationId",
"value": "your-location-uuid-here"
}
]
},
"description": "Get a single location by ID"
}
},
{
"name": "Update Location",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"city_village\": \"Updated City Name\",\n \"pincode\": \"413103\"\n}"
},
"url": {
"raw": "http://localhost:3200/locations/:locationId",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"locations",
":locationId"
],
"variable": [
{
"key": "locationId",
"value": "your-location-uuid-here"
}
]
},
"description": "Update location information"
}
}
]
},
{
"name": "Chat",
"item": [
{
"name": "Create/Get Conversation",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"buyer_id\": \"buyer-uuid-here\",\n \"seller_id\": \"seller-uuid-here\"\n}"
},
"url": {
"raw": "http://localhost:3200/chat/conversations",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"chat",
"conversations"
]
},
"description": "Create a new conversation or get existing one between buyer and seller"
}
},
{
"name": "Get User Conversations",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/chat/conversations/user/:userId",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"chat",
"conversations",
"user",
":userId"
],
"variable": [
{
"key": "userId",
"value": "aaf295cb-a19e-4179-a2df-31c0c64ea9f4"
}
]
},
"description": "Get all conversations for a specific user"
}
},
{
"name": "Get Messages",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3200/chat/conversations/:conversationId/messages?limit=50&offset=0",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"chat",
"conversations",
":conversationId",
"messages"
],
"query": [
{
"key": "limit",
"value": "50"
},
{
"key": "offset",
"value": "0"
}
],
"variable": [
{
"key": "conversationId",
"value": "your-conversation-uuid-here"
}
]
},
"description": "Get messages for a conversation"
}
},
{
"name": "Send Message",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"conversation_id\": \"conversation-uuid-here\",\n \"sender_id\": \"sender-uuid-here\",\n \"receiver_id\": \"receiver-uuid-here\",\n \"content\": \"Hello, I'm interested in your listing.\",\n \"message_type\": \"text\"\n}"
},
"url": {
"raw": "http://localhost:3200/chat/messages",
"protocol": "http",
"host": [
"localhost"
],
"port": "3200",
"path": [
"chat",
"messages"
]
},
"description": "Send a message in a conversation"
}
}
]
}
],
"variable": [
{
"key": "baseUrl",
"value": "http://localhost:3200",
"type": "string"
}
]
}

143
POSTMAN_TESTING_GUIDE.md Normal file
View File

@ -0,0 +1,143 @@
# Postman Testing Guide for BuySellService API
## 📥 How to Import the Collection
1. **Open Postman**
2. Click **Import** button (top left)
3. Select **File** tab
4. Choose `BuySellService_API.postman_collection.json`
5. Click **Import**
The collection will appear in your Postman sidebar with all endpoints organized by category.
## 🚀 Quick Start Testing
### Step 1: Create a User (Required First Step)
Before creating listings or locations, you need to create a user:
1. Go to **Users** → **Create User**
2. The request body is pre-filled with the UUID from your error: `aaf295cb-a19e-4179-a2df-31c0c64ea9f4`
3. Click **Send**
4. You should get a 201 response with the created user
**Request Body:**
```json
{
"id": "aaf295cb-a19e-4179-a2df-31c0c64ea9f4",
"name": "Test User",
"phone_number": "+919876543210",
"avatar_url": null,
"language": "en",
"timezone": "Asia/Kolkata",
"country_code": "+91"
}
```
### Step 2: Get Species and Breeds
Before creating a listing, you need to know the UUIDs of species and breeds:
1. Go to **Listings** → **Get Species**
2. Click **Send** - This will show you all available species with their UUIDs
3. Copy a species UUID
4. Go to **Listings** → **Get Breeds**
5. Add the species UUID as a query parameter: `?species_id=your-species-uuid`
6. Click **Send** - This will show breeds for that species
7. Copy a breed UUID
### Step 3: Create a Location (Optional)
1. Go to **Locations** → **Create Location**
2. Update the `user_id` in the request body to your user UUID
3. Modify other fields as needed
4. Click **Send**
### Step 4: Create a Listing
1. Go to **Listings** → **Create Listing**
2. Update the request body:
- `seller_id`: Use your user UUID
- `species_id`: Use the species UUID from Step 2
- `breed_id`: Use the breed UUID from Step 2
3. Click **Send**
## 📋 Endpoint Categories
### 👤 Users
- **Create User** - Create a new user (with optional UUID)
- **Get All Users** - List all users
- **Get User by ID** - Get specific user details
- **Update User** - Update user information
### 📋 Listings
- **Get All Listings** - List all active listings
- **Get Listing by ID** - Get specific listing with full details
- **Create Listing** - Create a new listing with animal details
- **Update Listing** - Update listing information
- **Search Listings** - Search by text query
- **Near Me Search** - Find listings near coordinates
- **Get Species** - Get all available species
- **Get Breeds** - Get all breeds (optionally filtered by species)
### 📍 Locations
- **Create Location** - Create a location (user_id optional for captured locations)
- **Get User Locations** - Get all locations for a user
- **Get Location by ID** - Get specific location
- **Update Location** - Update location information
### 💬 Chat
- **Create/Get Conversation** - Create or get existing conversation
- **Get User Conversations** - Get all conversations for a user
- **Get Messages** - Get messages in a conversation
- **Send Message** - Send a message
## 🔧 Important Notes
### UUIDs Required
- All IDs in this API are **UUIDs**, not integers
- Make sure to use valid UUID format: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
### User ID Requirements
- **For Listings**: `seller_id` must exist in users table
- **For Locations**:
- `user_id` is **optional** if `is_saved_address = false` (captured location)
- `user_id` is **required** if `is_saved_address = true` (saved address)
### Testing Order
1. ✅ Create User first
2. ✅ Get Species and Breeds
3. ✅ Create Location (optional)
4. ✅ Create Listing
## 🐛 Common Errors
### "User does not exist"
- **Solution**: Create the user first using the **Create User** endpoint
### "Invalid input syntax for type uuid"
- **Solution**: Make sure you're using UUIDs, not integers. Check species_id and breed_id are UUIDs.
### "Foreign key constraint violation"
- **Solution**: Ensure all referenced IDs (user_id, species_id, breed_id) exist in their respective tables
## 💡 Tips
1. **Use Environment Variables**: Create a Postman environment with:
- `baseUrl`: `http://localhost:3200`
- `userId`: Your user UUID
- `speciesId`: A species UUID
- `breedId`: A breed UUID
2. **Save Responses**: After creating resources, save the returned UUIDs for use in other requests
3. **Test in Order**: Follow the testing order above to avoid foreign key errors
4. **Check Server**: Make sure your server is running on port 3200 before testing
## 🔗 Base URL
All endpoints use: `http://localhost:3200`
If your server runs on a different port, update the base URL in the collection variables.

View File

@ -5,7 +5,9 @@
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js",
"dev": "node server.js"
},
"repository": {
"type": "git",

1108
public/index.html Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,20 @@ const createNewLocation = async (client, userId, locationData) => {
pincode,
} = locationData;
// Validate user exists if this is a saved address
let finalUserId = userId;
if (is_saved_address && userId) {
const userCheck = await client.query("SELECT id FROM users WHERE id = $1 AND deleted = FALSE", [userId]);
if (userCheck.rows.length === 0) {
throw new Error(`User with id ${userId} does not exist. Cannot create saved address for non-existent user.`);
}
} else if (!is_saved_address) {
// For captured locations (not saved addresses), user_id can be NULL
finalUserId = null;
} else if (is_saved_address && !userId) {
throw new Error("user_id is required when is_saved_address is true");
}
// 1. Insert into locations (Merged Table)
const insertLocationQuery = `
INSERT INTO locations (
@ -31,7 +45,7 @@ const createNewLocation = async (client, userId, locationData) => {
RETURNING id
`;
const locationValues = [
userId,
finalUserId,
lat,
lng,
source_type || "manual",
@ -266,6 +280,18 @@ router.post("/", async (req, res) => {
media // Array of { media_url, media_type, is_primary, sort_order }
} = req.body;
// Validate seller exists
if (seller_id) {
const sellerCheck = await client.query("SELECT id FROM users WHERE id = $1 AND deleted = FALSE", [seller_id]);
if (sellerCheck.rows.length === 0) {
await client.query("ROLLBACK");
return res.status(400).json({ error: `Seller with id ${seller_id} does not exist. Please create the user first.` });
}
} else {
await client.query("ROLLBACK");
return res.status(400).json({ error: "seller_id is required" });
}
let final_location_id = animal?.location_id;
// 1. Create Location (if needed)
@ -531,11 +557,32 @@ router.delete("/:id", async (req, res) => {
});
// Get the list of species
router.get("/species", async (req, res) => {
try {
const queryText = "SELECT * FROM species WHERE deleted = FALSE ORDER BY name";
const result = await pool.query(queryText);
res.json(result.rows);
} catch (err) {
console.error("Error fetching species:", err);
res.status(500).json({ error: "Internal server error" });
}
});
// Get the list of breeds
router.get("/breeds", async (req, res) => {
try {
const queryText = "SELECT * FROM breeds";
const result = await pool.query(queryText);
const { species_id } = req.query;
let queryText = "SELECT * FROM breeds WHERE deleted = FALSE";
const queryParams = [];
if (species_id) {
queryText += " AND species_id = $1";
queryParams.push(species_id);
}
queryText += " ORDER BY name";
const result = await pool.query(queryText, queryParams);
res.json(result.rows);
} catch (err) {
console.error("Error fetching breeds:", err);

View File

@ -25,6 +25,26 @@ router.post("/", async (req, res) => {
pincode,
} = req.body;
// Validate user exists if this is a saved address
let finalUserId = user_id;
if (is_saved_address && user_id) {
const userCheck = await client.query("SELECT id FROM users WHERE id = $1 AND deleted = FALSE", [user_id]);
if (userCheck.rows.length === 0) {
await client.query("ROLLBACK");
return res.status(400).json({
error: `User with id ${user_id} does not exist. Cannot create saved address for non-existent user.`
});
}
} else if (!is_saved_address) {
// For captured locations (not saved addresses), user_id can be NULL
finalUserId = null;
} else if (is_saved_address && !user_id) {
await client.query("ROLLBACK");
return res.status(400).json({
error: "user_id is required when is_saved_address is true"
});
}
// 1. Insert into locations
const insertLocationQuery = `
INSERT INTO locations (
@ -35,7 +55,7 @@ router.post("/", async (req, res) => {
RETURNING *
`;
const locationValues = [
user_id,
finalUserId,
lat,
lng,
source_type || "manual",
@ -59,7 +79,15 @@ router.post("/", async (req, res) => {
} catch (err) {
await client.query("ROLLBACK");
console.error("Error creating location:", err);
res.status(500).json({ error: "Internal server error" });
// Provide more specific error messages
if (err.code === '23503') { // Foreign key violation
return res.status(400).json({
error: `Foreign key constraint violation: ${err.detail || 'The provided user_id does not exist in the users table.'}`
});
}
res.status(500).json({ error: err.message || "Internal server error" });
} finally {
client.release();
}

169
routes/userRoutes.js Normal file
View File

@ -0,0 +1,169 @@
import express from "express";
import pool from "../db/pool.js";
const router = express.Router();
// 1. CREATE User
router.post("/", async (req, res) => {
const client = await pool.connect();
try {
await client.query("BEGIN");
const {
id, // Optional: if provided, use this UUID; otherwise generate one
name,
phone_number,
avatar_url,
language,
timezone,
country_code = "+91",
} = req.body;
// If id is provided, use it; otherwise let the database generate one
const insertUserQuery = id
? `
INSERT INTO users (id, name, phone_number, avatar_url, language, timezone, country_code)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *
`
: `
INSERT INTO users (name, phone_number, avatar_url, language, timezone, country_code)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *
`;
const userValues = id
? [id, name, phone_number, avatar_url, language, timezone, country_code]
: [name, phone_number, avatar_url, language, timezone, country_code];
const userResult = await client.query(insertUserQuery, userValues);
await client.query("COMMIT");
res.status(201).json(userResult.rows[0]);
} catch (err) {
await client.query("ROLLBACK");
console.error("Error creating user:", err);
if (err.code === "23505") {
// Unique constraint violation
return res.status(400).json({
error: err.detail || "A user with this phone number or ID already exists",
});
}
if (err.code === "23503") {
// Foreign key violation
return res.status(400).json({
error: err.detail || "Foreign key constraint violation",
});
}
res.status(500).json({ error: err.message || "Internal server error" });
} finally {
client.release();
}
});
// 2. GET All Users
router.get("/", async (req, res) => {
try {
const { limit = 100, offset = 0 } = req.query;
const queryText = `
SELECT id, name, phone_number, avatar_url, language, timezone, country_code,
is_active, created_at, updated_at
FROM users
WHERE deleted = FALSE
ORDER BY created_at DESC
LIMIT $1 OFFSET $2
`;
const result = await pool.query(queryText, [limit, offset]);
res.json(result.rows);
} catch (err) {
console.error("Error fetching users:", err);
res.status(500).json({ error: "Internal server error" });
}
});
// 3. GET Single User
router.get("/:id", async (req, res) => {
try {
const { id } = req.params;
const queryText = `
SELECT id, name, phone_number, avatar_url, language, timezone, country_code,
is_active, created_at, updated_at
FROM users
WHERE id = $1 AND deleted = FALSE
`;
const result = await pool.query(queryText, [id]);
if (result.rows.length === 0) {
return res.status(404).json({ error: "User not found" });
}
res.json(result.rows[0]);
} catch (err) {
console.error("Error fetching user:", err);
res.status(500).json({ error: "Internal server error" });
}
});
// 4. UPDATE User
router.put("/:id", async (req, res) => {
try {
const { id } = req.params;
const { name, phone_number, avatar_url, language, timezone, country_code, is_active } = req.body;
const updateQuery = `
UPDATE users
SET name = COALESCE($1, name),
phone_number = COALESCE($2, phone_number),
avatar_url = COALESCE($3, avatar_url),
language = COALESCE($4, language),
timezone = COALESCE($5, timezone),
country_code = COALESCE($6, country_code),
is_active = COALESCE($7, is_active)
WHERE id = $8 AND deleted = FALSE
RETURNING *
`;
const values = [name, phone_number, avatar_url, language, timezone, country_code, is_active, id];
const result = await pool.query(updateQuery, values);
if (result.rows.length === 0) {
return res.status(404).json({ error: "User not found" });
}
res.json(result.rows[0]);
} catch (err) {
console.error("Error updating user:", err);
res.status(500).json({ error: "Internal server error" });
}
});
// 5. DELETE User (Soft Delete)
router.delete("/:id", async (req, res) => {
try {
const { id } = req.params;
const queryText = `
UPDATE users
SET deleted = TRUE
WHERE id = $1
RETURNING id
`;
const result = await pool.query(queryText, [id]);
if (result.rows.length === 0) {
return res.status(404).json({ error: "User not found" });
}
res.json({ message: "User deleted successfully" });
} catch (err) {
console.error("Error deleting user:", err);
res.status(500).json({ error: "Internal server error" });
}
});
export default router;

View File

@ -1,24 +1,31 @@
import express from "express";
import cors from "cors";
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import http from "http";
import listingRoutes from "./routes/listingRoutes.js";
import locationRoutes from "./routes/locationRoutes.js";
import chatRoutes from "./routes/chatRoutes.js";
import userRoutes from "./routes/userRoutes.js";
import { initSocket } from "./socket.js";
import { startExpirationJob } from "./jobs/expirationJob.js";
const app = express();
app.use(cors());
app.use(express.json());
// Serve static files from public directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
app.use(express.static(join(__dirname, 'public')));
const PORT = process.env.PORT || 3200;
// Add routes here
import listingRoutes from "./routes/listingRoutes.js";
import locationRoutes from "./routes/locationRoutes.js";
import chatRoutes from "./routes/chatRoutes.js";
app.use("/listings", listingRoutes);
app.use("/locations", locationRoutes);
app.use("/chat", chatRoutes);
import http from "http";
import { initSocket } from "./socket.js";
import { startExpirationJob } from "./jobs/expirationJob.js";
app.use("/users", userRoutes);
const server = http.createServer(app);
initSocket(server);