diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml deleted file mode 100644 index bb625ca..0000000 --- a/.idea/dataSources.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - postgresql - true - org.postgresql.Driver - jdbc:postgresql://localhost:5432/freetube - $ProjectFileDir$ - - - \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index 327116a..997cf62 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -11,5 +11,7 @@ COPY . . # Expose the port the app runs on EXPOSE 8000 +RUN apt-get update && apt-get install -y netcat-openbsd + # Start the application CMD ["npm", "start"] \ No newline at end of file diff --git a/backend/app/controllers/recommendation.controller.js b/backend/app/controllers/recommendation.controller.js index df2e21e..026e06d 100644 --- a/backend/app/controllers/recommendation.controller.js +++ b/backend/app/controllers/recommendation.controller.js @@ -9,7 +9,11 @@ export async function getRecommendations(req, res) { // GET MOST USED TOKEN let client = await getClient(); - let queryMostUsedToken = `SELECT * FROM tags ` + let queryMostUsedToken = `SELECT id, name FROM tags ORDER BY usage_count DESC LIMIT 3;`; + let result = await client.query(queryMostUsedToken); + + const recommendations = result.rows; + res.status(200).json(recommendations); } else { diff --git a/backend/app/controllers/video.controller.js b/backend/app/controllers/video.controller.js index 402b2cb..0f31563 100644 --- a/backend/app/controllers/video.controller.js +++ b/backend/app/controllers/video.controller.js @@ -218,21 +218,39 @@ export async function addTags(req, res) { const client = await getClient(); + // DECREASE USAGE COUNT FOR ALL TAGS + const decreaseUsageCountQuery = `UPDATE tags SET usage_count = usage_count - 1 WHERE id IN (SELECT tag FROM video_tags WHERE video = ${videoId})`; + await client.query(decreaseUsageCountQuery); + + // DELETE ALL TAGS FOR VIDEO + let deleteQuery = `DELETE FROM video_tags WHERE video = ${videoId}`; + await client.query(deleteQuery); + + // INSERT NEW TAGS for (const tag of tags) { - const tagQuery = `SELECT * FROM tags WHERE name = '${tag}' AND video = ${videoId}`; + const tagQuery = `SELECT * FROM tags WHERE name = '${tag}'`; const tagResult = await client.query(tagQuery); let id = null; if (tagResult.rows.length === 0) { - const insertTagQuery = `INSERT INTO tags (name, video) VALUES ('${tag}') RETURNING id`; + const insertTagQuery = `INSERT INTO tags (name, usage_count) VALUES ('${tag}', 1) RETURNING id`; const result = await client.query(insertTagQuery); id = result.rows[0].id; } else { logger.write("Tag " + tag + " already exists for video " + videoId, 200); + const getTagQuery = `SELECT usage_count FROM tags WHERE name = '${tag}'`; + const getTagResult = await client.query(getTagQuery); + const usageCount = getTagResult.rows[0].usage_count + 1; + const updateTagQuery = `UPDATE tags SET usage_count = ${usageCount} WHERE name = '${tag}'`; + await client.query(updateTagQuery); id = tagResult.rows[0].id; } - const insertVideoTagQuery = `INSERT INTO video_tags (video, tag) VALUES (${id}, ${videoId}) ON CONFLICT DO NOTHING`; + console.log("Tag ID: ", id); + console.log("Video ID: ", videoId); + + // INSERT INTO video_tags + const insertVideoTagQuery = `INSERT INTO video_tags (tag, video) VALUES (${id}, ${videoId})`; await client.query(insertVideoTagQuery); } logger.write("successfully added tags to video " + videoId, 200); diff --git a/backend/app/routes/redommendation.routes.js b/backend/app/routes/redommendation.route.js similarity index 50% rename from backend/app/routes/redommendation.routes.js rename to backend/app/routes/redommendation.route.js index 8120463..b08c915 100644 --- a/backend/app/routes/redommendation.routes.js +++ b/backend/app/routes/redommendation.route.js @@ -1,4 +1,5 @@ import { Router } from 'express'; +import {getRecommendations} from "../controllers/recommendation.controller.js"; const router = Router(); diff --git a/backend/app/utils/database.js b/backend/app/utils/database.js index f8a4048..49c0b1a 100644 --- a/backend/app/utils/database.js +++ b/backend/app/utils/database.js @@ -89,7 +89,8 @@ export async function initDb() { query = `CREATE TABLE IF NOT EXISTS tags( id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL + name VARCHAR(255) NOT NULL, + usage_count INTEGER NOT NULL DEFAULT 0 )`; await client.query(query); diff --git a/backend/app/utils/wait-for-it.sh b/backend/app/utils/wait-for-it.sh new file mode 100755 index 0000000..10fdca6 --- /dev/null +++ b/backend/app/utils/wait-for-it.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +hostport="$1" +shift +cmd="$@" + +host="${hostport%%:*}" +port="${hostport##*:}" + +while ! nc -z "$host" "$port"; do + echo "Waiting for $host:$port..." + sleep 1 +done + +echo "$host:$port is available. Running command: $cmd" +exec $cmd \ No newline at end of file diff --git a/backend/logs/access.log b/backend/logs/access.log index f60f4f6..4aef878 100644 --- a/backend/logs/access.log +++ b/backend/logs/access.log @@ -18,3 +18,64 @@ [2025-07-16 18:30:45.577] [172.21.0.1] POST(/): successfully registered with status 200 [2025-07-16 19:33:49.799] [172.21.0.1] POST(/): try to register a user with username: test and email: test@test.com [2025-07-16 19:33:49.860] [172.21.0.1] POST(/): successfully registered with status 200 +[2025-07-17 20:13:17.329] [172.18.0.1] PUT(/:id): failed due to invalid values with status 400 +[2025-07-17 20:13:41.254] [172.18.0.1] POST(/login): try to login with username 'test2' +[2025-07-17 20:13:41.262] [172.18.0.1] POST(/login): failed to login with status 401 +[2025-07-17 20:39:05.889] [172.18.0.1] POST(/): try to register a user with username: astria and email: sacha@gmail.com +[2025-07-17 20:43:42.183] [172.18.0.1] POST(/): try to register a user with username: astria and email: sacha@gmail.com +[2025-07-17 20:43:42.232] [172.18.0.1] POST(/): successfully registered with status 200 +[2025-07-17 20:44:14.202] [172.18.0.1] POST(/login): try to login with username 'astria' +[2025-07-17 20:44:14.253] [172.18.0.1] POST(/login): Successfully logged in with status 200 +[2025-07-17 20:45:50.748] [172.18.0.1] POST(/): try to create new channel with owner 1 and name Machin +[2025-07-17 20:45:50.748] [172.18.0.1] POST(/): Successfully created new channel with name Machin with status 200 +[2025-07-17 20:46:19.093] [172.18.0.1] POST(/): Invalid token with status 401 +[2025-07-17 20:46:31.901] [172.18.0.1] POST(/): try to upload video with status undefined +[2025-07-17 20:46:31.904] [172.18.0.1] POST(/): successfully uploaded video with status 200 +[2025-07-17 20:48:05.807] [172.18.0.1] PUT(/:id/tags): Invalid token with status 401 +[2025-07-17 20:49:19.716] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 20:51:10.841] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 20:52:43.517] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 20:54:52.671] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 20:54:52.677] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 20:54:52.678] [172.18.0.1] PUT(/:id/tags): Tag test2 already exists for video 1 with status 200 +[2025-07-17 20:57:53.156] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 20:57:53.161] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 20:57:53.163] [172.18.0.1] PUT(/:id/tags): Tag test2 already exists for video 1 with status 200 +[2025-07-17 20:59:37.446] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 20:59:37.452] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 20:59:37.454] [172.18.0.1] PUT(/:id/tags): Tag test2 already exists for video 1 with status 200 +[2025-07-17 20:59:37.454] [172.18.0.1] PUT(/:id/tags): successfully added tags to video 1 with status 200 +[2025-07-17 21:01:02.819] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 21:01:02.825] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 21:01:02.827] [172.18.0.1] PUT(/:id/tags): Tag test2 already exists for video 1 with status 200 +[2025-07-17 21:01:02.828] [172.18.0.1] PUT(/:id/tags): successfully added tags to video 1 with status 200 +[2025-07-17 21:10:48.984] [172.18.0.1] POST(/): try to register a user with username: astria and email: sacha@gmail.com +[2025-07-17 21:10:58.810] [172.18.0.1] POST(/): try to register a user with username: astria and email: sacha@gmail.com +[2025-07-17 21:10:58.859] [172.18.0.1] POST(/): successfully registered with status 200 +[2025-07-17 21:11:08.794] [172.18.0.1] POST(/login): try to login with username 'astria' +[2025-07-17 21:11:08.844] [172.18.0.1] POST(/login): Successfully logged in with status 200 +[2025-07-17 21:11:18.922] [172.18.0.1] POST(/): try to create new channel with owner 1 and name Machin +[2025-07-17 21:11:18.923] [172.18.0.1] POST(/): Successfully created new channel with name Machin with status 200 +[2025-07-17 21:11:28.593] [172.18.0.1] POST(/): try to upload video with status undefined +[2025-07-17 21:11:28.595] [172.18.0.1] POST(/): successfully uploaded video with status 200 +[2025-07-17 21:11:41.192] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 21:11:41.202] [172.18.0.1] PUT(/:id/tags): successfully added tags to video 1 with status 200 +[2025-07-17 21:14:10.932] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 21:14:10.938] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 21:14:10.942] [172.18.0.1] PUT(/:id/tags): Tag test2 already exists for video 1 with status 200 +[2025-07-17 21:14:10.944] [172.18.0.1] PUT(/:id/tags): successfully added tags to video 1 with status 200 +[2025-07-17 21:14:16.478] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 21:14:16.485] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 21:14:16.487] [172.18.0.1] PUT(/:id/tags): Tag test2 already exists for video 1 with status 200 +[2025-07-17 21:14:16.489] [172.18.0.1] PUT(/:id/tags): successfully added tags to video 1 with status 200 +[2025-07-17 21:16:39.779] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 21:16:39.785] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 21:16:39.788] [172.18.0.1] PUT(/:id/tags): Tag test2 already exists for video 1 with status 200 +[2025-07-17 21:16:39.790] [172.18.0.1] PUT(/:id/tags): successfully added tags to video 1 with status 200 +[2025-07-17 21:18:06.248] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 21:18:06.257] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 21:18:06.259] [172.18.0.1] PUT(/:id/tags): Tag test2 already exists for video 1 with status 200 +[2025-07-17 21:18:06.261] [172.18.0.1] PUT(/:id/tags): successfully added tags to video 1 with status 200 +[2025-07-17 21:18:20.739] [172.18.0.1] PUT(/:id/tags): try to add tags to video 1 +[2025-07-17 21:18:20.746] [172.18.0.1] PUT(/:id/tags): Tag test already exists for video 1 with status 200 +[2025-07-17 21:18:20.749] [172.18.0.1] PUT(/:id/tags): successfully added tags to video 1 with status 200 diff --git a/backend/requests/channel.http b/backend/requests/channel.http index bce4621..e85393d 100644 --- a/backend/requests/channel.http +++ b/backend/requests/channel.http @@ -1,4 +1,4 @@ -@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwidXNlcm5hbWUiOiJ0ZXN0IiwiaWF0IjoxNzUxODA5OTA3fQ.uCJFysaOiXC8qZKykWLi58WDcItKSkQPUIZ7kH-87Tg +@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhc3RyaWEiLCJpYXQiOjE3NTI3ODY2Njh9._naj-ip2UeBxbflzGOWLxjHNTXJobLJ9s_70xL6ylKw ### CREATE CHANNEL POST http://localhost:8000/api/channels @@ -8,7 +8,7 @@ Content-Type: application/json { "name": "Machin", "description": "kljsdfjklsdfjkl", - "owner": 5 + "owner": 1 } ### GET CHANNEL BY ID diff --git a/backend/requests/recommendation.http b/backend/requests/recommendation.http new file mode 100644 index 0000000..5ad6591 --- /dev/null +++ b/backend/requests/recommendation.http @@ -0,0 +1,2 @@ +### GET NON-AUTHENTICATED RECOMMENDATIONS +GET http://localhost:8000/api/recommendations/ \ No newline at end of file diff --git a/backend/requests/user.http b/backend/requests/user.http index 1f01eee..be24e85 100644 --- a/backend/requests/user.http +++ b/backend/requests/user.http @@ -1,12 +1,12 @@ -@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwidXNlcm5hbWUiOiJ0ZXN0IiwiaWF0IjoxNzUxODA5OTA3fQ.uCJFysaOiXC8qZKykWLi58WDcItKSkQPUIZ7kH-87Tg +@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhc3RyaWEiLCJpYXQiOjE3NTI3ODUwNTR9.yOEsanvke0yejwbizkA33SwJMzohfn-xymetVJwSFSs ### CREATE USER POST http://localhost:8000/api/users/ Content-Type: application/json { - "email": "test2@test.com", - "username": "test2", + "email": "sacha@gmail.com", + "username": "astria", "password": "Test_974", "picture": "null" } @@ -16,8 +16,8 @@ POST http://localhost:8000/api/users/login Content-Type: application/json { - "username": "test2", - "password": "Test_974" + "username": "astria", + "password": "Test1234!" } ### GET USER BY ID diff --git a/backend/requests/video.http b/backend/requests/video.http index bf03a0b..c5ab88e 100644 --- a/backend/requests/video.http +++ b/backend/requests/video.http @@ -1,4 +1,4 @@ -@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwidXNlcm5hbWUiOiJ0ZXN0IiwiaWF0IjoxNzUxODA5OTA3fQ.uCJFysaOiXC8qZKykWLi58WDcItKSkQPUIZ7kH-87Tg +@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhc3RyaWEiLCJpYXQiOjE3NTI3ODY2Njh9._naj-ip2UeBxbflzGOWLxjHNTXJobLJ9s_70xL6ylKw ### UPDATE VIDEO PUT http://127.0.0.1:8000/api/videos/12 @@ -13,4 +13,17 @@ Authorization: Bearer {{token}} ### TOGGLE LIKE GET http://127.0.0.1:8000/api/videos/14/like -Authorization: Bearer {{token}} \ No newline at end of file +Authorization: Bearer {{token}} + +### ADD TAGS +PUT http://127.0.0.1:8000/api/videos/1/tags +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "tags": [ + "test", + "chat" + ], + "channel": 1 +} \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index 55262b7..89f33f5 100644 --- a/backend/server.js +++ b/backend/server.js @@ -4,6 +4,7 @@ import UserRoute from "./app/routes/user.route.js"; import ChannelRoute from "./app/routes/channel.route.js"; import VideoRoute from "./app/routes/video.route.js"; import CommentRoute from "./app/routes/comment.route.js"; +import RecommendationRoute from "./app/routes/redommendation.route.js"; import * as http from "node:http"; import cors from "cors"; import PlaylistRoute from "./app/routes/playlist.route.js"; @@ -28,6 +29,7 @@ app.use("/api/channels/", ChannelRoute); app.use("/api/videos/", VideoRoute); app.use("/api/comments/", CommentRoute); app.use("/api/playlists", PlaylistRoute); +app.use("/api/recommendations", RecommendationRoute); const port = process.env.PORT; diff --git a/developpement.yaml b/developpement.yaml index f748284..5b12e0b 100644 --- a/developpement.yaml +++ b/developpement.yaml @@ -4,31 +4,35 @@ services: build: context: ./backend dockerfile: Dockerfile + network: host container_name: resit_backend ports: - - "8000:8000" + - "${BACKEND_PORT}:${BACKEND_PORT}" environment: - DB_USER: astria - DB_NAME: freetube - DB_HOST: db - DB_PASSWORD: 9Zv1Y1MAfWKKmtP7JlSQPX7ZmHkS1J6iTlOFQb6OFZEzWjQGeJIKictaGvKyOBIz - JWT_SECRET: Aed4GPa8BtriElyuwy65bf598D8MgxWCiE6Xzc4riV0J7AiLpxeu2DexjQPx4cBO - LOG_FILE: /var/log/freetube/access.log - PORT: 8000 + DB_USER: ${POSTGRES_USER} + DB_NAME: ${POSTGRES_DB} + DB_HOST: ${POSTGRES_HOST} + DB_PASSWORD: ${POSTGRES_PASSWORD} + JWT_SECRET: ${JWT_SECRET} + LOG_FILE: ${LOG_FILE} + PORT: ${BACKEND_PORT} volumes: - ./backend/logs:/var/log/freetube - ./backend/app/uploads:/app/app/uploads + - ./backend/app/utils/wait-for-it.sh:/wait-for-it.sh depends_on: - db + command: ["/wait-for-it.sh", "${POSTGRES_HOST}:5432", "--", "npm", "start"] db: image: postgres:latest + container_name: resit_db ports: - "5432:5432" environment: - POSTGRES_USER: astria - POSTGRES_PASSWORD: 9Zv1Y1MAfWKKmtP7JlSQPX7ZmHkS1J6iTlOFQb6OFZEzWjQGeJIKictaGvKyOBIz - POSTGRES_DB: freetube + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} volumes: - db_data:/var/lib/postgresql/data @@ -36,6 +40,7 @@ services: build: context: ./frontend dockerfile: Dockerfile + network: host ports: - "5173:5173" volumes: @@ -46,4 +51,4 @@ services: volumes: db_data: - driver: local \ No newline at end of file + driver: local