goosebit 0.1.0__py3-none-any.whl → 0.1.2__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 +8 -5
- goosebit/api/__init__.py +1 -1
- goosebit/api/devices.py +60 -36
- goosebit/api/download.py +28 -14
- goosebit/api/firmware.py +37 -44
- goosebit/api/helper.py +30 -0
- goosebit/api/rollouts.py +87 -0
- goosebit/api/routes.py +15 -7
- goosebit/auth/__init__.py +37 -21
- goosebit/db.py +5 -0
- goosebit/models.py +125 -6
- goosebit/permissions.py +33 -13
- goosebit/realtime/__init__.py +1 -1
- goosebit/realtime/logs.py +4 -6
- goosebit/settings.py +38 -29
- goosebit/telemetry/__init__.py +28 -0
- goosebit/telemetry/prometheus.py +10 -0
- goosebit/ui/__init__.py +1 -1
- goosebit/ui/routes.py +36 -39
- goosebit/ui/static/js/devices.js +191 -239
- goosebit/ui/static/js/firmware.js +234 -88
- goosebit/ui/static/js/index.js +83 -84
- goosebit/ui/static/js/logs.js +17 -10
- goosebit/ui/static/js/rollouts.js +198 -0
- goosebit/ui/static/js/util.js +66 -0
- goosebit/ui/templates/devices.html +75 -42
- goosebit/ui/templates/firmware.html +150 -34
- goosebit/ui/templates/index.html +9 -23
- goosebit/ui/templates/login.html +58 -27
- goosebit/ui/templates/logs.html +18 -3
- goosebit/ui/templates/nav.html +78 -25
- goosebit/ui/templates/rollouts.html +76 -0
- goosebit/updater/__init__.py +1 -1
- goosebit/updater/controller/__init__.py +1 -1
- goosebit/updater/controller/v1/__init__.py +1 -1
- goosebit/updater/controller/v1/routes.py +112 -24
- goosebit/updater/manager.py +237 -94
- goosebit/updater/routes.py +7 -8
- goosebit/updates/__init__.py +70 -0
- goosebit/updates/swdesc.py +83 -0
- goosebit-0.1.2.dist-info/METADATA +123 -0
- goosebit-0.1.2.dist-info/RECORD +51 -0
- goosebit/updater/download/__init__.py +0 -1
- goosebit/updater/download/routes.py +0 -6
- goosebit/updater/download/v1/__init__.py +0 -1
- goosebit/updater/download/v1/routes.py +0 -26
- goosebit/updater/misc.py +0 -69
- goosebit/updater/updates.py +0 -93
- goosebit-0.1.0.dist-info/METADATA +0 -37
- goosebit-0.1.0.dist-info/RECORD +0 -48
- {goosebit-0.1.0.dist-info → goosebit-0.1.2.dist-info}/LICENSE +0 -0
- {goosebit-0.1.0.dist-info → goosebit-0.1.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: goosebit
|
3
|
+
Version: 0.1.2
|
4
|
+
Summary:
|
5
|
+
Author: Upstream Data
|
6
|
+
Author-email: brett@upstreamdata.ca
|
7
|
+
Requires-Python: >=3.11,<4.0
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
11
|
+
Requires-Dist: aerich (>=0.7.2,<0.8.0)
|
12
|
+
Requires-Dist: aiocache (>=0.12.2,<0.13.0)
|
13
|
+
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
14
|
+
Requires-Dist: argon2-cffi (>=23.1.0,<24.0.0)
|
15
|
+
Requires-Dist: fastapi[uvicorn] (>=0.111.0,<0.112.0)
|
16
|
+
Requires-Dist: itsdangerous (>=2.2.0,<3.0.0)
|
17
|
+
Requires-Dist: jinja2 (>=3.1.4,<4.0.0)
|
18
|
+
Requires-Dist: joserfc (>=1.0.0,<2.0.0)
|
19
|
+
Requires-Dist: libconf (>=2.0.1,<3.0.0)
|
20
|
+
Requires-Dist: opentelemetry-distro (>=0.47b0,<0.48)
|
21
|
+
Requires-Dist: opentelemetry-exporter-prometheus (>=0.47b0,<0.48)
|
22
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi (>=0.47b0,<0.48)
|
23
|
+
Requires-Dist: python-multipart (>=0.0.9,<0.0.10)
|
24
|
+
Requires-Dist: semver (>=3.0.2,<4.0.0)
|
25
|
+
Requires-Dist: tortoise-orm (>=0.21.4,<0.22.0)
|
26
|
+
Requires-Dist: websockets (>=12.0,<13.0)
|
27
|
+
Description-Content-Type: text/markdown
|
28
|
+
|
29
|
+
# gooseBit
|
30
|
+
|
31
|
+
<img src="docs/img/goosebit-logo.png" style="width: 100px; height: 100px; display: block;">
|
32
|
+
|
33
|
+
---
|
34
|
+
|
35
|
+
A simplistic, opinionated remote update server implementing hawkBit™'s [DDI API](https://eclipse.dev/hawkbit/apis/ddi_api/).
|
36
|
+
|
37
|
+
## Setup
|
38
|
+
|
39
|
+
To set up, install the dependencies in `pyproject.toml` with `poetry install`. Then you can run gooseBit by running `main.py`.
|
40
|
+
|
41
|
+
## Initial Startup
|
42
|
+
|
43
|
+
The first time you start gooseBit, you should change the default username and password inside `settings.yaml`.
|
44
|
+
The default login credentials for testing are `admin@goosebit.local`, `admin`.
|
45
|
+
|
46
|
+
## Assumptions
|
47
|
+
|
48
|
+
- [SWUpdate](https://swupdate.org) used on device side.
|
49
|
+
|
50
|
+
## Current Feature Set
|
51
|
+
|
52
|
+
### Firmware repository
|
53
|
+
|
54
|
+
Uploading firmware images through frontend. All files should follow the format `{model}_{revision}_{version}`, where
|
55
|
+
`version` is either a semantic version or a datetime version in the format `YYYYMMDD-HHmmSS`.
|
56
|
+
|
57
|
+
### Automatic device registration
|
58
|
+
|
59
|
+
First time a new device connects, its configuration data is requested. `hw_model` and `hw_revision` are captured from
|
60
|
+
the configuration data (both fall back to `default` if not provided) which allows to distinguish different device
|
61
|
+
types and their revisions.
|
62
|
+
|
63
|
+
### Automatically update device to newest firmware
|
64
|
+
|
65
|
+
Once a device is registered it will get the newest available firmware from the repository based on model and revision.
|
66
|
+
|
67
|
+
### Manually update device to specific firmware
|
68
|
+
|
69
|
+
Frontend allows to assign specific firmware to be rolled out.
|
70
|
+
|
71
|
+
### Firmware rollout
|
72
|
+
|
73
|
+
Rollouts allow a fine-grained assignment of firmwares to devices. The reported device model and revision is combined
|
74
|
+
with the manually set feed and flavor values on a device to determine a matching rollout.
|
75
|
+
|
76
|
+
The feed is meant to model either different environments (like: dev, qa, live) or update channels (like. candidate,
|
77
|
+
fast, stable).
|
78
|
+
|
79
|
+
The flavor can be used for different type of builds (like: debug, prod).
|
80
|
+
|
81
|
+
### Pause updates
|
82
|
+
|
83
|
+
Device can be pinned to its current firmware.
|
84
|
+
|
85
|
+
### Realtime update logs
|
86
|
+
|
87
|
+
While an update is running, the update logs are captured and visualized in the frontend.
|
88
|
+
|
89
|
+
## Development
|
90
|
+
|
91
|
+
### Code formatting and linting
|
92
|
+
|
93
|
+
Code is formatted using different tools
|
94
|
+
|
95
|
+
- black and isort for `*.py`
|
96
|
+
- biomejs for `*.js`, `*.json`
|
97
|
+
- prettier for `*.html`, `*.md`, `*.yml`, `*.yaml`
|
98
|
+
|
99
|
+
Code is linted using different tools as well
|
100
|
+
|
101
|
+
- flake8 for `*.py`
|
102
|
+
- biomejs for `*.js`
|
103
|
+
|
104
|
+
Best to have pre-commit install git hooks that run all those tools before a commit:
|
105
|
+
|
106
|
+
```bash
|
107
|
+
poetry run pre-commit install
|
108
|
+
```
|
109
|
+
|
110
|
+
To manually apply the hooks to all files use:
|
111
|
+
|
112
|
+
```bash
|
113
|
+
pre-commit run --all-files
|
114
|
+
```
|
115
|
+
|
116
|
+
### Testing
|
117
|
+
|
118
|
+
Tests are implemented using pytest. To run all tests
|
119
|
+
|
120
|
+
```bash
|
121
|
+
poetry run pytest
|
122
|
+
```
|
123
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
goosebit/__init__.py,sha256=zoQORspnBSSDfv2MpqFWrZtE2U_pE8KLWFIpiPSZVE0,2026
|
2
|
+
goosebit/api/__init__.py,sha256=4RRIzqC6KbCESIC9Vc6G4iAa28IWQH4KROTGd2S4AoI,41
|
3
|
+
goosebit/api/devices.py,sha256=w7KVp1NjVPMeSG1lr6nbhFExiavCz2bWYhWlngIqegA,4940
|
4
|
+
goosebit/api/download.py,sha256=kyOs8iZDnSma-HXb2qB7d3CC2tqP_2EJEODXcVvmwTY,998
|
5
|
+
goosebit/api/firmware.py,sha256=AfWZy41N0LGfyPhOOZ8Y8TQVHQJSHc5YNp12GNf5GaQ,1696
|
6
|
+
goosebit/api/helper.py,sha256=ErvjXru-FKhScE_toNKaxLabFyx_uZgDRNEGBZks-60,1000
|
7
|
+
goosebit/api/rollouts.py,sha256=6WDhPtzx1iiAmhqEx7NjeIDfmi5io1yWUDPFxO7Yn44,2673
|
8
|
+
goosebit/api/routes.py,sha256=gnQGxgZBQGRXQjagAN2tAo7aNzcULClQlVfYZcsrdfk,698
|
9
|
+
goosebit/auth/__init__.py,sha256=BpneDLWS7yfW4QIjFejdTRsUP8rIOQJjC5nggC5FMsg,4222
|
10
|
+
goosebit/db.py,sha256=2jqsYhJklJQ8Zuh9D4sy9lYfcqg3E-R6bOUSAbvjpjk,984
|
11
|
+
goosebit/models.py,sha256=zrZr-bKFOnd3CxLFtgTMullwRS49614O4b51OLQX6wo,4312
|
12
|
+
goosebit/permissions.py,sha256=4WmoReDCK2oIZJQyMOU8eQvdczb-KGY1nqPn4hOf1n4,1836
|
13
|
+
goosebit/realtime/__init__.py,sha256=4RRIzqC6KbCESIC9Vc6G4iAa28IWQH4KROTGd2S4AoI,41
|
14
|
+
goosebit/realtime/logs.py,sha256=t-tYO5EM5J9PGHUrz_7VCPiyGHIr2zQ3nkObYNaS4-A,1248
|
15
|
+
goosebit/realtime/routes.py,sha256=DzL1xBsdSc4SF2FiOAeuVKmcAq3m4DYo6KqD_pCNMww,269
|
16
|
+
goosebit/settings.py,sha256=MaCmklqfDO75WSrHkVdZ978DnO-iq7nnKabqkv6MVI0,1589
|
17
|
+
goosebit/telemetry/__init__.py,sha256=Gxc2gRyX8CQy6cdHepa2Hsn6n50f07_fr-OS_OOFukY,707
|
18
|
+
goosebit/telemetry/prometheus.py,sha256=vORU7zgqlAepTk7IwTE5_vt18_tbILWlJhCfeGMwTsM,382
|
19
|
+
goosebit/ui/__init__.py,sha256=4RRIzqC6KbCESIC9Vc6G4iAa28IWQH4KROTGd2S4AoI,41
|
20
|
+
goosebit/ui/routes.py,sha256=-I0muVY2SDAhF57Vn6jR8oGMye8ouKG55f8z3wB461Y,3249
|
21
|
+
goosebit/ui/static/__init__.py,sha256=AsZiM3h9chQ_YaBbAD6TVFf0244Q9DetkDMl70q8swQ,135
|
22
|
+
goosebit/ui/static/favicon.ico,sha256=FLzKyAkMvQcMg58nzCJxQOfPFxT4yYbP7S82pdY6zng,4286
|
23
|
+
goosebit/ui/static/favicon.svg,sha256=2iEZocLSave4QTjaR3GxGVT1zaDNl6T_GtN61YHsvJU,6595
|
24
|
+
goosebit/ui/static/js/devices.js,sha256=hdGaB4_az6AyVeFs09Ah9obMaOEONgXMFeXp-VVXWm4,11686
|
25
|
+
goosebit/ui/static/js/firmware.js,sha256=cfaVeJz2It4JvO7POBd3nag1_Mc5w51vByja-04g5Ac,9062
|
26
|
+
goosebit/ui/static/js/index.js,sha256=sxguHagyK3agGqnNHMf2AbhhmawewP5YMts6IYKfGRI,5550
|
27
|
+
goosebit/ui/static/js/logs.js,sha256=e9gBfWC7WNS87sO6tBlU1c8oKIch_Ot9sVkUTta-vbU,862
|
28
|
+
goosebit/ui/static/js/rollouts.js,sha256=jzAnX-waTPWim_uY8mC99KzkIQvAP-CZOT0HNARPOqY,6619
|
29
|
+
goosebit/ui/static/js/util.js,sha256=zJJH3lUc_spskseRSAV57dCFJJPgZxpiVA3WUP0-_tk,2049
|
30
|
+
goosebit/ui/static/svg/goosebit-logo.svg,sha256=2iEZocLSave4QTjaR3GxGVT1zaDNl6T_GtN61YHsvJU,6595
|
31
|
+
goosebit/ui/templates/__init__.py,sha256=M-F9UIAOVyRdsVIjU-19cCiXh48-LZBAKvl8R4CLlkM,140
|
32
|
+
goosebit/ui/templates/devices.html,sha256=8NH9NJqtzi6quqTA8B3TTKRnydJcVvJg0jkGeqr1CNY,3899
|
33
|
+
goosebit/ui/templates/firmware.html,sha256=yBV7Jzx4cy_aXLpSQfni9DB0byQ8b44Xv1GNQ-Q0NyI,7254
|
34
|
+
goosebit/ui/templates/index.html,sha256=QxRMJwUb37dQCiLSR84ICsUOHfZmo1QuW-3mlSaPTHg,796
|
35
|
+
goosebit/ui/templates/login.html,sha256=Uql0T8JIUuTXYOzto_BNTfKRQiZAqZvyr3wPGjRjhWE,2729
|
36
|
+
goosebit/ui/templates/logs.html,sha256=rGqLPHALZoFxJzyB82_vOaWndzDbr7ZbvyAuah5nbxM,1289
|
37
|
+
goosebit/ui/templates/nav.html,sha256=njPQEUXst2tR_nKWh4reaOyslC2dfNXNOBIT8pbPnb4,4948
|
38
|
+
goosebit/ui/templates/rollouts.html,sha256=3BGmRpfj5RaRNljVAYUYgKSdMpv-Mkn88lS1-V29naE,2620
|
39
|
+
goosebit/updater/__init__.py,sha256=4RRIzqC6KbCESIC9Vc6G4iAa28IWQH4KROTGd2S4AoI,41
|
40
|
+
goosebit/updater/controller/__init__.py,sha256=4RRIzqC6KbCESIC9Vc6G4iAa28IWQH4KROTGd2S4AoI,41
|
41
|
+
goosebit/updater/controller/routes.py,sha256=8CnLb-kDuO-yFeWdu4apIyctCf9OzvJ011Az3QDGemU,123
|
42
|
+
goosebit/updater/controller/v1/__init__.py,sha256=4RRIzqC6KbCESIC9Vc6G4iAa28IWQH4KROTGd2S4AoI,41
|
43
|
+
goosebit/updater/controller/v1/routes.py,sha256=sbMJZ69vOS1P28jECHfq48HVZyct37FI7E3NJc9xst4,6585
|
44
|
+
goosebit/updater/manager.py,sha256=UIV3FOABplB0jmA4pZFSoL2yEtByX1Kw2CEydmt0g7U,11400
|
45
|
+
goosebit/updater/routes.py,sha256=B9ufgYIkSsdKniiUTeJnwYsa_MWdQtuu5m3MNmHXZQQ,717
|
46
|
+
goosebit/updates/__init__.py,sha256=uVpqyW2q3j7cf20zo9yIXzdHGaduyfWMjTWWpP4bUzY,1946
|
47
|
+
goosebit/updates/swdesc.py,sha256=wT4TQSE5S13akMLGrCGUfWWi2j1freFENhDvaC5oTx4,2545
|
48
|
+
goosebit-0.1.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
49
|
+
goosebit-0.1.2.dist-info/METADATA,sha256=CFvsYgfBtR0Ve4ejtAp6q-b-En6K43l3R-o0lWWx2Ec,3827
|
50
|
+
goosebit-0.1.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
51
|
+
goosebit-0.1.2.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
from .routes import router
|
@@ -1 +0,0 @@
|
|
1
|
-
from .routes import router
|
@@ -1,26 +0,0 @@
|
|
1
|
-
from fastapi import APIRouter, Depends
|
2
|
-
from fastapi.requests import Request
|
3
|
-
from fastapi.responses import FileResponse
|
4
|
-
|
5
|
-
from goosebit.settings import TOKEN_SWU_DIR, UPDATES_DIR
|
6
|
-
from goosebit.updater.manager import UpdateManager, get_update_manager
|
7
|
-
|
8
|
-
router = APIRouter(prefix="/v1")
|
9
|
-
|
10
|
-
|
11
|
-
@router.get("/{dev_id}/{file}")
|
12
|
-
async def download_file(request: Request, tenant: str, dev_id: str, file: str):
|
13
|
-
filename = UPDATES_DIR.joinpath(file)
|
14
|
-
return FileResponse(filename, media_type="application/octet-stream")
|
15
|
-
|
16
|
-
|
17
|
-
@router.get("/{dev_id}/cfd_conf/{file}")
|
18
|
-
async def download_cfd_conf(
|
19
|
-
request: Request,
|
20
|
-
tenant: str,
|
21
|
-
dev_id: str,
|
22
|
-
file: str,
|
23
|
-
updater: UpdateManager = Depends(get_update_manager),
|
24
|
-
):
|
25
|
-
filename = TOKEN_SWU_DIR.joinpath(dev_id, file)
|
26
|
-
return FileResponse(filename, media_type="application/octet-stream")
|
goosebit/updater/misc.py
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import datetime
|
4
|
-
import hashlib
|
5
|
-
from pathlib import Path
|
6
|
-
|
7
|
-
from goosebit.models import Device
|
8
|
-
from goosebit.settings import UPDATES_DIR
|
9
|
-
|
10
|
-
|
11
|
-
def sha1_hash_file(file_path: Path):
|
12
|
-
with file_path.open("rb") as f:
|
13
|
-
sha1_hash = hashlib.file_digest(f, "sha1")
|
14
|
-
return sha1_hash.hexdigest()
|
15
|
-
|
16
|
-
|
17
|
-
def get_newest_fw() -> str:
|
18
|
-
fw_files = [f for f in UPDATES_DIR.iterdir() if f.suffix == ".swu"]
|
19
|
-
if len(fw_files) == 0:
|
20
|
-
return ""
|
21
|
-
|
22
|
-
return str(sorted(fw_files, key=lambda x: fw_sort_key(x), reverse=True)[0].name)
|
23
|
-
|
24
|
-
|
25
|
-
def fw_sort_key(filename: Path) -> datetime.datetime:
|
26
|
-
image_data = filename.stem.split("_")
|
27
|
-
if len(image_data) == 3:
|
28
|
-
tenant, date, time = image_data
|
29
|
-
elif len(image_data) == 4:
|
30
|
-
tenant, hw_version, date, time = image_data
|
31
|
-
else:
|
32
|
-
return datetime.datetime.now()
|
33
|
-
|
34
|
-
return datetime.datetime.strptime(f"{date}_{time}", "%Y%m%d_%H%M%S")
|
35
|
-
|
36
|
-
|
37
|
-
def get_fw_components(filename: Path) -> dict:
|
38
|
-
image_data = filename.stem.split("_")
|
39
|
-
if len(image_data) == 3:
|
40
|
-
tenant, date, time = image_data
|
41
|
-
return {
|
42
|
-
"date": datetime.datetime.strptime(f"{date}_{time}", "%Y%m%d_%H%M%S"),
|
43
|
-
"day": date,
|
44
|
-
"time": time,
|
45
|
-
"tenant": tenant,
|
46
|
-
"hw_version": 0,
|
47
|
-
}
|
48
|
-
elif len(image_data) == 4:
|
49
|
-
tenant, hw_version, date, time = image_data
|
50
|
-
return {
|
51
|
-
"date": datetime.datetime.strptime(f"{date}_{time}", "%Y%m%d_%H%M%S"),
|
52
|
-
"day": date,
|
53
|
-
"time": time,
|
54
|
-
"tenant": tenant,
|
55
|
-
"hw_version": int(hw_version.upper().replace("V", "")),
|
56
|
-
}
|
57
|
-
|
58
|
-
|
59
|
-
async def get_device_by_uuid(dev_id: str) -> Device:
|
60
|
-
if dev_id == "unknown":
|
61
|
-
return Device(
|
62
|
-
uuid="unknown",
|
63
|
-
name="Unknown",
|
64
|
-
fw_file="latest",
|
65
|
-
fw_version=None,
|
66
|
-
last_state=None,
|
67
|
-
last_log=None,
|
68
|
-
)
|
69
|
-
return (await Device.get_or_create(uuid=dev_id))[0]
|
goosebit/updater/updates.py
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
import datetime
|
2
|
-
from pathlib import Path
|
3
|
-
from typing import Optional
|
4
|
-
|
5
|
-
from fastapi.requests import Request
|
6
|
-
|
7
|
-
from goosebit.settings import TOKEN_SWU_DIR, UPDATES_DIR
|
8
|
-
from goosebit.updater.misc import get_newest_fw, sha1_hash_file
|
9
|
-
|
10
|
-
|
11
|
-
class FirmwareArtifact:
|
12
|
-
def __init__(self, file: str = None, dev_id: str = None):
|
13
|
-
if file == "latest":
|
14
|
-
self.file = get_newest_fw()
|
15
|
-
elif file == "pinned":
|
16
|
-
self.file = None
|
17
|
-
else:
|
18
|
-
self.file = file
|
19
|
-
self.dev_id = dev_id
|
20
|
-
|
21
|
-
def __eq__(self, other):
|
22
|
-
if isinstance(other, str):
|
23
|
-
return self.name == other
|
24
|
-
elif isinstance(other, FirmwareArtifact):
|
25
|
-
return self.file == other.file
|
26
|
-
return False
|
27
|
-
|
28
|
-
def is_empty(self) -> bool:
|
29
|
-
return self.file is None
|
30
|
-
|
31
|
-
def file_exists(self) -> bool:
|
32
|
-
if self.is_empty():
|
33
|
-
return False
|
34
|
-
if self.file == "cloudflared.swu":
|
35
|
-
return True
|
36
|
-
return self.path.exists()
|
37
|
-
|
38
|
-
@property
|
39
|
-
def name(self):
|
40
|
-
if not self.is_empty():
|
41
|
-
return self.file.split(".")[0]
|
42
|
-
|
43
|
-
@property
|
44
|
-
def version(self):
|
45
|
-
if not self.is_empty():
|
46
|
-
return "_".join(self.name.split("_")[2:])
|
47
|
-
|
48
|
-
@property
|
49
|
-
def timestamp(self):
|
50
|
-
return datetime.datetime.strptime(self.version, "%Y%m%d_%H%M%S")
|
51
|
-
|
52
|
-
@property
|
53
|
-
def path(self) -> Optional[Path]:
|
54
|
-
if not self.is_empty():
|
55
|
-
if self.file == "cloudflared.swu":
|
56
|
-
return TOKEN_SWU_DIR.joinpath(self.dev_id, self.file)
|
57
|
-
return UPDATES_DIR.joinpath(self.file)
|
58
|
-
|
59
|
-
@property
|
60
|
-
def dl_endpoint(self):
|
61
|
-
if self.file == "cloudflared.swu":
|
62
|
-
return "download_cfd_conf"
|
63
|
-
return "download_file"
|
64
|
-
|
65
|
-
def generate_chunk(self, request: Request, tenant: str, dev_id: str) -> list:
|
66
|
-
if not self.file_exists():
|
67
|
-
return []
|
68
|
-
return [
|
69
|
-
{
|
70
|
-
"part": "os",
|
71
|
-
"version": "1",
|
72
|
-
"name": self.file,
|
73
|
-
"artifacts": [
|
74
|
-
{
|
75
|
-
"filename": self.file,
|
76
|
-
"hashes": {"sha1": sha1_hash_file(self.path)},
|
77
|
-
"size": self.path.stat().st_size,
|
78
|
-
"_links": {
|
79
|
-
"download": {
|
80
|
-
"href": str(
|
81
|
-
request.url_for(
|
82
|
-
self.dl_endpoint,
|
83
|
-
tenant=tenant,
|
84
|
-
dev_id=dev_id,
|
85
|
-
file=self.file,
|
86
|
-
)
|
87
|
-
)
|
88
|
-
}
|
89
|
-
},
|
90
|
-
}
|
91
|
-
],
|
92
|
-
}
|
93
|
-
]
|
@@ -1,37 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: goosebit
|
3
|
-
Version: 0.1.0
|
4
|
-
Summary:
|
5
|
-
Author: Upstream Data
|
6
|
-
Author-email: brett@upstreamdata.ca
|
7
|
-
Requires-Python: >=3.12,<4.0
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
9
|
-
Classifier: Programming Language :: Python :: 3.12
|
10
|
-
Requires-Dist: aerich (>=0.7.2,<0.8.0)
|
11
|
-
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
12
|
-
Requires-Dist: fastapi[uvicorn] (>=0.111.0,<0.112.0)
|
13
|
-
Requires-Dist: itsdangerous (>=2.2.0,<3.0.0)
|
14
|
-
Requires-Dist: jinja2 (>=3.1.4,<4.0.0)
|
15
|
-
Requires-Dist: passlib[bcrypt] (>=1.7.4,<2.0.0)
|
16
|
-
Requires-Dist: python-jose (>=3.3.0,<4.0.0)
|
17
|
-
Requires-Dist: python-multipart (>=0.0.9,<0.0.10)
|
18
|
-
Requires-Dist: tortoise-orm (>=0.21.4,<0.22.0)
|
19
|
-
Requires-Dist: websockets (>=12.0,<13.0)
|
20
|
-
Description-Content-Type: text/markdown
|
21
|
-
|
22
|
-
# goosebit
|
23
|
-
<img src="docs/img/goosebit-logo.png?raw=true" style="width: 100px; height: 100px; display: block;">
|
24
|
-
|
25
|
-
---
|
26
|
-
|
27
|
-
A simplistic remote update server.
|
28
|
-
|
29
|
-
|
30
|
-
## Setup
|
31
|
-
|
32
|
-
To set up, install the dependencies in `pyproject.toml` with `poetry install`. Then you can run GooseBit by running `main.py`.
|
33
|
-
|
34
|
-
## Initial Startup
|
35
|
-
|
36
|
-
The first time you start GooseBit, you should change the default username and password inside `goosebit/settings.py`, as well as generate a new secret key.
|
37
|
-
The default login credentials for testing are `admin@goosebit.local`, `admin`.
|
goosebit-0.1.0.dist-info/RECORD
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
goosebit/__init__.py,sha256=hfY3hKnwkJewwVT_DDFm_FjsvSUMsVDsBECL2g1gMOQ,1837
|
2
|
-
goosebit/api/__init__.py,sha256=Pn8KJu4sVaJKSSley4e1K5D_I3PrDd8ZLpWXchouH_o,27
|
3
|
-
goosebit/api/devices.py,sha256=PwjYuG3omM59EDQNGwyPik4wuZ_NOwfRoLBSFHoTMBM,3219
|
4
|
-
goosebit/api/download.py,sha256=I9aL3ZkT2T5hfyTsuwwR5vFF5XjqnjHV0WjK0p-oToQ,607
|
5
|
-
goosebit/api/firmware.py,sha256=0bvQuDX4ReiT6Xp4OA6pYQQ7u62DkOFoAnK7soZY2OQ,1778
|
6
|
-
goosebit/api/routes.py,sha256=JLXWOwvOyLGcHX2nDl9ZVJSan5wdfDueYn0XGLPQ0NA,365
|
7
|
-
goosebit/auth/__init__.py,sha256=47vSo8srLG_8P1gdb0mmnrxfqsvohIwdzrQbcliEo5g,3638
|
8
|
-
goosebit/db.py,sha256=lIQR3s290qZX54NL1J-85Ge6l98xbV9_oRaCO48vdTY,787
|
9
|
-
goosebit/models.py,sha256=7DMMZ29XnNnMjkfOiZ_lgLNf_3j_az4PlrfxfB2PTlw,765
|
10
|
-
goosebit/permissions.py,sha256=Yg90HiQLfInSiP-_tUeirXrTc4CpuNSrIsBp3QYCTYI,1166
|
11
|
-
goosebit/realtime/__init__.py,sha256=Pn8KJu4sVaJKSSley4e1K5D_I3PrDd8ZLpWXchouH_o,27
|
12
|
-
goosebit/realtime/logs.py,sha256=hopd4zpEmTU4_De_tJwItoyyNjk1JiN5onyM8_q07S0,1207
|
13
|
-
goosebit/realtime/routes.py,sha256=DzL1xBsdSc4SF2FiOAeuVKmcAq3m4DYo6KqD_pCNMww,269
|
14
|
-
goosebit/settings.py,sha256=4bryUrICSDB_GDb0mNv7ix5vGH8qmZCo5KDYHqgqZuA,1163
|
15
|
-
goosebit/ui/__init__.py,sha256=Pn8KJu4sVaJKSSley4e1K5D_I3PrDd8ZLpWXchouH_o,27
|
16
|
-
goosebit/ui/routes.py,sha256=Iy7B1v5_Ao_KKEe_hI10C7hiQOOIiJJxAgcnW3_CW9U,2791
|
17
|
-
goosebit/ui/static/__init__.py,sha256=AsZiM3h9chQ_YaBbAD6TVFf0244Q9DetkDMl70q8swQ,135
|
18
|
-
goosebit/ui/static/favicon.ico,sha256=FLzKyAkMvQcMg58nzCJxQOfPFxT4yYbP7S82pdY6zng,4286
|
19
|
-
goosebit/ui/static/favicon.svg,sha256=2iEZocLSave4QTjaR3GxGVT1zaDNl6T_GtN61YHsvJU,6595
|
20
|
-
goosebit/ui/static/js/devices.js,sha256=IpDhT_9nm9lELVkBriKsIpEReajGfZ7bhsHfpvRaZtE,12550
|
21
|
-
goosebit/ui/static/js/firmware.js,sha256=S-a8dG6JB_Qvxw0mhG0ksYuPPzk4gLs6AIR_a7suVqk,3919
|
22
|
-
goosebit/ui/static/js/index.js,sha256=9Mlf679A_P2FcGx7cYfLPi3FVWJ6Yl2_vUPUpP4bEGg,5744
|
23
|
-
goosebit/ui/static/js/logs.js,sha256=ajaOBtW__CToZLOo17bxNIaY6WkPyy_TDQD0Emwh0DI,611
|
24
|
-
goosebit/ui/static/svg/goosebit-logo.svg,sha256=2iEZocLSave4QTjaR3GxGVT1zaDNl6T_GtN61YHsvJU,6595
|
25
|
-
goosebit/ui/templates/__init__.py,sha256=M-F9UIAOVyRdsVIjU-19cCiXh48-LZBAKvl8R4CLlkM,140
|
26
|
-
goosebit/ui/templates/devices.html,sha256=RQGeBnYZxTI382DuEbUyIRDyd3ZCkxo0vie4hOM905k,3025
|
27
|
-
goosebit/ui/templates/firmware.html,sha256=JW6h0LxLHcvq4KIgEMXk7qyxWE0bTTbbbpjlOK_vQgE,2059
|
28
|
-
goosebit/ui/templates/index.html,sha256=hzqo3o1rAqIXw-mTDxc4ah2M35oY3BJibnYNzlSEhiw,1096
|
29
|
-
goosebit/ui/templates/login.html,sha256=Ip_-p3DbRyCkIjAfEBO2MyzEynQk1m9eKVGpuPLEF3c,1862
|
30
|
-
goosebit/ui/templates/logs.html,sha256=ysdE8luiO8NNpeqtW2XCZKU7_4Muo13OGh9AzlmJtDg,633
|
31
|
-
goosebit/ui/templates/nav.html,sha256=0ZWCkg4hJNFM64h_khcg-xs-8VixnYo35LbNi1aiF-I,3529
|
32
|
-
goosebit/updater/__init__.py,sha256=Pn8KJu4sVaJKSSley4e1K5D_I3PrDd8ZLpWXchouH_o,27
|
33
|
-
goosebit/updater/controller/__init__.py,sha256=Pn8KJu4sVaJKSSley4e1K5D_I3PrDd8ZLpWXchouH_o,27
|
34
|
-
goosebit/updater/controller/routes.py,sha256=8CnLb-kDuO-yFeWdu4apIyctCf9OzvJ011Az3QDGemU,123
|
35
|
-
goosebit/updater/controller/v1/__init__.py,sha256=Pn8KJu4sVaJKSSley4e1K5D_I3PrDd8ZLpWXchouH_o,27
|
36
|
-
goosebit/updater/controller/v1/routes.py,sha256=-5sjiIRz5_rGppV9tL13ftGKDPYqNhXhzuiY7htXiLo,2290
|
37
|
-
goosebit/updater/download/__init__.py,sha256=Pn8KJu4sVaJKSSley4e1K5D_I3PrDd8ZLpWXchouH_o,27
|
38
|
-
goosebit/updater/download/routes.py,sha256=HNvpdlZvEeVfpY-JLhvUKHDd9eGKCAOFjh1EN98J9T0,121
|
39
|
-
goosebit/updater/download/v1/__init__.py,sha256=Pn8KJu4sVaJKSSley4e1K5D_I3PrDd8ZLpWXchouH_o,27
|
40
|
-
goosebit/updater/download/v1/routes.py,sha256=cwzdseHhOzfuze6r8_YuOC-YuPyu9QDvhSs1UZE5dho,840
|
41
|
-
goosebit/updater/manager.py,sha256=179DJtW0hy5ztv-uM8jhTuavgVTm4JwgMjucSHTpfUU,5896
|
42
|
-
goosebit/updater/misc.py,sha256=NIYdquYoH6ibGDxnIFVLzTKbb7wwc3eOIxhHXwLR4Ew,2000
|
43
|
-
goosebit/updater/routes.py,sha256=pziZ15nTUV6zCkvlrdi7r5Az9U2INq3C6X_cj0yYHDw,788
|
44
|
-
goosebit/updater/updates.py,sha256=OhSeRsXQgzUPffR_SJ_UGepOqScVZREoxNWx6PrxhbA,2837
|
45
|
-
goosebit-0.1.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
46
|
-
goosebit-0.1.0.dist-info/METADATA,sha256=zta3l3gBIW3roHD4lS-ow0ypntg_lz31u9AVSVbUrEs,1287
|
47
|
-
goosebit-0.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
48
|
-
goosebit-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|