//#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 += ""; }); text += ""; } text += ""; Object.values(row).forEach(t => { if (t instanceof Date) t = t.toLocaleString('ru-RU') text += ""; }); text += ` `; if (index == Object.values(jsonData).length - 1) { Object.values(row).forEach(t => { if (t instanceof Date) t = t.toLocaleString('ru-RU') text += ""; }); text += ""; } //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
" + t + "
" + (t == null ? "" : t.toString()) + "
" + `` + "" + `Create` + "