diff --git a/README.md b/README.md index 6db6456..9de1b6b 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,9 @@ This app will send an I-Ching horoscope to the pre-configured mailhog instance i This app uses the I-Ching library app https://github.com/Velfi/i-ching.git. - - -## First Start -The app can be deployed by running: - -```bash -$ kubectl apply -f deployment.yaml -``` +It is also possible to configure the intervall of days between the sending of horoscopes and the time of sending. The default is to send one email every seven days at 8 am. This will start the deployment of the app in the pod, the service and an ingress for the HTTP frontend server and an ingress for the backend server. When a pod with the app is initally started, one email will be sent to the configured receiver. @@ -38,10 +31,35 @@ The backend server has a separate ingress, so API requests can get forwarded to The frontend server is running inside the pod on the address http://localhost:8080/iching. It provides an HTML homepage with a button. Upon pressing the button, the backend server will be called (to send an E-Mail). The frontend server has a separate ingress from the backend server, since the frontend server needs the 'rewrite target' annotation: So the Frontend is not running in the root folder but using the sub-path 'iching'. - \ No newline at end of file + +## Running the App + +### Locally +The app can be deployed locally by running: +The app can be deployed by running: +```bash +$ ./start-local.sh +``` +Der HTTP-Server can be access in a browser with the address http://localhost:8080/. + +### Locally with Docker +The app can be deployed by running: +```bash +$ docker run -e DEPLOY_MODE=docker -p 8080:8080 -p 8090:8090 --rm my-typescript-app +``` +Der HTTP-Server can be access in a browser with the address http://localhost:8080/. + +### In Kubernetes + +The app can be deployed by running: + +```bash +$ kubectl apply -f deployment.yaml +``` +Der HTTP-Server can be access in a browser with the address https://192-168-197-2.c-one-infra.de/iching/ diff --git a/backend/broker.test.ts b/backend/broker.test.ts index c4232d1..555a47a 100644 --- a/backend/broker.test.ts +++ b/backend/broker.test.ts @@ -1,5 +1,5 @@ -/*import { html } from "./broker"; -import { test } from "@jest/globals"; +import { html } from "./broker"; +//import { test } from "@jest/globals"; jest.useFakeTimers(); @@ -17,4 +17,3 @@ test("Generate HTML for images", () => { expect(html("Images:\nOver the earth, the lake:\nThe image of Gathering Together.\nThus the superior man renews his weapons\nIn order to meet the unforseen.")) .toBe("

Images:

Over the earth, the lake:
The image of Gathering Together.
Thus the superior man renews his weapons
In order to meet the unforseen.

"); }); -*/ \ No newline at end of file diff --git a/backend/broker.ts b/backend/broker.ts index 391235b..e672cb6 100644 --- a/backend/broker.ts +++ b/backend/broker.ts @@ -1,13 +1,15 @@ import { exec } from 'child_process'; import * as fs from 'fs'; 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 -/*interface Config { +interface Config { emailReceiver: string; mailHost: string; mailPort: number; -}*/ +} // Node structure for the Parse Tree (it's a degenerated tree with one or zero children per node) interface Node { @@ -16,13 +18,13 @@ interface Node { value?: string; } -//var config : Config; +var config : Config; export function executeCommand(): void { exec('iching divine', (error, stdout, stderr) => { - console.log(`Begin`); + console.log(`I-Ching-Broker: \'iching divine\' called`); if (error) { console.error(`Error: ${error.message}`); @@ -35,11 +37,11 @@ export function executeCommand(): void { } //Load config once - /*if (config == undefined) { + if (config == undefined) { config = loadConfig(); - } */ + } - console.log(`Send E-Mail`); + console.log(`Horoscope received; sending E-Mail next`); sendEmail(stdout); }); } @@ -48,36 +50,43 @@ export function executeCommand(): void { executeCommand(); // Load the Configuration -/*function loadConfig(): Config { +function loadConfig(): 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); -}*/ +} // Send E-Mail async function sendEmail(content: string) { // Create Transporter const transporter = nodemailer.createTransport({ - host: "mailhog.mailhog.svc.cluster.local", //config.mailHost, - port: 1025, //config.mailPort, + 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" ', - to: "test@mailhog.local", //config.emailReceiver, + 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"); + console.log("E-Mail sent: ", info.messageId + "\n\n"); } catch (error) { - console.error("Error Sending E-Mail:", error); + console.error("Error Sending E-Mail:", error + "\n\n"); + + console.log("Failed to send this horoscope: \n", content + "\n"); } } @@ -119,7 +128,7 @@ function parse(input: string): Node { const changingLines: Node = { type: "ChangingLines"}; currentNode.child = changingLines; currentNode = changingLines; - currentNode.value = line; // + "
"; TODO: try without this
+ currentNode.value = line; } else { currentNode.value = currentNode.value + line + "
"; } @@ -132,13 +141,11 @@ function parse(input: string): Node { function render(node: Node): string { if (node == undefined) { - console.log("Rendering of nodes finished!") + console.log("I-Ching-Broker: Rendering of nodes finished!") return ""; } - console.log("Render node" + node.type); - var outputHTML: string = ""; switch (node.type) { @@ -161,7 +168,6 @@ function render(node: Node): string { case "ChangingLines" : const regex = new RegExp("~", "g"); node.value = node.value?.replace(regex, ""); - //outputHTML = "

" + node.value + "


"; outputHTML = "

" + node.value + "


"; outputHTML = outputHTML + render(node.child!); return outputHTML; diff --git a/backend/server.ts b/backend/server.ts index c045e0e..4a76fc7 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -8,10 +8,13 @@ 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('Command executed\n'); + res.status(200).send('Backend-Server: Broker was called\n'); }); app.listen(port, '0.0.0.0', () => { - console.log(`Server läuft auf http://0.0.0.0:${port}`); + console.log(`Backend-Server running on http://0.0.0.0:${port}`); }); diff --git a/deployment.yaml b/deployment.yaml index e75a70c..6e77607 100644 --- a/deployment.yaml +++ b/deployment.yaml @@ -1,3 +1,11 @@ +#env: +# - name: DEPLOY_MODE +# value: "kubernetes" +# - name: BACKEND_PORT +# value: "8090" + +#--- + # --- DEPLOYMENT -------------------------------------------- apiVersion: apps/v1 diff --git a/frontend/deploy.js b/frontend/deploy.js new file mode 100644 index 0000000..12e8f02 --- /dev/null +++ b/frontend/deploy.js @@ -0,0 +1 @@ +window.DEPLOY_MODE = "k8s" \ No newline at end of file diff --git a/frontend/event.ts b/frontend/event.ts index 4a7a778..9b464a3 100644 --- a/frontend/event.ts +++ b/frontend/event.ts @@ -1,10 +1,20 @@ function handleClick(): void { console.log("Der Button wurde geklickt!"); - fetch("/iching/api/command", { method: "POST" }) + //const deployMode = (window as { DEPLOY_MODE?: 'docker' | 'k8s' }).DEPLOY_MODE ?? 'k8s'; + const deployMode = (window as { DEPLOY_MODE?: 'docker' | 'k8s' | 'local' }).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("Server sagt:", text)) - .catch(error => console.error("Fehler:", error)); + .then(text => console.log("HTTP-Server says:", text)) + .catch(error => console.error("HTTP-Server - Error:", error)); } document.addEventListener("DOMContentLoaded", () => { diff --git a/frontend/index.html b/frontend/index.html index e3a618b..9c6984a 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -53,12 +53,15 @@
-

🧘 My I-Ging Horoscope

+

Do you want to know the future?

+

☝️ Look into the future with your personal I-Ging Horoscope! ☝️

Click on the Button to receive your personal Horoscope. 100% accuracy guaranteed!

- +
+ + \ No newline at end of file diff --git a/jest.config.ts b/old.jest.config.ts similarity index 100% rename from jest.config.ts rename to old.jest.config.ts diff --git a/package.json b/package.json index d28af51..ce50417 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,9 @@ "express": "^5.1.0", "http-server": "^14.1.1", "nodemailer": "^6.10.0" - } + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node" + } } diff --git a/start-local.sh b/start-local.sh new file mode 100755 index 0000000..93cdbc5 --- /dev/null +++ b/start-local.sh @@ -0,0 +1,38 @@ + +# build +#npm install +#npx tsc -p tsconfig.backend.json +#npx tsc -p tsconfig.frontend.json + +#npm run start:local + +#!/bin/bash + +# Exit on error +set -e + +# 1. Install dependencies +npm install + +# 2. Compile backend and frontend +npx tsc -p tsconfig.backend.json +npx tsc -p tsconfig.frontend.json + +# 3. Ensure dist/frontend exists and copy index.html +mkdir -p dist/frontend +cp frontend/index.html dist/frontend/ + +mkdir -p dist/backend +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 +echo "window.DEPLOY_MODE = 'local';" > dist/frontend/deploy.js + +# 6. Start backend +node dist/backend/server.js & + +# 7. Start frontend +npx http-server dist/frontend -p 8080 --cors --mime application/javascript=js diff --git a/start.sh b/start.sh old mode 100644 new mode 100755 index a48ebb0..1efc7c5 --- a/start.sh +++ b/start.sh @@ -1,3 +1,13 @@ +#!/bin/bash + +# Setze Standard-Wert auf "k8s" wenn DEPLOY_MODE nicht gesetzt ist +DEPLOY_MODE=${DEPLOY_MODE:-k8s} + +echo "DEPLOY_MODE ist: $DEPLOY_MODE" + +# Injektiere ins Frontend +echo "window.DEPLOY_MODE = \"$DEPLOY_MODE\";" > dist/frontend/deploy.js + # start backend in the background node dist/backend/server.js & diff --git a/tsconfig.backend.json b/tsconfig.backend.json index db21beb..c5bc144 100644 --- a/tsconfig.backend.json +++ b/tsconfig.backend.json @@ -8,7 +8,6 @@ "esModuleInterop": true, "skipLibCheck": true }, - "include": [ - "backend/**/*.ts" - ] + "include": ["backend/**/*.ts"], + "exclude": ["backend/**/*.test.ts"] } \ No newline at end of file