add hacky database error handling to prevent the server from crashing due to trivial errors
This commit is contained in:
parent
6fe398c82a
commit
9540bc6178
7 changed files with 47 additions and 22 deletions
|
@ -83,7 +83,7 @@ export function decodeToken(encoded: string): Promise<User> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await query("SELECT * FROM users WHERE id = $1", [decoded.id]);
|
const user = await query("SELECT * FROM users WHERE id = $1", [decoded.id]);
|
||||||
if (user.rowCount < 1) {
|
if (!user || user.rowCount < 1) {
|
||||||
reject("user does not exist (could not find in database by id)");
|
reject("user does not exist (could not find in database by id)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,25 @@
|
||||||
import { Pool } from "pg";
|
import { Pool, QueryResult } from "pg";
|
||||||
|
|
||||||
const pool = new Pool();
|
const pool = new Pool();
|
||||||
|
|
||||||
export const query = pool.query.bind(pool);
|
// hacky wrapper function that returns null on database errors.
|
||||||
|
// this is done because express doesn't automatically call next()
|
||||||
|
// when an async function throws, so this prevents the server
|
||||||
|
// from crashing due to trivial database errors that can be handled.
|
||||||
|
// we could use a try catch block for each query, but that will
|
||||||
|
// quickly get cumbersome.
|
||||||
|
export const query = function(text: string, params: any[] = [], rejectOnError = false): Promise<QueryResult | null> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
pool.query(text, params)
|
||||||
|
.then((data) => {
|
||||||
|
resolve(data);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (rejectOnError) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -20,4 +20,5 @@ export const gatewayErrors = {
|
||||||
PAYLOAD_TOO_LARGE: { code: 4007, message: "Payload too large" },
|
PAYLOAD_TOO_LARGE: { code: 4007, message: "Payload too large" },
|
||||||
TOO_MANY_SESSIONS: { code: 4008, message: "Too many sessions" },
|
TOO_MANY_SESSIONS: { code: 4008, message: "Too many sessions" },
|
||||||
NOT_AUTHENTICATED: { code: 4009, message: "Not authenticated" },
|
NOT_AUTHENTICATED: { code: 4009, message: "Not authenticated" },
|
||||||
|
GOT_NO_DATABASE_DATA: { code: 4010, message: "Unexpectedly got no data from database" },
|
||||||
};
|
};
|
||||||
|
|
|
@ -223,6 +223,10 @@ export default function(server: Server) {
|
||||||
// TODO: each user should have their own list of channels that they join
|
// TODO: each user should have their own list of channels that they join
|
||||||
const channels = await query("SELECT id, name, owner_id FROM channels ORDER BY id ASC");
|
const channels = await query("SELECT id, name, owner_id FROM channels ORDER BY id ASC");
|
||||||
|
|
||||||
|
if (!channels) {
|
||||||
|
return closeWithError(ws, gatewayErrors.GOT_NO_DATABASE_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
clientSubscribe(ws, "*");
|
clientSubscribe(ws, "*");
|
||||||
channels.rows.forEach(c => {
|
channels.rows.forEach(c => {
|
||||||
clientSubscribe(ws, `channel:${c.id}`);
|
clientSubscribe(ws, `channel:${c.id}`);
|
||||||
|
|
|
@ -26,7 +26,7 @@ router.post(
|
||||||
|
|
||||||
const { name } = req.body;
|
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]);
|
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) {
|
if (!result || result.rowCount < 1) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
...errors.GOT_NO_DATABASE_DATA
|
...errors.GOT_NO_DATABASE_DATA
|
||||||
});
|
});
|
||||||
|
@ -60,7 +60,7 @@ router.put(
|
||||||
const id = parseInt(req.params.id); // TODO: ??
|
const id = parseInt(req.params.id); // TODO: ??
|
||||||
|
|
||||||
const permissionCheckResult = await query("SELECT owner_id FROM channels WHERE id = $1", [id]);
|
const permissionCheckResult = await query("SELECT owner_id FROM channels WHERE id = $1", [id]);
|
||||||
if (permissionCheckResult.rowCount < 1) {
|
if (!permissionCheckResult || permissionCheckResult.rowCount < 1) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
...errors.NOT_FOUND
|
...errors.NOT_FOUND
|
||||||
});
|
});
|
||||||
|
@ -72,7 +72,7 @@ router.put(
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await query("UPDATE channels SET name = $1 WHERE id = $2", [name, id]);
|
const result = await query("UPDATE channels SET name = $1 WHERE id = $2", [name, id]);
|
||||||
if (result.rowCount < 1) {
|
if (!result || result.rowCount < 1) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
...errors.GOT_NO_DATABASE_DATA
|
...errors.GOT_NO_DATABASE_DATA
|
||||||
});
|
});
|
||||||
|
@ -106,7 +106,7 @@ router.delete(
|
||||||
const id = parseInt(req.params.id); // TODO: ??
|
const id = parseInt(req.params.id); // TODO: ??
|
||||||
|
|
||||||
const permissionCheckResult = await query("SELECT owner_id FROM channels WHERE id = $1", [id]);
|
const permissionCheckResult = await query("SELECT owner_id FROM channels WHERE id = $1", [id]);
|
||||||
if (permissionCheckResult.rowCount < 1) {
|
if (!permissionCheckResult || permissionCheckResult.rowCount < 1) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
...errors.NOT_FOUND
|
...errors.NOT_FOUND
|
||||||
});
|
});
|
||||||
|
@ -118,7 +118,7 @@ router.delete(
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await query("DELETE FROM channels WHERE id = $1", [id]);
|
const result = await query("DELETE FROM channels WHERE id = $1", [id]);
|
||||||
if (result.rowCount < 1) {
|
if (!result || result.rowCount < 1) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
...errors.GOT_NO_DATABASE_DATA
|
...errors.GOT_NO_DATABASE_DATA
|
||||||
});
|
});
|
||||||
|
@ -148,7 +148,7 @@ router.get(
|
||||||
|
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const result = await query("SELECT id, name, owner_id FROM channels WHERE id = $1", [id]);
|
const result = await query("SELECT id, name, owner_id FROM channels WHERE id = $1", [id]);
|
||||||
if (result.rowCount < 1) {
|
if (!result || result.rowCount < 1) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
...errors.NOT_FOUND
|
...errors.NOT_FOUND
|
||||||
});
|
});
|
||||||
|
@ -164,7 +164,7 @@ router.get(
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const result = await query("SELECT id, name, owner_id FROM channels");
|
const result = await query("SELECT id, name, owner_id FROM channels");
|
||||||
|
|
||||||
return res.status(200).send(result.rows);
|
return res.status(200).send(result ? result.rows : []);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ router.post(
|
||||||
const createdAt = Date.now().toString();
|
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]);
|
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) {
|
if (!result || result.rowCount < 1) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
...errors.GOT_NO_DATABASE_DATA
|
...errors.GOT_NO_DATABASE_DATA
|
||||||
});
|
});
|
||||||
|
@ -226,10 +226,10 @@ router.get(
|
||||||
|
|
||||||
if (before) {
|
if (before) {
|
||||||
const result = await query(getMessagesByChannelPage, [before, channelId]);
|
const result = await query(getMessagesByChannelPage, [before, channelId]);
|
||||||
finalRows = result.rows;
|
finalRows = result ? result.rows : [];
|
||||||
} else {
|
} else {
|
||||||
const result = await query(getMessagesByChannelFirstPage, [channelId]);
|
const result = await query(getMessagesByChannelFirstPage, [channelId]);
|
||||||
finalRows = result.rows;
|
finalRows = result ? result.rows : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send(finalRows);
|
return res.status(200).send(finalRows);
|
||||||
|
|
|
@ -22,7 +22,7 @@ router.delete(
|
||||||
const id = parseInt(req.params.id); // TODO: ??
|
const id = parseInt(req.params.id); // TODO: ??
|
||||||
|
|
||||||
const permissionCheckResult = await query("SELECT author_id, channel_id FROM messages WHERE id = $1", [id]);
|
const permissionCheckResult = await query("SELECT author_id, channel_id FROM messages WHERE id = $1", [id]);
|
||||||
if (permissionCheckResult.rowCount < 1) {
|
if (!permissionCheckResult || permissionCheckResult.rowCount < 1) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
...errors.NOT_FOUND
|
...errors.NOT_FOUND
|
||||||
});
|
});
|
||||||
|
@ -34,7 +34,7 @@ router.delete(
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await query("DELETE FROM messages WHERE id = $1", [id]);
|
const result = await query("DELETE FROM messages WHERE id = $1", [id]);
|
||||||
if (result.rowCount < 1) {
|
if (!result || result.rowCount < 1) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
...errors.GOT_NO_DATABASE_DATA
|
...errors.GOT_NO_DATABASE_DATA
|
||||||
});
|
});
|
||||||
|
@ -67,7 +67,7 @@ router.put(
|
||||||
const id = parseInt(req.params.id); // TODO: ??
|
const id = parseInt(req.params.id); // TODO: ??
|
||||||
|
|
||||||
const permissionCheckResult = await query(getMessageById, [id]);
|
const permissionCheckResult = await query(getMessageById, [id]);
|
||||||
if (permissionCheckResult.rowCount < 1) {
|
if (!permissionCheckResult || permissionCheckResult.rowCount < 1) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
...errors.NOT_FOUND
|
...errors.NOT_FOUND
|
||||||
});
|
});
|
||||||
|
@ -79,7 +79,7 @@ router.put(
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await query("UPDATE messages SET content = $1 WHERE id = $2", [content, id]);
|
const result = await query("UPDATE messages SET content = $1 WHERE id = $2", [content, id]);
|
||||||
if (result.rowCount < 1) {
|
if (!result || result.rowCount < 1) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
...errors.GOT_NO_DATABASE_DATA
|
...errors.GOT_NO_DATABASE_DATA
|
||||||
});
|
});
|
||||||
|
@ -111,7 +111,7 @@ router.get(
|
||||||
|
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const result = await query(getMessageById, [id]);
|
const result = await query(getMessageById, [id]);
|
||||||
if (result.rowCount < 1) {
|
if (!result || result.rowCount < 1) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
...errors.NOT_FOUND
|
...errors.NOT_FOUND
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,7 +26,7 @@ router.post(
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
|
|
||||||
const existingUser = await query("SELECT * FROM users WHERE username = $1", [username]);
|
const existingUser = await query("SELECT * FROM users WHERE username = $1", [username]);
|
||||||
if (existingUser.rowCount > 0) {
|
if (existingUser && existingUser.rowCount > 0) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
...errors.INVALID_DATA,
|
...errors.INVALID_DATA,
|
||||||
errors: [ { location: "body", msg: "Username already exists", param: "username" } ]
|
errors: [ { location: "body", msg: "Username already exists", param: "username" } ]
|
||||||
|
@ -35,7 +35,7 @@ router.post(
|
||||||
|
|
||||||
const hashedPassword = await hash(password, 10);
|
const hashedPassword = await hash(password, 10);
|
||||||
const insertedUser = await query("INSERT INTO users(username, password, is_superuser) VALUES ($1, $2, $3) RETURNING id, username, is_superuser", [username, hashedPassword, false]);
|
const insertedUser = await query("INSERT INTO users(username, password, is_superuser) VALUES ($1, $2, $3) RETURNING id, username, is_superuser", [username, hashedPassword, false]);
|
||||||
if (insertedUser.rowCount < 1) {
|
if (!insertedUser || insertedUser.rowCount < 1) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
...errors.GOT_NO_DATABASE_DATA
|
...errors.GOT_NO_DATABASE_DATA
|
||||||
});
|
});
|
||||||
|
@ -58,7 +58,7 @@ router.post(
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
|
|
||||||
const existingUser = await query("SELECT * FROM users WHERE username = $1", [username]);
|
const existingUser = await query("SELECT * FROM users WHERE username = $1", [username]);
|
||||||
if (existingUser.rowCount < 1) {
|
if (!existingUser || existingUser.rowCount < 1) {
|
||||||
return res.status(400).json({ ...errors.BAD_LOGIN_CREDENTIALS });
|
return res.status(400).json({ ...errors.BAD_LOGIN_CREDENTIALS });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ router.post(
|
||||||
|
|
||||||
if (superuserKey && superuserKey.length >= 1 && key === superuserKey && req.user) {
|
if (superuserKey && superuserKey.length >= 1 && key === superuserKey && req.user) {
|
||||||
const updateUserResult = await query("UPDATE users SET is_superuser = true WHERE id = $1", [req.user.id]);
|
const updateUserResult = await query("UPDATE users SET is_superuser = true WHERE id = $1", [req.user.id]);
|
||||||
if (updateUserResult.rowCount < 1) {
|
if (!updateUserResult || updateUserResult.rowCount < 1) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
...errors.GOT_NO_DATABASE_DATA
|
...errors.GOT_NO_DATABASE_DATA
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue