AeThex-Connect/src/backend/routes/serverRoutes.js
2026-02-28 23:50:09 -07:00

411 lines
10 KiB
JavaScript

/**
* Server Routes
* API endpoints for servers (guilds), channels, and members
*/
const express = require('express');
const router = express.Router();
const { authenticateUser } = require('../middleware/auth');
const db = require('../database/db');
const { v4: uuidv4 } = require('uuid');
/**
* GET /api/servers
* Get all servers the current user is a member of
*/
router.get('/', authenticateUser, async (req, res) => {
try {
const result = await db.query(`
SELECT s.*, sm.role, sm.nickname, sm.joined_at as member_joined_at
FROM servers s
INNER JOIN server_members sm ON s.id = sm.server_id
WHERE sm.user_id = $1
ORDER BY sm.joined_at ASC
`, [req.user.id]);
res.json({
success: true,
servers: result.rows
});
} catch (error) {
console.error('Failed to get servers:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* POST /api/servers
* Create a new server
*/
router.post('/', authenticateUser, async (req, res) => {
try {
const { name, description, icon_url, is_public } = req.body;
if (!name || name.trim().length === 0) {
return res.status(400).json({
success: false,
error: 'Server name is required'
});
}
// Generate invite code
const invite_code = uuidv4().substring(0, 8).toUpperCase();
// Create server
const serverResult = await db.query(`
INSERT INTO servers (name, description, icon_url, owner_id, invite_code, is_public)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *
`, [name.trim(), description || null, icon_url || null, req.user.id, invite_code, is_public || false]);
const server = serverResult.rows[0];
// Add owner as member
await db.query(`
INSERT INTO server_members (server_id, user_id, role)
VALUES ($1, $2, 'owner')
`, [server.id, req.user.id]);
// Create default channels
await db.query(`
INSERT INTO channels (server_id, name, channel_type, position) VALUES
($1, 'general', 'text', 0),
($1, 'voice', 'voice', 1)
`, [server.id]);
res.json({
success: true,
server
});
} catch (error) {
console.error('Failed to create server:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* GET /api/servers/:id
* Get server details
*/
router.get('/:id', authenticateUser, async (req, res) => {
try {
// Check if user is a member
const memberCheck = await db.query(`
SELECT * FROM server_members WHERE server_id = $1 AND user_id = $2
`, [req.params.id, req.user.id]);
if (memberCheck.rows.length === 0) {
return res.status(403).json({
success: false,
error: 'Not a member of this server'
});
}
const result = await db.query(`
SELECT * FROM servers WHERE id = $1
`, [req.params.id]);
if (result.rows.length === 0) {
return res.status(404).json({
success: false,
error: 'Server not found'
});
}
res.json({
success: true,
server: result.rows[0]
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* POST /api/servers/join/:inviteCode
* Join a server by invite code
*/
router.post('/join/:inviteCode', authenticateUser, async (req, res) => {
try {
const server = await db.query(`
SELECT * FROM servers WHERE invite_code = $1
`, [req.params.inviteCode.toUpperCase()]);
if (server.rows.length === 0) {
return res.status(404).json({
success: false,
error: 'Invalid invite code'
});
}
// Check if already a member
const existingMember = await db.query(`
SELECT * FROM server_members WHERE server_id = $1 AND user_id = $2
`, [server.rows[0].id, req.user.id]);
if (existingMember.rows.length > 0) {
return res.json({
success: true,
server: server.rows[0],
message: 'Already a member'
});
}
// Add as member
await db.query(`
INSERT INTO server_members (server_id, user_id, role)
VALUES ($1, $2, 'member')
`, [server.rows[0].id, req.user.id]);
// Update member count
await db.query(`
UPDATE servers SET member_count = member_count + 1 WHERE id = $1
`, [server.rows[0].id]);
res.json({
success: true,
server: server.rows[0]
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* GET /api/servers/:id/channels
* Get channels for a server
*/
router.get('/:id/channels', authenticateUser, async (req, res) => {
try {
// Check membership
const memberCheck = await db.query(`
SELECT * FROM server_members WHERE server_id = $1 AND user_id = $2
`, [req.params.id, req.user.id]);
if (memberCheck.rows.length === 0) {
return res.status(403).json({
success: false,
error: 'Not a member of this server'
});
}
const channels = await db.query(`
SELECT * FROM channels WHERE server_id = $1 ORDER BY position ASC
`, [req.params.id]);
res.json({
success: true,
channels: channels.rows
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* POST /api/servers/:id/channels
* Create a new channel in a server
*/
router.post('/:id/channels', authenticateUser, async (req, res) => {
try {
const { name, description, channel_type } = req.body;
// Check if user is admin/owner
const memberCheck = await db.query(`
SELECT * FROM server_members WHERE server_id = $1 AND user_id = $2 AND role IN ('owner', 'admin')
`, [req.params.id, req.user.id]);
if (memberCheck.rows.length === 0) {
return res.status(403).json({
success: false,
error: 'Permission denied'
});
}
// Get next position
const posResult = await db.query(`
SELECT COALESCE(MAX(position), -1) + 1 as next_pos FROM channels WHERE server_id = $1
`, [req.params.id]);
const channel = await db.query(`
INSERT INTO channels (server_id, name, description, channel_type, position)
VALUES ($1, $2, $3, $4, $5)
RETURNING *
`, [req.params.id, name, description || null, channel_type || 'text', posResult.rows[0].next_pos]);
res.json({
success: true,
channel: channel.rows[0]
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* GET /api/servers/:id/members
* Get members of a server
*/
router.get('/:id/members', authenticateUser, async (req, res) => {
try {
// Check membership
const memberCheck = await db.query(`
SELECT * FROM server_members WHERE server_id = $1 AND user_id = $2
`, [req.params.id, req.user.id]);
if (memberCheck.rows.length === 0) {
return res.status(403).json({
success: false,
error: 'Not a member of this server'
});
}
const members = await db.query(`
SELECT u.id, u.username, u.display_name, u.avatar_url, u.status, u.custom_status,
sm.role, sm.nickname, sm.joined_at
FROM server_members sm
INNER JOIN users u ON sm.user_id = u.id
WHERE sm.server_id = $1
ORDER BY
CASE sm.role
WHEN 'owner' THEN 0
WHEN 'admin' THEN 1
WHEN 'moderator' THEN 2
ELSE 3
END,
sm.joined_at ASC
`, [req.params.id]);
res.json({
success: true,
members: members.rows
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* GET /api/channels/:channelId/messages
* Get messages from a channel
*/
router.get('/channels/:channelId/messages', authenticateUser, async (req, res) => {
try {
const { limit = 50, before } = req.query;
// Get channel and check membership
const channel = await db.query(`SELECT server_id FROM channels WHERE id = $1`, [req.params.channelId]);
if (channel.rows.length === 0) {
return res.status(404).json({ success: false, error: 'Channel not found' });
}
const memberCheck = await db.query(`
SELECT * FROM server_members WHERE server_id = $1 AND user_id = $2
`, [channel.rows[0].server_id, req.user.id]);
if (memberCheck.rows.length === 0) {
return res.status(403).json({ success: false, error: 'Not a member' });
}
let query = `
SELECT m.*, u.username, u.display_name, u.avatar_url
FROM messages m
INNER JOIN users u ON m.user_id = u.id
WHERE m.channel_id = $1
`;
const params = [req.params.channelId];
if (before) {
query += ` AND m.created_at < $2`;
params.push(before);
}
query += ` ORDER BY m.created_at DESC LIMIT $${params.length + 1}`;
params.push(parseInt(limit));
const messages = await db.query(query, params);
res.json({
success: true,
messages: messages.rows.reverse()
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* POST /api/channels/:channelId/messages
* Send a message to a channel
*/
router.post('/channels/:channelId/messages', authenticateUser, async (req, res) => {
try {
const { content } = req.body;
if (!content || content.trim().length === 0) {
return res.status(400).json({ success: false, error: 'Message content is required' });
}
// Get channel and check membership
const channel = await db.query(`SELECT server_id FROM channels WHERE id = $1`, [req.params.channelId]);
if (channel.rows.length === 0) {
return res.status(404).json({ success: false, error: 'Channel not found' });
}
const memberCheck = await db.query(`
SELECT * FROM server_members WHERE server_id = $1 AND user_id = $2
`, [channel.rows[0].server_id, req.user.id]);
if (memberCheck.rows.length === 0) {
return res.status(403).json({ success: false, error: 'Not a member' });
}
const message = await db.query(`
INSERT INTO messages (channel_id, user_id, content)
VALUES ($1, $2, $3)
RETURNING *
`, [req.params.channelId, req.user.id, content.trim()]);
// Get user info
const user = await db.query(`SELECT username, display_name, avatar_url FROM users WHERE id = $1`, [req.user.id]);
res.json({
success: true,
message: {
...message.rows[0],
...user.rows[0]
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;