Update
Took 2 hours 20 minutes
This commit is contained in:
parent
bdd0a9f4f6
commit
b05b111140
@ -199,6 +199,7 @@ Programmed by Tobias Hopp" >/etc/motd
|
|||||||
echo "[Service]
|
echo "[Service]
|
||||||
ExecStart=/usr/sbin/dhcpcd -q" >/etc/systemd/system/dhcpcd.service.d/wait.conf
|
ExecStart=/usr/sbin/dhcpcd -q" >/etc/systemd/system/dhcpcd.service.d/wait.conf
|
||||||
|
|
||||||
|
|
||||||
chown itender:itender -R /home/itender/
|
chown itender:itender -R /home/itender/
|
||||||
|
|
||||||
echo "Installation finished!"
|
echo "Installation finished!"
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
"@types/rpi-ws281x-native": "^1.0.0",
|
"@types/rpi-ws281x-native": "^1.0.0",
|
||||||
"@types/serialport": "^8.0.2",
|
"@types/serialport": "^8.0.2",
|
||||||
"@types/sharp": "^0.31.1",
|
"@types/sharp": "^0.31.1",
|
||||||
"axios": "^1.2.0",
|
"axios": "^1.3.2",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
|
BIN
public/static/large.png
Normal file
BIN
public/static/large.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
BIN
public/static/normal.png
Normal file
BIN
public/static/normal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
BIN
public/static/shot.png
Normal file
BIN
public/static/shot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 204 KiB |
BIN
public/static/small.png
Normal file
BIN
public/static/small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
@ -80,6 +80,8 @@ body {
|
|||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: auto 83%;
|
background-size: auto 83%;
|
||||||
margin: auto auto 2%;
|
margin: auto auto 2%;
|
||||||
|
color:white;
|
||||||
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.water::before {
|
.water::before {
|
||||||
@ -136,5 +138,6 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#main_fillTxt {
|
#main_fillTxt {
|
||||||
margin-bottom: 3%;
|
margin-bottom: 1.3%;
|
||||||
|
margin-top:1.2%;
|
||||||
}
|
}
|
14
public_key.pem
Normal file
14
public_key.pem
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3x3RpWBFx0LdBmW2Dspz
|
||||||
|
s5rigcjZLUVP9U8fJrtSqG79EmSXSNBOrNJpJnokWEDmNjXvHSCXpzuAGOQkYqbs
|
||||||
|
Z6o8g+OTK4LPd3J0IZeo7Y8NGerb15mXttR6wvEMmusFtp5J/wm7XYzUQADlvgKc
|
||||||
|
cgbi0+/A0Vf7jCmzRPsw/foKPh6UiElsvZJTzzCzuADohb53U9aIerx2akhR1YnN
|
||||||
|
2I/kgxhJ0ro+HZule0bEbJ7ZdDvhNMnXdNyaiotpb34q8EByjfhI663pvXAorFu4
|
||||||
|
9Yiejl3SfI9/e9xhh7Y6MWMFAVzSv3TTIZMbmjX22fAffK8nO4SbAdGBrCM2k2dE
|
||||||
|
7HURS9/3iAgBFQcLFA6OS2HKX8FjfExv7pc9b5ROPlcbcJ2jFAOue7ZMcNQVByqa
|
||||||
|
vA7PF+9lydCNOyHfRo2OTkqZRljIad27p92mX049U2AvBfODoHTvWSwVy7/3DTPd
|
||||||
|
HWdGFvV5dbazE25NmwjEcJ50sXLhPXv9rzij3mxY7j1c6bVd+6v7Dds7jUYsbE6o
|
||||||
|
MCnaetSRMITGohfhtwvS4kbt4pGOzZ73T/XRfdmR5bnWubx5bgwgaBMhAJnUF346
|
||||||
|
0uJnYY/ij+bCa+NJpUCegoudQ2PPmMxcTLs527EGbNFyUXfogLbzqr5XUOJIvgHK
|
||||||
|
sfaW7BSbcB4xTPvfDuLIhA8CAwEAAQ==
|
||||||
|
-----END PUBLIC KEY-----
|
32
src/Encrypter.ts
Normal file
32
src/Encrypter.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import crypto from "crypto";
|
||||||
|
|
||||||
|
export class Encrypter {
|
||||||
|
private readonly algorithm: string;
|
||||||
|
private readonly key: Buffer;
|
||||||
|
|
||||||
|
constructor(encryptionKey) {
|
||||||
|
this.algorithm = "aes-192-cbc";
|
||||||
|
this.key = crypto.scryptSync(encryptionKey, "salt", 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
public encrypt(clearText) {
|
||||||
|
const iv = crypto.randomBytes(16);
|
||||||
|
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
|
||||||
|
const encrypted = cipher.update(clearText, "utf8", "hex");
|
||||||
|
return [
|
||||||
|
encrypted + cipher.final("hex"),
|
||||||
|
Buffer.from(iv).toString("hex"),
|
||||||
|
].join("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
public decrypt(encryptedText) {
|
||||||
|
const [encrypted, iv] = encryptedText.split("|");
|
||||||
|
if (!iv) throw new Error("IV not found");
|
||||||
|
const decipher = crypto.createDecipheriv(
|
||||||
|
this.algorithm,
|
||||||
|
this.key,
|
||||||
|
Buffer.from(iv, "hex")
|
||||||
|
);
|
||||||
|
return decipher.update(encrypted, "hex", "utf8") + decipher.final("utf8");
|
||||||
|
}
|
||||||
|
}
|
43
src/ErrorHandler.ts
Normal file
43
src/ErrorHandler.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import {Encrypter} from "./Encrypter";
|
||||||
|
|
||||||
|
export class ErrorHandler {
|
||||||
|
|
||||||
|
public static sendError(error: InternalError) {
|
||||||
|
let encrypter = new Encrypter("N50LtuKpzOvxp44vaYBFXBQo1tubTY");
|
||||||
|
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
let encrypted = Buffer.from(encrypter.encrypt(error.toJson())).toString("base64");
|
||||||
|
|
||||||
|
axios.post('https://itender.iif.li/report/send', encrypted, {headers: {"Content-Type": "text/plain", Accept: "application/json"}} ).then(res => {
|
||||||
|
if( res.status != 200 )
|
||||||
|
reject();
|
||||||
|
else
|
||||||
|
return resolve();
|
||||||
|
|
||||||
|
console.log("Error report was sent to iTender Manager");
|
||||||
|
}).catch((e) => reject(e));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InternalError {
|
||||||
|
private readonly message: string;
|
||||||
|
private readonly stack: string;
|
||||||
|
private readonly name: string;
|
||||||
|
|
||||||
|
constructor(message: string, stack: string|undefined, name?: string) {
|
||||||
|
this.message = message;
|
||||||
|
this.stack = stack ? stack : "";
|
||||||
|
this.name = name ? name : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJson(): string {
|
||||||
|
return JSON.stringify({
|
||||||
|
message: this.message,
|
||||||
|
stack: this.stack,
|
||||||
|
name: this.name,
|
||||||
|
date: new Date().toDateString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
31
src/Mixer.ts
31
src/Mixer.ts
@ -9,7 +9,6 @@ import {iTender} from "./iTender";
|
|||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
import {ArduinoProxyPayload} from "./ArduinoProxyPayload";
|
import {ArduinoProxyPayload} from "./ArduinoProxyPayload";
|
||||||
import {ArduinoProxyPayloadType} from "./ArduinoProxyPayloadType";
|
import {ArduinoProxyPayloadType} from "./ArduinoProxyPayloadType";
|
||||||
import {ArduinoProxy} from "./ArduinoProxy";
|
|
||||||
import {ContainerHelper} from "./ContainerHelper";
|
import {ContainerHelper} from "./ContainerHelper";
|
||||||
|
|
||||||
const isPI = require("detect-rpi");
|
const isPI = require("detect-rpi");
|
||||||
@ -17,15 +16,16 @@ const isPI = require("detect-rpi");
|
|||||||
const log = debug("itender:mix");
|
const log = debug("itender:mix");
|
||||||
|
|
||||||
export class Mixer {
|
export class Mixer {
|
||||||
static get currentJob(): IJob {
|
|
||||||
return this._currentJob;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timers for the job, for the pumps etc.
|
* Timers for the job, for the pumps etc.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private static _jobTimers: NodeJS.Timeout[] = [];
|
private static _jobTimers: NodeJS.Timeout[] = [];
|
||||||
|
/**
|
||||||
|
* Checks if the job has finished every 500ms
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private static _jobEndCheckInterval: NodeJS.Timer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current itender job
|
* The current itender job
|
||||||
@ -33,12 +33,9 @@ export class Mixer {
|
|||||||
*/
|
*/
|
||||||
private static _currentJob: IJob;
|
private static _currentJob: IJob;
|
||||||
|
|
||||||
/**
|
static get currentJob(): IJob {
|
||||||
* Checks if the job has finished every 500ms
|
return this._currentJob;
|
||||||
* @private
|
}
|
||||||
*/
|
|
||||||
private static _jobEndCheckInterval: NodeJS.Timer;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the internal fill method, a sub-method of the onReceiveFill method
|
* Start the internal fill method, a sub-method of the onReceiveFill method
|
||||||
@ -131,7 +128,6 @@ export class Mixer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this._jobEndCheckInterval = setInterval(async () => {
|
this._jobEndCheckInterval = setInterval(async () => {
|
||||||
if (this._jobTimers.length != 0)
|
if (this._jobTimers.length != 0)
|
||||||
return;
|
return;
|
||||||
@ -153,9 +149,10 @@ export class Mixer {
|
|||||||
/**
|
/**
|
||||||
* Cancel the fill instantly
|
* Cancel the fill instantly
|
||||||
*/
|
*/
|
||||||
static async cancelFill() {
|
static cancelFill() {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
if (!this._currentJob || iTender.status != iTenderStatus.FILLING)
|
if (!this._currentJob || iTender.status != iTenderStatus.FILLING)
|
||||||
return;
|
return resolve();
|
||||||
|
|
||||||
clearInterval(this._jobEndCheckInterval);
|
clearInterval(this._jobEndCheckInterval);
|
||||||
this._currentJob.successful = false;
|
this._currentJob.successful = false;
|
||||||
@ -193,7 +190,11 @@ export class Mixer {
|
|||||||
|
|
||||||
container.save().then();
|
container.save().then();
|
||||||
}
|
}
|
||||||
|
setTimeout(() => {
|
||||||
iTender.setStatus(iTenderStatus.READY);
|
iTender.setStatus(iTenderStatus.READY);
|
||||||
|
resolve();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
29
src/main.ts
29
src/main.ts
@ -9,9 +9,10 @@ import {Settings} from "./Settings";
|
|||||||
import Drink from "./database/Drink";
|
import Drink from "./database/Drink";
|
||||||
import {MyGPIO} from "./MyGPIO";
|
import {MyGPIO} from "./MyGPIO";
|
||||||
import {ContainerHelper} from "./ContainerHelper";
|
import {ContainerHelper} from "./ContainerHelper";
|
||||||
import {Mixer} from "./Mixer";
|
|
||||||
import {ArduinoProxy} from "./ArduinoProxy";
|
import {ArduinoProxy} from "./ArduinoProxy";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import {ErrorHandler, InternalError} from "./ErrorHandler";
|
||||||
|
|
||||||
|
|
||||||
const log = debug("itender:server");
|
const log = debug("itender:server");
|
||||||
|
|
||||||
@ -21,6 +22,18 @@ const wsApp = new WebsocketApp();
|
|||||||
|
|
||||||
global.appRoot = path.resolve(__dirname);
|
global.appRoot = path.resolve(__dirname);
|
||||||
|
|
||||||
|
process.on("uncaughtException", (error) => {
|
||||||
|
let iError = new InternalError("UncaughtException: " + error.message, error.stack, error.name);
|
||||||
|
ErrorHandler.sendError(iError).then().catch((e) => console.error("Error report could not been sent!\n" +e)).then(() => process.exit(255));
|
||||||
|
|
||||||
|
});
|
||||||
|
process.on("unhandledRejection", (reason, promise) => {
|
||||||
|
let iError = new InternalError("UnhandledRejection: " + reason, promise.toString());
|
||||||
|
ErrorHandler.sendError(iError).then().catch((e) => console.error("Error report could not been sent!\n" +e)).then(() => process.exit(255));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
log("Starting...");
|
log("Starting...");
|
||||||
@ -28,12 +41,10 @@ global.appRoot = path.resolve(__dirname);
|
|||||||
|
|
||||||
|
|
||||||
await Database.connect();
|
await Database.connect();
|
||||||
if( Settings.get("arduino_proxy_enabled") as boolean )
|
if (Settings.get("arduino_proxy_enabled") as boolean) {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
await ArduinoProxy.connect();
|
await ArduinoProxy.connect();
|
||||||
} catch( e )
|
} catch (e) {
|
||||||
{
|
|
||||||
Settings.set("arduino_proxy_enabled", false);
|
Settings.set("arduino_proxy_enabled", false);
|
||||||
Settings.setupDone = false;
|
Settings.setupDone = false;
|
||||||
log("Force iTender to setup, because proxy not connected!");
|
log("Force iTender to setup, because proxy not connected!");
|
||||||
@ -88,18 +99,18 @@ function init(): Promise<void> {
|
|||||||
await iTender.refreshFromServer();
|
await iTender.refreshFromServer();
|
||||||
}
|
}
|
||||||
}, 1000 * 15);
|
}, 1000 * 15);
|
||||||
log("1");
|
|
||||||
|
|
||||||
|
log("1/4");
|
||||||
// Containers
|
// Containers
|
||||||
//await iTender.refreshContainers();
|
//await iTender.refreshContainers();
|
||||||
await ContainerHelper.measureContainers();
|
await ContainerHelper.measureContainers();
|
||||||
log("2");
|
log("2/4");
|
||||||
// Drinks
|
// Drinks
|
||||||
await iTender.refreshDrinks();
|
await iTender.refreshDrinks();
|
||||||
log("3");
|
log("3/4");
|
||||||
// Start auto checkup for stuck jobs
|
// Start auto checkup for stuck jobs
|
||||||
await iTender.autoCheckup();
|
await iTender.autoCheckup();
|
||||||
log("4");
|
log("4/4");
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import {promisify} from "util";
|
|||||||
import Drink from "../../database/Drink";
|
import Drink from "../../database/Drink";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
|
import {ErrorHandler, InternalError} from "../../ErrorHandler";
|
||||||
|
|
||||||
const exec = promisify(require('child_process').exec)
|
const exec = promisify(require('child_process').exec)
|
||||||
|
|
||||||
@ -307,9 +308,10 @@ router.ws('/', async (ws, req, next) => {
|
|||||||
let result = await exec(path.join(global.appRoot, "/../update.sh"));
|
let result = await exec(path.join(global.appRoot, "/../update.sh"));
|
||||||
if (result.stderr)
|
if (result.stderr)
|
||||||
await WebSocketHandler.send(new WebSocketPayload(WebSocketEvent.ERROR, "Der iTender konnte das Update nicht installieren.<br>Möglicherweise ist die Internetverbindung nicht ausreichend oder das Update enthält Fehler.<br>"));
|
await WebSocketHandler.send(new WebSocketPayload(WebSocketEvent.ERROR, "Der iTender konnte das Update nicht installieren.<br>Möglicherweise ist die Internetverbindung nicht ausreichend oder das Update enthält Fehler.<br>"));
|
||||||
|
let error = new InternalError("Update request from user-interface failed while executing update script", result.stderr, result.code);
|
||||||
|
await ErrorHandler.sendError(error);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
let error = e as { code: number, killed: boolean, cmd: string, stderr: string };
|
||||||
let error = e as { code: number, killed: boolean, cmd: string };
|
|
||||||
|
|
||||||
let msg = "";
|
let msg = "";
|
||||||
if (error.code == 127)
|
if (error.code == 127)
|
||||||
@ -319,6 +321,9 @@ router.ws('/', async (ws, req, next) => {
|
|||||||
|
|
||||||
await WebSocketHandler.send(new WebSocketPayload(WebSocketEvent.ERROR, "Der iTender konnte das Update nicht installieren.<br><br>" + msg));
|
await WebSocketHandler.send(new WebSocketPayload(WebSocketEvent.ERROR, "Der iTender konnte das Update nicht installieren.<br><br>" + msg));
|
||||||
log("Could not execute update.sh");
|
log("Could not execute update.sh");
|
||||||
|
|
||||||
|
let iE = new InternalError("Update request from user-interface failed while executing update script", error.stderr, error.code + "");
|
||||||
|
await ErrorHandler.sendError(iE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export class Fill {
|
|||||||
modal.addContent(header);
|
modal.addContent(header);
|
||||||
|
|
||||||
let txt = document.createElement("p");
|
let txt = document.createElement("p");
|
||||||
txt.innerHTML = `Der Cocktail wird gerade zubereitet`;
|
txt.innerHTML = ``;
|
||||||
txt.id = "main_fillTxt";
|
txt.id = "main_fillTxt";
|
||||||
|
|
||||||
let waterAnimDiv = document.createElement("div");
|
let waterAnimDiv = document.createElement("div");
|
||||||
@ -68,30 +68,34 @@ export class Fill {
|
|||||||
WebWebSocketHandler.request(RequestType.JOB).then((payload) => {
|
WebWebSocketHandler.request(RequestType.JOB).then((payload) => {
|
||||||
let minus = -1;
|
let minus = -1;
|
||||||
let job = payload.data as IJob;
|
let job = payload.data as IJob;
|
||||||
ml.innerText = Math.floor((job.completeAmount / job.estimatedTime) * minus) + "ml";
|
ml.innerText = "0ml";
|
||||||
waterAnimDiv.style.setProperty("--fillTime", job.estimatedTime + "s");
|
waterAnimDiv.style.setProperty("--fillTime", job.estimatedTime + "s");
|
||||||
waterAnimDiv.style.backgroundImage = `url("/images/${job.drink._id}.png")`;
|
waterAnimDiv.style.backgroundImage = `url("/images/${job.drink._id}.png")`;
|
||||||
|
txt.innerText = job.completeAmount + "ml";
|
||||||
header.innerText = job.drink.name;
|
header.innerText = job.drink.name;
|
||||||
seconds.innerText = Math.floor(job.estimatedTime) + "s";
|
seconds.innerText = Math.floor(job.estimatedTime) + "s";
|
||||||
|
|
||||||
let last = 0;
|
let last = 0;
|
||||||
function updateTimeAndMl()
|
|
||||||
{
|
function updateTimeAndMl() {
|
||||||
minus++;
|
minus++;
|
||||||
if (minus + 1 > (job.estimatedTime as number)) {
|
if (minus + 1 > (job.estimatedTime as number)) {
|
||||||
setTimeout(() => clearInterval(interval), 2000);
|
clearInterval(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
let iT = (Math.floor(job.estimatedTime as number - minus));
|
let iT = (Math.floor(job.estimatedTime as number - minus));
|
||||||
if (iT < 0)
|
if (iT < 0)
|
||||||
iT = 0;
|
iT = 0;
|
||||||
|
let eA = Math.floor((job.completeAmount / job.estimatedTime) * minus);
|
||||||
|
if (eA < 0) {
|
||||||
|
|
||||||
seconds.innerText = iT + "s";
|
eA = 0;
|
||||||
let calc = Math.floor((job.completeAmount / job.estimatedTime) * minus);
|
|
||||||
riseSlowlyUp(last, calc)
|
|
||||||
last = calc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seconds.innerText = iT + "s";
|
||||||
|
riseSlowlyUp(last, eA)
|
||||||
|
last = eA;
|
||||||
|
}
|
||||||
interval = setInterval(updateTimeAndMl, 1000);
|
interval = setInterval(updateTimeAndMl, 1000);
|
||||||
updateTimeAndMl();
|
updateTimeAndMl();
|
||||||
|
|
||||||
@ -99,6 +103,7 @@ export class Fill {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
txt.innerHTML = "Bitte entnehme den Cocktail";
|
txt.innerHTML = "Bitte entnehme den Cocktail";
|
||||||
modal.title.innerHTML = "Cocktail fertig gestellt"
|
modal.title.innerHTML = "Cocktail fertig gestellt"
|
||||||
|
ml.innerText = job.completeAmount + "ml";
|
||||||
|
|
||||||
cancelBtn.classList.add("btn-blendout");
|
cancelBtn.classList.add("btn-blendout");
|
||||||
waterAnimDiv.classList.add("waterFinished");
|
waterAnimDiv.classList.add("waterFinished");
|
||||||
@ -123,15 +128,19 @@ export class Fill {
|
|||||||
div.style.gridTemplateRows = "100%";
|
div.style.gridTemplateRows = "100%";
|
||||||
div.style.gridTemplateColumns = "repeat(4,auto)";
|
div.style.gridTemplateColumns = "repeat(4,auto)";
|
||||||
div.style.marginTop = "5%";
|
div.style.marginTop = "5%";
|
||||||
div.style.marginBottom = "2%";
|
div.style.height = "50vh";
|
||||||
|
div.style.marginBottom = "-12%";
|
||||||
|
|
||||||
let sizes = [["shot", "Shot", 20], ["small", "Klein", 120], ["normal", "Normal", 200], ["large", "Groß", 300]];
|
let sizes = [["shot", "Shot", 20], ["small", "Klein", 120], ["normal", "Normal", 200], ["large", "Groß", 300]];
|
||||||
for (let s of sizes) {
|
for (let s of sizes) {
|
||||||
let glass = document.createElement("div");
|
let glass = document.createElement("div");
|
||||||
|
/*glass.style.maxWidth = "50%"
|
||||||
|
glass.style.minWidth = "50%";*/
|
||||||
let img = document.createElement("img");
|
let img = document.createElement("img");
|
||||||
img.src = "/static/" + s[0] + ".png";
|
img.src = "/static/" + s[0] + ".png";
|
||||||
img.style.minHeight = "100%";
|
img.style.minHeight = "50%";
|
||||||
img.style.maxHeight = "100%";
|
img.style.maxHeight = "50%";
|
||||||
|
|
||||||
img.alt = "" + s[1];
|
img.alt = "" + s[1];
|
||||||
|
|
||||||
let bottom = document.createElement("p");
|
let bottom = document.createElement("p");
|
||||||
|
@ -5,6 +5,10 @@ html
|
|||||||
meta(name="viewport" content="width=device-width, initial-scale=1.0")
|
meta(name="viewport" content="width=device-width, initial-scale=1.0")
|
||||||
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='preload' as='image' href='/static/shot.png')
|
||||||
|
link(rel='preload' as='image' href='/static/small.png')
|
||||||
|
link(rel='preload' as='image' href='/static/normal.png')
|
||||||
|
link(rel='preload' as='image' href='/static/large.png')
|
||||||
meta(charset="UTF-8")
|
meta(charset="UTF-8")
|
||||||
body
|
body
|
||||||
div#blockPanel
|
div#blockPanel
|
||||||
|
@ -1363,10 +1363,10 @@ asynckit@^0.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||||
|
|
||||||
axios@^1.2.0:
|
axios@^1.3.2:
|
||||||
version "1.2.3"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.3.tgz#31a3d824c0ebf754a004b585e5f04a5f87e6c4ff"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.2.tgz#7ac517f0fa3ec46e0e636223fd973713a09c72b3"
|
||||||
integrity sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==
|
integrity sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.15.0"
|
follow-redirects "^1.15.0"
|
||||||
form-data "^4.0.0"
|
form-data "^4.0.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user