API Documentation - jihadkhawaja/Egroo GitHub Wiki
This guide provides comprehensive documentation for the Egroo API, including authentication, endpoints, and usage examples.
The Egroo API is a RESTful service built with ASP.NET Core that provides:
- Authentication & Authorization (JWT-based)
- Real-time Communication (SignalR hubs)
- User Management
- Channel Management
- Message Handling
- Friend System
Base URL: http://localhost:5175
(development) or https://api.yourdomain.com
(production)
Egroo uses JWT (JSON Web Tokens) for authentication. Include the token in the Authorization
header:
Authorization: Bearer <your-jwt-token>
POST /api/auth/login
{
"username": "your_username",
"password": "your_password"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "user-guid",
"username": "your_username",
"email": "[email protected]"
}
}
POST /api/auth/register
{
"username": "new_username",
"email": "[email protected]",
"password": "secure_password"
}
Response:
{
"success": true,
"message": "User registered successfully",
"userId": "user-guid"
}
GET /api/users/me
Headers: Authorization: Bearer <token>
Response:
{
"id": "user-guid",
"username": "your_username",
"email": "[email protected]",
"createdAt": "2024-01-01T00:00:00Z",
"isOnline": true
}
PUT /api/users/me
Headers: Authorization: Bearer <token>
{
"email": "[email protected]",
"displayName": "New Display Name"
}
GET /api/users/search?query=username
Headers: Authorization: Bearer <token>
Response:
[
{
"id": "user-guid",
"username": "found_user",
"displayName": "Display Name",
"isOnline": false
}
]
GET /api/friends
Headers: Authorization: Bearer <token>
Response:
[
{
"id": "friend-guid",
"username": "friend_username",
"displayName": "Friend Name",
"isOnline": true,
"lastSeen": "2024-01-01T12:00:00Z"
}
]
POST /api/friends/request
Headers: Authorization: Bearer <token>
{
"targetUsername": "username_to_add"
}
POST /api/friends/accept/{requestId}
Headers: Authorization: Bearer <token>
POST /api/friends/decline/{requestId}
Headers: Authorization: Bearer <token>
GET /api/channels
Headers: Authorization: Bearer <token>
Response:
[
{
"id": "channel-guid",
"name": "Channel Name",
"description": "Channel description",
"isPrivate": false,
"memberCount": 5,
"lastActivity": "2024-01-01T12:00:00Z",
"unreadCount": 2
}
]
POST /api/channels
Headers: Authorization: Bearer <token>
{
"name": "New Channel",
"description": "Channel description",
"isPrivate": true,
"initialMembers": ["username1", "username2"]
}
GET /api/channels/{channelId}
Headers: Authorization: Bearer <token>
Response:
{
"id": "channel-guid",
"name": "Channel Name",
"description": "Channel description",
"isPrivate": false,
"createdAt": "2024-01-01T00:00:00Z",
"members": [
{
"id": "user-guid",
"username": "member1",
"role": "admin",
"joinedAt": "2024-01-01T00:00:00Z"
}
]
}
POST /api/channels/{channelId}/join
Headers: Authorization: Bearer <token>
POST /api/channels/{channelId}/leave
Headers: Authorization: Bearer <token>
POST /api/channels/{channelId}/members
Headers: Authorization: Bearer <token>
{
"usernames": ["user1", "user2"]
}
GET /api/channels/{channelId}/messages?page=1&limit=50
Headers: Authorization: Bearer <token>
Response:
{
"messages": [
{
"id": "message-guid",
"content": "Hello, world!",
"senderId": "user-guid",
"senderUsername": "sender_name",
"timestamp": "2024-01-01T12:00:00Z",
"messageType": "text",
"isEdited": false
}
],
"hasMore": true,
"totalCount": 150
}
POST /api/channels/{channelId}/messages
Headers: Authorization: Bearer <token>
{
"content": "Hello, everyone!",
"messageType": "text"
}
PUT /api/messages/{messageId}
Headers: Authorization: Bearer <token>
{
"content": "Updated message content"
}
DELETE /api/messages/{messageId}
Headers: Authorization: Bearer <token>
Connect to the SignalR hub at /chathub
with the JWT token:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub", {
accessTokenFactory: () => localStorage.getItem("authToken")
})
.build();
await connection.invoke("JoinChannel", "channel-guid");
await connection.invoke("LeaveChannel", "channel-guid");
await connection.invoke("SendMessage", "channel-guid", "Hello, world!");
await connection.invoke("StartTyping", "channel-guid");
await connection.invoke("StopTyping", "channel-guid");
connection.on("ReceiveMessage", (channelId, message) => {
console.log("New message:", message);
});
connection.on("UserJoinedChannel", (channelId, user) => {
console.log("User joined:", user);
});
connection.on("UserLeftChannel", (channelId, userId) => {
console.log("User left:", userId);
});
connection.on("UserTyping", (channelId, username) => {
console.log(`${username} is typing...`);
});
connection.on("UserOnlineStatusChanged", (userId, isOnline) => {
console.log(`User ${userId} is ${isOnline ? 'online' : 'offline'}`);
});
POST /api/files/upload
Headers:
Authorization: Bearer <token>
Content-Type: multipart/form-data
Body: Form data with file
Response:
{
"fileId": "file-guid",
"fileName": "document.pdf",
"fileSize": 1024000,
"fileType": "application/pdf",
"downloadUrl": "/api/files/download/file-guid"
}
GET /api/files/download/{fileId}
Headers: Authorization: Bearer <token>
GET /api/search?query=search_term&type=all
Headers: Authorization: Bearer <token>
Query Parameters:
-
query
: Search term -
type
:users
,channels
,messages
, orall
-
limit
: Maximum results (default: 20)
Response:
{
"users": [
{
"id": "user-guid",
"username": "found_user",
"displayName": "Display Name"
}
],
"channels": [
{
"id": "channel-guid",
"name": "Found Channel",
"description": "Channel description"
}
],
"messages": [
{
"id": "message-guid",
"content": "Message containing search term",
"channelId": "channel-guid",
"channelName": "Channel Name"
}
]
}
GET /health
Response:
{
"status": "Healthy",
"totalDuration": "00:00:00.0123456",
"entries": {
"database": {
"status": "Healthy",
"duration": "00:00:00.0089123"
},
"signalr": {
"status": "Healthy",
"duration": "00:00:00.0001234"
}
}
}
GET /api/system/info
Response:
{
"version": "1.0.0",
"environment": "Production",
"uptime": "2.15:30:25",
"connections": {
"signalr": 45,
"database": 12
}
}
All API errors follow this format:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{
"field": "username",
"message": "Username is required"
}
]
},
"timestamp": "2024-01-01T12:00:00Z",
"traceId": "trace-guid"
}
Code | Status | Description |
---|---|---|
UNAUTHORIZED |
401 | Invalid or missing authentication token |
FORBIDDEN |
403 | Insufficient permissions |
NOT_FOUND |
404 | Resource not found |
VALIDATION_ERROR |
400 | Invalid input data |
CONFLICT |
409 | Resource already exists |
RATE_LIMITED |
429 | Too many requests |
INTERNAL_ERROR |
500 | Server error |
API endpoints are rate-limited to prevent abuse:
Endpoint Category | Limit | Window |
---|---|---|
Authentication | 5 requests | 1 minute |
General API | 100 requests | 1 minute |
File Upload | 10 requests | 1 minute |
Search | 20 requests | 1 minute |
Rate limit headers are included in responses:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1609459200
Interactive API documentation is available at:
- Development:
http://localhost:5175/swagger
- Production:
https://api.yourdomain.com/swagger
# Login
curl -X POST http://localhost:5175/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "password123"}'
# Get channels (with token)
curl -X GET http://localhost:5175/api/channels \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
// Login
const loginResponse = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'testuser',
password: 'password123'
})
});
const loginData = await loginResponse.json();
const token = loginData.token;
// Get channels
const channelsResponse = await fetch('/api/channels', {
headers: {
'Authorization': `Bearer ${token}`
}
});
const channels = await channelsResponse.json();
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:5175");
// Login
var loginData = new { username = "testuser", password = "password123" };
var loginJson = JsonSerializer.Serialize(loginData);
var loginContent = new StringContent(loginJson, Encoding.UTF8, "application/json");
var loginResponse = await httpClient.PostAsync("/api/auth/login", loginContent);
var loginResult = await loginResponse.Content.ReadAsStringAsync();
var token = JsonDocument.Parse(loginResult).RootElement.GetProperty("token").GetString();
// Set authorization header
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// Get channels
var channelsResponse = await httpClient.GetAsync("/api/channels");
var channels = await channelsResponse.Content.ReadAsStringAsync();
class EgrooApiClient {
private baseUrl: string;
private token?: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async login(username: string, password: string): Promise<LoginResponse> {
const response = await fetch(`${this.baseUrl}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
this.token = data.token;
return data;
}
async getChannels(): Promise<Channel[]> {
const response = await fetch(`${this.baseUrl}/api/channels`, {
headers: { 'Authorization': `Bearer ${this.token}` }
});
return response.json();
}
}
public class EgrooApiClient
{
private readonly HttpClient _httpClient;
private string? _token;
public EgrooApiClient(string baseUrl)
{
_httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };
}
public async Task<LoginResponse> LoginAsync(string username, string password)
{
var loginData = new { username, password };
var json = JsonSerializer.Serialize(loginData);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("/api/auth/login", content);
var result = await response.Content.ReadFromJsonAsync<LoginResponse>();
_token = result.Token;
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", _token);
return result;
}
public async Task<List<Channel>> GetChannelsAsync()
{
var response = await _httpClient.GetAsync("/api/channels");
return await response.Content.ReadFromJsonAsync<List<Channel>>();
}
}
Common API issues and solutions:
- 401 Unauthorized: Check if JWT token is valid and not expired
- 403 Forbidden: Verify user has required permissions
- CORS errors: Ensure client origin is in allowed origins configuration
- SignalR connection failures: Check WebSocket support and authentication
- Slow responses: Check database connection and query performance
- Rate limiting: Implement client-side rate limiting and retry logic
For more troubleshooting help, see the Troubleshooting Guide.