You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
6.5 KiB
JavaScript
238 lines
6.5 KiB
JavaScript
const pool = require('../config/database');
|
|
const redisClient = require('../config/redis');
|
|
const logger = require('../config/logger');
|
|
|
|
const CACHE_TTL = 3600;
|
|
|
|
class StopsController {
|
|
// Get all stops
|
|
async getAllStops(req, res) {
|
|
try {
|
|
const cached = await redisClient.get('stops:all');
|
|
if (cached) {
|
|
return res.json(JSON.parse(cached));
|
|
}
|
|
|
|
const result = await pool.query(`
|
|
SELECT
|
|
s.*,
|
|
ST_Y(s.location::geometry) as latitude,
|
|
ST_X(s.location::geometry) as longitude,
|
|
COUNT(DISTINCT rs.route_id) as routes_count
|
|
FROM stops s
|
|
LEFT JOIN route_stops rs ON s.id = rs.stop_id
|
|
WHERE s.is_active = true
|
|
GROUP BY s.id
|
|
ORDER BY s.name
|
|
`);
|
|
|
|
const stops = result.rows;
|
|
await redisClient.setEx('stops:all', CACHE_TTL, JSON.stringify(stops));
|
|
|
|
res.json(stops);
|
|
} catch (error) {
|
|
logger.error('Error fetching stops:', error);
|
|
res.status(500).json({ error: 'Failed to fetch stops' });
|
|
}
|
|
}
|
|
|
|
// Get stop by ID
|
|
async getStopById(req, res) {
|
|
const { id } = req.params;
|
|
|
|
try {
|
|
const result = await pool.query(`
|
|
SELECT
|
|
s.*,
|
|
ST_Y(s.location::geometry) as latitude,
|
|
ST_X(s.location::geometry) as longitude
|
|
FROM stops s
|
|
WHERE s.id = $1 AND s.is_active = true
|
|
`, [id]);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Stop not found' });
|
|
}
|
|
|
|
// Get routes passing through this stop
|
|
const routesResult = await pool.query(`
|
|
SELECT
|
|
r.*,
|
|
rs.sequence,
|
|
rs.time_offset_minutes
|
|
FROM routes r
|
|
JOIN route_stops rs ON r.id = rs.route_id
|
|
WHERE rs.stop_id = $1 AND r.is_active = true
|
|
ORDER BY r.route_number
|
|
`, [id]);
|
|
|
|
const stop = {
|
|
...result.rows[0],
|
|
routes: routesResult.rows
|
|
};
|
|
|
|
res.json(stop);
|
|
} catch (error) {
|
|
logger.error('Error fetching stop:', error);
|
|
res.status(500).json({ error: 'Failed to fetch stop' });
|
|
}
|
|
}
|
|
|
|
// Get nearby stops (PostGIS query)
|
|
async getNearbyStops(req, res) {
|
|
const { lat, lon, radius = 500 } = req.query;
|
|
|
|
if (!lat || !lon) {
|
|
return res.status(400).json({
|
|
error: 'Missing required parameters',
|
|
required: ['lat', 'lon']
|
|
});
|
|
}
|
|
|
|
try {
|
|
const result = await pool.query(`
|
|
SELECT
|
|
s.*,
|
|
ST_Y(s.location::geometry) as latitude,
|
|
ST_X(s.location::geometry) as longitude,
|
|
ST_Distance(
|
|
s.location,
|
|
ST_MakePoint($2, $1)::geography
|
|
) as distance_meters
|
|
FROM stops s
|
|
WHERE s.is_active = true
|
|
AND ST_DWithin(
|
|
s.location,
|
|
ST_MakePoint($2, $1)::geography,
|
|
$3
|
|
)
|
|
ORDER BY distance_meters
|
|
LIMIT 20
|
|
`, [parseFloat(lat), parseFloat(lon), parseFloat(radius)]);
|
|
|
|
res.json(result.rows);
|
|
} catch (error) {
|
|
logger.error('Error fetching nearby stops:', error);
|
|
res.status(500).json({ error: 'Failed to fetch nearby stops' });
|
|
}
|
|
}
|
|
|
|
// Create stop
|
|
async createStop(req, res) {
|
|
const { name, latitude, longitude, address, description } = req.body;
|
|
|
|
if (!name || !latitude || !longitude) {
|
|
return res.status(400).json({
|
|
error: 'Missing required fields',
|
|
required: ['name', 'latitude', 'longitude']
|
|
});
|
|
}
|
|
|
|
try {
|
|
const result = await pool.query(`
|
|
INSERT INTO stops (name, location, address, description)
|
|
VALUES (
|
|
$1,
|
|
ST_SetSRID(ST_MakePoint($3, $2), 4326)::geography,
|
|
$4,
|
|
$5
|
|
)
|
|
RETURNING
|
|
*,
|
|
ST_Y(location::geometry) as latitude,
|
|
ST_X(location::geometry) as longitude
|
|
`, [name, latitude, longitude, address, description]);
|
|
|
|
await redisClient.del('stops:all');
|
|
|
|
logger.info(`Stop created: ${name}`);
|
|
res.status(201).json(result.rows[0]);
|
|
} catch (error) {
|
|
logger.error('Error creating stop:', error);
|
|
res.status(500).json({ error: 'Failed to create stop' });
|
|
}
|
|
}
|
|
|
|
// Update stop
|
|
async updateStop(req, res) {
|
|
const { id } = req.params;
|
|
const { name, latitude, longitude, address, description, is_active } = req.body;
|
|
|
|
try {
|
|
let query = 'UPDATE stops SET ';
|
|
const values = [];
|
|
let paramCount = 1;
|
|
|
|
if (name !== undefined) {
|
|
query += `name = $${paramCount}, `;
|
|
values.push(name);
|
|
paramCount++;
|
|
}
|
|
if (latitude !== undefined && longitude !== undefined) {
|
|
query += `location = ST_SetSRID(ST_MakePoint($${paramCount + 1}, $${paramCount}), 4326)::geography, `;
|
|
values.push(latitude, longitude);
|
|
paramCount += 2;
|
|
}
|
|
if (address !== undefined) {
|
|
query += `address = $${paramCount}, `;
|
|
values.push(address);
|
|
paramCount++;
|
|
}
|
|
if (description !== undefined) {
|
|
query += `description = $${paramCount}, `;
|
|
values.push(description);
|
|
paramCount++;
|
|
}
|
|
if (is_active !== undefined) {
|
|
query += `is_active = $${paramCount}, `;
|
|
values.push(is_active);
|
|
paramCount++;
|
|
}
|
|
|
|
query = query.slice(0, -2); // Remove trailing comma
|
|
query += ` WHERE id = $${paramCount} RETURNING *, ST_Y(location::geometry) as latitude, ST_X(location::geometry) as longitude`;
|
|
values.push(id);
|
|
|
|
const result = await pool.query(query, values);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Stop not found' });
|
|
}
|
|
|
|
await redisClient.del('stops:all');
|
|
|
|
logger.info(`Stop updated: ${id}`);
|
|
res.json(result.rows[0]);
|
|
} catch (error) {
|
|
logger.error('Error updating stop:', error);
|
|
res.status(500).json({ error: 'Failed to update stop' });
|
|
}
|
|
}
|
|
|
|
// Delete stop
|
|
async deleteStop(req, res) {
|
|
const { id } = req.params;
|
|
|
|
try {
|
|
const result = await pool.query(
|
|
'DELETE FROM stops WHERE id = $1 RETURNING *',
|
|
[id]
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Stop not found' });
|
|
}
|
|
|
|
await redisClient.del('stops:all');
|
|
|
|
logger.info(`Stop deleted: ${id}`);
|
|
res.json({ message: 'Stop deleted successfully' });
|
|
} catch (error) {
|
|
logger.error('Error deleting stop:', error);
|
|
res.status(500).json({ error: 'Failed to delete stop' });
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new StopsController();
|