add huuuge stuff
This commit is contained in:
parent
0ddfa4d66f
commit
6aa6955fa8
@ -16,13 +16,16 @@
|
|||||||
"@types/debug": "^4.1.7",
|
"@types/debug": "^4.1.7",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
"@types/express-ws": "^3.0.1",
|
"@types/express-ws": "^3.0.1",
|
||||||
|
"@types/mongoose": "^5.11.97",
|
||||||
"@types/morgan": "^1.9.3",
|
"@types/morgan": "^1.9.3",
|
||||||
"@types/node": "^18.11.9",
|
"@types/node": "^18.11.9",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"express": "~4.16.1",
|
"express": "~4.16.1",
|
||||||
"express-ws": "^5.0.2",
|
"express-ws": "^5.0.2",
|
||||||
|
"hc-sr04": "^0.0.1",
|
||||||
"http-errors": "~1.6.3",
|
"http-errors": "~1.6.3",
|
||||||
|
"mongoose": "^6.7.2",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"pug": "2.0.0-beta11"
|
"pug": "2.0.0-beta11"
|
||||||
},
|
},
|
||||||
|
85
public/stylesheets/buttons.css
Normal file
85
public/stylesheets/buttons.css
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
.btn {
|
||||||
|
padding: 11px 15px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn:disabled {
|
||||||
|
background-color: var(--disabled) !important;
|
||||||
|
color: #FFFFFF;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: var(--hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: var(--primary-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: var(--secondary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background-color: var(--secondary-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background-color: var(--success);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-success:hover {
|
||||||
|
background-color: var(--success-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-warning {
|
||||||
|
background-color: var(--warning);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-warning:hover {
|
||||||
|
background-color: var(--warning-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
background-color: var(--danger);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-danger:hover {
|
||||||
|
background-color: var(--danger-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-dark {
|
||||||
|
color: white;
|
||||||
|
background-color: var(--dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-dark:hover {
|
||||||
|
background-color: var(--dark-bright);
|
||||||
|
}
|
24
public/stylesheets/colors.css
Normal file
24
public/stylesheets/colors.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
:root {
|
||||||
|
--primary-dark: #1E66B2;
|
||||||
|
--primary: #0086FD;
|
||||||
|
--primary-bright: #409DFC;
|
||||||
|
|
||||||
|
--secondary: #555555;
|
||||||
|
--secondary-bright: #A3A3A3;
|
||||||
|
|
||||||
|
--hover: grey;
|
||||||
|
|
||||||
|
--disabled: #A4A4A4;
|
||||||
|
|
||||||
|
--success: #31B600;
|
||||||
|
--success-bright: #6DC946;
|
||||||
|
|
||||||
|
--warning: #ED6D05;
|
||||||
|
--warning-bright: #E28433;
|
||||||
|
|
||||||
|
--danger: #FF1818;
|
||||||
|
--danger-bright: #DC4040;
|
||||||
|
|
||||||
|
--dark: #911010;
|
||||||
|
--dark-bright: #A22F2F;
|
||||||
|
}
|
119
public/stylesheets/fonts.css
Normal file
119
public/stylesheets/fonts.css
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/* roboto-100 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-100.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-100.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-100italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 100;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-100italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-100italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-300 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-300italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-300italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-300italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-regular - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-500 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-500.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-500.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-500italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 500;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-500italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-500italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-700 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-700italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-700italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-900 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-900.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-900.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roboto-900italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 900;
|
||||||
|
src: local(''),
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-900italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
|
url('https://fonts.gaminggeneration.de/Roboto/roboto-v30-latin-900italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* The Modal (background) */
|
/* The Modal (background) */
|
||||||
.modal {
|
.modal {
|
||||||
display: none; /* Hidden by default */
|
display: none; /* Hidden by default */
|
||||||
@ -15,6 +14,7 @@
|
|||||||
background-color: rgba(0, 0, 0, 0.7); /* Black w/ opacity */
|
background-color: rgba(0, 0, 0, 0.7); /* Black w/ opacity */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Modal Content */
|
/* Modal Content */
|
||||||
.modal-content {
|
.modal-content {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
@ -23,15 +23,22 @@
|
|||||||
border: 1px solid #888888;
|
border: 1px solid #888888;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: #fefefe;
|
background-color: #FEFEFE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#modalInnerContent {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#modalTitle {
|
#modalTitle {
|
||||||
font-size: 1.9em;
|
font-size: 1.9em;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes modalBlendOut {
|
@keyframes modalBlendOut {
|
||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -41,6 +48,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes modalBlendIn {
|
@keyframes modalBlendIn {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@ -50,25 +58,88 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.modalBlendIn {
|
.modalBlendIn {
|
||||||
animation: modalBlendIn 0.5s ease;
|
animation: modalBlendIn 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.modalBlendOut {
|
.modalBlendOut {
|
||||||
animation: modalBlendOut 0.8s ease;
|
animation: modalBlendOut 0.8s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* The Close Button */
|
/* The Close Button */
|
||||||
.close {
|
.close {
|
||||||
color: #aaaaaa;
|
color: #AAAAAA;
|
||||||
float: right;
|
float: right;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.close:hover,
|
.close:hover,
|
||||||
.close:focus {
|
.close:focus {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.lds-ellipsis {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
left: 44.9%;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
.lds-ellipsis div {
|
||||||
|
position: absolute;
|
||||||
|
top: 27px;
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #00303F;
|
||||||
|
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||||
|
}
|
||||||
|
.lds-ellipsis div:nth-child(1) {
|
||||||
|
left: 6px;
|
||||||
|
animation: lds-ellipsis1 0.6s infinite;
|
||||||
|
}
|
||||||
|
.lds-ellipsis div:nth-child(2) {
|
||||||
|
left: 6px;
|
||||||
|
animation: lds-ellipsis2 0.6s infinite;
|
||||||
|
}
|
||||||
|
.lds-ellipsis div:nth-child(3) {
|
||||||
|
left: 26px;
|
||||||
|
animation: lds-ellipsis2 0.6s infinite;
|
||||||
|
}
|
||||||
|
.lds-ellipsis div:nth-child(4) {
|
||||||
|
left: 45px;
|
||||||
|
animation: lds-ellipsis3 0.6s infinite;
|
||||||
|
}
|
||||||
|
@keyframes lds-ellipsis1 {
|
||||||
|
0% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes lds-ellipsis3 {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes lds-ellipsis2 {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(19px, 0);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
@import url("https://fonts.gaminggeneration.de/Roboto.css");
|
@import url("/stylesheets/colors.css");
|
||||||
|
@import url("/stylesheets/fonts.css");
|
||||||
|
@import url("/stylesheets/buttons.css");
|
||||||
|
@import url("/stylesheets/modal.css");
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: white;
|
color: white;
|
||||||
|
6
src/Category.ts
Normal file
6
src/Category.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export enum Category {
|
||||||
|
ALCOHOLIC,
|
||||||
|
ALCOHOL_FREE,
|
||||||
|
SYRUP,
|
||||||
|
JUICE
|
||||||
|
}
|
15
src/Utils.ts
Normal file
15
src/Utils.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import * as dns from "dns";
|
||||||
|
|
||||||
|
export class Utils {
|
||||||
|
public static checkInternet(): Promise<boolean> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
dns.resolve('gaminggeneration.de', (err) => {
|
||||||
|
if (err)
|
||||||
|
resolve(false);
|
||||||
|
else
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
7
src/WebSocketEvent.ts
Normal file
7
src/WebSocketEvent.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export enum WebSocketEvent {
|
||||||
|
STATUS_STARTING,
|
||||||
|
STATUS_READY,
|
||||||
|
STATUS_FILLING,
|
||||||
|
STATUS_REFRESHING,
|
||||||
|
STATUS_ERROR
|
||||||
|
}
|
52
src/WebSocketPayload.ts
Normal file
52
src/WebSocketPayload.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {WebSocketEvent} from "./WebSocketEvent";
|
||||||
|
|
||||||
|
export class WebSocketPayload {
|
||||||
|
set event(value: WebSocketEvent) {
|
||||||
|
this._event = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set status(value: boolean) {
|
||||||
|
this._status = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set data(value: object | undefined) {
|
||||||
|
this._data = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _event: WebSocketEvent;
|
||||||
|
private _status: boolean;
|
||||||
|
private _data: object | undefined;
|
||||||
|
|
||||||
|
|
||||||
|
get event(): WebSocketEvent {
|
||||||
|
return this._event;
|
||||||
|
}
|
||||||
|
|
||||||
|
get status(): boolean {
|
||||||
|
return this._status;
|
||||||
|
}
|
||||||
|
|
||||||
|
get data(): object | undefined {
|
||||||
|
return this._data;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(event: WebSocketEvent, status: boolean, data?: object) {
|
||||||
|
this._event = event;
|
||||||
|
this._status = status;
|
||||||
|
this._data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static parseFromJSON(json: string): WebSocketPayload | null {
|
||||||
|
json = (window) ? atob(json) : Buffer.from(json, "base64").toString();
|
||||||
|
let rawPayload: { event: string, status: boolean, data: object };
|
||||||
|
try {
|
||||||
|
rawPayload = JSON.parse(json);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let wsEvent = <keyof typeof WebSocketEvent> rawPayload.event;
|
||||||
|
|
||||||
|
return new WebSocketPayload(wsEvent, rawPayload.status, rawPayload.data);
|
||||||
|
}
|
||||||
|
}
|
@ -33,13 +33,12 @@ export class WebsocketApp {
|
|||||||
this.log("Error " + err);
|
this.log("Error " + err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._app.set("port", WebsocketApp.port);
|
this.loadRoutes();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadRoutes( ) : void
|
public loadRoutes( ) : void
|
||||||
{
|
{
|
||||||
|
this._app.use( "/", require("./routes/ws/websocket") );
|
||||||
}
|
}
|
||||||
|
|
||||||
public listen(): Promise<void> {
|
public listen(): Promise<void> {
|
||||||
|
26
src/database/AbstractContainer.ts
Normal file
26
src/database/AbstractContainer.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import {IContainer} from "./IContainer";
|
||||||
|
import {IIngredient} from "./IIngredient";
|
||||||
|
import {HCSR04} from "hc-sr04";
|
||||||
|
|
||||||
|
export abstract class AbstractContainer implements IContainer {
|
||||||
|
slot: number = 1;
|
||||||
|
content: IIngredient | undefined;
|
||||||
|
sensorEcho: number = 26;
|
||||||
|
sensorFilledMax: number = 0;
|
||||||
|
sensorFilledMin: number = 0;
|
||||||
|
sensorTrigger: number = 27;
|
||||||
|
volume: number = 1000;
|
||||||
|
sensor: HCSR04;
|
||||||
|
|
||||||
|
protected constructor() {
|
||||||
|
this.sensor = new HCSR04(this.sensorTrigger, this.sensorEcho);
|
||||||
|
}
|
||||||
|
|
||||||
|
public measure() : Number {
|
||||||
|
let dist = this.sensor.distance();
|
||||||
|
return dist * 100 / (this.sensorFilledMax + this.sensorFilledMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
17
src/database/Container.ts
Normal file
17
src/database/Container.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import * as Mongoose from "mongoose";
|
||||||
|
import {AbstractContainer} from "./AbstractContainer";
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
export const ContainerSchema = new Mongoose.Schema<AbstractContainer>({
|
||||||
|
slot: {type: Number},
|
||||||
|
volume: {type: Number, required: true, default: 1000},
|
||||||
|
sensorEcho: Number,
|
||||||
|
sensorTrigger: Number,
|
||||||
|
content: {type: String},
|
||||||
|
sensorFilledMax: Number,
|
||||||
|
sensorFilledMin: Number
|
||||||
|
});
|
||||||
|
|
||||||
|
const Container = mongoose.model<AbstractContainer>('Container', ContainerSchema);
|
||||||
|
export default Container;
|
||||||
|
|
33
src/database/Database.ts
Normal file
33
src/database/Database.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import * as Mongoose from "mongoose";
|
||||||
|
import debug from "debug";
|
||||||
|
import Ingredient from "./Ingredient";
|
||||||
|
import Drink from "./Drink";
|
||||||
|
import Container from "./Container";
|
||||||
|
|
||||||
|
const log = debug("itender:server");
|
||||||
|
|
||||||
|
export class Database {
|
||||||
|
public static connect(): Promise<void> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await Mongoose.connect("mongodb://localhost:27017/iTender?retryWrites=true");
|
||||||
|
log("Connected to Database");
|
||||||
|
/*if (Mongoose.connection.readyState == Mongoose.ConnectionStates.connected) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject("Can't connect to database");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Preload schemes
|
||||||
|
Ingredient.find();
|
||||||
|
Drink.find();
|
||||||
|
Container.find();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (e) {
|
||||||
|
reject("Exception whilst connecting to Database " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
11
src/database/Drink.ts
Normal file
11
src/database/Drink.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import {IDrink} from "./IDrink";
|
||||||
|
import * as mongoose from "mongoose";
|
||||||
|
|
||||||
|
export const DrinkSchema = new mongoose.Schema<IDrink>({
|
||||||
|
name: {type: String, required: true},
|
||||||
|
ingredients: [{type: {type: mongoose.Types.ObjectId, ref: "Ingredient", required: true}, amount: { type: Number } }],
|
||||||
|
category: String
|
||||||
|
});
|
||||||
|
|
||||||
|
const Drink = mongoose.model<IDrink>('Drink', DrinkSchema);
|
||||||
|
export default Drink;
|
12
src/database/IContainer.ts
Normal file
12
src/database/IContainer.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import {IIngredient} from "./IIngredient";
|
||||||
|
import * as mongoose from "mongoose";
|
||||||
|
|
||||||
|
export interface IContainer extends mongoose.Document {
|
||||||
|
slot: number;
|
||||||
|
content: IIngredient|undefined;
|
||||||
|
volume: number;
|
||||||
|
sensorFilledMin : number;
|
||||||
|
sensorFilledMax: number;
|
||||||
|
sensorTrigger: number;
|
||||||
|
sensorEcho: number;
|
||||||
|
}
|
15
src/database/IDrink.ts
Normal file
15
src/database/IDrink.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import {IIngredient} from "./IIngredient";
|
||||||
|
import {Category} from "../Category";
|
||||||
|
import * as mongoose from "mongoose";
|
||||||
|
|
||||||
|
export interface IDrink extends mongoose.Document {
|
||||||
|
// Name for the Drink
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
// Ingredients
|
||||||
|
ingredients: { type: IIngredient, amount: Number }[];
|
||||||
|
|
||||||
|
// Category of the drink
|
||||||
|
category: Category;
|
||||||
|
|
||||||
|
}
|
10
src/database/IIngredient.ts
Normal file
10
src/database/IIngredient.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import {Category} from "../Category";
|
||||||
|
import * as mongoose from "mongoose";
|
||||||
|
|
||||||
|
export interface IIngredient extends mongoose.Document{
|
||||||
|
// Name of the ingredient
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
// Category of the ingredient
|
||||||
|
category: Category;
|
||||||
|
}
|
12
src/database/Ingredient.ts
Normal file
12
src/database/Ingredient.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import * as Mongoose from "mongoose";
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
import {IIngredient} from "./IIngredient";
|
||||||
|
|
||||||
|
export const IngredientSchema = new Mongoose.Schema<IIngredient>({
|
||||||
|
name: {type: String, required: true},
|
||||||
|
category: String
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const Ingredient = mongoose.model<IIngredient>('Ingredient', IngredientSchema);
|
||||||
|
export default Ingredient;
|
36
src/iTender.ts
Normal file
36
src/iTender.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {iTenderStatus} from "./iTenderStatus";
|
||||||
|
import {AbstractContainer} from "./database/AbstractContainer";
|
||||||
|
|
||||||
|
export class iTender {
|
||||||
|
private static _status: iTenderStatus;
|
||||||
|
|
||||||
|
private static containers: AbstractContainer[];
|
||||||
|
|
||||||
|
static set status(value: iTenderStatus) {
|
||||||
|
this._status = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get status(): iTenderStatus {
|
||||||
|
return this._status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static startFill() {
|
||||||
|
// todo Fill method
|
||||||
|
}
|
||||||
|
|
||||||
|
static stopFill() {
|
||||||
|
// todo Stop fill method
|
||||||
|
}
|
||||||
|
|
||||||
|
static measureContainers() {
|
||||||
|
for( let container of this.containers )
|
||||||
|
{
|
||||||
|
container.measure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static loadContainers() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
7
src/iTenderStatus.ts
Normal file
7
src/iTenderStatus.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export enum iTenderStatus {
|
||||||
|
STARTING,
|
||||||
|
READY,
|
||||||
|
FILLING,
|
||||||
|
REFRESHING,
|
||||||
|
ERROR
|
||||||
|
}
|
36
src/main.ts
36
src/main.ts
@ -1,6 +1,10 @@
|
|||||||
import {App} from "./App";
|
import {App} from "./App";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
import {WebsocketApp} from "./WebsocketApp";
|
import {WebsocketApp} from "./WebsocketApp";
|
||||||
|
import {Database} from "./database/Database";
|
||||||
|
import Drink from "./database/Drink";
|
||||||
|
import Ingredient from "./database/Ingredient";
|
||||||
|
import {Category} from "./Category";
|
||||||
|
|
||||||
const log = debug("itender:server");
|
const log = debug("itender:server");
|
||||||
|
|
||||||
@ -11,9 +15,11 @@ const wsApp = new WebsocketApp();
|
|||||||
|
|
||||||
(async( ) => {
|
(async( ) => {
|
||||||
try {
|
try {
|
||||||
log("Starting...")
|
log("Starting...");
|
||||||
|
await Database.connect();
|
||||||
await app.listen();
|
await app.listen();
|
||||||
await wsApp.listen();
|
await wsApp.listen();
|
||||||
|
await test();
|
||||||
} catch( e )
|
} catch( e )
|
||||||
{
|
{
|
||||||
console.error("---- ERROR ----")
|
console.error("---- ERROR ----")
|
||||||
@ -22,3 +28,31 @@ const wsApp = new WebsocketApp();
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
async function test() {
|
||||||
|
console.log("Testing fn");
|
||||||
|
|
||||||
|
|
||||||
|
let drink = await Drink.findOne( { name: "Mezzo Mix" } ).populate("ingredients.type");
|
||||||
|
console.log(drink);
|
||||||
|
//console.log(drink.ingredients)
|
||||||
|
/*let ingredient = new Ingredient();
|
||||||
|
ingredient.name = "Cola";
|
||||||
|
ingredient.category = Category.ALCOHOL_FREE;
|
||||||
|
|
||||||
|
await ingredient.save();
|
||||||
|
|
||||||
|
let ingredient2 = new Ingredient();
|
||||||
|
ingredient2.name = "Fanta";
|
||||||
|
ingredient2.category = Category.ALCOHOL_FREE;
|
||||||
|
|
||||||
|
await ingredient2.save();
|
||||||
|
|
||||||
|
let drink = new Drink();
|
||||||
|
drink.name = "Mezzo Mix";
|
||||||
|
drink.ingredients = [ {type: ingredient2, amount: 2}, { type: ingredient, amount: 10 } ];
|
||||||
|
|
||||||
|
await drink.save();*/
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
var express = require('express');
|
|
||||||
var router = express.Router();
|
|
||||||
|
|
||||||
/* GET users listing. */
|
|
||||||
router.get('/', function(req, res, next) {
|
|
||||||
res.send('respond with a resource');
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
33
src/routes/ws/websocket.ts
Normal file
33
src/routes/ws/websocket.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {WebSocketPayload} from "../../WebSocketPayload";
|
||||||
|
import debug from "debug";
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
let currentWS: WebSocket;
|
||||||
|
|
||||||
|
const log = debug("itender:websocket");
|
||||||
|
|
||||||
|
router.ws('/', (ws, req, next) => {
|
||||||
|
log("Incoming websocket connection")
|
||||||
|
ws.on('message', async (raw, bool) => {
|
||||||
|
let msg = WebSocketPayload.parseFromJSON(raw);
|
||||||
|
// If message is null, close the socket because it could not be decompiled
|
||||||
|
if (!msg) {
|
||||||
|
ws.close(1011);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (msg.event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('open', (listener) => {
|
||||||
|
if (currentWS)
|
||||||
|
currentWS.close(1001);
|
||||||
|
currentWS = ws;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
5
src/web/ButtonType.ts
Normal file
5
src/web/ButtonType.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum ButtonType {
|
||||||
|
SUCCESS = "success",
|
||||||
|
ERROR = "error",
|
||||||
|
|
||||||
|
}
|
105
src/web/Modal.ts
Normal file
105
src/web/Modal.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import {ButtonType} from "./ButtonType";
|
||||||
|
|
||||||
|
export class Modal {
|
||||||
|
|
||||||
|
private static currentModalId = "";
|
||||||
|
|
||||||
|
private _title: string = "iTender";
|
||||||
|
private _content: string | undefined = "";
|
||||||
|
private _id: string = "";
|
||||||
|
private _loader: boolean = false;
|
||||||
|
private _buttons: { type: string, content: string, onclick: Function }[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
constructor(id, title: string, content?: string) {
|
||||||
|
this._id = id;
|
||||||
|
this._title = title;
|
||||||
|
this._content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set title(value: string) {
|
||||||
|
this._title = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set content(value: string | undefined) {
|
||||||
|
this._content = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set id(value: string) {
|
||||||
|
this._id = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set loader(value: boolean) {
|
||||||
|
this._loader = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addButton( type: ButtonType, content: string, onclick: Function )
|
||||||
|
{
|
||||||
|
this._buttons.push({type: type, content: content, onclick: onclick});
|
||||||
|
}
|
||||||
|
|
||||||
|
public open() {
|
||||||
|
if (!this._content)
|
||||||
|
this._content = "";
|
||||||
|
|
||||||
|
if (this._loader)
|
||||||
|
this._content += "<br><div class=\"lds-ellipsis\">\n" +
|
||||||
|
" <div></div><div></div><div></div><div></div>\n" +
|
||||||
|
"</div>";
|
||||||
|
|
||||||
|
for (let btn of this._buttons) {
|
||||||
|
this._content += `<button class="btn btn-${btn.type}" onclick="${btn.onclick}">${btn.content}</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.open(this._title, this._content, this._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param title
|
||||||
|
* @param content
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
public static open(title: string, content: string, id?: string): void {
|
||||||
|
const modal = document.getElementById("modal");
|
||||||
|
const modalContent = document.getElementById("modalInnerContent");
|
||||||
|
|
||||||
|
if (!modal || !modalContent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
modalContent.classList.add("modalBlendIn");
|
||||||
|
modal.classList.add("modalBlendIn");
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
modalContent.classList.remove("modalBlendIn");
|
||||||
|
modal.classList.remove("modalBlendIn");
|
||||||
|
}, 800);
|
||||||
|
|
||||||
|
modalContent.innerHTML = `<h1 id="modalTitle">${title}</h1>${content}`;
|
||||||
|
modal.style.display = "block";
|
||||||
|
|
||||||
|
this.currentModalId = id ? id : "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static close(id?: string): void {
|
||||||
|
if (this.currentModalId != id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const modal = document.getElementById("modal");
|
||||||
|
const modalContent = document.getElementById("modal-content");
|
||||||
|
const modalInnerContent = document.getElementById("modalInnerContent");
|
||||||
|
|
||||||
|
if (!modal || !modalContent || !modalInnerContent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
modalContent.classList.add("modalBlendOut");
|
||||||
|
modal.classList.add("modalBlendOut");
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.style.display = "none";
|
||||||
|
modalInnerContent.innerHTML = "";
|
||||||
|
modalContent.classList.remove("modalBlendOut");
|
||||||
|
modal.classList.remove("modalBlendOut");
|
||||||
|
}, 800);
|
||||||
|
}
|
||||||
|
}
|
29
src/web/WebSocketHandler.ts
Normal file
29
src/web/WebSocketHandler.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import {Modal} from "./Modal";
|
||||||
|
|
||||||
|
export class WebSocketHandler {
|
||||||
|
private socket : WebSocket;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.socket = new WebSocket((window.location.protocol == "http:" ? "ws://" : "wss://") + window.location.hostname + ":3005");
|
||||||
|
this.socket.onopen = this.onOpen;
|
||||||
|
this.socket.onclose = this.onClose;
|
||||||
|
this.socket.onerror = this.onError;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onOpen( event )
|
||||||
|
{
|
||||||
|
Modal.close("start");
|
||||||
|
}
|
||||||
|
|
||||||
|
private onClose( event ) {
|
||||||
|
console.error("WS Closed!", event );
|
||||||
|
//openModal("Einen Augenblick...", `Es wurde ein Verbindungsfehler erkannt.\nBitte warten Sie, während der Prozess neu gestartet wird...` );
|
||||||
|
//window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private onError( event ) {
|
||||||
|
console.error("WS Error", event);
|
||||||
|
//openModal("Einen Augenblick...", `Es wurde ein kritischer Fehler festgestellt.\nBitte warten Sie, während der Prozess neu gestartet wird...` );
|
||||||
|
//window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
import {WebSocketHandler} from "./WebSocketHandler";
|
||||||
|
import {Modal} from "./Modal";
|
||||||
|
|
||||||
const main = document.getElementById("main");
|
const main = document.getElementById("main");
|
||||||
const time = document.getElementById("title");
|
const time = document.getElementById("title");
|
||||||
@ -5,12 +7,13 @@ const time = document.getElementById("title");
|
|||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
console.log("DOM Loaded");
|
console.log("DOM Loaded");
|
||||||
|
|
||||||
openModal("iTender", "Starten...")
|
let modal = new Modal("start", "iTender");
|
||||||
|
modal.content = "Willkommen";
|
||||||
|
modal.loader = true;
|
||||||
|
modal.open();
|
||||||
|
connect();
|
||||||
|
|
||||||
setTimeout( load, 1000);
|
setTimeout( load, 1000);
|
||||||
setTimeout( () => closeModal(), 2500 );
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function load()
|
function load()
|
||||||
@ -48,44 +51,9 @@ function load()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let wsHandler;
|
||||||
function openModal(title, content) {
|
function connect()
|
||||||
const modal = document.getElementById("modal");
|
{
|
||||||
const modalContent = document.getElementById("modalInnerContent");
|
wsHandler = new WebSocketHandler();
|
||||||
|
|
||||||
if(!modal || !modalContent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
modalContent.classList.add("modalBlendIn");
|
|
||||||
modal.classList.add("modalBlendIn");
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
modalContent.classList.remove("modalBlendIn");
|
|
||||||
modal.classList.remove("modalBlendIn");
|
|
||||||
}, 800);
|
|
||||||
|
|
||||||
modalContent.innerHTML = `<h1 id="modalTitle">${title}</h1>${content}`;
|
|
||||||
modal.style.display = "block";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
const modal = document.getElementById("modal");
|
|
||||||
const modalContent = document.getElementById("modal-content");
|
|
||||||
const modalInnerContent = document.getElementById("modalInnerContent");
|
|
||||||
|
|
||||||
if(!modal || !modalContent || !modalInnerContent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
modalContent.classList.add("modalBlendOut");
|
|
||||||
modal.classList.add("modalBlendOut");
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
modal.style.display = "none";
|
|
||||||
modalInnerContent.innerHTML = "";
|
|
||||||
modalContent.classList.remove("modalBlendOut");
|
|
||||||
modal.classList.remove("modalBlendOut");
|
|
||||||
}, 800);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -4,7 +4,6 @@ html
|
|||||||
title iTender
|
title iTender
|
||||||
link(rel='stylesheet', href='/stylesheets/reset.css')
|
link(rel='stylesheet', href='/stylesheets/reset.css')
|
||||||
link(rel='stylesheet', href='/stylesheets/style.css')
|
link(rel='stylesheet', href='/stylesheets/style.css')
|
||||||
link(rel='stylesheet', href='/stylesheets/modal.css')
|
|
||||||
body
|
body
|
||||||
div.modal#modal
|
div.modal#modal
|
||||||
div.modal-content#modal-content
|
div.modal-content#modal-content
|
||||||
@ -29,4 +28,4 @@ html
|
|||||||
setTimeout( () =>
|
setTimeout( () =>
|
||||||
{
|
{
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}, 30000 );
|
}, 120000 );
|
||||||
|
Loading…
x
Reference in New Issue
Block a user