This commit is contained in:
parent
b61a166649
commit
628855b5d9
8 changed files with 266 additions and 13 deletions
|
@ -1,13 +1,15 @@
|
||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as nodemailer from 'nodemailer';
|
import * as nodemailer from 'nodemailer';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
// Config for scheduling the next time the main process (sending of horoscope) is run
|
// Config for scheduling the next time the main process (sending of horoscope) is run
|
||||||
/*interface Config {
|
interface Config {
|
||||||
emailReceiver: string;
|
emailReceiver: string;
|
||||||
mailHost: string;
|
mailHost: string;
|
||||||
mailPort: number;
|
mailPort: number;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
// Node structure for the Parse Tree (it's a degenerated tree with one or zero children per node)
|
// Node structure for the Parse Tree (it's a degenerated tree with one or zero children per node)
|
||||||
interface Node {
|
interface Node {
|
||||||
|
@ -16,7 +18,7 @@ interface Node {
|
||||||
value?: string;
|
value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
//var config : Config;
|
var config : Config;
|
||||||
|
|
||||||
export function executeCommand(): void {
|
export function executeCommand(): void {
|
||||||
|
|
||||||
|
@ -35,9 +37,9 @@ export function executeCommand(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load config once
|
//Load config once
|
||||||
/*if (config == undefined) {
|
if (config == undefined) {
|
||||||
config = loadConfig();
|
config = loadConfig();
|
||||||
} */
|
}
|
||||||
|
|
||||||
console.log(`Horoscope received; sending E-Mail next`);
|
console.log(`Horoscope received; sending E-Mail next`);
|
||||||
sendEmail(stdout);
|
sendEmail(stdout);
|
||||||
|
@ -48,28 +50,33 @@ export function executeCommand(): void {
|
||||||
executeCommand();
|
executeCommand();
|
||||||
|
|
||||||
// Load the Configuration
|
// Load the Configuration
|
||||||
/*function loadConfig(): Config {
|
function loadConfig(): Config {
|
||||||
|
|
||||||
console.log(`Load Config`);
|
console.log(`Load Config`);
|
||||||
|
|
||||||
const data = fs.readFileSync('config.json', 'utf-8');
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const configPath = path.join(__dirname, 'config.json');
|
||||||
|
|
||||||
|
const data = fs.readFileSync(configPath, 'utf-8');
|
||||||
|
|
||||||
return JSON.parse(data);
|
return JSON.parse(data);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
// Send E-Mail
|
// Send E-Mail
|
||||||
async function sendEmail(content: string) {
|
async function sendEmail(content: string) {
|
||||||
|
|
||||||
// Create Transporter
|
// Create Transporter
|
||||||
const transporter = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
host: "mailhog.mailhog.svc.cluster.local", //config.mailHost,
|
host: config.mailHost, //"mailhog.mailhog.svc.cluster.local", //config.mailHost,
|
||||||
port: 1025, //config.mailPort,
|
port: config.mailPort, //1025, //config.mailPort,
|
||||||
secure: false
|
secure: false
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const info = await transporter.sendMail({
|
const info = await transporter.sendMail({
|
||||||
from: '"The Oracle" <the.oracle@holy.mountain>',
|
from: '"The Oracle" <the.oracle@holy.mountain>',
|
||||||
to: "test@mailhog.local", //config.emailReceiver,
|
to: config.emailReceiver, //"test@mailhog.local", //config.emailReceiver,
|
||||||
subject: "Your Horoscope Is Ready",
|
subject: "Your Horoscope Is Ready",
|
||||||
text: content,
|
text: content,
|
||||||
html: html(content)
|
html: html(content)
|
||||||
|
|
137
dist/backend/broker.js
vendored
Normal file
137
dist/backend/broker.js
vendored
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as nodemailer from 'nodemailer';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import * as path from 'path';
|
||||||
|
var config;
|
||||||
|
export function executeCommand() {
|
||||||
|
exec('iching divine', (error, stdout, stderr) => {
|
||||||
|
console.log(`I-Ching-Broker: \'iching divine\' called`);
|
||||||
|
if (error) {
|
||||||
|
console.error(`Error: ${error.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`Stderr: ${stderr}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Load config once
|
||||||
|
if (config == undefined) {
|
||||||
|
config = loadConfig();
|
||||||
|
}
|
||||||
|
console.log(`Horoscope received; sending E-Mail next`);
|
||||||
|
sendEmail(stdout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Run function once initially, called from Dockerfile
|
||||||
|
executeCommand();
|
||||||
|
// Load the Configuration
|
||||||
|
function loadConfig() {
|
||||||
|
console.log(`Load Config`);
|
||||||
|
//const data = fs.readFileSync('config.json', 'utf-8');
|
||||||
|
//const configPath = path.join(__dirname, 'config.json');
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const configPath = path.join(__dirname, 'config.json');
|
||||||
|
const data = fs.readFileSync(configPath, 'utf-8');
|
||||||
|
return JSON.parse(data);
|
||||||
|
}
|
||||||
|
// Send E-Mail
|
||||||
|
async function sendEmail(content) {
|
||||||
|
// Create Transporter
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: config.mailHost, //"mailhog.mailhog.svc.cluster.local", //config.mailHost,
|
||||||
|
port: config.mailPort, //1025, //config.mailPort,
|
||||||
|
secure: false
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const info = await transporter.sendMail({
|
||||||
|
from: '"The Oracle" <the.oracle@holy.mountain>',
|
||||||
|
to: config.emailReceiver, //"test@mailhog.local", //config.emailReceiver,
|
||||||
|
subject: "Your Horoscope Is Ready",
|
||||||
|
text: content,
|
||||||
|
html: html(content)
|
||||||
|
});
|
||||||
|
console.log("E-Mail sent: ", info.messageId + "\n\n");
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error("Error Sending E-Mail:", error + "\n\n");
|
||||||
|
console.log("Failed to send this horoscope: \n", content + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate 1) Parse Tree and 2) HTML
|
||||||
|
export function html(inputText) {
|
||||||
|
const parseTree = parse(inputText);
|
||||||
|
const htmlOutput = render(parseTree);
|
||||||
|
return htmlOutput;
|
||||||
|
}
|
||||||
|
// Generate the Parse Tree
|
||||||
|
function parse(input) {
|
||||||
|
console.log("Parse input text");
|
||||||
|
const root = { type: "Root" };
|
||||||
|
var currentNode = root;
|
||||||
|
const lines = input.split("\n");
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith("Hexagram")) {
|
||||||
|
const hexagram = { type: "Hexagram" };
|
||||||
|
currentNode.child = hexagram;
|
||||||
|
currentNode = hexagram;
|
||||||
|
currentNode.value = "<h1>" + line + "</h1>";
|
||||||
|
}
|
||||||
|
else if (line.startsWith("Judgement")) {
|
||||||
|
const judgement = { type: "Judgement" };
|
||||||
|
currentNode.child = judgement;
|
||||||
|
currentNode = judgement;
|
||||||
|
currentNode.value = "<h3>" + line + "</h3>";
|
||||||
|
}
|
||||||
|
else if (line.startsWith("Images")) {
|
||||||
|
const images = { type: "Images" };
|
||||||
|
currentNode.child = images;
|
||||||
|
currentNode = images;
|
||||||
|
currentNode.value = "<h3>" + line + "</h3>";
|
||||||
|
}
|
||||||
|
else if (line.startsWith("~") && currentNode.type != "ChangingLines") {
|
||||||
|
const changingLines = { type: "ChangingLines" };
|
||||||
|
currentNode.child = changingLines;
|
||||||
|
currentNode = changingLines;
|
||||||
|
currentNode.value = line;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentNode.value = currentNode.value + line + "<br>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
// Generate HTML from Parse Tree
|
||||||
|
function render(node) {
|
||||||
|
if (node == undefined) {
|
||||||
|
console.log("I-Ching-Broker: Rendering of nodes finished!");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
var outputHTML = "";
|
||||||
|
switch (node.type) {
|
||||||
|
case "Root":
|
||||||
|
return render(node.child);
|
||||||
|
case "Hexagram":
|
||||||
|
node.value = node.value?.replace("<h1>", "<div style=\"border: 1px dotted gray; border-radius: 10px; padding-left: 10px; padding-right: 10px; padding-top: 10px; padding-bottom: 10px; width: auto; display: inline-block; box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);\"><h1>");
|
||||||
|
node.value = node.value?.replace("</h1>", "</h1><div style=\"border: 1px solid gray; border-radius: 25px; padding-left: 30px; padding-right: 30px; padding-top: 20px; width: auto; display: inline-block; text-align: center; box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);\"><h2>");
|
||||||
|
node.value = node.value?.replace("<br>", " - ");
|
||||||
|
outputHTML = "<br><p>" + node.value + "</h2></div></p>";
|
||||||
|
outputHTML = outputHTML + render(node.child);
|
||||||
|
return outputHTML;
|
||||||
|
case "Images":
|
||||||
|
outputHTML = "<p>" + node.value + "</p></div>"; //EXTRA closing div (was opened at beginning of hexagram)
|
||||||
|
outputHTML = outputHTML + render(node.child);
|
||||||
|
return outputHTML;
|
||||||
|
case "ChangingLines":
|
||||||
|
const regex = new RegExp("~", "g");
|
||||||
|
node.value = node.value?.replace(regex, "");
|
||||||
|
outputHTML = "<br><br><p><div style=\"padding-left: 20px; padding-right: 20px; padding-top: 20px; display: inline-block; color: gray; text-align: center;\">" + node.value + "</div></p><br>";
|
||||||
|
outputHTML = outputHTML + render(node.child);
|
||||||
|
return outputHTML;
|
||||||
|
default:
|
||||||
|
outputHTML = "<p>" + node.value + "</p>";
|
||||||
|
outputHTML = outputHTML + render(node.child);
|
||||||
|
return outputHTML;
|
||||||
|
}
|
||||||
|
}
|
5
dist/backend/config.json
vendored
Normal file
5
dist/backend/config.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"emailReceiver": "test@mailhog.local",
|
||||||
|
"mailHost": "mailhog.mailhog.svc.cluster.local",
|
||||||
|
"mailPort": 1025
|
||||||
|
}
|
15
dist/backend/server.js
vendored
Normal file
15
dist/backend/server.js
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
import { executeCommand } from './broker.js';
|
||||||
|
const app = express();
|
||||||
|
const port = 8090;
|
||||||
|
app.use(cors());
|
||||||
|
app.post('/iching/api/command', (req, res) => {
|
||||||
|
//TODO no logging from inside this method???
|
||||||
|
console.log(`Backend-Server: receiving POST to /iching/api/command`);
|
||||||
|
executeCommand();
|
||||||
|
res.status(200).send('Backend-Server: Broker was called\n');
|
||||||
|
});
|
||||||
|
app.listen(port, '0.0.0.0', () => {
|
||||||
|
console.log(`Backend-Server running on http://0.0.0.0:${port}`);
|
||||||
|
});
|
1
dist/frontend/deploy.js
vendored
Normal file
1
dist/frontend/deploy.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
window.DEPLOY_MODE = 'local';
|
18
dist/frontend/event.mjs
vendored
Normal file
18
dist/frontend/event.mjs
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
"use strict";
|
||||||
|
function handleClick() {
|
||||||
|
console.log("Der Button wurde geklickt!");
|
||||||
|
//const deployMode = (window as { DEPLOY_MODE?: 'docker' | 'k8s' }).DEPLOY_MODE ?? 'k8s';
|
||||||
|
const deployMode = window.DEPLOY_MODE ?? 'k8s';
|
||||||
|
const endpoint = deployMode === 'docker' || deployMode === 'local'
|
||||||
|
? 'http://localhost:8090/iching/api/command'
|
||||||
|
: '/iching/api/command';
|
||||||
|
console.log(`DEPLOY_MODE=${deployMode}, sende POST an: ${endpoint}`);
|
||||||
|
fetch(endpoint, { method: 'POST' })
|
||||||
|
.then(res => res.text())
|
||||||
|
.then(text => console.log("HTTP-Server says:", text))
|
||||||
|
.catch(error => console.error("HTTP-Server - Error:", error));
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const button = document.getElementById("myButton");
|
||||||
|
button?.addEventListener("click", handleClick);
|
||||||
|
});
|
67
dist/frontend/index.html
vendored
Normal file
67
dist/frontend/index.html
vendored
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Your I-Ging Horoscope</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: linear-gradient(to bottom right, #fdf6e3, #e3f2fd);
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
background: white;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #3f51b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#myButton {
|
||||||
|
background-color: #3f51b5;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.8rem 1.5rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#myButton:hover {
|
||||||
|
background-color: #303f9f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 2rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Do you want to know the future?</h1>
|
||||||
|
<h2> ☝️ Look into the future with your personal I-Ging Horoscope! ☝️</h2>
|
||||||
|
<p>Click on the Button to receive your personal Horoscope. 100% accuracy guaranteed!</p>
|
||||||
|
<button id="myButton">✨ Receive Horoscope ✨</button>
|
||||||
|
<div class="footer">🧘 With Love & Fortune from the Cosmos 💫</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="./deploy.js"></script>
|
||||||
|
|
||||||
|
<script type="module" src="./event.mjs"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -22,8 +22,11 @@ npx tsc -p tsconfig.frontend.json
|
||||||
mkdir -p dist/frontend
|
mkdir -p dist/frontend
|
||||||
cp frontend/index.html dist/frontend/
|
cp frontend/index.html dist/frontend/
|
||||||
|
|
||||||
# 4. Rename compiled frontend .js → .mjs
|
mkdir -p dist/backend
|
||||||
find dist/frontend -name "*.js" -exec bash -c 'mv "$0" "${0%.js}.mjs"' {} \;
|
cp backend/config.json dist/backend/
|
||||||
|
|
||||||
|
# 4. Rename compiled frontend event.js → event.mjs
|
||||||
|
find dist/frontend -name "event.js" -exec bash -c 'mv "$0" "${0%.js}.mjs"' {} \;
|
||||||
|
|
||||||
# 5. Inject deploy mode
|
# 5. Inject deploy mode
|
||||||
echo "window.DEPLOY_MODE = 'local';" > dist/frontend/deploy.js
|
echo "window.DEPLOY_MODE = 'local';" > dist/frontend/deploy.js
|
||||||
|
|
Loading…
Reference in a new issue