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.
Files changed (105) hide show
  1. goosebit/__init__.py +50 -19
  2. goosebit/__main__.py +7 -0
  3. goosebit/api/responses.py +5 -0
  4. goosebit/api/routes.py +5 -15
  5. goosebit/api/telemetry/__init__.py +1 -0
  6. goosebit/{telemetry/__init__.py → api/telemetry/metrics.py} +9 -3
  7. goosebit/api/telemetry/prometheus/__init__.py +2 -0
  8. goosebit/api/telemetry/prometheus/readers.py +3 -0
  9. goosebit/api/telemetry/prometheus/routes.py +18 -0
  10. goosebit/api/telemetry/routes.py +9 -0
  11. goosebit/api/v1/__init__.py +1 -0
  12. goosebit/api/v1/devices/__init__.py +1 -0
  13. goosebit/api/v1/devices/device/__init__.py +1 -0
  14. goosebit/api/v1/devices/device/responses.py +13 -0
  15. goosebit/api/v1/devices/device/routes.py +27 -0
  16. goosebit/api/v1/devices/requests.py +7 -0
  17. goosebit/api/v1/devices/responses.py +16 -0
  18. goosebit/api/v1/devices/routes.py +35 -0
  19. goosebit/api/v1/download/__init__.py +1 -0
  20. goosebit/api/v1/download/routes.py +22 -0
  21. goosebit/api/v1/rollouts/__init__.py +1 -0
  22. goosebit/api/v1/rollouts/requests.py +16 -0
  23. goosebit/api/v1/rollouts/responses.py +19 -0
  24. goosebit/api/v1/rollouts/routes.py +50 -0
  25. goosebit/api/v1/routes.py +9 -0
  26. goosebit/api/v1/software/__init__.py +1 -0
  27. goosebit/api/v1/software/requests.py +5 -0
  28. goosebit/api/v1/software/responses.py +16 -0
  29. goosebit/api/v1/software/routes.py +77 -0
  30. goosebit/auth/__init__.py +101 -101
  31. goosebit/db/__init__.py +11 -0
  32. goosebit/db/config.py +10 -0
  33. goosebit/db/migrations/models/0_20240830054046_init.py +136 -0
  34. goosebit/{models.py → db/models.py} +17 -10
  35. goosebit/realtime/logs.py +4 -3
  36. goosebit/realtime/routes.py +2 -2
  37. goosebit/schema/__init__.py +0 -0
  38. goosebit/schema/devices.py +73 -0
  39. goosebit/schema/rollouts.py +31 -0
  40. goosebit/schema/software.py +37 -0
  41. goosebit/settings/__init__.py +17 -0
  42. goosebit/settings/const.py +21 -0
  43. goosebit/settings/schema.py +86 -0
  44. goosebit/ui/bff/__init__.py +1 -0
  45. goosebit/ui/bff/devices/__init__.py +1 -0
  46. goosebit/ui/bff/devices/requests.py +12 -0
  47. goosebit/ui/bff/devices/responses.py +39 -0
  48. goosebit/ui/bff/devices/routes.py +72 -0
  49. goosebit/ui/bff/download/__init__.py +1 -0
  50. goosebit/ui/bff/download/routes.py +22 -0
  51. goosebit/ui/bff/rollouts/__init__.py +1 -0
  52. goosebit/ui/bff/rollouts/responses.py +37 -0
  53. goosebit/ui/bff/rollouts/routes.py +52 -0
  54. goosebit/ui/bff/routes.py +11 -0
  55. goosebit/ui/bff/software/__init__.py +1 -0
  56. goosebit/ui/bff/software/responses.py +37 -0
  57. goosebit/ui/bff/software/routes.py +83 -0
  58. goosebit/ui/nav.py +16 -0
  59. goosebit/ui/routes.py +29 -66
  60. goosebit/ui/static/favicon.ico +0 -0
  61. goosebit/ui/static/favicon.svg +1 -1
  62. goosebit/ui/static/js/devices.js +47 -71
  63. goosebit/ui/static/js/index.js +4 -9
  64. goosebit/ui/static/js/login.js +23 -0
  65. goosebit/ui/static/js/logs.js +1 -1
  66. goosebit/ui/static/js/rollouts.js +33 -19
  67. goosebit/ui/static/js/{firmware.js → software.js} +87 -86
  68. goosebit/ui/static/js/util.js +60 -6
  69. goosebit/ui/static/svg/goosebit-logo.svg +1 -1
  70. goosebit/ui/templates/__init__.py +9 -1
  71. goosebit/ui/templates/devices.html.jinja +75 -0
  72. goosebit/ui/templates/index.html.jinja +25 -0
  73. goosebit/ui/templates/login.html.jinja +57 -0
  74. goosebit/ui/templates/logs.html.jinja +31 -0
  75. goosebit/ui/templates/nav.html.jinja +84 -0
  76. goosebit/ui/templates/rollouts.html.jinja +93 -0
  77. goosebit/ui/templates/software.html.jinja +139 -0
  78. goosebit/updater/controller/v1/routes.py +101 -96
  79. goosebit/updater/controller/v1/schema.py +56 -0
  80. goosebit/updater/manager.py +65 -65
  81. goosebit/updater/routes.py +3 -11
  82. goosebit/updates/__init__.py +91 -32
  83. goosebit/updates/swdesc.py +2 -7
  84. goosebit-0.2.1.dist-info/METADATA +173 -0
  85. goosebit-0.2.1.dist-info/RECORD +95 -0
  86. goosebit/api/devices.py +0 -136
  87. goosebit/api/download.py +0 -34
  88. goosebit/api/firmware.py +0 -57
  89. goosebit/api/helper.py +0 -30
  90. goosebit/api/rollouts.py +0 -87
  91. goosebit/db.py +0 -37
  92. goosebit/permissions.py +0 -75
  93. goosebit/settings.py +0 -64
  94. goosebit/telemetry/prometheus.py +0 -10
  95. goosebit/ui/templates/devices.html +0 -115
  96. goosebit/ui/templates/firmware.html +0 -163
  97. goosebit/ui/templates/index.html +0 -23
  98. goosebit/ui/templates/login.html +0 -65
  99. goosebit/ui/templates/logs.html +0 -36
  100. goosebit/ui/templates/nav.html +0 -117
  101. goosebit/ui/templates/rollouts.html +0 -76
  102. goosebit-0.1.2.dist-info/METADATA +0 -123
  103. goosebit-0.1.2.dist-info/RECORD +0 -51
  104. {goosebit-0.1.2.dist-info → goosebit-0.2.1.dist-info}/LICENSE +0 -0
  105. {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/upload/local", {
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 === 400) {
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/upload/remote", {
89
+ const response = await fetch("/ui/bff/software", {
85
90
  method: "POST",
86
91
  body: formData,
87
92
  });
88
93
 
89
- if (!response.ok) {
90
- if (response.status === 400) {
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 updateFirmwareList() {
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
- updateFirmwareList();
125
+ updateSoftwareList();
116
126
  }
117
127
 
118
128
  document.addEventListener("DOMContentLoaded", () => {
119
- dataTable = new DataTable("#firmware-table", {
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: "/api/firmware/all",
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", searchable: true },
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
- // Compatibility tooltip
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 deleteFirmware(files) {
267
+ async function deleteSoftware(software_ids) {
267
268
  try {
268
- await post("/api/firmware/delete", files);
269
- updateFirmwareList();
269
+ await delete_request("/ui/bff/software", { software_ids });
270
+ updateSoftwareList();
270
271
  } catch (error) {
271
- console.error("Deleting firmwares failed:", error);
272
+ console.error("Deleting software list failed:", error);
272
273
  }
273
274
  }
274
275
 
275
- function downloadFirmware(file) {
276
- window.location.href = `/api/download/${file}`;
276
+ function downloadSoftware(file) {
277
+ window.location.href = `/ui/bff/download/${file}`;
277
278
  }
@@ -20,15 +20,15 @@ function secondsToRecentDate(t) {
20
20
  return s + (s === 1 ? " second" : " seconds");
21
21
  }
22
22
 
23
- async function updateFirmwareSelection(addSpecialMode = false) {
23
+ async function updateSoftwareSelection(addSpecialMode = false) {
24
24
  try {
25
- const response = await fetch("/api/firmware/all");
25
+ const response = await fetch("/ui/bff/software");
26
26
  if (!response.ok) {
27
- console.error("Retrieving firmwares failed.");
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-fw");
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.name;
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 post(url, object) {
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 transform="translate(-32.316 -71.486)"><g transform="translate(3.1433 .77798)" stroke="#000"><path d="m112.72 77.662a3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 0.89762 2.1699v13.542h-43.886c-7.6869 0-14.133 5.0881-16.192 12.091h-12.416a3.072 3.072 0 0 0-2.159-0.89762 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 2.1699-0.89762h11.731c-0.0039 0.15084-0.02274 0.29776-0.02274 0.44958v7.3003h-11.719a3.072 3.072 0 0 0-2.159-0.89762 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 2.1699-0.89762h11.708v7.7499h-11.719a3.072 3.072 0 0 0-2.159-0.89762 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 2.1699-0.89762h11.708v7.7499h-11.719a3.072 3.072 0 0 0-2.159-0.89762 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 2.1699-0.89762h11.708v7.7499h-11.719a3.072 3.072 0 0 0-2.159-0.89762 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 2.1699-0.89762h11.708v7.7499h-11.719a3.072 3.072 0 0 0-2.159-0.89762 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 2.1699-0.89761h11.708v7.7494h-11.719a3.072 3.072 0 0 0-2.159-0.89762 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 2.1699-0.89762h11.708v7.3008c0 0.1517 0.0188 0.29835 0.02274 0.44907h-11.742a3.072 3.072 0 0 0-2.159-0.89762 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 2.1699-0.89762h12.405c1.6399 5.5765 6.0655 9.9236 11.683 11.468v15.152a3.072 3.072 0 0 0-0.89762 2.159 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-0.89762-2.1699v-14.527c0.05398 5e-4 0.10609 8e-3 0.1602 8e-3h7.5897v14.529a3.072 3.072 0 0 0-0.89762 2.159 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-0.89762-2.1699v-14.518h7.7494v14.529a3.072 3.072 0 0 0-0.89762 2.159 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-0.89762-2.1699v-14.518h7.7499v14.529a3.072 3.072 0 0 0-0.89762 2.159 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-0.89762-2.1699v-14.518h7.7499v14.529a3.072 3.072 0 0 0-0.89762 2.159 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-0.89762-2.1699v-14.518h7.7499v14.529a3.072 3.072 0 0 0-0.89762 2.159 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-0.89762-2.1699v-14.518h7.7499v14.529a3.072 3.072 0 0 0-0.89762 2.159 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-0.89762-2.1699v-14.518h7.5892c0.0543 0 0.10656-8e-3 0.16071-8e-3v14.538a3.072 3.072 0 0 0-0.89762 2.159 3.072 3.072 0 0 0 3.0722 3.0722 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-0.89762-2.1699v-15.141c5.6174-1.5445 10.043-5.8916 11.682-11.468h12.992a3.072 3.072 0 0 0 2.159 0.89762 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-3.0722-3.0722 3.072 3.072 0 0 0-2.1699 0.89762h-12.307c4e-3 -0.15072 0.0227-0.29737 0.0227-0.44907v-7.3008h12.295a3.072 3.072 0 0 0 2.159 0.89762 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-3.0722-3.0722 3.072 3.072 0 0 0-2.1699 0.89762h-12.284v-7.7494h12.295a3.072 3.072 0 0 0 2.159 0.89761 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-3.0722-3.0722 3.072 3.072 0 0 0-2.1699 0.89762h-12.284v-7.7499h12.295a3.072 3.072 0 0 0 2.159 0.89762 3.072 3.072 0 0 0 3.0722-3.0722 3.072 3.072 0 0 0-3.0722-3.0722 3.072 3.072 0 0 0-2.1699 0.89762h-12.284v-43.597c0-7.791-5.2299-14.3-12.38-16.266v-14.176a3.072 3.072 0 0 0 0.89762-2.159 3.072 3.072 0 0 0-3.0722-3.0722 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 0.89762 2.1699v13.551c-0.0542-5.05e-4 -0.10644-0.0083-0.16071-0.0083h-7.5892v-13.553a3.072 3.072 0 0 0 0.89762-2.159 3.072 3.072 0 0 0-3.0722-3.0722 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 0.89762 2.1699v13.542h-7.7499v-13.553a3.072 3.072 0 0 0 0.89762-2.159 3.072 3.072 0 0 0-3.0722-3.0722 3.072 3.072 0 0 0-3.0722 3.0722 3.072 3.072 0 0 0 0.89762 2.1699v13.542h-7.7499v-13.553a3.072 3.072 0 0 0 0.89762-2.159 3.072 3.072 0 0 0-3.0722-3.0722z" fill="#fff"/><g><path d="m83.315 71.988c-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="m69.443 107.99c-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="m178.96 112.14-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></g></g></svg>
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
- templates = Jinja2Templates(str(Path(__file__).resolve().parent))
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 %}