Line data Source code
1 : // SPDX-License-Identifier: LGPL-2.1-or-later
2 4 : import cockpit from "cockpit";
3 :
4 : import { Channel } from '../lib/cockpit/channel';
5 :
6 : import '../lib/patternfly/patternfly-6-cockpit.scss';
7 : import "../../node_modules/@patternfly/patternfly/components/Button/button.css";
8 : import "../../node_modules/@patternfly/patternfly/components/Page/page.css";
9 :
10 4 : document.addEventListener("DOMContentLoaded", () => {
11 0 : document.getElementById("hammer").addEventListener("click", e => e.target.setAttribute("hidden", "hidden"));
12 :
13 1 : document.querySelector(".cockpit-internal-reauthorize .pf-v6-c-button").addEventListener("click", () => {
14 1 : document.querySelector(".cockpit-internal-reauthorize span").textContent = "checking...";
15 1 : cockpit.script("pkcheck --action-id org.freedesktop.policykit.exec --process $$ -u 2>&1", { superuser: "try" })
16 1 : .stream(data => console.debug(data))
17 1 : .then(() => {
18 1 : document.querySelector(".cockpit-internal-reauthorize span").textContent = "result: authorized";
19 1 : })
20 1 : .catch(() => {
21 1 : document.querySelector(".cockpit-internal-reauthorize span").textContent = "result: access-denied";
22 1 : });
23 1 : });
24 :
25 3 : document.querySelector(".super-channel .pf-v6-c-button").addEventListener("click", () => {
26 3 : document.querySelector(".super-channel span").textContent = "checking...";
27 3 : cockpit.spawn(["id"], { superuser: "require" })
28 2 : .then(data => {
29 2 : console.log("done");
30 2 : document.querySelector(".super-channel span").textContent = "result: " + data;
31 2 : })
32 1 : .catch(ex => {
33 1 : console.log("fail");
34 1 : document.querySelector(".super-channel span").textContent = "result: " + ex.problem;
35 1 : });
36 3 : });
37 :
38 1 : document.querySelector(".lock-channel .pf-v6-c-button").addEventListener("click", () => {
39 1 : document.querySelector(".lock-channel span").textContent = "locking...";
40 1 : cockpit.spawn(["flock", "-o", "/tmp/playground-test-lock", "-c", "echo locked; sleep infinity"],
41 1 : { superuser: "try", err: "message" })
42 1 : .stream(data => {
43 1 : document.querySelector(".lock-channel span").textContent = data;
44 1 : })
45 1 : .catch(ex => {
46 1 : document.querySelector(".lock-channel span").textContent = "failed: " + ex.toString();
47 1 : });
48 1 : });
49 :
50 4 : function update_nav() {
51 4 : document.getElementById("nav").textContent = '';
52 4 : const path = ["top"].concat(cockpit.location.path);
53 4 : const e_nav = document.getElementById("nav");
54 4 : path.forEach((p, i) => {
55 1 : if (i < path.length - 1) {
56 1 : const e_link = document.createElement("a");
57 1 : e_link.setAttribute("tabindex", "0");
58 1 : e_link.textContent = p;
59 0 : e_link.addEventListener("click", () => cockpit.location.go(path.slice(1, i + 1)));
60 1 : e_nav.append(e_link, " >> ");
61 1 : } else {
62 4 : const e_span = document.createElement("span");
63 4 : e_span.textContent = p;
64 4 : e_nav.appendChild(e_span);
65 4 : }
66 4 : });
67 4 : }
68 :
69 4 : cockpit.addEventListener('locationchanged', update_nav);
70 4 : update_nav();
71 :
72 0 : document.getElementById('go-down').addEventListener("click", () => {
73 0 : const len = cockpit.location.path.length;
74 0 : cockpit.location.go(cockpit.location.path.concat(len.toString()), { length: len.toString() });
75 0 : });
76 :
77 4 : const counter = cockpit.file("/tmp/counter", { syntax: JSON });
78 :
79 4 : function normalize_counter(obj) {
80 4 : obj = obj || { };
81 4 : obj.counter = obj.counter || 0;
82 4 : return obj;
83 4 : }
84 :
85 0 : function complain(error) {
86 0 : document.getElementById('file-error').textContent = error.toString();
87 0 : }
88 :
89 4 : function changed(content, tag, error) {
90 4 : if (error)
91 1 : return complain(error);
92 4 : document.getElementById('file-content').textContent = normalize_counter(content).counter;
93 4 : document.getElementById('file-error').textContent = "";
94 4 : }
95 :
96 4 : counter.watch(changed);
97 :
98 0 : document.getElementById('modify-file').addEventListener("click", () => {
99 0 : counter
100 0 : .modify(obj => {
101 0 : obj = normalize_counter(obj);
102 0 : obj.counter += 1;
103 0 : return obj;
104 0 : })
105 0 : .catch(complain);
106 0 : });
107 :
108 4 : function load_file() {
109 4 : cockpit.file("/tmp/counter").read()
110 4 : .then(content => {
111 4 : document.getElementById('edit-file').value = content;
112 4 : });
113 4 : }
114 :
115 0 : function save_file() {
116 0 : cockpit.file("/tmp/counter").replace(document.getElementById('edit-file').value);
117 0 : }
118 :
119 4 : document.getElementById('load-file').addEventListener("click", load_file);
120 4 : document.getElementById('save-file').addEventListener("click", save_file);
121 4 : load_file();
122 :
123 0 : document.getElementById('delete-file').addEventListener("click", () => cockpit.spawn(["rm", "-f", "/tmp/counter"]));
124 :
125 4 : document.body.removeAttribute("hidden");
126 :
127 4 : function show_hidden() {
128 1 : document.getElementById("hidden").textContent = cockpit.hidden ? "hidden" : "visible";
129 4 : }
130 :
131 4 : cockpit.user().then(info => {
132 4 : console.log(info);
133 4 : document.getElementById("user-info").textContent = JSON.stringify(info);
134 4 : });
135 :
136 4 : cockpit.addEventListener("visibilitychange", show_hidden);
137 4 : show_hidden();
138 :
139 : // HACK: The user/group/mode options are not part yet of the Cockpit File API so
140 : // we resort to creating our own channel here. We can't use the new `Channel`
141 : // API as importing it with cockpit leads to the wrong cockpit.Channel being
142 : // used instead of the new class.
143 0 : const replace = (filename, content, tag, attrs) => {
144 0 : const channel = new Channel({ payload: "fsreplace1", superuser: 'try', path: filename, tag, attrs });
145 0 : channel.wait();
146 :
147 0 : return new Promise((resolve, reject) => {
148 0 : channel.on('close', message => {
149 0 : if (message.problem) {
150 0 : reject(message);
151 0 : } else {
152 0 : resolve();
153 0 : }
154 0 : });
155 :
156 0 : channel.send_data(content);
157 0 : channel.send_control({ command: 'done' });
158 0 : });
159 0 : };
160 :
161 4 : const fsreplace_btn = document.getElementById("fsreplace1-create");
162 4 : const fsreplace_error = document.getElementById("fsreplace1-error");
163 0 : fsreplace_btn.addEventListener("click", e => {
164 0 : fsreplace_btn.disabled = true;
165 0 : fsreplace_error.textContent = '';
166 0 : const filename = document.getElementById("fsreplace1-filename").value;
167 0 : const content = document.getElementById("fsreplace1-content").value;
168 0 : const use_tag = document.getElementById("fsreplace1-use-tag").checked;
169 0 : const file = cockpit.file(filename, { superuser: "try" });
170 0 : const attrs = { };
171 0 : for (const field of ["user", "group", "mode"]) {
172 0 : const val = document.getElementById(`fsreplace1-${field}`).value;
173 0 : if (!val)
174 0 : continue;
175 :
176 0 : attrs[field] = val;
177 0 : }
178 :
179 0 : if ('mode' in attrs)
180 0 : attrs.mode = Number.parseInt(attrs.mode);
181 :
182 0 : file.read().then((_content, tag) => {
183 0 : replace(filename, content, use_tag ? tag : undefined, attrs).catch(exc => {
184 0 : fsreplace_error.textContent = cockpit.message(exc);
185 0 : })
186 0 : .finally(() => {
187 0 : fsreplace_btn.disabled = false;
188 0 : });
189 0 : });
190 0 : });
191 4 : });
|