goosebit 0.1.2__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- goosebit/__init__.py +50 -19
- goosebit/__main__.py +7 -0
- goosebit/api/responses.py +5 -0
- goosebit/api/routes.py +5 -15
- goosebit/api/telemetry/__init__.py +1 -0
- goosebit/{telemetry/__init__.py → api/telemetry/metrics.py} +9 -3
- goosebit/api/telemetry/prometheus/__init__.py +2 -0
- goosebit/api/telemetry/prometheus/readers.py +3 -0
- goosebit/api/telemetry/prometheus/routes.py +18 -0
- goosebit/api/telemetry/routes.py +9 -0
- goosebit/api/v1/__init__.py +1 -0
- goosebit/api/v1/devices/__init__.py +1 -0
- goosebit/api/v1/devices/device/__init__.py +1 -0
- goosebit/api/v1/devices/device/responses.py +13 -0
- goosebit/api/v1/devices/device/routes.py +27 -0
- goosebit/api/v1/devices/requests.py +7 -0
- goosebit/api/v1/devices/responses.py +16 -0
- goosebit/api/v1/devices/routes.py +35 -0
- goosebit/api/v1/download/__init__.py +1 -0
- goosebit/api/v1/download/routes.py +22 -0
- goosebit/api/v1/rollouts/__init__.py +1 -0
- goosebit/api/v1/rollouts/requests.py +16 -0
- goosebit/api/v1/rollouts/responses.py +19 -0
- goosebit/api/v1/rollouts/routes.py +50 -0
- goosebit/api/v1/routes.py +9 -0
- goosebit/api/v1/software/__init__.py +1 -0
- goosebit/api/v1/software/requests.py +5 -0
- goosebit/api/v1/software/responses.py +16 -0
- goosebit/api/v1/software/routes.py +77 -0
- goosebit/auth/__init__.py +101 -101
- goosebit/db/__init__.py +11 -0
- goosebit/db/config.py +10 -0
- goosebit/db/migrations/models/0_20240830054046_init.py +136 -0
- goosebit/{models.py → db/models.py} +17 -10
- goosebit/realtime/logs.py +4 -3
- goosebit/realtime/routes.py +2 -2
- goosebit/schema/__init__.py +0 -0
- goosebit/schema/devices.py +73 -0
- goosebit/schema/rollouts.py +31 -0
- goosebit/schema/software.py +37 -0
- goosebit/settings/__init__.py +17 -0
- goosebit/settings/const.py +21 -0
- goosebit/settings/schema.py +86 -0
- goosebit/ui/bff/__init__.py +1 -0
- goosebit/ui/bff/devices/__init__.py +1 -0
- goosebit/ui/bff/devices/requests.py +12 -0
- goosebit/ui/bff/devices/responses.py +39 -0
- goosebit/ui/bff/devices/routes.py +72 -0
- goosebit/ui/bff/download/__init__.py +1 -0
- goosebit/ui/bff/download/routes.py +22 -0
- goosebit/ui/bff/rollouts/__init__.py +1 -0
- goosebit/ui/bff/rollouts/responses.py +37 -0
- goosebit/ui/bff/rollouts/routes.py +52 -0
- goosebit/ui/bff/routes.py +11 -0
- goosebit/ui/bff/software/__init__.py +1 -0
- goosebit/ui/bff/software/responses.py +37 -0
- goosebit/ui/bff/software/routes.py +83 -0
- goosebit/ui/nav.py +16 -0
- goosebit/ui/routes.py +29 -66
- goosebit/ui/static/favicon.ico +0 -0
- goosebit/ui/static/favicon.svg +1 -1
- goosebit/ui/static/js/devices.js +47 -71
- goosebit/ui/static/js/index.js +4 -9
- goosebit/ui/static/js/login.js +23 -0
- goosebit/ui/static/js/logs.js +1 -1
- goosebit/ui/static/js/rollouts.js +33 -19
- goosebit/ui/static/js/{firmware.js → software.js} +87 -86
- goosebit/ui/static/js/util.js +60 -6
- goosebit/ui/static/svg/goosebit-logo.svg +1 -1
- goosebit/ui/templates/__init__.py +9 -1
- goosebit/ui/templates/devices.html.jinja +75 -0
- goosebit/ui/templates/index.html.jinja +25 -0
- goosebit/ui/templates/login.html.jinja +57 -0
- goosebit/ui/templates/logs.html.jinja +31 -0
- goosebit/ui/templates/nav.html.jinja +84 -0
- goosebit/ui/templates/rollouts.html.jinja +93 -0
- goosebit/ui/templates/software.html.jinja +139 -0
- goosebit/updater/controller/v1/routes.py +101 -96
- goosebit/updater/controller/v1/schema.py +56 -0
- goosebit/updater/manager.py +65 -65
- goosebit/updater/routes.py +3 -11
- goosebit/updates/__init__.py +91 -32
- goosebit/updates/swdesc.py +2 -7
- goosebit-0.2.1.dist-info/METADATA +173 -0
- goosebit-0.2.1.dist-info/RECORD +95 -0
- goosebit/api/devices.py +0 -136
- goosebit/api/download.py +0 -34
- goosebit/api/firmware.py +0 -57
- goosebit/api/helper.py +0 -30
- goosebit/api/rollouts.py +0 -87
- goosebit/db.py +0 -37
- goosebit/permissions.py +0 -75
- goosebit/settings.py +0 -64
- goosebit/telemetry/prometheus.py +0 -10
- goosebit/ui/templates/devices.html +0 -115
- goosebit/ui/templates/firmware.html +0 -163
- goosebit/ui/templates/index.html +0 -23
- goosebit/ui/templates/login.html +0 -65
- goosebit/ui/templates/logs.html +0 -36
- goosebit/ui/templates/nav.html +0 -117
- goosebit/ui/templates/rollouts.html +0 -76
- goosebit-0.1.2.dist-info/METADATA +0 -123
- goosebit-0.1.2.dist-info/RECORD +0 -51
- {goosebit-0.1.2.dist-info → goosebit-0.2.1.dist-info}/LICENSE +0 -0
- {goosebit-0.1.2.dist-info → goosebit-0.2.1.dist-info}/WHEEL +0 -0
@@ -12,6 +12,9 @@ uploadForm.addEventListener("submit", async (e) => {
|
|
12
12
|
});
|
13
13
|
|
14
14
|
async function sendFileChunks(file) {
|
15
|
+
const alerts = document.getElementById("upload-alerts");
|
16
|
+
alerts.innerHTML = "";
|
17
|
+
|
15
18
|
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
|
16
19
|
let start = 0;
|
17
20
|
let uploadedChunks = 0;
|
@@ -39,7 +42,7 @@ async function sendFileChunks(file) {
|
|
39
42
|
formData.append("done", "false");
|
40
43
|
}
|
41
44
|
|
42
|
-
const response = await fetch("/ui/
|
45
|
+
const response = await fetch("/ui/bff/software", {
|
43
46
|
method: "POST",
|
44
47
|
body: formData,
|
45
48
|
});
|
@@ -50,9 +53,8 @@ async function sendFileChunks(file) {
|
|
50
53
|
uploadProgressBar.style.width = `${progress}%`;
|
51
54
|
uploadProgressBar.innerHTML = `${Math.round(progress)}%`;
|
52
55
|
} else {
|
53
|
-
if (response.status
|
56
|
+
if (response.status >= 400 && response.status < 500) {
|
54
57
|
const result = await response.json();
|
55
|
-
const alerts = document.getElementById("upload-alerts");
|
56
58
|
alerts.innerHTML = `<div class="alert alert-warning alert-dismissible fade show" role="alert">
|
57
59
|
${result.detail}
|
58
60
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
@@ -78,18 +80,26 @@ urlForm.addEventListener("submit", async (e) => {
|
|
78
80
|
});
|
79
81
|
|
80
82
|
async function sendFileUrl(url) {
|
83
|
+
const alerts = document.getElementById("url-alerts");
|
84
|
+
alerts.innerHTML = "";
|
85
|
+
|
81
86
|
const formData = new FormData();
|
82
87
|
formData.append("url", url);
|
83
88
|
|
84
|
-
const response = await fetch("/ui/
|
89
|
+
const response = await fetch("/ui/bff/software", {
|
85
90
|
method: "POST",
|
86
91
|
body: formData,
|
87
92
|
});
|
88
93
|
|
89
|
-
if (
|
90
|
-
|
94
|
+
if (response.ok) {
|
95
|
+
alerts.innerHTML = `<div class="alert alert-success alert-dismissible fade show" role="alert">
|
96
|
+
Software creation (or replacement) successful
|
97
|
+
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
98
|
+
</div>`;
|
99
|
+
updateSoftwareList();
|
100
|
+
} else {
|
101
|
+
if (response.status >= 400 && response.status < 500) {
|
91
102
|
const result = await response.json();
|
92
|
-
const alerts = document.getElementById("url-alerts");
|
93
103
|
alerts.innerHTML = `<div class="alert alert-warning alert-dismissible fade show" role="alert">
|
94
104
|
${result.detail}
|
95
105
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
@@ -98,7 +108,7 @@ async function sendFileUrl(url) {
|
|
98
108
|
}
|
99
109
|
}
|
100
110
|
|
101
|
-
function
|
111
|
+
function updateSoftwareList() {
|
102
112
|
dataTable.ajax.reload(null, false);
|
103
113
|
}
|
104
114
|
|
@@ -112,11 +122,52 @@ function resetProgress() {
|
|
112
122
|
uploadProgressBar.innerHTML = "0%";
|
113
123
|
uploadProgressBar.parentElement.classList.add("d-none");
|
114
124
|
|
115
|
-
|
125
|
+
updateSoftwareList();
|
116
126
|
}
|
117
127
|
|
118
128
|
document.addEventListener("DOMContentLoaded", () => {
|
119
|
-
|
129
|
+
const buttons = [
|
130
|
+
{
|
131
|
+
text: '<i class="bi bi-cloud-download" ></i>',
|
132
|
+
action: (e, dt) => {
|
133
|
+
const selectedSoftware = dt
|
134
|
+
.rows({ selected: true })
|
135
|
+
.data()
|
136
|
+
.toArray()
|
137
|
+
.map((d) => d.id);
|
138
|
+
downloadSoftware(selectedSoftware[0]);
|
139
|
+
},
|
140
|
+
className: "buttons-download",
|
141
|
+
titleAttr: "Download Software",
|
142
|
+
},
|
143
|
+
{
|
144
|
+
text: '<i class="bi bi-trash" ></i>',
|
145
|
+
action: async (e, dt) => {
|
146
|
+
const selectedSoftware = dt
|
147
|
+
.rows({ selected: true })
|
148
|
+
.data()
|
149
|
+
.toArray()
|
150
|
+
.map((d) => d.id);
|
151
|
+
await deleteSoftware(selectedSoftware);
|
152
|
+
},
|
153
|
+
className: "buttons-delete",
|
154
|
+
titleAttr: "Delete Software",
|
155
|
+
},
|
156
|
+
];
|
157
|
+
|
158
|
+
// add create button at the beginning if upload modal exists
|
159
|
+
if ($("#upload-modal").length > 0) {
|
160
|
+
buttons.unshift({
|
161
|
+
text: '<i class="bi bi-plus" ></i>',
|
162
|
+
action: () => {
|
163
|
+
new bootstrap.Modal("#upload-modal").show();
|
164
|
+
},
|
165
|
+
className: "buttons-create",
|
166
|
+
titleAttr: "Add Software",
|
167
|
+
});
|
168
|
+
}
|
169
|
+
|
170
|
+
dataTable = new DataTable("#software-table", {
|
120
171
|
responsive: true,
|
121
172
|
paging: true,
|
122
173
|
processing: false,
|
@@ -136,7 +187,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
136
187
|
}
|
137
188
|
},
|
138
189
|
ajax: {
|
139
|
-
url: "/
|
190
|
+
url: "/ui/bff/software",
|
140
191
|
contentType: "application/json",
|
141
192
|
},
|
142
193
|
initComplete: () => {
|
@@ -152,8 +203,24 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
152
203
|
],
|
153
204
|
columns: [
|
154
205
|
{ data: "id", visible: false },
|
155
|
-
{ data: "name"
|
206
|
+
{ data: "name" },
|
156
207
|
{ data: "version", searchable: true, orderable: true },
|
208
|
+
{
|
209
|
+
data: "compatibility",
|
210
|
+
render: (data) => {
|
211
|
+
const result = data.reduce((acc, { model, revision }) => {
|
212
|
+
if (!acc[model]) {
|
213
|
+
acc[model] = [];
|
214
|
+
}
|
215
|
+
acc[model].push(revision);
|
216
|
+
return acc;
|
217
|
+
}, {});
|
218
|
+
|
219
|
+
return Object.entries(result)
|
220
|
+
.map(([model, revision]) => `${model} - ${revision.join(", ")}`)
|
221
|
+
.join("\n");
|
222
|
+
},
|
223
|
+
},
|
157
224
|
{
|
158
225
|
data: "size",
|
159
226
|
render: (data, type) => {
|
@@ -168,34 +235,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
168
235
|
rowId: "id",
|
169
236
|
layout: {
|
170
237
|
bottom1Start: {
|
171
|
-
buttons
|
172
|
-
{
|
173
|
-
text: '<i class="bi bi-cloud-download" ></i>',
|
174
|
-
action: (e, dt) => {
|
175
|
-
const selectedFirmware = dt
|
176
|
-
.rows({ selected: true })
|
177
|
-
.data()
|
178
|
-
.toArray()
|
179
|
-
.map((d) => d.id);
|
180
|
-
downloadFirmware(selectedFirmware[0]);
|
181
|
-
},
|
182
|
-
className: "buttons-download",
|
183
|
-
titleAttr: "Download Firmware",
|
184
|
-
},
|
185
|
-
{
|
186
|
-
text: '<i class="bi bi-trash" ></i>',
|
187
|
-
action: async (e, dt) => {
|
188
|
-
const selectedFirmware = dt
|
189
|
-
.rows({ selected: true })
|
190
|
-
.data()
|
191
|
-
.toArray()
|
192
|
-
.map((d) => d.id);
|
193
|
-
await deleteFirmware(selectedFirmware);
|
194
|
-
},
|
195
|
-
className: "buttons-delete",
|
196
|
-
titleAttr: "Delete Firmware",
|
197
|
-
},
|
198
|
-
],
|
238
|
+
buttons,
|
199
239
|
},
|
200
240
|
},
|
201
241
|
});
|
@@ -208,46 +248,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
208
248
|
updateBtnState();
|
209
249
|
});
|
210
250
|
|
211
|
-
|
212
|
-
$(() => {
|
213
|
-
$('[data-toggle="tooltip"]').tooltip();
|
214
|
-
});
|
215
|
-
|
216
|
-
$("#firmware-table tbody")
|
217
|
-
.on("mouseenter", "tr", function () {
|
218
|
-
const rowData = dataTable.row(this).data();
|
219
|
-
const compat = rowData.compatibility;
|
220
|
-
let tooltipText = "";
|
221
|
-
if (compat) {
|
222
|
-
const result = compat.reduce((acc, { model, revision }) => {
|
223
|
-
if (!acc[model]) {
|
224
|
-
acc[model] = [];
|
225
|
-
}
|
226
|
-
acc[model].push(revision);
|
227
|
-
return acc;
|
228
|
-
}, {});
|
229
|
-
|
230
|
-
tooltipText = Object.entries(result)
|
231
|
-
.map(([model, revision]) => `<b>${model}</b> [${revision.join(", ")}]`)
|
232
|
-
.join(", ");
|
233
|
-
}
|
234
|
-
|
235
|
-
// Initialize Bootstrap tooltip
|
236
|
-
$(this)
|
237
|
-
.attr("title", tooltipText)
|
238
|
-
.tooltip({
|
239
|
-
placement: "top",
|
240
|
-
trigger: "hover",
|
241
|
-
container: "body",
|
242
|
-
html: true,
|
243
|
-
})
|
244
|
-
.tooltip("show");
|
245
|
-
})
|
246
|
-
.on("mouseleave", "tr", function () {
|
247
|
-
$(this).tooltip("dispose");
|
248
|
-
});
|
249
|
-
|
250
|
-
updateFirmwareList();
|
251
|
+
updateSoftwareList();
|
251
252
|
});
|
252
253
|
|
253
254
|
function updateBtnState() {
|
@@ -263,15 +264,15 @@ function updateBtnState() {
|
|
263
264
|
}
|
264
265
|
}
|
265
266
|
|
266
|
-
async function
|
267
|
+
async function deleteSoftware(software_ids) {
|
267
268
|
try {
|
268
|
-
await
|
269
|
-
|
269
|
+
await delete_request("/ui/bff/software", { software_ids });
|
270
|
+
updateSoftwareList();
|
270
271
|
} catch (error) {
|
271
|
-
console.error("Deleting
|
272
|
+
console.error("Deleting software list failed:", error);
|
272
273
|
}
|
273
274
|
}
|
274
275
|
|
275
|
-
function
|
276
|
-
window.location.href = `/
|
276
|
+
function downloadSoftware(file) {
|
277
|
+
window.location.href = `/ui/bff/download/${file}`;
|
277
278
|
}
|
goosebit/ui/static/js/util.js
CHANGED
@@ -20,15 +20,15 @@ function secondsToRecentDate(t) {
|
|
20
20
|
return s + (s === 1 ? " second" : " seconds");
|
21
21
|
}
|
22
22
|
|
23
|
-
async function
|
23
|
+
async function updateSoftwareSelection(addSpecialMode = false) {
|
24
24
|
try {
|
25
|
-
const response = await fetch("/
|
25
|
+
const response = await fetch("/ui/bff/software");
|
26
26
|
if (!response.ok) {
|
27
|
-
console.error("Retrieving
|
27
|
+
console.error("Retrieving software list failed.");
|
28
28
|
return;
|
29
29
|
}
|
30
30
|
const data = (await response.json()).data;
|
31
|
-
const selectElem = document.getElementById("selected-
|
31
|
+
const selectElem = document.getElementById("selected-sw");
|
32
32
|
|
33
33
|
if (addSpecialMode) {
|
34
34
|
let optionElem = document.createElement("option");
|
@@ -45,7 +45,9 @@ async function updateFirmwareSelection(addSpecialMode = false) {
|
|
45
45
|
for (const item of data) {
|
46
46
|
const optionElem = document.createElement("option");
|
47
47
|
optionElem.value = item.id;
|
48
|
-
optionElem.textContent = item.
|
48
|
+
optionElem.textContent = `${item.version}`;
|
49
|
+
const models = [...new Set(item.compatibility.map((item) => item.model))];
|
50
|
+
optionElem.textContent = `${item.version} (${models})`;
|
49
51
|
selectElem.appendChild(optionElem);
|
50
52
|
}
|
51
53
|
} catch (error) {
|
@@ -53,7 +55,7 @@ async function updateFirmwareSelection(addSpecialMode = false) {
|
|
53
55
|
}
|
54
56
|
}
|
55
57
|
|
56
|
-
async function
|
58
|
+
async function post_request(url, object) {
|
57
59
|
const response = await fetch(url, {
|
58
60
|
method: "POST",
|
59
61
|
headers: { "Content-Type": "application/json" },
|
@@ -61,6 +63,58 @@ async function post(url, object) {
|
|
61
63
|
});
|
62
64
|
|
63
65
|
if (!response.ok) {
|
66
|
+
const result = await response.json();
|
67
|
+
if (result.detail) {
|
68
|
+
Swal.fire({
|
69
|
+
title: "Warning",
|
70
|
+
text: result.detail,
|
71
|
+
icon: "warning",
|
72
|
+
confirmButtonText: "Understood",
|
73
|
+
});
|
74
|
+
}
|
75
|
+
|
76
|
+
throw new Error(`POST ${url} failed for ${JSON.stringify(object)}`);
|
77
|
+
}
|
78
|
+
}
|
79
|
+
async function patch_request(url, object) {
|
80
|
+
const response = await fetch(url, {
|
81
|
+
method: "PATCH",
|
82
|
+
headers: { "Content-Type": "application/json" },
|
83
|
+
body: JSON.stringify(object),
|
84
|
+
});
|
85
|
+
|
86
|
+
if (!response.ok) {
|
87
|
+
const result = await response.json();
|
88
|
+
if (result.detail) {
|
89
|
+
Swal.fire({
|
90
|
+
title: "Warning",
|
91
|
+
text: result.detail,
|
92
|
+
icon: "warning",
|
93
|
+
confirmButtonText: "Understood",
|
94
|
+
});
|
95
|
+
}
|
96
|
+
|
97
|
+
throw new Error(`POST ${url} failed for ${JSON.stringify(object)}`);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
async function delete_request(url, object) {
|
101
|
+
const response = await fetch(url, {
|
102
|
+
method: "DELETE",
|
103
|
+
headers: { "Content-Type": "application/json" },
|
104
|
+
body: JSON.stringify(object),
|
105
|
+
});
|
106
|
+
|
107
|
+
if (!response.ok) {
|
108
|
+
const result = await response.json();
|
109
|
+
if (result.detail) {
|
110
|
+
Swal.fire({
|
111
|
+
title: "Warning",
|
112
|
+
text: result.detail,
|
113
|
+
icon: "warning",
|
114
|
+
confirmButtonText: "Understood",
|
115
|
+
});
|
116
|
+
}
|
117
|
+
|
64
118
|
throw new Error(`POST ${url} failed for ${JSON.stringify(object)}`);
|
65
119
|
}
|
66
120
|
}
|
@@ -1 +1 @@
|
|
1
|
-
<svg width="160mm" height="160mm" version="1.1" viewBox="0 0 160 160" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g
|
1
|
+
<svg width="160mm" height="160mm" version="1.1" viewBox="0 0 160 160" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g stroke="#000"><path d="m71.456 6.9438c-1.6968-1.1e-4 -3.0723 1.3754-3.0722 3.0722 5e-5 0.81363 0.32287 1.594 0.89762 2.1699v13.554l-31.794-0.00155c-0.053963 0-0.10636 0.006217-0.1602 0.006718v-13.557c0.57209-0.57322 0.89469-1.3492 0.89762-2.159 1.1e-4 -1.6968-1.3759-3.0723-3.0727-3.0722-1.6968-1.11e-4 -3.0728 1.3754-3.0727 3.0722 5e-5 0.81363 0.32287 1.594 0.89762 2.1699v14.156c-5.6155 1.5454-10.042 5.898-11.682 11.475h-12.416c-0.57321-0.57209-1.3492-0.89469-2.159-0.89762-1.6968-1.1e-4 -3.0723 1.3754-3.0722 3.0722-1.105e-4 1.6968 1.3754 3.0723 3.0722 3.0722 0.81363-4.8e-5 1.594-0.32287 2.1699-0.89762h11.731c-0.0039 0.15084-0.022737 0.29776-0.022737 0.44958v7.3003h-11.719c-0.57321-0.57209-1.3492-0.89469-2.159-0.89762-1.6968-1.1e-4 -3.0723 1.3754-3.0722 3.0722-1.105e-4 1.6968 1.3754 3.0728 3.0722 3.0727 0.81363-4.8e-5 1.594-0.32287 2.1699-0.89762h11.708v7.7499h-11.719c-0.57321-0.57209-1.3487-0.89469-2.1585-0.89762-1.6968-1.1e-4 -3.0728 1.3754-3.0727 3.0722-1.105e-4 1.6968 1.3759 3.0723 3.0727 3.0722 0.81363-4.8e-5 1.594-0.32287 2.1699-0.89762h11.708v7.7499h-11.719c-0.57321-0.57209-1.3492-0.89469-2.159-0.89762-1.6968-1.1e-4 -3.0723 1.3754-3.0722 3.0722-1.105e-4 1.6968 1.3754 3.0723 3.0722 3.0722 0.81363-4.8e-5 1.594-0.32287 2.1699-0.89762h11.708v7.7499h-11.719c-0.57321-0.57209-1.3487-0.89469-2.1585-0.89762-1.6968-1.1e-4 -3.0728 1.3754-3.0727 3.0722-1.105e-4 1.6968 1.3759 3.0723 3.0727 3.0722 0.81363-4.8e-5 1.594-0.32287 2.1699-0.89762h11.708v7.7499h-11.719c-0.57321-0.57209-1.3492-0.89469-2.159-0.89762-1.6968-1.1e-4 -3.0723 1.3754-3.0722 3.0722-1.105e-4 1.6968 1.3754 3.0723 3.0722 3.0722 0.81363-5e-5 1.594-0.32287 2.1699-0.89762h11.708v7.7494h-11.719c-0.57321-0.57209-1.3492-0.89469-2.159-0.89762-1.6968-1.1e-4 -3.0723 1.3754-3.0722 3.0722-1.105e-4 1.6968 1.3754 3.0728 3.0722 3.0727 0.81363-5e-5 1.594-0.32287 2.1699-0.89762h11.708v7.3003c0 0.1517 0.018798 0.29835 0.022738 0.44907h-11.742c-0.57322-0.57209-1.3492-0.89469-2.159-0.89762-1.6968-1.1e-4 -3.0723 1.3759-3.0722 3.0727-1.105e-4 1.6968 1.3754 3.0723 3.0722 3.0722 0.81363-5e-5 1.594-0.32287 2.1699-0.89762h12.405c1.6399 5.5765 6.0655 9.9236 11.683 11.468v15.152c-0.57209 0.57322-0.89469 1.3492-0.89762 2.159-1.1e-4 1.6968 1.3754 3.0723 3.0722 3.0722 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722-4.8e-5 -0.81362-0.32287-1.594-0.89762-2.1699v-14.527c0.05398 5e-4 0.10609 0.00827 0.1602 0.00827h7.5897v14.529c-0.57209 0.57322-0.89469 1.3492-0.89762 2.159-1.1e-4 1.6968 1.3754 3.0723 3.0722 3.0722 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722-4.8e-5 -0.81363-0.32287-1.594-0.89762-2.1699v-14.518h7.7494v14.529c-0.57209 0.57322-0.89469 1.3492-0.89762 2.159-1.1e-4 1.6968 1.3759 3.0723 3.0727 3.0722 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722-4.8e-5 -0.81363-0.32287-1.594-0.89762-2.1699v-14.518h7.7499v14.529c-0.57209 0.57322-0.89469 1.3492-0.89762 2.159-1.1e-4 1.6968 1.3754 3.0723 3.0722 3.0722 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722-4.8e-5 -0.81363-0.32287-1.594-0.89762-2.1699v-14.518h7.7499v14.529c-0.57209 0.57322-0.89469 1.3492-0.89762 2.159-1.1e-4 1.6968 1.3754 3.0723 3.0722 3.0722 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722-4.8e-5 -0.81363-0.32287-1.594-0.89762-2.1699v-14.518h7.7499v14.529c-0.57209 0.57322-0.89469 1.3492-0.89762 2.159-1.1e-4 1.6968 1.3754 3.0723 3.0722 3.0722 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722-4.8e-5 -0.81363-0.32287-1.594-0.89762-2.1699v-14.518h7.7499v14.529c-0.57209 0.57322-0.89469 1.3492-0.89762 2.159-1.1e-4 1.6968 1.3754 3.0723 3.0722 3.0722 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722-5e-5 -0.81363-0.32287-1.594-0.89762-2.1699v-14.518h7.5892c0.0543 0 0.10656-0.00775 0.16071-0.00775v14.538c-0.57209 0.57322-0.89469 1.3492-0.89762 2.159-1.1e-4 1.6968 1.3754 3.0723 3.0722 3.0722 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722-5e-5 -0.81362-0.32287-1.594-0.89762-2.1699v-15.141c5.6174-1.5445 10.043-5.8916 11.682-11.468h12.992c0.57322 0.57209 1.3492 0.89469 2.159 0.89762 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722 1.1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722-0.81363 5e-5 -1.5945 0.32287-2.1704 0.89762h-12.307c4e-3 -0.15072 0.02274-0.29737 0.02274-0.44907v-7.3008h12.295c0.57321 0.57209 1.3492 0.89469 2.159 0.89762 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722 1.1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722-0.81363 5e-5 -1.594 0.32287-2.1699 0.89762h-12.284v-7.7494h12.295c0.57321 0.57209 1.3492 0.8947 2.159 0.89762 1.6968 1.1e-4 3.0723-1.3759 3.0722-3.0727 1.1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722-0.81363 4.8e-5 -1.594 0.32287-2.1699 0.89762h-12.284v-7.7499h12.295c0.57322 0.57209 1.3492 0.89469 2.159 0.89762 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722 1.1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722-0.81363 4.8e-5 -1.5945 0.32287-2.1704 0.89762h-12.283v-7.753h12.295c0.57321 0.5721 1.3492 0.8947 2.159 0.89762 1.6968 1.1e-4 3.0723-1.3759 3.0722-3.0727 1.1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722-0.81363 5e-5 -1.594 0.32287-2.1699 0.89762h-12.284v-7.7499h12.295c0.57321 0.5721 1.3492 0.8947 2.159 0.89762 1.6968 1.1e-4 3.0723-1.3754 3.0722-3.0722 1.1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722-0.81363 5e-5 -1.594 0.32287-2.1699 0.89762h-12.284v-19.396c0-0.15199-0.01517-0.30015-0.01913-0.45114h12.303c0.5759 0.57475 1.3563 0.89757 2.1699 0.89762 1.6968 1e-4 3.0723-1.3759 3.0722-3.0727 1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722-0.8098 0.00293-1.5858 0.32553-2.159 0.89762h-12.997c-1.6432-5.5691-6.0616-9.9213-11.677-11.465v-14.176c0.57208-0.57321 0.89469-1.3487 0.89762-2.1585 1.1e-4 -1.6968-1.3754-3.0728-3.0722-3.0727-1.6968-1.105e-4 -3.0723 1.3759-3.0722 3.0727 5e-5 0.81363 0.32287 1.594 0.89762 2.1699v13.551c-0.0542-5.05e-4 -0.10644-0.008785-0.16071-0.008785h-7.5892v-13.553c0.57209-0.57322 0.89469-1.3492 0.89762-2.159 1.1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722-1.6968-1.105e-4 -3.0723 1.3754-3.0722 3.0722 5e-5 0.81363 0.32287 1.594 0.89762 2.1699v13.542h-7.7499v-13.553c0.57209-0.57322 0.89469-1.3487 0.89762-2.1585 1.1e-4 -1.6968-1.3754-3.0728-3.0722-3.0727-1.6968-1.105e-4 -3.0723 1.3759-3.0722 3.0727 4.8e-5 0.81363 0.32287 1.594 0.89762 2.1699v13.542l-7.7411-5.17e-4v-13.565c0.5721-0.57321 0.8947-1.3492 0.89762-2.159 1.1e-4 -1.6968-1.3759-3.0723-3.0727-3.0722-1.6968-1.1e-4 -3.0723 1.3754-3.0722 3.0722 5e-5 0.81363 0.32287 1.594 0.89762 2.1699v13.554l-7.7499-5.16e-4v-13.565c0.5721-0.57321 0.8947-1.3492 0.89762-2.159 1.1e-4 -1.6968-1.3754-3.0723-3.0722-3.0722l-0.00155-5.168e-4z" fill="#fff"/><path d="m54.142 1.28c-7.7785-0.10611-12.693 4.0778-12.693 4.0778s-4.8045 3.244-6.8197 5.6885c-2.0152 2.4446-3.9998 3.3161-3.9998 3.3161l-10.92 4.4292s-4.3039 2.004-4.6829 6.8874c-0.37903 4.8834 8.6052 6.8151 8.6052 6.8151l10.779 1.2867c-3.797 2.1848-6.349 6.2714-6.349 10.984v74.649c0 7.0224 5.6533 12.676 12.676 12.676h74.649c7.0224 0 12.676-5.6533 12.676-12.676v-28.141c19.08-22.042 22.782-33.839 22.782-33.839s2.3205-7.1538 4.8922-15.174c2.5717-8.0199-3.6974-6.8585-3.6974-6.8585l-24.252 6.7293c-1.207-5.7485-6.2825-10.041-12.4-10.041h-33.177c-1.9266-19.83-13.78-25.894-13.78-25.894-5.3521-3.6273-10.213-4.8599-14.287-4.9155z" fill="#121e2c"/><path d="m40.27 37.282c-3.9647 0-7.1567 3.1915-7.1567 7.1562v40.219c0.96019-8.4807 4.4328-18.052 9.6733-24.824 9.8714-12.755 9.3931-19.262 5.7521-22.552zm42.644 0c0.24496 10.488-4.3418 20.083-4.3418 20.083l44.46-13.835c-0.44487-3.5314-3.4372-6.2477-7.0931-6.2477zm-49.801 61.069v21.844c0 3.9647 3.192 7.1562 7.1567 7.1562h18.112c-8.3075-3.693-21.415-12.054-25.268-29z" fill="#29befe"/><path d="m149.79 41.432-69.703 20.837s-18.894 12.226-6.2239 35.914c0.31428-4e-3 39.979 0.0384 39.979 0.0384s5.3864-3.9688 9.3648-9.8406l-40.695-0.25269s-2.0754 0.0793-2.0075-2.3201c0.068-2.3994 1.2177-2.4825 1.2177-2.4825l44.832 0.25605s3.3352-2.6554 8.4851-11.307l-46.34-0.0399s-1.8324-0.34051-1.7768-2.6268c0.0556-2.2863 2.6305-2.585 2.6305-2.585l45.957-0.17048s2.2148 0.37311 4.94-2.6224c2.7252-2.9954 9.801-21.321 9.801-21.321s0.63122-1.6448-0.46214-1.4781zm-72.26 60.929 33.354-0.22045s-4.43 6.7245-14.346 7.0264c-14.503 0.44144-19.008-6.8059-19.008-6.8059zm-39.296-87.393 7.3033 15.24s22.892 2.1374 4.4586 29.071c-18.433 26.934-7.9015 44.329-7.9015 44.329s10.309 21.075 36.028 23.294l35.722 0.45927s7.6669 5e-3 8.9801-7.2795l-0.0255-22.663s-10.703 16.455-23.723 17.408c-9.6849 0.70923-14.934-1.1038-17.672-2.5611-2.2513-1.1981-30.046-14.686-11.338-50.201 16.735-31.77-0.73572-47.166-0.73572-47.166l-13.986 3.0599s0.67535 2.9676-1.8951 4.436c-1.9916 1.1377-5.4854 0.11131-6.6183-1.8831-1.1522-2.0285-0.22903-5.5118 1.8024-6.7625 2.7445-1.6898 5.1812-0.15486 5.1812-0.15486l12.185-2.4294s-15.541-10.99-27.765 3.8033zm2.126 14.889s-16.877-2.0953-19.634-2.9542c-2.7568-0.85892-0.37994-2.5393-0.37994-2.5393s10.175-2.8442 14.302-6.5923z" fill="#fff"/></g></svg>
|
@@ -1,5 +1,13 @@
|
|
1
1
|
from pathlib import Path
|
2
2
|
|
3
|
+
from fastapi.requests import Request
|
3
4
|
from fastapi.templating import Jinja2Templates
|
4
5
|
|
5
|
-
|
6
|
+
from goosebit.auth import check_permissions
|
7
|
+
|
8
|
+
|
9
|
+
def attach_permissions_comparison(_: Request):
|
10
|
+
return {"compare_permissions": check_permissions}
|
11
|
+
|
12
|
+
|
13
|
+
templates = Jinja2Templates(str(Path(__file__).resolve().parent), context_processors=[attach_permissions_comparison])
|
@@ -0,0 +1,75 @@
|
|
1
|
+
{% extends "nav.html.jinja" %}
|
2
|
+
{% block content %}
|
3
|
+
<div class="container-fluid">
|
4
|
+
<div class="row p-2 d-flex justify-content-center">
|
5
|
+
<div class="col">
|
6
|
+
<table id="device-table" class="table table-hover">
|
7
|
+
<thead>
|
8
|
+
<tr>
|
9
|
+
<th>Up</th>
|
10
|
+
<th>UUID</th>
|
11
|
+
<th>Name</th>
|
12
|
+
<th>Model</th>
|
13
|
+
<th>Revision</th>
|
14
|
+
<th>Feed</th>
|
15
|
+
<th>Installed Software</th>
|
16
|
+
<th>Target Software</th>
|
17
|
+
<th>Update Mode</th>
|
18
|
+
<th>State</th>
|
19
|
+
<th>Force Update</th>
|
20
|
+
<th>Progress</th>
|
21
|
+
<th>Last IP</th>
|
22
|
+
<th>Last Seen</th>
|
23
|
+
</tr>
|
24
|
+
</thead>
|
25
|
+
<tbody id="devices-list">
|
26
|
+
</tbody>
|
27
|
+
</table>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
<div class="modal" id="device-config-modal">
|
32
|
+
<div class="modal-dialog modal-lg">
|
33
|
+
<div class="modal-content">
|
34
|
+
<div class="modal-header">
|
35
|
+
<h5 class="modal-title">Configure Devices</h5>
|
36
|
+
<button type="button"
|
37
|
+
class="btn-close"
|
38
|
+
data-bs-dismiss="modal"
|
39
|
+
aria-label="Close"></button>
|
40
|
+
</div>
|
41
|
+
<form id="device-config-form" class="needs-validation" novalidate>
|
42
|
+
<div class="modal-body">
|
43
|
+
<div class="form-group mb-3">
|
44
|
+
<label for="device-selected-name">Name</label>
|
45
|
+
<input id="device-selected-name" class="form-control" placeholder="Name" />
|
46
|
+
</div>
|
47
|
+
<div class="form-group mb-3">
|
48
|
+
<label for="device-selected-feed">Feed</label>
|
49
|
+
<input id="device-selected-feed"
|
50
|
+
class="form-control"
|
51
|
+
placeholder="Feed"
|
52
|
+
required />
|
53
|
+
<div class="invalid-feedback">
|
54
|
+
Feed missing. Use "default" if working with a single
|
55
|
+
feed.
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
<div class="form-group mb-3">
|
59
|
+
<label for="selected-sw">Update Mode</label>
|
60
|
+
<select class="form-select" id="selected-sw" required>
|
61
|
+
<option value="" disabled selected>Select software</option>
|
62
|
+
</select>
|
63
|
+
<div class="invalid-feedback">Mode missing.</div>
|
64
|
+
</div>
|
65
|
+
</div>
|
66
|
+
<div class="modal-footer">
|
67
|
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
68
|
+
<button type="submit" class="btn btn-outline-light">Save changes</button>
|
69
|
+
</div>
|
70
|
+
</form>
|
71
|
+
</div>
|
72
|
+
</div>
|
73
|
+
</div>
|
74
|
+
<script src="{{ url_for('static', path='js/devices.js') }}"></script>
|
75
|
+
{% endblock content %}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
{% extends "nav.html.jinja" %}
|
2
|
+
{% block content %}
|
3
|
+
<div class="container-fluid">
|
4
|
+
<div class="row p-2 d-flex justify-content-center">
|
5
|
+
<div class="col">
|
6
|
+
<table id="device-table" class="table table-hover">
|
7
|
+
<thead>
|
8
|
+
<tr>
|
9
|
+
<th>Name</th>
|
10
|
+
<th>Up</th>
|
11
|
+
<th>UUID</th>
|
12
|
+
<th>Software</th>
|
13
|
+
<th>Progress</th>
|
14
|
+
<th>Last IP</th>
|
15
|
+
<th>Last Seen</th>
|
16
|
+
</tr>
|
17
|
+
</thead>
|
18
|
+
<tbody id="devices-list">
|
19
|
+
</tbody>
|
20
|
+
</table>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
<script src="{{ url_for('static', path='js/index.js') }}"></script>
|
25
|
+
{% endblock content %}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
6
|
+
<title>Login</title>
|
7
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
8
|
+
rel="stylesheet"
|
9
|
+
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
10
|
+
crossorigin="anonymous" />
|
11
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
12
|
+
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
13
|
+
crossorigin="anonymous"></script>
|
14
|
+
<link rel="stylesheet"
|
15
|
+
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
|
16
|
+
<link rel="icon" href="{{ url_for('static', path='favicon.svg') }}" />
|
17
|
+
</head>
|
18
|
+
<body data-bs-theme="dark">
|
19
|
+
<div class="container py-2">
|
20
|
+
<div class="row d-flex justify-content-center align-items-center h-100">
|
21
|
+
<div class="col-12 col-md-8 col-lg-6 col-xl-5">
|
22
|
+
<div class="mb-md-5 mt-md-4 pb-5">
|
23
|
+
<div class="row mb-2 p-2">
|
24
|
+
<div class="col d-flex align-items-center">
|
25
|
+
<h2 class="fw-bold m-0 text-uppercase">gooseBit</h2>
|
26
|
+
</div>
|
27
|
+
<div class="col d-flex justify-content-end px-3">
|
28
|
+
<img src="{{ request.url_for('static', path='svg/goosebit-logo.svg') }}"
|
29
|
+
height="35px"
|
30
|
+
width="35px"
|
31
|
+
alt="gooseBit Logo" />
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
<form method="post" id="login_form">
|
35
|
+
<div class="form-outline form-white mb-4">
|
36
|
+
<input type="email"
|
37
|
+
id="username"
|
38
|
+
name="username"
|
39
|
+
placeholder="Email"
|
40
|
+
class="form-control form-control-lg" />
|
41
|
+
</div>
|
42
|
+
<div class="form-outline form-white mb-4">
|
43
|
+
<input type="password"
|
44
|
+
id="password"
|
45
|
+
name="password"
|
46
|
+
placeholder="Password"
|
47
|
+
class="form-control form-control-lg" />
|
48
|
+
</div>
|
49
|
+
<button class="btn btn-outline-light btn-lg px-5 w-100" type="submit">Login</button>
|
50
|
+
</form>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
<script src="{{ url_for('static', path='js/login.js') }}"></script>
|
56
|
+
</body>
|
57
|
+
</html>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
{% extends "nav.html.jinja" %}
|
2
|
+
{% block content %}
|
3
|
+
<div class="container-fluid">
|
4
|
+
<div class="row p-2 d-flex justify-content-center">
|
5
|
+
<div class="col col-12 col-lg-9">
|
6
|
+
<div class="card border rounded">
|
7
|
+
<div class="card-header">
|
8
|
+
<h3>Logs - {{ device }}</h3>
|
9
|
+
</div>
|
10
|
+
<div class="card-header">
|
11
|
+
<div class="progress m-2"
|
12
|
+
role="progressbar"
|
13
|
+
aria-label="Basic example"
|
14
|
+
aria-valuenow="0"
|
15
|
+
aria-valuemin="0"
|
16
|
+
aria-valuemax="100">
|
17
|
+
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
18
|
+
id="install-progress"
|
19
|
+
style="width: 0%"></div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<div class="card-body">
|
23
|
+
<pre id="device-log"></pre>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<script>device = "{{ device }}";</script>
|
30
|
+
<script src="{{ url_for('static', path='js/logs.js') }}"></script>
|
31
|
+
{% endblock content %}
|