Browse Source

ADD recommendation system

fix/clean
Astri4-4 5 months ago
parent
commit
f60f1e0cad
  1. 12
      .idea/dataSources.xml
  2. 2
      backend/Dockerfile
  3. 6
      backend/app/controllers/recommendation.controller.js
  4. 24
      backend/app/controllers/video.controller.js
  5. 1
      backend/app/routes/redommendation.route.js
  6. 3
      backend/app/utils/database.js
  7. 16
      backend/app/utils/wait-for-it.sh
  8. 61
      backend/logs/access.log
  9. 4
      backend/requests/channel.http
  10. 2
      backend/requests/recommendation.http
  11. 10
      backend/requests/user.http
  12. 17
      backend/requests/video.http
  13. 2
      backend/server.js
  14. 29
      developpement.yaml

12
.idea/dataSources.xml

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="freetube@localhost" uuid="d425327f-375d-4dc5-a079-c813210a67d8">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://localhost:5432/freetube</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

2
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"]

6
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 {

24
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);

1
backend/app/routes/redommendation.routes.js → backend/app/routes/redommendation.route.js

@ -1,4 +1,5 @@
import { Router } from 'express';
import {getRecommendations} from "../controllers/recommendation.controller.js";
const router = Router();

3
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);

16
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

61
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

4
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

2
backend/requests/recommendation.http

@ -0,0 +1,2 @@
### GET NON-AUTHENTICATED RECOMMENDATIONS
GET http://localhost:8000/api/recommendations/

10
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

17
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}}
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
}

2
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;

29
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
driver: local

Loading…
Cancel
Save