Initial commit: StreamFlow IPTV platform
This commit is contained in:
commit
73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions
139
backend/routes/search.js
Normal file
139
backend/routes/search.js
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { db } = require('../database/db');
|
||||
const { authenticate, requireAdmin } = require('../middleware/auth');
|
||||
const { readLimiter } = require('../middleware/rateLimiter');
|
||||
const { sanitizeString } = require('../utils/inputValidator');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* Global search endpoint
|
||||
* Searches across channels, radio stations, users, settings, etc.
|
||||
*/
|
||||
router.get('/', authenticate, readLimiter, async (req, res) => {
|
||||
try {
|
||||
const { q } = req.query;
|
||||
const isAdmin = req.user.role === 'admin';
|
||||
|
||||
if (!q || q.trim().length < 2) {
|
||||
return res.json({
|
||||
channels: [],
|
||||
radio: [],
|
||||
users: [],
|
||||
settings: [],
|
||||
groups: []
|
||||
});
|
||||
}
|
||||
|
||||
// Validate and sanitize search query
|
||||
const sanitized = sanitizeString(q.trim());
|
||||
if (sanitized.length > 100) {
|
||||
return res.status(400).json({ error: 'Search query too long' });
|
||||
}
|
||||
|
||||
const searchTerm = `%${sanitized}%`;
|
||||
const results = {
|
||||
channels: [],
|
||||
radio: [],
|
||||
users: [],
|
||||
settings: [],
|
||||
groups: []
|
||||
};
|
||||
|
||||
// Search TV channels (only from user's playlists)
|
||||
results.channels = await new Promise((resolve, reject) => {
|
||||
db.all(
|
||||
`SELECT DISTINCT c.id, c.name, c.url, COALESCE(c.custom_logo, c.logo) as logo, c.group_name, c.is_radio
|
||||
FROM channels c
|
||||
JOIN playlists p ON c.playlist_id = p.id
|
||||
WHERE p.user_id = ? AND c.is_radio = 0 AND c.is_active = 1
|
||||
AND (c.name LIKE ? OR c.group_name LIKE ?)
|
||||
ORDER BY c.name
|
||||
LIMIT 20`,
|
||||
[req.user.userId, searchTerm, searchTerm],
|
||||
(err, rows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows || []);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Search Radio channels (only from user's playlists)
|
||||
results.radio = await new Promise((resolve, reject) => {
|
||||
db.all(
|
||||
`SELECT DISTINCT c.id, c.name, c.url, COALESCE(c.custom_logo, c.logo) as logo, c.group_name, c.is_radio
|
||||
FROM channels c
|
||||
JOIN playlists p ON c.playlist_id = p.id
|
||||
WHERE p.user_id = ? AND c.is_radio = 1 AND c.is_active = 1
|
||||
AND (c.name LIKE ? OR c.group_name LIKE ?)
|
||||
ORDER BY c.name
|
||||
LIMIT 20`,
|
||||
[req.user.userId, searchTerm, searchTerm],
|
||||
(err, rows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows || []);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Search groups (only from user's playlists)
|
||||
results.groups = await new Promise((resolve, reject) => {
|
||||
db.all(
|
||||
`SELECT DISTINCT c.group_name as name, c.is_radio
|
||||
FROM channels c
|
||||
JOIN playlists p ON c.playlist_id = p.id
|
||||
WHERE p.user_id = ? AND c.is_active = 1
|
||||
AND c.group_name LIKE ?
|
||||
ORDER BY c.group_name
|
||||
LIMIT 10`,
|
||||
[req.user.userId, searchTerm],
|
||||
(err, rows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows || []);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Search users (admin only)
|
||||
if (isAdmin) {
|
||||
results.users = await new Promise((resolve, reject) => {
|
||||
db.all(
|
||||
`SELECT id, username, email, role, created_at
|
||||
FROM users
|
||||
WHERE username LIKE ? OR email LIKE ?
|
||||
ORDER BY username
|
||||
LIMIT 10`,
|
||||
[searchTerm, searchTerm],
|
||||
(err, rows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows || []);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Add settings/pages results (static)
|
||||
const settingsOptions = [
|
||||
{ id: 'settings', name: 'Settings', path: '/settings', icon: 'settings' },
|
||||
{ id: 'user-management', name: 'User Management', path: '/settings?tab=users', icon: 'people' },
|
||||
{ id: 'vpn-settings', name: 'VPN Settings', path: '/settings?tab=vpn', icon: 'vpn_lock' },
|
||||
{ id: '2fa', name: 'Two-Factor Authentication', path: '/settings?tab=2fa', icon: 'security' },
|
||||
{ id: 'live-tv', name: 'Live TV', path: '/live', icon: 'tv' },
|
||||
{ id: 'radio', name: 'Radio', path: '/radio', icon: 'radio' },
|
||||
{ id: 'movies', name: 'Movies', path: '/movies', icon: 'movie' },
|
||||
{ id: 'series', name: 'Series', path: '/series', icon: 'subscriptions' },
|
||||
{ id: 'favorites', name: 'Favorites', path: '/favorites', icon: 'favorite' },
|
||||
];
|
||||
|
||||
results.settings = settingsOptions.filter(option =>
|
||||
option.name.toLowerCase().includes(q.toLowerCase())
|
||||
);
|
||||
|
||||
res.json(results);
|
||||
} catch (error) {
|
||||
console.error('Search error:', error);
|
||||
res.status(500).json({ error: 'Search failed' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Loading…
Add table
Add a link
Reference in a new issue