//#region import
var http = require('http');
var fs = require('fs');
const express = require('express');
const WebSocket = require('ws');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
server.listen(80, () => {
console.log(`Server started on port ${server.address().port} :)`);
});
const pg = require('pg');
const { Pool, Client } = pg
const pool = new Pool({
user: 'd',
password: '123',
host: '192.168.56.104',
port: 5432,
database: 'mydb',
})
async function query(q) {
return (await pool.query(q)).rows;
}
//#endregion
//#region Server CRUD Interface
function getTables() {
return query(`SELECT table_name
FROM information_schema.tables
WHERE table_schema='public'
AND table_type='BASE TABLE';`);
}
async function getTable(data) {
let table = data[0], filters = data[1];
if (table == "moovie") {
//q =
}
let q = `select * from ` + table;
let isFilters = false;
if (Array.isArray(filters) && filters.length != 0) {
q += " WHERE ";
filters.forEach(f => {
isFilters = true;
let n = typeof f[1] == "string" && f[1].length > 1 && (f[1][0] == ">" || f[1][0] == "<");
if (f[0] != null && f[1] != "") {
if (typeof f[1] == "number")
q += f[0] + "=" + f[1] + " AND ";
else
q += f[0] + (n ? "" : " LIKE '%") + f[1] + (n ? "" : "%'") + " AND ";
}
})
}
if (isFilters) q = q.slice(0, -4);
let querry = q;
try {
q = await query(q);
} catch {
return null;
}
q[q.length] = table;
q["querry"] = querry;
return q;
}
async function getDifficultMoovies() {
let q = `SELECT moovie_id, moovie_name, rating_rate, moovie_productionYear, moovie_about, moovie_timeAdded, subscription_name, subscription_price
FROM moovie
INNER JOIN rating ON moovie.moovie_id = rating.rating__fk_moovie
INNER JOIN subscription ON subscription_id = (
select customer__fk_subsription from customer
where customer_id = 9
)
where moovie_id IN (
select mooviesubscription_fk_moovie_id from moovieSubscription
Where moovieSubscription_fk_subscription_id = (
select customer__fk_subsription from customer
where customer_id = 9
)
);
`;
let querry = q;
q = await query(q);
q["querry"] = querry;
return q;
}
//#region Generate Interface
function createTabless() {
let q = `DROP TABLE if exists moovie cascade;
DROP SEQUENCE if exists SEQ_moovie;
CREATE SEQUENCE SEQ_moovie INCREMENT BY 1 START WITH 1;
CREATE TABLE moovie (
moovie_id integer PRIMARY KEY DEFAULT nextval('SEQ_moovie'),
moovie_name varchar(50) NOT NULL,
moovie_productionYear integer NOT NULL,
moovie_about varchar(50) NOT NULL,
moovie_timeAdded date NOT NULL
);
DROP TABLE if exists genre cascade;
DROP SEQUENCE if exists SEQ_genre;
CREATE SEQUENCE SEQ_genre INCREMENT BY 1 START WITH 1;
CREATE TABLE genre (
genre_id integer PRIMARY KEY DEFAULT nextval('SEQ_genre'),
genre_name varchar(50) NOT NULL
);
DROP TABLE if exists moovieGenre cascade;
DROP SEQUENCE if exists SEQ_moovieGenre;
CREATE SEQUENCE SEQ_moovieGenre INCREMENT BY 1 START WITH 1;
CREATE TABLE moovieGenre (
moovieGenre_id integer PRIMARY KEY DEFAULT nextval('SEQ_moovieGenre'),
moovieGenre_fk_moovie_id int REFERENCES moovie (moovie_id),
moovieGenre_fk_genre_id int REFERENCES genre (genre_id)
);
DROP TABLE if exists subscription cascade;
DROP SEQUENCE if exists SEQ_subscription;
CREATE SEQUENCE SEQ_subscription INCREMENT BY 1 START WITH 1;
CREATE TABLE subscription (
subscription_id integer PRIMARY KEY DEFAULT nextval('SEQ_subscription'),
subscription_name varchar(50) NOT NULL,
subscription_price integer NOT NULL,
subscription_fk_parentSubscription_id int NULL REFERENCES subscription (subscription_id)
);
DROP TABLE if exists moovieSubscription cascade;
DROP SEQUENCE if exists SEQ_moovieSubscription;
CREATE SEQUENCE SEQ_moovieSubscription INCREMENT BY 1 START WITH 1;
CREATE TABLE moovieSubscription (
moovieSubscription_id integer PRIMARY KEY DEFAULT nextval('SEQ_moovieSubscription'),
moovieSubscription_fk_moovie_id int REFERENCES moovie (moovie_id),
moovieSubscription_fk_subscription_id int REFERENCES subscription (subscription_id)
);
DROP TABLE if exists customer cascade;
DROP SEQUENCE if exists SEQ_customer;
CREATE SEQUENCE SEQ_customer INCREMENT BY 1 START WITH 1;
CREATE TABLE customer (
customer_id integer PRIMARY KEY DEFAULT nextval('SEQ_customer'),
customer_name varchar(50) NOT NULL,
customer_email varchar(50) NOT NULL,
customer_password varchar(50) NOT NULL,
customer_age int NOT NULL,
customer_phone varchar(50) NOT NULL,
customer__fk_subsription int NOT NULL REFERENCES subscription (subscription_id)
);
DROP TABLE if exists rating cascade;
DROP SEQUENCE if exists SEQ_rating;
CREATE SEQUENCE SEQ_rating INCREMENT BY 1 START WITH 1;
CREATE TABLE rating (
rating_id integer PRIMARY KEY DEFAULT nextval('SEQ_rating'),
rating_rate int NOT NULL,
rating_fk_user int NOT NULL REFERENCES customer (customer_id),
rating__fk_moovie int NOT NULL REFERENCES moovie (moovie_id)
);
COMMENT ON TABLE public.moovie IS 'Это таблица о Фильмах';
ALTER TABLE public.moovie ALTER COLUMN moovie_name SET NOT NULL;
ALTER TABLE public.moovie ALTER COLUMN moovie_name SET DEFAULT 'Название фильма';
ALTER TABLE public.moovie ALTER COLUMN moovie_productionYear SET NOT NULL;
--ALTER TABLE public.moovie ALTER COLUMN moovie_productionYear SET DEFAULT 2000;
ALTER TABLE public.moovie ALTER COLUMN moovie_about SET NOT NULL;
ALTER TABLE public.moovie ALTER COLUMN moovie_about SET DEFAULT 'Описание фильма';
ALTER TABLE public.moovie ALTER COLUMN moovie_timeAdded SET NOT NULL;
ALTER TABLE public.moovie ALTER COLUMN moovie_timeAdded SET DEFAULT '2024-06-16 12:00:00';
COMMENT ON TABLE public.genre IS 'Это таблица о Жанрах фильмов';
ALTER TABLE public.genre ALTER COLUMN genre_name SET NOT NULL;
ALTER TABLE public.genre ALTER COLUMN genre_name SET DEFAULT 'Название жанра';
COMMENT ON TABLE public.moovieGenre IS 'Это таблица много ко многим для таблиц Фильм и Жанр';
ALTER TABLE public.moovieGenre ALTER COLUMN moovieGenre_fk_moovie_id SET NOT NULL;
ALTER TABLE public.moovieGenre ALTER COLUMN moovieGenre_fk_genre_id SET NOT NULL;
COMMENT ON TABLE public.subscription IS 'Это таблица о подписках на фильмы';
ALTER TABLE public.subscription ALTER COLUMN subscription_name SET NOT NULL;
ALTER TABLE public.subscription ALTER COLUMN subscription_name SET DEFAULT 'Подписка';
ALTER TABLE public.subscription ALTER COLUMN subscription_price SET NOT NULL;
ALTER TABLE public.subscription ALTER COLUMN subscription_price SET DEFAULT 999;
COMMENT ON TABLE public.customer IS 'Это таблица о пользователях';
ALTER TABLE public.customer ALTER COLUMN customer_name SET NOT NULL;
ALTER TABLE public.customer ALTER COLUMN customer_name SET DEFAULT 'Пользователь 1';
ALTER TABLE public.customer ALTER COLUMN customer_password SET NOT NULL;
ALTER TABLE public.customer ALTER COLUMN customer_password SET DEFAULT '123';
ALTER TABLE public.customer ALTER COLUMN customer_age SET NOT NULL;
ALTER TABLE public.customer ALTER COLUMN customer_age SET DEFAULT '18';
ALTER TABLE public.customer ALTER COLUMN customer_phone SET NOT NULL;
ALTER TABLE public.customer ALTER COLUMN customer__fk_subsription SET NOT NULL;
COMMENT ON TABLE public.rating IS 'Это таблица об отзывах про фильмы';
ALTER TABLE public.rating ALTER COLUMN rating_rate SET NOT NULL;
ALTER TABLE public.rating ALTER COLUMN rating_rate SET DEFAULT 10;
ALTER TABLE public.rating ALTER COLUMN rating_fk_user SET NOT NULL;
ALTER TABLE public.rating ALTER COLUMN rating_fk_user SET DEFAULT 0;
ALTER TABLE public.rating ALTER COLUMN rating__fk_moovie SET NOT NULL;
ALTER TABLE public.rating ALTER COLUMN rating__fk_moovie SET DEFAULT 0;
ALTER TABLE public.mooviegenre DROP CONSTRAINT mooviegenre_mooviegenre_fk_genre_id_fkey;
ALTER TABLE public.mooviegenre ADD CONSTRAINT mooviegenre_mooviegenre_fk_genre_id_fkey FOREIGN KEY (mooviegenre_fk_genre_id) REFERENCES public.genre(genre_id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE public.mooviegenre DROP CONSTRAINT mooviegenre_mooviegenre_fk_moovie_id_fkey;
ALTER TABLE public.mooviegenre ADD CONSTRAINT mooviegenre_mooviegenre_fk_moovie_id_fkey FOREIGN KEY (mooviegenre_fk_moovie_id) REFERENCES public.moovie(moovie_id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE public.mooviesubscription DROP CONSTRAINT mooviesubscription_mooviesubscription_fk_moovie_id_fkey;
ALTER TABLE public.mooviesubscription ADD CONSTRAINT mooviesubscription_mooviesubscription_fk_moovie_id_fkey FOREIGN KEY (mooviesubscription_fk_moovie_id) REFERENCES public.moovie(moovie_id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE public.mooviesubscription DROP CONSTRAINT mooviesubscription_mooviesubscription_fk_subscription_id_fkey;
ALTER TABLE public.mooviesubscription ADD CONSTRAINT mooviesubscription_mooviesubscription_fk_subscription_id_fkey FOREIGN KEY (mooviesubscription_fk_subscription_id) REFERENCES public."subscription"(subscription_id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE public."subscription" DROP CONSTRAINT subscription_subscription_fk_parentsubscription_id_fkey;
ALTER TABLE public."subscription" ADD CONSTRAINT subscription_subscription_fk_parentsubscription_id_fkey FOREIGN KEY (subscription_fk_parentsubscription_id) REFERENCES public."subscription"(subscription_id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE public.customer DROP CONSTRAINT customer_customer__fk_subsription_fkey;
ALTER TABLE public.customer ADD CONSTRAINT customer_customer__fk_subsription_fkey FOREIGN KEY (customer__fk_subsription) REFERENCES public."subscription"(subscription_id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE public.rating DROP CONSTRAINT rating_rating__fk_moovie_fkey;
ALTER TABLE public.rating ADD CONSTRAINT rating_rating__fk_moovie_fkey FOREIGN KEY (rating__fk_moovie) REFERENCES public.moovie(moovie_id) ON DELETE CASCADE ON UPDATE CASCADE;
`;
return pool.query(q);
}
async function del() {
let q = `
DELETE FROM moovieSubscription;
DELETE FROM customer;
DELETE FROM genre;
DELETE FROM subscription;
DELETE FROM moovie;
ALTER SEQUENCE SEQ_moovie RESTART WITH 1;
ALTER SEQUENCE SEQ_genre RESTART WITH 1;
ALTER SEQUENCE SEQ_moovieGenre RESTART WITH 1;
ALTER SEQUENCE SEQ_subscription RESTART WITH 1;
ALTER SEQUENCE SEQ_moovieSubscription RESTART WITH 1;
ALTER SEQUENCE SEQ_customer RESTART WITH 1;
ALTER SEQUENCE SEQ_rating RESTART WITH 1;
`;
await pool.query(q);
}
let genress = ` Action Thriller Western Fantasy Science fiction Comedy Satire Drama Adventure Mystery Romantic comedy Melodrama Romance Hybrid genre Fairy tale Coming-of-age story Fantasy Suspense Detective fiction Biography Dark comedy Mystery Apocalyptic and post-apocalyptic fiction Slapstick Screenplay Historical drama Comedy horror Historical fantasy Body horror Tech noir Legal drama Film criticism`.split(" ");
async function createRandomGenres() {
let q = `
INSERT INTO genre VALUES
`;
for (let i = 0; i < count; i++) {
q += "(DEFAULT, '" + genress[Math.round((genress.length - 1) * Math.random())] + "'),";
}
let w = await pool.query(q.slice(0, -1));
w["querry"] = "INSERT INTO genre VALUES";
return w;
}
async function createRandomUsers(subscriptionsLength = count) {
let q = `
INSERT INTO customer VALUES
`;
for (let i = 0; i < count; i++) {
let w = Math.round(Math.random() * (subscriptionsLength - 2) + 1);
//console.log(w);
q += "(DEFAULT, " +
"'user" + i + "', 'email" + i + "@gmail.com'," + " 'password" + i + "', "
+ Math.round(Math.random() * (50 - 18) + 18) + ", " + "'phoneNumber" + i + "', "
+ w
+ "),";
}
let w = await pool.query(q.slice(0, -1));
w["querry"] = "INSERT INTO customer VALUES";
return w;
}
async function createRandomSubscriptions() {
let q = `
INSERT INTO subscription VALUES
`;
for (let i = 0; i < count; i++) {
q += "(DEFAULT, 'Подписка " + i.toString() + "', "
+ Math.round(Math.random() * 5000) + " " + ""/* Math.max(1, Math.round(Math.random() * (i-1))) */ + "),";
}
let w = await pool.query(q.slice(0, -1));
w["querry"] = " INSERT INTO subscription VALUES";
return w;
//query(q);
}
async function createRandomMoovies() {
let q = `
INSERT INTO moovie VALUES
`;
for (let i = 0; i < count; i++) {
q += "(DEFAULT, 'Фильм" + i + "', "
+ Math.round(Math.random() * (2024 - 1980) + 1980) + ", 'Описание" + i + "', '" + "2004-01-22" + "'),";
}
let w = await pool.query(q.slice(0, -1));
w["querry"] = "INSERT INTO moovie VALUES";
return w;
//query(q);
}
async function createRandomMoovieSubscriptions() {
let q = `
INSERT INTO moovieSubscription VALUES
`;
for (let i = 1; i < count / 10; i++) {
for (let t = 1; t < count / 5; t++) {
q += "(DEFAULT, " + t.toString() + ", " + i.toString() + "),";
}
}
let w = await pool.query(q.slice(0, -1));
w["querry"] = "INSERT INTO moovieSubscription VALUES";
return w;
//query(q);
}
async function createRandomRatings() {
let q = `
INSERT INTO rating VALUES
`;
for (let t = 1; t < count; t++) {
q += "(DEFAULT, " + Math.round(Math.random() * 10) + ", " +
Math.round(Math.random() * (count - 2) + 1) + ", " +
Math.round(Math.random() * (count - 2) + 1) + "),";
}
let w = await pool.query(q.slice(0, -1));
w["querry"] = "INSERT INTO rating VALUES";
return w;
//query(q);
}
//#endregion
//#endregion
//#region Measure
var result = [];
async function measure(f, args = [], multipleTimes = true) {
return new Promise(async resolve => {
let commonTime = null, res = "";
for (let i = 0; i < 10; i++) {
let t1 = process.hrtime.bigint();
res = await f(...args);
let t2 = process.hrtime.bigint();
if (commonTime == null) commonTime = (Number(t2 - t1) / 10 ** 6);
else commonTime = (commonTime + (Number(t2 - t1) / 10 ** 6)) / 2;
if (multipleTimes == false) break;
}
//console.log(Number(t2 - t1).toString() + " nanoseconds");
result.push(res["querry"] + " : " + commonTime.toFixed(1).toString() + "мс"/* " милиСекунд (10*-3)" */);
resolve();
})
}
var count = 15;
async function createTables() {
let ts = await getTables();
if (true || ts.length == 0) {
await createTabless();
}
//await del();
await measure(createRandomSubscriptions, [], false);
await measure(createRandomGenres, [], false);
await measure(createRandomUsers, [], false);
await measure(createRandomMoovies, [], false);
await measure(createRandomMoovieSubscriptions, [], false);
await measure(createRandomRatings, [], false);
await measure(getTable, [["subscription", []]])
await measure(getTable, [["moovie", []]])
await measure(getTable, [["moovie", [["moovie_productionYear", ">2021"], ["moovie_name", "2"]]]]);
await measure(getTable, [["genre", []]]);
await measure(getTable, [["moovieGenre", []]]);
await measure(getTable, [["subscription", []]]);
await measure(getTable, [["moovieSubscription", []]]);
await measure(getTable, [["customer", []]]);
await measure(getTable, [["rating", []]]);
await measure(getDifficultMoovies);
result.unshift("№ Запрос:Среднее время")
console.log(result.slice(0, -1).map((a, index) => ((index).toString() + ": " + a + ": ")).join(" "));
console.log(result[result.length - 1]);
}
//#endregion
function wsInterface() {
try {
ws = new WebSocket('ws://localhost:80')
} catch { }
ws.onopen = () => {
//document.getElementById('selects').children[0].select = true;
getTableInterface(document.getElementById('selects'));
console.log('ws opened on browser')
//ws.send('hello world')
}
ws.onclose = () => {
location.reload()
};
ws.onmessage = (message) => {
let data = JSON.parse(message.data).data;
callbacks[data[0].toString()](data[1]);
//console.log(`message received ${message}`)
}
}
wss.on('connection', (ws) => {
wsocket = ws;
ws.on('message', async (message) => {
let data = JSON.parse(message);
if (data?.type == "getTable") {
send([data.querry[0], await getTable(data.querry[1])]);
}
else if (data?.type == "create") {
pool.query(data.querry)
}
else if (data?.type == "update") {
pool.query(data.querry)
}
else if (data?.type == "delete") {
let qe = `
SET FOREIGN_KEY_CHECKS=0;
` + data.querry + `
SET FOREIGN_KEY_CHECKS=1;
`;
pool.query(data.querry)
}
});
});
//#region HTML CRUD Interface
function create(th) {
let newValues = Array.from(th.parentElement.children).slice(0, -1).map(a => "'" + a.children[0].value + "'");
ws.send(JSON.stringify({
"type": "create",
"querry": "INSERT INTO " + th.parentElement.parentElement.parentElement.getAttribute("name") + " VALUES (DEFAULT, " + newValues.slice(1, newValues.length).join(", ") + ")"
}));
setTimeout(() => {
getTableInterface(document.getElementById("selects"))
}, 100);
}
function change(th) {
let textarea = true;
let a = Array.from(th.parentElement.children);
a.slice(0, -2).forEach(element => {
if (textarea)
element.innerHTML = ``;
else
element.innerHTML = ``;
})
a[a.length - 2].onclick = function () { update(this) };
a[a.length - 2].innerHTML = `
`;
}
function update(th) {
let a = Array.from(th.parentElement.children);
let querry = `UPDATE ` + th.parentElement.parentElement.parentElement.getAttribute("name") + " SET ";
let types = Array.from(th.parentElement.parentElement.children[0].children).map(a => a.getAttribute("type"));
let at = Array.from(th.parentElement.parentElement.children[0].children).map(a => a.outerText);
a.slice(1, -2).forEach((element, index) => {
querry += at[index + 1] + " = ";
if (types[index + 1] == "number")
querry += element.children[0].value + ",";
else
querry += "'" + element.children[0].value + "',";
})
querry = querry.slice(0, -1);
querry += " WHERE " + at[0] + "=" + a[0].children[0].value;
ws.send(JSON.stringify({
"type": "update",
"querry": querry
}));
a.slice(0, -2).forEach(element => {
element.innerHTML = element.children[0].value
})
a[a.length - 2].onclick = function () { change(this) };
a[a.length - 2].innerHTML = `
`;
}
function remove(th) {
let a = Array.from(th.parentElement.children);
let at = Array.from(th.parentElement.parentElement.children[0].children).map(a => a.outerText);
ws.send(JSON.stringify({
"type": "delete",
"querry": "DELETE FROM " + th.parentElement.parentElement.parentElement.getAttribute("name") + " Cascade WHERE " + at[0] + "=" + + a[0].outerText
}));
th.parentElement.remove();
}
async function getTableInterface(th) {
let newFilter = th.id == "selects";
var g = document.getElementById("filter");
if (newFilter == false && g != null) {
let types = Array.from(document.getElementById("table").children[0].children[0].children).map(a => a.getAttribute("type"));
let at = Array.from(document.getElementById("table").children[0].children[0].children).map(a => a.outerText);;
g = Array.from(g.children[0].children[0].children)
.map((a, index) => [at[index], (types[index] == "number" && a.children[0].value != "" && a.children[0].value[0] != "<" && a.children[0].value[0] != ">" ? Number(a.children[0].value) : a.children[0].value)]);
}
else {
g = null
}
let table = [await sendInterface("getTable", [document.querySelector("#selects")[document.querySelector("#selects").selectedIndex].value, g])];
//let tables = await createTables();
if (table[0] == null) {
return;
}
table = table.map(t => createTableFromJSON(t, newFilter));
let d = document.getElementById("table")
if (newFilter) d = document.body.children[1];
if (table[0].length < 50) {
while (d.children[0].childNodes.length > 1) {
d.children[0].removeChild(d.children[0].lastChild);
}
} else {
d.innerHTML = table;
}
document.body.children[1].id = "";
}
//#endregion
//#region html Table Interfaces
function createTableFromJSON(jsonData, newFilter = false) {
let name = jsonData[jsonData.length - 1];
delete jsonData[jsonData.length - 1];
let text = "";
if (newFilter || document.getElementById("filter") == null) {
text = "
";
Object.values(jsonData).forEach((row, index) => {
if (index == 0) {
text += "
";
Object.keys(row).forEach((t, index) => {
text += "
" + `` + "
";
});
text += "
" + `` + "
";
text += "
" + `` + "
";
text += "
";
}
});
text += "
";
}
text += "
";
Object.values(jsonData).forEach((row, index) => {
if (index == 0) {
text += "
";
let v = Object.values(row);
Object.keys(row).forEach((t, index) => {
text += "
" + t + "
";
});
text += "
";
}
text += "
";
Object.values(row).forEach(t => {
if (t instanceof Date) t = t.toLocaleString('ru-RU')
text += "
" + (t == null ? "" : t.toString()) + "
";
});
text += `
`;
if (index == Object.values(jsonData).length - 1) {
Object.values(row).forEach(t => {
if (t instanceof Date) t = t.toLocaleString('ru-RU')
text += "
" + `` + "
";
});
text += "
" + `Create` + "
";
}
//text += " ";
});
return text;
};
let functionsToImport = [
createTableFromJSON,
create, change, update, remove, getTableInterface,
wsInterface, sendInterface,
"var ws; var id = 0, callbacks = []; wsInterface(); let w; ",
];
var once = true;
app.get('/', async function (request, response) {
if (once) {
once = false;
let tables = await createTables();
}
let ts = await getTables();
ts = ts.map(t => {
return ""
var option = document.createElement("option");
option.value = t.table_name;
option.text = t.table_name;
return option.toString();
})
let qw = `
Document
`
+ " " + `
`;
response.writeHeader(200, { 'Content-Type': 'html' });
response.write(qw);
response.end();
})
let wsocket;
function send(data) {
wsocket.send(JSON.stringify({
data: data
}));
}
function sendInterface(s, data) {
return new Promise(resolve => {
ws.send(JSON.stringify({
"type": s,
"querry": [id, data]
}));
callbacks[id] = resolve;
id++
})
}
//#endregion