320 أسطر
9.9 KiB
Markdown
320 أسطر
9.9 KiB
Markdown
# Frontend Integration
|
|
|
|
## Network setup
|
|
|
|
When testing from a phone or another device on the same LAN:
|
|
|
|
1. Set `HOST=0.0.0.0`
|
|
2. Set `PUBLIC_BASE_URL` to the machine IP, not `localhost`
|
|
3. Add frontend origins to `CORS_ORIGINS`
|
|
4. Keep `GOOGLE_CALLBACK_URL` on the same reachable host if Google auth is used
|
|
|
|
Example:
|
|
|
|
```env
|
|
HOST=0.0.0.0
|
|
PUBLIC_BASE_URL=http://192.168.1.12:4000
|
|
CORS_ORIGINS=http://192.168.1.12:3000,http://192.168.1.12:5173
|
|
GOOGLE_CALLBACK_URL=http://192.168.1.12:4000/api/v1/auth/google/callback
|
|
```
|
|
|
|
With `PUBLIC_BASE_URL` configured, file fields such as `avatar`, `coverImage`, `imageUrls`, `imageVariants`, `videoUrl`, `hlsUrl`, `audioUrl`, `thumbnailUrl`, `thumbnailVariants`, `mediaUrl`, and marketplace images are returned as absolute URLs.
|
|
|
|
## Pagination contract
|
|
|
|
List endpoints now keep the legacy fields and also return a unified `pagination` object.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"items": [],
|
|
"count": 0,
|
|
"page": 1,
|
|
"limit": 20,
|
|
"total": 0,
|
|
"totalPages": 1,
|
|
"nextCursor": null,
|
|
"pagination": {
|
|
"mode": "offset",
|
|
"page": 1,
|
|
"limit": 20,
|
|
"count": 0,
|
|
"total": 0,
|
|
"totalPages": 1,
|
|
"hasNextPage": false,
|
|
"hasPreviousPage": false,
|
|
"nextPage": null,
|
|
"previousPage": null,
|
|
"currentCursor": null,
|
|
"nextCursor": null
|
|
}
|
|
}
|
|
```
|
|
|
|
Notes:
|
|
|
|
- Offset endpoints use `page` and `limit`
|
|
- Cursor-aware endpoints also return `nextCursor`
|
|
- Existing clients can keep reading the old top-level fields
|
|
- `sortOrder=asc|desc` is available on paginated endpoints
|
|
|
|
## Filter and sorting contract
|
|
|
|
Boolean query filters are parsed consistently now. Send:
|
|
|
|
- `?isActive=true`
|
|
- `?isActive=false`
|
|
- `?read=true`
|
|
- `?read=false`
|
|
- `?followingOnly=true`
|
|
|
|
Common conventions:
|
|
|
|
- `page`, `limit`, `sortOrder`
|
|
- `sortOrder` defaults to `desc`
|
|
- `sortBy` is supported on selected endpoints with endpoint-specific allowed values
|
|
|
|
Supported filters:
|
|
|
|
- `GET /feed/me`
|
|
- defaults to followed accounts and the viewer's own posts
|
|
- use `followingOnly=false` to widen the home feed to public discovery posts
|
|
- `preferredPostType`, `followingOnly`, `radiusKm`, `includeSuggestions`, `suggestionInterval`
|
|
- `GET /marketplace/home`
|
|
- `listingsLimit`, `instrumentsLimit`, `repairShopsLimit`, `onlyActive`
|
|
- `GET /users`
|
|
- `q`, `isVerified`, `musicRole`, `experienceLevel`, `isPrivate`, `hasAvatar`, `sortBy`
|
|
- `sortBy`: `createdAt`, `name`, `username`, `followersCount`, `postsCount`
|
|
- `GET /users/discover`
|
|
- `q`, `musicRole`, `experienceLevel`, `hasAvatarOnly`, `includeRoleBuckets`, `sortBy`, `sortOrder`
|
|
- `GET /users/:id/profile-overview`
|
|
- returns `stats`, `contentCounts`, `tabs`, and `viewerState`
|
|
- `GET /posts/user/:userId`
|
|
- `visibility`, `postType`, `q`, `hashtag`, `sortBy`
|
|
- `sortBy`: `createdAt`, `updatedAt`, `likesCount`, `commentsCount`, `savesCount`, `shareCount`, `viewCount`, `playCount`
|
|
- `GET /posts/reels`
|
|
- `visibility`, `authorId`, `q`, `sortBy`
|
|
- `GET /marketplace/listings`
|
|
- `q`, `minPrice`, `maxPrice`, `isActive`, `listingCategory`, `condition`, `instrumentType`, `sortBy`
|
|
- `listingCategory`: `musical_instrument`, `accessory`, `audio_gear`, `sheet_music`, `other`
|
|
- `GET /marketplace/instruments`
|
|
- `q`, `minPrice`, `maxPrice`, `isActive`, `condition`, `instrumentType`, `sortBy`
|
|
- this route is now a musical-instruments-only view of marketplace listings
|
|
- `sortBy`: `createdAt`, `updatedAt`, `price`, `title`
|
|
- `GET /marketplace/repair-shops`
|
|
- `q`, `isActive`, `sortBy`
|
|
- `sortBy`: `createdAt`, `updatedAt`, `name`
|
|
- `GET /notifications`
|
|
- `read`, `type`, `resourceType`, `sortOrder`
|
|
- `GET /comments/post/:postId`
|
|
- `page`, `limit`, `sortOrder`
|
|
- `PATCH /comments/:commentId`
|
|
- JSON body: `content`, `mentionUsernames`
|
|
|
|
## WebSocket auth
|
|
|
|
Both namespaces accept the JWT access token in one of these places:
|
|
|
|
- `auth.token`
|
|
- `Authorization` header as `Bearer <token>`
|
|
|
|
## Chat namespace
|
|
|
|
Namespace: `/chat`
|
|
|
|
Client emits:
|
|
|
|
- `join_conversation`
|
|
- payload: `{ "conversationId": "..." }`
|
|
- `send_message`
|
|
- payload matches `SendMessageDto`
|
|
- `typing`
|
|
- payload: `{ "conversationId": "...", "isTyping": true }`
|
|
- `mark_seen`
|
|
- payload: `{ "messageId": "...", "conversationId": "..." }`
|
|
|
|
Server emits:
|
|
|
|
- `presence`
|
|
- payload: `{ "userId": "...", "online": true }`
|
|
- `joined_conversation`
|
|
- payload: `{ "conversationId": "..." }`
|
|
- `new_message`
|
|
- payload: message object
|
|
- `typing`
|
|
- payload: `{ "conversationId": "...", "userId": "...", "isTyping": true }`
|
|
- `message_seen`
|
|
- payload: `{ "messageId": "...", "userId": "..." }`
|
|
|
|
## Notifications namespace
|
|
|
|
Namespace: `/notifications`
|
|
|
|
Server emits:
|
|
|
|
- `notification_created`
|
|
- payload: notification object
|
|
- `notifications_unread_count`
|
|
- payload: `{ "unreadCount": 3 }`
|
|
|
|
Notification types currently used:
|
|
|
|
- `like`
|
|
- `comment`
|
|
- `follow`
|
|
- `message`
|
|
- `save`
|
|
- `share`
|
|
- `mention`
|
|
|
|
## Mentions
|
|
|
|
Posts and comments support:
|
|
|
|
- `taggedUserIds`: official tagged users by Mongo id
|
|
- `mentionUsernames`: usernames such as `["rami_sabry"]`
|
|
|
|
The backend also extracts `@username` from `content` automatically and emits mention notifications for matched users.
|
|
|
|
## Posts Upload Contract
|
|
|
|
Post creation and updates now require `multipart/form-data` for all post payloads, including text-only posts and posts that use external media links.
|
|
|
|
- `POST /posts`
|
|
- `PATCH /posts/:postId`
|
|
- `POST /posts/reels`
|
|
|
|
Send media uploads as files:
|
|
|
|
- `imageFiles`
|
|
- `videoFile`
|
|
- `audioFile`
|
|
|
|
Send link-based fields as text fields inside the same form-data payload:
|
|
|
|
- `imageUrls`
|
|
- `videoUrl`
|
|
- `audioUrl`
|
|
- `thumbnailUrl`
|
|
|
|
Array fields may be sent either as repeated form keys or JSON text:
|
|
|
|
- `taggedUserIds`
|
|
- `mentionUsernames`
|
|
- `imageUrls`
|
|
- `waveformPeaks`
|
|
|
|
## Video optimization
|
|
|
|
When `VIDEO_PROCESSING_ENABLED=true` and `ffmpeg` is available on the server:
|
|
|
|
- uploaded post/reel videos are converted to optimized `mp4`
|
|
- `+faststart` is applied so playback begins faster on mobile/web
|
|
- uploaded post/reel videos also produce an HLS playlist at `hlsUrl` when `VIDEO_PROCESSING_GENERATE_HLS=true`
|
|
- when `ffprobe` is available, `hlsUrl` points to a master playlist with multiple renditions so HLS players can downgrade quality automatically on weak networks
|
|
- local storage responses serve `mp4`, `m3u8`, `m4s`, and `ts` files with explicit media `Content-Type` headers and `Accept-Ranges: bytes`
|
|
- a thumbnail image is generated automatically if the client does not send `thumbnailUrl`
|
|
- generated thumbnails also expose `thumbnailVariants.lowUrl`, `thumbnailVariants.mediumUrl`, `thumbnailVariants.highUrl`, and `thumbnailVariants.originalUrl`
|
|
|
|
If `ffmpeg` is not installed or video processing is disabled, uploads still work and the original
|
|
video file is stored as-is.
|
|
|
|
Recommended client behavior:
|
|
|
|
- use `hlsUrl` first when present for adaptive/streaming playback
|
|
- fall back to `videoUrl` for progressive `mp4` playback
|
|
- for poster frames on weak networks, prefer `thumbnailVariants.lowUrl` or `thumbnailVariants.mediumUrl`
|
|
|
|
## Audio and image delivery
|
|
|
|
Managed uploads are stored under stable UUID-based paths, so local storage responses now send cache-friendly headers:
|
|
|
|
- images (`jpg`, `jpeg`, `png`, `webp`, `gif`) are served with explicit `Content-Type` and long-lived immutable `Cache-Control`
|
|
- audio (`mp3`, `wav`, `m4a`, `aac`, `ogg`) is served with explicit `Content-Type`, long-lived immutable `Cache-Control`, and `Accept-Ranges: bytes`
|
|
- uploaded post images are normalized into responsive variants and posts now include `imageVariants[]`
|
|
|
|
Recommended client behavior:
|
|
|
|
- for audio, stream `audioUrl` directly and allow byte-range playback/resume
|
|
- `imageUrls` now default to feed-friendly optimized images for backward compatibility
|
|
- on weak networks, prefer `imageVariants[index].lowUrl`
|
|
- on normal networks, use `imageUrls[index]` or `imageVariants[index].mediumUrl`
|
|
- on detail screens or zoom views, use `imageVariants[index].highUrl` or `imageVariants[index].originalUrl`
|
|
|
|
## Marketplace split
|
|
|
|
Marketplace is now separated from musical instruments at the API contract level:
|
|
|
|
- `GET /marketplace/listings`
|
|
- general sale listings across all listing categories
|
|
- `GET /marketplace/instruments`
|
|
- only musical instruments
|
|
- `GET /marketplace/repair-shops`
|
|
- service providers and maintenance shops
|
|
- `GET /marketplace/home`
|
|
- frontend-friendly grouped payload with `categories`, `summary`, `filters.listingCategories`, `featuredShops`, and `sections`
|
|
|
|
Admin endpoints follow the same split:
|
|
|
|
- `POST /marketplace/admin/listings`
|
|
- `GET /marketplace/admin/listings/me`
|
|
- `PATCH /marketplace/admin/listings/:id`
|
|
- `DELETE /marketplace/admin/listings/:id`
|
|
|
|
The older `/marketplace/admin/instruments*` routes still exist and always force `listingCategory=musical_instrument` for backward compatibility.
|
|
|
|
Marketplace shop ownership rule:
|
|
|
|
- each admin can own one repair-shop / marketplace shop record only
|
|
- if the admin already created one, the frontend should call update endpoints instead of create
|
|
|
|
Listing responses now also include:
|
|
|
|
- `shop`
|
|
- `{ adminId, name, username, avatar }`
|
|
- `storeName`
|
|
- direct shortcut for list cards
|
|
- `condition`
|
|
- `new`, `like_new`, `used`, `refurbished`
|
|
- `instrumentType`
|
|
- free-text type such as `oud`, `piano`, or `violin`
|
|
|
|
Search on marketplace listings now matches:
|
|
|
|
- listing title
|
|
- listing description
|
|
- instrument type
|
|
- shop name
|
|
|
|
## Talent discovery
|
|
|
|
The talents screen can now be built from:
|
|
|
|
- `GET /users/discover`
|
|
- paginated talent cards
|
|
- `roleBuckets` counts for tabs such as instrumentalists, singers, producers, and lyricists
|
|
- `GET /users/:id/profile-overview`
|
|
- summary for the profile header and tab counts
|
|
|
|
`profile-overview` returns:
|
|
|
|
- `stats.followersCount`
|
|
- `stats.followingCount`
|
|
- `stats.postsCount`
|
|
- `stats.collaborationsCount`
|
|
- `contentCounts.reels`
|
|
- `contentCounts.audio`
|
|
- `contentCounts.other`
|
|
- `viewerState.following`
|
|
|
|
## Smoke / e2e
|
|
|
|
Run:
|
|
|
|
```bash
|
|
npm run test:e2e
|
|
```
|
|
|
|
The smoke suite covers health, auth verification, profile setup, image upload, post creation, feed retrieval, notifications, and comment mentions.
|