updated code

This commit is contained in:
Naveen Kumar 2025-03-18 13:01:35 +05:30
parent c9cdbd13c0
commit dfd65ac6dd
10 changed files with 240 additions and 53 deletions

View File

@ -16,18 +16,13 @@ const createAdmin = async (password) => {
// Verify Admin Login
const verifyAdmin = async (accessKey, password, token) => {
// 1. Fetch admin from DB
const { rows: [admin] } = await db.query(
'SELECT * FROM admins WHERE access_key = $1',
[accessKey]
);
// 2. Verify password
if (!admin || !await argon2.verify(admin.argon2_hash, password)) {
return false;
}
// 3. Verify TOTP
return speakeasy.totp.verify({
secret: admin.totp_secret,
encoding: 'base32',

View File

@ -25,13 +25,17 @@ exports.login = async (accessKey, password) => {
exports.publishResult = async (data, authorization) => {
const token = authorization?.split(' ')[1];
const [admin] = await db.query('SELECT id FROM admins WHERE session_token = ?', [token]);
if (!admin) throw { status: 401, message: 'Unauthorized' };
const { team, date, result } = data;
// validate if the team exists
const teams = await db.query('SELECT id FROM teams WHERE name = ?', [team.toUpperCase()]);
if (!teams.length) throw { status: 400, message: 'Team does not exist. Create team first.' };
// publish result using team id
await db.query(`
INSERT INTO results (team_id, result_date, result)
SELECT id, ?, ? FROM teams WHERE name = ?
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE result = VALUES(result)
`, [date, result, team.toUpperCase()]);
`, [teams[0].id, date, result]);
};

View File

@ -0,0 +1,43 @@
const db = require('../db');
exports.getMonthlyResults = async (req, res) => {
try {
const { team, month } = req.body;
if (!team || !month) {
return res.status(400).json({ error: 'Team and month are required.' });
}
const results = await db.query(`
SELECT r.result, r.result_date
FROM results r
JOIN teams t ON r.team_id = t.id
WHERE t.name = ? AND DATE_FORMAT(r.result_date, '%Y-%m') = ?
`, [team.toUpperCase(), month]);
if (results.length === 0) {
return res.status(404).json({ message: 'No results found for this team in the specified month.' });
}
res.json(results);
} catch (error) {
res.status(500).json({ error: 'Server error' });
}
};
exports.getDailyResults = async (req, res) => {
try {
const date = req.query.date;
if (!date) {
return res.status(400).json({ error: 'Date query parameter is required.' });
}
const results = await db.query(`
SELECT t.name as team, r.result, r.result_date
FROM results r
JOIN teams t ON r.team_id = t.id
WHERE r.result_date = ?
`, [date]);
if (results.length === 0) {
return res.status(404).json({ message: 'No results found for the specified date.' });
}
res.json(results);
} catch (error) {
res.status(500).json({ error: 'Server error' });
}
};

View File

@ -1,6 +1,5 @@
const db = require('../db');
// Get all teams
exports.getAllTeams = async (req, res) => {
try {
const teams = await db.query('SELECT * FROM teams');
@ -11,7 +10,6 @@ exports.getAllTeams = async (req, res) => {
}
};
// Create a new team
exports.createTeam = async (req, res) => {
try {
const { name, announcement_time } = req.body;
@ -32,7 +30,6 @@ exports.createTeam = async (req, res) => {
}
};
// Update a team
exports.updateTeam = async (req, res) => {
try {
const { id } = req.params;
@ -66,7 +63,6 @@ exports.updateTeam = async (req, res) => {
}
};
// Delete a team
exports.deleteTeam = async (req, res) => {
try {
const { id } = req.params;

View File

@ -1,17 +1,37 @@
const crypto = require('crypto');
function sanitize(input) {
if (typeof input === 'string') {
return input.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
if (Array.isArray(input)) {
return input.map(sanitize);
}
if (input && typeof input === 'object') {
const sanitizedObj = {};
for (const key in input) {
if (Object.hasOwnProperty.call(input, key)) {
sanitizedObj[key] = sanitize(input[key]);
}
}
return sanitizedObj;
}
return input;
}
module.exports = {
// IP Anonymization
anonymizeIP: (req, res, next) => {
const ip = req.ip || '127.0.0.1';
const salt = Math.floor(Date.now() / 3600000); // Hourly salt
const salt = Math.floor(Date.now() / 3600000);
req.anonymizedIP = crypto.createHash('sha3-256')
.update(ip + salt + process.env.IP_PEPPER)
.digest('hex');
next();
},
sanitizeInput: (req, res, next) => {
// ...implement input sanitization if needed...
if (req.body) req.body = sanitize(req.body);
if (req.query) req.query = sanitize(req.query);
if (req.params) req.params = sanitize(req.params);
next();
}
};

View File

@ -1,7 +1,7 @@
{
"info": {
"_postman_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Satta Backend API",
"name": "Kings Backend API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
@ -156,6 +156,77 @@
"path": ["api", "health"]
}
}
},
{
"name": "Test Sanitization",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"<script>alert('xss');</script>\",\n \"announcement_time\": \"02:30:00\"\n}"
},
"url": {
"raw": "http://localhost:3000/api/teams",
"protocol": "http",
"host": [
"localhost"
],
"port": "3000",
"path": [
"api",
"teams"
]
}
}
},
{
"name": "Get Monthly Results",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"team\": \"BIKANER SUPER\",\n \"month\": \"2025-03\"\n}"
},
"url": {
"raw": "http://localhost:3000/api/results/monthly",
"protocol": "http",
"host": ["localhost"],
"port": "3000",
"path": ["api", "results", "monthly"]
}
}
},
{
"name": "Get Daily Results",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3000/api/results/daily?date=2025-03-12",
"protocol": "http",
"host": ["localhost"],
"port": "3000",
"path": ["api", "results", "daily"],
"query": [
{
"key": "date",
"value": "2025-03-12"
}
]
}
}
}
]
}

View File

@ -1,7 +1,7 @@
# Satta Backend API
# Kings Backend API
## Overview
This project provides a backend API for the Satta system. It includes endpoints for managing teams, publishing results, admin authentication, and a simple caching mechanism.
Kings Backend API is a RESTful API for managing teams, publishing match/show results, and handling admin authentication. It also includes a simple in-memory caching mechanism, input sanitization, and rate limiting for security.
## Prerequisites
- Node.js (v14 or higher)
@ -9,12 +9,13 @@ This project provides a backend API for the Satta system. It includes endpoints
## Installation
1. Clone the repository:
1. **Clone the Repository**
```
git clone <repository_url>
cd kingproject/bazar3
```
2. Install dependencies:
2. **Install Dependencies**
```
cd server
npm install
@ -22,7 +23,9 @@ This project provides a backend API for the Satta system. It includes endpoints
## Configuration
1. Create a `.env` file in `/server` (or modify the existing one) with the following variables:
1. **Environment Variables**
Create a `.env` file in the `/server` directory with the following variables:
```
DB_HOST=localhost
DB_USER=user
@ -35,11 +38,13 @@ This project provides a backend API for the Satta system. It includes endpoints
## Database Setup
1. Import the schema by running the SQL file `/server/schema.sql` in your MySQL client:
1. **Import Schema**
Run the following command in your MySQL client to create the database and tables:
```
mysql -u user -p < server/schema.sql
```
2. Ensure the database `kingdb_prod` is created with the required tables (teams, results, admins).
This creates the `kingdb_prod` database and the required tables: `teams`, `results`, and `admins`.
## Admin Account Setup
@ -47,64 +52,120 @@ To create an admin account, run:
```
npm run create-admin -- <your_password>
```
This command will output an `Access Key` which you'll use for admin login.
This script will output an `Access Key` for admin login.
## Running the Server
Start the API server with:
Start the API server by running:
```
npm start
```
The server listens on the port specified in the `.env` file (default 3000).
The server will listen on the port specified in your `.env` file (default is 3000).
## API Endpoints
### Public Endpoints
- **GET /api/results?team=<TEAM_NAME>&date=<YYYY-MM-DD>**
Retrieve the result for a specified team and date.
- **GET /api/results?team=&lt;TEAM_NAME&gt;&date=&lt;YYYY-MM-DD&gt;**
Retrieve the result for a specified team and date.
- **GET /api/today**
Retrieve all results for the current day.
- **GET /api/health**
Basic health check endpoint to verify server and database connectivity.
Health check endpoint to verify server and database connectivity.
- **POST /api/results/monthly**
Get monthly results for a team.
_Request Body Example:_
```json
{
"team": "BIKANER SUPER",
"month": "2025-03"
}
```
- **GET /api/results/daily?date=&lt;YYYY-MM-DD&gt;**
Get daily results for all teams.
### Admin Endpoints
- **POST /admin/login**
Login using `accessKey` and `password` to receive a session token.
Log in using `accessKey` and `password` to receive a session token.
_Request Body Example:_
```json
{
"accessKey": "<ACCESS_KEY>",
"password": "<PASSWORD>"
}
```
- **POST /admin/results**
Publish a result. Requires authorization header with the token:
`Authorization: Bearer <SESSION_TOKEN>`
Publish a result. Requires an authorization header with the session token.
_Request Body Example:_
```json
{
"team": "NEW TEAM",
"date": "2025-03-12",
"result": "45"
}
```
### Team Endpoints
- **GET /api/teams**
Retrieve all teams.
- **POST /api/teams**
Create a new team. Requires `name` and `announcement_time` in the body.
_Request Body Example:_
```json
{
"name": "NEW TEAM",
"announcement_time": "02:30:00"
}
```
- **PUT /api/teams/:id**
Update a team.
- **DELETE /api/teams/:id**
Delete a team.
### Testing Sanitization
A sample endpoint (POST /api/teams) will sanitize HTML input. For example, sending:
```json
{
"name": "<script>alert('xss');</script>",
"announcement_time": "02:30:00"
}
```
will have the `<` and `>` characters escaped to protect against XSS.
## Testing the API
A Postman collection is provided in `/server/postman_collection.json`. You can import this collection into Postman to test all endpoints easily.
1. **Using Postman**
Additionally, a simple test script is available:
```
npm run test-api
```
This script uses `axios` to perform a sequence of API calls, including admin login, creating a team, fetching teams, updating, deleting, and publishing a result.
Import the Postman collection from `/server/postman_collection.json` to test all endpoints, including admin authentication, team management, result retrieval, and sanitization.
2. **Using the Test Script**
A test script is available that performs a sequence of API calls:
```
npm run test-api
```
This script uses `axios` to:
- Log in as an admin.
- Create, fetch, update, and delete teams.
- Publish a result.
## Caching
Results are cached in-memory for 5 minutes. Any write operations (POST, PUT, DELETE) clear the cache automatically.
- Results are cached in memory for 5 minutes.
- Any write operations (POST, PUT, DELETE) clear the cache automatically.
## Rate Limiting and Security
- Rate limiting is implemented to allow 100 requests per minute per anonymized IP.
- IP addresses are anonymized using SHA3-256 with a salt and a secret pepper before being used for rate limiting.
- **Rate Limiting:**
The API allows 100 requests per minute per anonymized IP, using SHA3-256 based IP anonymization.
- **Input Sanitization:**
The middleware sanitizes incoming data (body, query, params) by escaping HTML characters to prevent XSS.
- **SQL Injection Protection:**
SQL queries use prepared statements with parameterized queries, ensuring inputs and queries remain separate.
## Additional Notes
- For input validation, the project leverages Joi.
- Changes to the project configuration or dependency versions may require updating the readme accordingly.
- Input validation is implemented using Joi.
- Keep your environment variables secure.
- Modify configurations as necessary when upgrading dependency versions.
## License
Please include your project's license details here.

View File

@ -2,8 +2,8 @@ const express = require('express');
const router = express.Router();
const db = require('../db');
const cache = require('../cache');
const resultController = require('../controllers/resultController');
// Get specific result
router.get('/results', async (req, res) => {
try {
const { team, date } = req.query;
@ -29,7 +29,6 @@ router.get('/results', async (req, res) => {
}
});
// Get all today's results
router.get('/today', async (req, res) => {
try {
const today = new Date().toISOString().split('T')[0];
@ -63,4 +62,10 @@ router.get('/health', async (req, res) => {
}
});
// (expects body: { team, month })
router.post('/results/monthly', resultController.getMonthlyResults);
// (expects query param: date=YYYY-MM-DD)
router.get('/results/daily', resultController.getDailyResults);
module.exports = router;

View File

@ -3,16 +3,12 @@ const router = express.Router();
const teamController = require('../controllers/teamController');
const { validateTeam } = require('../middlewares/validation');
// Get all teams
router.get('/', teamController.getAllTeams);
// Create a new team
router.post('/', validateTeam, teamController.createTeam);
// Update a team
router.put('/:id', validateTeam, teamController.updateTeam);
// Delete a team
router.delete('/:id', teamController.deleteTeam);
module.exports = router;

View File

@ -13,7 +13,6 @@ const BASE_URL = 'http://localhost:3000';
const sessionToken = loginResponse.data.token;
console.log('Login successful. Session Token:', sessionToken);
// Create a Team
console.log('Creating a new team...');
const createTeamResponse = await axios.post(
`${BASE_URL}/api/teams`,
@ -32,7 +31,6 @@ const BASE_URL = 'http://localhost:3000';
const teamsResponse = await axios.get(`${BASE_URL}/api/teams`);
console.log('Teams:', teamsResponse.data);
// Update a Team
console.log('Updating a team...');
const updateTeamResponse = await axios.put(
`${BASE_URL}/api/teams/1`,
@ -46,14 +44,12 @@ const BASE_URL = 'http://localhost:3000';
);
console.log('Team updated:', updateTeamResponse.data);
// Delete a Team
console.log('Deleting a team...');
const deleteTeamResponse = await axios.delete(`${BASE_URL}/api/teams/1`, {
headers: { Authorization: `Bearer ${sessionToken}` }
});
console.log('Team deleted:', deleteTeamResponse.data);
// Publish a Result
console.log('Publishing a result...');
const publishResultResponse = await axios.post(
`${BASE_URL}/admin/results`,