Merged API and Databank into Main branch
This commit is contained in:
499
api/doc/API.md
Normal file
499
api/doc/API.md
Normal file
@@ -0,0 +1,499 @@
|
|||||||
|
# This was generated using AI!!! Will edit if errors are found.
|
||||||
|
|
||||||
|
|
||||||
|
# API Documentation
|
||||||
|
|
||||||
|
## Index
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/` | Returns basic information and directions for using the API endpoints. |
|
||||||
|
| GET | `/api` | Redirects to the full API documentation. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Account Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|-------------------------------------------|----------------------------------------------------------------|
|
||||||
|
| POST | `/api/account/login` | Logs in a user with username and password. |
|
||||||
|
| POST | `/api/account/register` | Registers a new user with required details. |
|
||||||
|
| POST | `/api/account/logout` | Logs a user out. (TODO) |
|
||||||
|
| POST | `/api/account/password/forgot` | Initiates password recovery. (TODO) |
|
||||||
|
| POST | `/api/account/password/reset` | Resets the password. (TODO) |
|
||||||
|
| POST | `/api/account/password/change` | Changes the password. (TODO) |
|
||||||
|
| POST | `/api/account/email/change-email` | Changes the user's email address. (TODO) |
|
||||||
|
| POST | `/api/account/email/verify-email` | Verifies a user's email. (TODO) |
|
||||||
|
| POST | `/api/account/delete-account` | Deletes the user account. (TODO) |
|
||||||
|
| POST | `/api/account/block` | Blocks a user account. (TODO) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auth Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------------------|--------------------------------------------------|
|
||||||
|
| GET | `/api/auth` | Auth endpoint (placeholder). |
|
||||||
|
| GET | `/api/auth/verify` | Verifies an authentication token. (TODO) |
|
||||||
|
| GET | `/api/auth/refresh` | Refreshes an authentication token. (TODO) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|---------------|------------------------------------------------------------|
|
||||||
|
| GET | `/api/users` | Retrieves a list of all users from the database. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chat Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------------------------------|-----------------------------------------------------------------|
|
||||||
|
| GET | `/api/chats` | Retrieves chats for a user (requires an Authorization header). |
|
||||||
|
| GET | `/api/chat/:id` | Retrieves details of a specific chat using its ID. |
|
||||||
|
| POST | `/api/chat/create` | Creates a new chat with at least 2 participants. |
|
||||||
|
| POST | `/api/chat/:id/message` | Sends a message to the specified chat. |
|
||||||
|
| DELETE | `/api/chat/:id` | Deletes a specific chat by its ID. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Post Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------------------------------|-------------------------------------------------------------------|
|
||||||
|
| GET | `/api/posts` | Retrieves all posts. |
|
||||||
|
| GET | `/api/post/:id` | Retrieves a specific post by its ID. |
|
||||||
|
| POST | `/api/post/create` | Creates a new post. |
|
||||||
|
| PUT | `/api/post/:id` | Updates an existing post (requires at least one update field). |
|
||||||
|
| DELETE | `/api/post/:id` | Deletes a specific post by its ID. |
|
||||||
|
| POST | `/api/post/:id/like` | Likes a specific post. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comment Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|-----------------------------------|--------------------------------------------------------------------|
|
||||||
|
| GET | `/api/post/:id/comments` | Retrieves all comments associated with a specific post. |
|
||||||
|
| POST | `/api/post/:id/comment` | Adds a new comment to a specified post. |
|
||||||
|
| PUT | `/api/comment/:id` | Updates an existing comment using its ID. |
|
||||||
|
| DELETE | `/api/comment/:id` | Deletes a specific comment by its ID. |
|
||||||
|
| POST | `/api/comment/:id/like` | Likes a specific comment. |
|
||||||
|
|
||||||
|
> Note: Replace any `:id` placeholder in the URLs with the actual identifier when making requests.
|
||||||
|
> Note: Endpoints marked as TODO are placeholders for future implementation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Base URL
|
||||||
|
|
||||||
|
Assuming the server is running on port 8000, the base URL is:
|
||||||
|
```
|
||||||
|
http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Index & Documentation Routes
|
||||||
|
|
||||||
|
- **GET /**
|
||||||
|
A simple informational endpoint.
|
||||||
|
- **Response:** `"For endpoints, use /api/{name}"`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/
|
||||||
|
```
|
||||||
|
|
||||||
|
- **GET /api**
|
||||||
|
Directs users for API documentation.
|
||||||
|
- **Response:** `"For API Documentation, visit /docs"`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/api
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Account Endpoints
|
||||||
|
|
||||||
|
### 1. POST /api/account/login
|
||||||
|
|
||||||
|
Log in a user with their username and password.
|
||||||
|
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "johndoe",
|
||||||
|
"password": "secret123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** On success returns status 200 with body `"Success"`. If unsuccessful, appropriate error messages are returned.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/account/login \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"username": "johndoe", "password": "secret123"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. POST /api/account/register
|
||||||
|
|
||||||
|
Register a new user with all required fields.
|
||||||
|
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "johndoe",
|
||||||
|
"password": "secret123",
|
||||||
|
"userGroup": "default",
|
||||||
|
"displayname": "John Doe",
|
||||||
|
"user_email": "john@example.com",
|
||||||
|
"firstname": "John",
|
||||||
|
"surname": "Doe"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** On success returns a status of 200 and a message indicating the user ID registration.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/account/register \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"username": "johndoe",
|
||||||
|
"password": "secret123",
|
||||||
|
"userGroup": "default",
|
||||||
|
"displayname": "John Doe",
|
||||||
|
"user_email": "john@example.com",
|
||||||
|
"firstname": "John",
|
||||||
|
"surname": "Doe"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other Account Endpoints
|
||||||
|
|
||||||
|
- POST `/api/account/logout`
|
||||||
|
- POST `/api/account/password/forgot`
|
||||||
|
- POST `/api/account/password/reset`
|
||||||
|
- POST `/api/account/password/change`
|
||||||
|
- POST `/api/account/email/change-email`
|
||||||
|
- POST `/api/account/email/verify-email`
|
||||||
|
- POST `/api/account/delete-account`
|
||||||
|
- POST `/api/account/block`
|
||||||
|
|
||||||
|
These endpoints are marked as TODO and currently have no implemented logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auth Endpoints
|
||||||
|
|
||||||
|
- GET `/api/auth`
|
||||||
|
- GET `/api/auth/verify`
|
||||||
|
- GET `/api/auth/refresh`
|
||||||
|
|
||||||
|
These endpoints are also marked as TODO.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Endpoints
|
||||||
|
|
||||||
|
### GET /api/users
|
||||||
|
|
||||||
|
Retrieve all users from the database.
|
||||||
|
|
||||||
|
- **Response:** JSON array of user objects.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/api/users
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chat Endpoints
|
||||||
|
|
||||||
|
### 1. GET /api/chats
|
||||||
|
|
||||||
|
Retrieve all chats for a user. Requires an "Authorization" header and an optional query parameter for userId.
|
||||||
|
|
||||||
|
- **Headers:**
|
||||||
|
- Authorization: Bearer <token> (only required to simulate authentication)
|
||||||
|
- **Query Parameter:**
|
||||||
|
- userId (optional; defaults to "1" if not provided)
|
||||||
|
- **Response:** JSON array of chats for the specified user.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl "http://localhost:8000/api/chats?userId=2" \
|
||||||
|
-H "Authorization: Bearer mytoken123"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. GET /api/chat/:id
|
||||||
|
|
||||||
|
Retrieve details of a specific chat identified by its ID.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Chat ID
|
||||||
|
- **Response:** JSON object containing chat details.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/api/chat/10
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. POST /api/chat/create
|
||||||
|
|
||||||
|
Create a new chat with at least two participants.
|
||||||
|
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"participants": [1, 2],
|
||||||
|
"chatName": "Group Chat"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** Returns status 201 and a JSON object with the newly created "chatId".
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/chat/create \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"participants": [1, 2], "chatName": "Group Chat"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. POST /api/chat/:id/message
|
||||||
|
|
||||||
|
Send a message to a specific chat.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Chat ID
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"senderId": 1,
|
||||||
|
"content": "Hello, world!"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** Returns status 201 and a JSON object with the "messageId" of the new message.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/chat/10/message \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"senderId": 1, "content": "Hello, world!"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. DELETE /api/chat/:id
|
||||||
|
|
||||||
|
Delete a specific chat.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Chat ID
|
||||||
|
- **Response:** Returns status 200 with a confirmation message.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X DELETE http://localhost:8000/api/chat/10
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Post Endpoints
|
||||||
|
|
||||||
|
### 1. GET /api/posts
|
||||||
|
|
||||||
|
Retrieve all posts.
|
||||||
|
|
||||||
|
- **Response:** JSON array of posts.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/api/posts
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. GET /api/post/:id
|
||||||
|
|
||||||
|
Retrieve a specific post by its ID.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Post ID
|
||||||
|
- **Response:** JSON object representing the post.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/api/post/15
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. POST /api/post/create
|
||||||
|
|
||||||
|
Create a new post.
|
||||||
|
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"userId": 1,
|
||||||
|
"postText": "This is a new post",
|
||||||
|
"postType": "text"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** Returns status 201 with the "postId" of the created post.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/post/create \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"userId": 1, "postText": "This is a new post", "postType": "text"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. PUT /api/post/:id
|
||||||
|
|
||||||
|
Update an existing post. At least one of `postText` or `postType` must be provided.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Post ID
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"postText": "Updated post text",
|
||||||
|
"postType": "text"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** Returns status 200 with a confirmation message.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X PUT http://localhost:8000/api/post/15 \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"postText": "Updated post text", "postType": "text"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. DELETE /api/post/:id
|
||||||
|
|
||||||
|
Delete a specific post.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Post ID
|
||||||
|
- **Response:** Returns status 200 with a message confirming deletion.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X DELETE http://localhost:8000/api/post/15
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. POST /api/post/:id/like
|
||||||
|
|
||||||
|
Like a specific post.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Post ID
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"userId": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** Returns status 200 with a confirmation message.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/post/15/like \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"userId": 1}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comment Endpoints
|
||||||
|
|
||||||
|
### 1. GET /api/post/:id/comments
|
||||||
|
|
||||||
|
Retrieve all comments for a specific post.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Post ID
|
||||||
|
- **Response:** JSON array of comments.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/api/post/15/comments
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. POST /api/post/:id/comment
|
||||||
|
|
||||||
|
Create a new comment on a specific post.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Post ID
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"userId": 1,
|
||||||
|
"text": "This is a comment"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** Returns status 201 with the newly created "commentId".
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/post/15/comment \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"userId": 1, "text": "This is a comment"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. PUT /api/comment/:id
|
||||||
|
|
||||||
|
Update an existing comment.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Comment ID
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"text": "Updated comment text"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** Returns status 200 with a confirmation message.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X PUT http://localhost:8000/api/comment/20 \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"text": "Updated comment text"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. DELETE /api/comment/:id
|
||||||
|
|
||||||
|
Delete a specific comment.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Comment ID
|
||||||
|
- **Response:** Returns status 200 with a confirmation message.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X DELETE http://localhost:8000/api/comment/20
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. POST /api/comment/:id/like
|
||||||
|
|
||||||
|
Like a specific comment.
|
||||||
|
|
||||||
|
- **Route Parameter:**
|
||||||
|
- id: Comment ID
|
||||||
|
- **Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"userId": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Response:** Returns status 200 with a confirmation message.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/comment/20/like \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"userId": 1}'
|
||||||
|
```
|
||||||
73
api/helpers.ts
Normal file
73
api/helpers.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @author Esad Mustafoski
|
||||||
|
* @file api/helpers.ts
|
||||||
|
* @description Helper functions for the API
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +++ IMPORTS ------------------------------------------------------ //
|
||||||
|
import { Context } from "https://deno.land/x/oak/mod.ts";
|
||||||
|
import { encodeHex } from "jsr:@std/encoding/hex";
|
||||||
|
// import { hash } from "node:crypto";
|
||||||
|
|
||||||
|
export type ApiResponse = {
|
||||||
|
status: number;
|
||||||
|
body: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// +++ FUNCTIONS ---------------------------------------------------- //
|
||||||
|
/**
|
||||||
|
* @description Sends a response to the client
|
||||||
|
* @usage sendResponse(ctx, { status: 200, body: { message: "Success" } })
|
||||||
|
* Status is the HTTP Status code
|
||||||
|
* Body is the response body/message/data.
|
||||||
|
*/
|
||||||
|
const sendResponse = (ctx: Context, { status, body }: ApiResponse): void => {
|
||||||
|
ctx.response.status = status;
|
||||||
|
ctx.response.body = body as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @usage errorResponse(ctx, 401, "Unauthorized")
|
||||||
|
* @see sendResponse
|
||||||
|
*/
|
||||||
|
const errorResponse = (ctx: Context, status: number, message: string): void => {
|
||||||
|
sendResponse(ctx, { status, body: { error: message } });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description password "Salter", used to salt the passwords before the hash, this salt will be
|
||||||
|
* returned seperately to save the salt in the DB
|
||||||
|
* @param password The password to salt
|
||||||
|
* @returns {saltedPassword: string, salt: string} Password with the salt + Salt seperately, both strings
|
||||||
|
*/
|
||||||
|
const saltPassword = async (
|
||||||
|
password: string,
|
||||||
|
): Promise<{ saltedPassword: string; salt: string }> => {
|
||||||
|
const saltBytes = new Uint8Array(16); // 16 bytes = 128 bits for randomness
|
||||||
|
await crypto.getRandomValues(saltBytes);
|
||||||
|
const salt = encodeHex(saltBytes);
|
||||||
|
|
||||||
|
const saltedPassword = `${password}${salt}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
saltedPassword,
|
||||||
|
salt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Hashing Function for Passwords/etc
|
||||||
|
* @param password The password to hash
|
||||||
|
* @returns {hash: string} The hashed password as a string
|
||||||
|
*/
|
||||||
|
const hashPassword = async (password: string): Promise<string> => {
|
||||||
|
const to_hash = password;
|
||||||
|
const buffer = new TextEncoder().encode(to_hash);
|
||||||
|
const hash_buffer = await crypto.subtle.digest("SHA-256", buffer);
|
||||||
|
const hash = await encodeHex(hash_buffer);
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { errorResponse, hashPassword, saltPassword, sendResponse };
|
||||||
142
api/helpers/chat_api.ts
Normal file
142
api/helpers/chat_api.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @author Esad Mustafoski
|
||||||
|
* @description API file for Comments
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +++ IMPORTS ------------------------------------------------------ //
|
||||||
|
import * as db_utils from "../../database/utils.ts";
|
||||||
|
import * as helper_utils from "../helpers.ts";
|
||||||
|
|
||||||
|
// +++ FUNCTIONS ----------------------------------------------------- //
|
||||||
|
async function api_getChats(ctx: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const authHeader = ctx.request.headers.get("Authorization");
|
||||||
|
if (!authHeader) {
|
||||||
|
helper_utils.errorResponse(ctx, 401, "Authentication Required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get userId from query parameter (for testing) or use 1 as default (avoid errors because of userid)
|
||||||
|
// Assumes 1 is a test account
|
||||||
|
const userId = ctx.request.url.searchParams.get("userId") || "1";
|
||||||
|
|
||||||
|
const chats = await db_utils.getUserChats(userId);
|
||||||
|
ctx.response.body = chats;
|
||||||
|
} catch (error) {
|
||||||
|
helper_utils.errorResponse(ctx, 500, "Error retrieving chats");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_getChatById(ctx: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const chatId = ctx.params.id;
|
||||||
|
if (!chatId) {
|
||||||
|
helper_utils.errorResponse(ctx, 400, "Chat ID required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chat = await db_utils.getChatById(chatId);
|
||||||
|
if (!chat) {
|
||||||
|
helper_utils.errorResponse(ctx, 404, "Chat not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.response.body = chat;
|
||||||
|
} catch (error) {
|
||||||
|
helper_utils.errorResponse(ctx, 500, "Error retrieving chat");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_createChat(ctx: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const body = ctx.request.body;
|
||||||
|
const result = await body.json();
|
||||||
|
const { participants, chatName } = result;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!participants || !Array.isArray(participants) || participants.length < 2
|
||||||
|
) {
|
||||||
|
helper_utils.errorResponse(
|
||||||
|
ctx,
|
||||||
|
400,
|
||||||
|
"Two people required to create a chat",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatId = await db_utils.createChat(participants, chatName || "");
|
||||||
|
helper_utils.sendResponse(ctx, {
|
||||||
|
status: 201,
|
||||||
|
body: { chatId },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
helper_utils.errorResponse(ctx, 500, "Error creating chat");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_sendMessage(ctx: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const chatId = ctx.params.id;
|
||||||
|
if (!chatId) {
|
||||||
|
helper_utils.errorResponse(ctx, 400, "Chat ID required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = ctx.request.body;
|
||||||
|
const result = await body.json();
|
||||||
|
const { senderId, content } = result;
|
||||||
|
|
||||||
|
if (!senderId || !content) {
|
||||||
|
helper_utils.errorResponse(
|
||||||
|
ctx,
|
||||||
|
400,
|
||||||
|
"Sender ID and message content required",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageId = await db_utils.addMessageToChat(
|
||||||
|
chatId,
|
||||||
|
senderId,
|
||||||
|
content,
|
||||||
|
);
|
||||||
|
helper_utils.sendResponse(ctx, {
|
||||||
|
status: 201,
|
||||||
|
body: { messageId },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
helper_utils.errorResponse(ctx, 500, "Error sending message");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_deleteChat(ctx: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const chatId = ctx.params.id;
|
||||||
|
if (!chatId) {
|
||||||
|
helper_utils.errorResponse(ctx, 400, "Chat ID required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await db_utils.deleteChat(chatId);
|
||||||
|
helper_utils.sendResponse(ctx, {
|
||||||
|
status: 200,
|
||||||
|
body: { message: "Chat deleted successfully" },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
helper_utils.errorResponse(ctx, 500, "Error deleting chat");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
api_createChat,
|
||||||
|
api_deleteChat,
|
||||||
|
api_getChatById,
|
||||||
|
api_getChats,
|
||||||
|
api_sendMessage,
|
||||||
|
};
|
||||||
142
api/helpers/comments_api.ts
Normal file
142
api/helpers/comments_api.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @author Esad Mustafoski
|
||||||
|
* @description API file for Comments
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +++ IMPORTS ------------------------------------------------------ //
|
||||||
|
import * as db_utils from "../../database/utils.ts";
|
||||||
|
import * as helper_utils from "../helpers.ts";
|
||||||
|
|
||||||
|
// +++ FUNCTIONS ----------------------------------------------------- //
|
||||||
|
async function api_getPostComments(ctx: any): Promise<void> {
|
||||||
|
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");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_createComment(ctx: any): Promise<void> {
|
||||||
|
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");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_updateComment(ctx: any): Promise<void> {
|
||||||
|
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");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_deleteComment(ctx: any): Promise<void> {
|
||||||
|
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");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_likeComment(ctx: any): Promise<void> {
|
||||||
|
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");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
api_createComment,
|
||||||
|
api_deleteComment,
|
||||||
|
api_getPostComments,
|
||||||
|
api_likeComment,
|
||||||
|
api_updateComment,
|
||||||
|
};
|
||||||
11
api/helpers/mod.ts
Normal file
11
api/helpers/mod.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @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";
|
||||||
147
api/helpers/post_api.ts
Normal file
147
api/helpers/post_api.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @author Esad Mustafoski
|
||||||
|
* @description API file for Posts
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +++ IMPORTS ------------------------------------------------------ //
|
||||||
|
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";
|
||||||
|
|
||||||
|
// +++ FUNCTIONS ----------------------------------------------------- //
|
||||||
|
async function api_getPostById(ctx: any): Promise<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
api_createPost,
|
||||||
|
api_deletePost,
|
||||||
|
api_getPostById,
|
||||||
|
api_likePost,
|
||||||
|
api_updatePost,
|
||||||
|
};
|
||||||
45
api/helpers/user_api.ts
Normal file
45
api/helpers/user_api.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @author Esad Mustafoski
|
||||||
|
* @description API file for Users
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +++ IMPORTS ------------------------------------------------------ //
|
||||||
|
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";
|
||||||
|
|
||||||
|
// +++ FUNCTIONS ----------------------------------------------------- //
|
||||||
|
|
||||||
|
/*async function api_user_getInfo(ctx: any): Promise<void> {
|
||||||
|
const id = ctx.params.id;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
ctx.response.status = 400;
|
||||||
|
ctx.response.body = { error: "User ID required" };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await db_utils.getAllUserInfoByID(id);
|
||||||
|
if (user === null || user === undefined) {
|
||||||
|
helper_utils.errorResponse(ctx, 404, "User not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.response.body = user;
|
||||||
|
} catch (error) {
|
||||||
|
helper_utils.errorResponse(ctx, 500, error as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function api_getAllUsers(ctx: Context): Promise<void> {
|
||||||
|
const getUsers = await db_utils.getAllUsersFromDB();
|
||||||
|
ctx.response.body = getUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
api_getAllUsers,
|
||||||
|
// api_user_getInfo
|
||||||
|
};
|
||||||
307
api/main.ts
Normal file
307
api/main.ts
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @author Esad Mustafoski
|
||||||
|
* @description Main API file, Handles all the routing/api stuff
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +++ IMPORTS ------------------------------------------------------ //
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
Context,
|
||||||
|
Next,
|
||||||
|
Router,
|
||||||
|
} 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 {
|
||||||
|
dirname,
|
||||||
|
fromFileUrl,
|
||||||
|
join,
|
||||||
|
} from "https://deno.land/std@0.224.0/path/mod.ts";
|
||||||
|
|
||||||
|
import * as db_utils from "../database/utils.ts";
|
||||||
|
import * as helper_utils from "./helpers.ts";
|
||||||
|
|
||||||
|
import {
|
||||||
|
// --- Chat --- //
|
||||||
|
api_createChat,
|
||||||
|
api_createComment,
|
||||||
|
api_createPost,
|
||||||
|
api_deleteChat,
|
||||||
|
api_deleteComment,
|
||||||
|
api_deletePost,
|
||||||
|
// --- User --- //
|
||||||
|
api_getAllUsers,
|
||||||
|
api_getChatById,
|
||||||
|
api_getChats,
|
||||||
|
// --- Post --- //
|
||||||
|
api_getPostById,
|
||||||
|
// --- Comment --- //
|
||||||
|
api_getPostComments,
|
||||||
|
api_likeComment,
|
||||||
|
api_likePost,
|
||||||
|
api_sendMessage,
|
||||||
|
api_updateComment,
|
||||||
|
api_updatePost,
|
||||||
|
} from "./helpers/mod.ts";
|
||||||
|
|
||||||
|
// +++ VARIABLES / TYPES --------------------------------------------- //
|
||||||
|
const router = new Router();
|
||||||
|
const app = new Application();
|
||||||
|
|
||||||
|
// unused for now
|
||||||
|
type ApiResponse = {
|
||||||
|
status: number;
|
||||||
|
body: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
// database creation if missing, runs here because this is the main file executed by the API.
|
||||||
|
db_utils.ensureDatabaseExists();
|
||||||
|
|
||||||
|
// +++ ROUTER ------------------------------------------------------- //
|
||||||
|
// Creates the routes for the API server.
|
||||||
|
// Example: localhost:8000/api will show "testAPIPoint"
|
||||||
|
// in the HTML page.
|
||||||
|
|
||||||
|
// Docs Routes
|
||||||
|
router
|
||||||
|
.get("/", (ctx: any) => {
|
||||||
|
ctx.response.body = "For endpoints, use /api/{name}";
|
||||||
|
})
|
||||||
|
.get("/api", (ctx: any) => {
|
||||||
|
ctx.response.body = "For API Documentation, visit /docs";
|
||||||
|
});
|
||||||
|
|
||||||
|
// -- Account routes --
|
||||||
|
router
|
||||||
|
.post("/api/account/login", api_login)
|
||||||
|
.post("/api/account/register", api_register)
|
||||||
|
.post("/api/account/logout", () => {}) // TODO
|
||||||
|
.post("/api/account/password/forgot", () => {}) // TODO
|
||||||
|
.post("/api/account/password/reset", () => {}) // TODO
|
||||||
|
.post("/api/account/password/change", () => {}) // TODO
|
||||||
|
.post("/api/account/email/change-email", () => {}) // TODO
|
||||||
|
.post("/api/account/email/verify-email", () => {}) // TODO
|
||||||
|
.post("/api/account/delete-account", () => {}) // TODO
|
||||||
|
.post("/api/account/block", () => {}); // TODO
|
||||||
|
|
||||||
|
// -- Auth Routes -- //
|
||||||
|
router
|
||||||
|
.get("/api/auth", () => {}) // TODO
|
||||||
|
.get("/api/auth/verify", () => {}) // TODO
|
||||||
|
.get("/api/auth/refresh", () => {}); // TODO
|
||||||
|
|
||||||
|
// -- User routes -- //
|
||||||
|
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/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 ----------------------------------------------------- //
|
||||||
|
|
||||||
|
// ABANDONED FUNCTIONS //
|
||||||
|
async function _authenticator(ctx: Context, next: Next): Promise<void> {
|
||||||
|
const authHeader = ctx.request.headers.get("Authorization");
|
||||||
|
|
||||||
|
if (!authHeader) {
|
||||||
|
ctx.response.status = 401;
|
||||||
|
ctx.response.body = { error: "No header" };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bearer check
|
||||||
|
// Bearer is often used for authentication in API's and is a standard, I check it using RegEx (Regular Expressions)
|
||||||
|
const match = authHeader.match(/^Bearer (.+)$/);
|
||||||
|
if (!match) {
|
||||||
|
ctx.response.status = 401;
|
||||||
|
ctx.response.body = { error: "Invalid format" };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = match[1];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Token logic missing, unattempted.
|
||||||
|
await next();
|
||||||
|
} catch (error) {
|
||||||
|
ctx.response.status = 401;
|
||||||
|
ctx.response.body = { error: "Invalid token" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _tokenChecker(ctx: Context, next: Next): Promise<void> {
|
||||||
|
// logic below (TODO):
|
||||||
|
/**
|
||||||
|
* 1. check if the token is valid
|
||||||
|
* 2. if valid, set the user in the context
|
||||||
|
* 3. if not valid, return 401
|
||||||
|
* 4. if: token missing, expired, blacklisted, !DB, !user or !correct user, return 401 with associated error
|
||||||
|
* eg: wrong user: 401 -> "Token not associated with this user"
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// API: Posts //
|
||||||
|
async function api_posts_getAll(ctx: Context): Promise<void> {
|
||||||
|
const posts = await db_utils.getPostsFromDB();
|
||||||
|
ctx.response.body = posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API: login/register //
|
||||||
|
async function api_register(ctx: Context): Promise<void> {
|
||||||
|
try {
|
||||||
|
const body = ctx.request.body;
|
||||||
|
const result = await body.json();
|
||||||
|
const {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
userGroup,
|
||||||
|
displayname,
|
||||||
|
user_email,
|
||||||
|
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("-")
|
||||||
|
}`;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!username ||
|
||||||
|
!password ||
|
||||||
|
!userGroup ||
|
||||||
|
!displayname ||
|
||||||
|
!user_email ||
|
||||||
|
!firstname ||
|
||||||
|
!surname
|
||||||
|
) {
|
||||||
|
helper_utils.errorResponse(ctx, 400, "Missing required fields");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First salt the password
|
||||||
|
const { saltedPassword, salt } = await helper_utils.saltPassword(password);
|
||||||
|
// Then hash the salted password
|
||||||
|
const hash = await helper_utils.hashPassword(saltedPassword);
|
||||||
|
|
||||||
|
const userId = db_utils.registerUser(
|
||||||
|
username,
|
||||||
|
hash,
|
||||||
|
salt,
|
||||||
|
userGroup,
|
||||||
|
displayname,
|
||||||
|
user_email,
|
||||||
|
firstname,
|
||||||
|
surname,
|
||||||
|
account_created,
|
||||||
|
);
|
||||||
|
|
||||||
|
const user = await db_utils.getUserByUsername(username);
|
||||||
|
|
||||||
|
const responseBody: any = {
|
||||||
|
success: true,
|
||||||
|
message: "Register successful",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (user.user_id !== undefined) {
|
||||||
|
responseBody.userId = user.user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
helper_utils.sendResponse(ctx, {
|
||||||
|
status: 200,
|
||||||
|
body: responseBody,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
helper_utils.errorResponse(ctx, 500, "Invalid request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function api_login(ctx: Context): Promise<string> {
|
||||||
|
try {
|
||||||
|
const body = ctx.request.body;
|
||||||
|
const result = await body.json();
|
||||||
|
const { username, password } = result;
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
helper_utils.errorResponse(ctx, 400, "Missing required fields");
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await db_utils.getUserByUsername(username);
|
||||||
|
if (!user) {
|
||||||
|
helper_utils.errorResponse(ctx, 404, "User not found");
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
|
||||||
|
const storedSalt = user.password_salt;
|
||||||
|
const saltedPassword = `${password}${storedSalt}`;
|
||||||
|
const hash = await helper_utils.hashPassword(saltedPassword);
|
||||||
|
|
||||||
|
// Compare the hashed password with the stored hash
|
||||||
|
if (user.password !== hash) {
|
||||||
|
helper_utils.errorResponse(ctx, 401, "Invalid password");
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return success with the user ID if it exists
|
||||||
|
const responseBody: any = {
|
||||||
|
success: true,
|
||||||
|
message: "Login successful",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only add userId if it exists
|
||||||
|
if (user.user_id !== undefined) {
|
||||||
|
responseBody.userId = user.user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
helper_utils.sendResponse(ctx, {
|
||||||
|
status: 200,
|
||||||
|
body: responseBody,
|
||||||
|
});
|
||||||
|
|
||||||
|
return "Success";
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
helper_utils.errorResponse(ctx, 500, "Invalid request");
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// +++ APP ---------------------------------------------------------- //
|
||||||
|
app.use(
|
||||||
|
oakCors({
|
||||||
|
origin: "*",
|
||||||
|
credentials: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
app.use(router.routes());
|
||||||
|
app.use(router.allowedMethods());
|
||||||
|
|
||||||
|
export { app };
|
||||||
|
await app.listen({ port: 8000 });
|
||||||
107
database/create_db.ts
Normal file
107
database/create_db.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Esad Mustafoski
|
||||||
|
* @description This file is responsible for creating the database and the tables
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +++ IMPORTS ------------------------------------------------------ //
|
||||||
|
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";
|
||||||
|
|
||||||
|
// +++ VARIABLES ---------------------------------------------------- //
|
||||||
|
const _dirname: string = dirname(fromFileUrl(import.meta.url));
|
||||||
|
const dbPath: string = join(_dirname, "../database/esp-projekt.sqlite");
|
||||||
|
const db = new DB(dbPath);
|
||||||
|
|
||||||
|
export function createDatabase(): void {
|
||||||
|
db.execute(`
|
||||||
|
CREATE TABLE IF NOT EXISTS accounts (
|
||||||
|
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_group TEXT,
|
||||||
|
bio TEXT,
|
||||||
|
displayname TEXT,
|
||||||
|
username TEXT,
|
||||||
|
user_email TEXT,
|
||||||
|
password TEXT,
|
||||||
|
password_salt TEXT,
|
||||||
|
firstname TEXT,
|
||||||
|
surname TEXT,
|
||||||
|
account_created TEXT,
|
||||||
|
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 (
|
||||||
|
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, 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, 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)
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
|
db.query(
|
||||||
|
`INSERT INTO comments (post_id, author_user_id, date_created_at, text, likes) VALUES
|
||||||
|
(1, 2, '2024-01-15 10:05:00', 'Great post!', 3),
|
||||||
|
(1, 3, '2024-01-15 10:10:00', 'Very informative', 2),
|
||||||
|
(2, 3, '2024-01-15 11:35:00', 'Nice feature', 4),
|
||||||
|
(3, 1, '2024-01-16 09:20:00', 'TypeScript is awesome', 5),
|
||||||
|
(4, 2, '2024-01-16 14:25:00', 'Beautiful photos!', 6)
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
}
|
||||||
0
database/doc/DATABASE_UTILS.md
Normal file
0
database/doc/DATABASE_UTILS.md
Normal file
61
database/helpers/interfaces.ts
Normal file
61
database/helpers/interfaces.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 };
|
||||||
122
database/helpers/maphelper.ts
Normal file
122
database/helpers/maphelper.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
};
|
||||||
86
database/helpers/utils/chat_utils.ts
Normal file
86
database/helpers/utils/chat_utils.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<Chat[]> {
|
||||||
|
const query = `SELECT * FROM chats WHERE participants LIKE '%${userId}%'`;
|
||||||
|
return await queryDatabase<Chat>(db, query, [], mapChatRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getChatById(db: DB, chatId: string): Promise<Chat | null> {
|
||||||
|
const query = `SELECT * FROM chats WHERE chat_id = ?`;
|
||||||
|
const chats = await queryDatabase<Chat>(db, query, [chatId], mapChatRow);
|
||||||
|
return chats.length > 0 ? chats[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getChatMessages(db: DB, chatId: string): Promise<Message[]> {
|
||||||
|
const query =
|
||||||
|
`SELECT * FROM messages WHERE chat_id = ? ORDER BY timestamp ASC`;
|
||||||
|
return await queryDatabase<Message>(db, query, [chatId], mapMessageRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createChat(
|
||||||
|
db: DB,
|
||||||
|
participants: string[],
|
||||||
|
chatName: string,
|
||||||
|
): Promise<string> {
|
||||||
|
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<string> {
|
||||||
|
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<void> {
|
||||||
|
// 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
|
||||||
|
};
|
||||||
81
database/helpers/utils/comment_utils.ts
Normal file
81
database/helpers/utils/comment_utils.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<Comments[]> {
|
||||||
|
const query = post_id
|
||||||
|
? `SELECT * FROM comments WHERE post_id = ?`
|
||||||
|
: `SELECT * FROM comments`;
|
||||||
|
const params = post_id ? [post_id] : [];
|
||||||
|
return await queryDatabase<Comments>(db, query, params, mapCommentRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createComment(
|
||||||
|
db: DB,
|
||||||
|
postId: string,
|
||||||
|
userId: string,
|
||||||
|
createdAt: string,
|
||||||
|
text: string,
|
||||||
|
): string {
|
||||||
|
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<void> {
|
||||||
|
const query = `UPDATE comments SET text = ? WHERE comment_id = ?`;
|
||||||
|
db.query(query, [text, commentId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteComment(db: DB, commentId: string): Promise<void> {
|
||||||
|
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<void> {
|
||||||
|
const query = `UPDATE comments SET likes = likes + 1 WHERE comment_id = ?`;
|
||||||
|
db.query(query, [commentId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
createComment,
|
||||||
|
deleteComment,
|
||||||
|
getCommentsFromDB,
|
||||||
|
likeComment,
|
||||||
|
updateComment,
|
||||||
|
};
|
||||||
46
database/helpers/utils/core_utils.ts
Normal file
46
database/helpers/utils/core_utils.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @author Esad Mustafoski
|
||||||
|
* @description Core Utility for querying the Database and inserting Sample Data
|
||||||
|
* @file core_utils.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +++ IMPORTS ------------------------------------------------------ //
|
||||||
|
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<T>(
|
||||||
|
db: DB,
|
||||||
|
query: string,
|
||||||
|
params: any[],
|
||||||
|
mapRow: (row: Row) => T,
|
||||||
|
): Promise<T[]> {
|
||||||
|
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 };
|
||||||
13
database/helpers/utils/mod.ts
Normal file
13
database/helpers/utils/mod.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
/**
|
||||||
|
* @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";
|
||||||
128
database/helpers/utils/post_utils.ts
Normal file
128
database/helpers/utils/post_utils.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<Post[]> {
|
||||||
|
const query = user_uuid
|
||||||
|
? `SELECT * FROM posts WHERE user_id = ?`
|
||||||
|
: `SELECT * FROM posts`;
|
||||||
|
const params = user_uuid ? [user_uuid] : [];
|
||||||
|
return await queryDatabase<Post>(db, query, params, mapPostRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPostById(db: DB, postId: string): Promise<Post | null> {
|
||||||
|
const query = `SELECT * FROM posts WHERE posts_uuid = ?`;
|
||||||
|
const posts = await queryDatabase<Post>(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<string> {
|
||||||
|
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<void> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function deletes the comments on the post first, then
|
||||||
|
// deletes the post to avoid errors
|
||||||
|
async function deletePost(db: DB, postId: string): Promise<void> {
|
||||||
|
const deleteCommentsQuery = `DELETE FROM comments WHERE post_id = ?`;
|
||||||
|
db.query(deleteCommentsQuery, [postId]);
|
||||||
|
|
||||||
|
const deletePostQuery = `DELETE FROM posts WHERE posts_uuid = ?`;
|
||||||
|
db.query(deletePostQuery, [postId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is simplified and doesn't work as it would in a real application
|
||||||
|
// or website like twitter, this only exists as a test
|
||||||
|
async function likePost(db: DB, postId: string, userId: string): Promise<void> {
|
||||||
|
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<number>();
|
||||||
|
// set.add(1); Will work
|
||||||
|
// set.add("2"); Will not work, it will error because
|
||||||
|
// it is a string.
|
||||||
|
const includedPostIds = new Set<number>();
|
||||||
|
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,
|
||||||
|
};
|
||||||
103
database/helpers/utils/user_utils.ts
Normal file
103
database/helpers/utils/user_utils.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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, Row } from "https://deno.land/x/sqlite@v3.9.1/mod.ts";
|
||||||
|
import { mapAccountRow, queryDatabase } 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,
|
||||||
|
): any {
|
||||||
|
const query_user_exists =
|
||||||
|
`SELECT * FROM accounts WHERE displayname = '${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);
|
||||||
|
const userId = db.query(
|
||||||
|
`SELECT user_id FROM accounts WHERE displayname = '${user}'`,
|
||||||
|
);
|
||||||
|
console.log(`New user: ${user}`);
|
||||||
|
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Array of all Users in the Database
|
||||||
|
*/
|
||||||
|
async function getAllUsersFromDB(db: DB): Promise<Accounts[]> {
|
||||||
|
const query = `SELECT * FROM accounts`;
|
||||||
|
return await queryDatabase<Accounts>(db, query, [], mapAccountRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param username
|
||||||
|
* @returns Returns the Accounts for the User with the given username
|
||||||
|
*/
|
||||||
|
async function getUserByUsername(db: DB, username: string): Promise<Accounts> {
|
||||||
|
const query = `SELECT * FROM accounts WHERE username = '${username}'`;
|
||||||
|
const params: string[] = [];
|
||||||
|
const result = await queryDatabase<Accounts>(
|
||||||
|
db,
|
||||||
|
query,
|
||||||
|
params,
|
||||||
|
mapAccountRow,
|
||||||
|
);
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getAllUsersFromDB, getUserByUsername, registerUser };
|
||||||
203
database/utils.ts
Normal file
203
database/utils.ts
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 } 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 {
|
||||||
|
addMessageToChat as addMessageToChatInternal,
|
||||||
|
createChat as createChatInternal,
|
||||||
|
// getCommentsForComments as getCommentsForCommentsInternal,
|
||||||
|
// Accidentally deleted function...
|
||||||
|
createComment as createCommentInternal,
|
||||||
|
createPost as createPostInternal,
|
||||||
|
deleteChat as deleteChatInternal,
|
||||||
|
deleteComment as deleteCommentInternal,
|
||||||
|
deletePost as deletePostInternal,
|
||||||
|
filterPosts,
|
||||||
|
// --- Account Functions --- //
|
||||||
|
getAllUsersFromDB as getAllUsersFromDBInternal,
|
||||||
|
getChatById as getChatByIdInternal,
|
||||||
|
getChatMessages as getChatMessagesInternal,
|
||||||
|
// --- Comment Functions --- //
|
||||||
|
getCommentsFromDB as getCommentsFromDBInternal,
|
||||||
|
getPostById as getPostByIdInternal,
|
||||||
|
// --- Post Functions --- //
|
||||||
|
getPostsFromDB as getPostsFromDBInternal,
|
||||||
|
getUserByUsername as getUserByUsernameInternal,
|
||||||
|
// --- Chat Functions --- //
|
||||||
|
getUserChats as getUserChatsInternal,
|
||||||
|
likeComment as likeCommentInternal,
|
||||||
|
likePost as likePostInternal,
|
||||||
|
// --- Mapper Functions --- //
|
||||||
|
queryDatabase as queryDatabaseInternal,
|
||||||
|
// getAllUserInfoByID as getAllUserInfoByIDInternal,
|
||||||
|
// Accidentally deleted function...
|
||||||
|
registerUser as registerUserInternal,
|
||||||
|
updateComment as updateCommentInternal,
|
||||||
|
updatePost as updatePostInternal,
|
||||||
|
} 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
|
||||||
|
// A Workaround
|
||||||
|
const _dirname: string = dirname(fromFileUrl(import.meta.url));
|
||||||
|
const dbPath: string = join(_dirname, "../database/esp-projekt.sqlite");
|
||||||
|
const db = new DB(dbPath);
|
||||||
|
|
||||||
|
// +++ INTERFACES --------------------------------------------------- //
|
||||||
|
// Only re-export interfaces that are needed by external code
|
||||||
|
export type {
|
||||||
|
Accounts,
|
||||||
|
Chat,
|
||||||
|
Comments,
|
||||||
|
Message,
|
||||||
|
Post,
|
||||||
|
} from "./helpers/interfaces.ts";
|
||||||
|
// +++ HELPER FUNCTIONS --------------------------------------------- //
|
||||||
|
export function queryDatabase<T>(
|
||||||
|
query: string,
|
||||||
|
params: any[],
|
||||||
|
mapRow: (row: any) => T,
|
||||||
|
): Promise<T[]> {
|
||||||
|
return queryDatabaseInternal(db, query, params, mapRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// +++ DATABASE INITIALIZATION -------------------------------------- //
|
||||||
|
export async function ensureDatabaseExists(): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Check if the database directory exists, create it if not
|
||||||
|
const dbDir = dirname(dbPath);
|
||||||
|
try {
|
||||||
|
await Deno.stat(dbDir);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Deno.errors.NotFound) {
|
||||||
|
// Create the database directory
|
||||||
|
await Deno.mkdir(dbDir, { recursive: true });
|
||||||
|
console.log(`Created database directory: ${dbDir}`);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the database file exists
|
||||||
|
try {
|
||||||
|
await Deno.stat(dbPath);
|
||||||
|
console.log("Database file already exists");
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Deno.errors.NotFound) {
|
||||||
|
createDatabaseIfNotExist();
|
||||||
|
insertSamples();
|
||||||
|
// Nothing, file will be created below
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error ensuring database exists:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDatabaseIfNotExist(): void {
|
||||||
|
db_create.createDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function insertSamples(): void {
|
||||||
|
db_create.insertSampleData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// +++ 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,
|
||||||
|
userGroup: string,
|
||||||
|
displayname: string,
|
||||||
|
user_email: string,
|
||||||
|
firstname: string,
|
||||||
|
surname: string,
|
||||||
|
account_created: string,
|
||||||
|
) =>
|
||||||
|
registerUserInternal(
|
||||||
|
db,
|
||||||
|
user,
|
||||||
|
password,
|
||||||
|
salt,
|
||||||
|
userGroup,
|
||||||
|
displayname,
|
||||||
|
user_email,
|
||||||
|
firstname,
|
||||||
|
surname,
|
||||||
|
account_created,
|
||||||
|
);
|
||||||
|
|
||||||
|
// +++ 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);
|
||||||
|
|
||||||
|
// +++ 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);
|
||||||
|
|
||||||
|
// +++ 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 };
|
||||||
Reference in New Issue
Block a user