import express from "express"; import { body, param, validationResult } from "express-validator"; import { authenticateRoute } from "../../../auth"; import { query } from "../../../database"; import { errors } from "../../../errors"; import { dispatch, dispatchChannelSubscribe } from "../../../gateway"; import { GatewayPayloadType } from "../../../gateway/gatewaypayloadtype"; const router = express.Router(); router.post( "/", authenticateRoute(), body("name").isLength({ min: 1, max: 40 }).isAlphanumeric("en-US", { ignore: " _-" }), async (req, res) => { const validationErrors = validationResult(req); if (!validationErrors.isEmpty()) { return res.status(400).json({ ...errors.INVALID_DATA, errors: validationErrors.array() }); } const { name } = req.body; const result = await query("INSERT INTO channels(name, owner_id) VALUES ($1, $2) RETURNING id, name, owner_id", [name, req.user.id]); if (result.rowCount < 1) { return res.status(500).json({ ...errors.GOT_NO_DATABASE_DATA }); } dispatch("*", { t: GatewayPayloadType.ChannelCreate, d: result.rows[0] }); // When a new channel is created, we will currently subscribe every client // on the gateway (this will be changed when the concept of "communities" is added) dispatchChannelSubscribe("*", `channel:${result.rows[0].id}`); res.status(201).send(result.rows[0]); } ); router.put( "/:id", authenticateRoute(), body("name").isLength({ min: 1, max: 40 }).isAlphanumeric("en-US", { ignore: " _-" }), param("id").isNumeric(), async (req, res) => { const validationErrors = validationResult(req); if (!validationErrors.isEmpty()) { return res.status(400).json({ ...errors.INVALID_DATA, errors: validationErrors.array() }); } const { name } = req.body; const id = parseInt(req.params.id); // TODO: ?? const permissionCheckResult = await query("SELECT owner_id FROM channels WHERE id = $1", [id]); if (permissionCheckResult.rowCount < 1) { return res.status(404).json({ ...errors.NOT_FOUND }); } if (permissionCheckResult.rows[0].owner_id !== req.user.id) { return res.status(403).json({ ...errors.FORBIDDEN_DUE_TO_MISSING_PERMISSIONS }); } const result = await query("UPDATE channels SET name = $1 WHERE id = $2", [name, id]); if (result.rowCount < 1) { return res.status(500).json({ ...errors.GOT_NO_DATABASE_DATA }); } const updatePayload = { id, name, owner_id: permissionCheckResult.rows[0].owner_id }; dispatch(`channel:${id}`, { t: GatewayPayloadType.ChannelUpdate, d: updatePayload }); return res.status(200).send(updatePayload); } ); router.delete( "/:id", authenticateRoute(), param("id").isNumeric(), async (req, res) => { const validationErrors = validationResult(req); if (!validationErrors.isEmpty()) { return res.status(400).json({ ...errors.INVALID_DATA, errors: validationErrors.array() }); } const id = parseInt(req.params.id); // TODO: ?? const permissionCheckResult = await query("SELECT owner_id FROM channels WHERE id = $1", [id]); if (permissionCheckResult.rowCount < 1) { return res.status(404).json({ ...errors.NOT_FOUND }); } if (permissionCheckResult.rows[0].owner_id !== req.user.id) { return res.status(403).json({ ...errors.FORBIDDEN_DUE_TO_MISSING_PERMISSIONS }); } const result = await query("DELETE FROM channels WHERE id = $1", [id]); if (result.rowCount < 1) { return res.status(500).json({ ...errors.GOT_NO_DATABASE_DATA }); } dispatch(`channel:${id}`, { t: GatewayPayloadType.ChannelDelete, d: { id } }); return res.status(204).send(""); } ); router.get( "/:id", authenticateRoute(), param("id").isNumeric(), async (req, res) => { const validationErrors = validationResult(req); if (!validationErrors.isEmpty()) { return res.status(400).json({ ...errors.INVALID_DATA, errors: validationErrors.array() }); } const { id } = req.params; const result = await query("SELECT id, name, owner_id FROM channels WHERE id = $1", [id]); if (result.rowCount < 1) { return res.status(404).json({ ...errors.NOT_FOUND }); } return res.status(200).send(result.rows[0]); } ); router.get( "/", authenticateRoute(), async (req, res) => { const result = await query("SELECT id, name, owner_id FROM channels"); return res.status(200).send(result.rows); } ); router.post( "/:id/messages", authenticateRoute(), param("id").isNumeric(), body("content").isLength({ min: 1, max: 4000 }), async (req, res) => { const validationErrors = validationResult(req); if (!validationErrors.isEmpty()) { return res.status(400).json({ ...errors.INVALID_DATA, errors: validationErrors.array() }); } const channelId = parseInt(req.params.id); const { content } = req.body; const authorId = req.user.id; const createdAt = Date.now().toString(); const result = await query("INSERT INTO messages(content, channel_id, author_id, created_at) VALUES ($1, $2, $3, $4) RETURNING id", [content, channelId, authorId, createdAt]); if (result.rowCount < 1) { return res.status(500).json({ ...errors.GOT_NO_DATABASE_DATA }); } const returnObject = { id: result.rows[0].id, content, channel_id: channelId, author_id: authorId, created_at: createdAt }; dispatch(`channel:${channelId}`, { t: GatewayPayloadType.MessageCreate, d: returnObject }); return res.status(201).send(returnObject); } ); router.get( "/:id/messages", authenticateRoute(), param("id").isNumeric(), async (req, res) => { const validationErrors = validationResult(req); if (!validationErrors.isEmpty()) { return res.status(400).json({ ...errors.INVALID_DATA, errors: validationErrors.array() }); } const { before } = req.query; const channelId = parseInt(req.params.id); let finalRows = []; if (before) { const result = await query("SELECT id, content, channel_id, author_id, created_at FROM messages WHERE id < $1 AND channel_id = $2 ORDER BY id DESC LIMIT 50", [before, channelId]); finalRows = result.rows; } else { const result = await query("SELECT id, content, channel_id, author_id, created_at FROM messages WHERE channel_id = $1 ORDER BY id DESC LIMIT 50", [channelId]); finalRows = result.rows; } return res.status(200).send(finalRows); } ); export default router;