diff --git a/.gitignore b/.gitignore index fce631c..709d4af 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,7 @@ public/assets/build/ .vscode/ package-lock.json bun.lockb -bundle.js \ No newline at end of file +bundle.js +mydb.sqlite +public/poster/ +output/ \ No newline at end of file diff --git a/app.ts b/app.ts index 1efcaa8..df38962 100644 --- a/app.ts +++ b/app.ts @@ -1,20 +1,18 @@ import express from "express"; -import path from 'path' -import 'dotenv/config' const hostname = '127.0.0.1'; const httpPort = 4080; const app = express(); -app.set('views', path.join(__dirname, 'views')); +app.set('views', 'views'); app.set('view engine', 'hbs'); // import morgan from 'morgan' // app.use(morgan('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); -app.use(express.static(path.join(__dirname, 'public'))); +app.use(express.static('public')); import mainRouter from './routes/main'; import apiRouter from './routes/api/apiRouter'; diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..05f4eb2 --- /dev/null +++ b/build.sh @@ -0,0 +1,6 @@ +mkdir output +bun build ./app.ts --outfile=output/app.js --target=bun --minify +bun build ./frontend/list/list.tsx --outfile=public/assets/build/list.js --minify + +cp -r views/ output/ +cp -r public/ output/ \ No newline at end of file diff --git a/build.ts b/build.ts deleted file mode 100644 index a9f189f..0000000 --- a/build.ts +++ /dev/null @@ -1,20 +0,0 @@ - -async function build() { - const minify = { - whitespace: true, - syntax: true, - identifiers: true, - } - - const sa = await Bun.build({ - entrypoints: ["./frontend/list/list"], - outdir: "./public/assets/build/", - minify, - }) - - console.log(sa); -} - -build(); - -export default build; diff --git a/controllers/mediaController.ts b/controllers/mediaController.ts index 7962386..8b5540e 100644 --- a/controllers/mediaController.ts +++ b/controllers/mediaController.ts @@ -1,7 +1,14 @@ import { type Request, type Response } from "express"; import UserModel, { values } from '../models/userModel'; -import MediaModel, { Table } from '../models/mediaModel'; +import MediaModel, { Table, Media } from '../models/mediaModel'; +interface omdbRes{ + Title: string, + Released: string, + Response: string, + Poster: string, + Type: string +} function fromStringToTable(value: string): (Table | undefined) { if (value.localeCompare("games") == 0) return Table.games; @@ -10,10 +17,31 @@ function fromStringToTable(value: string): (Table | undefined) { return; } +async function downloadImage(mData: Media, type: Table) { + // Specify the path where you want to save the image + const outputPath = '/poster/' + type + '/' + mData.code + '.jpg'; + + // Use Bun's built-in fetch to download the image + const response = await fetch(mData.webImg); + + // Check if the request was successful + if (!response.ok) { + console.log("fetch image error"); + return + } + + // Convert the response to a blob + const imageBlob = await response.blob(); + // Use Bun's write to save the image to a file + await Bun.write('./public/' + outputPath, await imageBlob.arrayBuffer()); + MediaModel.updateWebImg(type, mData.code, outputPath); + +} + async function createMed(req: Request, res: Response) { const mediaCode: string = req.body.code; - const omdb_key = await UserModel.getValue(values.omdb_key); + const omdb_key = UserModel.getValue(values.omdb_key); if (!omdb_key) { return res.status(500).json({ message: 'Error when creating media' }); @@ -27,39 +55,44 @@ async function createMed(req: Request, res: Response) { const uri = `http://www.omdbapi.com/?i=${mediaCode}&apikey=${omdb_key}`; const mJson = await fetch(uri); - const mData = await mJson.json(); + const mData: omdbRes = await mJson.json(); if (mData.Response == 'False') { return res.status(404).json({ message: 'wrong code' }); } - const seriesFound = await MediaModel.findOne(Table.series, cleanCode); - if (seriesFound.length != 0) { - await MediaModel.updateWebImg(Table.series, cleanCode, mData.Poster); - return res.status(409).json({ message: 'Media already exists' }); - } - - const moviesFound = await MediaModel.findOne(Table.movies, cleanCode); - if (moviesFound.length != 0) { - await MediaModel.updateWebImg(Table.movies, cleanCode, mData.Poster); - return res.status(409).json({ message: 'Media already exists' }); - } - - if (mData.Type.localeCompare("movie") == 0) { - const savedMedia = await MediaModel.save(Table.movies, cleanCode, mData.Title, mData.Released, mData.Poster); - } - else if (mData.Type.localeCompare("series") == 0) { - const savedMedia = await MediaModel.save(Table.series, cleanCode, mData.Title, mData.Released, mData.Poster); - } - - const media = { - code: mediaCode, + const media: Media = { + id:0, + code: cleanCode, title: mData.Title, released: mData.Released, webImg: mData.Poster, }; - return res.status(201).json(media); + const seriesFound = MediaModel.findOne(Table.series, cleanCode); + if (seriesFound.length != 0) { + res.status(409).json({ message: 'Media already exists' }); + await downloadImage(media, Table.series); + return; + } + + const moviesFound = MediaModel.findOne(Table.movies, cleanCode); + if (moviesFound.length != 0) { + res.status(409).json({ message: 'Media already exists' }); + await downloadImage(media, Table.movies); + return; + } + + if (mData.Type.localeCompare("movie") == 0) { + const savedMedia = MediaModel.save(Table.movies, cleanCode, mData.Title, mData.Released, mData.Poster); + await downloadImage(media, Table.movies); + } + else if (mData.Type.localeCompare("series") == 0) { + const savedMedia = MediaModel.save(Table.series, cleanCode, mData.Title, mData.Released, mData.Poster); + await downloadImage(media, Table.series); + } + + res.status(201).json(media); } catch (err) { return res.status(500).json({ message: 'Error when creating media' }); } @@ -68,15 +101,15 @@ async function createMed(req: Request, res: Response) { async function createGame(req: Request, res: Response) { var gameCode = req.body.code; - const twitch_client_id = await UserModel.getValue(values.twitch_client_id); - const twitch_client_secret = await UserModel.getValue(values.twitch_client_secret); + const twitch_client_id = UserModel.getValue(values.twitch_client_id); + const twitch_client_secret = UserModel.getValue(values.twitch_client_secret); if (!twitch_client_id || !twitch_client_secret) { return res.status(500).json({ message: 'Error when creating game' }); } try { - const gameFound = await MediaModel.findOne(Table.games, gameCode); + const gameFound = MediaModel.findOne(Table.games, gameCode); if (gameFound) { return res.status(409).json({ message: 'Game already exists' }); } @@ -121,14 +154,15 @@ async function createGame(req: Request, res: Response) { ) const coverData = await response.json() - const game = { + const game: Media = { + id: 0, code: gameCode, title: gameData[0].name, released: dateStr, webImg: `https://images.igdb.com/igdb/image/upload/t_cover_big/${coverData[0].image_id}.jpg`, }; - const savedGame = await MediaModel.save(Table.games, game.code, game.title, game.released, game.webImg); + const savedGame = MediaModel.save(Table.games, game.code, game.title, game.released, game.webImg); return res.status(201).json(game); } catch (error) { @@ -150,16 +184,8 @@ export default { }); } - MediaModel.find(mediaTable) - .then(media => { - return res.json(media); - }) - .catch(err => { - return res.status(500).json({ - message: 'Error when getting media.', - error: err - }); - }); + const media = MediaModel.find(mediaTable); + return res.json(media); }, create: async function (req: Request, res: Response) { @@ -174,7 +200,7 @@ export default { /** * mediaController.delete() */ - remove: async function (req: Request, res: Response) { + remove: function (req: Request, res: Response) { const mediaTable = fromStringToTable(req.params.mediaType); if (!mediaTable) { return res.status(500).json({ @@ -186,7 +212,7 @@ export default { try { const mediaTable = req.baseUrl.includes('movies') ? Table.movies : Table.series; - const media = await MediaModel.findOneAndDelete(mediaTable, code); + const media = MediaModel.findOneAndDelete(mediaTable, code); if (!media) { return res.status(404).json({ message: 'No such media' }); } diff --git a/controllers/userController.ts b/controllers/userController.ts index ef130b8..f8893f3 100644 --- a/controllers/userController.ts +++ b/controllers/userController.ts @@ -3,20 +3,20 @@ import UserModel, { values } from '../models/userModel'; export default { - render: async function (req: Request, res: Response) { + render: function (req: Request, res: Response) { res.render('user', { keys: UserModel.namesOfValues }); }, - create: async function (req: Request, res: Response) { + create: function (req: Request, res: Response) { const reqPassword: string = req.body.reqPassword; if (!reqPassword) return res.render('user', { keys: UserModel.namesOfValues }); - const password = await UserModel.getValue(values.pass); - + const password = UserModel.getValue(values.pass); + // if no password in db save reqPassword if (!password) { - const affectedRows = await UserModel.updateValue("pass", reqPassword); + const affectedRows = UserModel.updateValue("pass", reqPassword); if (affectedRows > 0) { return res.redirect('/list'); } @@ -35,7 +35,7 @@ export default { return res.render('user', { keys: UserModel.namesOfValues }); } - const affectedRows = await UserModel.updateValue(name, value); + const affectedRows = UserModel.updateValue(name, value); if (affectedRows == 0) { return res.render('user', { keys: UserModel.namesOfValues }); } @@ -43,8 +43,8 @@ export default { return res.redirect('/list'); }, - get: async function (req: Request, res: Response) { - const usersFound = await UserModel.getAll(); + get: function (req: Request, res: Response) { + const usersFound = UserModel.getAll(); return res.status(200).json(usersFound); }, }; \ No newline at end of file diff --git a/dev.ts b/dev.ts index 8af899b..61d5aa6 100644 --- a/dev.ts +++ b/dev.ts @@ -1,5 +1,4 @@ -const build_cash = Bun.spawn(["bun", "build", "./frontend/cash/cash.tsx", "--outfile=public/assets/build/cash/cash.js", "--watch"]); const build_list = Bun.spawn(["bun", "build", "./frontend/list/list.tsx", "--outfile=public/assets/build/list/list.js", "--watch"]); const build_app = Bun.spawn(["bun", "--watch", "./app.ts"]); diff --git a/frontend/list/list.tsx b/frontend/list/list.tsx index 80dc9c1..d000552 100644 --- a/frontend/list/list.tsx +++ b/frontend/list/list.tsx @@ -244,6 +244,7 @@ function removeMedia(evt: Event) { function onImgError(evt: Event) { const imgT = evt.target as HTMLImageElement; imgT.src = "/images/no_poster.jpg"; + console.log(imgT.parentElement?.parentElement?.id); } function renderMedias(unsorted_movies: Array) { diff --git a/miscellaneous/checkAuthenticated.ts b/miscellaneous/checkAuthenticated.ts index f28a25c..aa4218c 100644 --- a/miscellaneous/checkAuthenticated.ts +++ b/miscellaneous/checkAuthenticated.ts @@ -1,9 +1,9 @@ import { type NextFunction, type Request, type Response } from "express"; import userModel, { values } from 'models/userModel'; -async function checkAuthenticated(req: Request, res: Response, next: NextFunction) { +function checkAuthenticated(req: Request, res: Response, next: NextFunction) { const pass = req.body.pass; - const password = await userModel.getValue(values.pass); + const password = userModel.getValue(values.pass); if (pass && password) { if (pass == password) { return next(); diff --git a/miscellaneous/db.ts b/miscellaneous/db.ts index 667eb2b..8884d5d 100644 --- a/miscellaneous/db.ts +++ b/miscellaneous/db.ts @@ -1,34 +1,65 @@ -import mysql, { type PoolOptions } from 'mysql2/promise' +import { Database } from "bun:sqlite"; -const poolOptions: PoolOptions = { - host: "", - port: 0, - user: "", - password: "", - database: "" +const pool = new Database("mydb.sqlite", { strict: true }); + +pool.exec(` +CREATE TABLE IF NOT EXISTS series ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code INT NOT NULL, + title TEXT NOT NULL, + released TEXT NOT NULL, + webImg TEXT NOT NULL, + UNIQUE (code) +); +`); + +pool.exec(` +CREATE TABLE IF NOT EXISTS movies ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code INT NOT NULL, + title TEXT NOT NULL, + released TEXT NOT NULL, + webImg TEXT NOT NULL, + UNIQUE (code) +); +`); + +pool.exec(` +CREATE TABLE IF NOT EXISTS games ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code INT NOT NULL, + title TEXT NOT NULL, + released TEXT NOT NULL, + webImg TEXT NOT NULL, + UNIQUE (code) +); +`); + +pool.exec(` +CREATE TABLE IF NOT EXISTS userData ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + value TEXT NOT NULL +); +`); + +function inset_keys() { + class co { + count!: number; + } + const result = pool.query("SELECT count(*) as count FROM userData;").as(co).get(); + if(result && result.count >= 4){ + return; + } + + pool.exec(` + INSERT INTO userData (name, value) VALUES ("pass", ""); + INSERT INTO userData (name, value) VALUES ("omdb_key", ""); + INSERT INTO userData (name, value) VALUES ("twitch_client_id", ""); + INSERT INTO userData (name, value) VALUES ("twitch_client_secret", ""); + `); } -if (process.env.DBIP) { - poolOptions.host = process.env.DBIP; -} - -if (process.env.DBPort) { - poolOptions.port = parseInt(process.env.DBPort); -} - -if (process.env.DBUser) { - poolOptions.user = process.env.DBUser; -} - -if (process.env.DBPassword) { - poolOptions.password = process.env.DBPassword; -} - -if (process.env.DBDatabase) { - poolOptions.database = process.env.DBDatabase; -} - - -const pool = mysql.createPool(poolOptions); +inset_keys(); export default pool; \ No newline at end of file diff --git a/models/mediaModel.ts b/models/mediaModel.ts index c42f67d..204cbe7 100644 --- a/models/mediaModel.ts +++ b/models/mediaModel.ts @@ -1,13 +1,12 @@ -import { type ResultSetHeader, type RowDataPacket, type QueryOptions } from "mysql2" import pool from 'miscellaneous/db' -interface Media extends RowDataPacket { - id?: number; - code?: number; - title?: string; - released?: string; - webImg?: string; +export class Media { + id!: number; + code!: number; + title!: string; + released!: string; + webImg!: string; } export enum Table { @@ -16,14 +15,12 @@ export enum Table { games = "games", } -async function save(table: Table, code: number, title: string, released: string, webImg: string): Promise { +function save(table: Table, code: number, title: string, released: string, webImg: string): number { try { - const options: QueryOptions = { - sql: "INSERT INTO " + table + " (code, title, released, webImg) VALUES (?,?,?,?)", - values: [code, title, released, webImg] - }; - const [result, fields] = await pool.query(options); - return result.affectedRows; + const sql = "INSERT INTO " + table + " (code, title, released, webImg) VALUES (?,?,?,?)"; + + const result = pool.query(sql).run(code, title, released, webImg); + return result.changes; } catch (err) { console.log(err); @@ -31,14 +28,11 @@ async function save(table: Table, code: number, title: string, released: string, return 0; } -async function updateWebImg(table: Table, code: number, webImg: string): Promise { +function updateWebImg(table: Table, code: number, webImg: string): number { try { - const options: QueryOptions = { - sql: "UPDATE " + table + "SET webImg = ? WHERE code = ?;", - values: [webImg, code] - }; - const [result, fields] = await pool.query(options); - return result.affectedRows; + const sql = "UPDATE " + table + " SET webImg = ? WHERE code = ?;"; + const result = pool.query(sql).run(webImg, code); + return result.changes; } catch (err) { console.log(err); @@ -46,10 +40,10 @@ async function updateWebImg(table: Table, code: number, webImg: string): Promise return 0; } -async function findOneAndDelete(table: Table, code: number): Promise { +function findOneAndDelete(table: Table, code: number): number { try { - const [result, fields] = await pool.query("DELETE FROM " + table + " WHERE code = ?;", [code]); - return result.affectedRows; + const result = pool.query("DELETE FROM " + table + " WHERE code = ?;").run(code); + return result.changes; } catch (err) { console.log(err); @@ -57,9 +51,9 @@ async function findOneAndDelete(table: Table, code: number): Promise { return 0; } -async function findOne(table: Table, code: number): Promise { +function findOne(table: Table, code: number): Media[] { try { - const [rows, fields] = await pool.query("SELECT * FROM " + table + " WHERE code = ?;", [code]); + const rows = pool.query("SELECT * FROM " + table + " WHERE code = ?;").as(Media).all(code); return rows; } catch (err) { @@ -68,9 +62,9 @@ async function findOne(table: Table, code: number): Promise { return []; } -async function find(table: Table): Promise { +function find(table: Table): Media[] { try { - const [rows, fields] = await pool.query("SELECT * FROM " + table + ";"); + const rows = pool.query("SELECT * FROM " + table + ";").as(Media).all(); return rows; } catch (err) { diff --git a/models/userModel.ts b/models/userModel.ts index c453417..2fdd139 100644 --- a/models/userModel.ts +++ b/models/userModel.ts @@ -1,7 +1,6 @@ -import { type ResultSetHeader, type RowDataPacket } from "mysql2" import pool from 'miscellaneous/db' -interface UserD extends RowDataPacket { +class UserD { name?: string; value?: string; } @@ -15,9 +14,9 @@ export enum values { const namesOfValues: string[] = ["", "pass", "omdb_key", "twitch_client_id", "twitch_client_secret"]; -async function getValue(name: values): Promise { +function getValue(name: values): string | undefined { try { - const [rows, fields] = await pool.query("SELECT name, value FROM userData where id = ?;", [name]); + const rows = pool.query("SELECT name, value FROM userData where id = ?;").as(UserD).all(name); if (rows.length > 0) return rows[0].value; } @@ -27,10 +26,10 @@ async function getValue(name: values): Promise { return; } -async function updateValue(name: string, value: string): Promise { +function updateValue(name: string, value: string): number { try { - const [result, fields] = await pool.query("UPDATE userData SET value = ? WHERE name = ?", [value, name]); - return result.affectedRows; + const result = pool.query("UPDATE userData SET value = ? WHERE name = ?").run(value, name); + return result.changes; } catch (err) { console.log(err); @@ -38,9 +37,9 @@ async function updateValue(name: string, value: string): Promise { return 0; } -async function getAll(): Promise { +function getAll(): UserD[] { try { - const [rows, fields] = await pool.query("SELECT name, value FROM userData;"); + const rows = pool.query("SELECT name, value FROM userData;").as(UserD).all(); return rows; } catch (err) { diff --git a/package.json b/package.json index 1954bf3..9f20d02 100644 --- a/package.json +++ b/package.json @@ -2,19 +2,12 @@ "name": "web", "version": "0.0.0", "private": true, - "scripts": { - "start": "bun ./app.ts", - "build_app": "bun build ./app.ts --outfile=bundle.js --target=bun" - }, "dependencies": { "@types/express": "^4.17.21", "@types/morgan": "^1.9.9", - "dotenv": "^16.4.5", "express": "^4.18.2", "hbs": "^4.2.0", "morgan": "~1.9.1", - "mysql2": "^3.10.3", - "chart.js": "^4.4.1", "bun-types": "^1.0.23", "typescript": "^5.3.3" } diff --git a/routes/api/apiRouter.ts b/routes/api/apiRouter.ts index 0882e12..5fd3caa 100644 --- a/routes/api/apiRouter.ts +++ b/routes/api/apiRouter.ts @@ -1,5 +1,4 @@ import express, { type Request, type Response } from "express"; -import checkAuthenticated from 'miscellaneous/checkAuthenticated'; import mediaRouter from 'routes/api/mediaRouter'; const router = express.Router(); diff --git a/routes/user.ts b/routes/user.ts index 9c3e856..b0d951c 100644 --- a/routes/user.ts +++ b/routes/user.ts @@ -1,4 +1,4 @@ -import express, { type Request, type Response } from "express"; +import express from "express"; import userController from 'controllers/userController'; import checkAuthenticated from 'miscellaneous/checkAuthenticated'; diff --git a/sql/base.sql b/sql/base.sql deleted file mode 100644 index da7c359..0000000 --- a/sql/base.sql +++ /dev/null @@ -1,59 +0,0 @@ -CREATE TABLE series ( - id INT NOT NULL AUTO_INCREMENT, - code INT NOT NULL, - title TEXT NOT NULL, - released TEXT NOT NULL, - webImg TEXT NOT NULL, - PRIMARY KEY (id), - UNIQUE code (code) -) ENGINE = InnoDB; - -CREATE TABLE movies ( - id INT NOT NULL AUTO_INCREMENT, - code INT NOT NULL, - title TEXT NOT NULL, - released TEXT NOT NULL, - webImg TEXT NOT NULL, - PRIMARY KEY (id), - UNIQUE code (code) -) ENGINE = InnoDB; - -CREATE TABLE games ( - id INT NOT NULL AUTO_INCREMENT, - code INT NOT NULL, - title TEXT NOT NULL, - released TEXT NOT NULL, - webImg TEXT NOT NULL, - PRIMARY KEY (id), - UNIQUE code (code) -) ENGINE = InnoDB; - -CREATE TABLE userData ( - id INT NOT NULL AUTO_INCREMENT, - name TEXT NOT NULL, - value TEXT NOT NULL, - PRIMARY KEY (id), -) ENGINE = InnoDB; - -INSERT INTO userData (name, value) VALUES ("pass", ""); - -INSERT INTO userData (name, value) VALUES ("omdb_key", ""); - -INSERT INTO userData (name, value) VALUES ("twitch_client_id", ""); - -INSERT INTO - userData (name, value) VALUES ("twitch_client_secret", ""); - -CREATE TABLE bankCardTransaction ( - id INT NOT NULL AUTO_INCREMENT, - day INT NOT NULL, - month INT NOT NULL, - year INT NOT NULL, - amount INT NOT NULL, - type INT NOT NULL, - raw TEXT NOT NULL, - company TEXT NOT NULL, - PRIMARY KEY (id), - INDEX date(day, month, year), - INDEX type (type) -) ENGINE = InnoDB; \ No newline at end of file diff --git a/templ.env b/templ.env deleted file mode 100644 index 50085af..0000000 --- a/templ.env +++ /dev/null @@ -1,5 +0,0 @@ -DBIP='' -DBPort= -DBUser='' -DBPassword='' -DBDatabase='' \ No newline at end of file