From 6335e3594038810e808bf1209800612d5085c2c1 Mon Sep 17 00:00:00 2001 From: kry008 Date: Wed, 1 Jan 2025 18:30:11 +0100 Subject: [PATCH] Przygotowanie --- .gitignore | 5 + macOs-i-Linux.sh | 54 ++ readme.md | 21 + serwer/Dockerfile | 22 + serwer/Dockerfile.db | 16 + serwer/app/apiRoutes.js | 182 ++++ serwer/app/baza-import.sql | 159 ++++ serwer/app/baza.sql | 156 ++++ serwer/app/css/style.css | 443 ++++++++++ serwer/app/eksport/none | 0 serwer/app/func.js | 463 ++++++++++ serwer/app/index.js | 328 +++++++ serwer/app/liczacy.js | 453 ++++++++++ serwer/app/logs/none | 0 serwer/app/package.json | 27 + serwer/app/panelRoutes.js | 1721 ++++++++++++++++++++++++++++++++++++ serwer/app/static/logo.png | Bin 0 -> 122100 bytes serwer/app/tmp/none | 0 serwer/docker-compose.yml | 29 + serwer/logs/none | 0 serwer/tmp/none | 0 windows.bat | 56 ++ 22 files changed, 4135 insertions(+) create mode 100644 .gitignore create mode 100755 macOs-i-Linux.sh create mode 100644 readme.md create mode 100644 serwer/Dockerfile create mode 100644 serwer/Dockerfile.db create mode 100644 serwer/app/apiRoutes.js create mode 100644 serwer/app/baza-import.sql create mode 100644 serwer/app/baza.sql create mode 100644 serwer/app/css/style.css create mode 100644 serwer/app/eksport/none create mode 100644 serwer/app/func.js create mode 100644 serwer/app/index.js create mode 100644 serwer/app/liczacy.js create mode 100644 serwer/app/logs/none create mode 100644 serwer/app/package.json create mode 100644 serwer/app/panelRoutes.js create mode 100644 serwer/app/static/logo.png create mode 100644 serwer/app/tmp/none create mode 100644 serwer/docker-compose.yml create mode 100644 serwer/logs/none create mode 100644 serwer/tmp/none create mode 100644 windows.bat diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98399cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +serwer/node_modules +serwer/app/package-lock.json +serwer/package-lock.json +serwer/.env +serwer/prod.env \ No newline at end of file diff --git a/macOs-i-Linux.sh b/macOs-i-Linux.sh new file mode 100755 index 0000000..e0a45ef --- /dev/null +++ b/macOs-i-Linux.sh @@ -0,0 +1,54 @@ +#!/bin/bash +cd serwer +# Funkcja do sprawdzania dostępności polecenia +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Funkcja do uruchamiania polecenia na różnych systemach +run_command() { + if [ "$OS" = "Windows_NT" ]; then + cmd.exe /C "$1" + else + eval "$1" + fi +} + +# Sprawdzenie systemu operacyjnego +if [ "$OS" = "Windows_NT" ]; then + echo "Wykryto system Windows." + WINDOWS=true +else + echo "Wykryto system Unix/MacOS." + WINDOWS=false +fi + +# Sprawdź czy jest dostępne `docker compose` +if command_exists "docker" && docker compose version >/dev/null 2>&1; then + echo "Docker Compose dostępny jako 'docker compose'." + COMPOSE_COMMAND="docker compose" + +# Sprawdź czy jest dostępne `docker-compose` +elif command_exists "docker-compose"; then + echo "Docker Compose dostępny jako 'docker-compose'." + COMPOSE_COMMAND="docker-compose" + +# Jeśli brak obu, wyświetl komunikat i zakończ +else + echo "Docker Compose nie jest zainstalowany. Zainstaluj go przed uruchomieniem tego skryptu." + exit 1 +fi + +# Uruchomienie docker compose up +if [ "$WINDOWS" = true ]; then + run_command "$COMPOSE_COMMAND up --build -d" +else + $COMPOSE_COMMAND up --build -d +fi + +if [ $? -eq 0 ]; then + echo "Docker Compose został uruchomiony pomyślnie." +else + echo "Wystąpił błąd podczas uruchamiania Docker Compose." + exit 1 +fi diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..0e32588 --- /dev/null +++ b/readme.md @@ -0,0 +1,21 @@ +Domyślne dane dostępowe: + +``` +Login: szef +Hasło: szef +``` + +Domyślnie działa na ADRESIP:8880 + +## Jeżeli chcesz korzystać za darmo, w stopka musi pozostać niezmieniona, a także widoczna na każdej stronie z wyjątkiem stron druku list + +# WOŚP APP by KRY008 +Pomocnik dla sztabu przy rozliczeniach wolontariuszy + +## Wymagania +- Docker desktop (wybrać Docker Personal na stronie https://www.docker.com/products/docker-desktop i założyć darmowe konto) + +## Instalacja +Uruchomić skrypt odpowiednio dla systemu operacyjnego: +- Windows: `windows.bat` +- MacOS lub Linux: `macOs-i-Linux.sh` \ No newline at end of file diff --git a/serwer/Dockerfile b/serwer/Dockerfile new file mode 100644 index 0000000..4041b61 --- /dev/null +++ b/serwer/Dockerfile @@ -0,0 +1,22 @@ +#Dockerfile +# Użycie oficjalnego obrazu Node.js +FROM node:20 + +# Ustawienie katalogu roboczego w kontenerze +WORKDIR /usr/src/app + +# Kopiowanie plików aplikacji +COPY app/ ./ +COPY prod.env .env + +# Instalowanie zależności +RUN npm install + +# Ustawienie zmiennej środowiskowej (opcjonalnie) +ENV NODE_ENV=production + +# Ekspozycja portu (jeśli aplikacja tego wymaga) +EXPOSE 8880 + +# Uruchomienie aplikacji +CMD ["npm", "start"] diff --git a/serwer/Dockerfile.db b/serwer/Dockerfile.db new file mode 100644 index 0000000..4c99bdd --- /dev/null +++ b/serwer/Dockerfile.db @@ -0,0 +1,16 @@ +FROM mysql:8.0 + +# Definiowanie argumentów, które można przekazać z docker-compose.yml +ARG MYSQL_ROOT_PASSWORD +ARG MYSQL_DATABASE +ARG MYSQL_USER +ARG MYSQL_PASSWORD + +# Ustawienie zmiennych środowiskowych na podstawie argumentów +ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} +ENV MYSQL_DATABASE=${MYSQL_DATABASE} +ENV MYSQL_USER=${MYSQL_USER} +ENV MYSQL_PASSWORD=${MYSQL_PASSWORD} + +# Kopiowanie skryptu SQL do inicjalizacji bazy danych +COPY app/baza-import.sql /docker-entrypoint-initdb.d/ diff --git a/serwer/app/apiRoutes.js b/serwer/app/apiRoutes.js new file mode 100644 index 0000000..74ca410 --- /dev/null +++ b/serwer/app/apiRoutes.js @@ -0,0 +1,182 @@ +const express = require('express'); +const route = express.Router(); +const fs = require('fs'); + +require('dotenv').config(); +//mysql +var mysql = require('mysql2'); +var con = mysql.createConnection({ + host: process.env.MYSQLHOST, + user: process.env.MYSQLUSER, + password: process.env.MYSQLPASS, + port : process.env.MYSQLPORT, + database: process.env.MYSQLDB, + insecureAuth : true + +}); +con.connect(function(err) { + if (err) throw err; + console.log('Connected!'); + +}); +var cookie = require('cookie'); +var app = express(); +var bodyParser = require('body-parser'); +app.use(bodyParser.urlencoded({ + extended: true +})); +//import functions from func.js +const {makeid, loger} = require('./func.js'); + +//login via api +route.post('/', function(req, res) { + //in post there is login and password (already hashed) + console.log(req.body); + var login = req.body.login; + var password = req.body.password; + con.query('SELECT * FROM login WHERE login = ? AND haslo = ? AND aktywne = 1', [login, password], function(err, result) { + if (err) throw err; + if (result.length > 0) { + var token = makeid(32); + con.query('INSERT INTO tokeny (token, typ, userId) VALUES (?, 1, ?)', [token, result[0].id], function(err, result) { + if (err) throw err; + res.cookie('token', token); + res.json({status: 'ok', token: token}); + loger(fs, 'Użytkownik zalogował się do panelu, token: ' + token, 'info'); + }); + } + else { + //send code 401 + res.status(401).json({status: 'error'}); + loger(fs, 'Użytkownik nie zalogował się do panelu, login: ' + login, 'error'); + } + }); +}); +route.use(function(req, res, next) { + var cookies = cookie.parse(req.headers.cookie || '') || req.body.token || req.query.token || req.headers['x-access-token']; + var token = cookies.token; + con.query('SELECT * FROM tokeny, login WHERE token = ? AND tokeny.userId = login.id AND aktywny = 1', [token], function(err, result) { + if (err) throw err; + if (result.length > 0) { + req.user = result[0]; + next(); + } else { + res.json({status: 'not logged', goTo: '/api'}); + loger(fs, 'Użytkownik nie zalogował się do panelu, token: ' + token, 'error'); + } + }); +}); + +route.all("/test", function(req, res) { + res.json({status: 'ok'}); + loger(fs, 'Użytkownik wykonał test', 'info'); +}); + +route.get("/osobyLiczace", function(req, res) { + con.query('SELECT * FROM liczacy WHERE aktywne = 1', function(err, result) { + if (err) throw err; + res.json(result); + loger(fs, 'Użytkownik wyświetlił listę osób liczących', 'info'); + }); +}); + +route.post("/osobyLiczace", function(req, res) { + //sprawdzenie czy nie ma już takiej osoby + con.query('SELECT * FROM liczacy WHERE imie = ? AND nazwisko = ? AND aktywne = 1', [req.body.imie, req.body.nazwisko], function(err, result) { + if (err) throw err; + if (result.length > 0) { + res.json({status: 'error', message: 'Taka osoba już istnieje.'}); + loger(fs, 'Użytkownik próbował dodać osobę liczącą, która już istnieje', 'error'); + } else { + con.query('INSERT INTO liczacy (imie, nazwisko) VALUES (?, ?)', [req.body.imie, req.body.nazwisko], function(err, result) { + if (err) throw err; + res.json({status: 'ok'}); + loger(fs, 'Użytkownik dodał osobę liczącą', 'info'); + }); + } + }); +}); + +route.get("/listaWolontariuszy", function(req, res) { + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1', function(err, result) { + if (err) throw err; + res.json(result); + loger(fs, 'Użytkownik wyświetlił listę wolontariuszy', 'info'); + }); +}); +//edycja wolontariusza +route.put("/listaWolontariuszy", function(req, res) { + con.query('UPDATE wolontariusz SET imie = ?, nazwisko = ?, discord = ?, email = ?, telefon = ?, pesel = ?, terminal = ?, aktywny = ? WHERE id = ?', [req.body.imie, req.body.nazwisko, req.body.discord, req.body.email, req.body.telefon, req.body.pesel, req.body.terminal, req.body.aktywny, req.body.id], function(err, result) { + if (err) throw err; + res.json({status: 'ok'}); + loger(fs, 'Użytkownik edytował wolontariusza', 'info'); + }); +}); +//dodanie rozliczenia +route.post("/rozlicz", function(req, res) { + //sprawdzenie czy nie ma już takiego wolontariusza + con.query('SELECT * FROM rozliczenie WHERE wolontariuszID = ? AND aktywne = 1', [req.body.wolontariuszID], function(err, result) { + if (err) throw err; + if (result.length > 0) { + res.json({status: 'error', message: 'Taki wolontariusz już rozliczony.'}); + loger(fs, 'Użytkownik próbował dodać rozliczenie, które już istnieje ID: ' + req.body.wolontariuszID, 'error'); + } else { + $sql = "INSERT INTO `rozliczenie` (`id`, `wolontariuszID`, `czasRozliczenia`, `terminal`, `sumaZTerminala`, `1gr`, `2gr`, `5gr`, `10gr`, `20gr`, `50gr`, `1zl`, `2zl`, `5zl`, `10zl`, `20zl`, `50zl`, `100zl`, `200zl`, `500zl`, `walutaObca`, `daryInne`, `uwagi`, `liczacy1`, `liczacy2`, `liczacy3`, `sala`, `weryfikowal`, `wpisaneDoBSS`, `ostatniaZmiana`, `aktywne`) "; + $sql += 'VALUES (NULL, ?, CURRENT_TIME(), ?, ?, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0,CURRENT_TIME(),1)'; + con.query($sql, [req.body.wolontariuszID, req.body.terminal, req.body.sumaZTerminala, req.body['1gr'], req.body['2gr'], req.body['5gr'], req.body['10gr'], req.body['20gr'], req.body['50gr'], req.body['1zl'], req.body['2zl'], req.body['5zl'], req.body['10zl'], req.body['20zl'], req.body['50zl'], req.body['100zl'], req.body['200zl'], req.body['500zl'], req.body.walutaObca, req.body.daryInne, req.body.uwagi, req.body.liczacy1, req.body.liczacy2, req.body.liczacy3, req.body.sala, req.body.weryfikowal], function(err, result) { + if (err) throw err; + res.json({status: 'ok'}); + loger(fs, 'Użytkownik dodał rozliczenie dla wolontariusza ID: ' + req.body.wolontariuszID, 'info'); + }); + } + }); +}); +//lista rozliczeń +route.get("/rozliczenia", function(req, res) { + con.query('SELECT * FROM rozliczenie WHERE aktywne = 1', function(err, result) { + if (err) throw err; + res.json(result); + loger(fs, 'Użytkownik wyświetlił listę rozliczeń', 'info'); + }); +}); +//edycja rozliczenia +route.put("/rozliczenia", function(req, res) { + con.query('UPDATE rozliczenie SET wolontariuszID = ?, czasRozliczenia = ?, terminal = ?, sumaZTerminala = ?, `1gr` = ?, `2gr` = ?, `5gr` = ?, `10gr` = ?, `20gr` = ?, `50gr` = ?, `1zl` = ?, `2zl` = ?, `5zl` = ?, `10zl` = ?, `20zl` = ?, `50zl` = ?, `100zl` = ?, `200zl` = ?, `500zl` = ?, walutaObca = ?, daryInne = ?, uwagi = ?, liczacy1 = ?, liczacy2 = ?, liczacy3 = ?, sala = ?, weryfikowal = ?, wpisaneDoBSS = ?, ostatniaZmiana = ? WHERE id = ?', [req.body.wolontariuszID, req.body.czasRozliczenia, req.body.terminal, req.body.sumaZTerminala, req.body['1gr'], req.body['2gr'], req.body['5gr'], req.body['10gr'], req.body['20gr'], req.body['50gr'], req.body['1zl'], req.body['2zl'], req.body['5zl'], req.body['10zl'], req.body['20zl'], req.body['50zl'], req.body['100zl'], req.body['200zl'], req.body['500zl'], req.body.walutaObca, req.body.daryInne, req.body.uwagi, req.body.liczacy1, req.body.liczacy2, req.body.liczacy3, req.body.sala, req.body.weryfikowal, req.body.wpisaneDoBSS, req.body.ostatniaZmiana, req.body.id], function(err, result) { + if (err) throw err; + res.json({status: 'ok'}); + loger(fs, 'Użytkownik edytował rozliczenie ID: ' + req.body.id, 'info'); + }); +}); + +//statystyki +route.get("/statystyki/zebranePrzezWolontariuszy", function(req, res) { + con.query('SELECT * FROM SumaZebranaPrzezWolontariuszy ORDER BY suma ASC', function(err, result) { + if (err) throw err; + res.json(result); + }); +}); + +route.get("/statystyki/liczacy", function(req, res) { + con.query('SELECT * FROM sumaPrzeliczona ORDER BY sumaPrzeliczona DESC', function(err, result) { + if (err) throw err; + res.json(result); + }); +}); + +route.get("/statystyki/rozliczenia", function(req, res) { + con.query('SELECT COUNT(*) AS liczba FROM rozliczenie WHERE aktywne = 1', function(err, result) { + if (err) throw err; + res.json(result); + }); +}); + +route.get("/statystyki/rozliczenia/ostatnie", function(req, res) { + con.query('SELECT * FROM rozliczenie WHERE aktywne = 1 ORDER BY czasRozliczenia DESC LIMIT 10', function(err, result) { + if (err) throw err; + res.json(result); + }); +}); + + + +module.exports = route; \ No newline at end of file diff --git a/serwer/app/baza-import.sql b/serwer/app/baza-import.sql new file mode 100644 index 0000000..4db1676 --- /dev/null +++ b/serwer/app/baza-import.sql @@ -0,0 +1,159 @@ +CREATE TABLE IF NOT EXISTS `liczacy` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `imie` varchar(255) NOT NULL, + `nazwisko` varchar(255) NOT NULL, + `aktywne` tinyint(1) NOT NULL DEFAULT 1, + `qr` varchar(25) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `login` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `login` text NOT NULL, + `haslo` text NOT NULL, + `kto` text NOT NULL, + `aktywne` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `rozliczenie` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `wolontariuszID` int(11) NOT NULL, + `czasRozliczenia` timestamp NOT NULL DEFAULT current_timestamp(), + `terminal` tinyint(1) NOT NULL DEFAULT 0, + `sumaZTerminala` float DEFAULT NULL, + `1gr` int(11) DEFAULT NULL, + `2gr` int(11) DEFAULT NULL, + `5gr` int(11) DEFAULT NULL, + `10gr` int(11) DEFAULT NULL, + `20gr` int(11) DEFAULT NULL, + `50gr` int(11) DEFAULT NULL, + `1zl` int(11) DEFAULT NULL, + `2zl` int(11) DEFAULT NULL, + `5zl` int(11) DEFAULT NULL, + `10zl` int(11) DEFAULT NULL, + `20zl` int(11) DEFAULT NULL, + `50zl` int(11) DEFAULT NULL, + `100zl` int(11) DEFAULT NULL, + `200zl` int(11) DEFAULT NULL, + `500zl` int(11) DEFAULT NULL, + `walutaObca` text NOT NULL, + `daryInne` text DEFAULT NULL, + `uwagi` text DEFAULT NULL, + `liczacy1` int(11) NOT NULL, + `liczacy2` int(11) NOT NULL, + `liczacy3` int(11) DEFAULT NULL, + `sala` varchar(10) NOT NULL, + `weryfikowal` int(11) NOT NULL, + `wpisaneDoBSS` tinyint(1) NOT NULL DEFAULT 0, + `ostatniaZmiana` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `aktywne` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `wolontariuszID` (`wolontariuszID`), + KEY `liczacy1` (`liczacy1`), + KEY `liczacy2` (`liczacy2`), + KEY `liczacy3` (`liczacy3`), + KEY `weryfikowal` (`weryfikowal`) +) ENGINE=InnoDB; +CREATE TABLE IF NOT EXISTS `sumaPrzeliczona` ( +`idLiczacego` int(11) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`sumaPrzeliczona` decimal(65,2) +); +CREATE TABLE IF NOT EXISTS `sumaPrzeliczona1` ( +`idLiczacego` int(11) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`sumaPrzeliczona` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `sumaPrzeliczona2` ( +`idLiczacego` int(11) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`sumaPrzeliczona` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `sumaPrzeliczona3` ( +`idLiczacego` int(11) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`sumaPrzeliczona` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `SumaZebranaPrzezWolontariuszy` ( +`numerIdentyfikatora` varchar(8) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`suma` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `suma_przeliczona2` ( +`ID_Liczącego` int(11) +,`Imię` varchar(255) +,`Nazwisko` varchar(255) +,`Suma_Przeliczona` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `sumy` ( +`wolontariuszID` int(11) +,`suma` decimal(49,2) +); + +CREATE TABLE IF NOT EXISTS `tokeny` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `token` text NOT NULL, + `czasAktywacji` timestamp NOT NULL DEFAULT current_timestamp(), + `typ` int(11) NOT NULL DEFAULT 1, + `userId` int(11) NOT NULL, + `aktywny` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `userId` (`userId`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `tokenyLiczacy` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `token` varchar(255) NOT NULL, + `typ` int(11) NOT NULL, + `userId` int(11) NOT NULL, + `czasAktywacji` timestamp NOT NULL DEFAULT current_timestamp(), + `aktywny` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `wolontariusz` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `numerIdentyfikatora` varchar(8) NOT NULL, + `imie` varchar(255) NOT NULL, + `nazwisko` varchar(255) NOT NULL, + `discord` text NOT NULL, + `email` text NOT NULL, + `telefon` varchar(12) NOT NULL, + `pesel` varchar(11) NOT NULL, + `rodzic` varchar(255) NOT NULL DEFAULT 'BRAK', + `terminal` tinyint(1) NOT NULL DEFAULT 0, + `ostatniaZmiana` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `aktywny` tinyint(4) NOT NULL DEFAULT 1, + `zaznacz` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB; +DROP TABLE IF EXISTS `sumaPrzeliczona`; + +CREATE OR REPLACE VIEW `sumaPrzeliczona` AS SELECT `combinedData`.`idLiczacego` AS `idLiczacego`, `combinedData`.`imie` AS `imie`, `combinedData`.`nazwisko` AS `nazwisko`, sum(`combinedData`.`sumaPrzeliczona`) AS `sumaPrzeliczona` FROM (select `r`.`liczacy1` AS `idLiczacego`,`l`.`imie` AS `imie`,`l`.`nazwisko` AS `nazwisko`,sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` from (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy1` = `l`.`id`)) group by `r`.`liczacy1` union all select `r`.`liczacy2` AS `idLiczacego`,`l`.`imie` AS `imie`,`l`.`nazwisko` AS `nazwisko`,sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` from (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy2` = `l`.`id`)) group by `r`.`liczacy2` union all select `r`.`liczacy3` AS `idLiczacego`,`l`.`imie` AS `imie`,`l`.`nazwisko` AS `nazwisko`,sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` from (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy3` = `l`.`id`)) group by `r`.`liczacy3`) AS `combinedData` GROUP BY `combinedData`.`idLiczacego`, `combinedData`.`imie`, `combinedData`.`nazwisko` ; +DROP TABLE IF EXISTS `sumaPrzeliczona1`; + +CREATE OR REPLACE VIEW `sumaPrzeliczona1` AS SELECT `r`.`liczacy1` AS `idLiczacego`, `l`.`imie` AS `imie`, `l`.`nazwisko` AS `nazwisko`, sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` FROM (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy1` = `l`.`id`)) GROUP BY `r`.`liczacy1` ; +DROP TABLE IF EXISTS `sumaPrzeliczona2`; + +CREATE OR REPLACE VIEW `sumaPrzeliczona2` AS SELECT `r`.`liczacy2` AS `idLiczacego`, `l`.`imie` AS `imie`, `l`.`nazwisko` AS `nazwisko`, sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` FROM (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy2` = `l`.`id`)) GROUP BY `r`.`liczacy2` ; +DROP TABLE IF EXISTS `sumaPrzeliczona3`; + +CREATE OR REPLACE VIEW `sumaPrzeliczona3` AS SELECT `r`.`liczacy3` AS `idLiczacego`, `l`.`imie` AS `imie`, `l`.`nazwisko` AS `nazwisko`, sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` FROM (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy3` = `l`.`id`)) GROUP BY `r`.`liczacy3` ; +DROP TABLE IF EXISTS `SumaZebranaPrzezWolontariuszy`; + +CREATE OR REPLACE VIEW `SumaZebranaPrzezWolontariuszy` AS SELECT `wolontariusz`.`numerIdentyfikatora` AS `numerIdentyfikatora`, `wolontariusz`.`imie` AS `imie`, `wolontariusz`.`nazwisko` AS `nazwisko`, `sumy`.`suma` AS `suma` FROM (`wolontariusz` join `sumy`) WHERE `wolontariusz`.`id` = `sumy`.`wolontariuszID` ORDER BY `wolontariusz`.`numerIdentyfikatora` ASC ; +DROP TABLE IF EXISTS `suma_przeliczona2`; + +CREATE OR REPLACE VIEW `suma_przeliczona2` AS SELECT coalesce(`r`.`liczacy1`,`r`.`liczacy2`,`r`.`liczacy3`) AS `ID_Liczącego`, `l`.`imie` AS `Imię`, `l`.`nazwisko` AS `Nazwisko`, sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `Suma_Przeliczona` FROM (`rozliczenie` `r` join `liczacy` `l` on(coalesce(`r`.`liczacy1`,`r`.`liczacy2`,`r`.`liczacy3`) = `l`.`id`)) GROUP BY coalesce(`r`.`liczacy1`,`r`.`liczacy2`,`r`.`liczacy3`), `l`.`imie`, `l`.`nazwisko` ; +DROP TABLE IF EXISTS `sumy`; + +CREATE OR REPLACE VIEW `sumy` AS SELECT `rozliczenie`.`wolontariuszID` AS `wolontariuszID`, sum(`rozliczenie`.`1gr` * 0.01 + `rozliczenie`.`2gr` * 0.02 + `rozliczenie`.`5gr` * 0.05 + `rozliczenie`.`10gr` * 0.1 + `rozliczenie`.`20gr` * 0.2 + `rozliczenie`.`50gr` * 0.5 + `rozliczenie`.`1zl` + `rozliczenie`.`2zl` * 2 + `rozliczenie`.`5zl` * 5 + `rozliczenie`.`10zl` * 10 + `rozliczenie`.`20zl` * 20 + `rozliczenie`.`50zl` * 50 + `rozliczenie`.`100zl` * 100 + `rozliczenie`.`200zl` * 200 + `rozliczenie`.`500zl` * 500) AS `suma` FROM `rozliczenie` GROUP BY `rozliczenie`.`wolontariuszID` ; + +INSERT INTO `login` (`id`, `login`, `haslo`, `kto`, `aktywne`) VALUES +(NULL, 'szef', '0c1aba4f114d80faa3b08016fe94443462adadd7', 'Szef Sztabu', 1); diff --git a/serwer/app/baza.sql b/serwer/app/baza.sql new file mode 100644 index 0000000..6fcccf9 --- /dev/null +++ b/serwer/app/baza.sql @@ -0,0 +1,156 @@ +CREATE TABLE IF NOT EXISTS `liczacy` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `imie` varchar(255) NOT NULL, + `nazwisko` varchar(255) NOT NULL, + `aktywne` tinyint(1) NOT NULL DEFAULT 1, + `qr` varchar(25) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1; + +CREATE TABLE IF NOT EXISTS `login` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `login` text NOT NULL, + `haslo` text NOT NULL, + `kto` text NOT NULL, + `aktywne` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1; + +CREATE TABLE IF NOT EXISTS `rozliczenie` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `wolontariuszID` int(11) NOT NULL, + `czasRozliczenia` timestamp NOT NULL DEFAULT current_timestamp(), + `terminal` tinyint(1) NOT NULL DEFAULT 0, + `sumaZTerminala` float DEFAULT NULL, + `1gr` int(11) DEFAULT NULL, + `2gr` int(11) DEFAULT NULL, + `5gr` int(11) DEFAULT NULL, + `10gr` int(11) DEFAULT NULL, + `20gr` int(11) DEFAULT NULL, + `50gr` int(11) DEFAULT NULL, + `1zl` int(11) DEFAULT NULL, + `2zl` int(11) DEFAULT NULL, + `5zl` int(11) DEFAULT NULL, + `10zl` int(11) DEFAULT NULL, + `20zl` int(11) DEFAULT NULL, + `50zl` int(11) DEFAULT NULL, + `100zl` int(11) DEFAULT NULL, + `200zl` int(11) DEFAULT NULL, + `500zl` int(11) DEFAULT NULL, + `walutaObca` text NOT NULL, + `daryInne` text DEFAULT NULL, + `uwagi` text DEFAULT NULL, + `liczacy1` int(11) NOT NULL, + `liczacy2` int(11) NOT NULL, + `liczacy3` int(11) DEFAULT NULL, + `sala` varchar(10) NOT NULL, + `weryfikowal` int(11) NOT NULL, + `wpisaneDoBSS` tinyint(1) NOT NULL DEFAULT 0, + `ostatniaZmiana` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `aktywne` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `wolontariuszID` (`wolontariuszID`), + KEY `liczacy1` (`liczacy1`), + KEY `liczacy2` (`liczacy2`), + KEY `liczacy3` (`liczacy3`), + KEY `weryfikowal` (`weryfikowal`) +) ENGINE=InnoDB AUTO_INCREMENT=1; +CREATE TABLE IF NOT EXISTS `sumaPrzeliczona` ( +`idLiczacego` int(11) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`sumaPrzeliczona` decimal(65,2) +); +CREATE TABLE IF NOT EXISTS `sumaPrzeliczona1` ( +`idLiczacego` int(11) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`sumaPrzeliczona` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `sumaPrzeliczona2` ( +`idLiczacego` int(11) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`sumaPrzeliczona` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `sumaPrzeliczona3` ( +`idLiczacego` int(11) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`sumaPrzeliczona` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `SumaZebranaPrzezWolontariuszy` ( +`numerIdentyfikatora` varchar(8) +,`imie` varchar(255) +,`nazwisko` varchar(255) +,`suma` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `suma_przeliczona2` ( +`ID_Liczącego` int(11) +,`Imię` varchar(255) +,`Nazwisko` varchar(255) +,`Suma_Przeliczona` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS `sumy` ( +`wolontariuszID` int(11) +,`suma` decimal(49,2) +); + +CREATE TABLE IF NOT EXISTS `tokeny` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `token` text NOT NULL, + `czasAktywacji` timestamp NOT NULL DEFAULT current_timestamp(), + `typ` int(11) NOT NULL DEFAULT 1, + `userId` int(11) NOT NULL, + `aktywny` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `userId` (`userId`) +) ENGINE=InnoDB AUTO_INCREMENT=1; + +CREATE TABLE IF NOT EXISTS `tokenyLiczacy` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `token` varchar(255) NOT NULL, + `typ` int(11) NOT NULL, + `userId` int(11) NOT NULL, + `czasAktywacji` timestamp NOT NULL DEFAULT current_timestamp(), + `aktywny` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1; + +CREATE TABLE IF NOT EXISTS `wolontariusz` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `numerIdentyfikatora` varchar(8) NOT NULL, + `imie` varchar(255) NOT NULL, + `nazwisko` varchar(255) NOT NULL, + `discord` text NOT NULL, + `email` text NOT NULL, + `telefon` varchar(12) NOT NULL, + `pesel` varchar(11) NOT NULL, + `rodzic` varchar(255) NOT NULL DEFAULT 'BRAK', + `terminal` tinyint(1) NOT NULL DEFAULT 0, + `ostatniaZmiana` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `aktywny` tinyint(4) NOT NULL DEFAULT 1, + `zaznacz` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1; +DROP TABLE IF EXISTS `sumaPrzeliczona`; + +CREATE OR REPLACE VIEW `sumaPrzeliczona` AS SELECT `combinedData`.`idLiczacego` AS `idLiczacego`, `combinedData`.`imie` AS `imie`, `combinedData`.`nazwisko` AS `nazwisko`, sum(`combinedData`.`sumaPrzeliczona`) AS `sumaPrzeliczona` FROM (select `r`.`liczacy1` AS `idLiczacego`,`l`.`imie` AS `imie`,`l`.`nazwisko` AS `nazwisko`,sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` from (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy1` = `l`.`id`)) group by `r`.`liczacy1` union all select `r`.`liczacy2` AS `idLiczacego`,`l`.`imie` AS `imie`,`l`.`nazwisko` AS `nazwisko`,sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` from (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy2` = `l`.`id`)) group by `r`.`liczacy2` union all select `r`.`liczacy3` AS `idLiczacego`,`l`.`imie` AS `imie`,`l`.`nazwisko` AS `nazwisko`,sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` from (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy3` = `l`.`id`)) group by `r`.`liczacy3`) AS `combinedData` GROUP BY `combinedData`.`idLiczacego`, `combinedData`.`imie`, `combinedData`.`nazwisko` ; +DROP TABLE IF EXISTS `sumaPrzeliczona1`; + +CREATE OR REPLACE VIEW `sumaPrzeliczona1` AS SELECT `r`.`liczacy1` AS `idLiczacego`, `l`.`imie` AS `imie`, `l`.`nazwisko` AS `nazwisko`, sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` FROM (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy1` = `l`.`id`)) GROUP BY `r`.`liczacy1` ; +DROP TABLE IF EXISTS `sumaPrzeliczona2`; + +CREATE OR REPLACE VIEW `sumaPrzeliczona2` AS SELECT `r`.`liczacy2` AS `idLiczacego`, `l`.`imie` AS `imie`, `l`.`nazwisko` AS `nazwisko`, sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` FROM (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy2` = `l`.`id`)) GROUP BY `r`.`liczacy2` ; +DROP TABLE IF EXISTS `sumaPrzeliczona3`; + +CREATE OR REPLACE VIEW `sumaPrzeliczona3` AS SELECT `r`.`liczacy3` AS `idLiczacego`, `l`.`imie` AS `imie`, `l`.`nazwisko` AS `nazwisko`, sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `sumaPrzeliczona` FROM (`rozliczenie` `r` join `liczacy` `l` on(`r`.`liczacy3` = `l`.`id`)) GROUP BY `r`.`liczacy3` ; +DROP TABLE IF EXISTS `SumaZebranaPrzezWolontariuszy`; + +CREATE OR REPLACE VIEW `SumaZebranaPrzezWolontariuszy` AS SELECT `wolontariusz`.`numerIdentyfikatora` AS `numerIdentyfikatora`, `wolontariusz`.`imie` AS `imie`, `wolontariusz`.`nazwisko` AS `nazwisko`, `sumy`.`suma` AS `suma` FROM (`wolontariusz` join `sumy`) WHERE `wolontariusz`.`id` = `sumy`.`wolontariuszID` ORDER BY `wolontariusz`.`numerIdentyfikatora` ASC ; +DROP TABLE IF EXISTS `suma_przeliczona2`; + +CREATE OR REPLACE VIEW `suma_przeliczona2` AS SELECT coalesce(`r`.`liczacy1`,`r`.`liczacy2`,`r`.`liczacy3`) AS `ID_Liczącego`, `l`.`imie` AS `Imię`, `l`.`nazwisko` AS `Nazwisko`, sum(`r`.`1gr` * 0.01 + `r`.`2gr` * 0.02 + `r`.`5gr` * 0.05 + `r`.`10gr` * 0.1 + `r`.`20gr` * 0.2 + `r`.`50gr` * 0.5 + `r`.`1zl` * 1 + `r`.`2zl` * 2 + `r`.`5zl` * 5 + `r`.`10zl` * 10 + `r`.`20zl` * 20 + `r`.`50zl` * 50 + `r`.`100zl` * 100 + `r`.`200zl` * 200 + `r`.`500zl` * 500) AS `Suma_Przeliczona` FROM (`rozliczenie` `r` join `liczacy` `l` on(coalesce(`r`.`liczacy1`,`r`.`liczacy2`,`r`.`liczacy3`) = `l`.`id`)) GROUP BY coalesce(`r`.`liczacy1`,`r`.`liczacy2`,`r`.`liczacy3`), `l`.`imie`, `l`.`nazwisko` ; +DROP TABLE IF EXISTS `sumy`; + +CREATE OR REPLACE VIEW `sumy` AS SELECT `rozliczenie`.`wolontariuszID` AS `wolontariuszID`, sum(`rozliczenie`.`1gr` * 0.01 + `rozliczenie`.`2gr` * 0.02 + `rozliczenie`.`5gr` * 0.05 + `rozliczenie`.`10gr` * 0.1 + `rozliczenie`.`20gr` * 0.2 + `rozliczenie`.`50gr` * 0.5 + `rozliczenie`.`1zl` + `rozliczenie`.`2zl` * 2 + `rozliczenie`.`5zl` * 5 + `rozliczenie`.`10zl` * 10 + `rozliczenie`.`20zl` * 20 + `rozliczenie`.`50zl` * 50 + `rozliczenie`.`100zl` * 100 + `rozliczenie`.`200zl` * 200 + `rozliczenie`.`500zl` * 500) AS `suma` FROM `rozliczenie` GROUP BY `rozliczenie`.`wolontariuszID` ; diff --git a/serwer/app/css/style.css b/serwer/app/css/style.css new file mode 100644 index 0000000..162e69f --- /dev/null +++ b/serwer/app/css/style.css @@ -0,0 +1,443 @@ +* +{ + margin: 0; + padding: 0; + box-sizing: border-box; + font-size: 14pt; + font-family: "Roboto", sans-serif; +} +a +{ + color: #000; +} +#container +{ + width: 100%; + min-height: 100vh; + background-color: #fff; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + align-content: center; + flex-wrap: wrap; + overflow:visible; +} +.menu +{ + width: 100%; + background-color: #fff; + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; + align-content: center; + flex-wrap: wrap; +} +.menu a +{ + text-decoration: none; + color: #000; + font-size: larger; + margin: 0 10px; + padding: 5px; +} +a +{ + transition: .25s; +} +a:hover +{ + color: #ff0000; +} + +footer +{ + width: 100%; + height: 50px; + background-color: #fff; + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; + align-content: center; + flex-wrap: wrap; +} + +.content +{ + margin: 10px; + padding: 10px; + width: 100%; + background-color: #fff; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + align-content: center; + flex-wrap: wrap; + border-bottom: 1px solid #000; + border-top: 1px solid #000; +} +.content * +{ + padding: 2px; + margin: 1px; +} +form +{ + width: 100%; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + align-content: center; + flex-wrap: wrap; +} + +table.dane +{ + border-collapse: collapse; + border: 1px solid #000; +} +table.dane td +{ + border: 1px solid #000; + padding: 5px; + text-align: center; +} +table.dane th +{ + border: 1px solid #000; + padding: 5px; +} + +table.dane tr:nth-child(2n) +{ + background-color: #ddd; +} +table.dane tr:hover +{ + background-color: #aaa; +} + +.szerokie +{ + width: calc(100% - 20px); + margin: 5px 10px; +} + +div.kafelki +{ + width: 100%; + display: flex; + justify-content: center; + flex-direction: row; + align-items: center; + align-content: center; + flex-wrap: wrap; +} +div.kafelki a +{ + width: 200px; + height: 140px; + margin: 10px; + border: 1px solid #ddd; + border-radius: 25px; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + align-content: center; + flex-wrap: wrap; + text-decoration: none; + color: black; + font-size: larger; + text-align: center; +} +div.kafelki a:hover +{ + background-color: #ddd; + border: 1px solid #fff; +} + + +.przewijanie +{ + overflow-x: auto; + white-space: nowrap; + padding: 5px; + margin: 5px; + font-size: small; +} + +.stala, #suma, #suma2 +{ + /*stała szerokość liter w czcionce*/ + font-family: "Courier New", Courier, monospace; +} + +.kafelki2 +{ + width: 100%; + display: flex; + justify-content: center; + flex-direction: row; + align-items: center; + align-content: center; + flex-wrap: wrap; + border-top: #000 1px solid; + border-bottom: #000 1px solid; +} +.kafelki2 .kafelek2 +{ + min-width: 200px; + min-height: 140px; + padding: 10px; + margin: 10px; + border: 1px solid #ddd; + border-radius: 25px; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + align-content: center; + flex-wrap: wrap; + text-decoration: none; + color: black; + font-size: larger; + text-align: center; +} + + +div.kafelki3 +{ + width: 100%; + display: flex; + justify-content: center; + flex-direction: row; + align-items: center; + align-content: center; + flex-wrap: wrap; +} +div.kafelki3 a +{ + width: 230px; + height: 110px; + margin: 10px; + border: 1px solid #ddd; + border-radius: 25px; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + align-content: center; + flex-wrap: wrap; + text-decoration: none; + color: black; + font-size: larger; + text-align: center; +} +div.kafelki3 a:hover +{ + background-color: #ddd; + border: 1px solid #fff; +} + + +div.kafelki3 a img +{ + width: 100px; + height: 100px; + margin: 10px; + border: 1px solid #ddd; + border-radius: 25px; +} + +div.kafelki3 a span +{ + width: 100%; + text-align: center; +} + +div.kafelki3 a.borderColorBlue +{ + border: 1px solid rgb(0, 183, 255); +} +div.kafelki3 a.borderColorBlue:hover +{ + background-color: rgba(0, 183, 255, .4); + border: 1px solid #fff; +} + +div.kafelki3 a.borderColorGreen +{ + border: 1px solid rgb(0, 255, 0); +} +div.kafelki3 a.borderColorGreen:hover +{ + background-color: rgba(0, 255, 0, .4); + border: 1px solid #fff; +} + +div.kafelki3 a.borderColorRed +{ + border: 1px solid rgb(255, 0, 0); +} +div.kafelki3 a.borderColorRed:hover +{ + background-color: rgba(255, 0, 0, .4); + border: 1px solid #fff; +} + +div.kafelki3 a.borderColorYellow +{ + border: 1px solid rgb(255, 255, 0); +} +div.kafelki3 a.borderColorYellow:hover +{ + background-color: rgba(255, 255, 0, .4); + border: 1px solid #fff; +} + +div.kafelki3 a.borderColorOrange +{ + border: 1px solid rgb(255, 165, 0); +} +div.kafelki3 a.borderColorOrange:hover +{ + background-color: rgba(255, 165, 0, .4); + border: 1px solid #fff; +} + +div.kafelki3 a.borderColorPurple +{ + border: 1px solid rgb(128, 0, 128); +} +div.kafelki3 a.borderColorPurple:hover +{ + background-color: rgba(128, 0, 128, .4); + border: 1px solid #fff; +} +div.kafelki3 a.borderColorBlack +{ + border: 1px solid rgb(0, 0, 0); +} +div.kafelki3 a.borderColorBlack:hover +{ + background-color: rgba(0, 0, 0, 0.4); + border: 1px solid #fff; +} + +/*input submit*/ +input[type=submit] +{ + min-width: 150px; + height: 30px; + margin: 10px; + border: 1px solid #ddd; + border-radius: 25px; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + align-content: center; + flex-wrap: wrap; + text-decoration: none; + color: black; + font-size: larger; + text-align: center; + background-color: #fff; + transition: .25s; +} +input[type=submit]:hover +{ + background-color: #ddd; + border: 1px solid #fff; +} + +footer +{ + text-align: center; +} + +.sidenav { + height: 100%; /* 100% Full-height */ + width: 0; /* 0 width - change this with JavaScript */ + position: fixed; /* Stay in place */ + z-index: 1; /* Stay on top */ + top: 0; /* Stay at the top */ + right: 0; + background-color: #FFF; /* Black*/ + overflow-x: hidden; /* Disable horizontal scroll */ + padding-top: 60px; /* Place content 60px from the top */ + transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */ + border-left: 1px solid #000; + box-shadow: 0 0 10px #000; +} + +.sidenav .closebtn { + position: absolute; + top: 0; + right: 25px; + font-size: 36px; + margin-left: 50px; + text-decoration: none; +} + +.sidenav iframe +{ + width: 80%; + height: 80%; + margin: 5px 5px; + border: none; +} + +.menuOpen +{ + /*top left */ + position: fixed; + top: 5px; + right: 5px; + width: 50px; + + background-color: #fff; + font-size: xx-large; + text-align: center; + text-decoration: none; + color: #000; + transition: .25s; + /*rotate 90deg*/ + transform: rotate(90deg); + +} + + +/* on print */ +@media print +{ + .menu, footer, .sidenav, .menuOpen, .drukujBtn + { + display: none; + } + .content + { + border: none; + } + .content * + { + padding: 0; + margin: 0; + } + .szerokie + { + width: 100%; + margin: 0; + } + table.dane + { + border-collapse: collapse; + border: 1px solid #000; + width: 100%; + } +} diff --git a/serwer/app/eksport/none b/serwer/app/eksport/none new file mode 100644 index 0000000..e69de29 diff --git a/serwer/app/func.js b/serwer/app/func.js new file mode 100644 index 0000000..3d36487 --- /dev/null +++ b/serwer/app/func.js @@ -0,0 +1,463 @@ +function headerHtml(tytul = 'WOŚP ELEKTRONIK') { + return '' + tytul +'
'; +} +function menuHtml($login = 0) { + var toReturn = ''; +} + +function footerHtml(login = 0) { + var toReturn = ''; + if(login == 1) + { + toReturn = '
'; + toReturn += '×'; + toReturn += ''; + toReturn += '
'; + toReturn += '|||'; + toReturn += ''; + toReturn += ''; + } + if(login == 2) + { + toReturn = '
'; + toReturn += '×'; + toReturn += ''; + toReturn += '
'; + toReturn += '|||'; + toReturn += ''; + toReturn += ''; + } + toReturn += ''; + toReturn += '
'; + if (new Date().getFullYear() > 2023) + toReturn += 'Stworzone przez KRY008 dla sztabu '+ process.env.SZTAB + ' © 2023-' + new Date().getFullYear() + ''; + else + toReturn += 'Stworzone przez KRY008 dla sztabu '+ process.env.SZTAB + ' © 2023'; + return toReturn + "
Wersja programu: " + process.env.VERSION + '
'; + + +} + +function makeid(length) { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + let counter = 0; + while (counter < length) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + counter += 1; + } + return result; +} + +//sprawdzanie kiedy PESEL ma 18 lat, czy minęło 16 lat od daty urodzenia, data sprawdzenia to 28 stycznia 2024 +function checkPesel(pesel) { + //pobierz datę urodzenia + // Wycinamy daty z numeru + var rok = parseInt(pesel.substring(0, 2), 10); + var miesiac = parseInt(pesel.substring(2, 4), 10) - 1; + var dzien = parseInt(pesel.substring(4, 6), 10); + // Pesel został wprowadzony w 20 wieku, ale zawiera modyfikatory na przysłość + // Miesiąc zawiera dodatkowe liczby dla dat z przyszłości. + if (miesiac > 80) { + rok = rok + 1800; + miesiac = miesiac - 80; + } else if (miesiac >= 60) { + rok = rok + 2200; + miesiac = miesiac - 60; + } else if (miesiac >= 40) { + rok = rok + 2100; + miesiac = miesiac - 40; + } else if (miesiac >= 20) { + rok = rok + 2000; + miesiac = miesiac - 20; + } else { + rok += 1900; + } + if( miesiac >=0 && miesiac < 12 && dzien > 0 && dzien < 32 ) { + // Daty sa ok. Teraz ustawiamy. + var urodzony = new Date(); + urodzony.setFullYear(rok, miesiac, dzien); + } else { + var urodzony = false; + } + //sprawdź czy minęło 18 lat + var today = new Date(process.env.DATAFINALU); + var diff = today - urodzony; + var age = Math.floor(diff/31557600000); + if(age >= 16) + return true; + else + return false; +} + +function loger(fs, text, type = 'info') { + if(process.env.LOGS == '1') + { + var date = new Date(); + var time = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds(); + console.log('[' + time + ']\t' + type + ': ' + text); + if(process.platform == 'win32') + fs.appendFileSync('logs\\' + date.getDate() + '-' + (date.getMonth() + 1) +'log.txt', '[' + time + ']\t' + type + ': ' + text + '\r\n'); + else + fs.appendFileSync('logs/' + date.getDate() + '-' + (date.getMonth() + 1) +'log.txt', '[' + time + ']\t' + type + ': ' + text + '\n'); + } + +} + +function peselToShow(pesel, nrId) { + //weź pierwsze 3 znaki nrId + nrId = nrId.substring(0, 3); + nrId = parseInt(nrId) % 9; + if (nrId == 0) + nrId = 4; + //pokaż co nrId znak, resztę zastąp x + var result = ''; + var counter = 0; + while (counter < pesel.length) { + if (counter % nrId == 0) + result += pesel[counter] + ' '; + else + result += '█ '; + counter += 1; + } + return result; +} + +//telefon, zwraca telefon w formacje +48XXXXXXXXX, możliwe podanie +48XXXXXXXXX/XXXXXXXXX/48XXXXXXXXX/XXX-XXX-XXX/XXX XXX XXX +function telefon(telefon, withLink = 0) { + telefon = telefon.replace(/\s/g, ''); + telefon = telefon.replace(/-/g, ''); + telefon = telefon.replace(/\//g, ''); + if (telefon.substring(0, 1) == '+') + telefon = telefon.substring(1, telefon.length); + if (telefon.substring(0, 2) == '48') + telefon = telefon.substring(2, telefon.length); + if (telefon.substring(0, 3) == '0048') + telefon = telefon.substring(3, telefon.length); + if (telefon.length == 9) + telefon = '48' + telefon; + if (telefon.length == 11) + telefon = telefon.substring(2, telefon.length); + if (telefon.length == 12) + telefon = telefon.substring(3, telefon.length); + if (telefon.length == 0) + telefon = '000000000'; + if(withLink == 1) + return '' + telefon + ''; + else + return telefon; +} + +//random color +function randomColor() { + var letters = '0123456789ABCDEF'; + var color = '#'; + var counter = 0; + while (counter < 6) { + color += letters[Math.floor(Math.random() * 16)]; + counter += 1; + } + return color; +} + +function sendToDiscord(imie, nazwisko, suma, nickDC) +{ + const { Webhook, MessageBuilder } = require('discord-webhook-node'); + const hook = new Webhook(process.env.DISCORDWEBHOOK); + if(nickDC == 'BRAK') + { + const embed = new MessageBuilder() + .setTitle(imie + ' ' + nazwisko) + .setColor(randomColor()) + .setAuthor("Bot by KRY008", "https://raw.githubusercontent.com/kry008/kry008.xyz/main/images/logo.webp", "https://kry008.xyz") + .setDescription('Dorzuca się kwotą: ' + suma + ' zł') + .setFooter('Rozliczenie', process.env.LOGO) + .setTimestamp(); + //console.log(embed); + hook.send(embed); + } + else + { + //mention user nickDC + const embed = new MessageBuilder() + .setTitle(imie + ' ' + nazwisko) + .setColor(randomColor()) + .setAuthor("Bot by KRY008", "https://raw.githubusercontent.com/kry008/kry008.xyz/main/images/logo.webp", "https://kry008.xyz") + .setDescription('Dorzuca się kwotą: ' + suma + ' zł') + .setFooter('Rozliczenie' + nickDC, process.env.LOGO) + .setTimestamp(); + //console.log(embed); + hook.send(embed); + } +} + +function sendEmail(imie, nazwisko, suma, email) +{ + const nodemailer = require('nodemailer'); + const transporter = nodemailer.createTransport({ + host: process.env.SMTPHOST, + port: process.env.SMTPPORT, + secure: true, + auth: { + user: process.env.SMTPLOGIN, + pass: process.env.SMTPPASS + } + }); + const mailOptions = { + from: process.env.SMTPLOGIN, + to: email, + subject: 'Twoje rozliczenie w ' + process.env.NRFINALU + '. Finale WOŚP', + html: '

Rozliczenie

Witaj ' + imie + ' ' + nazwisko + ',
Twoja suma z rozliczenia to: ' + suma + ' zł.

Dziękujemy za udział w WOŚP!

Pozdrawiamy,
' + process.env.SZTAB + '

' + footerHtml() + }; + transporter.sendMail(mailOptions, function(error, info){ + if (error) { + console.log(error); + loger(fs, 'Błąd wysyłania maila do ' + imie + ' ' + nazwisko + ' (' + email + ')', 'error'); + } else { + console.log('Email został wysłany: ' + info.response); + loger(fs, 'Email został wysłany do ' + imie + ' ' + nazwisko + ' (' + email + ') ' + info.response, 'info'); + } + }); +} + + +function checkSendEmail(email) +{ + const nodemailer = require('nodemailer'); + const transporter = nodemailer.createTransport({ + host: process.env.SMTPHOST, + port: process.env.SMTPPORT, + secure: true, + auth: { + user: process.env.SMTPLOGIN, + pass: process.env.SMTPPASS + } + }); + const mailOptions = { + from: process.env.SMTPLOGIN, + to: email, + subject: 'Twoje rozliczenie w ' + process.env.NRFINALU + '. Finale WOŚP', + html: '

Rozliczenie

Witaj Test,
Twoja suma z rozliczenia to: 0 zł.

Dziękujemy za udział w WOŚP!

Pozdrawiamy,
' + process.env.SZTAB + '

' + footerHtml() + }; + transporter.sendMail(mailOptions, function(error, info){ + if (error) { + console.log(error); + loger(fs, 'Błąd wysyłania maila', 'error'); + return 1; + } else { + console.log('Email został wysłany: ' + info.response); + loger(fs, 'Email został wysłany do', 'info'); + return 0; + } + }); +} + + +function baza() +{ + var toSend = `CREATE TABLE IF NOT EXISTS \`liczacy\` ( + \`id\` int(11) NOT NULL AUTO_INCREMENT, + \`imie\` varchar(255) NOT NULL, + \`nazwisko\` varchar(255) NOT NULL, + \`aktywne\` tinyint(1) NOT NULL DEFAULT 1, + \`qr\` varchar(25) NOT NULL, + PRIMARY KEY (\`id\`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS \`login\` ( + \`id\` int(11) NOT NULL AUTO_INCREMENT, + \`login\` text NOT NULL, + \`haslo\` text NOT NULL, + \`kto\` text NOT NULL, + \`aktywne\` int(11) NOT NULL, + PRIMARY KEY (\`id\`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS \`rozliczenie\` ( + \`id\` int(11) NOT NULL AUTO_INCREMENT, + \`wolontariuszID\` int(11) NOT NULL, + \`czasRozliczenia\` timestamp NOT NULL DEFAULT current_timestamp(), + \`terminal\` tinyint(1) NOT NULL DEFAULT 0, + \`sumaZTerminala\` float DEFAULT NULL, + \`1gr\` int(11) DEFAULT NULL, + \`2gr\` int(11) DEFAULT NULL, + \`5gr\` int(11) DEFAULT NULL, + \`10gr\` int(11) DEFAULT NULL, + \`20gr\` int(11) DEFAULT NULL, + \`50gr\` int(11) DEFAULT NULL, + \`1zl\` int(11) DEFAULT NULL, + \`2zl\` int(11) DEFAULT NULL, + \`5zl\` int(11) DEFAULT NULL, + \`10zl\` int(11) DEFAULT NULL, + \`20zl\` int(11) DEFAULT NULL, + \`50zl\` int(11) DEFAULT NULL, + \`100zl\` int(11) DEFAULT NULL, + \`200zl\` int(11) DEFAULT NULL, + \`500zl\` int(11) DEFAULT NULL, + \`walutaObca\` text NOT NULL, + \`daryInne\` text DEFAULT NULL, + \`uwagi\` text DEFAULT NULL, + \`liczacy1\` int(11) NOT NULL, + \`liczacy2\` int(11) NOT NULL, + \`liczacy3\` int(11) DEFAULT NULL, + \`sala\` varchar(10) NOT NULL, + \`weryfikowal\` int(11) NOT NULL, + \`wpisaneDoBSS\` tinyint(1) NOT NULL DEFAULT 0, + \`ostatniaZmiana\` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + \`aktywne\` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (\`id\`), + KEY \`wolontariuszID\` (\`wolontariuszID\`), + KEY \`liczacy1\` (\`liczacy1\`), + KEY \`liczacy2\` (\`liczacy2\`), + KEY \`liczacy3\` (\`liczacy3\`), + KEY \`weryfikowal\` (\`weryfikowal\`) +) ENGINE=InnoDB; +CREATE TABLE IF NOT EXISTS \`sumaPrzeliczona\` ( +\`idLiczacego\` int(11) +,\`imie\` varchar(255) +,\`nazwisko\` varchar(255) +,\`sumaPrzeliczona\` decimal(65,2) +); +CREATE TABLE IF NOT EXISTS \`sumaPrzeliczona1\` ( +\`idLiczacego\` int(11) +,\`imie\` varchar(255) +,\`nazwisko\` varchar(255) +,\`sumaPrzeliczona\` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS \`sumaPrzeliczona2\` ( +\`idLiczacego\` int(11) +,\`imie\` varchar(255) +,\`nazwisko\` varchar(255) +,\`sumaPrzeliczona\` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS \`sumaPrzeliczona3\` ( +\`idLiczacego\` int(11) +,\`imie\` varchar(255) +,\`nazwisko\` varchar(255) +,\`sumaPrzeliczona\` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS \`SumaZebranaPrzezWolontariuszy\` ( +\`numerIdentyfikatora\` varchar(8) +,\`imie\` varchar(255) +,\`nazwisko\` varchar(255) +,\`suma\` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS \`suma_przeliczona2\` ( +\`ID_Liczącego\` int(11) +,\`Imię\` varchar(255) +,\`Nazwisko\` varchar(255) +,\`Suma_Przeliczona\` decimal(49,2) +); +CREATE TABLE IF NOT EXISTS \`sumy\` ( +\`wolontariuszID\` int(11) +,\`suma\` decimal(49,2) +); + +CREATE TABLE IF NOT EXISTS \`tokeny\` ( + \`id\` int(11) NOT NULL AUTO_INCREMENT, + \`token\` text NOT NULL, + \`czasAktywacji\` timestamp NOT NULL DEFAULT current_timestamp(), + \`typ\` int(11) NOT NULL DEFAULT 1, + \`userId\` int(11) NOT NULL, + \`aktywny\` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (\`id\`), + KEY \`userId\` (\`userId\`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS \`tokenyLiczacy\` ( + \`id\` int(11) NOT NULL AUTO_INCREMENT, + \`token\` varchar(255) NOT NULL, + \`typ\` int(11) NOT NULL, + \`userId\` int(11) NOT NULL, + \`czasAktywacji\` timestamp NOT NULL DEFAULT current_timestamp(), + \`aktywny\` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (\`id\`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS \`wolontariusz\` ( + \`id\` int(11) NOT NULL AUTO_INCREMENT, + \`numerIdentyfikatora\` varchar(8) NOT NULL, + \`imie\` varchar(255) NOT NULL, + \`nazwisko\` varchar(255) NOT NULL, + \`discord\` text NOT NULL, + \`email\` text NOT NULL, + \`telefon\` varchar(12) NOT NULL, + \`pesel\` varchar(11) NOT NULL, + \`rodzic\` varchar(255) NOT NULL DEFAULT 'BRAK', + \`terminal\` tinyint(1) NOT NULL DEFAULT 0, + \`ostatniaZmiana\` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + \`aktywny\` tinyint(4) NOT NULL DEFAULT 1, + \`zaznacz\` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (\`id\`) +) ENGINE=InnoDB; +DROP TABLE IF EXISTS \`sumaPrzeliczona\`; + +CREATE OR REPLACE VIEW \`sumaPrzeliczona\` AS SELECT \`combinedData\`.\`idLiczacego\` AS \`idLiczacego\`, \`combinedData\`.\`imie\` AS \`imie\`, \`combinedData\`.\`nazwisko\` AS \`nazwisko\`, sum(\`combinedData\`.\`sumaPrzeliczona\`) AS \`sumaPrzeliczona\` FROM (select \`r\`.\`liczacy1\` AS \`idLiczacego\`,\`l\`.\`imie\` AS \`imie\`,\`l\`.\`nazwisko\` AS \`nazwisko\`,sum(\`r\`.\`1gr\` * 0.01 + \`r\`.\`2gr\` * 0.02 + \`r\`.\`5gr\` * 0.05 + \`r\`.\`10gr\` * 0.1 + \`r\`.\`20gr\` * 0.2 + \`r\`.\`50gr\` * 0.5 + \`r\`.\`1zl\` * 1 + \`r\`.\`2zl\` * 2 + \`r\`.\`5zl\` * 5 + \`r\`.\`10zl\` * 10 + \`r\`.\`20zl\` * 20 + \`r\`.\`50zl\` * 50 + \`r\`.\`100zl\` * 100 + \`r\`.\`200zl\` * 200 + \`r\`.\`500zl\` * 500) AS \`sumaPrzeliczona\` from (\`rozliczenie\` \`r\` join \`liczacy\` \`l\` on(\`r\`.\`liczacy1\` = \`l\`.\`id\`)) group by \`r\`.\`liczacy1\` union all select \`r\`.\`liczacy2\` AS \`idLiczacego\`,\`l\`.\`imie\` AS \`imie\`,\`l\`.\`nazwisko\` AS \`nazwisko\`,sum(\`r\`.\`1gr\` * 0.01 + \`r\`.\`2gr\` * 0.02 + \`r\`.\`5gr\` * 0.05 + \`r\`.\`10gr\` * 0.1 + \`r\`.\`20gr\` * 0.2 + \`r\`.\`50gr\` * 0.5 + \`r\`.\`1zl\` * 1 + \`r\`.\`2zl\` * 2 + \`r\`.\`5zl\` * 5 + \`r\`.\`10zl\` * 10 + \`r\`.\`20zl\` * 20 + \`r\`.\`50zl\` * 50 + \`r\`.\`100zl\` * 100 + \`r\`.\`200zl\` * 200 + \`r\`.\`500zl\` * 500) AS \`sumaPrzeliczona\` from (\`rozliczenie\` \`r\` join \`liczacy\` \`l\` on(\`r\`.\`liczacy2\` = \`l\`.\`id\`)) group by \`r\`.\`liczacy2\` union all select \`r\`.\`liczacy3\` AS \`idLiczacego\`,\`l\`.\`imie\` AS \`imie\`,\`l\`.\`nazwisko\` AS \`nazwisko\`,sum(\`r\`.\`1gr\` * 0.01 + \`r\`.\`2gr\` * 0.02 + \`r\`.\`5gr\` * 0.05 + \`r\`.\`10gr\` * 0.1 + \`r\`.\`20gr\` * 0.2 + \`r\`.\`50gr\` * 0.5 + \`r\`.\`1zl\` * 1 + \`r\`.\`2zl\` * 2 + \`r\`.\`5zl\` * 5 + \`r\`.\`10zl\` * 10 + \`r\`.\`20zl\` * 20 + \`r\`.\`50zl\` * 50 + \`r\`.\`100zl\` * 100 + \`r\`.\`200zl\` * 200 + \`r\`.\`500zl\` * 500) AS \`sumaPrzeliczona\` from (\`rozliczenie\` \`r\` join \`liczacy\` \`l\` on(\`r\`.\`liczacy3\` = \`l\`.\`id\`)) group by \`r\`.\`liczacy3\`) AS \`combinedData\` GROUP BY \`combinedData\`.\`idLiczacego\`, \`combinedData\`.\`imie\`, \`combinedData\`.\`nazwisko\` ; +DROP TABLE IF EXISTS \`sumaPrzeliczona1\`; + +CREATE OR REPLACE VIEW \`sumaPrzeliczona1\` AS SELECT \`r\`.\`liczacy1\` AS \`idLiczacego\`, \`l\`.\`imie\` AS \`imie\`, \`l\`.\`nazwisko\` AS \`nazwisko\`, sum(\`r\`.\`1gr\` * 0.01 + \`r\`.\`2gr\` * 0.02 + \`r\`.\`5gr\` * 0.05 + \`r\`.\`10gr\` * 0.1 + \`r\`.\`20gr\` * 0.2 + \`r\`.\`50gr\` * 0.5 + \`r\`.\`1zl\` * 1 + \`r\`.\`2zl\` * 2 + \`r\`.\`5zl\` * 5 + \`r\`.\`10zl\` * 10 + \`r\`.\`20zl\` * 20 + \`r\`.\`50zl\` * 50 + \`r\`.\`100zl\` * 100 + \`r\`.\`200zl\` * 200 + \`r\`.\`500zl\` * 500) AS \`sumaPrzeliczona\` FROM (\`rozliczenie\` \`r\` join \`liczacy\` \`l\` on(\`r\`.\`liczacy1\` = \`l\`.\`id\`)) GROUP BY \`r\`.\`liczacy1\` ; +DROP TABLE IF EXISTS \`sumaPrzeliczona2\`; + +CREATE OR REPLACE VIEW \`sumaPrzeliczona2\` AS SELECT \`r\`.\`liczacy2\` AS \`idLiczacego\`, \`l\`.\`imie\` AS \`imie\`, \`l\`.\`nazwisko\` AS \`nazwisko\`, sum(\`r\`.\`1gr\` * 0.01 + \`r\`.\`2gr\` * 0.02 + \`r\`.\`5gr\` * 0.05 + \`r\`.\`10gr\` * 0.1 + \`r\`.\`20gr\` * 0.2 + \`r\`.\`50gr\` * 0.5 + \`r\`.\`1zl\` * 1 + \`r\`.\`2zl\` * 2 + \`r\`.\`5zl\` * 5 + \`r\`.\`10zl\` * 10 + \`r\`.\`20zl\` * 20 + \`r\`.\`50zl\` * 50 + \`r\`.\`100zl\` * 100 + \`r\`.\`200zl\` * 200 + \`r\`.\`500zl\` * 500) AS \`sumaPrzeliczona\` FROM (\`rozliczenie\` \`r\` join \`liczacy\` \`l\` on(\`r\`.\`liczacy2\` = \`l\`.\`id\`)) GROUP BY \`r\`.\`liczacy2\` ; +DROP TABLE IF EXISTS \`sumaPrzeliczona3\`; + +CREATE OR REPLACE VIEW \`sumaPrzeliczona3\` AS SELECT \`r\`.\`liczacy3\` AS \`idLiczacego\`, \`l\`.\`imie\` AS \`imie\`, \`l\`.\`nazwisko\` AS \`nazwisko\`, sum(\`r\`.\`1gr\` * 0.01 + \`r\`.\`2gr\` * 0.02 + \`r\`.\`5gr\` * 0.05 + \`r\`.\`10gr\` * 0.1 + \`r\`.\`20gr\` * 0.2 + \`r\`.\`50gr\` * 0.5 + \`r\`.\`1zl\` * 1 + \`r\`.\`2zl\` * 2 + \`r\`.\`5zl\` * 5 + \`r\`.\`10zl\` * 10 + \`r\`.\`20zl\` * 20 + \`r\`.\`50zl\` * 50 + \`r\`.\`100zl\` * 100 + \`r\`.\`200zl\` * 200 + \`r\`.\`500zl\` * 500) AS \`sumaPrzeliczona\` FROM (\`rozliczenie\` \`r\` join \`liczacy\` \`l\` on(\`r\`.\`liczacy3\` = \`l\`.\`id\`)) GROUP BY \`r\`.\`liczacy3\` ; +DROP TABLE IF EXISTS \`SumaZebranaPrzezWolontariuszy\`; + +CREATE OR REPLACE VIEW \`SumaZebranaPrzezWolontariuszy\` AS SELECT \`wolontariusz\`.\`numerIdentyfikatora\` AS \`numerIdentyfikatora\`, \`wolontariusz\`.\`imie\` AS \`imie\`, \`wolontariusz\`.\`nazwisko\` AS \`nazwisko\`, \`sumy\`.\`suma\` AS \`suma\` FROM (\`wolontariusz\` join \`sumy\`) WHERE \`wolontariusz\`.\`id\` = \`sumy\`.\`wolontariuszID\` ORDER BY \`wolontariusz\`.\`numerIdentyfikatora\` ASC ; +DROP TABLE IF EXISTS \`suma_przeliczona2\`; + +CREATE OR REPLACE VIEW \`suma_przeliczona2\` AS SELECT coalesce(\`r\`.\`liczacy1\`,\`r\`.\`liczacy2\`,\`r\`.\`liczacy3\`) AS \`ID_Liczącego\`, \`l\`.\`imie\` AS \`Imię\`, \`l\`.\`nazwisko\` AS \`Nazwisko\`, sum(\`r\`.\`1gr\` * 0.01 + \`r\`.\`2gr\` * 0.02 + \`r\`.\`5gr\` * 0.05 + \`r\`.\`10gr\` * 0.1 + \`r\`.\`20gr\` * 0.2 + \`r\`.\`50gr\` * 0.5 + \`r\`.\`1zl\` * 1 + \`r\`.\`2zl\` * 2 + \`r\`.\`5zl\` * 5 + \`r\`.\`10zl\` * 10 + \`r\`.\`20zl\` * 20 + \`r\`.\`50zl\` * 50 + \`r\`.\`100zl\` * 100 + \`r\`.\`200zl\` * 200 + \`r\`.\`500zl\` * 500) AS \`Suma_Przeliczona\` FROM (\`rozliczenie\` \`r\` join \`liczacy\` \`l\` on(coalesce(\`r\`.\`liczacy1\`,\`r\`.\`liczacy2\`,\`r\`.\`liczacy3\`) = \`l\`.\`id\`)) GROUP BY coalesce(\`r\`.\`liczacy1\`,\`r\`.\`liczacy2\`,\`r\`.\`liczacy3\`), \`l\`.\`imie\`, \`l\`.\`nazwisko\` ; +DROP TABLE IF EXISTS \`sumy\`; + +CREATE OR REPLACE VIEW \`sumy\` AS SELECT \`rozliczenie\`.\`wolontariuszID\` AS \`wolontariuszID\`, sum(\`rozliczenie\`.\`1gr\` * 0.01 + \`rozliczenie\`.\`2gr\` * 0.02 + \`rozliczenie\`.\`5gr\` * 0.05 + \`rozliczenie\`.\`10gr\` * 0.1 + \`rozliczenie\`.\`20gr\` * 0.2 + \`rozliczenie\`.\`50gr\` * 0.5 + \`rozliczenie\`.\`1zl\` + \`rozliczenie\`.\`2zl\` * 2 + \`rozliczenie\`.\`5zl\` * 5 + \`rozliczenie\`.\`10zl\` * 10 + \`rozliczenie\`.\`20zl\` * 20 + \`rozliczenie\`.\`50zl\` * 50 + \`rozliczenie\`.\`100zl\` * 100 + \`rozliczenie\`.\`200zl\` * 200 + \`rozliczenie\`.\`500zl\` * 500) AS \`suma\` FROM \`rozliczenie\` GROUP BY \`rozliczenie\`.\`wolontariuszID\` ; + + `; + return toSend; +} + +exports.headerHtml = headerHtml; +exports.menuHtml = menuHtml; +exports.footerHtml = footerHtml; +exports.makeid = makeid; +exports.checkPesel = checkPesel; +exports.loger = loger; +exports.peselToShow = peselToShow; +exports.telefon = telefon; +exports.sendToDiscord = sendToDiscord; +exports.sendEmail = sendEmail; +exports.checkSendEmail = checkSendEmail; +exports.baza = baza; \ No newline at end of file diff --git a/serwer/app/index.js b/serwer/app/index.js new file mode 100644 index 0000000..61a4108 --- /dev/null +++ b/serwer/app/index.js @@ -0,0 +1,328 @@ + +//dot.env +require('dotenv').config(); +var mysql = require('mysql2'); + +var con = mysql.createConnection({ + host: process.env.MYSQLHOST, + user: process.env.MYSQLUSER, + password: process.env.MYSQLPASS, + port: process.env.MYSQLPORT, + database: process.env.MYSQLDB, + insecureAuth: true +}); + +let attempts = 0; +const maxAttempts = 5; + +function tryConnect() { + con.connect(function (err) { + if (err) { + attempts++; + console.log(`Attempt ${attempts} failed: ${err.message}`); + if (attempts < maxAttempts) { + console.log('Retrying...'); + setTimeout(tryConnect, 2500); // Odczekaj 2.5 sekundy przed kolejną próbą + } else { + console.error('Failed to connect after 10 attempts.'); + process.exit(1); // Zakończ aplikację + } + } else { + console.log('Connected!'); + + + var fs = require('fs'); + var express = require('express'); + var cookie = require('cookie'); + var app = express(); + var bodyParser = require('body-parser'); + app.use(bodyParser.urlencoded({ + extended: true + })); + + const fileUpload = require('express-fileupload'); + + app.use(fileUpload({ + useTempFiles: true, // Użycie tymczasowych plików + tempFileDir: '/tmp/', // Ścieżka do katalogu tymczasowego + limits: { fileSize: 10 * 1024 * 1024 } // Maksymalny rozmiar pliku: 10 MB + })); + + //import functions from func.js + const {headerHtml, menuHtml, footerHtml, makeid, loger} = require('./func.js'); + + const panelRoutes = require('./panelRoutes.js'); + const liczacy = require('./liczacy.js'); + //const apiRoutes = require('./apiRoutes'); + + app.get('/', function(req, res) { + res.redirect('/panel'); + }); + app.all('/*', function(req, res, next) { + //save to file, route, coockies, date, time + var date = new Date(); + var day = date.getDate(); + var month = date.getMonth() + 1; + var year = date.getFullYear(); + var hour = date.toLocaleTimeString(); + var minute = date.toLocaleTimeString(); + var second = date.toLocaleTimeString(); + var time = hour + ':' + minute + ':' + second; + var fullDate = year + '-' + month + '-' + day; + var route = req.originalUrl; + var cookies = req.headers.cookie; + var userAgent = req.headers['user-agent']; + var toSave = fullDate + '\t' + time + '\t' + route + '\t' + cookies || req.body.token || req.query.token || req.headers['x-access-token'] + '\t' + userAgent + '\n'; + loger(fs, toSave, 'info'); + next(); + }); + + //send style.css from html folder + app.get('/style.css', function(req, res) { + res.sendFile(__dirname + '/css/style.css'); + }); + + //static files + app.use(express.static('static')); + + app.get('/login', function(req, res) { + var toReturn = headerHtml(); + toReturn += menuHtml(); + toReturn += '
'; + toReturn += '

Logowanie

'; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(); + res.send(toReturn); + + }); + + app.post('/login', function(req, res) { + var login = req.body.login; + var password = req.body.password; + //sprawdź czy istnieje taki login i hasło + con.query('SELECT * FROM login WHERE login = ? AND haslo = SHA1(?) AND aktywne = 1', [login, password], function(err, result) { + if (err) throw err; + if (result.length > 0) { + //utwórz token + var token = makeid(32); + //zapisz token do bazy + con.query('INSERT INTO tokeny (token, typ, userId) VALUES (?, 1, ?)', [token, result[0].id], function(err, result) { + if (err) throw err; + //ustaw ciasteczko + res.setHeader('Set-Cookie', cookie.serialize('token', token, { + httpOnly: true, + maxAge: 60 * 60 * 24 * 7 // 1 week + })); + res.redirect('/panel') + }); + } else { + //niepoprawne dane + res.redirect('/login'); + } + }); + }); + + app.get('/loginliczacy', function(req, res) { + //pobierz kod, 10 znaków i sprawdź czy istnieje w liczacy w polu qr + var toReturn = headerHtml(); + toReturn += menuHtml(); + toReturn += '
'; + toReturn += '

Logowanie osoby liczącej

'; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(); + res.send(toReturn); + }) + + app.post('/loginliczacy', function(req, res) { + var password = req.body.password; + //sprawdź czy istnieje taki login i hasło + con.query('SELECT * FROM liczacy WHERE qr = ? AND aktywne = 1', [password], function(err, result) { + if (err) throw err; + if (result.length > 0) { + //utwórz token + var token = makeid(32); + //zapisz token do bazy + con.query('INSERT INTO tokenyLiczacy (token, typ, userId) VALUES (?, 1, ?)', [token, result[0].id], function(err, result) { + if (err) throw err; + //ustaw ciasteczko + res.setHeader('Set-Cookie', cookie.serialize('liczacy', token, { + httpOnly: true, + maxAge: 60 * 60 * 24 * 7 // 1 week + })); + res.redirect('/liczacy') + }); + } else { + //niepoprawne dane + res.redirect('/loginliczacy'); + } + }); + }) + + + app.all("/panel", function(req, res) { + //redirect to /panel/home + res.redirect('/panel/home'); + }); + + + app.use('/panel', panelRoutes); + app.use('/liczacy', liczacy); + //app.use('/api', apiRoutes); + + app.all('/statystyki2', function(req, res) { + var toReturn = headerHtml("Statystyki"); + toReturn += menuHtml(0); + //make script to refresh every 5 seconds, and full screen this page + toReturn += ''; + + toReturn += '
'; + toReturn += '
'; + toReturn += '

Całkowita suma

'; + toReturn += ''; + //wypisz sumę zebranych pieniędzy, sumę poszczególnych nominałów + //pobierz wszystkie rozliczenia + con.query('SELECT * FROM rozliczenie WHERE aktywne = 1', function(err, result) { + if (err) throw err; + var suma = 0; + var sumaTerminal = 0; + var suma1gr = 0; + var suma2gr = 0; + var suma5gr = 0; + var suma10gr = 0; + var suma20gr = 0; + var suma50gr = 0; + var suma1zl = 0; + var suma2zl = 0; + var suma5zl = 0; + var suma10zl = 0; + var suma20zl = 0; + var suma50zl = 0; + var suma100zl = 0; + var suma200zl = 0; + var suma500zl = 0; + result.forEach(function(row) { + suma += row['1gr'] + row['2gr'] * 2 + row['5gr'] * 5 + row['10gr'] * 10 + row['20gr'] * 20 + row['50gr'] * 50 + row['1zl'] * 100 + row['2zl'] * 200 + row['5zl'] * 500 + row['10zl'] * 1000 + row['20zl'] * 2000 + row['50zl'] * 5000 + row['100zl'] * 10000 + row['200zl'] * 20000 + row['500zl'] * 50000; + sumaTerminal += row.sumaZTerminala; + suma1gr += row['1gr']; + suma2gr += row['2gr']; + suma5gr += row['5gr']; + suma10gr += row['10gr']; + suma20gr += row['20gr']; + suma50gr += row['50gr']; + suma1zl += row['1zl']; + suma2zl += row['2zl']; + suma5zl += row['5zl']; + suma10zl += row['10zl']; + suma20zl += row['20zl']; + suma50zl += row['50zl']; + suma100zl += row['100zl']; + suma200zl += row['200zl']; + suma500zl += row['500zl']; + }); + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Suma' + suma/100.0 + ' zł
Suma z terminali' + sumaTerminal + ' zł
1 gr' + suma1gr + '
2 gr' + suma2gr + '
5 gr' + suma5gr + '
10 gr' + suma10gr + '
20 gr' + suma20gr + '
50 gr' + suma50gr + '
1 zł' + suma1zl + '
2 zł' + suma2zl + '
5 zł' + suma5zl + '
10 zł' + suma10zl + '
20 zł' + suma20zl + '
50 zł' + suma50zl + '
100 zł' + suma100zl + '
200 zł' + suma200zl + '
500 zł' + suma500zl + '
'; + toReturn += '
'; + toReturn += '
'; + toReturn += '

Top 10 wolontariuszy

'; + //SELECT * FROM `SumaZebranaPrzezWolontariuszy` ORDER BY `SumaZebranaPrzezWolontariuszy`.`suma` ASC LIMIT 10; + toReturn += ''; + toReturn += ''; + con.query('SELECT numerIdentyfikatora, imie, nazwisko, suma FROM `SumaZebranaPrzezWolontariuszy` ORDER BY `SumaZebranaPrzezWolontariuszy`.`suma` ASC LIMIT 10;', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
WolontariuszSuma
' + row.numerIdentyfikatora + '' + row.suma + '
'; + toReturn += '
'; + toReturn += '
'; + //który liczący najwięcej liczył + toReturn += '

Najwięcej puszek przeliczonych

'; + toReturn += ''; + toReturn += ''; + con.query("SELECT idLiczacego, imie, nazwisko, sumaPrzeliczona FROM `sumaPrzeliczona` ORDER BY `sumaPrzeliczona`.`sumaPrzeliczona` DESC LIMIT 10;", function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
LiczącySuma
' + row.idLiczacego + '' + row.sumaPrzeliczona + '
'; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(); + res.send(toReturn); + }) + + }); + }); + }); + + app.all('/logout', function(req, res) { + //sprawdź czy token istnieje i jest aktywny + var cookies = cookie.parse(req.headers.cookie || ''); + var token = cookies.token; + con.query('UPDATE tokeny SET aktywny = 0 WHERE token = ?', [token], function(err, result) { + if (err) throw err; + res.redirect('/panel/login'); + loger(fs, 'Wylogowano użytkownika ' + req.user.kto, 'info'); + }); + }); + + //404 + app.all('/*', function(req, res, next) { + var toReturn = headerHtml(); + toReturn += menuHtml(2); + toReturn += '
'; + toReturn += '

404

'; + toReturn += '
'; + toReturn += footerHtml(); + res.status(404).send(toReturn); + + + }); + app.listen(process.env.PORT || 8880, function() { + console.log('Example app listening on port http://localhost:' + process.env.PORT || 8880 + '!'); + }); + + } + }); +} + +tryConnect(); diff --git a/serwer/app/liczacy.js b/serwer/app/liczacy.js new file mode 100644 index 0000000..d7f17f9 --- /dev/null +++ b/serwer/app/liczacy.js @@ -0,0 +1,453 @@ +const express = require('express'); +const liczacy = express.Router(); +const fs = require('fs'); + +require('dotenv').config(); +//mysql +var mysql = require('mysql2'); +var con = mysql.createConnection({ + host: process.env.MYSQLHOST, + user: process.env.MYSQLUSER, + password: process.env.MYSQLPASS, + port : process.env.MYSQLPORT, + database: process.env.MYSQLDB, + insecureAuth : true +}); +con.connect(function(err) { + if (err) throw err; + console.log('Connected!'); +}); +var cookie = require('cookie'); +var app = express(); +var bodyParser = require('body-parser'); +app.use(bodyParser.urlencoded({ + extended: true +})); +//import functions from func.js +const {headerHtml, menuHtml, footerHtml, checkPesel, loger, telefon, sendToDiscord, sendEmail} = require('./func.js'); +liczacy.use(function(req, res, next) { + var cookies = cookie.parse(req.headers.cookie || ''); + var liczacy = cookies.liczacy; + con.query('SELECT * FROM tokenyLiczacy, liczacy WHERE token = ? AND aktywny = 1 AND tokenyLiczacy.userId = liczacy.id', [liczacy], function(err, result) { + if (err) throw err; + if (result.length > 0) { + //get from result imie + req.user = result[0]; + next(); + } else { + res.redirect('/loginliczacy'); + loger(fs, 'Nieudana próba dostępu do panelu liczącego przy użyciu tokenu: ' + liczacy, 'warning'); + } + }); +}); + +liczacy.get('/', function(req, res) { + var toReturn = headerHtml(); + toReturn += menuHtml(4); + toReturn += '
'; + toReturn += '

Panel

'; + toReturn += '

Witaj ' + req.user.imie + '

'; + toReturn += '
'; + toReturn += 'Rozlicz wolontariusza'; + toReturn += 'Wyloguj się'; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(2); + res.send(toReturn); +}) + +liczacy.all('/statystyki2', function(req, res) { + var toReturn = headerHtml("Statystyki"); + toReturn += menuHtml(4); + //make script to refresh every 5 seconds, and full screen this page + toReturn += ''; + + toReturn += '
'; + toReturn += '
'; + toReturn += '

Całkowita suma

'; + toReturn += ''; + //wypisz sumę zebranych pieniędzy, sumę poszczególnych nominałów + //pobierz wszystkie rozliczenia + con.query('SELECT * FROM rozliczenie WHERE aktywne = 1', function(err, result) { + if (err) throw err; + var suma = 0; + var sumaTerminal = 0; + var suma1gr = 0; + var suma2gr = 0; + var suma5gr = 0; + var suma10gr = 0; + var suma20gr = 0; + var suma50gr = 0; + var suma1zl = 0; + var suma2zl = 0; + var suma5zl = 0; + var suma10zl = 0; + var suma20zl = 0; + var suma50zl = 0; + var suma100zl = 0; + var suma200zl = 0; + var suma500zl = 0; + result.forEach(function(row) { + suma += row['1gr'] + row['2gr'] * 2 + row['5gr'] * 5 + row['10gr'] * 10 + row['20gr'] * 20 + row['50gr'] * 50 + row['1zl'] * 100 + row['2zl'] * 200 + row['5zl'] * 500 + row['10zl'] * 1000 + row['20zl'] * 2000 + row['50zl'] * 5000 + row['100zl'] * 10000 + row['200zl'] * 20000 + row['500zl'] * 50000; + sumaTerminal += row.sumaZTerminala; + suma1gr += row['1gr']; + suma2gr += row['2gr']; + suma5gr += row['5gr']; + suma10gr += row['10gr']; + suma20gr += row['20gr']; + suma50gr += row['50gr']; + suma1zl += row['1zl']; + suma2zl += row['2zl']; + suma5zl += row['5zl']; + suma10zl += row['10zl']; + suma20zl += row['20zl']; + suma50zl += row['50zl']; + suma100zl += row['100zl']; + suma200zl += row['200zl']; + suma500zl += row['500zl']; + }); + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Suma' + suma/100.0 + ' zł
Suma z terminali' + sumaTerminal + ' zł
1 gr' + suma1gr + '
2 gr' + suma2gr + '
5 gr' + suma5gr + '
10 gr' + suma10gr + '
20 gr' + suma20gr + '
50 gr' + suma50gr + '
1 zł' + suma1zl + '
2 zł' + suma2zl + '
5 zł' + suma5zl + '
10 zł' + suma10zl + '
20 zł' + suma20zl + '
50 zł' + suma50zl + '
100 zł' + suma100zl + '
200 zł' + suma200zl + '
500 zł' + suma500zl + '
'; + toReturn += '
'; + toReturn += '
'; + toReturn += '

Top 10 wolontariuszy

'; + //SELECT * FROM `SumaZebranaPrzezWolontariuszy` ORDER BY `SumaZebranaPrzezWolontariuszy`.`suma` ASC LIMIT 10; + toReturn += ''; + toReturn += ''; + con.query('SELECT numerIdentyfikatora, imie, nazwisko, suma FROM `SumaZebranaPrzezWolontariuszy` ORDER BY `SumaZebranaPrzezWolontariuszy`.`suma` ASC LIMIT 10;', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
WolontariuszSuma
' + row.numerIdentyfikatora + '' + row.suma + '
'; + toReturn += '
'; + toReturn += '
'; + //który liczący najwięcej liczył + toReturn += '

Najwięcej puszek przeliczonych

'; + toReturn += ''; + toReturn += ''; + con.query("SELECT idLiczacego, imie, nazwisko, sumaPrzeliczona FROM `sumaPrzeliczona` ORDER BY `sumaPrzeliczona`.`sumaPrzeliczona` DESC LIMIT 10;", function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
LiczącySuma
' + row.idLiczacego + '' + row.sumaPrzeliczona + '
'; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(); + res.send(toReturn); + }) + + }); + }); +}); + + +liczacy.get('/rozlicz', function(req, res) { + var toReturn = headerHtml("Lista wolontariuszy do rozliczenia"); + toReturn += menuHtml(4); + toReturn += '
'; + toReturn += '

Rozlicz

'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1 AND id NOT IN (SELECT wolontariuszID FROM rozliczenie WHERE aktywne = 1) ORDER BY numerIdentyfikatora ASC', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + if(row.rodzic == "BRAK") + toReturn += ''; + else + toReturn += ''; + toReturn += ''; + toReturn += ''; + }); + toReturn += '
NumerImięNazwiskoTerminalRodzicOpcje
' + row.numerIdentyfikatora + '' + row.imie + '' + row.nazwisko + '' + (row.terminal == 1 ? 'Tak' : 'Nie') + ' ' + row.rodzic + 'Rozlicz
'; + toReturn += '
'; + toReturn += footerHtml(2); + res.send(toReturn); + }); +}); + + +liczacy.get('/rozliczWolontariusza', function(req, res) { + var idWolontariusza = req.query.id; + //pobierz dane wolontariusza, wyświetl formularz rozliczenia + var toReturn = headerHtml("Rozlicz wolontariusza"); + toReturn += menuHtml(4); + toReturn += '
'; + toReturn += '

Rozlicz wolontariusza

'; + toReturn += '
'; + con.query('SELECT * FROM wolontariusz WHERE id = ?', [idWolontariusza], function(err, result) { + if (err) throw err; + if (result.length > 0) { + toReturn += "

" + result[0].imie + " " + result[0].nazwisko + "

"; + toReturn += "

ID: " + result[0].numerIdentyfikatora + "

"; + toReturn += '

Suma: 0

'; + //pokaż formularz do wpisywania zebranej kwoaty i monet (sumę liczy program, użytkownik podaje ilość monet) + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //kwota z terminala + toReturn += ''; + //waluta obca, tekstarea + toReturn += ''; + //dary inne, tekstarea + toReturn += ''; + //uwagi, tekstarea + toReturn += ''; + //sala + toReturn += ''; + //liczący 1 + toReturn += ''; + console.log(req.user); + + toReturn += ''; + + //liczący 2 + toReturn += ''; + //liczący 3 + toReturn += ''; + toReturn += '
Suma
WalutaIlość
1 gr
2 gr
5 gr
10 gr
20 gr
50 gr
1 zł
2 zł
5 zł
10 zł
20 zł
50 zł
100 zł
200 zł
500 zł
Kwota z terminala
Waluta obca
Dary inne
Uwagi
Sala
Liczący 1' + req.user.imie + ' ' + req.user.nazwisko + '
Liczący 2
Liczący 3
'; + toReturn += ''; + toReturn += '

Suma: 0

'; + toReturn += ''; + + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(2); + res.send(toReturn); + }); + }); + + } + }); +}); +liczacy.post('/rozliczWolontariusza', function(req, res) { + //weryfikowal to numer id z tokenu + var idWolontariusza = req.query.id; + var idLiczacy1 = req.body.liczacy1; + var idLiczacy2 = req.body.liczacy2; + var idLiczacy3 = req.body.liczacy3 == 0 ? null : req.body.liczacy3; + var sala = req.body.sala; + var uwagi = req.body.uwagi; + var daryInne = req.body.daryInne; + var walutaObca = req.body.walutaObca; + var terminal = req.body.terminal > 0 ? 1 : 0; + var gr1 = req.body['1gr']; + var gr2 = req.body['2gr']; + var gr5 = req.body['5gr']; + var gr10 = req.body['10gr']; + var gr20 = req.body['20gr']; + var gr50 = req.body['50gr']; + var zl1 = req.body['1zl']; + var zl2 = req.body['2zl']; + var zl5 = req.body['5zl']; + var zl10 = req.body['10zl']; + var zl20 = req.body['20zl']; + var zl50 = req.body['50zl']; + var zl100 = req.body['100zl']; + var zl200 = req.body['200zl']; + var zl500 = req.body['500zl']; + var sumaZTerminala = req.body.terminal; + //zapisz dane do bazy + $sql = "INSERT INTO `rozliczenie` (`id`, `wolontariuszID`, `czasRozliczenia`, `terminal`, `sumaZTerminala`, `1gr`, `2gr`, `5gr`, `10gr`, `20gr`, `50gr`, `1zl`, `2zl`, `5zl`, `10zl`, `20zl`, `50zl`, `100zl`, `200zl`, `500zl`, `walutaObca`, `daryInne`, `uwagi`, `liczacy1`, `liczacy2`, `liczacy3`, `sala`, `weryfikowal`, `wpisaneDoBSS`, `ostatniaZmiana`, `aktywne`) "; + $sql += 'VALUES (NULL, ?, CURRENT_TIME(), ?, ?, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0,0,CURRENT_TIME(),1)'; + con.query($sql, [idWolontariusza, terminal, sumaZTerminala, gr1, gr2, gr5, gr10, gr20, gr50, zl1, zl2, zl5, zl10, zl20, zl50, zl100, zl200, zl500, walutaObca, daryInne, uwagi, idLiczacy1, idLiczacy2, idLiczacy3, sala], function(err, result) { + if (err) throw err; + if(process.env.DISCORD == "TAK") + { + con.query('SELECT * FROM wolontariusz WHERE id = ?', [idWolontariusza], function(err, result) { + if (err) throw err; + var suma = Number(gr1) +Number(gr2)*2 + Number(gr5)*5 + Number(gr10)*10 + Number(gr20)*20 + Number(gr50)*50 + Number(zl1)*100 + Number(zl2)*200 + Number(zl5)*500 + Number(zl10)*1000 + Number(zl20)*2000 + Number(zl50)*5000 + Number(zl100)*10000 + Number(zl200)*20000 + Number(zl500)*50000 + Number(sumaZTerminala)*100; + sendToDiscord(result[0].imie, result[0].nazwisko, suma/100, result[0].discord); + }); + } + if(process.env.SENDEMAILS == "TAK") + { + con.query('SELECT * FROM wolontariusz WHERE id = ?', [idWolontariusza], function(err, result) { + if (err) throw err; + var suma = Number(gr1) +Number(gr2)*2 + Number(gr5)*5 + Number(gr10)*10 + Number(gr20)*20 + Number(gr50)*50 + Number(zl1)*100 + Number(zl2)*200 + Number(zl5)*500 + Number(zl10)*1000 + Number(zl20)*2000 + Number(zl50)*5000 + Number(zl100)*10000 + Number(zl200)*20000 + Number(zl500)*50000 + Number(sumaZTerminala)*100; + sendEmail(result[0].imie, result[0].nazwisko, suma/100, result[0].email); + }); + } + res.redirect('/liczacy/rozliczenia#'+idWolontariusza); + loger(fs, 'Rozliczono wolontariusza o id: ' + idWolontariusza, 'info'); + }); +}); + + +liczacy.get("/szybkieInfo", function(req, res) { + var toReturn = ''; + toReturn += '
'; + toReturn += '

Szybkie informacje

'; + //sprawdź ile jest wolontariuszy oraz ilu jest rozliczonych + var iloscWolontariuszy = 0; + var iloscRozliczonych = 0; + var iloscLiczących = 0; + con.query("SELECT * FROM `wolontariusz` WHERE `aktywny` = 1", function(err, result) { + if (err) throw err; + iloscWolontariuszy = result.length; + con.query("SELECT * FROM `rozliczenie` WHERE `aktywne` = 1", function(err, result) { + if (err) throw err; + iloscRozliczonych = result.length; + con.query("SELECT * FROM `liczacy` WHERE `aktywne` = 1", function(err, result) { + if (err) throw err; + iloscLiczących = result.length; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Ilość wolontariuszy' + iloscWolontariuszy + '
Ilość rozliczonych' + iloscRozliczonych + '
Ilość liczących' + iloscLiczących + '
'; + //ostatni rozliczony + toReturn += '

Ostatnio rozliczeni

'; + toReturn += ''; + toReturn += ''; + con.query("SELECT * FROM `rozliczenie`, `wolontariusz` WHERE `rozliczenie`.`wolontariuszID` = `wolontariusz`.`id` AND `rozliczenie`.`aktywne` = 1 ORDER BY `rozliczenie`.`czasRozliczenia` DESC LIMIT 5", function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
WolontariuszSuma
' + row.numerIdentyfikatora + '' + (row['1gr'] + row['2gr'] * 2 + row['5gr'] * 5 + row['10gr'] * 10 + row['20gr'] * 20 + row['50gr'] * 50 + row['1zl'] * 100 + row['2zl'] * 200 + row['5zl'] * 500 + row['10zl'] * 1000 + row['20zl'] * 2000 + row['50zl'] * 5000 + row['100zl'] * 10000 + row['200zl'] * 20000 + row['500zl'] * 50000 + row.sumaZTerminala * 100)/100.0 + ' zł
'; + toReturn += '
'; + res.send(toReturn); + }); + }); + }); + }); +}); +liczacy.all('/wyloguj', function(req, res) { + //tokenyLiczacy ustaw aktywny na 0 + con.query('UPDATE tokenyLiczacy SET aktywny = 0 WHERE userId = ?', [req.user.id], function(err, result) { + if (err) throw err; + res.clearCookie('liczacy'); + res.redirect('/loginliczacy'); + }); +}); + +module.exports = liczacy; \ No newline at end of file diff --git a/serwer/app/logs/none b/serwer/app/logs/none new file mode 100644 index 0000000..e69de29 diff --git a/serwer/app/package.json b/serwer/app/package.json new file mode 100644 index 0000000..18701c0 --- /dev/null +++ b/serwer/app/package.json @@ -0,0 +1,27 @@ +{ + "name": "wosp", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node index.js", + "test": "nodemon index.js" + }, + "keywords": [], + "author": "kry008", + "license": "MIT", + "dependencies": { + "body-parser": "^1.20.2", + "cookie": "^0.6.0", + "cors": "^2.8.5", + "discord-webhook-node": "^1.1.8", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "mysql2": "^3.12.0", + "nodemailer": "^6.9.8", + "nodemon": "^3.0.2", + "csv-parser": "^3.0.0", + "express-fileupload": "^1.5.1", + "strip-bom-stream": "^4.0.0" + } +} diff --git a/serwer/app/panelRoutes.js b/serwer/app/panelRoutes.js new file mode 100644 index 0000000..95b563c --- /dev/null +++ b/serwer/app/panelRoutes.js @@ -0,0 +1,1721 @@ +const express = require('express'); +const panelRouter = express.Router(); +const fs = require('fs'); + +require('dotenv').config(); +//mysql +var mysql = require('mysql2'); +var con = mysql.createConnection({ + host: process.env.MYSQLHOST, + user: process.env.MYSQLUSER, + password: process.env.MYSQLPASS, + port : process.env.MYSQLPORT, + database: process.env.MYSQLDB, + insecureAuth : true +}); +con.connect(function(err) { + if (err) throw err; + console.log('Connected!'); +}); +var cookie = require('cookie'); +var app = express(); +var bodyParser = require('body-parser'); +app.use(bodyParser.urlencoded({ + extended: true +})); +const fileUpload = require('express-fileupload'); + +app.use(fileUpload({ + useTempFiles: true, // Użycie tymczasowych plików + tempFileDir: '/tmp/', // Ścieżka do katalogu tymczasowego + limits: { fileSize: 10 * 1024 * 1024 } // Maksymalny rozmiar pliku: 10 MB +})); + +//import functions from func.js +const {headerHtml, menuHtml, footerHtml, checkPesel, loger, telefon, sendToDiscord, sendEmail, makeid, checkSendEmail, baza} = require('./func.js'); +//function loger(fs, text, type = 'info') +panelRouter.use(function(req, res, next) { + var cookies = cookie.parse(req.headers.cookie || ''); + var token = cookies.token; + con.query('SELECT * FROM tokeny, login WHERE token = ? AND tokeny.userId = login.id AND aktywny = 1', [token], function(err, result) { + if (err) throw err; + if (result.length > 0) { + req.user = result[0]; + next(); + } else { + res.redirect('/login'); + loger(fs, 'Nieudana próba dostępu do panelu administracyjnego przy użyciu tokenu: ' + token, 'warning'); + } + }); +}); +panelRouter.get('/home', function(req, res) { + var toReturn = headerHtml(); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Panel

'; + toReturn += '

Witaj ' + req.user.kto + '

'; + toReturn += '
'; + toReturn += 'Rozlicz wolontariusza'; + toReturn += 'Dodaj wolontariusza'; + toReturn += 'Lista wolontariuszy'; + toReturn += 'Lista rozliczonych wolontariuszy'; + toReturn += 'Dodaj osobę liczącą'; + toReturn += 'Lista osób liczących'; + toReturn += 'Drukuj listy'; + toReturn += 'Statystyki'; + toReturn += 'Dodaj dostęp do panelu'; + toReturn += 'Usuń dostęp do panelu'; + toReturn += 'Pokaż osoby z dostępem do panelu'; + toReturn += 'Unieważnij wszystkie tokeny'; + toReturn += 'Importuj z BSS Wolontariuszy'; + //toReturn += 'Wykonaj kopię zapasową'; + toReturn += 'Zmień hasło'; + toReturn += 'Wyloguj się'; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + +}); +panelRouter.get('/osobyLiczace', function(req, res) { +//pokaż w ładnej tabeli wszystkie osoby liczące, posortuj według nazwiska, na górze strony dodaj przycisk dodaj osobę liczącą + var toReturn = headerHtml(); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Osoby liczące

'; + toReturn += 'Dodaj osobę liczącą'; + if (req.query.error == 1) { + toReturn += '

Osoba o takich danych już istnieje

'; + } + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM liczacy WHERE aktywne = 1 ORDER BY nazwisko ASC', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + }); + toReturn += '
IDImięNazwiskoPokaż kod
' + row.id + '' + row.imie + '' + row.nazwisko + 'Pokaż
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + loger(fs, 'Wyświetlono listę osób liczących', 'info'); + }); +}); + +panelRouter.get('/kod', function(req, res) { + var id = req.query.id; + //SELECT qr FROM liczacy WHERE id = ? + con.query('SELECT qr FROM liczacy WHERE id = ?', [id], function(err, result) { + if (err) throw err; + if (result.length == 1) { + var toReturn = headerHtml(); + toReturn += menuHtml(1); + toReturn += ' '; + toReturn += '
'; + toReturn += '

Kod osoby liczącej

'; + toReturn += '
'; + toReturn += '
'; + toReturn += 'Kod: ' + result[0].qr; + toReturn += ''; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + loger(fs, 'Wyświetlono kod osoby liczącej o id: ' + id, 'info'); + } + }); +}); + + + + +panelRouter.get('/dodajOsobeLiczaca', function(req, res) { + var toReturn = headerHtml(); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Dodaj osobę liczącą

'; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); +panelRouter.post('/dodajOsobeLiczaca', function(req, res) { + //sprawdź czy istnieje taka osoba + var imie = req.body.imie; + var nazwisko = req.body.nazwisko; + con.query('SELECT * FROM liczacy WHERE imie = ? AND nazwisko = ?', [imie, nazwisko], function(err, result) { + if (err) throw err; + if (result.length > 0) { + //istnieje + res.redirect('/panel/osobyLiczace?error=1'); + loger(fs, 'Nieudana próba dodania osoby liczącej o imieniu: ' + imie + ' i nazwisku: ' + nazwisko, 'warning'); + } else { + //nie istnieje + var qr = makeid(24) + con.query('INSERT INTO liczacy (imie, nazwisko, qr) VALUES (?, ?, ?)', [imie, nazwisko, qr], function(err, result) { + if (err) throw err; + res.redirect('/panel/osobyLiczace'); + loger(fs, 'Dodano osobę liczącą o imieniu: ' + imie + ' i nazwisku: ' + nazwisko + ' z kodem: ' + qr, 'info'); + }); + } + }); +}); +panelRouter.get('/listaWolontariuszy', function(req, res) { + //pokaż w ładnej tabeli wszystkich wolontariuszy, posortuj według nazwiska, na górze strony dodaj przycisk dodaj wolontariusza + var toReturn = headerHtml(); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Lista wolontariuszy

'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + if(req.user.id == 1) + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1 ORDER BY numerIdentyfikatora ASC', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + //jeżeli zaznacz, to kolor wiersza na rgba(0, 255, 0, 0.5) + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + if(req.user.id == 1) + toReturn += ''; + toReturn += ''; + if(row.rodzic == "BRAK") + toReturn += ''; + else + toReturn += ''; + toReturn += ''; + toReturn += ''; + }); + toReturn += '
NumerImięNazwiskoDiscordEmailTelefonPESELTerminalRodzicOpcje
' + row.numerIdentyfikatora + '' + row.imie + '' + row.nazwisko + '' + row.discord + '' + row.email + '' + telefon(row.telefon, 1) + '' + '###########' + '' + (row.terminal == 1 ? 'Tak' : 'Nie') + ' ' + row.rodzic + 'Edytuj
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + loger(fs, 'Wyświetlono listę wolontariuszy ' + req.user.id, 'info'); + }); +}); +panelRouter.get('/edytujWolontariusza', function(req, res) { + var toReturn = headerHtml(); + toReturn += menuHtml(1); + //pobierz dane wolontariusza + con.query('SELECT * FROM wolontariusz WHERE id = ?', [req.query.id], function(err, result) { + if (err) throw err; + if (result.length > 0) { + //istnieje + toReturn += '
'; + toReturn += '

Edytuj wolontariusza

'; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Numer identyfikatora
Imię
Nazwisko
Discord
Email
Telefon
PESEL
Terminal
Rodzic
'; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + } + }); +}); +panelRouter.post('/edytujWolontariusza', function(req, res) { + var id = req.body.id; + var imie = req.body.imie; + var nazwisko = req.body.nazwisko; + var discord = req.body.discord; + var email = req.body.email; + var telefon = req.body.telefon; + var pesel = req.body.pesel; + var terminal = req.body.terminal == 1 ? 1 : 0; + var rodzic = req.body.rodzic; + //zapisz dane + con.query('UPDATE wolontariusz SET imie = ?, nazwisko = ?, discord = ?, email = ?, telefon = ?, pesel = ?, terminal = ?, rodzic = ? WHERE id = ?', [imie, nazwisko, discord, email, telefon, pesel, terminal, rodzic, id], function(err, result) { + if (err) throw err; + res.redirect('/panel/listaWolontariuszy'); + loger(fs, 'Edytowano wolontariusza o id: ' + id, 'info'); + }); +}); +panelRouter.get('/dodajWolontariusza', function(req, res) { + var toReturn = headerHtml(); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Dodaj wolontariusza

'; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Numer identyfikatora
Imię
Nazwisko
Discord
Email
Telefon
PESEL
Terminal
Rodzic
Zaznacz na liście
'; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); +panelRouter.post('/dodajWolontariusza', function(req, res) { + //sprawdź czy istnieje taki wolontariusz + var numerIdentyfikatora = req.body.numerIdentyfikatora; + var imie = req.body.imie; + var nazwisko = req.body.nazwisko; + var discord = req.body.discord; + var email = req.body.email; + var telefon = req.body.telefon; + var pesel = req.body.pesel; + var terminal = req.body.terminal == 1 ? 1 : 0; + var rodzic = req.body.rodzic; + var zaznacz = req.body.zaznacz == 1 ? 1 : 0; + con.query('SELECT * FROM wolontariusz WHERE numerIdentyfikatora = ? OR pesel = ?', [numerIdentyfikatora, pesel], function(err, result) { + if (err) throw err; + if (result.length > 0) { + //istnieje + res.redirect('/panel/listaWolontariuszy?error=1'); + loger(fs, 'Nieudana próba dodania wolontariusza o numerze identyfikatora: ' + numerIdentyfikatora + ' lub peselu: ' + pesel.substring(0, 3) + '########', 'warning'); + } else { + //nie istnieje + con.query('INSERT INTO wolontariusz (numerIdentyfikatora, imie, nazwisko, discord, email, telefon, pesel, terminal, rodzic, zaznacz) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [numerIdentyfikatora, imie, nazwisko, discord, email, telefon, pesel, terminal, rodzic, zaznacz], function(err, result) { + if (err) throw err; + res.redirect('/panel/listaWolontariuszy'); + loger(fs, 'Dodano wolontariusza o numerze identyfikatora: ' + numerIdentyfikatora + ' i peselu: ' + pesel.substring(0, 3) + '########', 'info'); + }); + } + }); +}); +panelRouter.all('/druki', function(req, res) { + var toReturn = headerHtml(); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '
'; + toReturn += 'Listy obecności wolontariuszy'; + toReturn += 'Odbiór puszek i innych rzeczy'; + toReturn += 'Lista przyjść'; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); +panelRouter.all('/listyObecnosci', function(req, res) { + //pobierz wszystkich wolontariuszy, w tabelce wypisz numer identyfikatora, imię, nazwisko, telefon, rodzic, pole do wpisania daty (puste), pole do wpisania podpisu (puste) + var toReturn = headerHtml("Lista obecności na spotkaniu organizacyjnym"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1 ORDER BY numerIdentyfikatora ASC', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + if(row.rodzic == "BRAK") + toReturn += ''; + else + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + }); + toReturn += '
NumerImięNazwiskoTelefonRodzicDataPodpis
' + row.numerIdentyfikatora + '' + row.imie + '' + row.nazwisko + '' + telefon(row.telefon, 0) + ' ' + row.rodzic + '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }); +}); +panelRouter.all('/listyRzeczy', function(req, res) { + //pobierz wszystkich wolontariuszy, w tabelce wypisz numer identyfikatora, imię, nazwisko, telefon, rodzic, pole do wpisania daty (puste), pole do wpisania podpisu (puste) + var toReturn = headerHtml("Odbiór puszek i innych rzeczy"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1 ORDER BY numerIdentyfikatora ASC', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + }); + toReturn += '
NumerImięNazwiskoDataOdbieram nienaruszoną puszkęOdbieram zestaw wolontariusza
(w tym identyfikator)
' + row.numerIdentyfikatora + '' + row.imie + '' + row.nazwisko + '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }); +}); +panelRouter.all('/listyPrzyjsc', function(req, res) { + var toReturn = headerHtml("Lista przed rozliczeniem"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1 ORDER BY numerIdentyfikatora ASC', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //wyświetl tylko 3 lub 4 losowe pozycje cyfry PESEL, resztę zastąp #, 11 cyfr jest w PESELu + var tab = [1,1,1,1,1,1,1,1,1,1,1]; + //wylosuj pozycję i zamień na 0 + var counter = 0; + while (counter < 7) { + var random = Math.floor(Math.random() * 11); + if (tab[random] == 1) { + tab[random] = 0; + counter += 1; + } + } + var peselToShow = ''; + for (var i = 0; i < 11; i++) { + if (tab[i] == 1) { + if(i == 10) + peselToShow += row.pesel[i]; + else + peselToShow += row.pesel[i] + ' '; + } else { + if(i == 10) + peselToShow += '█'; + else + peselToShow += '█ '; + } + } + if(!checkPesel(row.pesel)) + toReturn += ''; + else + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + }); + toReturn += '
NumerImięNazwiskoPESELGodzSalaZdaję nienaruszoną puszkę do rozliczeniaWeryfikacja
' + row.numerIdentyfikatora + '' + row.imie + '' + row.nazwisko + '' + peselToShow + '' + peselToShow + '



'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + loger(fs, 'Wyświetlono listę przyjść z nemerami pesel', 'warning'); + }); +}); +panelRouter.all('/logout', function(req, res) { + //sprawdź czy token istnieje i jest aktywny + var cookies = cookie.parse(req.headers.cookie || ''); + var token = cookies.token; + con.query('UPDATE tokeny SET aktywny = 0 WHERE token = ?', [token], function(err, result) { + if (err) throw err; + res.redirect('/login'); + loger(fs, 'Wylogowano użytkownika ' + req.user.kto, 'info'); + }); +}); +//rozlicz, pokaż w tabelce wszystkich wolontariuszy, którzy jeszcze są nie rozliczeni, posortuj według numeru identyfikatora, ostatnie pole dodaj rozliczenie +panelRouter.get('/rozlicz', function(req, res) { + var toReturn = headerHtml("Lista wolontariuszy do rozliczenia"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Rozlicz

'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1 AND id NOT IN (SELECT wolontariuszID FROM rozliczenie WHERE aktywne = 1) ORDER BY numerIdentyfikatora ASC', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + if(row.rodzic == "BRAK") + toReturn += ''; + else + toReturn += ''; + toReturn += ''; + toReturn += ''; + }); + toReturn += '
NumerImięNazwiskoTelefonTerminalRodzicOpcje
' + row.numerIdentyfikatora + '' + row.imie + '' + row.nazwisko + '' + telefon(row.telefon, 1) + '' + (row.terminal == 1 ? 'Tak' : 'Nie') + ' ' + row.rodzic + 'Rozlicz
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }); +}); +panelRouter.get('/rozliczWolontariusza', function(req, res) { + var idWolontariusza = req.query.id; + //pobierz dane wolontariusza, wyświetl formularz rozliczenia + var toReturn = headerHtml("Rozlicz wolontariusza"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Rozlicz wolontariusza

'; + toReturn += '
'; + con.query('SELECT * FROM wolontariusz WHERE id = ?', [idWolontariusza], function(err, result) { + if (err) throw err; + if (result.length > 0) { + toReturn += "

" + result[0].imie + " " + result[0].nazwisko + "

"; + toReturn += "

ID: " + result[0].numerIdentyfikatora + "

"; + toReturn += '

Suma: 0

'; + //pokaż formularz do wpisywania zebranej kwoaty i monet (sumę liczy program, użytkownik podaje ilość monet) + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //kwota z terminala + toReturn += ''; + //waluta obca, tekstarea + toReturn += ''; + //dary inne, tekstarea + toReturn += ''; + //uwagi, tekstarea + toReturn += ''; + //sala + toReturn += ''; + //liczący 1 + toReturn += ''; + //liczący 2 + toReturn += ''; + //liczący 3 + toReturn += ''; + toReturn += '
Suma
WalutaIlość
1 gr
2 gr
5 gr
10 gr
20 gr
50 gr
1 zł
2 zł
5 zł
10 zł
20 zł
50 zł
100 zł
200 zł
500 zł
Kwota z terminala
Waluta obca
Dary inne
Uwagi
Sala
Liczący 1
Liczący 2
Liczący 3
'; + toReturn += ''; + toReturn += '

Suma: 0

'; + toReturn += ''; + + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }); + }); + }); + } + }); +}); +panelRouter.post('/rozliczWolontariusza', function(req, res) { + //weryfikowal to numer id z tokenu + var idWolontariusza = req.query.id; + var idLiczacy1 = req.body.liczacy1; + var idLiczacy2 = req.body.liczacy2; + var idLiczacy3 = req.body.liczacy3 == 0 ? null : req.body.liczacy3; + var sala = req.body.sala; + var uwagi = req.body.uwagi; + var daryInne = req.body.daryInne; + var walutaObca = req.body.walutaObca; + var terminal = req.body.terminal > 0 ? 1 : 0; + var gr1 = req.body['1gr']; + var gr2 = req.body['2gr']; + var gr5 = req.body['5gr']; + var gr10 = req.body['10gr']; + var gr20 = req.body['20gr']; + var gr50 = req.body['50gr']; + var zl1 = req.body['1zl']; + var zl2 = req.body['2zl']; + var zl5 = req.body['5zl']; + var zl10 = req.body['10zl']; + var zl20 = req.body['20zl']; + var zl50 = req.body['50zl']; + var zl100 = req.body['100zl']; + var zl200 = req.body['200zl']; + var zl500 = req.body['500zl']; + var sumaZTerminala = req.body.terminal; + //zapisz dane do bazy + $sql = "INSERT INTO `rozliczenie` (`id`, `wolontariuszID`, `czasRozliczenia`, `terminal`, `sumaZTerminala`, `1gr`, `2gr`, `5gr`, `10gr`, `20gr`, `50gr`, `1zl`, `2zl`, `5zl`, `10zl`, `20zl`, `50zl`, `100zl`, `200zl`, `500zl`, `walutaObca`, `daryInne`, `uwagi`, `liczacy1`, `liczacy2`, `liczacy3`, `sala`, `weryfikowal`, `wpisaneDoBSS`, `ostatniaZmiana`, `aktywne`) "; + $sql += 'VALUES (NULL, ?, CURRENT_TIME(), ?, ?, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0,CURRENT_TIME(),1)'; + con.query($sql, [idWolontariusza, terminal, sumaZTerminala, gr1, gr2, gr5, gr10, gr20, gr50, zl1, zl2, zl5, zl10, zl20, zl50, zl100, zl200, zl500, walutaObca, daryInne, uwagi, idLiczacy1, idLiczacy2, idLiczacy3, sala, req.user.id], function(err, result) { + if (err) throw err; + if(process.env.DISCORD == "TAK") + { + con.query('SELECT * FROM wolontariusz WHERE id = ?', [idWolontariusza], function(err, result) { + if (err) throw err; + var suma = Number(gr1) +Number(gr2)*2 + Number(gr5)*5 + Number(gr10)*10 + Number(gr20)*20 + Number(gr50)*50 + Number(zl1)*100 + Number(zl2)*200 + Number(zl5)*500 + Number(zl10)*1000 + Number(zl20)*2000 + Number(zl50)*5000 + Number(zl100)*10000 + Number(zl200)*20000 + Number(zl500)*50000 + Number(sumaZTerminala)*100; + sendToDiscord(result[0].imie, result[0].nazwisko, suma/100, result[0].discord); + }); + } + if(process.env.SENDEMAILS == "TAK") + { + con.query('SELECT * FROM wolontariusz WHERE id = ?', [idWolontariusza], function(err, result) { + if (err) throw err; + var suma = Number(gr1) +Number(gr2)*2 + Number(gr5)*5 + Number(gr10)*10 + Number(gr20)*20 + Number(gr50)*50 + Number(zl1)*100 + Number(zl2)*200 + Number(zl5)*500 + Number(zl10)*1000 + Number(zl20)*2000 + Number(zl50)*5000 + Number(zl100)*10000 + Number(zl200)*20000 + Number(zl500)*50000 + Number(sumaZTerminala)*100; + sendEmail(result[0].imie, result[0].nazwisko, suma/100, result[0].email); + }); + } + res.redirect('/panel/rozliczenia#'+idWolontariusza); + loger(fs, 'Rozliczono wolontariusza o id: ' + idWolontariusza, 'info'); + }); +}); +panelRouter.all('/rozliczenia', function(req, res) { + var toReturn = headerHtml("Rozliczenia"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += ''; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1 ORDER BY numerIdentyfikatora ASC', function(err, result) { + if (err) throw err; + + const promises = []; + + result.forEach(function(row) { + const promise = new Promise((resolve, reject) => { + con.query('SELECT * FROM rozliczenie WHERE wolontariuszID = ? AND aktywne = 1 ORDER BY czasRozliczenia DESC', [row.id], function(err, result2) { + if (err) reject(err); + if (result2.length === 1) { + //istnieje + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + resolve(); + + } else if (result2.length === 0) { + resolve(); + } + }); + }); + + promises.push(promise); + }); + + Promise.all(promises) + .then(() => { + toReturn += '
NumerImięNazwiskoSumaKwota z terminala500zł200zł100zł50zł20zł10zł5zł2zł1zł50gr20gr10gr5gr2gr1grWaluta obcaDary inneUwagiSalaEdytuj
' + row.numerIdentyfikatora + '' + row.imie + '' + row.nazwisko + ''; + //policz sumę + var suma = 0; + suma += result2[0]['1gr']; + suma += result2[0]['2gr'] * 2; + suma += result2[0]['5gr'] * 5; + suma += result2[0]['10gr'] * 10; + suma += result2[0]['20gr'] * 20; + suma += result2[0]['50gr'] * 50; + suma += result2[0]['1zl'] * 100; + suma += result2[0]['2zl'] * 200; + suma += result2[0]['5zl'] * 500; + suma += result2[0]['10zl'] * 1000; + suma += result2[0]['20zl'] * 2000; + suma += result2[0]['50zl'] * 5000; + suma += result2[0]['100zl'] * 10000; + suma += result2[0]['200zl'] * 20000; + suma += result2[0]['500zl'] * 50000; + suma += result2[0].sumaZTerminala * 100; + toReturn += suma/100.0; + toReturn += '' + result2[0].sumaZTerminala + '' + result2[0]['500zl'] + '' + result2[0]['200zl'] + '' + result2[0]['100zl'] + '' + result2[0]['50zl'] + '' + result2[0]['20zl'] + '' + result2[0]['10zl'] + '' + result2[0]['5zl'] + '' + result2[0]['2zl'] + '' + result2[0]['1zl'] + '' + result2[0]['50gr'] + '' + result2[0]['20gr'] + '' + result2[0]['10gr'] + '' + result2[0]['5gr'] + '' + result2[0]['2gr'] + '' + result2[0]['1gr'] + '' + result2[0].walutaObca + '' + result2[0].daryInne + '' + result2[0].uwagi + '' + result2[0].sala + 'Edytuj'; + if(result2[0].weryfikowal == 0) + { + toReturn += ' | Weryfikuj'; + } + toReturn += '
'; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }) + .catch((error) => { + // Obsługa błędów zapytań + res.status(500).send('Wystąpił błąd podczas pobierania danych.'); + console.error(error); + }); + }); + +}); + + +panelRouter.get('/potwierdz', function(req, res) { + var idRozliczenia = req.query.id; + if(idRozliczenia != undefined || idRozliczenia != null) + { + //sprawdź czy istnieje rozliczenie + con.query('SELECT * FROM rozliczenie WHERE id = ?', [idRozliczenia], function(err, result) { + if (err) throw err; + if (result.length == 1) { + //istnieje + var toReturn = headerHtml("Potwierdź rozliczenie"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Potwierdź rozliczenie

'; + toReturn += '
'; + toReturn += '

' + result[0].wolontariuszID + '

'; + toReturn += '

' + result[0].czasRozliczenia + '

'; + toReturn += '

Suma: ' + (result[0]['1gr'] + result[0]['2gr'] * 2 + result[0]['5gr'] * 5 + result[0]['10gr'] * 10 + result[0]['20gr'] * 20 + result[0]['50gr'] * 50 + result[0]['1zl'] * 100 + result[0]['2zl'] * 200 + result[0]['5zl'] * 500 + result[0]['10zl'] * 1000 + result[0]['20zl'] * 2000 + result[0]['50zl'] * 5000 + result[0]['100zl'] * 10000 + result[0]['200zl'] * 20000 + result[0]['500zl'] * 50000 + result[0].sumaZTerminala)/100.0 + '

'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
NominałIlość
1 gr' + result[0]['1gr'] + '
2 gr' + result[0]['2gr'] + '
5 gr' + result[0]['5gr'] + '
10 gr' + result[0]['10gr'] + '
20 gr' + result[0]['20gr'] + '
50 gr' + result[0]['50gr'] + '
1 zł' + result[0]['1zl'] + '
2 zł' + result[0]['2zl'] + '
5 zł' + result[0]['5zl'] + '
10 zł' + result[0]['10zl'] + '
20 zł' + result[0]['20zl'] + '
50 zł' + result[0]['50zl'] + '
100 zł' + result[0]['100zl'] + '
200 zł' + result[0]['200zl'] + '
500 zł' + result[0]['500zl'] + '
Kwota z terminala' + result[0].sumaZTerminala + '
Waluta obca' + result[0].walutaObca + '
Dary inne' + result[0].daryInne + '
Uwagi' + result[0].uwagi + '
Sala' + result[0].sala + '
Liczący 1' + result[0].liczacy1 + '
Liczący 2' + result[0].liczacy2 + '
Liczący 3' + result[0].liczacy3 + '
'; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + } + }); + } + else + { + var toReturn = headerHtml("Rozliczenia"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += ''; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //pobierz osoby liczące + con.query('SELECT * FROM wolontariusz WHERE aktywny = 1 ORDER BY numerIdentyfikatora ASC', function(err, result) { + if (err) throw err; + + const promises = []; + + result.forEach(function(row) { + const promise = new Promise((resolve, reject) => { + con.query('SELECT * FROM rozliczenie WHERE wolontariuszID = ? AND aktywne = 1 AND weryfikowal = 0 ORDER BY czasRozliczenia DESC', [row.id], function(err, result2) { + if (err) reject(err); + if (result2.length === 1) { + //istnieje + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + resolve(); + + } else if (result2.length === 0) { + resolve(); + } + }); + }); + + promises.push(promise); + }); + + Promise.all(promises) + .then(() => { + toReturn += '
NumerImięNazwiskoSumaKwota z terminalaWaluta obcaDary inneUwagiSalaEdytuj
' + row.numerIdentyfikatora + '' + row.imie + '' + row.nazwisko + ''; + //policz sumę + var suma = 0; + suma += result2[0]['1gr']; + suma += result2[0]['2gr'] * 2; + suma += result2[0]['5gr'] * 5; + suma += result2[0]['10gr'] * 10; + suma += result2[0]['20gr'] * 20; + suma += result2[0]['50gr'] * 50; + suma += result2[0]['1zl'] * 100; + suma += result2[0]['2zl'] * 200; + suma += result2[0]['5zl'] * 500; + suma += result2[0]['10zl'] * 1000; + suma += result2[0]['20zl'] * 2000; + suma += result2[0]['50zl'] * 5000; + suma += result2[0]['100zl'] * 10000; + suma += result2[0]['200zl'] * 20000; + suma += result2[0]['500zl'] * 50000; + suma += result2[0].sumaZTerminala * 100; + toReturn += suma/100.0; + toReturn += '' + result2[0].sumaZTerminala + '' + result2[0].walutaObca + '' + result2[0].daryInne + '' + result2[0].uwagi + '' + result2[0].sala + 'Edytuj'; + if(result2[0].weryfikowal == 0) + { + toReturn += ' | Weryfikuj'; + } + toReturn += '
'; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }) + .catch((error) => { + // Obsługa błędów zapytań + res.status(500).send('Wystąpił błąd podczas pobierania danych.'); + console.error(error); + }); + }); + } +}); + +panelRouter.post('/potwierdz', function(req, res) { + //weryfikowal to numer id z tokenu + var idRozliczenia = req.query.id; + //zapisz dane do bazy + $sql = "UPDATE `rozliczenie` SET `weryfikowal` = ? WHERE `rozliczenie`.`id` = ?"; + con.query($sql, [req.user.id, idRozliczenia], function(err, result) { + if (err) throw err; + res.redirect('/panel/rozliczenia'); + loger(fs, 'Potwierdzono rozliczenie o id: ' + idRozliczenia, 'info'); + }); +}); + + +panelRouter.get('/edytujRozliczenie', function(req, res) { + var idRozliczenia = req.query.id; + //sprawdź czy istnieje rozliczenie + con.query('SELECT * FROM rozliczenie WHERE id = ?', [idRozliczenia], function(err, result) { + if (err) throw err; + if (result.length == 1) { + //istnieje + var toReturn = headerHtml("Edytuj rozliczenie"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Edytuj rozliczenie

'; + toReturn += '
'; + toReturn += '

' + result[0].wolontariuszID + '

'; + toReturn += '

' + result[0].czasRozliczenia + '

'; + toReturn += '

Suma: 0

'; + //pokaż formularz do wpisywania zebranej kwoaty i monet (sumę liczy program, użytkownik podaje ilość monet) + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + //kwota z terminala + toReturn += ''; + //waluta obca, tekstarea + toReturn += ''; + //dary inne, tekstarea + toReturn += ''; + //uwagi, tekstarea + toReturn += ''; + //sala + toReturn += ''; + //liczący 1 + toReturn += ''; + //liczący 2 + toReturn += ''; + //liczący 3 + toReturn += ''; + toReturn += '
Suma
WalutaIlość
1 gr
2 gr
5 gr
10 gr
20 gr
50 gr
1 zł
2 zł
5 zł
10 zł
20 zł
50 zł
100 zł
200 zł
500 zł
Kwota z terminala
Waluta obca
Dary inne
Uwagi
Sala
Liczący 1
Liczący 2
Liczący 3
'; + toReturn += ''; + toReturn += '

Suma: 0

'; + toReturn += ''; + + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }); + }); + }); + } + else + { + //nie istnieje + res.redirect('/rozliczenia'); + } + }); +}); +panelRouter.post('/edytujRozliczenie', function(req, res) { + var idRozliczenia = req.query.id; + var idLiczacy1 = req.body.liczacy1; + var idLiczacy2 = req.body.liczacy2; + var idLiczacy3 = req.body.liczacy3 == 0 ? null : req.body.liczacy3; + var sala = req.body.sala; + var uwagi = req.body.uwagi; + var daryInne = req.body.daryInne; + var walutaObca = req.body.walutaObca; + var terminal = req.body.terminal > 0 ? 1 : 0; + var gr1 = req.body['1gr']; + var gr2 = req.body['2gr']; + var gr5 = req.body['5gr']; + var gr10 = req.body['10gr']; + var gr20 = req.body['20gr']; + var gr50 = req.body['50gr']; + var zl1 = req.body['1zl']; + var zl2 = req.body['2zl']; + var zl5 = req.body['5zl']; + var zl10 = req.body['10zl']; + var zl20 = req.body['20zl']; + var zl50 = req.body['50zl']; + var zl100 = req.body['100zl']; + var zl200 = req.body['200zl']; + var zl500 = req.body['500zl']; + var sumaZTerminala = req.body.terminal; + //zapisz dane do bazy + $sql = "UPDATE `rozliczenie` SET `terminal` = ?, `sumaZTerminala` = ?, `1gr` = ?, `2gr` = ?, `5gr` = ?, `10gr` = ?, `20gr` = ?, `50gr` = ?, `1zl` = ?, `2zl` = ?, `5zl` = ?, `10zl` = ?, `20zl` = ?, `50zl` = ?, `100zl` = ?, `200zl` = ?, `500zl` = ?, `walutaObca` = ?, `daryInne` = ?, `uwagi` = ?, `liczacy1` = ?, `liczacy2` = ?, `liczacy3` = ?, `sala` = ?, `weryfikowal` = ?, `ostatniaZmiana` = CURRENT_TIME() WHERE `rozliczenie`.`id` = ?"; + con.query($sql, [terminal, sumaZTerminala, gr1, gr2, gr5, gr10, gr20, gr50, zl1, zl2, zl5, zl10, zl20, zl50, zl100, zl200, zl500, walutaObca, daryInne, uwagi, idLiczacy1, idLiczacy2, idLiczacy3, sala, req.user.id, idRozliczenia], function(err, result) { + if (err) throw err; + res.redirect('/panel/rozliczenia#'+idRozliczenia); + loger(fs, 'Edytowano rozliczenie o id: ' + idRozliczenia, 'info'); + }); +}); +panelRouter.all('/statystyki', function(req, res) { + var toReturn = headerHtml("Statystyki"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '
'; + toReturn += '

Całkowita suma

'; + toReturn += ''; + //wypisz sumę zebranych pieniędzy, sumę poszczególnych nominałów + //pobierz wszystkie rozliczenia + con.query('SELECT * FROM rozliczenie WHERE aktywne = 1', function(err, result) { + if (err) throw err; + var suma = 0; + var sumaTerminal = 0; + var suma1gr = 0; + var suma2gr = 0; + var suma5gr = 0; + var suma10gr = 0; + var suma20gr = 0; + var suma50gr = 0; + var suma1zl = 0; + var suma2zl = 0; + var suma5zl = 0; + var suma10zl = 0; + var suma20zl = 0; + var suma50zl = 0; + var suma100zl = 0; + var suma200zl = 0; + var suma500zl = 0; + result.forEach(function(row) { + suma += row['1gr'] + row['2gr'] * 2 + row['5gr'] * 5 + row['10gr'] * 10 + row['20gr'] * 20 + row['50gr'] * 50 + row['1zl'] * 100 + row['2zl'] * 200 + row['5zl'] * 500 + row['10zl'] * 1000 + row['20zl'] * 2000 + row['50zl'] * 5000 + row['100zl'] * 10000 + row['200zl'] * 20000 + row['500zl'] * 50000; + sumaTerminal += row.sumaZTerminala; + suma1gr += row['1gr']; + suma2gr += row['2gr']; + suma5gr += row['5gr']; + suma10gr += row['10gr']; + suma20gr += row['20gr']; + suma50gr += row['50gr']; + suma1zl += row['1zl']; + suma2zl += row['2zl']; + suma5zl += row['5zl']; + suma10zl += row['10zl']; + suma20zl += row['20zl']; + suma50zl += row['50zl']; + suma100zl += row['100zl']; + suma200zl += row['200zl']; + suma500zl += row['500zl']; + }); + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Suma' + suma/100.0 + ' zł
Suma z terminali' + sumaTerminal + ' zł
1 gr' + suma1gr + '
2 gr' + suma2gr + '
5 gr' + suma5gr + '
10 gr' + suma10gr + '
20 gr' + suma20gr + '
50 gr' + suma50gr + '
1 zł' + suma1zl + '
2 zł' + suma2zl + '
5 zł' + suma5zl + '
10 zł' + suma10zl + '
20 zł' + suma20zl + '
50 zł' + suma50zl + '
100 zł' + suma100zl + '
200 zł' + suma200zl + '
500 zł' + suma500zl + '
'; + toReturn += '
'; + toReturn += '
'; + toReturn += '

Top 10 wolontariuszy

'; + //SELECT * FROM `SumaZebranaPrzezWolontariuszy` ORDER BY `SumaZebranaPrzezWolontariuszy`.`suma` ASC LIMIT 10; + toReturn += ''; + toReturn += ''; + con.query('SELECT numerIdentyfikatora, imie, nazwisko, suma FROM `SumaZebranaPrzezWolontariuszy` ORDER BY `SumaZebranaPrzezWolontariuszy`.`suma` DESC LIMIT 10;', function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
WolontariuszImię i nazwiskoSuma
' + row.numerIdentyfikatora + '' + row.imie + ' ' + row.nazwisko + '' + row.suma + '
'; + toReturn += '
'; + toReturn += '
'; + //który liczący najwięcej liczył + toReturn += '

Najwięcej puszek przeliczonych

'; + toReturn += ''; + toReturn += ''; + con.query("SELECT idLiczacego, imie, nazwisko, sumaPrzeliczona FROM `sumaPrzeliczona` ORDER BY `sumaPrzeliczona`.`sumaPrzeliczona` DESC LIMIT 10;", function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
LiczącyImię i nazwiskoSuma
' + row.idLiczacego + '' + row.imie + ' ' + row.nazwisko + '' + row.sumaPrzeliczona + '
'; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }) + + }); + }); +}); +panelRouter.get("/haslo", function(req, res) { + var toReturn = headerHtml("Zmiana hasła"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Zmiana hasła

'; + if (req.query.success) { + toReturn += '

Hasło zostało zmienione

'; + } else if (req.query.error) { + toReturn += '

Stare hasło jest niepoprawne

'; + } + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Stare hasło
Nowe hasło
Powtórz nowe hasło
'; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); +panelRouter.post("/haslo", function(req, res) { + //console.log(req.body); + var stareHaslo = req.body.stareHaslo; + var noweHaslo = req.body.noweHaslo; + var powtorzHaslo = req.body.powtorzHaslo; + //sprawdź czy stare hasło jest poprawne, tabelka loginy, kolumna haslo + con.query("SELECT * FROM `login` WHERE `id` = ? AND `haslo` = SHA1(?)", [req.user.id, stareHaslo], function(err, result) { + if (err) throw err; + if (result.length == 1) { + //stare hasło jest poprawne + if (noweHaslo == powtorzHaslo) { + //hasła są takie same + //zmień hasło + con.query("UPDATE `login` SET `haslo` = SHA1(?) WHERE `login`.`id` = ?", [noweHaslo, req.user.id], function(err, result) { + if (err) throw err; + res.redirect("/panel/haslo?success"); + }); + } else { + //hasła nie są takie same + res.redirect("/panel/haslo?error"); + } + } else { + //stare hasło jest niepoprawne + res.redirect("/panel/haslo?error"); + } + + }); +}); +///nowyAdmin +panelRouter.get("/nowyAdmin", function(req, res) { + var toReturn = headerHtml("Dodaj administratora"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Dodaj administratora

'; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Imię
Nazwisko
Login
Hasło
'; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); +panelRouter.post("/nowyAdmin", function(req, res) { + //sprawdź czy login jest wolny + var imie = req.body.imie; + var nazwisko = req.body.nazwisko; + var login = req.body.login; + var haslo = req.body.haslo; + con.query("SELECT * FROM `login` WHERE `login` = ?", [login], function(err, result) { + if (err) throw err; + if (result.length == 0) { + //login jest wolny + //dodaj admina + con.query("INSERT INTO `login` (`id`, `login`, `haslo`, `kto`, `aktywne`) VALUES (NULL, ?, SHA1(?), ?, 1)", [login, haslo, imie + " " + nazwisko], function(err, result) { + if (err) throw err; + res.redirect("/panel/nowyAdmin?success"); + }); + } else { + //login jest zajęty + res.redirect("/panel/nowyAdmin?error"); + } + }); +}); +//usunAdmin +panelRouter.get("/usunAdmin", function(req, res) { + var toReturn = headerHtml("Usuń administratora"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Usuń administratora

'; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + toReturn += '
Login
'; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); +panelRouter.post("/usunAdmin", function(req, res) { + //sprawdź czy login jest wolny + var login = req.body.login; + con.query("SELECT * FROM `login` WHERE `login` = ?", [login], function(err, result) { + if (err) throw err; + if (result.length == 1) { + //login jest zajęty + //usuń admina + con.query("UPDATE `login` SET `aktywne` = 0 WHERE `login`.`login` = ?", [login], function(err, result) { + if (err) throw err; + res.redirect("/panel/usunAdmin?success"); + }); + } else { + //login jest wolny + res.redirect("/panel/usunAdmin?error"); + } + }); +}); +//listaAdminow +panelRouter.get("/listaAdminow", function(req, res) { + var toReturn = headerHtml("Lista administratorów"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Lista administratorów

'; + toReturn += ''; + toReturn += ''; + con.query("SELECT * FROM `login` WHERE `aktywne` = 1", function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
Imię i nazwiskoLogin
' + row.kto + '' + row.login + '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); + }); +}); +//uniewaznijTokeny +panelRouter.all("/uniewaznijTokeny", function(req, res) { + //ustaw wszystkie tokeny na aktywne = 0 i przejdź do /panel/logout + con.query("UPDATE `tokeny` SET `aktywny` = 0 WHERE `tokeny`.`aktywny` = 1", function(err, result) { + if (err) throw err; + res.redirect("/panel/logout"); + }); +}); +//to będzie na szybkie informacje jako iframe +panelRouter.get("/szybkieInfo", function(req, res) { + var toReturn = ''; + toReturn += '
'; + toReturn += '

Szybkie informacje

'; + //sprawdź ile jest wolontariuszy oraz ilu jest rozliczonych + var iloscWolontariuszy = 0; + var iloscRozliczonych = 0; + var iloscLiczących = 0; + con.query("SELECT * FROM `wolontariusz` WHERE `aktywny` = 1", function(err, result) { + if (err) throw err; + iloscWolontariuszy = result.length; + con.query("SELECT * FROM `rozliczenie` WHERE `aktywne` = 1", function(err, result) { + if (err) throw err; + iloscRozliczonych = result.length; + con.query("SELECT * FROM `liczacy` WHERE `aktywne` = 1", function(err, result) { + if (err) throw err; + iloscLiczących = result.length; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += ''; + toReturn += '
Ilość wolontariuszy' + iloscWolontariuszy + '
Ilość rozliczonych' + iloscRozliczonych + '
Ilość liczących' + iloscLiczących + '
'; + //ostatni rozliczony + toReturn += '

Ostatnio rozliczeni

'; + toReturn += ''; + toReturn += ''; + con.query("SELECT * FROM `rozliczenie`, `wolontariusz` WHERE `rozliczenie`.`wolontariuszID` = `wolontariusz`.`id` AND `rozliczenie`.`aktywne` = 1 ORDER BY `rozliczenie`.`czasRozliczenia` DESC LIMIT 5", function(err, result) { + if (err) throw err; + result.forEach(function(row) { + toReturn += ''; + }); + toReturn += '
WolontariuszSuma
' + row.numerIdentyfikatora + '' + (row['1gr'] + row['2gr'] * 2 + row['5gr'] * 5 + row['10gr'] * 10 + row['20gr'] * 20 + row['50gr'] * 50 + row['1zl'] * 100 + row['2zl'] * 200 + row['5zl'] * 500 + row['10zl'] * 1000 + row['20zl'] * 2000 + row['50zl'] * 5000 + row['100zl'] * 10000 + row['200zl'] * 20000 + row['500zl'] * 50000 + row.sumaZTerminala * 100)/100.0 + ' zł
'; + toReturn += '
'; + res.send(toReturn); + }); + }); + }); + }); +}); + +panelRouter.get('/sprawdzenieWysylki', function(req, res) { + var toReturn = headerHtml("Sprawdzenie wysyłki"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Sprawdzenie wysyłki powiadomien

'; + + if(process.env.SENDEMAILS == 'TAK' || process.env.DISCORD == 'TAK') + { + toReturn += '
'; + toReturn += ''; + if(process.env.SENDEMAILS == 'TAK') + toReturn += ''; + toReturn += '
Email
'; + toReturn += ''; + toReturn += '
'; + } + else + { + toReturn += 'Funkcja wyłączona'; + } + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); + +panelRouter.post('/sprawdzenieWysylki', function(req, res) { + if(process.env.SENDEMAILS == 'TAK') + checkSendEmail(req.body.email) + if(process.env.DISCORD == 'TAK') + sendToDiscord("Test", "Test", 500, "BRAK") + var toReturn = headerHtml("Sprawdzenie wysyłki"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Sprawdzenie wysyłki email

'; + toReturn += 'Powiadomienia wysłane'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); + +//przyjmij plik csv lub sql +panelRouter.get('/import', function(req, res) { + var toReturn = headerHtml("Import danych"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Import danych

'; + toReturn += '

Aby zrobić kopię zapasową, kliknij tutaj

'; + toReturn += '
'; + toReturn += ''; + toReturn += ''; + //toReturn += ''; + toReturn += '
Plik
Typ
'; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); + + + +panelRouter.post('/import', function(req, res) { + if (!req.files || !req.files.plik) { + return res.status(400).send("Nie przesłano pliku!"); + } + + const file = req.files.plik; + + // Jeżeli plik CSV, czy .csv + if (file.name.endsWith('.csv')) + { + const csv = require('csv-parser'); + const fs = require('fs'); + const stripBom = require('strip-bom-stream'); + const results = []; + + fs.createReadStream(file.tempFilePath) + .pipe(stripBom()) + .pipe(csv({ separator: ';' })) + .on('data', (data) => { + console.log(Object.keys(data)); // Debugowanie kluczy z CSV + + console.log("Surowe dane:", data); // Debugowanie surowych danych z CSV + const pesel = data.pesel ? data.pesel.trim() : "BRAK"; // Usuwanie spacji i walidacja + console.log("PESEL po walidacji:", pesel); // Debugowanie PESEL + + if (data.id_number && data.full_name && data.email && data.phone) { + results.push({ + id_number: data.id_number, + firstName: data.full_name.split(" ")[0] || "BRAK", + lastName: data.full_name.split(" ")[1] || "BRAK", + email: data.email, + phone: data.phone.replace(/\s|-/g, ""), + pesel: pesel, + guardian: data.guardian || "BRAK", + }); + } + }) + + .on('end', () => { + results.forEach(row => { + const query = "INSERT INTO `wolontariusz` (`id`, `numerIdentyfikatora`, `imie`, `nazwisko`, `discord`, `email`, `telefon`, `pesel`, `rodzic`, `terminal`) VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, 0)"; + con.query(query, [ + row.id_number, + row.firstName, + row.lastName, + "BRAK", + row.email, + row.phone, + row.pesel, + row.guardian + ], (err) => { + if (err) console.error("Błąd przy dodawaniu danych: ", err); + }); + }); + console.log(results); + + res.redirect('/panel/listaWolontariuszy'); + }) + .on('error', (err) => { + console.error("Błąd odczytu pliku CSV: ", err); + res.status(500).send("Wystąpił błąd podczas importu CSV."); + }); + } + // Jeżeli plik SQL + /*else if (file.name.endsWith('.sql')) { + const fs = require('fs'); + fs.readFile(file.tempFilePath, 'utf8', (err, data) => { + if (err) { + console.error("Błąd odczytu pliku SQL: ", err); + return res.status(500).send("Wystąpił błąd podczas odczytu pliku SQL."); + } + + + con.query(data, (err) => { + if (err) { + console.error("Błąd wykonania pliku SQL: ", err); + return res.status(500).send("Wystąpił błąd podczas wykonywania pliku SQL."); + } + res.redirect('/panel/listaWolontariuszy'); + }); + + }); + } */ + else { + res.status(400).send("Nieobsługiwany typ pliku. Wybierz plik CSV lub SQL."); + } +}); + +//eksport, wybierz całą bazę (strukturę i dane) do pliku sql +panelRouter.get('/eksport', function(req, res) { + var toReturn = headerHtml("Eksport danych"); + toReturn += menuHtml(1); + toReturn += '
'; + toReturn += '

Eksport danych

'; + toReturn += '

Służy tylko do kopii zapasowej, aby przywrócić wymaga oprogramowania do zarządzania bazą danych

'; + toReturn += '
'; + toReturn += ''; + toReturn += '
'; + toReturn += '
'; + toReturn += footerHtml(1); + res.send(toReturn); +}); + +panelRouter.post('/eksport', async function(req, res) { + + + var mysql2 = require('mysql2/promise'); + var con2 = await mysql2.createConnection({ + host: process.env.MYSQLHOST, + user: process.env.MYSQLUSER, + password: process.env.MYSQLPASS, + port : process.env.MYSQLPORT, + database: process.env.MYSQLDB, + insecureAuth : true + }); + + var plikSQL = baza(); + + /*const tables = [ + 'liczacy', + 'login', + 'rozliczenie', + 'tokeny', + 'tokenyLiczacy', + 'wolontariusz' + ]*/try { + const tables = ['liczacy', 'login', 'rozliczenie', 'tokeny', 'tokenyLiczacy', 'wolontariusz']; + let dane = []; + + for (const table of tables) { + const [rows] = await con2.query(`SELECT * FROM ${table}`); + rows.forEach(row => { + const values = Object.values(row).map(value => { + if (value instanceof Date) { + // Konwersja daty na format SQL + return `'${value.toISOString().slice(0, 19).replace('T', ' ')}'`; + } else if (typeof value === 'string') { + // Escapowanie apostrofów w stringach + return `'${value.replace(/'/g, "''")}'`; + } + return value; + }); + const columns = Object.keys(row).join(', '); + dane.push(`INSERT INTO ${table} (${columns}) VALUES (${values.join(', ')});`); + }); + } + + + console.log(dane); + plikSQL += dane.join('\n'); + //make for download + fs.writeFileSync('eksport/export.sql', plikSQL); + res.download('eksport/export.sql'); + } catch (err) { + console.error('Błąd podczas eksportowania danych:', err); + res.status(500).send('Wystąpił błąd podczas eksportowania danych.'); + } finally { + await con2.end(); + } +}); + +panelRouter.all('/usunWolontariuszy', function(req, res) { + //turncate wolontariusz + con.query("TRUNCATE TABLE `wolontariusz`", function(err, result) { + if (err) throw err; + res.redirect('/panel/listaWolontariuszy'); + }); +}) + +module.exports = panelRouter; \ No newline at end of file diff --git a/serwer/app/static/logo.png b/serwer/app/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4f4f9fbc0da335c91e849a933d229302031b8329 GIT binary patch literal 122100 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 BstHIc5uJNwfsPia%A5*)__Vw^A`EX1@JUm;$7$ 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 PMZi$cOt<9bgUJ5_{|0Tc literal 0 HcmV?d00001 diff --git a/serwer/app/tmp/none b/serwer/app/tmp/none new file mode 100644 index 0000000..e69de29 diff --git a/serwer/docker-compose.yml b/serwer/docker-compose.yml new file mode 100644 index 0000000..d363059 --- /dev/null +++ b/serwer/docker-compose.yml @@ -0,0 +1,29 @@ +version: '3.8' +services: + app: + build: + context: . + dockerfile: Dockerfile + ports: + - "${APP_PORT}:${APP_PORT}" + volumes: + - ./prod.env:/usr/src/app/.env + - ./logs:/usr/src/app/logs + - ./tmp:/usr/src/app/tmp + depends_on: + - db + restart: always + + db: + build: + context: . + dockerfile: Dockerfile.db + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + ports: + - "${DB_PORT}:3306" + volumes: + - ./app/baza-import.sql:/docker-entrypoint-initdb.d/baza.sql diff --git a/serwer/logs/none b/serwer/logs/none new file mode 100644 index 0000000..e69de29 diff --git a/serwer/tmp/none b/serwer/tmp/none new file mode 100644 index 0000000..e69de29 diff --git a/windows.bat b/windows.bat new file mode 100644 index 0000000..1edee9c --- /dev/null +++ b/windows.bat @@ -0,0 +1,56 @@ +@echo off +cd serwer + +REM Funkcja do sprawdzania dostępności polecenia +:command_exists +where %1 >nul 2>nul +if %ERRORLEVEL% EQU 0 ( + exit /b 0 +) else ( + exit /b 1 +) + +REM Sprawdzenie systemu operacyjnego +if "%OS%"=="Windows_NT" ( + echo Wykryto system Windows. + set WINDOWS=true +) else ( + echo Wykryto system Unix/MacOS. + set WINDOWS=false +) + +REM Sprawdź czy jest dostępne `docker compose` +call :command_exists docker +if %ERRORLEVEL% EQU 0 ( + docker compose version >nul 2>nul + if %ERRORLEVEL% EQU 0 ( + echo Docker Compose dostępny jako 'docker compose'. + set COMPOSE_COMMAND=docker compose + ) else ( + call :command_exists docker-compose + if %ERRORLEVEL% EQU 0 ( + echo Docker Compose dostępny jako 'docker-compose'. + set COMPOSE_COMMAND=docker-compose + ) else ( + echo Docker Compose nie jest zainstalowany. Zainstaluj go przed uruchomieniem tego skryptu. + exit /b 1 + ) + ) +) else ( + echo Docker Compose nie jest zainstalowany. Zainstaluj go przed uruchomieniem tego skryptu. + exit /b 1 +) + +REM Uruchomienie docker compose up +if "%WINDOWS%"=="true" ( + %COMPOSE_COMMAND% up --build -d +) else ( + %COMPOSE_COMMAND% up --build -d +) + +if %ERRORLEVEL% EQU 0 ( + echo Docker Compose został uruchomiony pomyślnie. +) else ( + echo Wystąpił błąd podczas uruchamiania Docker Compose. + exit /b 1 +) \ No newline at end of file