V minulém díle jsme hovořili o cockpitu jako technologii pro snadnější správu serveru. Díky cockpit.js máte přístup na souborový systém serveru a dalším zdrojů. Dnes si takový plugin vytvoříme pomocí trochy HTML a Javascriptu, takže to zvládné každý průměrný webový vývojář s trochou znalosti Linuxu.

Pluginy sídlí v ~/.local/share/cockpit/ nebo v /usr/share/cockpit. My použijeme tu první složku. Pluginy jsou vlastně webové stránky, které mají vložené speciální javascriptový soubor. Můžete pro vývoj použít standardní nástroje (HTML, JS, CSS), nebo šáhnout po frameworcích, jako je třeba React. Aplikace používají grafickou knihovnu patternfly.

Začínáme

Založíme novou složku v adresáři a vytvoříme základní adresářovou strukturu.

# Založíme složku
mkdir ~/.local/share/cockpit/hello-world/

Vytvoříme manifest.json s obsahem popisující aplikaci, její kompatibilitu a další metadata.

{
    "requires": {
        "cockpit": "137"
    },

    "tools": {
        "index": {
            "label": "Hello World"
        }
    }
}

A vytvoříme triviální webovou stránku index.html pro zobrazení našeho pluginu.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Můj první plugin</title>
</head>
<body>
    <h1>Hello World</h1>
</body>
</html>

Pokud cockpit opravdu běží (systemctl start cockpit.socket), otevřete si webový prohlížeč na stránce https://localhost:9090 a přihlašte se svým uživatelským jménem a heslem. Objevuje se v levé části panelu s popiskem (zde Hello World) definovaným v manifest.json.

Cockpit.js

Zobrazit webovou stránku uvnitř je jistě zajímavé, ale stále nejde o nic sofistikované. To pravé přináší JS knihovna cockpit.js. Vkládá se z nadřazeného adresáře do aplikace pomocí <script src="../base1/cockpit.js">.

<!-- Zkráceno ... -->
<h1>Hello World</h1>
<div id="hostname">Loading ... </div>
</body>
<script src="../base1/cockpit.js"></script>
<script src="script.js"></script>
</html>

Teď máme vložený Cockpit.js a můžeme začít psát vlastní skript s využitím nabízeného API, co umí se můžete dočíst v dokumentaci. Jednoduchý příklad nám ukáže obsah souboru /etc/hostname. Vytvoříme obsah souboru script.js

cockpit.file('/etc/hostname').read()
  .then((content, tag) => {
        document.querySelector("#hostname").innerText = content;
  })
  .catch(error => {
        console.error(error);
  });

Všimněte si, že Cockpit.js používá Promise pro komunikaci o tom jak to přesně funguje se podívejte do dokumentace. Zjednodušeně jde o to, že funkce nevrací hodnoty, ale jakýsi příslib budoucích zisků no a vy můžete jejich výsledek zjistit kdy chcete pomocí metody .then a když by se stala po cestě nějaká chyba, zachytíte jí v metodě .catch.

Čteme Mojefedora.cz

Přestože určitě existuje spousta dobrých nápadů na projekty, rozhodli jsme se udělat jednoduchou čtečku RSS našeho portálu. Okomentovaný celý zdrojový kód najdete v mém gitlab repozitáři. Zde jenom upozorním na některé zajímavé části.

Slavný výsledek

Slavný výsledek

Vytvoření vzdáleného sezení pro komunikaci přes HTTPS. S objektem tohoto sezení můžeme začít získávat vzdálené soubory.

const session = cockpit.http(443, {address: "mojefedora.cz", tls: {}});
// Cockpit pracuje pomocí "Promise"
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
// Proto nevěřte žádnému, protože pořád jenom slibují. 
session.get("/feed/").then(data => {

Načtená data předávám funkci, která se bude starat o přečtení toho XML feedu. I na parsování XML máme v JS objekt, nicméně bohužel některé aspekty jsou trochu problematické. Sestavení objektu s články naštestí není tak těžké.

parser = new DOMParser();
    xmlDoc = parser.parseFromString(feed, "text/xml");
    console.log(xmlDoc);
    // Najdeme si číslo posledního sestavení RSS
    const lastBuild = new Date(xmlDoc.querySelector("lastBuildDate").textContent);
    // Projdeme nabídnuté články a sestavíme z nich nějaké objekty
    const articles = [];
    // Z článku si sestavíme objekty, které pak prostě nějak necháme vyrenderovat. :-)
    xmlDoc.querySelectorAll("rss channel item").forEach(articleData => {
        const article = {
            title: articleData.querySelector("title").textContent,
            // querySelector nemá rád dvojtečky. Takže je musíte escapovat.
            // No a protože se mi to hned nepovedlo, tak ho opustím a použiji jinou funkci
            creator: articleData.getElementsByTagName("dc:creator")[0].textContent,
            comments: articleData.getElementsByTagName("slash:comments")[0].textContent,
            link: articleData.querySelector("link").textContent,
            comments_link: articleData.querySelector("comments").textContent,
            pub_date: new Date(articleData.querySelector("pubDate").textContent),
            // V popisku dostanete i odkaz na článek, ale nebude fungovat, protože cockpit odkazy, které nevedou do nové záložky blokuje :-(
            // To by šlo vyřešit doplnění _blank do všech odkazů, ale to už je nad rámec našeho článku. 
            description: articleData.querySelector("description").textContent,
        }
        articles.push(article);
    });

Články renderujeme pomocí jedné funkce. Má to trochu háčky a pokud bychom do RSS propašovali nějaký ošklivý obsah, mohli bychom Váš systém teoreticky napadnout :-).

function renderMojefedoraArticle(article) {
    // jeden parametr, clanek z mojefedora.cz
    // a vrati HTML reprezentaci toho clanku.
    // Zranitelné pomocí XSS.
    return `<article>
        <h2><a target="_blank" href="${article.link}">${article.title}</a></h2>
        <div class="creator">Autor: ${article.creator}</div>
        <div class="pubDate">Vydáno: ${article.pub_date.toLocaleDateString()} ${article.pub_date.toLocaleTimeString()}</div>
        <div class="comments"><a href="${article.comments_link}">Komentářů: ${article.comments}</a></div>
        <div class="description">${article.description}</div>
    </article>`
}

Připomínám, že kód výše není kompletní a určitě obsahuje chyby. Jestli na nějakou narazíte, nebo chcete pomoci, napište nám komentář, nebo ideálně si přijďte popovídat do naší Telegram skupiny. Těšíme se na spoustu pluginů, které vzniknou a k tomu se vám bude určitě hodit oficiální dokumentace nebo příklad s využitím Reactu.