goosebit 0.2.4__py3-none-any.whl → 0.2.6__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 +56 -6
- goosebit/api/telemetry/metrics.py +1 -5
- goosebit/api/v1/devices/device/responses.py +1 -0
- goosebit/api/v1/devices/device/routes.py +8 -8
- goosebit/api/v1/devices/requests.py +20 -0
- goosebit/api/v1/devices/routes.py +83 -8
- goosebit/api/v1/download/routes.py +14 -3
- goosebit/api/v1/rollouts/routes.py +5 -4
- goosebit/api/v1/routes.py +2 -1
- goosebit/api/v1/settings/routes.py +14 -0
- goosebit/api/v1/settings/users/__init__.py +1 -0
- goosebit/api/v1/settings/users/requests.py +16 -0
- goosebit/api/v1/settings/users/responses.py +7 -0
- goosebit/api/v1/settings/users/routes.py +56 -0
- goosebit/api/v1/software/routes.py +18 -14
- goosebit/auth/__init__.py +54 -14
- goosebit/auth/permissions.py +80 -0
- goosebit/db/config.py +57 -1
- goosebit/db/migrations/models/1_20241109151811_update.py +11 -0
- goosebit/db/migrations/models/2_20241121113728_update.py +11 -0
- goosebit/db/migrations/models/3_20241121140210_update.py +11 -0
- goosebit/db/migrations/models/4_20250324110331_update.py +16 -0
- goosebit/db/migrations/models/4_20250402085235_rename_uuid_to_id.py +11 -0
- goosebit/db/migrations/models/5_20250619090242_null_feed.py +83 -0
- goosebit/db/models.py +22 -7
- goosebit/db/pg_ssl_context.py +51 -0
- goosebit/device_manager.py +262 -0
- goosebit/plugins/__init__.py +32 -0
- goosebit/schema/devices.py +9 -6
- goosebit/schema/plugins.py +67 -0
- goosebit/schema/updates.py +15 -0
- goosebit/schema/users.py +9 -0
- goosebit/settings/__init__.py +0 -3
- goosebit/settings/schema.py +62 -14
- goosebit/storage/__init__.py +62 -0
- goosebit/storage/base.py +14 -0
- goosebit/storage/filesystem.py +111 -0
- goosebit/storage/s3.py +104 -0
- goosebit/ui/bff/common/columns.py +50 -0
- goosebit/ui/bff/common/requests.py +3 -15
- goosebit/ui/bff/common/responses.py +17 -0
- goosebit/ui/bff/devices/device/__init__.py +1 -0
- goosebit/ui/bff/devices/device/routes.py +17 -0
- goosebit/ui/bff/devices/requests.py +1 -0
- goosebit/ui/bff/devices/responses.py +6 -2
- goosebit/ui/bff/devices/routes.py +71 -17
- goosebit/ui/bff/download/routes.py +14 -3
- goosebit/ui/bff/rollouts/responses.py +6 -2
- goosebit/ui/bff/rollouts/routes.py +32 -4
- goosebit/ui/bff/routes.py +6 -3
- goosebit/ui/bff/settings/__init__.py +1 -0
- goosebit/ui/bff/settings/routes.py +20 -0
- goosebit/ui/bff/settings/users/__init__.py +1 -0
- goosebit/ui/bff/settings/users/responses.py +33 -0
- goosebit/ui/bff/settings/users/routes.py +80 -0
- goosebit/ui/bff/software/responses.py +19 -9
- goosebit/ui/bff/software/routes.py +40 -12
- goosebit/ui/nav.py +12 -2
- goosebit/ui/routes.py +70 -26
- goosebit/ui/static/js/devices.js +72 -80
- goosebit/ui/static/js/login.js +21 -5
- goosebit/ui/static/js/logs.js +7 -22
- goosebit/ui/static/js/rollouts.js +39 -35
- goosebit/ui/static/js/settings.js +322 -0
- goosebit/ui/static/js/setup.js +28 -0
- goosebit/ui/static/js/software.js +127 -127
- goosebit/ui/static/js/util.js +45 -4
- goosebit/ui/templates/__init__.py +10 -1
- goosebit/ui/templates/devices.html.jinja +0 -20
- goosebit/ui/templates/login.html.jinja +5 -0
- goosebit/ui/templates/nav.html.jinja +26 -7
- goosebit/ui/templates/rollouts.html.jinja +4 -22
- goosebit/ui/templates/settings.html.jinja +88 -0
- goosebit/ui/templates/setup.html.jinja +71 -0
- goosebit/ui/templates/software.html.jinja +0 -11
- goosebit/updater/controller/v1/routes.py +120 -72
- goosebit/updater/routes.py +86 -7
- goosebit/updates/__init__.py +24 -31
- goosebit/updates/swdesc.py +15 -8
- goosebit/users/__init__.py +63 -0
- goosebit/util/__init__.py +0 -0
- goosebit/util/path.py +42 -0
- goosebit/util/version.py +92 -0
- goosebit-0.2.6.dist-info/METADATA +280 -0
- goosebit-0.2.6.dist-info/RECORD +133 -0
- {goosebit-0.2.4.dist-info → goosebit-0.2.6.dist-info}/WHEEL +1 -1
- goosebit-0.2.6.dist-info/entry_points.txt +3 -0
- goosebit/realtime/logs.py +0 -42
- goosebit/realtime/routes.py +0 -13
- goosebit/ui/static/js/index.js +0 -155
- goosebit/ui/templates/index.html.jinja +0 -25
- goosebit/updater/manager.py +0 -357
- goosebit-0.2.4.dist-info/METADATA +0 -181
- goosebit-0.2.4.dist-info/RECORD +0 -98
- /goosebit/{realtime → api/v1/settings}/__init__.py +0 -0
- {goosebit-0.2.4.dist-info → goosebit-0.2.6.dist-info}/LICENSE +0 -0
goosebit/ui/static/js/util.js
CHANGED
@@ -22,10 +22,10 @@ function secondsToRecentDate(t) {
|
|
22
22
|
|
23
23
|
async function updateSoftwareSelection(devices = null) {
|
24
24
|
try {
|
25
|
-
const url = new URL("/ui/bff/software", window.location.origin);
|
25
|
+
const url = new URL("/ui/bff/software?order[0][dir]=desc&order[0][name]=version", window.location.origin);
|
26
26
|
if (devices != null) {
|
27
27
|
for (const device of devices) {
|
28
|
-
url.searchParams.append("
|
28
|
+
url.searchParams.append("ids", device.id);
|
29
29
|
}
|
30
30
|
}
|
31
31
|
const response = await fetch(url.toString());
|
@@ -64,6 +64,26 @@ async function updateSoftwareSelection(devices = null) {
|
|
64
64
|
}
|
65
65
|
}
|
66
66
|
|
67
|
+
async function get_request(url) {
|
68
|
+
const response = await fetch(url, {
|
69
|
+
method: "GET",
|
70
|
+
});
|
71
|
+
|
72
|
+
const result = await response.json();
|
73
|
+
if (!response.ok) {
|
74
|
+
if (result.detail) {
|
75
|
+
Swal.fire({
|
76
|
+
title: "Warning",
|
77
|
+
text: result.detail,
|
78
|
+
icon: "warning",
|
79
|
+
confirmButtonText: "Understood",
|
80
|
+
});
|
81
|
+
}
|
82
|
+
|
83
|
+
throw new Error(`GET ${url} failed for ${JSON.stringify(object)}`);
|
84
|
+
}
|
85
|
+
return result;
|
86
|
+
}
|
67
87
|
async function post_request(url, object) {
|
68
88
|
const response = await fetch(url, {
|
69
89
|
method: "POST",
|
@@ -85,6 +105,27 @@ async function post_request(url, object) {
|
|
85
105
|
throw new Error(`POST ${url} failed for ${JSON.stringify(object)}`);
|
86
106
|
}
|
87
107
|
}
|
108
|
+
async function put_request(url, object) {
|
109
|
+
const response = await fetch(url, {
|
110
|
+
method: "PUT",
|
111
|
+
headers: { "Content-Type": "application/json" },
|
112
|
+
body: JSON.stringify(object),
|
113
|
+
});
|
114
|
+
|
115
|
+
if (!response.ok) {
|
116
|
+
const result = await response.json();
|
117
|
+
if (result.detail) {
|
118
|
+
Swal.fire({
|
119
|
+
title: "Warning",
|
120
|
+
text: result.detail,
|
121
|
+
icon: "warning",
|
122
|
+
confirmButtonText: "Understood",
|
123
|
+
});
|
124
|
+
}
|
125
|
+
|
126
|
+
throw new Error(`PUT ${url} failed for ${JSON.stringify(object)}`);
|
127
|
+
}
|
128
|
+
}
|
88
129
|
async function patch_request(url, object) {
|
89
130
|
const response = await fetch(url, {
|
90
131
|
method: "PATCH",
|
@@ -103,7 +144,7 @@ async function patch_request(url, object) {
|
|
103
144
|
});
|
104
145
|
}
|
105
146
|
|
106
|
-
throw new Error(`
|
147
|
+
throw new Error(`PATCH ${url} failed for ${JSON.stringify(object)}`);
|
107
148
|
}
|
108
149
|
}
|
109
150
|
async function delete_request(url, object) {
|
@@ -124,6 +165,6 @@ async function delete_request(url, object) {
|
|
124
165
|
});
|
125
166
|
}
|
126
167
|
|
127
|
-
throw new Error(`
|
168
|
+
throw new Error(`DELETE ${url} failed for ${JSON.stringify(object)}`);
|
128
169
|
}
|
129
170
|
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from pathlib import Path
|
2
2
|
|
3
|
+
import jinja2
|
3
4
|
from fastapi.requests import Request
|
4
5
|
from fastapi.templating import Jinja2Templates
|
5
6
|
|
@@ -10,4 +11,12 @@ def attach_permissions_comparison(_: Request):
|
|
10
11
|
return {"compare_permissions": check_permissions}
|
11
12
|
|
12
13
|
|
13
|
-
|
14
|
+
env = jinja2.Environment(loader=jinja2.ChoiceLoader([jinja2.FileSystemLoader(str(Path(__file__).resolve().parent))]))
|
15
|
+
templates = Jinja2Templates(context_processors=[attach_permissions_comparison], env=env)
|
16
|
+
|
17
|
+
|
18
|
+
def add_template_handler(handler: Jinja2Templates):
|
19
|
+
templates.env.loader.loaders.append(handler.env.loader)
|
20
|
+
|
21
|
+
|
22
|
+
templates.add_template_handler = add_template_handler
|
@@ -4,26 +4,6 @@
|
|
4
4
|
<div class="row p-2 d-flex justify-content-center">
|
5
5
|
<div class="col">
|
6
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
7
|
</table>
|
28
8
|
</div>
|
29
9
|
</div>
|
@@ -31,11 +31,15 @@
|
|
31
31
|
alt="gooseBit Logo" />
|
32
32
|
</div>
|
33
33
|
</div>
|
34
|
+
<div id="login_error" class="alert alert-danger d-none" role="alert">
|
35
|
+
<!-- Error message will be inserted here -->
|
36
|
+
</div>
|
34
37
|
<form method="post" id="login_form">
|
35
38
|
<div class="form-outline form-white mb-4">
|
36
39
|
<input type="email"
|
37
40
|
id="username"
|
38
41
|
name="username"
|
42
|
+
autocomplete="username"
|
39
43
|
placeholder="Email"
|
40
44
|
class="form-control form-control-lg" />
|
41
45
|
</div>
|
@@ -43,6 +47,7 @@
|
|
43
47
|
<input type="password"
|
44
48
|
id="password"
|
45
49
|
name="password"
|
50
|
+
autocomplete="current-password"
|
46
51
|
placeholder="Password"
|
47
52
|
class="form-control form-control-lg" />
|
48
53
|
</div>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
|
-
<script>const PERMISSIONS = {{request.user.
|
4
|
+
<script>const PERMISSIONS = {{request.user.permissions | safe}};</script>
|
5
5
|
<meta charset="utf-8" />
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7
7
|
<title>{{ title }}</title>
|
@@ -60,19 +60,31 @@
|
|
60
60
|
box-shadow: none!important;
|
61
61
|
}
|
62
62
|
</style>
|
63
|
-
<script>
|
63
|
+
<script>
|
64
|
+
const TABLE_UPDATE_TIME = 3000;
|
65
|
+
DataTable.ext.errMode = function ( e, settings, helpPage, message ) {
|
66
|
+
if (e.jqXHR.status == 401) {
|
67
|
+
window.location.reload();
|
68
|
+
} else if (e.jqXHR.status >= 200 && e.jqXHR.status < 300) {
|
69
|
+
console.error("AJAX query error when reloading datatable");
|
70
|
+
}
|
71
|
+
console.error("Unknown error when reloading datatable");
|
72
|
+
};
|
73
|
+
|
74
|
+
</script>
|
64
75
|
<script src="{{ url_for('static', path='js/util.js') }}"></script>
|
65
76
|
</head>
|
66
77
|
<body data-bs-theme="dark">
|
67
78
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
68
79
|
<div class="container-fluid">
|
69
|
-
<a class="navbar-brand" href="{{ request.url_for('
|
80
|
+
<a class="navbar-brand" href="{{ request.url_for('devices_ui') }}">
|
70
81
|
<img src="{{ request.url_for('static', path='svg/goosebit-logo.svg') }}"
|
71
82
|
class="me-2"
|
72
83
|
height="30px"
|
73
84
|
width="30px"
|
74
85
|
alt="gooseBit logo" />
|
75
86
|
gooseBit
|
87
|
+
<small class="text-muted">v{{ request.app.version }}</small>
|
76
88
|
</a>
|
77
89
|
<button class="navbar-toggler"
|
78
90
|
type="button"
|
@@ -86,14 +98,21 @@
|
|
86
98
|
<div class="collapse navbar-collapse" id="navbar">
|
87
99
|
<div class="navbar-nav">
|
88
100
|
{% for nav_item in request.nav %}
|
89
|
-
{% if
|
90
|
-
|
91
|
-
|
101
|
+
{% if nav_item.show %}
|
102
|
+
{% if compare_permissions(nav_item.permissions, request.user.permissions) %}
|
103
|
+
<a class="nav-link{% if request.url == request.url_for(nav_item.function) %} active{% endif %}"
|
104
|
+
href="{{ request.url_for(nav_item.function) }}">{{ nav_item.text }}</a>
|
105
|
+
{% endif %}
|
92
106
|
{% endif %}
|
93
107
|
{% endfor %}
|
94
108
|
</div>
|
95
109
|
<div class="navbar-nav d-flex flex-fill justify-content-end">
|
96
|
-
|
110
|
+
{% if compare_permissions("goosebit.settings", request.user.permissions) %}
|
111
|
+
<a class="nav-link ps-3 me-2 border-start"
|
112
|
+
href="{{ request.url_for('settings_ui') }}">Settings<i class="bi bi-gear ps-2"></i></a>
|
113
|
+
{% endif %}
|
114
|
+
<a class="nav-link ps-3 border-start"
|
115
|
+
href="{{ request.url_for('logout') }}">Logout<i class="bi bi-box-arrow-right ps-2"></i></a>
|
97
116
|
</div>
|
98
117
|
</div>
|
99
118
|
</div>
|
@@ -4,28 +4,13 @@
|
|
4
4
|
<div class="row p-2 d-flex justify-content-center">
|
5
5
|
<div class="col">
|
6
6
|
<table id="rollout-table" class="table table-hover">
|
7
|
-
<thead>
|
8
|
-
<tr>
|
9
|
-
<th>Id</th>
|
10
|
-
<th>Created</th>
|
11
|
-
<th>Name</th>
|
12
|
-
<th>Feed</th>
|
13
|
-
<th>Software File</th>
|
14
|
-
<th>Software Version</th>
|
15
|
-
<th>Paused</th>
|
16
|
-
<th>Success Count</th>
|
17
|
-
<th>Failure Count</th>
|
18
|
-
</tr>
|
19
|
-
</thead>
|
20
|
-
<tbody id="rollouts-list">
|
21
|
-
</tbody>
|
22
7
|
</table>
|
23
8
|
</div>
|
24
9
|
</div>
|
25
10
|
</div>
|
26
11
|
{% if compare_permissions(["rollout.write"], request.user.permissions) %}
|
27
|
-
<div class="modal" id="rollout-create-modal">
|
28
|
-
<div class="modal-dialog modal-
|
12
|
+
<div class="modal modal-lg fade" id="rollout-create-modal">
|
13
|
+
<div class="modal-dialog modal-dialog-centered modal-xl">
|
29
14
|
<div class="modal-content">
|
30
15
|
<div class="modal-header">
|
31
16
|
<h5 class="modal-title">Create Rollout</h5>
|
@@ -46,7 +31,7 @@
|
|
46
31
|
<input class="form-control" id="rollout-selected-feed" required />
|
47
32
|
<div class="invalid-feedback">Feed missing. Use "default" if working with a single feed.</div>
|
48
33
|
</div>
|
49
|
-
<div class="form-group
|
34
|
+
<div class="form-group">
|
50
35
|
<div class="input-group mb-3 has-validation">
|
51
36
|
<span class="input-group-text">Software</span>
|
52
37
|
<select class="form-control"
|
@@ -59,10 +44,7 @@
|
|
59
44
|
required></select>
|
60
45
|
<div class="invalid-feedback">Software missing.</div>
|
61
46
|
</div>
|
62
|
-
|
63
|
-
<div class="modal-footer">
|
64
|
-
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
65
|
-
<button type="submit" class="btn btn-outline-light">Save changes</button>
|
47
|
+
<button type="submit" class="btn btn-outline-light w-100">Save changes</button>
|
66
48
|
</div>
|
67
49
|
</div>
|
68
50
|
</form>
|
@@ -0,0 +1,88 @@
|
|
1
|
+
{% extends "nav.html.jinja" %}
|
2
|
+
{% block content %}
|
3
|
+
<style>
|
4
|
+
input[type="checkbox"].ignore-validation:valid,
|
5
|
+
input[type="checkbox"].ignore-validation:invalid {
|
6
|
+
border-color: var(--bs-border-color);
|
7
|
+
}
|
8
|
+
</style>
|
9
|
+
<div class="container-fluid">
|
10
|
+
<ul class="nav nav-underline nav-fill my-2">
|
11
|
+
<li class="nav-item" role="presentation">
|
12
|
+
<button class="nav-link active"
|
13
|
+
id="settings-users-tab"
|
14
|
+
data-bs-toggle="tab"
|
15
|
+
data-bs-target="#settings-users-panel"
|
16
|
+
type="button"
|
17
|
+
role="tab">Users</button>
|
18
|
+
</li>
|
19
|
+
</ul>
|
20
|
+
<div class="tab-content mb-2">
|
21
|
+
<div class="tab-pane fade show active"
|
22
|
+
id="settings-users-panel"
|
23
|
+
role="tabpanel">
|
24
|
+
<table id="users-table" class="table table-hover">
|
25
|
+
</table>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
{% if compare_permissions(["settings.write"], request.user.permissions) %}
|
30
|
+
<div class="modal modal-lg fade" id="create-user-modal">
|
31
|
+
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered modal-xl">
|
32
|
+
<div class="modal-content">
|
33
|
+
<div class="modal-header">
|
34
|
+
<h5 class="modal-title">Create User</h5>
|
35
|
+
<button type="button"
|
36
|
+
class="btn-close"
|
37
|
+
data-bs-dismiss="modal"
|
38
|
+
aria-label="Close"></button>
|
39
|
+
</div>
|
40
|
+
<form id="create-user-form" class="needs-validation" novalidate>
|
41
|
+
<div class="modal-body">
|
42
|
+
<div class="input-group mb-3 has-validation">
|
43
|
+
<span class="input-group-text">Username</span>
|
44
|
+
<input class="form-control" type="text" id="create-user-username" required />
|
45
|
+
<div class="invalid-feedback">Username missing.</div>
|
46
|
+
</div>
|
47
|
+
<div class="input-group mb-3 has-validation">
|
48
|
+
<span class="input-group-text">Password</span>
|
49
|
+
<input class="form-control" type="text" id="create-user-password" required />
|
50
|
+
<div class="invalid-feedback">Password missing.</div>
|
51
|
+
</div>
|
52
|
+
<div class="input-group mb-3 has-validation">
|
53
|
+
<input type="checkbox"
|
54
|
+
id="create-user-permissions-validator"
|
55
|
+
required
|
56
|
+
style="display:none">
|
57
|
+
<div class="col" id="create-user-permissions"></div>
|
58
|
+
<div class="invalid-feedback">Please set at least one permission.</div>
|
59
|
+
</div>
|
60
|
+
<input class="btn btn-outline-light w-100"
|
61
|
+
id="create-user-submit"
|
62
|
+
type="submit"
|
63
|
+
value="Create User" />
|
64
|
+
</div>
|
65
|
+
</form>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
</div>
|
69
|
+
{% else %}
|
70
|
+
<div class="modal modal-lg fade" id="upload-modal">
|
71
|
+
<div class="modal-dialog modal-dialog-centered modal-xl">
|
72
|
+
<div class="modal-content">
|
73
|
+
<div class="modal-header">
|
74
|
+
Unavailable
|
75
|
+
<button type="button"
|
76
|
+
class="btn-close"
|
77
|
+
data-bs-dismiss="modal"
|
78
|
+
aria-label="Close"></button>
|
79
|
+
</div>
|
80
|
+
<div class="modal-body">
|
81
|
+
<div class="alert alert-warning m-0" role="alert">You do not have permission to add users.</div>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
</div>
|
85
|
+
</div>
|
86
|
+
{% endif %}
|
87
|
+
<script src="{{ url_for('static', path='js/settings.js') }}"></script>
|
88
|
+
{% endblock content %}
|
@@ -0,0 +1,71 @@
|
|
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
|
+
<div class="row mb-2 p-2">
|
35
|
+
<div class="col d-flex align-items-center">
|
36
|
+
<p class="m-0">Welcome to gooseBit. Please create an admin user.</p>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
<form method="post" id="setup_form">
|
40
|
+
<div class="form-outline form-white mb-4">
|
41
|
+
<input type="email"
|
42
|
+
id="username"
|
43
|
+
name="username"
|
44
|
+
placeholder="Email"
|
45
|
+
class="form-control form-control-lg" />
|
46
|
+
</div>
|
47
|
+
<div class="form-outline form-white mb-4">
|
48
|
+
<input type="password"
|
49
|
+
id="password"
|
50
|
+
name="password"
|
51
|
+
placeholder="Password"
|
52
|
+
autocomplete="new-password"
|
53
|
+
class="form-control form-control-lg" />
|
54
|
+
</div>
|
55
|
+
<div class="form-outline form-white mb-4">
|
56
|
+
<input type="password"
|
57
|
+
id="password_confirm"
|
58
|
+
name="password_confirm"
|
59
|
+
placeholder="Confirm Password"
|
60
|
+
autocomplete="new-password"
|
61
|
+
class="form-control form-control-lg" />
|
62
|
+
</div>
|
63
|
+
<button class="btn btn-outline-light btn-lg px-5 w-100" type="submit">Setup</button>
|
64
|
+
</form>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
</div>
|
69
|
+
<script src="{{ url_for('static', path='js/setup.js') }}"></script>
|
70
|
+
</body>
|
71
|
+
</html>
|
@@ -4,17 +4,6 @@
|
|
4
4
|
<div class="row p-2 pt-4 g-4 d-flex justify-content-center">
|
5
5
|
<div class="col">
|
6
6
|
<table id="software-table" class="table table-hover">
|
7
|
-
<thead>
|
8
|
-
<tr>
|
9
|
-
<th>ID</th>
|
10
|
-
<th>Name</th>
|
11
|
-
<th>Version</th>
|
12
|
-
<th>Compatibility</th>
|
13
|
-
<th>Size</th>
|
14
|
-
</tr>
|
15
|
-
</thead>
|
16
|
-
<tbody id="software-list">
|
17
|
-
</tbody>
|
18
7
|
</table>
|
19
8
|
</div>
|
20
9
|
</div>
|