Files
relay/server/db/db.js
2024-11-21 17:12:11 +01:00

306 lines
7.9 KiB
JavaScript

const { Client } = require("pg");
const crypto = require("crypto");
require("dotenv").config();
const client = new Client({
user: process.env.PG_USER,
password: process.env.PG_PASSWORD,
database: process.env.PG_DATABASE,
host: process.env.PG_HOST,
port: 5433,
});
client
.connect()
.then(() => {
createTables()
.then(() => console.log("Tables created successfully"))
.catch((e) => console.error("Failed to create tables ", e));
console.log(
`Successfully connected to database: ${process.env.PG_DATABASE}`,
);
})
.catch((err) =>
console.error(
`Failed to connect to database: ${process.env.PG_DATABASE}, ${err}`,
),
);
// Creating database tables
async function createTables() {
try {
await client.query(`
CREATE TABLE IF NOT EXISTS accounts (
username VARCHAR(20) NOT NULL UNIQUE,
password VARCHAR(128) NOT NULL,
user_id VARCHAR(128) PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW()
);
`);
} catch (e) {
console.error("Failed to create accounts table ", e);
}
try {
await client.query(`
CREATE TABLE IF NOT EXISTS messages (
sender VARCHAR(128) NOT NULL,
recipient VARCHAR(128) NOT NULL,
message VARCHAR(10000) NOT NULL,
timestamp TIMESTAMPTZ DEFAULT NOW(),
message_id SERIAL PRIMARY KEY,
attachment VARCHAR(1000),
UNIQUE (sender, recipient, message_id)
);
CREATE INDEX IF NOT EXISTS idx_messages_conversation
ON messages (sender, recipient, message_id ASC);
`);
} catch (e) {
console.error("Failed to create messages table ", e);
}
try {
await client.query(`
CREATE TABLE IF NOT EXISTS contacts (
username VARCHAR(20) NOT NULL,
usernameContact VARCHAR(20) NOT NULL,
read BOOLEAN NOT NULL,
lastActive VARCHAR(100) NOT NULL,
CONSTRAINT unique_username_contact UNIQUE (username, usernameContact)
);
`);
} catch (e) {
console.error("Failed to create messages table ", e);
}
}
async function insertUser(username, password) {
const user_id = crypto.randomUUID();
const query = `
INSERT INTO accounts (username, password, user_id)
VALUES ($1, $2, $3);
`;
try {
await client.query(query, [username, password, user_id]);
} catch (e) {
console.error("Failed to insert user ", e);
}
}
async function getUserId(username) {
const query = `
SELECT user_id FROM accounts
WHERE username = $1;
`;
try {
const result = await client.query(query, [username]);
console.log("GETUSERID: ", result.rows[0].user_id);
return result.rows[0].user_id;
} catch (e) {
console.error("Failed to get user id", e);
}
}
async function insertMessage(sender, recipient, message) {
const query = `
INSERT INTO messages (sender, recipient, message)
VALUES ($1, $2, $3)
RETURNING message, timestamp, message_id;
`;
try {
const results = await client.query(query, [sender, recipient, message]);
return results.rows[0];
} catch (e) {
console.error("Failed to insert message ", e);
}
}
async function getMessages(username, recipient, limit = 50, cursor = 0) {
console.log(
`getMessages for Username: ${username}, recipient: ${recipient}, limit: ${limit}, cursor: ${cursor}`,
);
let query;
let params;
if (cursor) {
query = `
SELECT * FROM messages
WHERE ((sender = $1 AND recipient = $2) OR (sender = $2 AND recipient = $1))
AND message_id < $3
ORDER BY message_id DESC
LIMIT $4;
`;
params = [username, recipient, cursor, limit];
} else {
query = `
SELECT * FROM messages
WHERE (sender = $1 AND recipient = $2) OR (sender = $2 AND recipient = $1)
ORDER BY message_id DESC
LIMIT $3;
`;
params = [username, recipient, limit];
}
try {
const results = await client.query(query, params);
let messages = results.rows;
if (!cursor) {
messages = messages.reverse();
}
return messages;
} catch (e) {
console.error("Failed to get messages ", e);
}
}
async function checkUserExist(username) {
const query = `
SELECT 1 FROM accounts
WHERE LOWER(username) = LOWER($1)
LIMIT 1;
`;
try {
const result = await client.query(query, [username]);
return result.rows.length > 0;
} catch (e) {
console.error("Failed to check if user exist ", e);
return false;
}
}
async function getPassword(username) {
console.log(`Get password for: ${username}`);
const query = `
SELECT password FROM accounts
WHERE LOWER(username) = LOWER($1);
`;
try {
const result = await client.query(query, [username]);
return result.rows[0].password;
} catch (e) {
console.error("Failed to get user password ", e);
}
}
async function changePassword(username, newPassword) {
const query = `
UPDATE accounts
SET password = $1
WHERE username = $2;
`;
try {
await client.query(query, [newPassword, username]);
} catch (e) {
console.error("Failed to change password ", e);
}
}
async function insertContact(username, usernameContact, read) {
const timestamp = getTime();
console.log(
`insertContact username: ${username}, usernameContact: ${usernameContact}, read: ${read}`,
);
const query = `
INSERT INTO contacts (username, usernameContact, lastActive, read)
VALUES ($1, $2, $3, $4)
ON CONFLICT ON CONSTRAINT unique_username_contact
DO NOTHING;
`;
try {
await client.query(query, [username, usernameContact, timestamp, read]);
} catch (e) {
console.error("Failed to insert contact ", e);
}
}
async function getContacts(username) {
const query = `
SELECT usernameContact, read FROM contacts
WHERE username = $1
ORDER BY lastActive ASC;
`;
try {
const result = await client.query(query, [username]);
return result.rows;
} catch (e) {
console.error("Failed to get contacts ", e);
}
}
async function deleteContact(username, usernamecontact) {
const query = `
DELETE FROM contacts
WHERE (LOWER(username) = LOWER($1) AND LOWER(usernamecontact) = LOWER($2))
RETURNING *;
`;
try {
const result = await client.query(query, [username, usernamecontact]);
if (result.rowCount === 0) {
console.log("No matching contact found with:", {
username,
usernamecontact,
});
} else {
console.log("Successfully deleted contact");
}
} catch (e) {
console.error("Failed to remove contact ", e);
}
}
async function updateContactStatus(username, usernamecontact, read) {
const query = `
UPDATE contacts SET read = $1
WHERE username = $2 AND usernamecontact = $3
`;
try {
await client.query(query, [read, username, usernamecontact]);
await updateContactLastActive(username, usernamecontact);
console.log("Successfully updated contact status");
} catch (e) {
console.error("Failed to update contact status ", e);
}
}
async function updateContactLastActive(username, usernamecontact) {
const timestamp = getTime();
const query = `
UPDATE contacts SET lastActive = $1
WHERE username = $2 AND usernamecontact = $3
`;
try {
await client.query(query, [timestamp, username, usernamecontact]);
console.log("Successfully updated contact last active time");
} catch (e) {
console.error("Failed to update contact last active time");
}
}
function getTime() {
const now = new Date();
const year = now.getFullYear();
const month = ("0" + (now.getMonth() + 1)).slice(-2);
const day = ("0" + now.getDate()).slice(-2);
const hour = ("0" + now.getHours()).slice(-2);
const minute = ("0" + now.getMinutes()).slice(-2);
const second = ("0" + now.getSeconds()).slice(-2);
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
module.exports = {
client,
insertUser,
insertMessage,
checkUserExist,
changePassword,
getPassword,
insertContact,
deleteContact,
getMessages,
getUserId,
getContacts,
updateContactStatus,
updateContactLastActive,
};