http://localhost:5000/api
The API uses JWT (JSON Web Tokens) for authentication. Include the token in the Authorization header:
Authorization: Bearer <your_token>
Tokens are also sent as HTTP-only cookies for enhanced security.
POST /auth/register
Register a new user account.
Request Body:
{
"username": "johndoe",
"email": "john@example.com",
"password": "password123",
"country": "Ghana"
}Response (201):
{
"success": true,
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"email": "john@example.com",
"country": "Ghana",
"role": "contributor",
"points": 0,
"badges": [],
"joinDate": "2025-01-15T10:30:00.000Z",
"isActive": true
}
}Validation Rules:
- Username: 3-30 characters, alphanumeric and underscores only
- Email: Valid email format
- Password: Minimum 6 characters
- Country: Required
POST /auth/login
Authenticate a user and receive tokens.
Request Body:
{
"username": "johndoe",
"password": "password123"
}Response (200):
{
"success": true,
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"email": "john@example.com",
"country": "Ghana",
"role": "contributor",
"points": 150,
"badges": [],
"joinDate": "2025-01-15T10:30:00.000Z",
"isActive": true
}
}POST /auth/logout
Logout the current user (clears cookies).
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "Logged out successfully"
}GET /auth/me
Get the currently authenticated user's profile.
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"email": "john@example.com",
"country": "Ghana",
"role": "contributor",
"points": 150,
"badges": [],
"joinDate": "2025-01-15T10:30:00.000Z",
"isActive": true
}
}POST /auth/refresh
Get a new access token using a refresh token.
Request Body:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Response (200):
{
"success": true,
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"email": "john@example.com",
"country": "Ghana",
"role": "contributor",
"points": 150,
"badges": [],
"joinDate": "2025-01-15T10:30:00.000Z",
"isActive": true
}
}PUT /auth/profile
Update user profile information.
Headers: Authorization: Bearer <token>
Request Body:
{
"email": "newemail@example.com",
"country": "Nigeria"
}Response (200):
{
"success": true,
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"email": "newemail@example.com",
"country": "Nigeria",
"role": "contributor",
"points": 150,
"badges": [],
"joinDate": "2025-01-15T10:30:00.000Z",
"isActive": true
}
}PUT /auth/password
Change user password.
Headers: Authorization: Bearer <token>
Request Body:
{
"currentPassword": "oldpassword123",
"newPassword": "newpassword456"
}Response (200):
{
"success": true,
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": { ... }
}POST /submissions
Submit a new reference for verification.
Headers: Authorization: Bearer <token>
Request Body:
{
"url": "https://example.com/article",
"title": "Climate Change Report 2024",
"publisher": "Ghana Environmental Agency",
"country": "Ghana",
"category": "secondary",
"wikipediaArticle": "https://en.wikipedia.org/wiki/Climate_change",
"fileType": "url"
}Response (201):
{
"success": true,
"submission": {
"id": "507f1f77bcf86cd799439012",
"url": "https://example.com/article",
"title": "Climate Change Report 2024",
"publisher": "Ghana Environmental Agency",
"country": "Ghana",
"category": "secondary",
"status": "pending",
"submitter": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"country": "Ghana"
},
"wikipediaArticle": "https://en.wikipedia.org/wiki/Climate_change",
"fileType": "url",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:30:00.000Z"
}
}Points Awarded: 10 points for submission
GET /submissions
Get all submissions with optional filters.
Query Parameters:
country(optional): Filter by countrycategory(optional): Filter by category (primary, secondary, unreliable)status(optional): Filter by status (pending, approved, rejected)search(optional): Search in title and publisherpage(optional, default: 1): Page numberlimit(optional, default: 20): Items per page
Example:
GET /submissions?country=Ghana&status=approved&page=1&limit=20
Response (200):
{
"success": true,
"count": 20,
"total": 150,
"page": 1,
"pages": 8,
"submissions": [
{
"id": "507f1f77bcf86cd799439012",
"url": "https://example.com/article",
"title": "Climate Change Report 2024",
"publisher": "Ghana Environmental Agency",
"country": "Ghana",
"category": "secondary",
"status": "approved",
"submitter": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"country": "Ghana"
},
"verifier": {
"id": "507f1f77bcf86cd799439013",
"username": "janeverifier",
"country": "Ghana"
},
"verifierNotes": "Credible government source",
"verifiedAt": "2025-01-16T14:20:00.000Z",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-16T14:20:00.000Z"
}
]
}GET /submissions/:id
Get detailed information about a specific submission.
Response (200):
{
"success": true,
"submission": {
"id": "507f1f77bcf86cd799439012",
"url": "https://example.com/article",
"title": "Climate Change Report 2024",
"publisher": "Ghana Environmental Agency",
"country": "Ghana",
"category": "secondary",
"status": "approved",
"submitter": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"email": "john@example.com",
"country": "Ghana",
"role": "contributor",
"points": 150,
"badges": []
},
"verifier": {
"id": "507f1f77bcf86cd799439013",
"username": "janeverifier",
"country": "Ghana",
"role": "verifier"
},
"wikipediaArticle": "https://en.wikipedia.org/wiki/Climate_change",
"verifierNotes": "Credible government source",
"verifiedAt": "2025-01-16T14:20:00.000Z",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-16T14:20:00.000Z"
}
}GET /submissions/my/submissions
Get all submissions created by the authenticated user.
Headers: Authorization: Bearer <token>
Query Parameters:
page(optional, default: 1)limit(optional, default: 20)
Response (200):
{
"success": true,
"count": 5,
"total": 5,
"page": 1,
"pages": 1,
"submissions": [ ... ]
}PUT /submissions/:id
Update a pending submission (only by submitter).
Headers: Authorization: Bearer <token>
Request Body:
{
"title": "Updated Title",
"publisher": "Updated Publisher",
"category": "primary",
"wikipediaArticle": "https://en.wikipedia.org/wiki/New_article"
}Response (200):
{
"success": true,
"submission": { ... }
}Note: Can only update submissions with status "pending"
DELETE /submissions/:id
Delete a submission (submitter or admin only).
Headers: Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "Submission deleted successfully"
}PUT /submissions/:id/verify
Verify or reject a submission (verifier/admin only).
Headers: Authorization: Bearer <token>
Roles Required: verifier, admin
Request Body:
{
"status": "approved",
"verifierNotes": "Credible government source with proper citations"
}Response (200):
{
"success": true,
"submission": {
"id": "507f1f77bcf86cd799439012",
"status": "approved",
"verifier": {
"id": "507f1f77bcf86cd799439013",
"username": "janeverifier",
"country": "Ghana"
},
"verifierNotes": "Credible government source with proper citations",
"verifiedAt": "2025-01-16T14:20:00.000Z",
...
}
}Points Awarded:
- Submitter: 25 points (if approved)
- Verifier: 5 points
GET /submissions/pending/country
Get pending submissions for the verifier's country.
Headers: Authorization: Bearer <token>
Roles Required: verifier, admin
Query Parameters:
page(optional, default: 1)limit(optional, default: 20)
Response (200):
{
"success": true,
"count": 10,
"total": 10,
"page": 1,
"pages": 1,
"submissions": [ ... ]
}GET /submissions/stats
Get statistics about submissions.
Query Parameters:
country(optional): Filter by country
Response (200):
{
"success": true,
"stats": {
"total": 500,
"pending": 50,
"approved": 400,
"rejected": 50,
"primary": 100,
"secondary": 350,
"unreliable": 50
},
"topCountries": [
{
"_id": "Ghana",
"count": 150
},
{
"_id": "Nigeria",
"count": 120
}
]
}GET /users/:id
Get a user's public profile and statistics.
Response (200):
{
"success": true,
"user": {
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"email": "john@example.com",
"country": "Ghana",
"role": "contributor",
"points": 150,
"badges": [
{
"name": "First Submission",
"icon": "🎯",
"earnedAt": "2025-01-15T10:30:00.000Z"
}
],
"joinDate": "2025-01-15T10:30:00.000Z",
"isActive": true
},
"stats": {
"total": 15,
"approved": 12,
"pending": 2,
"rejected": 1
}
}GET /users/leaderboard
Get top users by points.
Query Parameters:
country(optional): Filter by countrylimit(optional, default: 20): Number of users to return
Response (200):
{
"success": true,
"count": 20,
"users": [
{
"id": "507f1f77bcf86cd799439011",
"username": "johndoe",
"country": "Ghana",
"role": "contributor",
"points": 500,
"badges": [ ... ],
"createdAt": "2025-01-15T10:30:00.000Z"
}
]
}GET /users
Get all users with filters (admin only).
Headers: Authorization: Bearer <token>
Roles Required: admin
Query Parameters:
role(optional): Filter by rolecountry(optional): Filter by countrysearch(optional): Search username or emailpage(optional, default: 1)limit(optional, default: 20)
Response (200):
{
"success": true,
"count": 20,
"total": 250,
"page": 1,
"pages": 13,
"users": [ ... ]
}POST /users/:id/badge
Award a badge to a user (admin only).
Headers: Authorization: Bearer <token>
Roles Required: admin
Request Body:
{
"name": "Top Contributor",
"icon": "🏆"
}Response (200):
{
"success": true,
"user": { ... }
}PUT /users/:id/role
Update a user's role (admin only).
Headers: Authorization: Bearer <token>
Roles Required: admin
Request Body:
{
"role": "verifier"
}Valid Roles: contributor, verifier, admin
Response (200):
{
"success": true,
"user": { ... }
}PUT /users/:id/deactivate
Deactivate a user account (admin only).
Headers: Authorization: Bearer <token>
Roles Required: admin
Response (200):
{
"success": true,
"message": "User deactivated successfully",
"user": { ... }
}PUT /users/:id/activate
Activate a user account (admin only).
Headers: Authorization: Bearer <token>
Roles Required: admin
Response (200):
{
"success": true,
"message": "User activated successfully",
"user": { ... }
}All endpoints may return the following error responses:
{
"success": false,
"message": "Validation error message",
"errors": [
{
"field": "email",
"message": "Please provide a valid email"
}
]
}{
"success": false,
"message": "Not authorized to access this route"
}{
"success": false,
"message": "User role 'contributor' is not authorized to access this route"
}{
"success": false,
"message": "Resource not found"
}{
"success": false,
"message": "Too many requests from this IP, please try again later."
}{
"success": false,
"message": "Server Error"
}The API implements rate limiting:
- 100 requests per 15 minutes per IP address
- Applies to all
/api/*endpoints
- Submit references
- Update/delete own pending submissions
- View public directory
- All contributor permissions
- Verify submissions for their country
- View pending submissions for their country
- All verifier permissions
- Manage all users
- Award badges
- Update user roles
- Deactivate/activate users
- Delete any submission
- Submit reference: 10 points
- Reference approved: 25 points (additional)
- Verify submission: 5 points
Admins can award custom badges to users for achievements.
- Node.js (v16+)
- MongoDB (v5+)
- Clone the repository
- Install dependencies:
cd backend
npm install- Create
.envfile:
cp .env.example .env- Update
.envwith your configuration:
PORT=5000
NODE_ENV=development
MONGODB_URI=mongodb://localhost:27017/wikisource-verifier
JWT_SECRET=your-super-secret-jwt-key
JWT_REFRESH_SECRET=your-super-secret-refresh-key
JWT_EXPIRE=15m
JWT_REFRESH_EXPIRE=7d
FRONTEND_URL=http://localhost:5173- Start MongoDB:
mongod- Run the server:
# Development
npm run dev
# Production
npm startThe API will be available at http://localhost:5000
Register:
curl -X POST http://localhost:5000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"email": "test@example.com",
"password": "password123",
"country": "Ghana"
}'Login:
curl -X POST http://localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "password123"
}'Create Submission:
curl -X POST http://localhost:5000/api/submissions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"url": "https://example.com/article",
"title": "Test Article",
"publisher": "Test Publisher",
"country": "Ghana",
"category": "secondary"
}'For issues or questions, please open an issue on the GitHub repository.