Files
looking-monorepo/backend/API.md
2025-12-28 13:52:25 -03:00

17 KiB

Looking API Reference

Complete REST API documentation for the Looking backend

This document provides detailed information about all API endpoints, including request/response formats, authentication requirements, and error codes.


📋 Table of Contents


🌐 Base URL

Development: http://localhost:3069
Production: https://api.pfosi.mifi.dev

All endpoints are prefixed with the base URL.


🔐 Authentication

Most endpoints require JWT authentication via the Authorization header.

Header Format

Authorization: Bearer <jwt_token>

Permission Levels

Permission Description
view Read-only access to profiles
add Create new profiles
edit Modify existing profiles
update Update profile details
delete Remove profiles
manage Approve submissions, manage content
super Full admin access, user management

Obtaining a Token

Use the /auth/login endpoint (see Authentication Endpoints).


📊 Common Response Codes

Code Status Description
200 OK Request successful
201 Created Resource created successfully
400 Bad Request Invalid request data
401 Unauthorized Missing or invalid token
403 Forbidden Insufficient permissions
404 Not Found Resource not found
500 Internal Server Error Server error

🔑 Authentication Endpoints

POST /auth/login

Authenticate user and receive JWT token.

Request:

POST /auth/login
Content-Type: application/json

{
  "username": "admin",
  "password": "password123"
}

Success Response (200):

{
  "status": 200,
  "authorized": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "507f1f77bcf86cd799439011",
    "username": "admin",
    "name": {
      "first": "John",
      "last": "Doe"
    },
    "email": "admin@example.com",
    "can": ["add", "edit", "delete", "manage", "super", "update", "view"]
  }
}

Failure Response (401):

{
  "status": 401,
  "authorized": false,
  "message": "Invalid username or password"
}

POST /auth/logout

Logout user (client-side token removal, no server-side action needed).

Request:

POST /auth/logout
Authorization: Bearer <token>

Response (200):

{
  "message": "Logged out successfully"
}

GET /auth/session

Validate existing JWT token and get user info.

Request:

GET /auth/session
Authorization: Bearer <token>

Success Response (200):

{
  "valid": true,
  "decoded": {
    "uid": "507f1f77bcf86cd799439011",
    "username": "admin",
    "can": ["add", "edit", "delete", "manage", "super", "update", "view"],
    "iat": 1640000000,
    "exp": 1640003600
  }
}

Failure Response (401):

{
  "valid": false,
  "message": "Token expired or invalid"
}

POST /auth/session

Create anonymous session token (limited permissions).

Request:

POST /auth/session

Response (200):

{
  "status": 200,
  "authorized": false,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

PUT /auth/session

Refresh existing JWT token (extend expiration).

Request:

PUT /auth/session
Authorization: Bearer <token>

Response (200):

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": "15m"
}

POST /auth/reset

Request password reset email.

Request:

POST /auth/reset
Content-Type: application/json

{
  "username": "admin"
}

Response (200):

{
  "message": "Password reset email sent",
  "email": "ad***@example.com"
}

GET /auth/reset/:id/:token

Verify password reset token validity.

Request:

GET /auth/reset/507f1f77bcf86cd799439011/abc123def456

Response (200):

{
  "valid": true,
  "userId": "507f1f77bcf86cd799439011",
  "expires": "2024-01-15T12:00:00Z"
}

PUT /auth/reset/:id/:token

Reset password with valid token.

Request:

PUT /auth/reset/507f1f77bcf86cd799439011/abc123def456
Content-Type: application/json

{
  "password": "newPassword123",
  "confirmPassword": "newPassword123"
}

Response (200):

{
  "message": "Password updated successfully",
  "success": true
}

👤 Profile Endpoints

GET /profiles

Get all approved profiles.

Request:

GET /profiles

Response (200):

[
  {
    "_id": "507f1f77bcf86cd799439011",
    "order": 1,
    "details": {
      "name": "John",
      "age": 28,
      "location": "San Francisco, CA",
      "about": "User's story about dating...",
      "pic": {
        "thumb": "profile/john_thumbnail.png",
        "detail": "profile/john_detail.png"
      },
      "position": ["Top", "Versatile"],
      "looking": ["Dates", "Friends"],
      "tribes": ["Geek", "Jock"],
      "ethnos": ["White", "Latino"]
    },
    "messages": [
      {
        "_id": "507f191e810c19729de860ea",
        "text": "What brought you to dating apps?",
        "isUser": false,
        "timestamp": "2024-01-15T10:30:00Z"
      },
      {
        "_id": "507f191e810c19729de860eb",
        "text": "I moved to a new city...",
        "isUser": true,
        "timestamp": "2024-01-15T10:32:00Z"
      }
    ],
    "submitted": true,
    "approved": true
  }
]

GET /profiles/submitted

Get all submitted (pending approval) profiles.

Authentication: Required (view permission)

Request:

GET /profiles/submitted
Authorization: Bearer <token>

Response (200):

[
  {
    "_id": "507f1f77bcf86cd799439012",
    "details": { ... },
    "submitted": true,
    "approved": false
  }
]

GET /profiles/verified

Alias for approved profiles.

Request:

GET /profiles/verified

Response: Same as GET /profiles


GET /profiles/:id

Get single profile by ID.

Request:

GET /profiles/507f1f77bcf86cd799439011

Response (200):

{
  "_id": "507f1f77bcf86cd799439011",
  "order": 1,
  "details": { ... },
  "messages": [ ... ],
  "submitted": true,
  "approved": true
}

Error (404):

{
  "message": "Profile not found"
}

POST /profiles

Create new profile.

Authentication: Required (add permission)

Request:

POST /profiles
Authorization: Bearer <token>
Content-Type: application/json

{
  "order": 10,
  "details": {
    "name": "Alex",
    "age": 25,
    "location": "New York, NY",
    "about": "My story...",
    "pic": {
      "thumb": "data:image/png;base64,iVBORw0KG...",  // Base64 or path
      "detail": "data:image/png;base64,iVBORw0KG..."
    }
  },
  "messages": [
    {
      "text": "Question text",
      "isUser": false
    }
  ],
  "submitted": true,
  "approved": false
}

Response (201):

{
  "_id": "507f1f77bcf86cd799439013",
  "message": "Profile created successfully",
  "profile": { ... }
}

PUT /profiles/:id

Update existing profile.

Authentication: Required (update permission)

Request:

PUT /profiles/507f1f77bcf86cd799439011
Authorization: Bearer <token>
Content-Type: application/json

{
  "details": {
    "about": "Updated story..."
  }
}

Response (200):

{
  "message": "Profile updated successfully",
  "profile": { ... }
}

DELETE /profiles/:id

Delete profile by ID.

Authentication: Required (delete permission)

Request:

DELETE /profiles/507f1f77bcf86cd799439011
Authorization: Bearer <token>

Response (200):

{
  "message": "Profile deleted successfully",
  "id": "507f1f77bcf86cd799439011"
}

GET /profiles/approve/:id

Approve submitted profile.

Authentication: Required (manage permission)

Request:

GET /profiles/approve/507f1f77bcf86cd799439012
Authorization: Bearer <token>

Response (200):

{
  "message": "Profile approved",
  "profile": {
    "_id": "507f1f77bcf86cd799439012",
    "approved": true
  }
}

POST /profiles/submission

Submit new profile (public endpoint, no auth required).

Request:

POST /profiles/submission
Content-Type: application/json

{
  "details": {
    "name": "Anonymous",
    "age": 30,
    "location": "Los Angeles, CA",
    "about": "My dating story...",
    "pic": {
      "thumb": "data:image/png;base64,...",
      "detail": "data:image/png;base64,..."
    }
  },
  "messages": [
    {
      "text": "Tell us your story",
      "isUser": false
    },
    {
      "text": "I've been using apps for 3 years...",
      "isUser": true
    }
  ]
}

Response (200):

{
  "message": "Story submitted successfully. It will be reviewed before publishing.",
  "profile": {
    "_id": "507f1f77bcf86cd799439014",
    "submitted": true,
    "approved": false
  }
}

GET /profiles/find/:limit?/:skip?/:min?/:max?/:pos?/:lkng?/:tribes?/:ethnos?

Advanced profile search with filters.

Authentication: Required (view permission)

Parameters:

  • limit - Number of results (default: all)
  • skip - Number to skip for pagination
  • min - Minimum age
  • max - Maximum age
  • pos - Position filter (pipe-separated: "Top|Bottom|Versatile")
  • lkng - Looking for filter (pipe-separated: "Dates|Friends|Relationship")
  • tribes - Tribes filter (pipe-separated: "Geek|Jock|Bear|Otter")
  • ethnos - Ethnicity filter (pipe-separated: "White|Black|Latino|Asian")

Request:

GET /profiles/find/10/0/25/35/Top|Versatile/Dates|Friends/Geek/null
Authorization: Bearer <token>

Response (200):

[
  {
    "_id": "507f1f77bcf86cd799439011",
    "details": {
      "age": 28,
      "position": ["Top", "Versatile"],
      "looking": ["Dates", "Friends"],
      "tribes": ["Geek"]
    }
  }
]

GET /profiles/list/:limit?/:skip?/:min?/:max?/:pos?/:lkng?/:tribes?/:ethnos?

List profiles with minimal data (name, thumbnail only).

Authentication: Required (view permission)

Response (200):

[
  {
    "_id": "507f1f77bcf86cd799439011",
    "order": 1,
    "details": {
      "name": "John",
      "pic": {
        "thumb": "profile/john_thumbnail.png"
      }
    }
  }
]

PUT /profiles/:profileId/message/:messageId

Update specific message in profile thread.

Authentication: Required (update permission)

Request:

PUT /profiles/507f1f77bcf86cd799439011/message/507f191e810c19729de860ea
Authorization: Bearer <token>
Content-Type: application/json

{
  "text": "Updated question text",
  "image": "data:image/png;base64,..."
}

Response (200):

{
  "message": "Message updated successfully",
  "profile": { ... }
}

👥 User Endpoints

GET /users

Get all users.

Authentication: Required (super permission)

Request:

GET /users
Authorization: Bearer <token>

Response (200):

[
  {
    "_id": "507f1f77bcf86cd799439011",
    "username": "admin",
    "name": {
      "first": "John",
      "last": "Doe"
    },
    "email": "admin@example.com",
    "can": ["add", "edit", "delete", "manage", "super", "update", "view"],
    "forceReset": false
  }
]

GET /users/:id

Get user by ID.

Authentication: Required (super permission)

Response (200):

{
  "_id": "507f1f77bcf86cd799439011",
  "username": "admin",
  "name": { "first": "John", "last": "Doe" },
  "email": "admin@example.com",
  "can": ["super"]
}

POST /users

Create new user.

Authentication: Required (super permission)

Request:

POST /users
Authorization: Bearer <token>
Content-Type: application/json

{
  "username": "newuser",
  "password": "password123",
  "confirmPassword": "password123",
  "name": {
    "first": "Jane",
    "last": "Smith"
  },
  "email": "jane@example.com",
  "can": ["view", "add"]
}

Response (201):

{
  "message": "User created successfully",
  "user": {
    "_id": "507f1f77bcf86cd799439015",
    "username": "newuser"
  }
}

PUT /users/:id

Update user information.

Authentication: Required (super permission or own user)

Request:

PUT /users/507f1f77bcf86cd799439011
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": {
    "first": "John",
    "last": "Updated"
  },
  "email": "newemail@example.com",
  "password": "newPassword123",
  "confirmPassword": "newPassword123",
  "currentPassword": "oldPassword123"
}

Response (200):

{
  "message": "User updated successfully",
  "user": { ... }
}

DELETE /users/:id

Delete user.

Authentication: Required (super permission)

Request:

DELETE /users/507f1f77bcf86cd799439011
Authorization: Bearer <token>

Response (200):

{
  "message": "User deleted successfully"
}

🗺️ Geocache Endpoints

GET /geocache/populate/:field

Populate geocache for a specific field (e.g., location).

Authentication: Required (manageAppPreferences permission)

Request:

GET /geocache/populate/location
Authorization: Bearer <token>

Response (200):

{
  "message": "Geocache populated",
  "count": 42,
  "results": [
    {
      "key": "San Francisco, CA",
      "formatted": "San Francisco, California, USA",
      "loc": {
        "type": "Point",
        "coordinates": [-122.4194, 37.7749]
      }
    }
  ]
}

PUT /geocache/:id

Update geocache entry.

Authentication: Required (manageAppPreferences permission)

Request:

PUT /geocache/507f1f77bcf86cd799439011
Authorization: Bearer <token>
Content-Type: application/json

{
  "key": "New York, NY",
  "formatted": "New York, New York, USA",
  "loc": {
    "type": "Point",
    "coordinates": [-74.0060, 40.7128]
  }
}

Response (200):

{
  "message": "Geocache updated",
  "geocache": { ... }
}

⚠️ Error Handling

Error Response Format

All errors follow this structure:

{
  "message": "Human-readable error message",
  "err": {
    "code": "ERROR_CODE",
    "details": "Additional error information"
  }
}

Common Error Messages

Error Code Cause
User not authorized to perform this action 403 Missing required permission
Token expired or invalid 401 JWT validation failed
Profile not found 404 Invalid profile ID
Invalid username or password 401 Login failed
Could not update profile 500 Database error during update
There was an error processing the image 500 Image upload/processing failed
No profile id or data specified 500 Missing required request data

Authentication Errors

Missing Token:

{
  "message": "No authorization token provided",
  "err": null
}

Invalid Token:

{
  "message": "User not authorized to perform this action",
  "err": {
    "name": "JsonWebTokenError",
    "message": "invalid signature"
  }
}

Expired Token:

{
  "message": "User not authorized to perform this action",
  "err": {
    "name": "TokenExpiredError",
    "message": "jwt expired",
    "expiredAt": "2024-01-15T12:00:00Z"
  }
}

Validation Errors

Password Mismatch:

{
  "success": false,
  "message": "There was an error saving the updated password."
}

Duplicate Username:

{
  "message": "There was a duplicate key error",
  "err": {
    "code": 11000,
    "keyPattern": { "username": 1 }
  }
}


Need Help? Check the backend troubleshooting section for common API issues.