From aeaae9fc58bb727e02c18a197a7c23eb922e13f2 Mon Sep 17 00:00:00 2001 From: Esad Mustafoski Date: Fri, 14 Mar 2025 06:33:12 +0100 Subject: [PATCH] Huge changes! Added some API features, but not fully. --- api/doc/API.md | 0 api/helpers/chat_api.ts | 137 +++++++++ api/helpers/comments_api.ts | 128 ++++++++ api/helpers/mod.ts | 11 + api/helpers/post_api.ts | 8 + api/helpers/user_api.ts | 2 + api/main.ts | 61 ++-- database/create_db.ts | 59 ++-- database/doc/DATABASE_UTILS.md | 0 database/esp-projekt.sqlite | Bin 20480 -> 0 bytes database/helpers/interfaces.ts | 61 ++++ database/helpers/maphelper.ts | 122 ++++++++ database/helpers/utils/chat_utils.ts | 86 ++++++ database/helpers/utils/comment_utils.ts | 62 ++++ database/helpers/utils/core_utils.ts | 45 +++ database/helpers/utils/mod.ts | 13 + database/helpers/utils/post_utils.ts | 118 +++++++ database/helpers/utils/user_utils.ts | 95 ++++++ database/utils.ts | 388 ++++++++---------------- tests/db.test.ts | 2 +- 20 files changed, 1093 insertions(+), 305 deletions(-) create mode 100644 api/doc/API.md create mode 100644 api/helpers/chat_api.ts create mode 100644 api/helpers/comments_api.ts create mode 100644 api/helpers/mod.ts create mode 100644 api/helpers/post_api.ts create mode 100644 api/helpers/user_api.ts create mode 100644 database/doc/DATABASE_UTILS.md delete mode 100644 database/esp-projekt.sqlite create mode 100644 database/helpers/interfaces.ts create mode 100644 database/helpers/maphelper.ts create mode 100644 database/helpers/utils/chat_utils.ts create mode 100644 database/helpers/utils/comment_utils.ts create mode 100644 database/helpers/utils/core_utils.ts create mode 100644 database/helpers/utils/mod.ts create mode 100644 database/helpers/utils/post_utils.ts create mode 100644 database/helpers/utils/user_utils.ts diff --git a/api/doc/API.md b/api/doc/API.md new file mode 100644 index 0000000..e69de29 diff --git a/api/helpers/chat_api.ts b/api/helpers/chat_api.ts new file mode 100644 index 0000000..3d579e7 --- /dev/null +++ b/api/helpers/chat_api.ts @@ -0,0 +1,137 @@ +/// +/** + * @author Esad Mustafoski + * @description API file for Comments + */ + +import * as db_utils from "../../database/utils.ts"; +import * as helper_utils from "../helpers.ts"; +import { Context } from "https://deno.land/x/oak@v17.1.2/mod.ts"; + +// Post functions +async function api_getPostById(ctx: any): Promise { + try { + const postId = ctx.params.id; + if (!postId) { + helper_utils.errorResponse(ctx, 400, "Post ID required"); + return; + } + + const post = await db_utils.getPostById(postId); + if (!post) { + helper_utils.errorResponse(ctx, 404, "Post not found"); + return; + } + + ctx.response.body = post; + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error retrieving post"); + } +} + +async function api_createPost(ctx: Context): Promise { + try { + const body = ctx.request.body; + const result = await body.json(); + const { userId, postText, postType } = result; + + if (!userId || !postText || !postType) { + helper_utils.errorResponse( + ctx, + 400, + "User ID, post text, and post type required", + ); + return; + } + + // Create timestamp in the format expected by the database + const createdAt = `${Math.floor(Date.now() / 1000)}-${ + new Date().toLocaleDateString("en-GB").split("/").join("-") + }`; + + const postId = await db_utils.createPost( + userId, + createdAt, + postText, + postType, + ); + helper_utils.sendResponse(ctx, { + status: 201, + body: { postId }, + }); + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error creating post"); + } +} + +async function api_updatePost(ctx: any): Promise { + try { + const postId = ctx.params.id; + if (!postId) { + helper_utils.errorResponse(ctx, 400, "Post ID required"); + return; + } + + const body = ctx.request.body; + const result = await body.json(); + const { postText, postType } = result; + + if (!postText && !postType) { + helper_utils.errorResponse(ctx, 400, "No update data provided"); + return; + } + + await db_utils.updatePost(postId, postText, postType); + helper_utils.sendResponse(ctx, { + status: 200, + body: { message: "Post updated successfully" }, + }); + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error updating post"); + } +} + +async function api_deletePost(ctx: any): Promise { + try { + const postId = ctx.params.id; + if (!postId) { + helper_utils.errorResponse(ctx, 400, "Post ID required"); + return; + } + + await db_utils.deletePost(postId); + helper_utils.sendResponse(ctx, { + status: 200, + body: { message: "Post deleted successfully" }, + }); + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error deleting post"); + } +} + +async function api_likePost(ctx: any): Promise { + try { + const postId = ctx.params.id; + if (!postId) { + helper_utils.errorResponse(ctx, 400, "Post ID required"); + return; + } + + const body = ctx.request.body; + const result = await body.json(); + const { userId } = result; + + if (!userId) { + helper_utils.errorResponse(ctx, 400, "User ID required"); + return; + } + + await db_utils.likePost(postId, userId); + helper_utils.sendResponse(ctx, { + status: 200, + body: { message: "Post liked successfully" }, + }); + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error liking post"); + } +} diff --git a/api/helpers/comments_api.ts b/api/helpers/comments_api.ts new file mode 100644 index 0000000..dd25202 --- /dev/null +++ b/api/helpers/comments_api.ts @@ -0,0 +1,128 @@ +/// +/** + * @author Esad Mustafoski + * @description API file for Comments + */ + + +import * as db_utils from "../../database/utils.ts"; +import * as helper_utils from "../helpers.ts"; + +async function api_getPostComments(ctx: any): Promise { + try { + const postId = ctx.params.id; + if (!postId) { + helper_utils.errorResponse(ctx, 400, "Post ID required"); + return; + } + + const comments = await db_utils.getCommentsFromDB(Number(postId)); + ctx.response.body = comments; + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error retrieving comments"); + } +} + +async function api_createComment(ctx: any): Promise { + try { + const postId = ctx.params.id; + if (!postId) { + helper_utils.errorResponse(ctx, 400, "Post ID required"); + return; + } + + const body = ctx.request.body; + const result = await body.json(); + const { userId, text } = result; + + if (!userId || !text) { + helper_utils.errorResponse(ctx, 400, "User ID and comment text required"); + return; + } + + // Create timestamp in the format expected by the database + const createdAt = `${Math.floor(Date.now() / 1000)}-${ + new Date().toLocaleDateString("en-GB").split("/").join("-") + }`; + + const commentId = await db_utils.createComment(postId, userId, createdAt, text); + helper_utils.sendResponse(ctx, { + status: 201, + body: { commentId } + }); + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error creating comment"); + } +} + +async function api_updateComment(ctx: any): Promise { + try { + const commentId = ctx.params.id; + if (!commentId) { + helper_utils.errorResponse(ctx, 400, "Comment ID required"); + return; + } + + const body = ctx.request.body; + const result = await body.json(); + const { text } = result; + + if (!text) { + helper_utils.errorResponse(ctx, 400, "Comment text required"); + return; + } + + await db_utils.updateComment(commentId, text); + helper_utils.sendResponse(ctx, { + status: 200, + body: { message: "Comment updated successfully" } + }); + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error updating comment"); + } +} + +async function api_deleteComment(ctx: any): Promise { + try { + const commentId = ctx.params.id; + if (!commentId) { + helper_utils.errorResponse(ctx, 400, "Comment ID required"); + return; + } + + await db_utils.deleteComment(commentId); + helper_utils.sendResponse(ctx, { + status: 200, + body: { message: "Comment deleted successfully" } + }); + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error deleting comment"); + } +} + +async function api_likeComment(ctx: any): Promise { + try { + const commentId = ctx.params.id; + if (!commentId) { + helper_utils.errorResponse(ctx, 400, "Comment ID required"); + return; + } + + const body = ctx.request.body; + const result = await body.json(); + const { userId } = result; + + if (!userId) { + helper_utils.errorResponse(ctx, 400, "User ID required"); + return; + } + + await db_utils.likeComment(commentId, userId); + helper_utils.sendResponse(ctx, { + status: 200, + body: { message: "Comment liked successfully" } + }); + } catch (error) { + helper_utils.errorResponse(ctx, 500, "Error liking comment"); + } +} \ No newline at end of file diff --git a/api/helpers/mod.ts b/api/helpers/mod.ts new file mode 100644 index 0000000..5320818 --- /dev/null +++ b/api/helpers/mod.ts @@ -0,0 +1,11 @@ +/// +/** + * @author Esad Mustafoski + * @description A mod file is used to export all the functions in the folder, making them easier to access. + * @file mod.ts + */ + +export * from "./chat_api.ts"; +export * from "./comments_api.ts"; +export * from "./post_api.ts"; +export * from "./user_api.ts"; \ No newline at end of file diff --git a/api/helpers/post_api.ts b/api/helpers/post_api.ts new file mode 100644 index 0000000..6736d58 --- /dev/null +++ b/api/helpers/post_api.ts @@ -0,0 +1,8 @@ +/// +/** + * @author Esad Mustafoski + * @description API file for Posts + */ + +import * as db_utils from "../../database/utils.ts"; +import * as helper_utils from "../helpers.ts"; diff --git a/api/helpers/user_api.ts b/api/helpers/user_api.ts new file mode 100644 index 0000000..0861bd6 --- /dev/null +++ b/api/helpers/user_api.ts @@ -0,0 +1,2 @@ +import * as db_utils from "../../database/utils.ts"; +import * as helper_utils from "../helpers.ts"; diff --git a/api/main.ts b/api/main.ts index b362598..7f2ecdf 100644 --- a/api/main.ts +++ b/api/main.ts @@ -1,7 +1,7 @@ /// /** * @author Esad Mustafoski - * @ðescription Main API file, Handles all the routing/api stuff + * @description Main API file, Handles all the routing/api stuff */ // +++ IMPORTS ------------------------------------------------------ // @@ -10,11 +10,13 @@ import { Context, Next, Router, -} from "https://deno.land/x/oak/mod.ts"; -import { oakCors } from "https://deno.land/x/cors/mod.ts"; +} from "https://deno.land/x/oak@v17.1.2/mod.ts"; +import { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts"; import * as db_utils from "../database/utils.ts"; import * as helper_utils from "./helpers.ts"; +import {} from "./helpers/mod.ts"; + // +++ VARIABLES / TYPES --------------------------------------------- // const router = new Router(); const app = new Application(); @@ -63,13 +65,36 @@ router .get("/api/users", api_getAllUsers) .get("/api/user/:id/info", api_user_getInfo); +// -- Chat routes -- +router + .get("/api/chats", api_getChats) + .get("/api/chat/:id", api_getChatById) + .post("/api/chat/create", api_createChat) + .post("/api/chat/:id/message", api_sendMessage) + .delete("/api/chat/:id", api_deleteChat); + // -- Post routes -- router - .get("/api/posts", api_posts_getAll); + .get("/api/posts", api_posts_getAll) + .get("/api/post/:id", api_getPostById) + .post("/api/post/create", api_createPost) + .put("/api/post/:id", api_updatePost) + .delete("/api/post/:id", api_deletePost) + .post("/api/post/:id/like", api_likePost); + +// -- Comment Routes -- +router + .get("/api/post/:id/comments", api_getPostComments) + .post("/api/post/:id/comment", api_createComment) + .put("/api/comment/:id", api_updateComment) + .delete("/api/comment/:id", api_deleteComment) + .post("/api/comment/:id/like", api_likeComment); // +++ FUNCTIONS ----------------------------------------------------- // -async function authenticator(ctx: Context, next: Next): Promise { + +// ABANDONED FUNCTIONS +async function _authenticator(ctx: Context, next: Next): Promise { const authHeader = ctx.request.headers.get("Authorization"); if (!authHeader) { @@ -78,6 +103,8 @@ async function authenticator(ctx: Context, next: Next): Promise { return; } + // Bearer check + // Bearer is often used for authentication in API's const match = authHeader.match(/^Bearer (.+)$/); if (!match) { ctx.response.status = 401; @@ -96,7 +123,7 @@ async function authenticator(ctx: Context, next: Next): Promise { } } -async function tokenChecker(ctx: Context, next: Next): Promise { +async function _tokenChecker(ctx: Context, next: Next): Promise { // logic below (TODO): /** * 1. check if the token is valid @@ -107,12 +134,9 @@ async function tokenChecker(ctx: Context, next: Next): Promise { */ } -async function api_getAllUsers(ctx: Context): Promise { - const getUsers = await db_utils.getAllUsersFromDB(); - ctx.response.body = getUsers; -} -// Users + +// API: Users async function api_user_getInfo(ctx: any): Promise { const id = ctx.params.id; @@ -135,15 +159,17 @@ async function api_user_getInfo(ctx: any): Promise { } } -// Posts +async function api_getAllUsers(ctx: Context): Promise { + const getUsers = await db_utils.getAllUsersFromDB(); + ctx.response.body = getUsers; +} +// API: Posts async function api_posts_getAll(ctx: Context): Promise { const posts = await db_utils.getPostsFromDB(); ctx.response.body = posts; } -// Comments (missing) - -// login/register +// API: login/register async function api_register(ctx: Context): Promise { try { const body = ctx.request.body; @@ -157,6 +183,7 @@ async function api_register(ctx: Context): Promise { firstname, surname, } = result; + // Claude 3-5 Sonnet was used for the first Date formatting const account_created = `${Math.floor(Date.now() / 1000)}-${ new Date().toLocaleDateString("en-GB").split("/").join("-") }`; @@ -218,10 +245,10 @@ async function api_login(ctx: Context): Promise { const storedSalt = user.password_salt; // Salt the provided password with the stored salt const saltedPassword = `${password}${storedSalt}`; - // Hash the salted password + // Hash the salted password const hash = await helper_utils.hashPassword(saltedPassword); - // Compare with stored hash + // Compare the phashed password with the stored hash if (user.password !== hash) { helper_utils.errorResponse(ctx, 401, "Invalid password"); return "Error"; diff --git a/database/create_db.ts b/database/create_db.ts index 8704cce..2f31eb1 100644 --- a/database/create_db.ts +++ b/database/create_db.ts @@ -17,58 +17,77 @@ const db = new DB(dbPath); export function createDatabase(): void { db.execute(` CREATE TABLE IF NOT EXISTS accounts ( - uuid INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER PRIMARY KEY AUTOINCREMENT, user_group TEXT, - user_bio TEXT, - user_displayname TEXT, - user_username TEXT, - user_e-mail TEXT, + bio TEXT, + displayname TEXT, + username TEXT, + user_email TEXT, password TEXT, password_salt TEXT, firstname TEXT, surname TEXT, account_created TEXT, - followers ANY, - following ANY, - contacts ANY - ) + blocked_users TEXT, + followers TEXT, + following TEXT, + contacts TEXT + ); CREATE TABLE IF NOT EXISTS posts ( posts_uuid INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, created_at TEXT, post_text TEXT, + post_type TEXT, likes INTEGER, comments INTEGER - ) + ); CREATE TABLE IF NOT EXISTS comments ( - commentd_id INTEGER PRIMARY KEY AUTOINCREMENT, + comment_id INTEGER PRIMARY KEY AUTOINCREMENT, post_id INTEGER, author_user_id INTEGER, date_created_at TEXT, text TEXT, likes INTEGER ) + + CREATE TABLE IF NOT EXISTS messages ( + message_id INTEGER PRIMARY KEY AUTOINCREMENT, + chat_id INTEGER, + sender_id INTEGER, + content TEXT, + timestamp TEXT, + FOREIGN KEY (chat_id) REFERENCES chats (chat_id), + FOREIGN KEY (sender_id) REFERENCES accounts (user_id) + ) + + CREATE TABLE IF NOT EXISTS chats ( + chat_id INTEGER PRIMARY KEY AUTOINCREMENT, + chat_name TEXT, + participants TEXT, + created_at TEXT + ) `); } // Sample data generated using AI, does not work yet and will be adjusted export function insertSampleData(): void { db.query( - `INSERT INTO accounts (user_group, user_bio, user_displayname, user_username, user_email, password, password_salt, firstname, surname, account_created, followers, following, contacts) VALUES - ('admin', 'Admin bio', 'Admin User', 'admin', 'admin@example.com', 'hashedpass1', 'salt1', 'Admin', 'User', '2024-01-01', '[]', '[]', '[]', '[]'), - ('user', 'I love coding!', 'John Dev', 'johndev', 'john@example.com', 'hashedpass2', 'salt2', 'John', 'Smith', '2024-01-02', '[]', '[3,4]', '[1,2]', '[]'), - ('user', 'Photography enthusiast', 'Alice', 'alice_photo', 'alice@example.com', 'hashedpass3', 'salt3', 'Alice', 'Johnson', '2024-01-03', '[5]', '[1]', '[2]', '[]') + `INSERT INTO accounts (user_group, bio, displayname, username, user_email, password, password_salt, firstname, surname, account_created, blocked_users, followers, following, contacts) VALUES + ('admin', 'Admin bio', 'Admin User', 'admin', 'admin@example.com', 'pw1', 'salt1', 'Admin', 'User', '2024-01-01', '[]', '[]', '[]', '[]'), + ('user', 'I love coding!', 'John Dev', 'johndev', 'john@example.com', 'pw2', 'salt2', 'John', 'Smith', '2024-01-02', '[]', '[]', '[3,4]', '[1,2]'), + ('user', 'Photography enthusiast', 'Alice', 'alice_photo', 'alice@example.com', 'pw3', 'salt3', 'Alice', 'Johnson', '2024-01-03', '[]', '[5]', '[1]', '[2]') `, ); db.query( - `INSERT INTO posts (user_id, created_at, post_text, likes, comments) VALUES - (1, '2024-01-15 10:00:00', 'First post about programming!', 5, 2), - (1, '2024-01-15 11:30:00', 'Check out this new feature', 10, 3), - (2, '2024-01-16 09:15:00', 'Just learned about TypeScript', 8, 1), - (3, '2024-01-16 14:20:00', 'Posted my new photo collection', 15, 4) + `INSERT INTO posts (user_id, created_at, post_text, post_type, likes, comments) VALUES + (1, '2024-01-15 10:00:00', 'First post about programming!', 'text', 5, 2), + (1, '2024-01-15 11:30:00', 'Check out this new feature', 'text', 10, 3), + (2, '2024-01-16 09:15:00', 'Just learned about TypeScript', 'text', 8, 1), + (3, '2024-01-16 14:20:00', 'Posted my new photo collection', 'image', 15, 4) `, ); diff --git a/database/doc/DATABASE_UTILS.md b/database/doc/DATABASE_UTILS.md new file mode 100644 index 0000000..e69de29 diff --git a/database/esp-projekt.sqlite b/database/esp-projekt.sqlite deleted file mode 100644 index bc9807a87cc0a74ac9e99d44baa4a94483d02ff5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI4&2HO95P(TZj%}qDV+5`asgae9qBSBb{E;HdL5nDf>&6X&G**igf}zslN}@xN zDoNSVr9ccHdhfmWq7TqxAE1}q`UZW8UOKb6yU5>c>^06J~{Zv@R^Ita1a~TUA8Sqm4nx4D0}rlUU>b%?^h4< z4=+=qb|OFohyW2F0z`la5P|=!&p zMBo@-g#6m^hnd-8u_&Kh6+;d$X&`plGs8<%#RNrJ43EUjTgTI>_}fQQIzZCsQnOJ{ zFB%tcD*s&i^(x_22WBO6T(|( z3if|Hl#-ab?=HXJ1fjm~jYqNZ^Lr`8_H8eX0L7w?iYChrMvggg%^^Q~o24Ly8194& z*g4G`nG{f-U(qG0D#r%98SnVif*>0&PTq z2oM1xKm>>Y5g-CYfCvx)BJkc5cy%qONK4Yq<@Hw!OG`E2fivkA4?^}06hbIq1s?D5 zm*#Nf@CvMBHF|ZU##O7=Fg45S)>VDKr<$5s-`_A)!{|Y~Rx?ah?dlDSZ|GfanO3*a zsMYn2#=crZ2WVfb8`Wy9Ufkce#nX&IkoWaXQR_84S3E(|$w9?sx+?@Lcq=u4?P5(bo3b`d)j_ z&VbLgOWcL`P5LwNCL_Mv_3cqOcV~81nm3xVoNCsXrfnMN_Q8O6Uocb%2X-KQ_aM1u z?DKrKaRv3Z&+4HP{C$Cs;TdL9#QsJ;E&&W-rra;hWU4b5?Pv0Q)Kv|p^EH-4yDL^I zB4x;fXSjX=YL&TKF4B^tKB87N1@zcE#K-L6fKNvml$4pzv#7Q^H+QOLFiqdABAPEb zJi2F)Sm0PC*1|r*A5a!~1R{pYoy;^J^s1+Pnvi`kfJ`XMoRpavpJ2Bu`K%$M-rblF z>S;`i`6Qk&+*4OT=G_>|)C(d&1c(3;AOb{y2oM1xKm>>Y5g-CY;B5rNkNlMzlJb}G ztMa4r6%=VB0z`la5CI}U1c(3;AOb{y2oM1x@IDc!VA6nW+?I3GIS!a0fe-TUb9h6D z-5Wbmazs$NqG7tgyiq@sPXS2=BA?V>xr0gApY|po=H) zV+9tcKjg}o6(AeyQ`SLJ-4svwzvb|P6Wfj6;%6W7%qN%&ARDW3>H_?JK@-v{+TF(Y Km55w}QubfNrVOkA diff --git a/database/helpers/interfaces.ts b/database/helpers/interfaces.ts new file mode 100644 index 0000000..f594231 --- /dev/null +++ b/database/helpers/interfaces.ts @@ -0,0 +1,61 @@ +/// + +/** + * @author Esad Mustafoski + * @description This file is responsible for making Interfaces accessible by every file, deduplicating it. + * @file interfaces.ts + */ + +interface Post { + posts_uuid: number; + user_id: number; + created_at: string; + post_text: string; + post_type: string; + likes: number; + comments: number; +} + +interface Accounts { + user_id: number; + user_group: string; + bio: string; + displayname: string; + username: string; + user_email: string; + password: string; + password_salt: string; + firstname: string; + surname: string; + account_created: string; + blocked_users: string; + followers: string; + following: string; + contacts: string; +} + +interface Comments { + comment_id: number; + post_id: number; + author_user_id: number; + date_created_at: string; + text: string; + likes: number; +} + +interface Chat { + chat_id: number; + chat_name: string; + participants: string; + created_at: string; +} + +interface Message { + message_id: number; + chat_id: number; + sender_id: number; + content: string; + timestamp: string; +} + +export type { Accounts, Chat, Comments, Message, Post }; diff --git a/database/helpers/maphelper.ts b/database/helpers/maphelper.ts new file mode 100644 index 0000000..693bfc1 --- /dev/null +++ b/database/helpers/maphelper.ts @@ -0,0 +1,122 @@ +/// +/** + * @author Esad Mustafoski + * @description This file makes accessing the Database easier by creating a map for each type of data + * @file maphelper.ts + */ + +// +++ IMPORTS ------------------------------------------------------ // +import { Row } from "https://deno.land/x/sqlite@v3.9.1/mod.ts"; +import { + Accounts, + Chat, + Comments, + Message, + Post, +} from "../helpers/interfaces.ts"; + +function mapPostRow(row: Row): Post { + const [ + posts_uuid, + user_id, + created_at, + post_text, + post_type, + likes, + comments, + ] = row; + return { + posts_uuid: Number(posts_uuid), + user_id: Number(user_id), + created_at: String(created_at), + post_text: String(post_text), + post_type: String(post_type), + likes: Number(likes), + comments: Number(comments), + }; +} + +function mapCommentRow(row: Row): Comments { + const [ + comment_id, + post_id, + author_user_id, + date_created_at, + text, + likes, + ] = row; + return { + comment_id: Number(comment_id), + post_id: Number(post_id), + author_user_id: Number(author_user_id), + date_created_at: String(date_created_at), + text: String(text), + likes: Number(likes), + }; +} + +function mapChatRow(row: Row): Chat { + const [chat_id, chat_name, participants, created_at] = row; + return { + chat_id: Number(chat_id), + chat_name: String(chat_name), + participants: String(participants), + created_at: String(created_at), + }; +} + +function mapMessageRow(row: Row): Message { + const [message_id, chat_id, sender_id, content, timestamp] = row; + return { + message_id: Number(message_id), + chat_id: Number(chat_id), + sender_id: Number(sender_id), + content: String(content), + timestamp: String(timestamp), + }; +} + +function mapAccountRow(row: Row): Accounts { + const [ + user_id, + user_group, + bio, + displayname, + username, + user_email, + password, + password_salt, + firstname, + surname, + account_created, + blocked_users, + followers, + following, + contacts, + ] = row; + return { + user_id: Number(user_id), + user_group: String(user_group), + bio: String(bio), + displayname: String(displayname), + username: String(username), + user_email: String(user_email), + password: String(password), + password_salt: String(password_salt), + firstname: String(firstname), + surname: String(surname), + account_created: String(account_created), + blocked_users: String(blocked_users), + followers: String(followers), + following: String(following), + contacts: String(contacts), + }; +} + +export { + mapAccountRow, + mapChatRow, + mapCommentRow, + mapMessageRow, + mapPostRow +}; \ No newline at end of file diff --git a/database/helpers/utils/chat_utils.ts b/database/helpers/utils/chat_utils.ts new file mode 100644 index 0000000..d6ac389 --- /dev/null +++ b/database/helpers/utils/chat_utils.ts @@ -0,0 +1,86 @@ +/// + +/** + * @author Esad Mustafoski + * @description This file is responsible for creating Functions to easily access the Database, Specifically for Chats + * @file chatUtil.ts + */ + +// +++ IMPORTS ------------------------------------------------------ // +import { DB } from "https://deno.land/x/sqlite@v3.9.1/mod.ts"; +import { Chat, Message } from "../interfaces.ts"; +import { mapChatRow, mapMessageRow, queryDatabase } from "./mod.ts"; + +async function getUserChats(db: DB, userId: string): Promise { + const query = `SELECT * FROM chats WHERE participants LIKE '%${userId}%'`; + return await queryDatabase(db, query, [], mapChatRow); +} + +async function getChatById(db: DB, chatId: string): Promise { + const query = `SELECT * FROM chats WHERE chat_id = ?`; + const chats = await queryDatabase(db, query, [chatId], mapChatRow); + return chats.length > 0 ? chats[0] : null; +} + +async function getChatMessages(db: DB, chatId: string): Promise { + const query = + `SELECT * FROM messages WHERE chat_id = ? ORDER BY timestamp ASC`; + return await queryDatabase(db, query, [chatId], mapMessageRow); +} + +async function createChat( + db: DB, + participants: string[], + chatName: string, +): Promise { + const participantsJson = JSON.stringify(participants); + const timestamp = `${Math.floor(Date.now() / 1000)}-${ + new Date().toLocaleDateString("en-GB").split("/").join("-") + }`; + + const query = ` + INSERT INTO chats (chat_name, participants, created_at) + VALUES (?, ?, ?) + `; + + db.query(query, [chatName, participantsJson, timestamp]); + return db.lastInsertRowId.toString(); +} + +async function addMessageToChat( + db: DB, + chatId: string, + senderId: string, + content: string, +): Promise { + const timestamp = `${Math.floor(Date.now() / 1000)}-${ + new Date().toLocaleDateString("en-GB").split("/").join("-") + }`; + + const query = ` + INSERT INTO messages (chat_id, sender_id, content, timestamp) + VALUES (?, ?, ?, ?) + `; + + db.query(query, [chatId, senderId, content, timestamp]); + return db.lastInsertRowId.toString(); +} + +async function deleteChat(db: DB, chatId: string): Promise { + // First delete all messages in the chat + const deleteMessagesQuery = `DELETE FROM messages WHERE chat_id = ?`; + db.query(deleteMessagesQuery, [chatId]); + + // Then delete the chat itself + const deleteChatQuery = `DELETE FROM chats WHERE chat_id = ?`; + db.query(deleteChatQuery, [chatId]); +} + +export { + getUserChats, + getChatById, + getChatMessages, + createChat, + addMessageToChat, + deleteChat +}; \ No newline at end of file diff --git a/database/helpers/utils/comment_utils.ts b/database/helpers/utils/comment_utils.ts new file mode 100644 index 0000000..5e29d7a --- /dev/null +++ b/database/helpers/utils/comment_utils.ts @@ -0,0 +1,62 @@ +/// + +/** + * @author Esad Mustafoski + * @description This file is responsible for creating Functions to easily access the Database, Specifically for Comments + * @file commentUtil.ts + */ + +import { DB } from "https://deno.land/x/sqlite@v3.9.1/mod.ts"; +import { Comments } from "../interfaces.ts"; +import { mapCommentRow, queryDatabase } from "./mod.ts"; + +async function getCommentsFromDB(db: DB, post_id?: number): Promise { + const query = post_id + ? `SELECT * FROM comments WHERE post_id = ?` + : `SELECT * FROM comments`; + const params = post_id ? [post_id] : []; + return await queryDatabase(db, query, params, mapCommentRow); +} + +async function createComment(db: DB, postId: string, userId: string, createdAt: string, text: string): Promise { + const query = ` + INSERT INTO comments (post_id, author_user_id, date_created_at, text, likes) + VALUES (?, ?, ?, ?, 0) + `; + db.query(query, [postId, userId, createdAt, text]); + + const updatePostQuery = `UPDATE posts SET comments = comments + 1 WHERE posts_uuid = ?`; + db.query(updatePostQuery, [postId]); + + return db.lastInsertRowId.toString(); +} + +async function updateComment(db: DB, commentId: string, text: string): Promise { + const query = `UPDATE comments SET text = ? WHERE comment_id = ?`; + db.query(query, [text, commentId]); +} + +async function deleteComment(db: DB, commentId: string): Promise { + const getPostIdQuery = `SELECT post_id FROM comments WHERE comment_id = ?`; + const result = db.query(getPostIdQuery, [commentId]); + const postId: any = result[0][0]; + + const deleteCommentQuery = `DELETE FROM comments WHERE comment_id = ?`; + db.query(deleteCommentQuery, [commentId]); + + const updatePostQuery = `UPDATE posts SET comments = comments - 1 WHERE posts_uuid = ?`; + db.query(updatePostQuery, [postId]); +} + +async function likeComment(db: DB, commentId: string, userId: string): Promise { + const query = `UPDATE comments SET likes = likes + 1 WHERE comment_id = ?`; + db.query(query, [commentId]); +} + +export { + createComment, + updateComment, + deleteComment, + likeComment, + getCommentsFromDB, +} \ No newline at end of file diff --git a/database/helpers/utils/core_utils.ts b/database/helpers/utils/core_utils.ts new file mode 100644 index 0000000..bc66ed4 --- /dev/null +++ b/database/helpers/utils/core_utils.ts @@ -0,0 +1,45 @@ +/// +/** + * @author Esad Mustafoski + * @description Core Utility for querying the Database and inserting Sample Data + * @file core_utils.ts + */ + +import { DB, Row } from "https://deno.land/x/sqlite@v3.9.1/mod.ts"; +import * as db_create from "../../create_db.ts"; + +// +++ Helper ------------------------------------------------------ // +// "T" is a generic type, it can be anything and makes the function "flexible"(?) +async function queryDatabase( + db: DB, + query: string, + params: any[], + mapRow: (row: Row) => T, +): Promise { + const results: T[] = []; + try { + const rows = await db.query(query, params); + for (const row of rows) { + results.push(mapRow(row)); + } + } catch (error) { + console.error("Database query error:", error); + } + return results; +} + +// +++ FUNCTIONS --------------------------------------------------- // + +/** + * See: + * @file ./create_db.ts + */ +function insertSamples(): void { + db_create.insertSampleData(); +} + +function createDatabaseIfNotExist(): void { + db_create.createDatabase(); +} + +export { queryDatabase, insertSamples, createDatabaseIfNotExist }; \ No newline at end of file diff --git a/database/helpers/utils/mod.ts b/database/helpers/utils/mod.ts new file mode 100644 index 0000000..f66c7ed --- /dev/null +++ b/database/helpers/utils/mod.ts @@ -0,0 +1,13 @@ +/// +/** + * @author Esad Mustafoski + * @description A mod file is used to export all the functions in the folder, making them easier to access. + * @file mod.ts + */ + +export * from "./chat_utils.ts"; +export * from "./comment_utils.ts"; +export * from "./post_utils.ts"; +export * from "./user_utils.ts"; +export * from "./core_utils.ts" +export * from "../maphelper.ts"; \ No newline at end of file diff --git a/database/helpers/utils/post_utils.ts b/database/helpers/utils/post_utils.ts new file mode 100644 index 0000000..7c88413 --- /dev/null +++ b/database/helpers/utils/post_utils.ts @@ -0,0 +1,118 @@ +/// + +/** + * @author Esad Mustafoski + * @description This file is responsible for creating Functions to easily access the Database, Specifically for Posts + * @file postUtil.ts + */ + + +// +++ IMPORTS ------------------------------------------------------ // +import { DB } from "https://deno.land/x/sqlite@v3.9.1/mod.ts"; +import { Post } from "../interfaces.ts"; +import { mapPostRow, queryDatabase } from "./mod.ts"; + +async function getPostsFromDB(db: DB, user_uuid?: string): Promise { + const query = user_uuid + ? `SELECT * FROM posts WHERE user_id = ?` + : `SELECT * FROM posts`; + const params = user_uuid ? [user_uuid] : []; + return await queryDatabase(db, query, params, mapPostRow); +} + +async function getPostById(db: DB, postId: string): Promise { + const query = `SELECT * FROM posts WHERE posts_uuid = ?`; + const posts = await queryDatabase(db, query, [postId], mapPostRow); + return posts.length > 0 ? posts[0] : null; +} + +async function createPost(db: DB, userId: string, createdAt: string, postText: string, postType: string): Promise { + const query = ` + INSERT INTO posts (user_id, created_at, post_text, post_type, likes, comments) + VALUES (?, ?, ?, ?, 0, 0) + `; + + db.query(query, [userId, createdAt, postText, postType]); + return db.lastInsertRowId.toString(); +} + +async function updatePost(db: DB, postId: string, postText?: string, postType?: string): Promise { + let query = `UPDATE posts SET `; + const params: any[] = []; + + if (postText) { + query += `post_text = ?`; + params.push(postText); + + if (postType) { + query += `, post_type = ?`; + params.push(postType); + } + } else if (postType) { + query += `post_type = ?`; + params.push(postType); + } + + query += ` WHERE posts_uuid = ?`; + params.push(postId); + + db.query(query, params); +} + +async function deletePost(db: DB, postId: string): Promise { + // First delete all comments on the post + const deleteCommentsQuery = `DELETE FROM comments WHERE post_id = ?`; + db.query(deleteCommentsQuery, [postId]); + + // Then delete the post itself + const deletePostQuery = `DELETE FROM posts WHERE posts_uuid = ?`; + db.query(deletePostQuery, [postId]); +} + +async function likePost(db: DB, postId: string, userId: string): Promise { + // In a real application, you would check if the user has already liked the post + // and store like relationships in a separate table. This is a simplified version. + const query = `UPDATE posts SET likes = likes + 1 WHERE posts_uuid = ?`; + db.query(query, [postId]); +} + +/** + * @param posts_to_filter The Posts in an array to filter + * @param post_types The types of Posts to filter for + * @returns Array of Posts + */ +// Filter functions merged to one +function filterPosts(posts_to_filter: Post[], post_types: string[]): Post[] { + if (post_types.length === 0) { + return posts_to_filter; + } + + // Set is a type that we can specify the values to + // example: const set = new Set(); + // set.add(1); Will work + // set.add("2"); Will not work, it will error because + // it is a string. + const includedPostIds = new Set(); + const result: Post[] = []; + + for (const type of post_types) { + for (const post of posts_to_filter) { + if (!includedPostIds.has(post.posts_uuid) && post.post_type === type) { + result.push(post); + includedPostIds.add(post.posts_uuid); + } + } + } + + return result; +} + +export { + getPostsFromDB, + getPostById, + createPost, + updatePost, + deletePost, + likePost, + filterPosts, +}; \ No newline at end of file diff --git a/database/helpers/utils/user_utils.ts b/database/helpers/utils/user_utils.ts new file mode 100644 index 0000000..e1d6ed9 --- /dev/null +++ b/database/helpers/utils/user_utils.ts @@ -0,0 +1,95 @@ +/// + +/** + * @author Esad Mustafoski + * @description This file is responsible for creating Functions to easily access the Database, Specifically for Users + * @file userUtil.ts + */ + +// +++ IMPORTS ------------------------------------------------------ // +import { DB } from "https://deno.land/x/sqlite@v3.9.1/mod.ts"; +import { queryDatabase, mapAccountRow } from "./mod.ts"; +import { Accounts } from "../interfaces.ts"; + +/** + * @param user The username of the User to add + * @param password The hashed password of the User to add + * @param salt The salt used for the password + * @returns "noUser" if user exists, "newUser" if registration successful + */ +function registerUser( + db: DB, + user: string, + password: string, + salt: string, + userGroup: string, + displayname: string, + user_email: string, + firstname: string, + surname: string, + account_created: string, +): string { + const query_user_exists = + `SELECT * FROM accounts WHERE user_username = '${user}'`; + if (!query_user_exists) { + return "noUser"; + } + + const query_add_user = ` + INSERT INTO accounts ( + username, + password, + password_salt, + user_group, + displayname, + user_email, + firstname, + surname, + account_created, + bio, + blocked_users, + followers, + following, + contacts + ) VALUES ( + '${user}', + '${password}', + '${salt}', + '${userGroup}', + '${displayname}', + '${user_email}', + '${firstname}', + '${surname}', + '${account_created}', + '', + '[]', + '[]', + '[]', + '[]' + )`; + db.query(query_add_user); + console.log(`New user: ${user}`); + + return "newUser"; +} + +/** + * @returns Array of all Users in the Database + */ +async function getAllUsersFromDB(db: DB): Promise { + const query = `SELECT * FROM accounts`; + return await queryDatabase(db, query, [], mapAccountRow); +} + +/** + * @param username + * @returns Returns the Accounts for the User with the given username + */ +async function getUserByUsername(db: DB, username: string): Promise { + const query = `SELECT * FROM accounts WHERE username = '${username}'`; + const params: string[] = []; + const result = await queryDatabase(db, query, params, mapAccountRow); + return result[0]; +} + +export { registerUser, getAllUsersFromDB, getUserByUsername }; \ No newline at end of file diff --git a/database/utils.ts b/database/utils.ts index 5ec1b81..c8a1637 100644 --- a/database/utils.ts +++ b/database/utils.ts @@ -2,14 +2,53 @@ * @author Esad Mustafoski * @description This file is responsible for creating Functions to easily access the Database, Intended for use in the API */ - /// - // +++ IMPORTS ------------------------------------------------------ // -import { DB, Row } from "https://deno.land/x/sqlite/mod.ts"; -import { dirname, fromFileUrl, join } from "https://deno.land/std/path/mod.ts"; +import { DB } from "https://deno.land/x/sqlite@v3.9.1/mod.ts"; +import { + dirname, + fromFileUrl, + join, +} from "https://deno.land/std@0.224.0/path/mod.ts"; import * as db_create from "./create_db.ts"; +// Import all internal utilities with renamed imports to avoid naming conflicts +import { + // --- Account Functions --- + getAllUsersFromDB as getAllUsersFromDBInternal, + getUserByUsername as getUserByUsernameInternal, + // getAllUserInfoByID as getAllUserInfoByIDInternal, + registerUser as registerUserInternal, + + // --- Post Functions --- + getPostsFromDB as getPostsFromDBInternal, + getPostById as getPostByIdInternal, + createPost as createPostInternal, + updatePost as updatePostInternal, + deletePost as deletePostInternal, + likePost as likePostInternal, + filterPosts, + + // --- Comment Functions --- + getCommentsFromDB as getCommentsFromDBInternal, + // getCommentsForComments as getCommentsForCommentsInternal, + createComment as createCommentInternal, + updateComment as updateCommentInternal, + deleteComment as deleteCommentInternal, + likeComment as likeCommentInternal, + + // --- Chat Functions --- + getUserChats as getUserChatsInternal, + getChatById as getChatByIdInternal, + getChatMessages as getChatMessagesInternal, + createChat as createChatInternal, + addMessageToChat as addMessageToChatInternal, + deleteChat as deleteChatInternal, + + // --- Mapper Functions --- + queryDatabase as queryDatabaseInternal, +} from "./helpers/utils/mod.ts"; + // +++ VARIABLES ---------------------------------------------------- // // _dirname Is never getting used again, It's only needed because the DB Import // from SQLite doesn't like relative paths, so I use this as @@ -19,235 +58,37 @@ const dbPath: string = join(_dirname, "../database/esp-projekt.sqlite"); const db = new DB(dbPath); // +++ INTERFACES --------------------------------------------------- // -// Used in the Functions to define the return type/Avoid type errors -interface Post { - posts_uuid: number; - user_id: number; - created_at: string; - post_text: string; - likes: number; - comments: number; -} - -interface Accounts { - user_id: number; - user_group: string; - bio: string; - displayname: string; - username: string; - user_email: string; - password: string; - password_salt: string; - firstname: string; - surname: string; - account_created: string; - blocked_users: string; - followers: string; - following: string; - contacts: string; -} - -interface Comments { - comment_id: number; - post_id: number; - author_user_id: number; - date_created_at: string; - text: string; - likes: number; -} - -// +++ Helper ------------------------------------------------------ // -function mapPostRow(row: Row): Post { - const [posts_uuid, user_id, created_at, post_text, likes, comments] = row; - return { - posts_uuid: Number(posts_uuid), - user_id: Number(user_id), - created_at: String(created_at), - post_text: String(post_text), - likes: Number(likes), - comments: Number(comments), - }; -} - -function mapAccountRow(row: Row): Accounts { - const [ - user_id, - user_group, - bio, - displayname, - username, - user_email, - password, - password_salt, - firstname, - surname, - account_created, - blocked_users, - followers, - following, - contacts, - ] = row; - return { - user_id: Number(user_id), - user_group: String(user_group), - bio: String(bio), - displayname: String(displayname), - username: String(username), - user_email: String(user_email), - password: String(password), - password_salt: String(password_salt), - firstname: String(firstname), - surname: String(surname), - account_created: String(account_created), - blocked_users: String(blocked_users), - followers: String(followers), - following: String(following), - contacts: String(contacts), - }; -} - -function mapCommentRow(row: Row): Comments { - const [ - comment_id, - post_id, - author_user_id, - date_created_at, - text, - likes, - ] = row; - return { - comment_id: Number(comment_id), - post_id: Number(post_id), - author_user_id: Number(author_user_id), - date_created_at: String(date_created_at), - text: String(text), - likes: Number(likes), - }; -} - -// "T" is a generic type, it can be anything and makes the function "flexible"(?) -async function queryDatabase( +// Only re-export interfaces that are needed by external code +export type { + Post, + Accounts, + Comments, + Chat, + Message, +} from "./helpers/interfaces.ts"; +// +++ HELPER FUNCTIONS --------------------------------------------- // +export function queryDatabase( query: string, params: any[], - mapRow: (row: Row) => T, + mapRow: (row: any) => T, ): Promise { - const results: T[] = []; - try { - const rows = await db.query(query, params); - for (const row of rows) { - results.push(mapRow(row)); - } - } catch (error) { - console.error("Database query error:", error); - } - return results; + return queryDatabaseInternal(db, query, params, mapRow); } -// +++ FUNCTIONS --------------------------------------------------- // - -/** - * See: - * @file ./create_db.ts - */ -function insertSamples(): void { - db_create.insertSampleData(); -} - -function createDatabaseIfNotExist(): void { +// +++ DATABASE INITIALIZATION -------------------------------------- // +export function createDatabaseIfNotExist(): void { db_create.createDatabase(); } -/** - * @param user_uuid The UUID of the User to get the Posts for. Not required - * @description If no user_uuid is provided, all Posts will be returned - * @description If a user_uuid is provided, only the Posts from that User will be returned - * @returns Array of all Posts in the Database - */ -async function getPostsFromDB(user_uuid?: string): Promise { - const query = user_uuid - ? `SELECT * FROM posts WHERE user_id = ?` - : `SELECT * FROM posts`; - const params = user_uuid ? [user_uuid] : []; - return await queryDatabase(query, params, mapPostRow); +export function insertSamples(): void { + db_create.insertSampleData(); } -/** - * @returns Array of all Users in the Database - */ -async function getAllUsersFromDB(): Promise { - const query = `SELECT * FROM accounts`; - return await queryDatabase(query, [], mapAccountRow); -} - -/** - * @param username - * @returns Returns the Accounts for the User with the given username - */ -async function getUserByUsername(username: string): Promise { - const query = `SELECT * FROM accounts WHERE user_username = '${username}'`; - const params: string[] = []; - const result = await queryDatabase(query, params, mapAccountRow); - return result[0]; -} - -/** - * @param post_id The ID of the Post to get the Comments for. Not required - * @description If no post_id is provided, all Comments will be returned - * @description If a post_id is provided, only the Comments for that Post will be returned - * @returns Array of Comments for the Post, or an empty Array if there are no Comments - */ -async function getCommentsFromDB(post_id?: number): Promise { - const query = post_id - ? `SELECT * FROM comments WHERE post_id = ?` - : `SELECT * FROM comments`; - const params = post_id ? [post_id] : []; - return await queryDatabase(query, params, mapCommentRow); -} - -/** - * @param comment_id The ID of the Comment to get the Comments for - * @returns Array of Comments for the Comment, or an empty Array if there are no Comments - */ -function getCommentsForComments(comment_id: number) { - // Will be rewritten to use the queryDatabase function -} - -/** - * @param user_id The ID of the User to get - * @returns All of an users Data from the Database - * Included: Comments, Posts... Everything including the specific user_uuid in the database - * Might be required for Administrating the User - */ -async function getAllUserInfoByID(user_id: string) { - // Will be rewritten to use the queryDatabase function -} - -/** - * @param user_id The ID of the User to get the Posts for - * @returns Array of Posts from the User, or an empty Array if the User doesn't exist or has no Posts - */ -// Filter Functions, Not yet implemented -function filterForImagePosts(posts_to_filter: Array) { - return []; -} - -function filterForVideoPosts(posts_to_filter: Array) { - return []; -} - -function filterForTextPosts(posts_to_filter: Array) { - return []; -} - -// Register/Login/User - -/** - * @param user The username of the User to add - * @param password The hashed password of the User to add - * @param salt The salt used for the password - * @returns "noUser" if user exists, "newUser" if registration successful - */ -function registerUser( +// +++ ACCOUNT FUNCTIONS -------------------------------------------- // +export const getAllUsersFromDB = () => getAllUsersFromDBInternal(db); +export const getUserByUsername = (username: string) => getUserByUsernameInternal(db, username); +// export const getAllUserInfoByID = (user_id: string) => getAllUserInfoByIDInternal(db, user_id); +export const registerUser = ( user: string, password: string, salt: string, @@ -257,50 +98,63 @@ function registerUser( firstname: string, surname: string, account_created: string, -): string { - const query_user_exists = - `SELECT * FROM accounts WHERE user_username = '${user}'`; - if (!query_user_exists) { - return "noUser"; - } +) => registerUserInternal( + db, + user, + password, + salt, + userGroup, + displayname, + user_email, + firstname, + surname, + account_created, +); - const query_add_user = ` - INSERT INTO accounts ( - user_username, - password, - password_salt, - user_group, - user_displayname, - user_email, - firstname, - surname, - account_created - ) VALUES ( - '${user}', - '${password}', - '${salt}', - '${userGroup}', - '${displayname}', - '${user_email}', - '${firstname}', - '${surname}', - '${account_created}' - )`; - db.query(query_add_user); - console.log(`New user: ${user}`); +// +++ POST FUNCTIONS ----------------------------------------------- // +export const getPostsFromDB = (user_uuid?: string) => getPostsFromDBInternal(db, user_uuid); +export const getPostById = (postId: string) => getPostByIdInternal(db, postId); +export const createPost = ( + userId: string, + createdAt: string, + postText: string, + postType: string, +) => createPostInternal(db, userId, createdAt, postText, postType); +export const updatePost = ( + postId: string, + postText?: string, + postType?: string, +) => updatePostInternal(db, postId, postText, postType); +export const deletePost = (postId: string) => deletePostInternal(db, postId); +export const likePost = (postId: string, userId: string) => likePostInternal(db, postId, userId); - return "newUser"; -} +// +++ COMMENT FUNCTIONS -------------------------------------------- // +export const getCommentsFromDB = (post_id?: number) => getCommentsFromDBInternal(db, post_id); +// export const getCommentsForComments = (comment_id: number) => getCommentsForCommentsInternal(db, comment_id); +export const createComment = ( + postId: string, + userId: string, + createdAt: string, + text: string, +) => createCommentInternal(db, postId, userId, createdAt, text); +export const updateComment = (commentId: string, text: string) => updateCommentInternal(db, commentId, text); +export const deleteComment = (commentId: string) => deleteCommentInternal(db, commentId); +export const likeComment = (commentId: string, userId: string) => likeCommentInternal(db, commentId, userId); -// Exporting all functions as this is a module -export { - createDatabaseIfNotExist, - getAllUserInfoByID, - getAllUsersFromDB, - getCommentsForComments, - getCommentsFromDB, - getPostsFromDB, - getUserByUsername, - insertSamples, - registerUser, -}; +// +++ CHAT FUNCTIONS ----------------------------------------------- // +export const getUserChats = (userId: string) => getUserChatsInternal(db, userId); +export const getChatById = (chatId: string) => getChatByIdInternal(db, chatId); +export const getChatMessages = (chatId: string) => getChatMessagesInternal(db, chatId); +export const createChat = ( + participants: string[], + chatName: string, +) => createChatInternal(db, participants, chatName); +export const addMessageToChat = ( + chatId: string, + senderId: string, + content: string, +) => addMessageToChatInternal(db, chatId, senderId, content); +export const deleteChat = (chatId: string) => deleteChatInternal(db, chatId); + +// +++ UTILITY FUNCTIONS -------------------------------------------- // +export { filterPosts }; \ No newline at end of file diff --git a/tests/db.test.ts b/tests/db.test.ts index bf5b1b8..b042ed3 100644 --- a/tests/db.test.ts +++ b/tests/db.test.ts @@ -13,7 +13,7 @@ import { assertMatch, } from "https://deno.land/std/assert/mod.ts"; -import * as db_utils from "../database/utils.ts"; +import * as db_utils from "../database/db_utils.ts"; // +++ TESTS ------------------------------------------------------- // // Database Tests