Remove list app
6
.gitignore
vendored
@@ -1,10 +1,4 @@
|
|||||||
node_modules/
|
|
||||||
UpImage/
|
|
||||||
archive/
|
|
||||||
public/
|
public/
|
||||||
.vscode/
|
.vscode/
|
||||||
package-lock.json
|
|
||||||
bundle.js
|
|
||||||
*.sqlite
|
|
||||||
output/
|
output/
|
||||||
resources/
|
resources/
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
|
# This is my personal website
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
```
|
```
|
||||||
bun run build.ts
|
hugo
|
||||||
bun run build_app
|
|
||||||
```
|
```
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import express from "express";
|
|
||||||
|
|
||||||
const hostname = '127.0.0.1';
|
|
||||||
const httpPort = 4080;
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
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('public'));
|
|
||||||
|
|
||||||
import mainRouter from './routes/main';
|
|
||||||
import apiRouter from './routes/api/apiRouter';
|
|
||||||
|
|
||||||
app.use('/', mainRouter);
|
|
||||||
app.use('/api', apiRouter);
|
|
||||||
|
|
||||||
app.listen(httpPort, () => {
|
|
||||||
console.log(`Server running at http://${hostname}:${httpPort}/`);
|
|
||||||
});
|
|
||||||
|
|
||||||
import mediaController from "./controllers/mediaController";
|
|
||||||
|
|
||||||
await mediaController.checkImages();
|
|
||||||
@@ -1,246 +0,0 @@
|
|||||||
import { type Request, type Response } from "express";
|
|
||||||
import UserModel, { values } from '../models/userModel';
|
|
||||||
import MediaModel, { Table, Media } from '../models/mediaModel';
|
|
||||||
import mediaModel from "../models/mediaModel";
|
|
||||||
|
|
||||||
interface omdbRes {
|
|
||||||
Title: string,
|
|
||||||
Released: string,
|
|
||||||
Response: string,
|
|
||||||
Poster: string,
|
|
||||||
Type: string,
|
|
||||||
imdbID: string,
|
|
||||||
Year: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromStringToTable(value: string): (Table | undefined) {
|
|
||||||
if (value.localeCompare("games") == 0) return Table.games;
|
|
||||||
if (value.localeCompare("movies") == 0) return Table.movies;
|
|
||||||
if (value.localeCompare("series") == 0) return Table.series;
|
|
||||||
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.poster);
|
|
||||||
|
|
||||||
// Check if the request was successful
|
|
||||||
if (!response.ok) {
|
|
||||||
console.log("fetch image error");
|
|
||||||
console.log(mData.title);
|
|
||||||
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 = UserModel.getValue(values.omdb_key);
|
|
||||||
|
|
||||||
if (!omdb_key) {
|
|
||||||
return res.status(500).json({ message: 'Error when creating media' });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
const uri = `http://www.omdbapi.com/?i=${mediaCode}&apikey=${omdb_key}`;
|
|
||||||
const mJson = await fetch(uri);
|
|
||||||
const mData: omdbRes = await mJson.json();
|
|
||||||
|
|
||||||
if (mData.Response == 'False') {
|
|
||||||
return res.status(404).json({ message: 'wrong code' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const media: Media = {
|
|
||||||
id: 0,
|
|
||||||
code: mData.imdbID,
|
|
||||||
title: mData.Title,
|
|
||||||
released: mData.Released,
|
|
||||||
webImg: "",
|
|
||||||
poster: mData.Poster,
|
|
||||||
year: mData.Year
|
|
||||||
};
|
|
||||||
|
|
||||||
var tableType = Table.series;
|
|
||||||
|
|
||||||
if (mData.Type.localeCompare("movie") == 0) {
|
|
||||||
tableType = Table.movies;
|
|
||||||
}
|
|
||||||
|
|
||||||
const found = MediaModel.findOne(tableType, mediaCode);
|
|
||||||
if (found.length != 0) {
|
|
||||||
res.status(409).json({ message: 'Media already exists' });
|
|
||||||
await downloadImage(media, tableType);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const savedMedia = MediaModel.save(tableType, mData.imdbID, mData.Title, mData.Released, "", mData.Poster, mData.Year);
|
|
||||||
await downloadImage(media, tableType);
|
|
||||||
|
|
||||||
res.status(201).json(media);
|
|
||||||
} catch (err) {
|
|
||||||
return res.status(500).json({ message: 'Error when creating media' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createGame(req: Request, res: Response) {
|
|
||||||
var gameCode = req.body.code;
|
|
||||||
|
|
||||||
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 = MediaModel.findOne(Table.games, gameCode);
|
|
||||||
if (gameFound) {
|
|
||||||
return res.status(409).json({ message: 'Game already exists' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const uri = "https://id.twitch.tv/oauth2/token?client_id=" + twitch_client_id + "&client_secret=" + twitch_client_secret + "&grant_type=client_credentials";
|
|
||||||
var response = await fetch(uri, { method: 'POST' });
|
|
||||||
const mData = await response.json();
|
|
||||||
|
|
||||||
const mheaders: HeadersInit = {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Client-ID': twitch_client_id,
|
|
||||||
'Authorization': 'Bearer ' + mData.access_token
|
|
||||||
}
|
|
||||||
|
|
||||||
gameCode = parseInt(gameCode)
|
|
||||||
|
|
||||||
response = await fetch(
|
|
||||||
"https://api.igdb.com/v4/games",
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: mheaders,
|
|
||||||
body: `fields name, first_release_date; where id = ${gameCode};`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const gameData = await response.json()
|
|
||||||
if (gameData.length == 0) {
|
|
||||||
return res.status(404).json({ message: 'wrong code' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const date = new Date(gameData[0].first_release_date * 1000);
|
|
||||||
const options: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'short', year: 'numeric' }
|
|
||||||
const dateStr = date.toLocaleDateString(undefined, options);
|
|
||||||
|
|
||||||
|
|
||||||
response = await fetch(
|
|
||||||
"https://api.igdb.com/v4/covers",
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: mheaders,
|
|
||||||
body: `fields image_id; where game = ${gameCode};`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const coverData = await response.json()
|
|
||||||
const game: Media = {
|
|
||||||
id: 0,
|
|
||||||
code: gameCode,
|
|
||||||
title: gameData[0].name,
|
|
||||||
released: dateStr,
|
|
||||||
webImg: "",
|
|
||||||
poster: `https://images.igdb.com/igdb/image/upload/t_cover_big/${coverData[0].image_id}.jpg`,
|
|
||||||
year: date.getFullYear().toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const savedGame = MediaModel.save(Table.games, game.code, game.title, game.released, game.webImg, game.poster, game.year);
|
|
||||||
await downloadImage(game, Table.games);
|
|
||||||
return res.status(201).json(game);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
|
|
||||||
return res.status(500).json({ message: 'Error when creating game', error: error });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function list(req: Request, res: Response) {
|
|
||||||
const mediaTable = fromStringToTable(req.params.mediaType);
|
|
||||||
if (!mediaTable) {
|
|
||||||
return res.status(500).json({
|
|
||||||
message: 'Error when getting media.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const media = MediaModel.find(mediaTable);
|
|
||||||
return res.json(media);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function create(req: Request, res: Response) {
|
|
||||||
const mediaCode: string = req.body.code;
|
|
||||||
if (mediaCode.startsWith("t")) {
|
|
||||||
return await createMed(req, res);
|
|
||||||
} else {
|
|
||||||
return await createGame(req, res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function remove(req: Request, res: Response) {
|
|
||||||
const mediaTable = fromStringToTable(req.params.mediaType);
|
|
||||||
if (!mediaTable) {
|
|
||||||
return res.status(500).json({
|
|
||||||
message: 'Error when deleting the media.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const code = req.body.code;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const mediaTable = req.baseUrl.includes('movies') ? Table.movies : Table.series;
|
|
||||||
const media = MediaModel.findOneAndDelete(mediaTable, code);
|
|
||||||
if (!media) {
|
|
||||||
return res.status(404).json({ message: 'No such media' });
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(204).json();
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
return res.status(500).json({ message: 'Error when deleting the media.' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkImages() {
|
|
||||||
await checkTableImages(Table.games);
|
|
||||||
await checkTableImages(Table.movies);
|
|
||||||
await checkTableImages(Table.series);
|
|
||||||
}
|
|
||||||
function delay(time:number) {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, time));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkTableImages(table: Table) {
|
|
||||||
const list = mediaModel.find(table);
|
|
||||||
|
|
||||||
for (const element of list) {
|
|
||||||
|
|
||||||
const path = "./public/" + element.webImg;
|
|
||||||
const f = Bun.file(path);
|
|
||||||
const exists = await f.exists();
|
|
||||||
if (!exists){
|
|
||||||
console.log(element.title);
|
|
||||||
await downloadImage(element, table);
|
|
||||||
await delay(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
list,
|
|
||||||
create,
|
|
||||||
remove,
|
|
||||||
checkImages
|
|
||||||
};
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import { type Request, type Response } from "express";
|
|
||||||
import UserModel, { values } from '../models/userModel';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
|
|
||||||
render: function (req: Request, res: Response) {
|
|
||||||
res.render('user', { keys: UserModel.namesOfValues });
|
|
||||||
},
|
|
||||||
|
|
||||||
create: function (req: Request, res: Response) {
|
|
||||||
|
|
||||||
const reqPassword: string = req.body.reqPassword;
|
|
||||||
if (!reqPassword) return res.render('user', { keys: UserModel.namesOfValues });
|
|
||||||
|
|
||||||
const password = UserModel.getValue(values.pass);
|
|
||||||
|
|
||||||
// if no password in db save reqPassword
|
|
||||||
if (!password) {
|
|
||||||
const affectedRows = UserModel.updateValue("pass", reqPassword);
|
|
||||||
if (affectedRows > 0) {
|
|
||||||
return res.redirect('/list');
|
|
||||||
}
|
|
||||||
return res.render('user', { keys: UserModel.namesOfValues });
|
|
||||||
}
|
|
||||||
// check if passwords equal
|
|
||||||
if (password != reqPassword) {
|
|
||||||
return res.render('user', { keys: UserModel.namesOfValues });
|
|
||||||
}
|
|
||||||
|
|
||||||
// update
|
|
||||||
const name: string = req.body.name;
|
|
||||||
const value: string = req.body.value;
|
|
||||||
|
|
||||||
if (!name || !value) {
|
|
||||||
return res.render('user', { keys: UserModel.namesOfValues });
|
|
||||||
}
|
|
||||||
|
|
||||||
const affectedRows = UserModel.updateValue(name, value);
|
|
||||||
if (affectedRows == 0) {
|
|
||||||
return res.render('user', { keys: UserModel.namesOfValues });
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.redirect('/list');
|
|
||||||
},
|
|
||||||
|
|
||||||
get: function (req: Request, res: Response) {
|
|
||||||
const usersFound = UserModel.getAll();
|
|
||||||
return res.status(200).json(usersFound);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { type NextFunction, type Request, type Response } from "express";
|
|
||||||
import userModel, { values } from 'backend/models/userModel';
|
|
||||||
|
|
||||||
function checkAuthenticated(req: Request, res: Response, next: NextFunction) {
|
|
||||||
const pass = req.body.pass;
|
|
||||||
const password = userModel.getValue(values.pass);
|
|
||||||
if (pass && password) {
|
|
||||||
if (pass == password) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.status(500).json({ message: 'Error when getting transactions.' });
|
|
||||||
}
|
|
||||||
|
|
||||||
export default checkAuthenticated;
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import { Database } from "bun:sqlite";
|
|
||||||
|
|
||||||
const pool = new Database("mydb.sqlite", { strict: true });
|
|
||||||
|
|
||||||
pool.run(`
|
|
||||||
CREATE TABLE IF NOT EXISTS series (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
code TEXT NOT NULL,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
released TEXT NOT NULL,
|
|
||||||
webImg TEXT NOT NULL,
|
|
||||||
poster TEXT NOT NULL,
|
|
||||||
year TEXT NOT NULL
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
pool.run(`
|
|
||||||
CREATE TABLE IF NOT EXISTS movies (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
code TEXT NOT NULL,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
released TEXT NOT NULL,
|
|
||||||
webImg TEXT NOT NULL,
|
|
||||||
poster TEXT NOT NULL,
|
|
||||||
year TEXT NOT NULL
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
pool.run(`
|
|
||||||
CREATE TABLE IF NOT EXISTS games (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
code TEXT NOT NULL,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
released TEXT NOT NULL,
|
|
||||||
webImg TEXT NOT NULL,
|
|
||||||
poster TEXT NOT NULL,
|
|
||||||
year TEXT NOT NULL
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
pool.run(`
|
|
||||||
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.run(`
|
|
||||||
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", "");
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
inset_keys();
|
|
||||||
|
|
||||||
export default pool;
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import pool from 'backend/miscellaneous/db'
|
|
||||||
|
|
||||||
|
|
||||||
export class Media {
|
|
||||||
id!: number;
|
|
||||||
code!: string
|
|
||||||
title!: string;
|
|
||||||
released!: string;
|
|
||||||
webImg!: string;
|
|
||||||
poster!: string;
|
|
||||||
year!: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Table {
|
|
||||||
movies = "movies",
|
|
||||||
series = "series",
|
|
||||||
games = "games",
|
|
||||||
}
|
|
||||||
|
|
||||||
function save(table: Table, code: string, title: string, released:string, webImg:string, poster: string, year: string): number {
|
|
||||||
try {
|
|
||||||
const sql = "INSERT INTO " + table + " (code, title, released, webImg, poster, year) VALUES (?,?,?,?,?,?)";
|
|
||||||
|
|
||||||
const result = pool.query(sql).run(code, title, released, webImg, poster, year);
|
|
||||||
return result.changes;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateWebImg(table: Table, code: string, webImg: string): number {
|
|
||||||
try {
|
|
||||||
const sql = "UPDATE " + table + " SET webImg = ? WHERE code = ?;";
|
|
||||||
const result = pool.query(sql).run(webImg, code);
|
|
||||||
return result.changes;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findOneAndDelete(table: Table, code: string): number {
|
|
||||||
try {
|
|
||||||
const result = pool.query("DELETE FROM " + table + " WHERE code = ?;").run(code);
|
|
||||||
return result.changes;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findOne(table: Table, code: string): Media[] {
|
|
||||||
try {
|
|
||||||
const rows = pool.query("SELECT * FROM " + table + " WHERE code = ?;").as(Media).all(code);
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function find(table: Table): Media[] {
|
|
||||||
try {
|
|
||||||
const rows = pool.query("SELECT * FROM " + table + ";").as(Media).all();
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
save,
|
|
||||||
updateWebImg,
|
|
||||||
findOneAndDelete,
|
|
||||||
findOne,
|
|
||||||
find
|
|
||||||
};
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import pool from 'backend/miscellaneous/db'
|
|
||||||
|
|
||||||
class UserD {
|
|
||||||
name?: string;
|
|
||||||
value?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum values {
|
|
||||||
pass = 1,
|
|
||||||
omdb_key,
|
|
||||||
twitch_client_id,
|
|
||||||
twitch_client_secret,
|
|
||||||
}
|
|
||||||
|
|
||||||
const namesOfValues: string[] = ["", "pass", "omdb_key", "twitch_client_id", "twitch_client_secret"];
|
|
||||||
|
|
||||||
function getValue(name: values): string | undefined {
|
|
||||||
try {
|
|
||||||
const rows = pool.query("SELECT name, value FROM userData where id = ?;").as(UserD).all(name);
|
|
||||||
if (rows.length > 0)
|
|
||||||
return rows[0].value;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateValue(name: string, value: string): number {
|
|
||||||
try {
|
|
||||||
const result = pool.query("UPDATE userData SET value = ? WHERE name = ?").run(value, name);
|
|
||||||
return result.changes;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAll(): UserD[] {
|
|
||||||
try {
|
|
||||||
const rows = pool.query("SELECT name, value FROM userData;").as(UserD).all();
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
getValue,
|
|
||||||
updateValue,
|
|
||||||
getAll,
|
|
||||||
namesOfValues
|
|
||||||
};
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import express, { type Request, type Response } from "express";
|
|
||||||
import mediaRouter from './mediaRouter';
|
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.use('/media', mediaRouter);
|
|
||||||
|
|
||||||
router.get('/', function (req: Request, res: Response) {
|
|
||||||
res.status(200).json({ message: 'API is working' });
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import express from "express";
|
|
||||||
import mediaController from '../../controllers/mediaController.js';
|
|
||||||
import checkAuthenticated from '../../miscellaneous/checkAuthenticated.js';
|
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.get('/:mediaType', mediaController.list);
|
|
||||||
|
|
||||||
router.post('/:mediaType', checkAuthenticated, mediaController.create);
|
|
||||||
|
|
||||||
router.delete('/:mediaType', checkAuthenticated, mediaController.remove);
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import express, { type Request, type Response } from "express";
|
|
||||||
import userData from '../userKnowledge.json';
|
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
/* GET home page. */
|
|
||||||
router.get('/2_0', function (req: Request, res: Response) {
|
|
||||||
res.render('main/2_0', { userData });
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/cv', function (req: Request, res: Response) {
|
|
||||||
res.render('cv', { userData });
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/1_0', function (req: Request, res: Response) {
|
|
||||||
res.render('main/1_0');
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/list', function (req: Request, res: Response) {
|
|
||||||
res.render('list');
|
|
||||||
});
|
|
||||||
|
|
||||||
//import userRouter from './user';
|
|
||||||
//router.use('/user', userRouter);
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import express from "express";
|
|
||||||
import userController from 'backend/controllers/userController';
|
|
||||||
import checkAuthenticated from 'backend/miscellaneous/checkAuthenticated';
|
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
/* GET home page. */
|
|
||||||
router.get('/', userController.render);
|
|
||||||
|
|
||||||
router.post('/', userController.create);
|
|
||||||
|
|
||||||
router.put('/', checkAuthenticated, userController.get);
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
{
|
|
||||||
"first_name": "Nikola",
|
|
||||||
"last_name": "Petrov",
|
|
||||||
"phone_number": "+38670749506",
|
|
||||||
"occupation": "Student",
|
|
||||||
"birth": "14, November, 2000",
|
|
||||||
"living_location": "Ljubljana, Slovenia",
|
|
||||||
"web_link": "https://petrovv.com",
|
|
||||||
"git_link": "https://git.petrovv.com/explore",
|
|
||||||
"email": "nikola@petrovv.com",
|
|
||||||
"instagram_handle":"@nikolainsta7",
|
|
||||||
"instagram_link":"https://www.instagram.com/nikolainsta7",
|
|
||||||
"about_me": [
|
|
||||||
"I am Nikola, currently pursuing my studies at the Faculty of Electrical Engineering and Computer Science (FERI) in Maribor. My academic journey is largely driven by my interest in application and web development. I find the process of creating functional and user-friendly digital solutions both challenging and rewarding. This field allows me to blend creativity with technical skills, which I find particularly engaging.",
|
|
||||||
"Recently, I have developed an interest in the game of Go. The strategic depth and complexity of the game have captivated my attention, providing a stimulating mental exercise. Additionally, I have started exploring photography. Capturing moments and expressing visual stories through a lens has become a newfound passion, offering a different kind of creative outlet that complements my technical pursuits."
|
|
||||||
],
|
|
||||||
"project": [
|
|
||||||
{
|
|
||||||
"img": "/images/projects/password_manager.jpeg",
|
|
||||||
"title": "Password manager",
|
|
||||||
"des": "CLI app",
|
|
||||||
"link": "https://git.petrovv.com/nikola/password_manager"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"img": "/images/projects/list.jpeg",
|
|
||||||
"title": "My watch/game list",
|
|
||||||
"des": "",
|
|
||||||
"link": "/list"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"img": "/images/logo.png",
|
|
||||||
"title": "Server",
|
|
||||||
"des": "Everything running on my server",
|
|
||||||
"link": "https://git.petrovv.com/nikola/personal_website"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"img": "/images/projects/projektna_naloga.jpeg",
|
|
||||||
"title": "Highway Tracker",
|
|
||||||
"des": "School project",
|
|
||||||
"link": "https://git.petrovv.com/nikola/school/src/branch/master/projektna_naloga"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"img": "/images/projects/bitshift.jpeg",
|
|
||||||
"title": "BitShifters",
|
|
||||||
"des": "unity",
|
|
||||||
"link": "https://git.petrovv.com/nikola/school/src/branch/master/semester_4/razvoj_programskih_sistemov/bitshifters"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"img": "/images/projects/tetris.jpeg",
|
|
||||||
"title": "Tetris",
|
|
||||||
"des": "WPF",
|
|
||||||
"link": "https://git.petrovv.com/nikola/school/src/branch/master/semester_4/razvoj_programskih_sistemov/tetris"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"experience": [
|
|
||||||
{
|
|
||||||
"title": "HW Developer",
|
|
||||||
"company": "Spica International",
|
|
||||||
"time": "17/03/2025 - 01/08/2025",
|
|
||||||
"des": "Worked on access menegment systems. Programed integrated devices, based on Buildroot using c++ and python web server."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Backend/Frontend",
|
|
||||||
"company": "RRC d.o.o",
|
|
||||||
"time": "01/09/2024 - 31/12/2024",
|
|
||||||
"des": "Worked on goverment websites for collage enrolment and student dorm requests."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Developer",
|
|
||||||
"company": "RRC d.o.o",
|
|
||||||
"time": "18/03/2024 - 31/05/2024",
|
|
||||||
"des": "Student practicum. Backend in java with frontend in ext JS and jQuery."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Developer/IT",
|
|
||||||
"company": "LightAct",
|
|
||||||
"time": "01/07/2022 - 01/09/2022",
|
|
||||||
"des": "I helped maintaining data base, worked on the application (integrated a capture card and IP camera), assembled new server rack, installed new UTP/power connectors in the office."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Mentor",
|
|
||||||
"company": "Institute 404",
|
|
||||||
"time": "08/06/2020 - 19/06/2020",
|
|
||||||
"des": "I helped primary school children with their projects with soldering, laser cutting, and building."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Maintenance technician",
|
|
||||||
"company": "Hella Saturnos d.o.o.",
|
|
||||||
"time": "04/09/2018 - 18/01/2019",
|
|
||||||
"des": "I maintained and repaired machines from plastic presses to personal stations."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Maintenance technician",
|
|
||||||
"company": "Best Western Premier Hotel Slon",
|
|
||||||
"time": "01/03/2018 - 04/05/2018",
|
|
||||||
"des": "I helped with setting up the conference/event rooms. I helped customers and fixed problems like replacing light bulbs, wall sockets, hair-dryers."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"education": [
|
|
||||||
{
|
|
||||||
"title": "(FERI) Faculty of Electrical Engineering and Computer Science, University of Maribor",
|
|
||||||
"time": "01/10/2021 - CURRENT",
|
|
||||||
"des": "Graduate engineer of computer science and information technology."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "(SSTS Siska) Secondary school of technical professions siska",
|
|
||||||
"time": "01/09/2016 - 07/07/2021",
|
|
||||||
"des": "Electrotechnician."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en" data-bs-theme="dark">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="shortcut icon" href="/images/logo.ico" type="image/x-icon">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<title>CV</title>
|
|
||||||
<script src="https://kit.fontawesome.com/cb81440751.js" crossorigin="anonymous"></script>
|
|
||||||
<!-- Import stylesheets in CSS3 -->
|
|
||||||
<link rel="stylesheet" href="/assets/cv/stylesheets/style.css" type="text/css">
|
|
||||||
|
|
||||||
<!-- HTML2PDF.js -->
|
|
||||||
<!-- Version 0.9.3 -->
|
|
||||||
<script defer src="/assets/cv/javascripts/html2pdf.v0.9.3.bundle.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Main Javascript file -->
|
|
||||||
<script defer src="/assets/cv/javascripts/main.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<header class="l-header" id="header">
|
|
||||||
<!-- Nav menu -->
|
|
||||||
<nav class="nav bd-container">
|
|
||||||
<a href="#" class="nav_logo">Nikola Petrov</a>
|
|
||||||
<div class="nav_menu" id="nav-menu">
|
|
||||||
<ul class="nav_list">
|
|
||||||
|
|
||||||
<li class="nav_item">
|
|
||||||
<a href="#home" class="nav_link active-link">
|
|
||||||
<i class="fa-solid fa-house nav_icon"></i>Home
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav_item">
|
|
||||||
<a href="#experience" class="nav_link">
|
|
||||||
<i class="fa-solid fa-briefcase nav_icon"></i>Experiences
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<li class="nav_item">
|
|
||||||
<a href="#education" class="nav_link">
|
|
||||||
<i class="fa-solid fa-book-bookmark nav_icon"></i>Education
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav_toggle" id="nav-toggle">
|
|
||||||
<i class="fa-solid fa-bars"></i>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
{{#with userData}}
|
|
||||||
<main class="l-main bd-container">
|
|
||||||
<!-- Resume CV -->
|
|
||||||
<div class="resume" id="area-cv">
|
|
||||||
<div class="resume_left">
|
|
||||||
|
|
||||||
<!-- HOME -->
|
|
||||||
<section class="home" id="home">
|
|
||||||
<div class="home_container section bd-grid">
|
|
||||||
|
|
||||||
<div class="home_data bd-grid">
|
|
||||||
<img src="/images/Jaz.jpg" alt="Icon picture" class="home_img" id="home-img">
|
|
||||||
<!--<img alt="Icon picture" class="home_img" id="home-img">-->
|
|
||||||
<h1 class="home_title">{{first_name}} {{last_name}}</h1>
|
|
||||||
<h3 class="home_profession">{{occupation}}</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="home_address bd-grid">
|
|
||||||
<span class="home_information">
|
|
||||||
<i class="fa-solid fa-location-dot home_icon"></i>{{living_location}}
|
|
||||||
</span>
|
|
||||||
<span class="home_information">
|
|
||||||
<i class="fa-solid fa-calendar-days home_icon"></i>{{birth}}
|
|
||||||
</span>
|
|
||||||
<span class="home_information">
|
|
||||||
<a href="mailto:{{email}}" class="home_link">
|
|
||||||
<i class="fa-solid fa-envelope home_icon"></i>{{email}}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
<span class="home_information">
|
|
||||||
<a href="tel:{{phone_number}}" class="home_link">
|
|
||||||
<i class="fa-solid fa-phone home_icon"></i>{{phone_number}}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
<span class="home_information">
|
|
||||||
<a href="{{web_link}}" class="home_link">
|
|
||||||
<i class="fa-solid fa-globe home_icon"></i>{{web_link}}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Theme change button -->
|
|
||||||
<i class="fa-solid fa-moon change-theme" title="Theme" id="theme-button"></i>
|
|
||||||
|
|
||||||
<!-- Button to generate and download the pdf. Available for desktop. -->
|
|
||||||
<i class="fa-solid fa-download generate-pdf" title="Generate PDF" id="resume-button"></i>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- SOCIAL -->
|
|
||||||
<section class="social section">
|
|
||||||
<h2 class="section-title">Social</h2>
|
|
||||||
|
|
||||||
<div class="social_container bd-grid">
|
|
||||||
<a href="{{git_link}}" target="_blank" class="social_link">
|
|
||||||
<i class="fa-brands fa-git social_icon"></i>{{git_link}}
|
|
||||||
</a>
|
|
||||||
<a href="{{instagram_link}}" target="_blank" class="social_link">
|
|
||||||
<i class="fa-brands fa-instagram social_icon"></i>{{instagram_handle}}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="resume_right">
|
|
||||||
|
|
||||||
<!-- EXPERIENCE -->
|
|
||||||
<section class="experience section" id="experience">
|
|
||||||
<h2 class="section-title">Experience</h2>
|
|
||||||
|
|
||||||
<div class="experience_container bd-grid">
|
|
||||||
{{#each experience}}
|
|
||||||
<div class="experience_content">
|
|
||||||
<div class="experience_time">
|
|
||||||
<span class="experience_rounder"></span>
|
|
||||||
{{#unless @last}}
|
|
||||||
<span class="experience_line"></span>
|
|
||||||
{{/unless}}
|
|
||||||
</div>
|
|
||||||
<div class="experience_data bd-grid">
|
|
||||||
<h3 class="experience_title">{{title}}</h3>
|
|
||||||
<span class="experience_company">{{company}}</span>
|
|
||||||
<span class="experience_year">{{time}}</span>
|
|
||||||
<p class="experience_description">
|
|
||||||
{{des}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- EDUCATION -->
|
|
||||||
<section class="education section" id="education">
|
|
||||||
<h2 class="section-title">Education</h2>
|
|
||||||
|
|
||||||
<div class="education_container bd-grid">
|
|
||||||
|
|
||||||
{{#each education}}
|
|
||||||
<div class="education_content">
|
|
||||||
<div class="education_time">
|
|
||||||
<span class="education_rounder"></span>
|
|
||||||
{{#unless @last}}
|
|
||||||
<span class="education_line"></span>
|
|
||||||
{{/unless}}
|
|
||||||
</div>
|
|
||||||
<div class="education_data bd-grid">
|
|
||||||
<h3 class="education_title">{{des}}</h3>
|
|
||||||
<span class="education_studies">{{title}}</span>
|
|
||||||
<span class="education_year">{{time}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
{{/with}}
|
|
||||||
<a href="#" class="scrolltop" id="scroll-top">
|
|
||||||
<i class="fa-solid fa-arrow-up scrolltop_icon"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en" data-bs-theme="dark">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="shortcut icon" href="/images/logo.ico" type="image/x-icon">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
|
||||||
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
|
||||||
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
|
|
||||||
<title>List</title>
|
|
||||||
<script src="/assets/build/list.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
|
||||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
|
||||||
<li class="nav-item">
|
|
||||||
<button class="nav-link active" id="movieButton">Movies</button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<button class="nav-link" id="seriesButton">Series</button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<button class="nav-link" id="gameButton">Games</button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item dropdown">
|
|
||||||
<button class="nav-link dropdown-toggle" id="navbarDropdown" role="button" data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false">
|
|
||||||
Sort
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
|
||||||
<li><button class="dropdown-item" id="titleButton">Title</button></li>
|
|
||||||
<li><button class="dropdown-item" id="yearButton">Year</button></li>
|
|
||||||
<li><button class="dropdown-item" id="idButton">Added</button></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<button class="nav-link" id="editButton">Edit</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<form class="d-flex" action="" id="myform">
|
|
||||||
<input class="form-control me-2" type="password" name="password" id="pass" placeholder="password">
|
|
||||||
<input class="form-control me-2" type="text" name="input_id" id="input_id" placeholder="id">
|
|
||||||
<input class="btn btn-outline-success" type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="py-5 bg-body-tertiary mt-3">
|
|
||||||
<div class="container" id="root">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en" data-bs-theme="dark">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="shortcut icon" href="/images/logo.ico" type="image/x-icon">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
|
||||||
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
|
||||||
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
|
|
||||||
<title>User</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<form action="/user" method="post">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Name</label>
|
|
||||||
<select name="name" class="form-select">
|
|
||||||
{{#each keys}}
|
|
||||||
<option value="{{this}}">{{this}}</option>
|
|
||||||
{{/each}}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Value</label>
|
|
||||||
<input type="text" class="form-control" name="value">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Password</label>
|
|
||||||
<input type="password" class="form-control" name="reqPassword" id="password">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Add</button>
|
|
||||||
</form>
|
|
||||||
<br>
|
|
||||||
<div class="btn btn-primary" id="get" onclick="getUser()"> Get</div>
|
|
||||||
<br>
|
|
||||||
<div id="text"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
async function getUser() {
|
|
||||||
const password = document.getElementById('password').value;
|
|
||||||
const response = await fetch('/user', {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ "pass": password }),
|
|
||||||
})
|
|
||||||
const data = await response.json();
|
|
||||||
document.getElementById('text').textContent = JSON.stringify(data, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
7
build.sh
@@ -1,7 +0,0 @@
|
|||||||
hugo -d output/public --minify
|
|
||||||
bun build ./backend/app.ts --outfile=output/app.js --target=bun --minify
|
|
||||||
bun build ./frontend/list/list.tsx --outfile=output/public/assets/build/list.js --minify
|
|
||||||
|
|
||||||
cp -r backend/views/ output/
|
|
||||||
|
|
||||||
rm -rf resources
|
|
||||||
221
bun.lock
@@ -1,221 +0,0 @@
|
|||||||
{
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"configVersion": 0,
|
|
||||||
"workspaces": {
|
|
||||||
"": {
|
|
||||||
"name": "web",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/express": "^5.0.3",
|
|
||||||
"@types/morgan": "^1.9.10",
|
|
||||||
"bun-types": "^1.2.22",
|
|
||||||
"express": "^5.1.0",
|
|
||||||
"hbs": "^4.2.0",
|
|
||||||
"morgan": "~1.10.1",
|
|
||||||
"typescript": "^5.9.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="],
|
|
||||||
|
|
||||||
"@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
|
|
||||||
|
|
||||||
"@types/express": ["@types/express@5.0.3", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "*" } }, "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw=="],
|
|
||||||
|
|
||||||
"@types/express-serve-static-core": ["@types/express-serve-static-core@5.0.7", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ=="],
|
|
||||||
|
|
||||||
"@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="],
|
|
||||||
|
|
||||||
"@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="],
|
|
||||||
|
|
||||||
"@types/morgan": ["@types/morgan@1.9.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA=="],
|
|
||||||
|
|
||||||
"@types/node": ["@types/node@24.5.1", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-/SQdmUP2xa+1rdx7VwB9yPq8PaKej8TD5cQ+XfKDPWWC+VDJU4rvVVagXqKUzhKjtFoNA8rXDJAkCxQPAe00+Q=="],
|
|
||||||
|
|
||||||
"@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="],
|
|
||||||
|
|
||||||
"@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="],
|
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
|
|
||||||
|
|
||||||
"@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="],
|
|
||||||
|
|
||||||
"@types/serve-static": ["@types/serve-static@1.15.8", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "*" } }, "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg=="],
|
|
||||||
|
|
||||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
|
||||||
|
|
||||||
"basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="],
|
|
||||||
|
|
||||||
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
|
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="],
|
|
||||||
|
|
||||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
|
||||||
|
|
||||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
|
||||||
|
|
||||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
|
||||||
|
|
||||||
"content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="],
|
|
||||||
|
|
||||||
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
|
||||||
|
|
||||||
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
|
||||||
|
|
||||||
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
|
||||||
|
|
||||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
||||||
|
|
||||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
|
||||||
|
|
||||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
|
||||||
|
|
||||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
|
||||||
|
|
||||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
|
||||||
|
|
||||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
|
||||||
|
|
||||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
|
||||||
|
|
||||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
|
||||||
|
|
||||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
|
||||||
|
|
||||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
|
||||||
|
|
||||||
"express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
|
|
||||||
|
|
||||||
"finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="],
|
|
||||||
|
|
||||||
"foreachasync": ["foreachasync@3.0.0", "", {}, "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw=="],
|
|
||||||
|
|
||||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
|
||||||
|
|
||||||
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
|
||||||
|
|
||||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
|
||||||
|
|
||||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
|
||||||
|
|
||||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
|
||||||
|
|
||||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
|
||||||
|
|
||||||
"handlebars": ["handlebars@4.7.7", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.0", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA=="],
|
|
||||||
|
|
||||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
|
||||||
|
|
||||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
|
||||||
|
|
||||||
"hbs": ["hbs@4.2.0", "", { "dependencies": { "handlebars": "4.7.7", "walk": "2.3.15" } }, "sha512-dQwHnrfWlTk5PvG9+a45GYpg0VpX47ryKF8dULVd6DtwOE6TEcYQXQ5QM6nyOx/h7v3bvEQbdn19EDAcfUAgZg=="],
|
|
||||||
|
|
||||||
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
|
||||||
|
|
||||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
|
||||||
|
|
||||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
|
||||||
|
|
||||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
|
||||||
|
|
||||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
|
||||||
|
|
||||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
|
||||||
|
|
||||||
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
|
||||||
|
|
||||||
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
|
||||||
|
|
||||||
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
|
||||||
|
|
||||||
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
|
||||||
|
|
||||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
|
||||||
|
|
||||||
"morgan": ["morgan@1.10.1", "", { "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.1.0" } }, "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A=="],
|
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
||||||
|
|
||||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
|
||||||
|
|
||||||
"neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
|
|
||||||
|
|
||||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
|
||||||
|
|
||||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
|
||||||
|
|
||||||
"on-headers": ["on-headers@1.1.0", "", {}, "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="],
|
|
||||||
|
|
||||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
|
||||||
|
|
||||||
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
|
||||||
|
|
||||||
"path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
|
||||||
|
|
||||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
|
||||||
|
|
||||||
"qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
|
||||||
|
|
||||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
|
||||||
|
|
||||||
"raw-body": ["raw-body@3.0.1", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.7.0", "unpipe": "1.0.0" } }, "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA=="],
|
|
||||||
|
|
||||||
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
|
||||||
|
|
||||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
|
||||||
|
|
||||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
|
||||||
|
|
||||||
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
|
|
||||||
|
|
||||||
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
|
|
||||||
|
|
||||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
|
||||||
|
|
||||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
|
||||||
|
|
||||||
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
|
||||||
|
|
||||||
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
|
||||||
|
|
||||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
|
||||||
|
|
||||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
|
||||||
|
|
||||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
|
||||||
|
|
||||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
|
||||||
|
|
||||||
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
|
||||||
|
|
||||||
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
|
|
||||||
|
|
||||||
"undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="],
|
|
||||||
|
|
||||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
|
||||||
|
|
||||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
|
||||||
|
|
||||||
"walk": ["walk@2.3.15", "", { "dependencies": { "foreachasync": "^3.0.0" } }, "sha512-4eRTBZljBfIISK1Vnt69Gvr2w/wc3U6Vtrw7qiN5iqYJPH7LElcYh/iU4XWhdCy2dZqv1ToMyYlybDylfG/5Vg=="],
|
|
||||||
|
|
||||||
"wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
|
|
||||||
|
|
||||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
|
||||||
|
|
||||||
"basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
|
||||||
|
|
||||||
"http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
|
||||||
|
|
||||||
"morgan/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
|
||||||
|
|
||||||
"morgan/on-finished": ["on-finished@2.3.0", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww=="],
|
|
||||||
|
|
||||||
"raw-body/iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="],
|
|
||||||
|
|
||||||
"morgan/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
+++
|
+++
|
||||||
title = "About Me"
|
title = "About Me"
|
||||||
description = "Des"
|
description = "About me page"
|
||||||
date = "2019-02-28"
|
date = "2019-02-28"
|
||||||
author = "Nikola Petrov"
|
author = "Nikola Petrov"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
I’m Nikola, a student at the Faculty of Electrical Engineering and Computer Science (FERI) in Maribor. My academic focus is on application and web development, where I enjoy creating functional, user-friendly digital solutions. This field allows me to combine technical skills with creativity, which I find both challenging and fulfilling.
|
I am Nikola, I have studied at the Faculty of Electrical Engineering and Computer Science (FERI) in Maribor. My focus is on application and web development, where I enjoy creating functional, user-friendly digital solutions. This field allows me to combine technical skills with creativity, which I find both challenging and fulfilling.
|
||||||
|
|
||||||
Beyond my studies, I’ve recently developed a passion for the game of Go. Its strategic depth and complexity provide a stimulating mental challenge that I find incredibly engaging.
|
Beyond my studies, I have developed a passion for the game of Go. Its strategic depth and complexity provide a stimulating mental challenge that I find incredibly engaging.
|
||||||
|
|
||||||
**Interests**
|
**Interests**
|
||||||
- **Backend Programming**: Specializing in server-side development, I work on data storage, processing, and business logic to power web and mobile applications.
|
- **Backend Programming**: Specializing in server-side development, I work on data storage, processing, and business logic to power web and mobile applications.
|
||||||
- **Embedded Systems**: I’m fascinated by specialized computing devices integrated into everyday products, designed to perform dedicated functions under real-time constraints.
|
- **Embedded Systems**: I am fascinated by specialized computing devices integrated into everyday products, designed to perform dedicated functions under real-time constraints.
|
||||||
- **Application Programming**: I enjoy writing code to build software applications that solve specific problems or perform targeted tasks.
|
- **Application Programming**: I enjoy writing code to build software applications that solve specific problems or perform targeted tasks.
|
||||||
|
|||||||
4
dev.ts
@@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
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"]);
|
|
||||||
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/// <reference path="./utils/element-types.d.ts" />
|
|
||||||
/// <reference path="./utils/events.d.ts" />
|
|
||||||
/// <reference path="./utils/intrinsic-elements.d.ts" />
|
|
||||||
|
|
||||||
export interface Children {
|
|
||||||
children?: AttributeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CustomElementHandler {
|
|
||||||
(attributes: Attributes, contents: (string | HTMLElement)[]): HTMLElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Attributes {
|
|
||||||
[key: string]: AttributeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function createElement(
|
|
||||||
tag: string | CustomElementHandler,
|
|
||||||
attrs: Attributes & Children | undefined = {},
|
|
||||||
...children: (string | HTMLElement)[]
|
|
||||||
): HTMLElement {
|
|
||||||
|
|
||||||
if (typeof tag === "function") {
|
|
||||||
if (attrs == null) {
|
|
||||||
attrs = { num: 0 };
|
|
||||||
}
|
|
||||||
if (children == null) {
|
|
||||||
children = [""];
|
|
||||||
}
|
|
||||||
return tag(attrs, children);
|
|
||||||
}
|
|
||||||
|
|
||||||
const retElement = document.createElement(tag);
|
|
||||||
|
|
||||||
for (let name in attrs) {
|
|
||||||
if (name && attrs.hasOwnProperty(name)) {
|
|
||||||
|
|
||||||
let value = attrs[name];
|
|
||||||
if (typeof value === "number") {
|
|
||||||
retElement.setAttribute(name, value.toString());
|
|
||||||
} else if (typeof value === "function") {
|
|
||||||
retElement.addEventListener(name.slice(2), value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
retElement.setAttribute(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 2; i < arguments.length; i++) {
|
|
||||||
let child = arguments[i];
|
|
||||||
|
|
||||||
// check if child is a HTMLElement
|
|
||||||
if (child.nodeType != undefined) {
|
|
||||||
retElement.appendChild(child);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (child instanceof Array) {
|
|
||||||
for (let j = 0; j < child.length; j++) {
|
|
||||||
if (child[j].nodeType != undefined) retElement.appendChild(child[j]);
|
|
||||||
else retElement.appendChild(document.createTextNode(child[j].toString()));
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// child is a string
|
|
||||||
retElement.appendChild(document.createTextNode(child.toString()));
|
|
||||||
}
|
|
||||||
return retElement;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import type { Attributes } from "frontend/elementcreate";
|
|
||||||
import * as elements from "frontend/elementcreate";
|
|
||||||
|
|
||||||
function MediaElement(attributes: Attributes, contents: string[]) {
|
|
||||||
const ret = <div class="col media-element" id={attributes['id']}>
|
|
||||||
<div class='card shadow-sm'>
|
|
||||||
<img class='card-img-top' src={attributes['webImg']} width='100%' onerror={attributes['imageError']} loading="lazy" style="min-height: 100px;"></img>
|
|
||||||
<div class='card-body'>
|
|
||||||
<h5 class='card-title'>{attributes['title']}</h5>
|
|
||||||
<p class='card-text'>{attributes['released']}</p>
|
|
||||||
<div class="d-none justify-content-between align-items-center">
|
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick={attributes['fun']}>delete</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function MyHeader(attributes: Attributes, contents: string[]) {
|
|
||||||
return <div class="row">
|
|
||||||
<div class='col'>
|
|
||||||
<h2 class='text-center'>{attributes['title']} {attributes['num'] ? ": " + attributes['num'] : ""}</h2>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function MediaContainer(attributes: Attributes, contents: string[]) {
|
|
||||||
return <div id={attributes['id']} class="row row-cols-2 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-5 g-3">{contents[0]}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { MediaElement, MyHeader, MediaContainer }
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
function splitByTitle(movies: Array<Movie>): { [s: string]: Movie[]; } {
|
|
||||||
const result = movies.reduce((r, a) => {
|
|
||||||
var letter = a.title[0].toUpperCase();
|
|
||||||
if (!isNaN(parseInt(letter))) letter = "#";
|
|
||||||
r[letter] = r[letter] || [];
|
|
||||||
r[letter].push(a);
|
|
||||||
return r;
|
|
||||||
}, Object.create(null));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function splitByYear(movies: Array<Movie>): { [s: string]: Movie[]; } {
|
|
||||||
const result = movies.reduce((r, a) => {
|
|
||||||
const year = new Date(a.released).getFullYear();
|
|
||||||
r[year] = r[year] || [];
|
|
||||||
r[year].push(a);
|
|
||||||
return r;
|
|
||||||
}, Object.create(null));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { splitByTitle, splitByYear };
|
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
import { MediaElement, MyHeader, MediaContainer } from "frontend/list/elements";
|
|
||||||
import { splitByTitle, splitByYear } from "frontend/list/functions";
|
|
||||||
|
|
||||||
import * as elements from "frontend/elementcreate";
|
|
||||||
|
|
||||||
var sortType = 0;
|
|
||||||
var listType = 0;
|
|
||||||
|
|
||||||
const sortTypeTitle = 0;
|
|
||||||
const sortTypeYear = 1;
|
|
||||||
const sortTypeId = 2;
|
|
||||||
|
|
||||||
const moviesType = 0;
|
|
||||||
const gamesType = 1;
|
|
||||||
const seriesType = 2;
|
|
||||||
|
|
||||||
var listButtons: Array<HTMLElement | null> = [];
|
|
||||||
var sortButtons: Array<HTMLElement | null> = [];
|
|
||||||
|
|
||||||
var root: HTMLElement | null;
|
|
||||||
var editButton: HTMLElement | null;
|
|
||||||
var movieElements: HTMLElement[] = [];
|
|
||||||
|
|
||||||
function getLink(): string {
|
|
||||||
switch (listType) {
|
|
||||||
case moviesType:
|
|
||||||
return "/api/media/movies";
|
|
||||||
case gamesType:
|
|
||||||
return "/api/media/games";
|
|
||||||
case seriesType:
|
|
||||||
return "/api/media/series";
|
|
||||||
}
|
|
||||||
return "/api/media/movies";
|
|
||||||
}
|
|
||||||
|
|
||||||
async function reload() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(getLink());
|
|
||||||
const movies = await response.json();
|
|
||||||
renderMedias(movies);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitMedia(event: SubmitEvent) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const pass = document.getElementById("pass") as HTMLInputElement | null;
|
|
||||||
if (!pass) return;
|
|
||||||
|
|
||||||
const input_id = document.getElementById("input_id") as HTMLInputElement | null;
|
|
||||||
if (!input_id) return;
|
|
||||||
|
|
||||||
|
|
||||||
if (pass.value == "" || input_id.value == "") return;
|
|
||||||
|
|
||||||
|
|
||||||
fetch(getLink(), {
|
|
||||||
body: JSON.stringify({ pass: pass.value, code: input_id.value }),
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
method: "POST"
|
|
||||||
})
|
|
||||||
.then(async (response) => {
|
|
||||||
if (response.status != 201) {
|
|
||||||
const json = await response.json();
|
|
||||||
console.log(json);
|
|
||||||
alert(json.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await reload();
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
input_id.value = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadState() {
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
|
||||||
if (searchParams.has("listType")) {
|
|
||||||
switch (searchParams.get("listType")) {
|
|
||||||
case "movies":
|
|
||||||
listType = moviesType;
|
|
||||||
break;
|
|
||||||
case "series":
|
|
||||||
listType = seriesType;
|
|
||||||
break;
|
|
||||||
case "games":
|
|
||||||
listType = gamesType;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
listType = moviesType;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchParams.has("sortType")) {
|
|
||||||
switch (searchParams.get("sortType")) {
|
|
||||||
case "title":
|
|
||||||
sortType = sortTypeTitle;
|
|
||||||
break;
|
|
||||||
case "year":
|
|
||||||
sortType = sortTypeYear;
|
|
||||||
break;
|
|
||||||
case "id":
|
|
||||||
sortType = sortTypeId;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sortType = sortTypeTitle;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeType(type: number) {
|
|
||||||
listType = type;
|
|
||||||
loadPage();
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
|
||||||
switch (listType) {
|
|
||||||
case moviesType:
|
|
||||||
searchParams.set("listType", "movies");
|
|
||||||
break;
|
|
||||||
case gamesType:
|
|
||||||
searchParams.set("listType", "games");
|
|
||||||
break;
|
|
||||||
case seriesType:
|
|
||||||
searchParams.set("listType", "series");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
history.replaceState({}, '', window.location.pathname + '?' + searchParams.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeSort(type: number) {
|
|
||||||
sortType = type;
|
|
||||||
loadPage();
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
|
||||||
switch (type) {
|
|
||||||
case sortTypeTitle:
|
|
||||||
searchParams.set("sortType", "title");
|
|
||||||
break;
|
|
||||||
case sortTypeYear:
|
|
||||||
searchParams.set("sortType", "year");
|
|
||||||
break;
|
|
||||||
case sortTypeId:
|
|
||||||
searchParams.set("sortType", "id");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
history.replaceState({}, '', window.location.pathname + '?' + searchParams.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function splitBySort(movies: Array<Movie>): { [s: string]: Movie[]; } {
|
|
||||||
switch (sortType) {
|
|
||||||
case sortTypeYear:
|
|
||||||
const sorted = movies.sort((a, b) => {
|
|
||||||
const ay = Date.parse(a.released);
|
|
||||||
const by = Date.parse(b.released);
|
|
||||||
return ay - by;
|
|
||||||
});
|
|
||||||
return splitByYear(sorted);
|
|
||||||
case sortTypeId:
|
|
||||||
movies.sort((a, b) => a.id < b.id ? 1 : -1);
|
|
||||||
return { "added": movies };
|
|
||||||
default:
|
|
||||||
return splitByTitle(movies.sort((a, b) => a.title.localeCompare(b.title)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleEdit() {
|
|
||||||
movieElements.forEach(element => {
|
|
||||||
const div = element.querySelector(".d-none");
|
|
||||||
if (!div) return;
|
|
||||||
div.classList.remove("d-none");
|
|
||||||
div.classList.add("d-flex");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
|
||||||
document.getElementById("myform")?.addEventListener("submit", submitMedia);
|
|
||||||
|
|
||||||
listButtons.push(document.getElementById("movieButton"));
|
|
||||||
listButtons.push(document.getElementById("gameButton"));
|
|
||||||
listButtons.push(document.getElementById("seriesButton"));
|
|
||||||
listButtons.forEach((button, index) => button?.addEventListener("click", () => changeType(index)));
|
|
||||||
|
|
||||||
sortButtons.push(document.getElementById("titleButton"));
|
|
||||||
sortButtons.push(document.getElementById("yearButton"));
|
|
||||||
sortButtons.push(document.getElementById("idButton"));
|
|
||||||
sortButtons.forEach((button, index) => button?.addEventListener("click", () => changeSort(index)));
|
|
||||||
|
|
||||||
editButton = document.getElementById("editButton");
|
|
||||||
editButton?.addEventListener("click", () => toggleEdit());
|
|
||||||
|
|
||||||
loadState();
|
|
||||||
loadPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function loadPage() {
|
|
||||||
|
|
||||||
listButtons.forEach(button => button?.classList.remove("active"));
|
|
||||||
listButtons[listType]?.classList.add("active");
|
|
||||||
|
|
||||||
await reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function removeMedia(evt: Event) {
|
|
||||||
const password = document.getElementById("pass") as HTMLInputElement | null;
|
|
||||||
if (!password) return;
|
|
||||||
if (password.value == "") return;
|
|
||||||
|
|
||||||
let elem = evt.target as HTMLElement | null;
|
|
||||||
|
|
||||||
while (elem && !elem.classList.contains('media-element')) {
|
|
||||||
elem = elem.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!elem) return;
|
|
||||||
const id = elem.id;
|
|
||||||
|
|
||||||
fetch(getLink(), {
|
|
||||||
body: JSON.stringify({ pass: password.value, code: id }),
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
method: "DELETE"
|
|
||||||
})
|
|
||||||
.then(async (response) => {
|
|
||||||
|
|
||||||
if (response.status != 204) {
|
|
||||||
console.log("error");
|
|
||||||
console.log(response.body);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.getElementById(id)?.remove();
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
password.value = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Movie>) {
|
|
||||||
root = document.getElementById('root');
|
|
||||||
if (!root) return;
|
|
||||||
|
|
||||||
root.innerHTML = "";
|
|
||||||
movieElements = [];
|
|
||||||
|
|
||||||
const splitMovies = splitBySort(unsorted_movies);
|
|
||||||
|
|
||||||
let years;
|
|
||||||
if (sortType == sortTypeTitle) {
|
|
||||||
years = Object.keys(splitMovies).sort((a, b) => -b.localeCompare(a));
|
|
||||||
} else {
|
|
||||||
years = Object.keys(splitMovies).sort((a, b) => b.localeCompare(a));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
root.appendChild(<MyHeader title={unsorted_movies.length} />);
|
|
||||||
|
|
||||||
for (const letter of years) {
|
|
||||||
|
|
||||||
const movies = splitMovies[letter];
|
|
||||||
|
|
||||||
const header = <MyHeader title={letter} num={movies.length} />;
|
|
||||||
root.appendChild(header);
|
|
||||||
|
|
||||||
const row =
|
|
||||||
<MediaContainer id={letter}>
|
|
||||||
{movies.map(movie => {
|
|
||||||
const med = <MediaElement webImg={movie.webImg} title={movie.title} released={movie.released} id={movie.code} fun={removeMedia} imageError={onImgError}></MediaElement>;
|
|
||||||
movieElements.push(med);
|
|
||||||
return med;
|
|
||||||
})}
|
|
||||||
</MediaContainer>;
|
|
||||||
|
|
||||||
root.appendChild(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
frontend/list/types.d.ts
vendored
@@ -1,7 +0,0 @@
|
|||||||
interface Movie {
|
|
||||||
title: string;
|
|
||||||
released: string;
|
|
||||||
code: string;
|
|
||||||
webImg: string;
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
1
frontend/utils/attr.d.ts
vendored
@@ -1 +0,0 @@
|
|||||||
type AttributeValue = number | string | EventListener;
|
|
||||||
341
frontend/utils/element-types.d.ts
vendored
@@ -1,341 +0,0 @@
|
|||||||
declare namespace JSX {
|
|
||||||
interface HtmlTag {
|
|
||||||
accesskey?: string;
|
|
||||||
class?: string;
|
|
||||||
contenteditable?: string;
|
|
||||||
dir?: string;
|
|
||||||
hidden?: string | boolean;
|
|
||||||
id?: AttributeValue;
|
|
||||||
role?: string;
|
|
||||||
lang?: string;
|
|
||||||
draggable?: string | boolean;
|
|
||||||
spellcheck?: string | boolean;
|
|
||||||
style?: string;
|
|
||||||
tabindex?: string;
|
|
||||||
title?: string;
|
|
||||||
translate?: string | boolean;
|
|
||||||
}
|
|
||||||
interface HtmlAnchorTag extends HtmlTag {
|
|
||||||
href?: string;
|
|
||||||
target?: string;
|
|
||||||
download?: string;
|
|
||||||
ping?: string;
|
|
||||||
rel?: string;
|
|
||||||
media?: string;
|
|
||||||
hreflang?: string;
|
|
||||||
type?: string;
|
|
||||||
}
|
|
||||||
interface HtmlAreaTag extends HtmlTag {
|
|
||||||
alt?: string;
|
|
||||||
coords?: string;
|
|
||||||
shape?: string;
|
|
||||||
href?: string;
|
|
||||||
target?: string;
|
|
||||||
ping?: string;
|
|
||||||
rel?: string;
|
|
||||||
media?: string;
|
|
||||||
hreflang?: string;
|
|
||||||
type?: string;
|
|
||||||
}
|
|
||||||
interface HtmlAudioTag extends HtmlTag {
|
|
||||||
src?: string;
|
|
||||||
autobuffer?: string;
|
|
||||||
autoplay?: string;
|
|
||||||
loop?: string;
|
|
||||||
controls?: string;
|
|
||||||
}
|
|
||||||
interface BaseTag extends HtmlTag {
|
|
||||||
href?: string;
|
|
||||||
target?: string;
|
|
||||||
}
|
|
||||||
interface HtmlQuoteTag extends HtmlTag {
|
|
||||||
cite?: string;
|
|
||||||
}
|
|
||||||
interface HtmlBodyTag extends HtmlTag {
|
|
||||||
}
|
|
||||||
interface HtmlButtonTag extends HtmlTag {
|
|
||||||
action?: string;
|
|
||||||
autofocus?: string;
|
|
||||||
disabled?: string;
|
|
||||||
enctype?: string;
|
|
||||||
form?: string;
|
|
||||||
method?: string;
|
|
||||||
name?: string;
|
|
||||||
novalidate?: string | boolean;
|
|
||||||
target?: string;
|
|
||||||
type?: string;
|
|
||||||
value?: string;
|
|
||||||
onClick?: Function;
|
|
||||||
}
|
|
||||||
interface HtmlDataListTag extends HtmlTag {
|
|
||||||
}
|
|
||||||
interface HtmlCanvasTag extends HtmlTag {
|
|
||||||
width?: string;
|
|
||||||
height?: string;
|
|
||||||
}
|
|
||||||
interface HtmlTableColTag extends HtmlTag {
|
|
||||||
span?: string;
|
|
||||||
}
|
|
||||||
interface HtmlTableSectionTag extends HtmlTag {
|
|
||||||
}
|
|
||||||
interface HtmlTableRowTag extends HtmlTag {
|
|
||||||
}
|
|
||||||
interface DataTag extends HtmlTag {
|
|
||||||
value?: string;
|
|
||||||
}
|
|
||||||
interface HtmlEmbedTag extends HtmlTag {
|
|
||||||
src?: string;
|
|
||||||
type?: string;
|
|
||||||
width?: string;
|
|
||||||
height?: string;
|
|
||||||
}
|
|
||||||
interface HtmlFieldSetTag extends HtmlTag {
|
|
||||||
disabled?: string;
|
|
||||||
form?: string;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
interface HtmlFormTag extends HtmlTag {
|
|
||||||
acceptCharset?: string;
|
|
||||||
action?: string;
|
|
||||||
autocomplete?: string;
|
|
||||||
enctype?: string;
|
|
||||||
method?: string;
|
|
||||||
name?: string;
|
|
||||||
novalidate?: string | boolean;
|
|
||||||
target?: string;
|
|
||||||
}
|
|
||||||
interface HtmlHtmlTag extends HtmlTag {
|
|
||||||
manifest?: string;
|
|
||||||
}
|
|
||||||
interface HtmlIFrameTag extends HtmlTag {
|
|
||||||
src?: string;
|
|
||||||
srcdoc?: string;
|
|
||||||
name?: string;
|
|
||||||
sandbox?: string;
|
|
||||||
seamless?: string;
|
|
||||||
width?: string;
|
|
||||||
height?: string;
|
|
||||||
}
|
|
||||||
interface HtmlImageTag extends HtmlTag {
|
|
||||||
alt?: string;
|
|
||||||
src?: AttributeValue;
|
|
||||||
crossorigin?: string;
|
|
||||||
usemap?: string;
|
|
||||||
ismap?: string;
|
|
||||||
width?: string;
|
|
||||||
height?: string;
|
|
||||||
}
|
|
||||||
interface HtmlInputTag extends HtmlTag {
|
|
||||||
accept?: string;
|
|
||||||
action?: string;
|
|
||||||
alt?: string;
|
|
||||||
autocomplete?: string;
|
|
||||||
autofocus?: string;
|
|
||||||
checked?: string | boolean;
|
|
||||||
disabled?: string | boolean;
|
|
||||||
enctype?: string;
|
|
||||||
form?: string;
|
|
||||||
height?: string;
|
|
||||||
list?: string;
|
|
||||||
max?: string;
|
|
||||||
maxlength?: string;
|
|
||||||
method?: string;
|
|
||||||
min?: string;
|
|
||||||
multiple?: string;
|
|
||||||
name?: string;
|
|
||||||
novalidate?: string | boolean;
|
|
||||||
pattern?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
readonly?: string;
|
|
||||||
required?: string;
|
|
||||||
size?: string;
|
|
||||||
src?: string;
|
|
||||||
step?: string;
|
|
||||||
target?: string;
|
|
||||||
type?: string;
|
|
||||||
value?: string;
|
|
||||||
width?: string;
|
|
||||||
}
|
|
||||||
interface HtmlModTag extends HtmlTag {
|
|
||||||
cite?: string;
|
|
||||||
datetime?: string | Date;
|
|
||||||
}
|
|
||||||
interface KeygenTag extends HtmlTag {
|
|
||||||
autofocus?: string;
|
|
||||||
challenge?: string;
|
|
||||||
disabled?: string;
|
|
||||||
form?: string;
|
|
||||||
keytype?: string;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
interface HtmlLabelTag extends HtmlTag {
|
|
||||||
form?: string;
|
|
||||||
for?: string;
|
|
||||||
}
|
|
||||||
interface HtmlLITag extends HtmlTag {
|
|
||||||
value?: string | number;
|
|
||||||
}
|
|
||||||
interface HtmlLinkTag extends HtmlTag {
|
|
||||||
href?: string;
|
|
||||||
crossorigin?: string;
|
|
||||||
rel?: string;
|
|
||||||
media?: string;
|
|
||||||
hreflang?: string;
|
|
||||||
type?: string;
|
|
||||||
sizes?: string;
|
|
||||||
integrity?: string;
|
|
||||||
}
|
|
||||||
interface HtmlMapTag extends HtmlTag {
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
interface HtmlMetaTag extends HtmlTag {
|
|
||||||
name?: string;
|
|
||||||
httpEquiv?: string;
|
|
||||||
content?: string;
|
|
||||||
charset?: string;
|
|
||||||
}
|
|
||||||
interface HtmlMeterTag extends HtmlTag {
|
|
||||||
value?: string | number;
|
|
||||||
min?: string | number;
|
|
||||||
max?: string | number;
|
|
||||||
low?: string | number;
|
|
||||||
high?: string | number;
|
|
||||||
optimum?: string | number;
|
|
||||||
}
|
|
||||||
interface HtmlObjectTag extends HtmlTag {
|
|
||||||
data?: string;
|
|
||||||
type?: string;
|
|
||||||
name?: string;
|
|
||||||
usemap?: string;
|
|
||||||
form?: string;
|
|
||||||
width?: string;
|
|
||||||
height?: string;
|
|
||||||
}
|
|
||||||
interface HtmlOListTag extends HtmlTag {
|
|
||||||
reversed?: string;
|
|
||||||
start?: string | number;
|
|
||||||
}
|
|
||||||
interface HtmlOptgroupTag extends HtmlTag {
|
|
||||||
disabled?: string;
|
|
||||||
label?: string;
|
|
||||||
}
|
|
||||||
interface HtmlOptionTag extends HtmlTag {
|
|
||||||
disabled?: string;
|
|
||||||
label?: string;
|
|
||||||
selected?: string;
|
|
||||||
value?: string;
|
|
||||||
}
|
|
||||||
interface HtmlOutputTag extends HtmlTag {
|
|
||||||
for?: string;
|
|
||||||
form?: string;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
interface HtmlParamTag extends HtmlTag {
|
|
||||||
name?: string;
|
|
||||||
value?: string;
|
|
||||||
}
|
|
||||||
interface HtmlProgressTag extends HtmlTag {
|
|
||||||
value?: string | number;
|
|
||||||
max?: string | number;
|
|
||||||
}
|
|
||||||
interface HtmlCommandTag extends HtmlTag {
|
|
||||||
type?: string;
|
|
||||||
label?: string;
|
|
||||||
icon?: string;
|
|
||||||
disabled?: string;
|
|
||||||
checked?: string;
|
|
||||||
radiogroup?: string;
|
|
||||||
default?: string;
|
|
||||||
}
|
|
||||||
interface HtmlLegendTag extends HtmlTag {
|
|
||||||
}
|
|
||||||
interface HtmlBrowserButtonTag extends HtmlTag {
|
|
||||||
type?: string;
|
|
||||||
}
|
|
||||||
interface HtmlMenuTag extends HtmlTag {
|
|
||||||
type?: string;
|
|
||||||
label?: string;
|
|
||||||
}
|
|
||||||
interface HtmlScriptTag extends HtmlTag {
|
|
||||||
src?: string;
|
|
||||||
type?: string;
|
|
||||||
charset?: string;
|
|
||||||
async?: string;
|
|
||||||
defer?: string;
|
|
||||||
crossorigin?: string;
|
|
||||||
integrity?: string;
|
|
||||||
text?: string;
|
|
||||||
}
|
|
||||||
interface HtmlDetailsTag extends HtmlTag {
|
|
||||||
open?: string;
|
|
||||||
}
|
|
||||||
interface HtmlSelectTag extends HtmlTag {
|
|
||||||
autofocus?: string;
|
|
||||||
disabled?: string;
|
|
||||||
form?: string;
|
|
||||||
multiple?: string;
|
|
||||||
name?: string;
|
|
||||||
required?: string;
|
|
||||||
size?: string;
|
|
||||||
}
|
|
||||||
interface HtmlSourceTag extends HtmlTag {
|
|
||||||
src?: string;
|
|
||||||
type?: string;
|
|
||||||
media?: string;
|
|
||||||
}
|
|
||||||
interface HtmlStyleTag extends HtmlTag {
|
|
||||||
media?: string;
|
|
||||||
type?: string;
|
|
||||||
disabled?: string;
|
|
||||||
scoped?: string;
|
|
||||||
}
|
|
||||||
interface HtmlTableTag extends HtmlTag {
|
|
||||||
}
|
|
||||||
interface HtmlTableDataCellTag extends HtmlTag {
|
|
||||||
colspan?: string | number;
|
|
||||||
rowspan?: string | number;
|
|
||||||
headers?: string;
|
|
||||||
}
|
|
||||||
interface HtmlTextAreaTag extends HtmlTag {
|
|
||||||
autofocus?: string;
|
|
||||||
cols?: string;
|
|
||||||
dirname?: string;
|
|
||||||
disabled?: string;
|
|
||||||
form?: string;
|
|
||||||
maxlength?: string;
|
|
||||||
minlength?: string;
|
|
||||||
name?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
readonly?: string;
|
|
||||||
required?: string;
|
|
||||||
rows?: string;
|
|
||||||
wrap?: string;
|
|
||||||
}
|
|
||||||
interface HtmlTableHeaderCellTag extends HtmlTag {
|
|
||||||
colspan?: string | number;
|
|
||||||
rowspan?: string | number;
|
|
||||||
headers?: string;
|
|
||||||
scope?: string;
|
|
||||||
}
|
|
||||||
interface HtmlTimeTag extends HtmlTag {
|
|
||||||
datetime?: string | Date;
|
|
||||||
}
|
|
||||||
interface HtmlTrackTag extends HtmlTag {
|
|
||||||
default?: string;
|
|
||||||
kind?: string;
|
|
||||||
label?: string;
|
|
||||||
src?: string;
|
|
||||||
srclang?: string;
|
|
||||||
}
|
|
||||||
interface HtmlVideoTag extends HtmlTag {
|
|
||||||
src?: string;
|
|
||||||
poster?: string;
|
|
||||||
autobuffer?: string;
|
|
||||||
autoplay?: string;
|
|
||||||
loop?: string;
|
|
||||||
controls?: string;
|
|
||||||
width?: string;
|
|
||||||
height?: string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=element-types.d.ts.map
|
|
||||||
98
frontend/utils/events.d.ts
vendored
@@ -1,98 +0,0 @@
|
|||||||
declare namespace JSX {
|
|
||||||
interface HtmlBodyTag {
|
|
||||||
onafterprint?: string;
|
|
||||||
onbeforeprint?: string;
|
|
||||||
onbeforeonload?: string;
|
|
||||||
onblur?: string;
|
|
||||||
onerror?: string;
|
|
||||||
onfocus?: string;
|
|
||||||
onhaschange?: string;
|
|
||||||
onload?: string;
|
|
||||||
onmessage?: string;
|
|
||||||
onoffline?: string;
|
|
||||||
ononline?: string;
|
|
||||||
onpagehide?: string;
|
|
||||||
onpageshow?: string;
|
|
||||||
onpopstate?: string;
|
|
||||||
onredo?: string;
|
|
||||||
onresize?: string;
|
|
||||||
onstorage?: string;
|
|
||||||
onundo?: string;
|
|
||||||
onunload?: string;
|
|
||||||
}
|
|
||||||
interface HtmlTag {
|
|
||||||
oncontextmenu?: string;
|
|
||||||
onkeydown?: string;
|
|
||||||
onkeypress?: string;
|
|
||||||
onkeyup?: string;
|
|
||||||
onclick?: AttributeValue;
|
|
||||||
ondblclick?: string;
|
|
||||||
ondrag?: string;
|
|
||||||
ondragend?: string;
|
|
||||||
ondragenter?: string;
|
|
||||||
ondragleave?: string;
|
|
||||||
ondragover?: string;
|
|
||||||
ondragstart?: string;
|
|
||||||
ondrop?: string;
|
|
||||||
onmousedown?: string;
|
|
||||||
onmousemove?: string;
|
|
||||||
onmouseout?: string;
|
|
||||||
onmouseover?: string;
|
|
||||||
onmouseup?: string;
|
|
||||||
onmousewheel?: string;
|
|
||||||
onscroll?: string;
|
|
||||||
}
|
|
||||||
interface FormEvents {
|
|
||||||
onblur?: string;
|
|
||||||
onchange?: string;
|
|
||||||
onfocus?: string;
|
|
||||||
onformchange?: string;
|
|
||||||
onforminput?: string;
|
|
||||||
oninput?: string;
|
|
||||||
oninvalid?: string;
|
|
||||||
onselect?: string;
|
|
||||||
onsubmit?: string;
|
|
||||||
}
|
|
||||||
interface HtmlInputTag extends FormEvents {
|
|
||||||
}
|
|
||||||
interface HtmlFieldSetTag extends FormEvents {
|
|
||||||
}
|
|
||||||
interface HtmlFormTag extends FormEvents {
|
|
||||||
}
|
|
||||||
interface MediaEvents {
|
|
||||||
onabort?: string;
|
|
||||||
oncanplay?: string;
|
|
||||||
oncanplaythrough?: string;
|
|
||||||
ondurationchange?: string;
|
|
||||||
onemptied?: string;
|
|
||||||
onended?: string;
|
|
||||||
onerror?: AttributeValue;
|
|
||||||
onloadeddata?: string;
|
|
||||||
onloadedmetadata?: string;
|
|
||||||
onloadstart?: string;
|
|
||||||
onpause?: string;
|
|
||||||
onplay?: string;
|
|
||||||
onplaying?: string;
|
|
||||||
onprogress?: string;
|
|
||||||
onratechange?: string;
|
|
||||||
onreadystatechange?: string;
|
|
||||||
onseeked?: string;
|
|
||||||
onseeking?: string;
|
|
||||||
onstalled?: string;
|
|
||||||
onsuspend?: string;
|
|
||||||
ontimeupdate?: string;
|
|
||||||
onvolumechange?: string;
|
|
||||||
onwaiting?: string;
|
|
||||||
}
|
|
||||||
interface HtmlAudioTag extends MediaEvents {
|
|
||||||
}
|
|
||||||
interface HtmlEmbedTag extends MediaEvents {
|
|
||||||
}
|
|
||||||
interface HtmlImageTag extends MediaEvents {
|
|
||||||
}
|
|
||||||
interface HtmlObjectTag extends MediaEvents {
|
|
||||||
}
|
|
||||||
interface HtmlVideoTag extends MediaEvents {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=events.d.ts.map
|
|
||||||
118
frontend/utils/intrinsic-elements.d.ts
vendored
@@ -1,118 +0,0 @@
|
|||||||
declare namespace JSX {
|
|
||||||
type Element = HTMLElement;
|
|
||||||
interface IntrinsicElements {
|
|
||||||
a: HtmlAnchorTag;
|
|
||||||
abbr: HtmlTag;
|
|
||||||
address: HtmlTag;
|
|
||||||
area: HtmlAreaTag;
|
|
||||||
article: HtmlTag;
|
|
||||||
aside: HtmlTag;
|
|
||||||
audio: HtmlAudioTag;
|
|
||||||
b: HtmlTag;
|
|
||||||
bb: HtmlBrowserButtonTag;
|
|
||||||
base: BaseTag;
|
|
||||||
bdi: HtmlTag;
|
|
||||||
bdo: HtmlTag;
|
|
||||||
blockquote: HtmlQuoteTag;
|
|
||||||
body: HtmlBodyTag;
|
|
||||||
br: HtmlTag;
|
|
||||||
button: HtmlButtonTag;
|
|
||||||
canvas: HtmlCanvasTag;
|
|
||||||
caption: HtmlTag;
|
|
||||||
cite: HtmlTag;
|
|
||||||
code: HtmlTag;
|
|
||||||
col: HtmlTableColTag;
|
|
||||||
colgroup: HtmlTableColTag;
|
|
||||||
commands: HtmlCommandTag;
|
|
||||||
data: DataTag;
|
|
||||||
datalist: HtmlDataListTag;
|
|
||||||
dd: HtmlTag;
|
|
||||||
del: HtmlModTag;
|
|
||||||
details: HtmlDetailsTag;
|
|
||||||
dfn: HtmlTag;
|
|
||||||
div: HtmlTag;
|
|
||||||
dl: HtmlTag;
|
|
||||||
dt: HtmlTag;
|
|
||||||
em: HtmlTag;
|
|
||||||
embed: HtmlEmbedTag;
|
|
||||||
fieldset: HtmlFieldSetTag;
|
|
||||||
figcaption: HtmlTag;
|
|
||||||
figure: HtmlTag;
|
|
||||||
footer: HtmlTag;
|
|
||||||
form: HtmlFormTag;
|
|
||||||
h1: HtmlTag;
|
|
||||||
h2: HtmlTag;
|
|
||||||
h3: HtmlTag;
|
|
||||||
h4: HtmlTag;
|
|
||||||
h5: HtmlTag;
|
|
||||||
h6: HtmlTag;
|
|
||||||
head: HtmlTag;
|
|
||||||
header: HtmlTag;
|
|
||||||
hr: HtmlTag;
|
|
||||||
html: HtmlHtmlTag;
|
|
||||||
i: HtmlTag;
|
|
||||||
iframe: HtmlIFrameTag;
|
|
||||||
img: HtmlImageTag;
|
|
||||||
input: HtmlInputTag;
|
|
||||||
ins: HtmlModTag;
|
|
||||||
kbd: HtmlTag;
|
|
||||||
keygen: KeygenTag;
|
|
||||||
label: HtmlLabelTag;
|
|
||||||
legend: HtmlLegendTag;
|
|
||||||
li: HtmlLITag;
|
|
||||||
link: HtmlLinkTag;
|
|
||||||
main: HtmlTag;
|
|
||||||
map: HtmlMapTag;
|
|
||||||
mark: HtmlTag;
|
|
||||||
menu: HtmlMenuTag;
|
|
||||||
meta: HtmlMetaTag;
|
|
||||||
meter: HtmlMeterTag;
|
|
||||||
nav: HtmlTag;
|
|
||||||
noscript: HtmlTag;
|
|
||||||
object: HtmlObjectTag;
|
|
||||||
ol: HtmlOListTag;
|
|
||||||
optgroup: HtmlOptgroupTag;
|
|
||||||
option: HtmlOptionTag;
|
|
||||||
output: HtmlOutputTag;
|
|
||||||
p: HtmlTag;
|
|
||||||
param: HtmlParamTag;
|
|
||||||
pre: HtmlTag;
|
|
||||||
progress: HtmlProgressTag;
|
|
||||||
q: HtmlQuoteTag;
|
|
||||||
rb: HtmlTag;
|
|
||||||
rp: HtmlTag;
|
|
||||||
rt: HtmlTag;
|
|
||||||
rtc: HtmlTag;
|
|
||||||
ruby: HtmlTag;
|
|
||||||
s: HtmlTag;
|
|
||||||
samp: HtmlTag;
|
|
||||||
script: HtmlScriptTag;
|
|
||||||
section: HtmlTag;
|
|
||||||
select: HtmlSelectTag;
|
|
||||||
small: HtmlTag;
|
|
||||||
source: HtmlSourceTag;
|
|
||||||
span: HtmlTag;
|
|
||||||
strong: HtmlTag;
|
|
||||||
style: HtmlStyleTag;
|
|
||||||
sub: HtmlTag;
|
|
||||||
sup: HtmlTag;
|
|
||||||
table: HtmlTableTag;
|
|
||||||
tbody: HtmlTag;
|
|
||||||
td: HtmlTableDataCellTag;
|
|
||||||
template: HtmlTag;
|
|
||||||
textarea: HtmlTextAreaTag;
|
|
||||||
tfoot: HtmlTableSectionTag;
|
|
||||||
th: HtmlTableHeaderCellTag;
|
|
||||||
thead: HtmlTableSectionTag;
|
|
||||||
time: HtmlTimeTag;
|
|
||||||
title: HtmlTag;
|
|
||||||
tr: HtmlTableRowTag;
|
|
||||||
track: HtmlTrackTag;
|
|
||||||
u: HtmlTag;
|
|
||||||
ul: HtmlTag;
|
|
||||||
var: HtmlTag;
|
|
||||||
video: HtmlVideoTag;
|
|
||||||
wbr: HtmlTag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=intrinsic-elements.d.ts.map
|
|
||||||
@@ -136,10 +136,10 @@ url = "about_me/"
|
|||||||
#weight = 2
|
#weight = 2
|
||||||
#url = "posts/"
|
#url = "posts/"
|
||||||
|
|
||||||
[[languages.en.menu.main]]
|
#[[languages.en.menu.main]]
|
||||||
name = "List"
|
#name = "List"
|
||||||
weight = 3
|
#weight = 3
|
||||||
url = "list/"
|
#url = "list/"
|
||||||
|
|
||||||
[[languages.en.menu.main]]
|
[[languages.en.menu.main]]
|
||||||
name = "CV"
|
name = "CV"
|
||||||
|
|||||||
14
package.json
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "web",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/express": "^5.0.3",
|
|
||||||
"@types/morgan": "^1.9.10",
|
|
||||||
"express": "^5.1.0",
|
|
||||||
"hbs": "^4.2.0",
|
|
||||||
"morgan": "~1.10.1",
|
|
||||||
"bun-types": "^1.2.22",
|
|
||||||
"typescript": "^5.9.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,18 +15,15 @@
|
|||||||
|
|
||||||
<title>Nikola Petrov</title>
|
<title>Nikola Petrov</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/assets/main/1_0/assets/fontawesome/css/all.css">
|
<link rel="stylesheet" href="assets/fontawesome/css/all.css">
|
||||||
<link rel="stylesheet" href="/assets/main/1_0/css/intro.css">
|
<link rel="stylesheet" href="css/intro.css">
|
||||||
<link rel="stylesheet" href="/assets/main/1_0/css/styles.css">
|
<link rel="stylesheet" href="css/styles.css">
|
||||||
<script defer src="/assets/main/1_0/assets/WinBox/winbox.bundle.js"></script>
|
<script defer src="assets/WinBox/winbox.bundle.js"></script>
|
||||||
<script defer src="/assets/main/1_0/js/main.js"></script>
|
<script defer src="js/main.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<section class="intro">
|
<section class="intro">
|
||||||
<h1 class="section__title">
|
<h1 class="section__title">
|
||||||
Hi, I am <strong>Nikola Petrov</strong>
|
Hi, I am <strong>Nikola Petrov</strong>
|
||||||
@@ -1,369 +1,490 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en" data-bs-theme="dark">
|
<html lang="en" data-bs-theme="dark">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="shortcut icon" href="/images/logo.ico" type="image/x-icon">
|
<link rel="shortcut icon" href="/images/logo.ico" type="image/x-icon">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<title>Nikola Petrov</title>
|
<title>Nikola Petrov</title>
|
||||||
|
|
||||||
<script defer src="/assets/main/2_0/js/script.js"></script>
|
<script defer src="js/script.js"></script>
|
||||||
<link rel="stylesheet" href="/assets/main/2_0/css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||||
<script defer type="module" src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"></script>
|
<script defer type="module" src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"></script>
|
||||||
<script defer nomodule src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js"></script>
|
<script defer nomodule src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
{{#with userData}}
|
|
||||||
|
<body>
|
||||||
<body>
|
<main>
|
||||||
<main>
|
<aside class="sidebar" data-sidebar>
|
||||||
<aside class="sidebar" data-sidebar>
|
<div class="sidebar-info">
|
||||||
<div class="sidebar-info">
|
<figure class="avatar-box">
|
||||||
<figure class="avatar-box">
|
<img src="/images/Jaz.jpg" alt="Nikola Petrov" width="80">
|
||||||
<img src="/images/Jaz.jpg" alt="Nikola Petrov" width="80">
|
</figure>
|
||||||
</figure>
|
|
||||||
|
<div class="info-content">
|
||||||
<div class="info-content">
|
<h1 class="name" title="Nikola Petrov">Nikola Petrov</h1>
|
||||||
<h1 class="name" title="Nikola Petrov">{{first_name}} {{last_name}}</h1>
|
|
||||||
|
<p class="title">Developer</p>
|
||||||
<p class="title">{{occupation}}</p>
|
</div>
|
||||||
</div>
|
|
||||||
|
<button class="info_more-btn" data-sidebar-btn>
|
||||||
<button class="info_more-btn" data-sidebar-btn>
|
<span>Show Contacts</span>
|
||||||
<span>Show Contacts</span>
|
|
||||||
|
<ion-icon name="chevron-down"></ion-icon>
|
||||||
<ion-icon name="chevron-down"></ion-icon>
|
</button>
|
||||||
</button>
|
|
||||||
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="sidebar-info_more">
|
||||||
<div class="sidebar-info_more">
|
|
||||||
|
<div class="separator"></div>
|
||||||
<div class="separator"></div>
|
|
||||||
|
<ul class="contacts-list">
|
||||||
<ul class="contacts-list">
|
|
||||||
|
<li class="contact-item">
|
||||||
<li class="contact-item">
|
|
||||||
|
<div class="icon-box">
|
||||||
<div class="icon-box">
|
<ion-icon name="mail-outline"></ion-icon>
|
||||||
<ion-icon name="mail-outline"></ion-icon>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="contact-info">
|
||||||
<div class="contact-info">
|
<p class="contact-title">Email</p>
|
||||||
<p class="contact-title">Email</p>
|
|
||||||
|
<a href="mailto:nikola@petrovv.com" class="contact-link">nikola@petrovv.com</a>
|
||||||
<a href="mailto:{{email}}" class="contact-link">{{email}}</a>
|
</div>
|
||||||
</div>
|
|
||||||
|
</li>
|
||||||
</li>
|
|
||||||
|
<li class="contact-item">
|
||||||
<li class="contact-item">
|
|
||||||
|
<div class="icon-box">
|
||||||
<div class="icon-box">
|
<ion-icon name="phone-portrait-outline"></ion-icon>
|
||||||
<ion-icon name="phone-portrait-outline"></ion-icon>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="contact-info">
|
||||||
<div class="contact-info">
|
<p class="contact-title">Phone</p>
|
||||||
<p class="contact-title">Phone</p>
|
|
||||||
|
<a href="tel:+38670749506" class="contact-link">+38670749506</a>
|
||||||
<a href="tel:{{phone_number}}" class="contact-link">{{phone_number}}</a>
|
</div>
|
||||||
</div>
|
|
||||||
|
</li>
|
||||||
</li>
|
|
||||||
|
<li class="contact-item">
|
||||||
<li class="contact-item">
|
|
||||||
|
<div class="icon-box">
|
||||||
<div class="icon-box">
|
<ion-icon name="calendar-outline"></ion-icon>
|
||||||
<ion-icon name="calendar-outline"></ion-icon>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="contact-info">
|
||||||
<div class="contact-info">
|
<p class="contact-title">Birthday</p>
|
||||||
<p class="contact-title">Birthday</p>
|
|
||||||
|
<time datetime="2000-11-01">14, November, 2000</time>
|
||||||
<time datetime="2000-11-01">{{birth}}</time>
|
</div>
|
||||||
</div>
|
|
||||||
|
</li>
|
||||||
</li>
|
|
||||||
|
<li class="contact-item">
|
||||||
<li class="contact-item">
|
|
||||||
|
<div class="icon-box">
|
||||||
<div class="icon-box">
|
<ion-icon name="location-outline"></ion-icon>
|
||||||
<ion-icon name="location-outline"></ion-icon>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="contact-info">
|
||||||
<div class="contact-info">
|
<p class="contact-title">Location</p>
|
||||||
<p class="contact-title">Location</p>
|
|
||||||
|
<address>Ljubljana, Slovenia</address>
|
||||||
<address>Ljubljana, Slovenia</address>
|
</div>
|
||||||
</div>
|
|
||||||
|
</li>
|
||||||
</li>
|
|
||||||
|
</ul>
|
||||||
</ul>
|
|
||||||
|
<div class="separator"></div>
|
||||||
<div class="separator"></div>
|
|
||||||
|
<ul class="social-list">
|
||||||
<ul class="social-list">
|
|
||||||
|
<li class="social-item">
|
||||||
<li class="social-item">
|
<a href="https://git.petrovv.com/explore" title="GitLab" class="social-link">
|
||||||
<a href="{{git_link}}" title="GitLab" class="social-link">
|
<ion-icon name="logo-gitlab"></ion-icon>
|
||||||
<ion-icon name="logo-gitlab"></ion-icon>
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
|
||||||
|
<li class="social-item">
|
||||||
<li class="social-item">
|
<a href="/doc/CV.pdf" title="CV" class="social-link">
|
||||||
<a href="/doc/CV.pdf" title="CV" class="social-link">
|
<p>CV</p>
|
||||||
<p>CV</p>
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
|
||||||
|
<li class="social-item">
|
||||||
<li class="social-item">
|
<a href="/doc/Certificate.pdf" title="Certificate" class="social-link">
|
||||||
<a href="/doc/Certificate.pdf" title="Certificate" class="social-link">
|
<ion-icon name="document-text-outline"></ion-icon>
|
||||||
<ion-icon name="document-text-outline"></ion-icon>
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
|
||||||
|
<!-- <li class="social-item">
|
||||||
<!-- <li class="social-item">
|
<a href="#" class="social-link">
|
||||||
<a href="#" class="social-link">
|
<ion-icon name="logo-gitlab"></ion-icon>
|
||||||
<ion-icon name="logo-gitlab"></ion-icon>
|
</a>
|
||||||
</a>
|
</li> -->
|
||||||
</li> -->
|
</ul>
|
||||||
</ul>
|
|
||||||
|
</div>
|
||||||
</div>
|
|
||||||
|
</aside>
|
||||||
</aside>
|
|
||||||
|
<!--
|
||||||
<!--
|
- #main-content
|
||||||
- #main-content
|
-->
|
||||||
-->
|
|
||||||
|
<div class="main-content">
|
||||||
<div class="main-content">
|
|
||||||
|
<!--
|
||||||
<!--
|
- #NAVBAR
|
||||||
- #NAVBAR
|
-->
|
||||||
-->
|
|
||||||
|
<nav class="navbar">
|
||||||
<nav class="navbar">
|
|
||||||
|
<ul class="navbar-list">
|
||||||
<ul class="navbar-list">
|
|
||||||
|
<li class="navbar-item">
|
||||||
<li class="navbar-item">
|
<button class="navbar-link active" data-nav-link>About</button>
|
||||||
<button class="navbar-link active" data-nav-link>About</button>
|
</li>
|
||||||
</li>
|
|
||||||
|
<li class="navbar-item">
|
||||||
<li class="navbar-item">
|
<button class="navbar-link" data-nav-link>Resume</button>
|
||||||
<button class="navbar-link" data-nav-link>Resume</button>
|
</li>
|
||||||
</li>
|
|
||||||
|
|
||||||
|
<li class="navbar-item">
|
||||||
<li class="navbar-item">
|
<button class="navbar-link" data-nav-link>Portfolio</button>
|
||||||
<button class="navbar-link" data-nav-link>Portfolio</button>
|
</li>
|
||||||
</li>
|
</ul>
|
||||||
</ul>
|
|
||||||
|
</nav>
|
||||||
</nav>
|
|
||||||
|
<!--
|
||||||
<!--
|
- #ABOUT
|
||||||
- #ABOUT
|
-->
|
||||||
-->
|
|
||||||
|
<article class="about active" data-page="about">
|
||||||
<article class="about active" data-page="about">
|
|
||||||
|
<header>
|
||||||
<header>
|
<h2 class="h2 article-title">About me</h2>
|
||||||
<h2 class="h2 article-title">About me</h2>
|
</header>
|
||||||
</header>
|
|
||||||
|
<section class="about-text">
|
||||||
<section class="about-text">
|
<p>
|
||||||
<p>
|
I am Nikola, currently pursuing my studies at the Faculty of Electrical Engineering and Computer Science (FERI) in Maribor. My academic journey is largely driven by my interest in application and web development. I find the process of creating functional and user-friendly digital solutions both challenging and rewarding. This field allows me to blend creativity with technical skills, which I find particularly engaging.
|
||||||
{{#each about_me}}
|
</br>
|
||||||
{{this}}
|
Recently, I have developed an interest in the game of Go. The strategic depth and complexity of the game have captivated my attention, providing a stimulating mental exercise. Additionally, I have started exploring photography. Capturing moments and expressing visual stories through a lens has become a newfound passion, offering a different kind of creative outlet that complements my technical pursuits.
|
||||||
{{#unless @last}}
|
</p>
|
||||||
</br>
|
</section>
|
||||||
{{/unless}}
|
|
||||||
{{/each}}
|
|
||||||
</p>
|
<!--
|
||||||
</section>
|
- project
|
||||||
|
-->
|
||||||
|
|
||||||
<!--
|
<section class="service">
|
||||||
- project
|
|
||||||
-->
|
<h3 class="h3 service-title">Interests</h3>
|
||||||
|
|
||||||
<section class="service">
|
<ul class="service-list">
|
||||||
|
|
||||||
<h3 class="h3 service-title">Interests</h3>
|
|
||||||
|
<li class="service-item">
|
||||||
<ul class="service-list">
|
<div class="service-icon-box">
|
||||||
|
<ion-icon name="server-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
<li class="service-item">
|
|
||||||
<div class="service-icon-box">
|
<div class="service-content-box">
|
||||||
<ion-icon name="server-outline"></ion-icon>
|
|
||||||
</div>
|
<h4 class="h4 service-item-title">Backend programming</h4>
|
||||||
|
<p class="service-item-text">
|
||||||
<div class="service-content-box">
|
Backend programming focuses on server-side development, handling data storage, processing, and
|
||||||
|
business
|
||||||
<h4 class="h4 service-item-title">Backend programming</h4>
|
logic to support the functionality of web and mobile applications
|
||||||
<p class="service-item-text">
|
</p>
|
||||||
Backend programming focuses on server-side development, handling data storage, processing, and
|
</div>
|
||||||
business
|
</li>
|
||||||
logic to support the functionality of web and mobile applications
|
|
||||||
</p>
|
|
||||||
</div>
|
<li class="service-item">
|
||||||
</li>
|
|
||||||
|
<div class="service-icon-box">
|
||||||
|
<ion-icon name="hardware-chip-outline"></ion-icon>
|
||||||
<li class="service-item">
|
</div>
|
||||||
|
|
||||||
<div class="service-icon-box">
|
<div class="service-content-box">
|
||||||
<ion-icon name="hardware-chip-outline"></ion-icon>
|
|
||||||
</div>
|
<h4 class="h4 service-item-title">Embedded systems</h4>
|
||||||
|
|
||||||
<div class="service-content-box">
|
<p class="service-item-text">
|
||||||
|
Embedded systems are specialized computing devices integrated into everyday products, performing
|
||||||
<h4 class="h4 service-item-title">Embedded systems</h4>
|
dedicated functions with real-time constraints.
|
||||||
|
</p>
|
||||||
<p class="service-item-text">
|
</div>
|
||||||
Embedded systems are specialized computing devices integrated into everyday products, performing
|
|
||||||
dedicated functions with real-time constraints.
|
</li>
|
||||||
</p>
|
|
||||||
</div>
|
<li class="service-item">
|
||||||
|
|
||||||
</li>
|
<div class="service-icon-box">
|
||||||
|
<ion-icon name="desktop-outline"></ion-icon>
|
||||||
<li class="service-item">
|
</div>
|
||||||
|
|
||||||
<div class="service-icon-box">
|
<div class="service-content-box">
|
||||||
<ion-icon name="desktop-outline"></ion-icon>
|
<h4 class="h4 service-item-title">Application programming</h4>
|
||||||
</div>
|
<p class="service-item-text">
|
||||||
|
Application programming involves writing code to create software applications that perform specific
|
||||||
<div class="service-content-box">
|
tasks or functions.
|
||||||
<h4 class="h4 service-item-title">Application programming</h4>
|
</p>
|
||||||
<p class="service-item-text">
|
</div>
|
||||||
Application programming involves writing code to create software applications that perform specific
|
|
||||||
tasks or functions.
|
</li>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
</ul>
|
||||||
</li>
|
|
||||||
|
</section>
|
||||||
|
|
||||||
</ul>
|
</article>
|
||||||
|
|
||||||
</section>
|
<!--
|
||||||
|
- #RESUME
|
||||||
</article>
|
-->
|
||||||
|
|
||||||
<!--
|
<article class="resume" data-page="resume">
|
||||||
- #RESUME
|
|
||||||
-->
|
<header>
|
||||||
|
<h2 class="h2 article-title">Resume</h2>
|
||||||
<article class="resume" data-page="resume">
|
</header>
|
||||||
|
|
||||||
<header>
|
<section class="timeline">
|
||||||
<h2 class="h2 article-title">Resume</h2>
|
|
||||||
</header>
|
<div class="title-wrapper">
|
||||||
|
<div class="icon-box">
|
||||||
<section class="timeline">
|
<ion-icon name="book-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
<div class="title-wrapper">
|
|
||||||
<div class="icon-box">
|
<h3 class="h3">Education</h3>
|
||||||
<ion-icon name="book-outline"></ion-icon>
|
</div>
|
||||||
</div>
|
|
||||||
|
<ol class="timeline-list">
|
||||||
<h3 class="h3">Education</h3>
|
<li class="timeline-item">
|
||||||
</div>
|
<h4 class="h4 timeline-item-title">(FERI) Faculty of Electrical Engineering and Computer Science, University of Maribor</h4>
|
||||||
|
<span>01/10/2021 - CURRENT</span>
|
||||||
<ol class="timeline-list">
|
<p class="timeline-text">Graduate engineer of computer science and information technology.</p>
|
||||||
{{#each education}}
|
</li>
|
||||||
<li class="timeline-item">
|
<li class="timeline-item">
|
||||||
<h4 class="h4 timeline-item-title">{{title}}</h4>
|
<h4 class="h4 timeline-item-title">(SSTS Siska) Secondary school of technical professions siska</h4>
|
||||||
<span>{{time}}</span>
|
<span>01/09/2016 - 07/07/2021</span>
|
||||||
<p class="timeline-text">{{des}}</p>
|
<p class="timeline-text">Electrotechnician.</p>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
</ol>
|
||||||
</ol>
|
|
||||||
|
</section>
|
||||||
</section>
|
|
||||||
|
<section class="timeline">
|
||||||
<section class="timeline">
|
|
||||||
|
<div class="title-wrapper">
|
||||||
<div class="title-wrapper">
|
<div class="icon-box">
|
||||||
<div class="icon-box">
|
<ion-icon name="book-outline"></ion-icon>
|
||||||
<ion-icon name="book-outline"></ion-icon>
|
</div>
|
||||||
</div>
|
|
||||||
|
<h3 class="h3">Experience</h3>
|
||||||
<h3 class="h3">Experience</h3>
|
</div>
|
||||||
</div>
|
|
||||||
|
<ol class="timeline-list">
|
||||||
<ol class="timeline-list">
|
<li class="timeline-item">
|
||||||
{{#each experience}}
|
<h4 class="h4 timeline-item-title">HW Developer [ Spica International ]</h4>
|
||||||
<li class="timeline-item">
|
<span>17/03/2025 - 01/08/2025</span>
|
||||||
<h4 class="h4 timeline-item-title">{{title}} [ {{company}} ]</h4>
|
<p class="timeline-text">
|
||||||
<span>{{time}}</span>
|
Worked on access menegment systems. Programed integrated devices, based on Buildroot using c++ and python web server.
|
||||||
<p class="timeline-text">
|
</p>
|
||||||
{{des}}
|
</li>
|
||||||
</p>
|
<li class="timeline-item">
|
||||||
</li>
|
<h4 class="h4 timeline-item-title">Backend/Frontend [ RRC d.o.o ]</h4>
|
||||||
{{/each}}
|
<span>01/09/2024 - 31/12/2024</span>
|
||||||
|
<p class="timeline-text">
|
||||||
</ol>
|
Worked on goverment websites for collage enrolment and student dorm requests.
|
||||||
|
</p>
|
||||||
</section>
|
</li>
|
||||||
|
<li class="timeline-item">
|
||||||
</article>
|
<h4 class="h4 timeline-item-title">Developer [ RRC d.o.o ]</h4>
|
||||||
|
<span>18/03/2024 - 31/05/2024</span>
|
||||||
|
<p class="timeline-text">
|
||||||
<!--
|
Student practicum. Backend in java with frontend in ext JS and jQuery.
|
||||||
- #PORTFOLIO
|
</p>
|
||||||
-->
|
</li>
|
||||||
|
<li class="timeline-item">
|
||||||
<article class="portfolio" data-page="portfolio">
|
<h4 class="h4 timeline-item-title">Developer/IT [ LightAct ]</h4>
|
||||||
|
<span>01/07/2022 - 01/09/2022</span>
|
||||||
<header>
|
<p class="timeline-text">
|
||||||
<h2 class="h2 article-title">Portfolio</h2>
|
I helped maintaining data base, worked on the application (integrated a capture card and IP camera), assembled new server rack, installed new UTP/power connectors in the office.
|
||||||
</header>
|
</p>
|
||||||
|
</li>
|
||||||
<section class="projects">
|
<li class="timeline-item">
|
||||||
|
<h4 class="h4 timeline-item-title">Mentor [ Institute 404 ]</h4>
|
||||||
<ul class="project-list">
|
<span>08/06/2020 - 19/06/2020</span>
|
||||||
|
<p class="timeline-text">
|
||||||
{{#each project}}
|
I helped primary school children with their projects with soldering, laser cutting, and building.
|
||||||
<li class="project-item active">
|
</p>
|
||||||
<a href="{{link}}">
|
</li>
|
||||||
|
<li class="timeline-item">
|
||||||
<figure class="project-img">
|
<h4 class="h4 timeline-item-title">Maintenance technician [ Hella Saturnos d.o.o. ]</h4>
|
||||||
<div class="project-item-icon-box">
|
<span>04/09/2018 - 18/01/2019</span>
|
||||||
<ion-icon name="eye-outline"></ion-icon>
|
<p class="timeline-text">
|
||||||
</div>
|
I maintained and repaired machines from plastic presses to personal stations.
|
||||||
|
</p>
|
||||||
<img src="{{img}}" loading="lazy">
|
</li>
|
||||||
</figure>
|
<li class="timeline-item">
|
||||||
|
<h4 class="h4 timeline-item-title">Maintenance technician [ Best Western Premier Hotel Slon ]</h4>
|
||||||
<h3 class="project-title">{{title}}</h3>
|
<span>01/03/2018 - 04/05/2018</span>
|
||||||
|
<p class="timeline-text">
|
||||||
<p class="project-category">{{des}}</p>
|
I helped with setting up the conference/event rooms. I helped customers and fixed problems like replacing light bulbs, wall sockets, hair-dryers.
|
||||||
|
</p>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
|
||||||
{{/each}}
|
</ol>
|
||||||
</ul>
|
|
||||||
|
</section>
|
||||||
</section>
|
|
||||||
|
</article>
|
||||||
</article>
|
|
||||||
|
|
||||||
</div>
|
<!--
|
||||||
|
- #PORTFOLIO
|
||||||
</main>
|
-->
|
||||||
|
|
||||||
</body>
|
<article class="portfolio" data-page="portfolio">
|
||||||
|
|
||||||
{{/with}}
|
<header>
|
||||||
|
<h2 class="h2 article-title">Portfolio</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="projects">
|
||||||
|
|
||||||
|
<ul class="project-list">
|
||||||
|
|
||||||
|
<li class="project-item active">
|
||||||
|
<a href="https://git.petrovv.com/nikola/password_manager">
|
||||||
|
|
||||||
|
<figure class="project-img">
|
||||||
|
<div class="project-item-icon-box">
|
||||||
|
<ion-icon name="eye-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src="projects/password_manager.jpeg" loading="lazy">
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<h3 class="project-title">Password manager</h3>
|
||||||
|
|
||||||
|
<p class="project-category">CLI app</p>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="project-item active">
|
||||||
|
<a href="/list">
|
||||||
|
|
||||||
|
<figure class="project-img">
|
||||||
|
<div class="project-item-icon-box">
|
||||||
|
<ion-icon name="eye-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src="projects/list.jpeg" loading="lazy">
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<h3 class="project-title">My watch/game list</h3>
|
||||||
|
|
||||||
|
<p class="project-category"></p>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="project-item active">
|
||||||
|
<a href="https://git.petrovv.com/nikola/personal_website">
|
||||||
|
|
||||||
|
<figure class="project-img">
|
||||||
|
<div class="project-item-icon-box">
|
||||||
|
<ion-icon name="eye-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src="/images/logo.png" loading="lazy">
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<h3 class="project-title">Server</h3>
|
||||||
|
|
||||||
|
<p class="project-category">Everything running on my server</p>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="project-item active">
|
||||||
|
<a href="https://git.petrovv.com/nikola/school/src/branch/master/projektna_naloga">
|
||||||
|
|
||||||
|
<figure class="project-img">
|
||||||
|
<div class="project-item-icon-box">
|
||||||
|
<ion-icon name="eye-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src="projects/projektna_naloga.jpeg" loading="lazy">
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<h3 class="project-title">Highway Tracker</h3>
|
||||||
|
|
||||||
|
<p class="project-category">School project</p>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="project-item active">
|
||||||
|
<a href="https://git.petrovv.com/nikola/school/src/branch/master/semester_4/razvoj_programskih_sistemov/bitshifters">
|
||||||
|
|
||||||
|
<figure class="project-img">
|
||||||
|
<div class="project-item-icon-box">
|
||||||
|
<ion-icon name="eye-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src="projects/bitshift.jpeg" loading="lazy">
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<h3 class="project-title">BitShifters</h3>
|
||||||
|
|
||||||
|
<p class="project-category">unity</p>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="project-item active">
|
||||||
|
<a href="https://git.petrovv.com/nikola/school/src/branch/master/semester_4/razvoj_programskih_sistemov/tetris">
|
||||||
|
|
||||||
|
<figure class="project-img">
|
||||||
|
<div class="project-item-icon-box">
|
||||||
|
<ion-icon name="eye-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src="projects/tetris.jpeg" loading="lazy">
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<h3 class="project-title">Tetris</h3>
|
||||||
|
|
||||||
|
<p class="project-category">WPF</p>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 231 KiB After Width: | Height: | Size: 231 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
272
static/cv/index.html
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en" data-bs-theme="dark">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="shortcut icon" href="/images/logo.ico" type="image/x-icon">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>CV</title>
|
||||||
|
<script src="https://kit.fontawesome.com/cb81440751.js" crossorigin="anonymous"></script>
|
||||||
|
<!-- Import stylesheets in CSS3 -->
|
||||||
|
<link rel="stylesheet" href="stylesheets/style.css" type="text/css">
|
||||||
|
|
||||||
|
<!-- HTML2PDF.js -->
|
||||||
|
<!-- Version 0.9.3 -->
|
||||||
|
<script defer src="javascripts/html2pdf.v0.9.3.bundle.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Main Javascript file -->
|
||||||
|
<script defer src="javascripts/main.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="l-header" id="header">
|
||||||
|
<!-- Nav menu -->
|
||||||
|
<nav class="nav bd-container">
|
||||||
|
<a href="#" class="nav_logo">Nikola Petrov</a>
|
||||||
|
<div class="nav_menu" id="nav-menu">
|
||||||
|
<ul class="nav_list">
|
||||||
|
|
||||||
|
<li class="nav_item">
|
||||||
|
<a href="#home" class="nav_link active-link">
|
||||||
|
<i class="fa-solid fa-house nav_icon"></i>Home
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav_item">
|
||||||
|
<a href="#experience" class="nav_link">
|
||||||
|
<i class="fa-solid fa-briefcase nav_icon"></i>Experiences
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li class="nav_item">
|
||||||
|
<a href="#education" class="nav_link">
|
||||||
|
<i class="fa-solid fa-book-bookmark nav_icon"></i>Education
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="nav_toggle" id="nav-toggle">
|
||||||
|
<i class="fa-solid fa-bars"></i>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main class="l-main bd-container">
|
||||||
|
<!-- Resume CV -->
|
||||||
|
<div class="resume" id="area-cv">
|
||||||
|
<div class="resume_left">
|
||||||
|
|
||||||
|
<!-- HOME -->
|
||||||
|
<section class="home" id="home">
|
||||||
|
<div class="home_container section bd-grid">
|
||||||
|
|
||||||
|
<div class="home_data bd-grid">
|
||||||
|
<img src="/images/Jaz.jpg" alt="Icon picture" class="home_img" id="home-img">
|
||||||
|
<!--<img alt="Icon picture" class="home_img" id="home-img">-->
|
||||||
|
<h1 class="home_title">Nikola Petrov</h1>
|
||||||
|
<h3 class="home_profession">Developer</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="home_address bd-grid">
|
||||||
|
<span class="home_information">
|
||||||
|
<i class="fa-solid fa-location-dot home_icon"></i>Ljubljana, Slovenia
|
||||||
|
</span>
|
||||||
|
<span class="home_information">
|
||||||
|
<i class="fa-solid fa-calendar-days home_icon"></i>14, November, 2000
|
||||||
|
</span>
|
||||||
|
<span class="home_information">
|
||||||
|
<a href="mailto:nikola@petrovv.com" class="home_link">
|
||||||
|
<i class="fa-solid fa-envelope home_icon"></i>nikola@petrovv.com
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="home_information">
|
||||||
|
<a href="tel:+38670749506" class="home_link">
|
||||||
|
<i class="fa-solid fa-phone home_icon"></i>+38670749506
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="home_information">
|
||||||
|
<a href="https://petrovv.com" class="home_link">
|
||||||
|
<i class="fa-solid fa-globe home_icon"></i>https://petrovv.com
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Theme change button -->
|
||||||
|
<i class="fa-solid fa-moon change-theme" title="Theme" id="theme-button"></i>
|
||||||
|
|
||||||
|
<!-- Button to generate and download the pdf. Available for desktop. -->
|
||||||
|
<i class="fa-solid fa-download generate-pdf" title="Generate PDF" id="resume-button"></i>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- SOCIAL -->
|
||||||
|
<section class="social section">
|
||||||
|
<h2 class="section-title">Social</h2>
|
||||||
|
|
||||||
|
<div class="social_container bd-grid">
|
||||||
|
<a href="https://git.petrovv.com/explore" target="_blank" class="social_link">
|
||||||
|
<i class="fa-brands fa-git social_icon"></i>https://git.petrovv.com/explore
|
||||||
|
</a>
|
||||||
|
<a href="https://www.instagram.com/nikolainsta7" target="_blank" class="social_link">
|
||||||
|
<i class="fa-brands fa-instagram social_icon"></i>@nikolainsta7
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume_right">
|
||||||
|
|
||||||
|
<!-- EXPERIENCE -->
|
||||||
|
<section class="experience section" id="experience">
|
||||||
|
<h2 class="section-title">Experience</h2>
|
||||||
|
|
||||||
|
<div class="experience_container bd-grid">
|
||||||
|
<div class="experience_content">
|
||||||
|
<div class="experience_time">
|
||||||
|
<span class="experience_rounder"></span>
|
||||||
|
<span class="experience_line"></span>
|
||||||
|
</div>
|
||||||
|
<div class="experience_data bd-grid">
|
||||||
|
<h3 class="experience_title">HW Developer</h3>
|
||||||
|
<span class="experience_company">Spica International</span>
|
||||||
|
<span class="experience_year">17/03/2025 - 01/08/2025</span>
|
||||||
|
<p class="experience_description">
|
||||||
|
Worked on access menegment systems. Programed integrated devices, based on Buildroot using c++ and python web server.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="experience_content">
|
||||||
|
<div class="experience_time">
|
||||||
|
<span class="experience_rounder"></span>
|
||||||
|
<span class="experience_line"></span>
|
||||||
|
</div>
|
||||||
|
<div class="experience_data bd-grid">
|
||||||
|
<h3 class="experience_title">Backend/Frontend</h3>
|
||||||
|
<span class="experience_company">RRC d.o.o</span>
|
||||||
|
<span class="experience_year">01/09/2024 - 31/12/2024</span>
|
||||||
|
<p class="experience_description">
|
||||||
|
Worked on goverment websites for collage enrolment and student dorm requests.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="experience_content">
|
||||||
|
<div class="experience_time">
|
||||||
|
<span class="experience_rounder"></span>
|
||||||
|
<span class="experience_line"></span>
|
||||||
|
</div>
|
||||||
|
<div class="experience_data bd-grid">
|
||||||
|
<h3 class="experience_title">Developer</h3>
|
||||||
|
<span class="experience_company">RRC d.o.o</span>
|
||||||
|
<span class="experience_year">18/03/2024 - 31/05/2024</span>
|
||||||
|
<p class="experience_description">
|
||||||
|
Student practicum. Backend in java with frontend in ext JS and jQuery.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="experience_content">
|
||||||
|
<div class="experience_time">
|
||||||
|
<span class="experience_rounder"></span>
|
||||||
|
<span class="experience_line"></span>
|
||||||
|
</div>
|
||||||
|
<div class="experience_data bd-grid">
|
||||||
|
<h3 class="experience_title">Developer/IT</h3>
|
||||||
|
<span class="experience_company">LightAct</span>
|
||||||
|
<span class="experience_year">01/07/2022 - 01/09/2022</span>
|
||||||
|
<p class="experience_description">
|
||||||
|
I helped maintaining data base, worked on the application (integrated a capture card and IP camera), assembled new server rack, installed new UTP/power connectors in the office.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="experience_content">
|
||||||
|
<div class="experience_time">
|
||||||
|
<span class="experience_rounder"></span>
|
||||||
|
<span class="experience_line"></span>
|
||||||
|
</div>
|
||||||
|
<div class="experience_data bd-grid">
|
||||||
|
<h3 class="experience_title">Mentor</h3>
|
||||||
|
<span class="experience_company">Institute 404</span>
|
||||||
|
<span class="experience_year">08/06/2020 - 19/06/2020</span>
|
||||||
|
<p class="experience_description">
|
||||||
|
I helped primary school children with their projects with soldering, laser cutting, and building.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="experience_content">
|
||||||
|
<div class="experience_time">
|
||||||
|
<span class="experience_rounder"></span>
|
||||||
|
<span class="experience_line"></span>
|
||||||
|
</div>
|
||||||
|
<div class="experience_data bd-grid">
|
||||||
|
<h3 class="experience_title">Maintenance technician</h3>
|
||||||
|
<span class="experience_company">Hella Saturnos d.o.o.</span>
|
||||||
|
<span class="experience_year">04/09/2018 - 18/01/2019</span>
|
||||||
|
<p class="experience_description">
|
||||||
|
I maintained and repaired machines from plastic presses to personal stations.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="experience_content">
|
||||||
|
<div class="experience_time">
|
||||||
|
<span class="experience_rounder"></span>
|
||||||
|
</div>
|
||||||
|
<div class="experience_data bd-grid">
|
||||||
|
<h3 class="experience_title">Maintenance technician</h3>
|
||||||
|
<span class="experience_company">Best Western Premier Hotel Slon</span>
|
||||||
|
<span class="experience_year">01/03/2018 - 04/05/2018</span>
|
||||||
|
<p class="experience_description">
|
||||||
|
I helped with setting up the conference/event rooms. I helped customers and fixed problems like replacing light bulbs, wall sockets, hair-dryers.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- EDUCATION -->
|
||||||
|
<section class="education section" id="education">
|
||||||
|
<h2 class="section-title">Education</h2>
|
||||||
|
|
||||||
|
<div class="education_container bd-grid">
|
||||||
|
|
||||||
|
<div class="education_content">
|
||||||
|
<div class="education_time">
|
||||||
|
<span class="education_rounder"></span>
|
||||||
|
<span class="education_line"></span>
|
||||||
|
</div>
|
||||||
|
<div class="education_data bd-grid">
|
||||||
|
<h3 class="education_title">Graduate engineer of computer science and information technology.</h3>
|
||||||
|
<span class="education_studies">(FERI) Faculty of Electrical Engineering and Computer Science, University of Maribor</span>
|
||||||
|
<span class="education_year">01/10/2021 - CURRENT</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="education_content">
|
||||||
|
<div class="education_time">
|
||||||
|
<span class="education_rounder"></span>
|
||||||
|
</div>
|
||||||
|
<div class="education_data bd-grid">
|
||||||
|
<h3 class="education_title">Electrotechnician.</h3>
|
||||||
|
<span class="education_studies">(SSTS Siska) Secondary school of technical professions siska</span>
|
||||||
|
<span class="education_year">01/09/2016 - 07/07/2021</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<a href="#" class="scrolltop" id="scroll-top">
|
||||||
|
<i class="fa-solid fa-arrow-up scrolltop_icon"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 78 KiB |
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
// Enable latest features
|
|
||||||
"lib": [
|
|
||||||
"ESNext",
|
|
||||||
"DOM"
|
|
||||||
],
|
|
||||||
"target": "ESNext",
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"jsx": "react",
|
|
||||||
"jsxFactory": "elements.createElement",
|
|
||||||
"allowJs": true,
|
|
||||||
// Bundler mode
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"verbatimModuleSyntax": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
// Best practices
|
|
||||||
"strict": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
// Some stricter flags (disabled by default)
|
|
||||||
"noUnusedLocals": false,
|
|
||||||
"noUnusedParameters": false,
|
|
||||||
"noPropertyAccessFromIndexSignature": false,
|
|
||||||
"types": [
|
|
||||||
"bun-types"
|
|
||||||
],
|
|
||||||
"baseUrl": "./"
|
|
||||||
}
|
|
||||||
}
|
|
||||||