mirror of
https://github.com/itsnaveenk/bazar3.git
synced 2025-12-19 21:27:05 +00:00
updated code
This commit is contained in:
parent
c9cdbd13c0
commit
dfd65ac6dd
@ -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',
|
||||
|
||||
@ -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]);
|
||||
};
|
||||
|
||||
43
server/controllers/resultController.js
Normal file
43
server/controllers/resultController.js
Normal 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' });
|
||||
}
|
||||
};
|
||||
@ -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;
|
||||
|
||||
@ -1,17 +1,37 @@
|
||||
const crypto = require('crypto');
|
||||
|
||||
function sanitize(input) {
|
||||
if (typeof input === 'string') {
|
||||
return input.replace(/</g, "<").replace(/>/g, ">");
|
||||
}
|
||||
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();
|
||||
}
|
||||
};
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
115
server/readme.md
115
server/readme.md
@ -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=<TEAM_NAME>&date=<YYYY-MM-DD>**
|
||||
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=<YYYY-MM-DD>**
|
||||
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.
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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`,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user