1.4. Rust JavaScript Einbindung

async fn get_mjs() -> impl IntoResponse {
    Response::builder()
        .header("content-type", "application/javascript;charset=utf-8")
        .body(tokio::fs::read_to_string("src/index.mjs").await.unwrap())
        .unwrap()
}

Der Code definiert eine asynchrone Funktion mit dem Namen get_mjs(), die ein impl IntoResponse-Objekt zurückgibt. Der IntoResponse-Trait ermöglicht es, einen Typen in einen HTTP-Response-Typen zu konvertieren, den der Webserver senden kann.

Im professionellen betrieb sollte impl IntoResponse vermieden werden, da die Fehlerbehandlung schnell unübersichtlich werden kann. Da wir unser Projekt in kleinem Rahmen halten, ist es in Ordnung diese Methodik anzuwenden.

In der Funktion wird zuerst ein ResponseBuilder-Objekt erstellt, das eine content-type-Headerzeile mit dem MIME-Typ application/javascript und Zeichensatz utf-8 enthält. Anschließend wird der Inhalt der Datei src/index.mjs asynchron aus dem Dateisystem gelesen und als Body der HTTP-Responses festgelegt.

Schließlich wird die vollständige HTTP-Response mit unwrap() zurückgegeben. Wenn das Lesen der Datei fehlschlägt, wird eine panic!()-Meldung ausgegeben. An dieser Stelle könnte man den Quellcode durch besseres Error-Handling verbessern, da panics im Nutzergebrauch in der Regel nicht zu hilfreicher Fehlerbehandlung führen.

Das Skript in src/index.mjs zur Beschreibung:

import { h, render } from "https://unpkg.com/preact?module"; // Import Preact
import htm from "https://unpkg.com/htm?module"; // Import htm

const html = htm.bind(h);

// Render CPU Stats
function CpuStats(props) {
    return html`
        ${props.cpus.map((cpu, index) => {
            // Create a Graph for every CPU
            return html`
                <div class="full">
                    <div class="free" style="height: ${100-cpu.toFixed(2)}%"><div class="core-sign">Core ${index}</div></div>
                    <div class="busy" style="height: ${cpu}%"></div>
                    <div class="sign">${cpu.toFixed(2)}%</div>
                </div>
            `;
        })}
    `;
}

// Render Memory Usage
function MemStats(props) {
    // Create the Memory Graph
    return html`
    <div style='text-align: center; font-size: 25px;color: red;'>
        Total Memory: ${(props.memory["totalMemory"]/1000000).toFixed(0)} Mb
    </div>
    <div style='background-color:yellow; height: 50px; width: 100%'>
        <div style='background-color:green; height: 50px; width: ${(props.memory["usedMemory"]/props.memory["totalMemory"]*100).toFixed(2)}%'></div>
    </div>
    <div style='display: flex;justify-content: space-between;'>
        <div style="font-size: 25px;color: blue;">
            Used Memory: ${(props.memory["usedMemory"]/1000000).toFixed(0)} Mb
        </div>
        <div style="font-size: 25px;color: blue;">
            Free Memory: ${((props.memory["totalMemory"]-props.memory["usedMemory"])/1000000).toFixed(0)} Mb
        </div>
    </div>
    `;
}

// Update the page
let update = async () => {
    let response = await fetch("/rest/cpu");
    let cpujson = await response.json();
    render(html`<${CpuStats} cpus=${cpujson}></${CpuStats}>`, document.getElementById("cpu_stats"))

    response = await fetch("/rest/memory");
    let memoryjson = await response.json();
    render(html`<${MemStats} memory=${memoryjson}></${MemStats}>`, document.getElementById("mem_stats"))
}

update()

// Update the page every 200ms.
// This could be improved by using a sender-receiver architecture
// using a websocket.
// Unfortunately, time is precious in this project and this method will do.
setInterval(update, 200);

Dieser Code verwendet die Bibliotheken preact und htm und definiert eine Funktionen CpuStats und MemStats, die eine Liste von Memory- und CPU-Auslastungswerten als Eingabe erhält und eine HTML-Darstellung dieser Werte erstellt. Die Funktion update ruft die REST endpunkte vom Server ab und benutztz die eben genannten Funktionen, um die HTML-Darstellung zu erstellen. Die setInterval -Funktion ruft die “update” -Funktion alle 200 Millisekunden auf, um die CPU-Auslastung zu aktualisieren. Die “console.log” -Anweisung dient lediglich zur Ausgabe einer Meldung in der Browserkonsole.

Wie im Code Beispiel schon genannt, ist setInterval nicht die optimale Lösung in dieser situation, da die Werte von Hand abgefragt werden müssen. Eine elegantere Methode wäre die Verwendung eines Web Sockets, der die Seite wenn neue CPU Werte vorliegen, aktualisiert.

Die HTML Seite zum eben genannten Javascript code ist kurz und beinhaltet nur die statischen Elemente, wie Überschrift, Hilfetext und Header.

<!DOCTYPE html>
<html lang="de">
<head>
    <title>Webserver</title>
    <script type="module" src="/index.mjs"></script>
    <link rel="stylesheet" href="/styles.css">
</head>
<body>
    <h1 class="title">Computer Statistics</h1>
    <div style="font-size:23px">
        <p style="font-size:23px">On this page you can get up-to-date information about the performance of your system.
        <br> Use this information to monitor performance and resources.</p>
    </div>
    <h2>CPU</h2>
    <div class="container" id="cpu_stats">
    </div>
    <div style="margin-top:100px;">
        <h2>Memory</h2>
        <div id="mem_stats">
        </div>
    </div>
</body>
</html>