mirror of
https://github.com/henrywhitaker3/Speedtest-Tracker.git
synced 2025-12-22 05:33:54 +01:00
Compare commits
379 Commits
v1.7.15
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e620845fce | ||
|
|
3955a2dad0 | ||
|
|
6905a5aac0 | ||
|
|
b3ba6a8b06 | ||
|
|
1e1a2c9b69 | ||
|
|
1c00beed86 | ||
|
|
603f618720 | ||
|
|
fa41bdd40d | ||
|
|
9d2f1858a0 | ||
|
|
6c6a12bb5f | ||
|
|
5885824c54 | ||
|
|
ea78415157 | ||
|
|
efecd28afb | ||
|
|
8d32d06768 | ||
|
|
8cb2e8a323 | ||
|
|
58e82dc9a8 | ||
|
|
0dff534dfd | ||
|
|
fba69514b2 | ||
|
|
1fcaa795cd | ||
|
|
858dcc1f7b | ||
|
|
283de67e17 | ||
|
|
d92dd33b84 | ||
|
|
0c8e074a86 | ||
|
|
0384f61f15 | ||
|
|
98e037bf97 | ||
|
|
0b1645cda0 | ||
|
|
370c73e1a5 | ||
|
|
9fb573e785 | ||
|
|
f6bb77ce71 | ||
|
|
219c5d6b7a | ||
|
|
ce549b5b7a | ||
|
|
a962865867 | ||
|
|
48fbbc3713 | ||
|
|
f809f816c1 | ||
|
|
91b7ec7fab | ||
|
|
5dbc6b900a | ||
|
|
4dc3e5b09c | ||
|
|
7cf8dfc0b2 | ||
|
|
99c64af482 | ||
|
|
0b8ccfd8d3 | ||
|
|
7b16f1ab09 | ||
|
|
845359726d | ||
|
|
4e6049b7ec | ||
|
|
5db1106c37 | ||
|
|
cb5abcfa98 | ||
|
|
76e21bfe9d | ||
|
|
dd871606a4 | ||
|
|
492b5df6ae | ||
|
|
f92c040f63 | ||
|
|
b82b7c6307 | ||
|
|
20b3cb98b6 | ||
|
|
bed7e841c2 | ||
|
|
1d9fba6421 | ||
|
|
78fcf49688 | ||
|
|
b0659fd0d3 | ||
|
|
5e2cc96830 | ||
|
|
39b3637b44 | ||
|
|
9870aeb9ba | ||
|
|
fe871a7d58 | ||
|
|
c9e86ac5aa | ||
|
|
b232c21ae1 | ||
|
|
305e4bb17f | ||
|
|
0b593e60b4 | ||
|
|
d91a5ef0a4 | ||
|
|
a71356f197 | ||
|
|
ac4fd6cb20 | ||
|
|
2861196cff | ||
|
|
85d2d87e53 | ||
|
|
a45f52f7b3 | ||
|
|
2d60f1c81d | ||
|
|
ce4913afa6 | ||
|
|
2ed811e949 | ||
|
|
75c5a49398 | ||
|
|
a2d8886bae | ||
|
|
ba8f82aa98 | ||
|
|
1fd033b3c1 | ||
|
|
a170a96ced | ||
|
|
99d80ed235 | ||
|
|
9541117125 | ||
|
|
db7e307b20 | ||
|
|
e90619b536 | ||
|
|
fa58ab0168 | ||
|
|
c98ddc74c1 | ||
|
|
2d6e00f51b | ||
|
|
744f045ccf | ||
|
|
56e3b15e75 | ||
|
|
8468ffc022 | ||
|
|
7fece6fd89 | ||
|
|
c5c3a54809 | ||
|
|
2837f9f031 | ||
|
|
d1efe9a166 | ||
|
|
be2c70605e | ||
|
|
e21ce23bbe | ||
|
|
6b7bbf089f | ||
|
|
cee8a887ef | ||
|
|
dcd3381259 | ||
|
|
dab973dbf8 | ||
|
|
0c23b16164 | ||
|
|
02f76ca617 | ||
|
|
11fb44b789 | ||
|
|
715dac7828 | ||
|
|
9b4a0afda0 | ||
|
|
da8e7f409c | ||
|
|
676df45ae9 | ||
|
|
2f8d12dba1 | ||
|
|
4929d0efac | ||
|
|
bee655dd8f | ||
|
|
32bcde92bb | ||
|
|
f412dc70e6 | ||
|
|
681fc8fbed | ||
|
|
a81adebe62 | ||
|
|
1ae66474f3 | ||
|
|
dd158c7c83 | ||
|
|
fd70dc6cb8 | ||
|
|
77576f6537 | ||
|
|
f41039ce78 | ||
|
|
2ada8a22a9 | ||
|
|
52a91af086 | ||
|
|
af66d17ba4 | ||
|
|
dd27802e0f | ||
|
|
3444b8be18 | ||
|
|
043fd42b9a | ||
|
|
eeec6ae26c | ||
|
|
05d276fca0 | ||
|
|
f5f378e8c3 | ||
|
|
2f8137db0e | ||
|
|
95dbbc96b6 | ||
|
|
3d44da1f87 | ||
|
|
4e8b46f786 | ||
|
|
f110704a7a | ||
|
|
73517f92f7 | ||
|
|
ea9e5c1a52 | ||
|
|
ad36edc749 | ||
|
|
1467bee847 | ||
|
|
67eec58d74 | ||
|
|
752ef06626 | ||
|
|
feffc0023f | ||
|
|
f918288728 | ||
|
|
b74e69314a | ||
|
|
44c6c256c0 | ||
|
|
fde30602b0 | ||
|
|
a7652af2ba | ||
|
|
95325db128 | ||
|
|
46d13fd08a | ||
|
|
1637e66bef | ||
|
|
71e132e513 | ||
|
|
21ed016d25 | ||
|
|
8bc6975b69 | ||
|
|
5a0f5a2b3c | ||
|
|
17b72b91d0 | ||
|
|
0910bbb757 | ||
|
|
e9de226c7a | ||
|
|
10137a602a | ||
|
|
342e87b08d | ||
|
|
100cc1d768 | ||
|
|
2d1af61fb1 | ||
|
|
41a3bb3b5b | ||
|
|
365acda11c | ||
|
|
2df827b722 | ||
|
|
48839a0aa3 | ||
|
|
05e856799a | ||
|
|
2a243d251c | ||
|
|
73999eba53 | ||
|
|
24a2c0e7df | ||
|
|
c18b334128 | ||
|
|
e479e828bb | ||
|
|
f9b6f99a05 | ||
|
|
d577954660 | ||
|
|
cf5bafe586 | ||
|
|
7eb668a892 | ||
|
|
bc28094e67 | ||
|
|
326b0edbac | ||
|
|
eb1ec516b9 | ||
|
|
33c61071d4 | ||
|
|
6306824351 | ||
|
|
47cbce56a9 | ||
|
|
f8387e4f4b | ||
|
|
353b381f25 | ||
|
|
065bc45d11 | ||
|
|
4d78eff393 | ||
|
|
14ba9e092f | ||
|
|
628b0fdbea | ||
|
|
2cb31d52a7 | ||
|
|
8b8c3ae801 | ||
|
|
846942929e | ||
|
|
8e151c9ce5 | ||
|
|
a568d83dbc | ||
|
|
dcf94b6a4b | ||
|
|
47ecb7d58c | ||
|
|
4e61e0fccb | ||
|
|
59a4a25bed | ||
|
|
0e60bf195f | ||
|
|
2b57d6c244 | ||
|
|
8f846bd4fe | ||
|
|
081eac4f1e | ||
|
|
7071d0ac4c | ||
|
|
87aba591c0 | ||
|
|
5e5e9d8992 | ||
|
|
d16889f406 | ||
|
|
5d0020a707 | ||
|
|
e9d8f4754b | ||
|
|
18439da15a | ||
|
|
2919c9fb9b | ||
|
|
3097c244de | ||
|
|
6c64613628 | ||
|
|
2e39a238e7 | ||
|
|
8e003017bd | ||
|
|
9190385f93 | ||
|
|
226e2c42a7 | ||
|
|
c55baae478 | ||
|
|
7654da9559 | ||
|
|
7922955217 | ||
|
|
c90b40a96f | ||
|
|
72ff3317fa | ||
|
|
b00db26910 | ||
|
|
b31252a7b3 | ||
|
|
a836967445 | ||
|
|
ced5a2e7a4 | ||
|
|
270abea478 | ||
|
|
efc9b29360 | ||
|
|
b9d289c9d2 | ||
|
|
1562e13e19 | ||
|
|
0b00ba2304 | ||
|
|
5f5b731fbe | ||
|
|
dd7df0df81 | ||
|
|
0e454f8143 | ||
|
|
f61be56f2e | ||
|
|
f33af5fc41 | ||
|
|
861d354d15 | ||
|
|
07b419801c | ||
|
|
be2be4583c | ||
|
|
b05168ac6f | ||
|
|
c70e7a26c4 | ||
|
|
daf9975853 | ||
|
|
7a03c64ceb | ||
|
|
29a0b3d0a1 | ||
|
|
1c09d39b1e | ||
|
|
439c6322f2 | ||
|
|
d093a89829 | ||
|
|
6ffd59485a | ||
|
|
7dd7cdda8c | ||
|
|
15e4537ea6 | ||
|
|
4403cf161c | ||
|
|
7a950b838a | ||
|
|
21d3aea934 | ||
|
|
342d3fabf4 | ||
|
|
4b95cb12ef | ||
|
|
8ff87c2e7a | ||
|
|
149c69ce64 | ||
|
|
933291c5fc | ||
|
|
29c36d88e2 | ||
|
|
4b167af50e | ||
|
|
7d0ea79798 | ||
|
|
375eab288d | ||
|
|
8e2ddd974a | ||
|
|
d78c3e2669 | ||
|
|
a83d4e363b | ||
|
|
334623454d | ||
|
|
a7aa3e7885 | ||
|
|
2e1022c116 | ||
|
|
355d38acb7 | ||
|
|
1e9887ac46 | ||
|
|
8538dd231f | ||
|
|
5793140e89 | ||
|
|
56ecff1a09 | ||
|
|
eff8f92016 | ||
|
|
0b8c633a75 | ||
|
|
8ae4bc602c | ||
|
|
9028ffb7cd | ||
|
|
62fde8fb2a | ||
|
|
601d46915c | ||
|
|
751acff32c | ||
|
|
4e133e97c7 | ||
|
|
61a85ced34 | ||
|
|
aa37a50f57 | ||
|
|
99ac0b008c | ||
|
|
b4147ce57a | ||
|
|
80b80811aa | ||
|
|
2d8bc7d71f | ||
|
|
a036ed7c8f | ||
|
|
01b097b4d3 | ||
|
|
a6326f6d98 | ||
|
|
ef34238043 | ||
|
|
212c7a99bf | ||
|
|
8e556250aa | ||
|
|
87fc35c1c5 | ||
|
|
b6fca4e1a7 | ||
|
|
ae40f503e2 | ||
|
|
61da652b82 | ||
|
|
ec56337b99 | ||
|
|
0ed4674c3f | ||
|
|
dd56a667cd | ||
|
|
3dc494b02e | ||
|
|
35a1de7333 | ||
|
|
f6de728265 | ||
|
|
d6da8962eb | ||
|
|
db12f8cfee | ||
|
|
9b8c764b0d | ||
|
|
7999867f50 | ||
|
|
1fcdd5bb1d | ||
|
|
900ecb2218 | ||
|
|
7da1d3d760 | ||
|
|
0eff685277 | ||
|
|
94e4919b44 | ||
|
|
1e4604d975 | ||
|
|
b30ec477f4 | ||
|
|
13a00d43bf | ||
|
|
2d779e50b0 | ||
|
|
322ef98608 | ||
|
|
cc2e4ae91d | ||
|
|
7ab788e61d | ||
|
|
da34c723a3 | ||
|
|
e7c3dcd369 | ||
|
|
0d80c198af | ||
|
|
41f4e9667f | ||
|
|
f93c801f27 | ||
|
|
30657a07e8 | ||
|
|
6e712cbbdc | ||
|
|
375ecf650c | ||
|
|
a15d633322 | ||
|
|
ef485b2909 | ||
|
|
79a85fdba9 | ||
|
|
a49979daae | ||
|
|
053138093c | ||
|
|
e10b4dccde | ||
|
|
2fbfe6f48e | ||
|
|
f3043baa5a | ||
|
|
c1f44fe733 | ||
|
|
cd87a902a7 | ||
|
|
71101f71eb | ||
|
|
5a56b80322 | ||
|
|
4173321ce5 | ||
|
|
de8884b550 | ||
|
|
4bc002ef6c | ||
|
|
ed50a05415 | ||
|
|
603c306107 | ||
|
|
49a0f9087e | ||
|
|
640a62eed7 | ||
|
|
dc2ee05344 | ||
|
|
30ead5e82a | ||
|
|
532837621f | ||
|
|
ad60f19a53 | ||
|
|
92544142a9 | ||
|
|
08926d19ab | ||
|
|
2a9f9e7c55 | ||
|
|
a73e058ab1 | ||
|
|
1a8f4d3139 | ||
|
|
f32fcf8256 | ||
|
|
6ab2ae9e53 | ||
|
|
33169be746 | ||
|
|
7dccdb3c1f | ||
|
|
f0cd810f43 | ||
|
|
5aee6dae70 | ||
|
|
c4a26624e6 | ||
|
|
372086bc8e | ||
|
|
b8a3a0e72e | ||
|
|
1a688ebe69 | ||
|
|
3102e9e7ab | ||
|
|
97bf5bc4af | ||
|
|
2a4f70c70e | ||
|
|
2d805e3936 | ||
|
|
6cccefa21f | ||
|
|
f0e2d28d8b | ||
|
|
8a8c608bd0 | ||
|
|
c8fdb13f45 | ||
|
|
0e1b69f96f | ||
|
|
f5b14cdf99 | ||
|
|
05bed7b831 | ||
|
|
2526f9596b | ||
|
|
e2967ac1dd | ||
|
|
7a62b113bf | ||
|
|
4b8b4ccfa3 | ||
|
|
9f6fcb7439 | ||
|
|
33bbe14100 | ||
|
|
2541a371fb | ||
|
|
f38e60346a | ||
|
|
f8c46dc047 | ||
|
|
7e3bc8724f | ||
|
|
4617213073 |
19
.dockerignore
Normal file
19
.dockerignore
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/node_modules
|
||||||
|
/public/hot
|
||||||
|
/public/storage
|
||||||
|
/storage/*.key
|
||||||
|
/vendor
|
||||||
|
.env
|
||||||
|
.env.backup
|
||||||
|
.phpunit.result.cache
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
_ide_helper.php
|
||||||
|
.idea
|
||||||
|
.config
|
||||||
|
reports/
|
||||||
79
.github/dependabot.yml
vendored
Normal file
79
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: composer
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
target-branch: dev
|
||||||
|
ignore:
|
||||||
|
- dependency-name: laravel/framework
|
||||||
|
versions:
|
||||||
|
- 7.30.3
|
||||||
|
- 8.36.2
|
||||||
|
- 8.37.0
|
||||||
|
- 8.38.0
|
||||||
|
- dependency-name: doctrine/dbal
|
||||||
|
versions:
|
||||||
|
- 2.13.0
|
||||||
|
- 3.0.0
|
||||||
|
- dependency-name: phpunit/phpunit
|
||||||
|
versions:
|
||||||
|
- 9.5.1
|
||||||
|
- 9.5.3
|
||||||
|
- dependency-name: facade/ignition
|
||||||
|
versions:
|
||||||
|
- 2.5.10
|
||||||
|
- 2.5.11
|
||||||
|
- 2.5.12
|
||||||
|
- 2.5.13
|
||||||
|
- 2.5.9
|
||||||
|
- dependency-name: laravel/tinker
|
||||||
|
versions:
|
||||||
|
- 2.6.0
|
||||||
|
- dependency-name: nunomaduro/collision
|
||||||
|
versions:
|
||||||
|
- 5.3.0
|
||||||
|
- dependency-name: nunomaduro/larastan
|
||||||
|
versions:
|
||||||
|
- 0.6.13
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
target-branch: dev
|
||||||
|
ignore:
|
||||||
|
- dependency-name: chart.js
|
||||||
|
versions:
|
||||||
|
- 3.1.0
|
||||||
|
- 3.1.1
|
||||||
|
- dependency-name: laravel-mix
|
||||||
|
versions:
|
||||||
|
- 6.0.16
|
||||||
|
- dependency-name: y18n
|
||||||
|
versions:
|
||||||
|
- 4.0.1
|
||||||
|
- 4.0.2
|
||||||
|
- dependency-name: react-dom
|
||||||
|
versions:
|
||||||
|
- 17.0.2
|
||||||
|
- dependency-name: react
|
||||||
|
versions:
|
||||||
|
- 17.0.2
|
||||||
|
- dependency-name: react-bootstrap
|
||||||
|
versions:
|
||||||
|
- 1.4.3
|
||||||
|
- 1.5.0
|
||||||
|
- dependency-name: sass
|
||||||
|
versions:
|
||||||
|
- 1.32.5
|
||||||
|
- 1.32.6
|
||||||
|
- 1.32.7
|
||||||
|
- dependency-name: "@babel/plugin-proposal-class-properties"
|
||||||
|
versions:
|
||||||
|
- 7.12.13
|
||||||
|
- dependency-name: react-toastify
|
||||||
|
versions:
|
||||||
|
- 7.0.1
|
||||||
|
- 7.0.2
|
||||||
58
.github/workflows/laravel-dev.yml
vendored
58
.github/workflows/laravel-dev.yml
vendored
@@ -1,45 +1,29 @@
|
|||||||
name: Dev
|
name: Build Dev Image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ dev ]
|
branches: [ dev ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
laravel-tests:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout
|
||||||
- name: Copy .env
|
uses: actions/checkout@v2
|
||||||
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
|
|
||||||
- name: Install Dependencies
|
- name: Docker Setup Buildx
|
||||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
uses: docker/setup-buildx-action@v1.2.0
|
||||||
- name: Create Database
|
|
||||||
run: |
|
- name: Login to DockerHub
|
||||||
mkdir -p database
|
uses: docker/login-action@v1
|
||||||
touch database/database.sqlite
|
with:
|
||||||
- name: Generate key
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
env:
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
DB_CONNECTION: sqlite
|
- name: Build and push
|
||||||
DB_DATABASE: database/database.sqlite
|
uses: docker/build-push-action@v2
|
||||||
run: php artisan key:generate
|
with:
|
||||||
- name: Generate JWT key
|
context: .
|
||||||
env:
|
file: ./docker/Dockerfile
|
||||||
DB_CONNECTION: sqlite
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
DB_DATABASE: database/database.sqlite
|
push: true
|
||||||
run: php artisan jwt:secret
|
tags: henrywhitaker3/speedtest-tracker:dev,henrywhitaker3/speedtest-tracker:dev-arm
|
||||||
- name: Directory Permissions
|
|
||||||
run: chmod -R 777 storage bootstrap/cache
|
|
||||||
- name: Download Speedtest binary
|
|
||||||
run: wget https://bintray.com/ookla/download/download_file?file_path=ookla-speedtest-1.0.0-x86_64-linux.tgz -O speedtest.tgz && tar zxvf speedtest.tgz && mv speedtest app/Bin/
|
|
||||||
- name: Accept EULA
|
|
||||||
env:
|
|
||||||
DB_CONNECTION: sqlite
|
|
||||||
DB_DATABASE: database/database.sqlite
|
|
||||||
run: php artisan speedtest:eula
|
|
||||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
|
||||||
env:
|
|
||||||
DB_CONNECTION: sqlite
|
|
||||||
DB_DATABASE: database/database.sqlite
|
|
||||||
run: vendor/bin/phpunit
|
|
||||||
|
|||||||
29
.github/workflows/laravel-master.yml
vendored
Normal file
29
.github/workflows/laravel-master.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: Build Latest Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Docker Setup Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1.2.0
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
push: true
|
||||||
|
tags: henrywhitaker3/speedtest-tracker:latest,henrywhitaker3/speedtest-tracker:latest-arm
|
||||||
18
.github/workflows/laravel-pr.yml
vendored
18
.github/workflows/laravel-pr.yml
vendored
@@ -10,9 +10,23 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- uses: shivammathur/setup-php@b7d1d9c9a92d8d8463ce36d7f60da34d461724f8
|
||||||
|
with:
|
||||||
|
php-version: '7.4'
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Copy .env
|
- name: Copy .env
|
||||||
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
|
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
|
||||||
|
- name: Update .env with secrets
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
|
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
|
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||||
|
HEALTHCHECKS_UUID: ${{ secrets.HEALTHCHECKS_UUID }}
|
||||||
|
run: |
|
||||||
|
echo SLACK_WEBHOOK=$SLACK_WEBHOOK >> .env
|
||||||
|
echo TELEGRAM_BOT_TOKEN=$TELEGRAM_BOT_TOKEN >> .env
|
||||||
|
echo TELEGRAM_CHAT_ID=$TELEGRAM_CHAT_ID >> .env
|
||||||
|
echo HEALTHCHECKS_UUID=$HEALTHCHECKS_UUID >> .env
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
||||||
- name: Create Database
|
- name: Create Database
|
||||||
@@ -32,7 +46,7 @@ jobs:
|
|||||||
- name: Directory Permissions
|
- name: Directory Permissions
|
||||||
run: chmod -R 777 storage bootstrap/cache
|
run: chmod -R 777 storage bootstrap/cache
|
||||||
- name: Download Speedtest binary
|
- name: Download Speedtest binary
|
||||||
run: wget https://bintray.com/ookla/download/download_file?file_path=ookla-speedtest-1.0.0-x86_64-linux.tgz -O speedtest.tgz && tar zxvf speedtest.tgz && mv speedtest app/Bin/
|
run: wget https://install.speedtest.net/app/cli/ookla-speedtest-1.0.0-x86_64-linux.tgz -O speedtest.tgz && tar zxvf speedtest.tgz && mv speedtest app/Bin/
|
||||||
- name: Accept EULA
|
- name: Accept EULA
|
||||||
env:
|
env:
|
||||||
DB_CONNECTION: sqlite
|
DB_CONNECTION: sqlite
|
||||||
@@ -42,4 +56,4 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DB_CONNECTION: sqlite
|
DB_CONNECTION: sqlite
|
||||||
DB_DATABASE: database/database.sqlite
|
DB_DATABASE: database/database.sqlite
|
||||||
run: vendor/bin/phpunit
|
run: php artisan test --parallel
|
||||||
|
|||||||
45
.github/workflows/laravel-stable.yml
vendored
45
.github/workflows/laravel-stable.yml
vendored
@@ -1,45 +0,0 @@
|
|||||||
name: Stable
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
laravel-tests:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Copy .env
|
|
||||||
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
|
||||||
- name: Create Database
|
|
||||||
run: |
|
|
||||||
mkdir -p database
|
|
||||||
touch database/database.sqlite
|
|
||||||
- name: Generate key
|
|
||||||
env:
|
|
||||||
DB_CONNECTION: sqlite
|
|
||||||
DB_DATABASE: database/database.sqlite
|
|
||||||
run: php artisan key:generate
|
|
||||||
- name: Generate JWT key
|
|
||||||
env:
|
|
||||||
DB_CONNECTION: sqlite
|
|
||||||
DB_DATABASE: database/database.sqlite
|
|
||||||
run: php artisan jwt:secret
|
|
||||||
- name: Directory Permissions
|
|
||||||
run: chmod -R 777 storage bootstrap/cache
|
|
||||||
- name: Download Speedtest binary
|
|
||||||
run: wget https://bintray.com/ookla/download/download_file?file_path=ookla-speedtest-1.0.0-x86_64-linux.tgz -O speedtest.tgz && tar zxvf speedtest.tgz && mv speedtest app/Bin/
|
|
||||||
- name: Accept EULA
|
|
||||||
env:
|
|
||||||
DB_CONNECTION: sqlite
|
|
||||||
DB_DATABASE: database/database.sqlite
|
|
||||||
run: php artisan speedtest:eula
|
|
||||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
|
||||||
env:
|
|
||||||
DB_CONNECTION: sqlite
|
|
||||||
DB_DATABASE: database/database.sqlite
|
|
||||||
run: vendor/bin/phpunit
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,3 +16,4 @@ yarn-error.log
|
|||||||
_ide_helper.php
|
_ide_helper.php
|
||||||
.idea
|
.idea
|
||||||
.config
|
.config
|
||||||
|
reports/
|
||||||
|
|||||||
28
README.md
28
README.md
@@ -1,9 +1,11 @@
|
|||||||
# Speedtest Tracker
|
# Speedtest Tracker
|
||||||
|
|
||||||
[](https://hub.docker.com/r/henrywhitaker3/speedtest-tracker) [](https://github.com/henrywhitaker3/Speedtest-Tracker/actions) [](https://github.com/henrywhitaker3/Speedtest-Tracker/actions) [](https://github.com/henrywhitaker3/Speedtest-Tracker/commits) [](https://github.com/henrywhitaker3/Speedtest-Tracker/issues) [](https://github.com/henrywhitaker3/Speedtest-Tracker/commits)  [](https://github.com/henrywhitaker3/Speedtest-Tracker/blob/master/LICENSE)
|
[](https://hub.docker.com/r/henrywhitaker3/speedtest-tracker) [](https://github.com/henrywhitaker3/Speedtest-Tracker/actions) [](https://github.com/henrywhitaker3/Speedtest-Tracker/actions) [](https://github.com/henrywhitaker3/Speedtest-Tracker/commits) [](https://github.com/henrywhitaker3/Speedtest-Tracker/issues) [](https://github.com/henrywhitaker3/Speedtest-Tracker/commits)  [](https://github.com/henrywhitaker3/Speedtest-Tracker/blob/master/LICENSE)
|
||||||
|
|
||||||
This program runs a speedtest check every hour and graphs the results. The back-end is written in [Laravel](https://laravel.com/) and the front-end uses [React](https://reactjs.org/). It uses the [Ookla's speedtest cli](https://www.speedtest.net/apps/cli) package to get the data and uses [Chart.js](https://www.chartjs.org/) to plot the results.
|
This program runs a speedtest check every hour and graphs the results. The back-end is written in [Laravel](https://laravel.com/) and the front-end uses [React](https://reactjs.org/). It uses the [Ookla's speedtest cli](https://www.speedtest.net/apps/cli) package to get the data and uses [Chart.js](https://www.chartjs.org/) to plot the results.
|
||||||
|
|
||||||
|
A demo can be found [here](https://speedtest.henrywhitaker.com)
|
||||||
|
|
||||||
Disclaimer: You will need to accept Ookla's EULA and privacy agreements in order to use this container.
|
Disclaimer: You will need to accept Ookla's EULA and privacy agreements in order to use this container.
|
||||||
|
|
||||||

|

|
||||||
@@ -14,7 +16,9 @@ Disclaimer: You will need to accept Ookla's EULA and privacy agreements in order
|
|||||||
- Graph of previous speedtests going back x days
|
- Graph of previous speedtests going back x days
|
||||||
- Backup/restore data in JSON/CSV format
|
- Backup/restore data in JSON/CSV format
|
||||||
- Slack/Discord/Telegram notifications
|
- Slack/Discord/Telegram notifications
|
||||||
|
- [healthchecks.io](https://healthchecks.io) integration
|
||||||
- Organizr integration
|
- Organizr integration
|
||||||
|
- InfluxDB integration (currently v1 only, v2 is a WIP)
|
||||||
|
|
||||||
## Installation & Setup
|
## Installation & Setup
|
||||||
|
|
||||||
@@ -80,7 +84,27 @@ Container images are configured using parameters passed at runtime (such as thos
|
|||||||
| `-e TELEGRAM_CHAT_ID` | Optional. Telegram chat ID. |
|
| `-e TELEGRAM_CHAT_ID` | Optional. Telegram chat ID. |
|
||||||
| `-e PUID` | Optional. Supply a local user ID for volume permissions |
|
| `-e PUID` | Optional. Supply a local user ID for volume permissions |
|
||||||
| `-e PGID` | Optional. Supply a local group ID for volume permissions |
|
| `-e PGID` | Optional. Supply a local group ID for volume permissions |
|
||||||
|
| `-e AUTH` | Optional. Set to 'true' to enable authentication for the app |
|
||||||
|
| `-e INFLUXDB_RETENTION`| Optional. Sets the InfluxDB retention period, defaults to `30d` |
|
||||||
|
| `-e INFLUXDB_HOST_TAG | Optional. Sets the InfluxDB host tag value, defaults to `speedtest` |
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
Authentication is optional. When enabled, unauthenticated users will only be able to see the graphs and tests table. To be able to queue a new speedtest, backup/restore data and update instance settings you will need to log in. To enable authentication, pass the `AUTH=true` environment variable in docker or run `php artisan speedtest:auth --enable` for manual installs (same command with `--disable` to turn it off).
|
||||||
|
|
||||||
|
The default credentials are:
|
||||||
|
|
||||||
|
| Field | Function |
|
||||||
|
| --- | --- |
|
||||||
|
| username | admin@admin.com |
|
||||||
|
| password | password |
|
||||||
|
|
||||||
|
After enabling, you should change the password through the web UI.
|
||||||
|
|
||||||
### Manual Install
|
### Manual Install
|
||||||
|
|
||||||
For manual installtions, please follow the instrucitons [here](https://github.com/henrywhitaker3/Speedtest-Tracker/wiki/Manual-Installation).
|
For manual installations, please follow the instructions [here](https://github.com/henrywhitaker3/Speedtest-Tracker/wiki/Manual-Installation).
|
||||||
|
|
||||||
|
### Kubernetes
|
||||||
|
|
||||||
|
There is a 3rd party helm chart available [here](https://github.com/sOblivionsCall/charts).
|
||||||
|
|||||||
48
app/Actions/GetFailedSpeedtestData.php
Normal file
48
app/Actions/GetFailedSpeedtestData.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use Cache;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use DB;
|
||||||
|
use Henrywhitaker3\LaravelActions\Interfaces\ActionInterface;
|
||||||
|
|
||||||
|
class GetFailedSpeedtestData implements ActionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the action.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function run($days = 7)
|
||||||
|
{
|
||||||
|
$ttl = Carbon::now()->addDays(1);
|
||||||
|
|
||||||
|
return Cache::remember('failure-rate-' . $days, $ttl, function () use ($days) {
|
||||||
|
$range = [
|
||||||
|
Carbon::today()
|
||||||
|
];
|
||||||
|
for ($i = 0; $i < ($days - 1); $i++) {
|
||||||
|
$prev = end($range);
|
||||||
|
$new = $prev->copy()->subDays(1);
|
||||||
|
array_push($range, $new);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rate = [];
|
||||||
|
|
||||||
|
foreach ($range as $day) {
|
||||||
|
$success = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', false)->get()[0]['rate'];
|
||||||
|
$fail = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', true)->get()[0]['rate'];
|
||||||
|
|
||||||
|
array_push($rate, [
|
||||||
|
'date' => $day->toDateString(),
|
||||||
|
'success' => $success,
|
||||||
|
'failure' => $fail,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_reverse($rate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
52
app/Actions/GetLatestSpeedtestData.php
Normal file
52
app/Actions/GetLatestSpeedtestData.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use DB;
|
||||||
|
use Henrywhitaker3\LaravelActions\Interfaces\ActionInterface;
|
||||||
|
|
||||||
|
class GetLatestSpeedtestData implements ActionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the action.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$data = SpeedtestHelper::latest();
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'data' => $data,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (SettingsHelper::get('show_average')) {
|
||||||
|
$avg = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
||||||
|
->where('failed', false)
|
||||||
|
->first()
|
||||||
|
->toArray();
|
||||||
|
$response['average'] = $avg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SettingsHelper::get('show_max')) {
|
||||||
|
$max = Speedtest::select(DB::raw('MAX(ping) as ping, MAX(download) as download, MAX(upload) as upload'))
|
||||||
|
->where('failed', false)
|
||||||
|
->first()
|
||||||
|
->toArray();
|
||||||
|
$response['maximum'] = $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SettingsHelper::get('show_min')) {
|
||||||
|
$min = Speedtest::select(DB::raw('MIN(ping) as ping, MIN(download) as download, MIN(upload) as upload'))
|
||||||
|
->where('failed', false)
|
||||||
|
->first()
|
||||||
|
->toArray();
|
||||||
|
$response['minimum'] = $min;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/Actions/GetSpeedtestTimeData.php
Normal file
37
app/Actions/GetSpeedtestTimeData.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use Cache;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Henrywhitaker3\LaravelActions\Interfaces\ActionInterface;
|
||||||
|
|
||||||
|
class GetSpeedtestTimeData implements ActionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the action.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function run($days = 7)
|
||||||
|
{
|
||||||
|
$ttl = Carbon::now()->addDays(1);
|
||||||
|
|
||||||
|
return Cache::remember('speedtest-days-' . $days, $ttl, function () use ($days) {
|
||||||
|
$showFailed = (bool)SettingsHelper::get('show_failed_tests_on_graph')->value;
|
||||||
|
|
||||||
|
if ($showFailed === true) {
|
||||||
|
return Speedtest::where('created_at', '>=', Carbon::now()->subDays($days))
|
||||||
|
->orderBy('created_at', 'asc')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Speedtest::where('created_at', '>=', Carbon::now()->subDays($days))
|
||||||
|
->where('failed', false)
|
||||||
|
->orderBy('created_at', 'asc')
|
||||||
|
->get();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/Actions/QueueSpeedtest.php
Normal file
35
app/Actions/QueueSpeedtest.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Jobs\SpeedtestJob;
|
||||||
|
use Henrywhitaker3\LaravelActions\Interfaces\ActionInterface;
|
||||||
|
|
||||||
|
class QueueSpeedtest implements ActionInterface
|
||||||
|
{
|
||||||
|
private SpeedtestProvider $speedtestProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new action instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(SpeedtestProvider $speedtestProvider)
|
||||||
|
{
|
||||||
|
$this->speedtestProvider = $speedtestProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the action.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
SettingsHelper::loadIntegrationConfig();
|
||||||
|
|
||||||
|
SpeedtestJob::dispatch(false, config('integrations'), $this->speedtestProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
app/Casts/CommaSeparatedArrayCast.php
Normal file
52
app/Casts/CommaSeparatedArrayCast.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Casts;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||||
|
|
||||||
|
class CommaSeparatedArrayCast implements CastsAttributes
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Array of settings that should be cast
|
||||||
|
*/
|
||||||
|
private array $shouldCast = [
|
||||||
|
'visible_columns',
|
||||||
|
'hidden_columns',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast the given value.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param array $attributes
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get($model, $key, $value, $attributes)
|
||||||
|
{
|
||||||
|
if (!in_array($model->name, $this->shouldCast)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return explode(',', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the given value for storage.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param array $attributes
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function set($model, $key, $value, $attributes)
|
||||||
|
{
|
||||||
|
if (!in_array($model->name, $this->shouldCast)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(',', $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,10 +34,11 @@ class AcceptEULACommand extends Command
|
|||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
shell_exec(config('speedtest.home') . ' && ' . app_path() . '/Bin/speedtest --accept-license --accept-gdpr');
|
$this->info('Acceping EULA');
|
||||||
|
shell_exec(config('speedtest.home') . ' && timeout 10s ' . app_path() . '/Bin/speedtest --accept-license --accept-gdpr');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
59
app/Console/Commands/AuthenticationCommand.php
Normal file
59
app/Console/Commands/AuthenticationCommand.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class AuthenticationCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'speedtest:auth {--enable} {--disable}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Toggle authentication for the app';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$opts = $this->options();
|
||||||
|
|
||||||
|
if($opts['enable'] === true && $opts['disable'] === true) {
|
||||||
|
$this->warn('Please specify only ONE of --enable and --disable');
|
||||||
|
} else if($opts['enable'] === false && $opts['disable'] === false) {
|
||||||
|
$this->warn('You need to specify either --enable OR --disable');
|
||||||
|
} else {
|
||||||
|
if($opts['enable'] === true) {
|
||||||
|
$this->info('Enabling authentication');
|
||||||
|
SettingsHelper::set('auth', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($opts['disable'] === true) {
|
||||||
|
$this->info('Disabling authentication');
|
||||||
|
SettingsHelper::set('auth', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
app/Console/Commands/ClearOldSessionsCommand.php
Normal file
48
app/Console/Commands/ClearOldSessionsCommand.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Auth\LoginSession;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
class ClearOldSessionsCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'speedtest:clear-sessions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Clear expired sessions from database';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$now = Carbon::now()->timestamp;
|
||||||
|
$sessions = LoginSession::where('expires', '<=', $now)
|
||||||
|
->delete();
|
||||||
|
$this->info('Invalidated expired sessions');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ class ClearQueueCommand extends Command
|
|||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class GetConfig extends Command
|
|||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class SetSlackWebhook extends Command
|
|||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class SetTelegramOptions extends Command
|
|||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Exceptions\SpeedtestFailureException;
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class SpeedtestCommand extends Command
|
class SpeedtestCommand extends Command
|
||||||
@@ -21,33 +23,37 @@ class SpeedtestCommand extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Performs a new speedtest';
|
protected $description = 'Performs a new speedtest';
|
||||||
|
|
||||||
|
private SpeedtestProvider $speedtestProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command instance.
|
* Create a new command instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct(SpeedtestProvider $speedtestProvider)
|
||||||
{
|
{
|
||||||
|
$this->speedtestProvider = $speedtestProvider;
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs a speedtest synchroonously and displays the results..
|
* Runs a speedtest synchroonously and displays the results..
|
||||||
*
|
*
|
||||||
* @return null
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info('Running speedtest, this might take a while...');
|
$this->info('Running speedtest, this might take a while...');
|
||||||
|
|
||||||
$results = SpeedtestHelper::runSpeedtest(false, false);
|
try {
|
||||||
|
$results = $this->speedtestProvider->run(false, false);
|
||||||
if(!is_object($results)) {
|
} catch (SpeedtestFailureException $e) {
|
||||||
$this->error('Something went wrong running the speedtest.');
|
$this->error('Something went wrong running the speedtest.');
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(property_exists($results, 'ping') && property_exists($results, 'download') && property_exists($results, 'upload')) {
|
if (property_exists($results, 'ping') && property_exists($results, 'download') && property_exists($results, 'upload')) {
|
||||||
$this->error('Something went wrong running the speedtest.');
|
$this->error('Something went wrong running the speedtest.');
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
@@ -22,27 +23,31 @@ class SpeedtestLatestCommand extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Returns the latest speedtest result';
|
protected $description = 'Returns the latest speedtest result';
|
||||||
|
|
||||||
|
private SpeedtestProvider $speedtestProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command instance.
|
* Create a new command instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct(SpeedtestProvider $speedtestProvider)
|
||||||
{
|
{
|
||||||
|
$this->speedtestProvider = $speedtestProvider;
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the latest speedtest values.
|
* Prints the latest speedtest values.
|
||||||
*
|
*
|
||||||
* @return null
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$latest = SpeedtestHelper::latest();
|
$latest = SpeedtestHelper::latest();
|
||||||
|
|
||||||
if($latest) {
|
if ($latest) {
|
||||||
if($latest->scheduled) {
|
if ($latest->scheduled) {
|
||||||
$extra = '(scheduled)';
|
$extra = '(scheduled)';
|
||||||
} else {
|
} else {
|
||||||
$extra = '(manual)';
|
$extra = '(manual)';
|
||||||
@@ -50,7 +55,7 @@ class SpeedtestLatestCommand extends Command
|
|||||||
|
|
||||||
$this->info('Last speedtest run at: ' . $latest->created_at . ' ' . $extra);
|
$this->info('Last speedtest run at: ' . $latest->created_at . ' ' . $extra);
|
||||||
|
|
||||||
if($latest->failed) {
|
if ($latest->failed) {
|
||||||
$this->error('Speedtest failed');
|
$this->error('Speedtest failed');
|
||||||
} else {
|
} else {
|
||||||
$this->info('Ping: ' . $latest->ping . ' ms');
|
$this->info('Ping: ' . $latest->ping . ' ms');
|
||||||
@@ -62,7 +67,7 @@ class SpeedtestLatestCommand extends Command
|
|||||||
|
|
||||||
$this->info('Running speedtest, this might take a while...');
|
$this->info('Running speedtest, this might take a while...');
|
||||||
|
|
||||||
$results = SpeedtestHelper::runSpeedtest();
|
$results = $this->speedtestProvider->run();
|
||||||
|
|
||||||
$this->info('Ping: ' . $results->ping . ' ms');
|
$this->info('Ping: ' . $results->ping . ' ms');
|
||||||
$this->info('Download: ' . $results->download . ' Mbit/s');
|
$this->info('Download: ' . $results->download . ' Mbit/s');
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class SpeedtestVersionCommand extends Command
|
|||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class TestNotification extends Command
|
|||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Console;
|
|||||||
use App\Events\SpeedtestOverviewEvent;
|
use App\Events\SpeedtestOverviewEvent;
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
use App\Jobs\SpeedtestJob;
|
use App\Jobs\SpeedtestJob;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
@@ -28,8 +29,19 @@ class Kernel extends ConsoleKernel
|
|||||||
*/
|
*/
|
||||||
protected function schedule(Schedule $schedule)
|
protected function schedule(Schedule $schedule)
|
||||||
{
|
{
|
||||||
$schedule->job(new SpeedtestJob)->cron(SettingsHelper::get('schedule')['value']);
|
if ((bool)SettingsHelper::get('schedule_enabled')->value) {
|
||||||
$schedule->command('speedtest:overview')->cron('0 ' . SettingsHelper::get('speedtest_overview_time')->value . ' * * *');
|
$schedule->job(
|
||||||
|
new SpeedtestJob(
|
||||||
|
true,
|
||||||
|
config('integrations'),
|
||||||
|
app()->make(SpeedtestProvider::class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->cron(SettingsHelper::get('schedule')['value'])
|
||||||
|
->timezone(env('TZ', 'UTC'));
|
||||||
|
}
|
||||||
|
$schedule->command('speedtest:overview')->cron('0 ' . SettingsHelper::get('speedtest_overview_time')->value . ' * * *')->timezone(env('TZ', 'UTC'));
|
||||||
|
$schedule->command('speedtest:clear-sessions')->everyMinute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +51,7 @@ class Kernel extends ConsoleKernel
|
|||||||
*/
|
*/
|
||||||
protected function commands()
|
protected function commands()
|
||||||
{
|
{
|
||||||
$this->load(__DIR__.'/Commands');
|
$this->load(__DIR__ . '/Commands');
|
||||||
|
|
||||||
require base_path('routes/console.php');
|
require base_path('routes/console.php');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class SpeedtestCompleteEvent
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public $speedtest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new event instance.
|
* Create a new event instance.
|
||||||
*
|
*
|
||||||
|
|||||||
10
app/Exceptions/InfluxDBConnectionErrorException.php
Normal file
10
app/Exceptions/InfluxDBConnectionErrorException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class InfluxDBConnectionErrorException extends Exception
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
10
app/Exceptions/InfluxDBNotEnabledException.php
Normal file
10
app/Exceptions/InfluxDBNotEnabledException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class InfluxDBNotEnabledException extends Exception
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
10
app/Exceptions/SpeedtestFailureException.php
Normal file
10
app/Exceptions/SpeedtestFailureException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class SpeedtestFailureException extends Exception
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
13
app/Facades/HealthchecksFacade.php
Normal file
13
app/Facades/HealthchecksFacade.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Facades;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
class HealthchecksFacade extends Facade
|
||||||
|
{
|
||||||
|
protected static function getFacadeAccessor()
|
||||||
|
{
|
||||||
|
return 'healthcheck';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
use App\Speedtest;
|
use App\Models\Speedtest;
|
||||||
|
use Cache;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@@ -26,13 +27,13 @@ class BackupHelper {
|
|||||||
case 'csv':
|
case 'csv':
|
||||||
$data = Speedtest::get();
|
$data = Speedtest::get();
|
||||||
|
|
||||||
$csv = Storage::disk('local')->getDriver()->getAdapter()->getPathPrefix() . $name . '.csv';
|
$csv = storage_path() . '/app/' . $name . '.csv';
|
||||||
$name = $name . '.csv';
|
$name = $name . '.csv';
|
||||||
$handle = fopen($csv, 'w+');
|
$handle = fopen($csv, 'w+');
|
||||||
fputcsv($handle, array('id', 'ping', 'download', 'upload', 'created_at', 'updated_at'));
|
fputcsv($handle, array('id', 'ping', 'download', 'upload', 'server_id', 'server_name', 'server_host', 'url', 'scheduled', 'failed', 'created_at', 'updated_at'));
|
||||||
|
|
||||||
foreach($data as $d) {
|
foreach($data as $d) {
|
||||||
fputcsv($handle, array($d->id, $d->ping, $d->download, $d->upload, $d->created_at, $d->updated_at));
|
fputcsv($handle, BackupHelper::createCSVBackupArray($d));
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
@@ -55,29 +56,32 @@ class BackupHelper {
|
|||||||
*
|
*
|
||||||
* @param array|string $array Backup data
|
* @param array|string $array Backup data
|
||||||
* @param string $format json|csv
|
* @param string $format json|csv
|
||||||
* @return boolean
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function restore($array, $format)
|
public static function restore($array, $format)
|
||||||
{
|
{
|
||||||
|
Cache::flush();
|
||||||
if($format == 'json') {
|
if($format == 'json') {
|
||||||
foreach($array as $test) {
|
foreach($array as $test) {
|
||||||
try {
|
try {
|
||||||
$st = Speedtest::create([
|
$data = BackupHelper::backupJSONToArray($test);
|
||||||
'ping' => $test['ping'],
|
|
||||||
'download' => $test['download'],
|
if($data === false) {
|
||||||
'upload' => $test['upload'],
|
continue;
|
||||||
'created_at' => $test['created_at'],
|
}
|
||||||
]);
|
|
||||||
|
Speedtest::create($data);
|
||||||
} catch(Exception $e) {
|
} catch(Exception $e) {
|
||||||
|
Log::error($e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if($format == 'csv') {
|
} else if($format == 'csv') {
|
||||||
$csv = explode(PHP_EOL, $array);
|
$csv = explode(PHP_EOL, $array);
|
||||||
$headers = 'id,ping,download,upload,created_at,updated_at';
|
|
||||||
if($csv[0] != $headers) {
|
$headers = BackupHelper::validateCSV($csv[0]);
|
||||||
Log::error('Incorrect CSV format');
|
if($headers === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,14 +89,14 @@ class BackupHelper {
|
|||||||
$csv = array_values($csv);
|
$csv = array_values($csv);
|
||||||
|
|
||||||
for($i = 0; $i < sizeof($csv); $i++) {
|
for($i = 0; $i < sizeof($csv); $i++) {
|
||||||
$e = explode(',', $csv[$i]);
|
$data = BackupHelper::backupCSVToArray($csv[$i]);
|
||||||
|
|
||||||
|
if($data === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$st = Speedtest::create([
|
Speedtest::create($data);
|
||||||
'ping' => $e[1],
|
|
||||||
'download' => $e[2],
|
|
||||||
'upload' => $e[3],
|
|
||||||
'created_at' => substr($e[4], 1, -1),
|
|
||||||
]);
|
|
||||||
} catch(Exception $e) {
|
} catch(Exception $e) {
|
||||||
Log::error($e);
|
Log::error($e);
|
||||||
continue;
|
continue;
|
||||||
@@ -101,5 +105,182 @@ class BackupHelper {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a CSV file passed for restore
|
||||||
|
*
|
||||||
|
* @param String $csv The line containing the CSV headers
|
||||||
|
* @return bool|string
|
||||||
|
*/
|
||||||
|
public static function validateCSV(String $csv)
|
||||||
|
{
|
||||||
|
$headers = [
|
||||||
|
'old' => 'id,ping,download,upload,created_at,updated_at',
|
||||||
|
'new' => 'id,ping,download,upload,server_id,server_name,server_host,url,scheduled,failed,created_at,updated_at',
|
||||||
|
];
|
||||||
|
$backupHeaders = null;
|
||||||
|
|
||||||
|
foreach($headers as $key => $h) {
|
||||||
|
if($csv == $h) {
|
||||||
|
$backupHeaders = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($backupHeaders === null) {
|
||||||
|
Log::info('Incorrect CSV format');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $backupHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array from the raw CSV data
|
||||||
|
*
|
||||||
|
* @param String $line The line of CSV data
|
||||||
|
* @param String $header The type of backup header
|
||||||
|
* @return array|bool
|
||||||
|
*/
|
||||||
|
public static function backupCSVToArray(String $line, String $header = 'new')
|
||||||
|
{
|
||||||
|
$basic = explode(',', $line);
|
||||||
|
|
||||||
|
if($header == 'old') {
|
||||||
|
$array = [
|
||||||
|
'ping' => $basic[1],
|
||||||
|
'download' => $basic[2],
|
||||||
|
'upload' => $basic[3],
|
||||||
|
'created_at' => substr($basic[4], 1, -1),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($header == 'new') {
|
||||||
|
$array = [
|
||||||
|
'ping' => $basic[1],
|
||||||
|
'download' => $basic[2],
|
||||||
|
'upload' => $basic[3],
|
||||||
|
'server_id' => $basic[4],
|
||||||
|
'server_name' => $basic[5],
|
||||||
|
'server_host' => $basic[6],
|
||||||
|
'url' => $basic[7],
|
||||||
|
'scheduled' => $basic[8],
|
||||||
|
'failed' => $basic[9],
|
||||||
|
'created_at' => substr($basic[10], 1, -1),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($array)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BackupHelper::cleanRestoreDataArray($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean an array, setting values with '' to null
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function cleanRestoreDataArray(array $array)
|
||||||
|
{
|
||||||
|
foreach($array as $key => $val) {
|
||||||
|
if($val === '') {
|
||||||
|
$array[$key] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array from the JSON data
|
||||||
|
*
|
||||||
|
* @param array $json json_decoded data
|
||||||
|
* @return array|bool
|
||||||
|
*/
|
||||||
|
public static function backupJSONToArray($json)
|
||||||
|
{
|
||||||
|
$required = [
|
||||||
|
'ping',
|
||||||
|
'upload',
|
||||||
|
'download',
|
||||||
|
'created_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
$extras = [
|
||||||
|
'server_id',
|
||||||
|
'server_name',
|
||||||
|
'server_host',
|
||||||
|
'url',
|
||||||
|
'failed',
|
||||||
|
'scheduled'
|
||||||
|
];
|
||||||
|
|
||||||
|
$array = [];
|
||||||
|
|
||||||
|
foreach($required as $req) {
|
||||||
|
if(!array_key_exists($req, $json)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$val = $json[$req];
|
||||||
|
|
||||||
|
if($val === '') {
|
||||||
|
$val = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$array[$req] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($extras as $extra) {
|
||||||
|
if(array_key_exists($extra, $json)) {
|
||||||
|
$val = $json[$extra];
|
||||||
|
|
||||||
|
if($val === '') {
|
||||||
|
$val = null;
|
||||||
|
}
|
||||||
|
$array[$extra] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array to store in CSV
|
||||||
|
*
|
||||||
|
* @param Speedtest $test
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function createCSVBackupArray(Speedtest $test)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
$test->id,
|
||||||
|
$test->ping,
|
||||||
|
$test->download,
|
||||||
|
$test->upload,
|
||||||
|
$test->server_id,
|
||||||
|
$test->server_name,
|
||||||
|
$test->server_host,
|
||||||
|
$test->url,
|
||||||
|
$test->scheduled,
|
||||||
|
$test->failed,
|
||||||
|
$test->created_at,
|
||||||
|
$test->updated_at
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach($data as $key => $val) {
|
||||||
|
if(strpos($val, ',') !== false) {
|
||||||
|
$val = str_replace(',', ' -', $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data[$key] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
use App\Auth\EmailVerification;
|
use App\Models\Auth\EmailVerification;
|
||||||
use App\User;
|
use App\Models\User;
|
||||||
|
|
||||||
class EmailVerificationHelper {
|
class EmailVerificationHelper {
|
||||||
public static function checkVerificationAttempt($userID, $token)
|
public static function checkVerificationAttempt($userID, $token)
|
||||||
|
|||||||
67
app/Helpers/NotificationsHelper.php
Normal file
67
app/Helpers/NotificationsHelper.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
class NotificationsHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse $errors and format message
|
||||||
|
*
|
||||||
|
* @param array $errors
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public static function formatPercentageThresholdMessage(array $errors)
|
||||||
|
{
|
||||||
|
$msg = NotificationsHelper::thresholdMessageStart($errors);
|
||||||
|
|
||||||
|
$msg = $msg . 'exceeded the percentage threshold';
|
||||||
|
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse $errors and format message
|
||||||
|
*
|
||||||
|
* @param array $errors
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public static function formatAbsoluteThresholdMessage(array $errors)
|
||||||
|
{
|
||||||
|
$msg = NotificationsHelper::thresholdMessageStart($errors);
|
||||||
|
|
||||||
|
$msg = $msg . 'exceeded the absolute threshold';
|
||||||
|
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate through errors to format message
|
||||||
|
*
|
||||||
|
* @param array $errors
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public static function thresholdMessageStart(array $errors)
|
||||||
|
{
|
||||||
|
$msg = 'For the latest speedtest, the ';
|
||||||
|
|
||||||
|
for($i = 0; $i < sizeof($errors); $i++) {
|
||||||
|
$key = $errors[$i];
|
||||||
|
$msg = $msg . $key;
|
||||||
|
if(sizeof($errors) > 1 && $i < (sizeof($errors) - 1)) {
|
||||||
|
$msg = $msg . ', ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($msg[-1] != '') {
|
||||||
|
$msg = $msg . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sizeof($errors) > 1) {
|
||||||
|
$msg = $msg . 'values ';
|
||||||
|
} else {
|
||||||
|
$msg = $msg . 'value ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,25 +3,28 @@
|
|||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
use App\Events\TestNotificationEvent;
|
use App\Events\TestNotificationEvent;
|
||||||
use App\Setting;
|
use App\Models\Setting;
|
||||||
|
use Cache;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
||||||
class SettingsHelper {
|
class SettingsHelper
|
||||||
|
{
|
||||||
|
private static $settings = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a Setting object by name
|
* Get a Setting object by name
|
||||||
*
|
*
|
||||||
* @param String $name The name field in the setting table
|
* @param String $name The name field in the setting table
|
||||||
* @return \App\Setting|boolean $name The Setting object. Returns false if no mathcing obj.
|
* @return \App\Setting|bool|array $name The Setting object. Returns false if no mathcing obj.
|
||||||
*/
|
*/
|
||||||
public static function get(String $name)
|
public static function get(String $name)
|
||||||
{
|
{
|
||||||
$name = Setting::where('name', $name)->get();
|
$name = Setting::where('name', $name)->get();
|
||||||
|
|
||||||
if(sizeof($name) == 0) {
|
if (sizeof($name) == 0) {
|
||||||
return false;
|
return false;
|
||||||
} else if(sizeof($name) == 1) {
|
} else if (sizeof($name) == 1) {
|
||||||
return $name[0];
|
return $name->first();
|
||||||
} else {
|
} else {
|
||||||
$name = $name->keyBy('name');
|
$name = $name->keyBy('name');
|
||||||
return $name->all();
|
return $name->all();
|
||||||
@@ -32,18 +35,18 @@ class SettingsHelper {
|
|||||||
* Create / update value for Setting object.
|
* Create / update value for Setting object.
|
||||||
*
|
*
|
||||||
* @param String $name Name of setting
|
* @param String $name Name of setting
|
||||||
* @param String $value Value of setting
|
* @param String|bool $value Value of setting
|
||||||
* @return \App\Setting
|
* @return \App\Setting
|
||||||
*/
|
*/
|
||||||
public static function set(String $name, $value)
|
public static function set(String $name, $value)
|
||||||
{
|
{
|
||||||
$setting = SettingsHelper::get($name);
|
$setting = SettingsHelper::get($name);
|
||||||
|
|
||||||
if($value === false) {
|
if ($value === false) {
|
||||||
$value = "0";
|
$value = "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
if($setting !== false) {
|
if ($setting !== false) {
|
||||||
$setting->value = $value;
|
$setting->value = $value;
|
||||||
$setting->save();
|
$setting->save();
|
||||||
} else {
|
} else {
|
||||||
@@ -53,6 +56,10 @@ class SettingsHelper {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($name == 'show_failed_tests_on_graph') {
|
||||||
|
Cache::flush();
|
||||||
|
}
|
||||||
|
|
||||||
return $setting;
|
return $setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,13 +71,13 @@ class SettingsHelper {
|
|||||||
public static function getBase()
|
public static function getBase()
|
||||||
{
|
{
|
||||||
$base = env('BASE_PATH', '/');
|
$base = env('BASE_PATH', '/');
|
||||||
if($base == '') {
|
if ($base == '') {
|
||||||
$base = '/';
|
$base = '/';
|
||||||
} else {
|
} else {
|
||||||
if($base[0] != '/') {
|
if ($base[0] != '/') {
|
||||||
$base = '/' . $base;
|
$base = '/' . $base;
|
||||||
}
|
}
|
||||||
if($base[-1] != '/') {
|
if ($base[-1] != '/') {
|
||||||
$base = $base . '/';
|
$base = $base . '/';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,12 +92,17 @@ class SettingsHelper {
|
|||||||
*/
|
*/
|
||||||
public static function settingIsEditable(string $key)
|
public static function settingIsEditable(string $key)
|
||||||
{
|
{
|
||||||
|
// Manual override for app_name
|
||||||
|
if ($key === 'app_name') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$results = [];
|
$results = [];
|
||||||
|
|
||||||
// Try exact key
|
// Try exact key
|
||||||
$val = exec('echo $' . $key);
|
$val = exec('echo $' . $key);
|
||||||
|
|
||||||
if($val == "") {
|
if ($val == "") {
|
||||||
array_push($results, true);
|
array_push($results, true);
|
||||||
} else {
|
} else {
|
||||||
array_push($results, false);
|
array_push($results, false);
|
||||||
@@ -99,25 +111,25 @@ class SettingsHelper {
|
|||||||
// Try key all caps
|
// Try key all caps
|
||||||
$val = exec('echo $' . strtoupper($key));
|
$val = exec('echo $' . strtoupper($key));
|
||||||
|
|
||||||
if($val == "") {
|
if ($val == "") {
|
||||||
array_push($results, true);
|
array_push($results, true);
|
||||||
} else {
|
} else {
|
||||||
array_push($results, false);
|
array_push($results, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(env($key, false) == false) {
|
if (env($key, false) == false) {
|
||||||
array_push($results, true);
|
array_push($results, true);
|
||||||
} else {
|
} else {
|
||||||
array_push($results, false);
|
array_push($results, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(env(strtoupper($key), false) == false) {
|
if (env(strtoupper($key), false) == false) {
|
||||||
array_push($results, true);
|
array_push($results, true);
|
||||||
} else {
|
} else {
|
||||||
array_push($results, false);
|
array_push($results, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(in_array(false, $results)) {
|
if (in_array(false, $results)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +145,12 @@ class SettingsHelper {
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'base' => SettingsHelper::getBase(),
|
'base' => SettingsHelper::getBase(),
|
||||||
|
'name' => SettingsHelper::get('app_name')->value,
|
||||||
|
'widgets' => [
|
||||||
|
'show_average' => (bool)SettingsHelper::get('show_average')->value,
|
||||||
|
'show_max' => (bool)SettingsHelper::get('show_max')->value,
|
||||||
|
'show_min' => (bool)SettingsHelper::get('show_min')->value,
|
||||||
|
],
|
||||||
'graphs' => [
|
'graphs' => [
|
||||||
'download_upload_graph_enabled' => SettingsHelper::get('download_upload_graph_enabled'),
|
'download_upload_graph_enabled' => SettingsHelper::get('download_upload_graph_enabled'),
|
||||||
'download_upload_graph_width' => SettingsHelper::get('download_upload_graph_width'),
|
'download_upload_graph_width' => SettingsHelper::get('download_upload_graph_width'),
|
||||||
@@ -145,7 +163,12 @@ class SettingsHelper {
|
|||||||
'slack_webhook' => SettingsHelper::settingIsEditable('slack_webhook'),
|
'slack_webhook' => SettingsHelper::settingIsEditable('slack_webhook'),
|
||||||
'telegram_bot_token' => SettingsHelper::settingIsEditable('telegram_bot_token'),
|
'telegram_bot_token' => SettingsHelper::settingIsEditable('telegram_bot_token'),
|
||||||
'telegram_chat_id' => SettingsHelper::settingIsEditable('telegram_chat_id'),
|
'telegram_chat_id' => SettingsHelper::settingIsEditable('telegram_chat_id'),
|
||||||
]
|
],
|
||||||
|
'tables' => [
|
||||||
|
'visible_columns' => SettingsHelper::get('visible_columns')->value,
|
||||||
|
'hidden_columns' => SettingsHelper::get('hidden_columns')->value,
|
||||||
|
],
|
||||||
|
'auth' => (bool)SettingsHelper::get('auth')->value
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,20 +176,43 @@ class SettingsHelper {
|
|||||||
* Send test notification to agents
|
* Send test notification to agents
|
||||||
*
|
*
|
||||||
* @param boolean|string $agent
|
* @param boolean|string $agent
|
||||||
* @return void
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function testNotification($agent = true)
|
public static function testNotification($agent = true)
|
||||||
{
|
{
|
||||||
$agents = [ 'slack', 'telegram' ];
|
$agents = ['slack', 'telegram'];
|
||||||
|
|
||||||
if($agent === true) {
|
if ($agent === true) {
|
||||||
event(new TestNotificationEvent($agents));
|
event(new TestNotificationEvent($agents));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(in_array($agent, $agents)) {
|
if (in_array($agent, $agents)) {
|
||||||
event(new TestNotificationEvent([ $agent ]));
|
event(new TestNotificationEvent([$agent]));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function loadIntegrationConfig()
|
||||||
|
{
|
||||||
|
$settings = [
|
||||||
|
'healthchecks_enabled' => (bool)SettingsHelper::get('healthchecks_enabled')->value,
|
||||||
|
'healthchecks_uuid' => SettingsHelper::get('healthchecks_uuid')->value,
|
||||||
|
'slack_webhook' => SettingsHelper::get('slack_webhook')->value,
|
||||||
|
'telegram_bot_token' => SettingsHelper::get('telegram_bot_token')->value,
|
||||||
|
'telegram_chat_id' => SettingsHelper::get('telegram_chat_id')->value,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($settings as $key => $value) {
|
||||||
|
$key = 'integrations.' . $key;
|
||||||
|
|
||||||
|
if ($value === "") {
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
config()->set([$key => $value]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,98 +2,32 @@
|
|||||||
|
|
||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
use App\Speedtest;
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use App\Utils\OoklaTester;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Henrywhitaker3\Healthchecks\Healthchecks;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use InvalidArgumentException;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
|
|
||||||
class SpeedtestHelper {
|
class SpeedtestHelper
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs/processes speedtest output to created a Speedtest object
|
* Runs/processes speedtest output to created a Speedtest object
|
||||||
*
|
*
|
||||||
* @param boolean|string $output If false, new speedtest runs. If anything else, will try to parse as JSON for speedtest results.
|
* @param boolean|string $output If false, new speedtest runs. If anything else, will try to parse as JSON for speedtest results.
|
||||||
* @return \App\Speedtest|boolean
|
* @return \App\Speedtest|bool
|
||||||
*/
|
*/
|
||||||
public static function runSpeedtest($output = false, $scheduled = true)
|
public static function runSpeedtest()
|
||||||
{
|
{
|
||||||
if($output === false) {
|
$tester = app()->make(SpeedtestProvider::class);
|
||||||
$output = SpeedtestHelper::output();
|
return $tester->run();
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$output = json_decode($output, true, 512, JSON_THROW_ON_ERROR);
|
|
||||||
|
|
||||||
if(!SpeedtestHelper::checkOutputIsComplete($output)) {
|
|
||||||
$test = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$test = Speedtest::create([
|
|
||||||
'ping' => $output['ping']['latency'],
|
|
||||||
'download' => SpeedtestHelper::convert($output['download']['bandwidth']),
|
|
||||||
'upload' => SpeedtestHelper::convert($output['upload']['bandwidth']),
|
|
||||||
'server_id' => $output['server']['id'],
|
|
||||||
'server_name' => $output['server']['name'],
|
|
||||||
'server_host' => $output['server']['host'] . ':' . $output['server']['port'],
|
|
||||||
'url' => $output['result']['url'],
|
|
||||||
'scheduled' => $scheduled
|
|
||||||
]);
|
|
||||||
} catch(JsonException $e) {
|
|
||||||
Log::error('Failed to parse speedtest JSON');
|
|
||||||
Log::error($output);
|
|
||||||
$test = false;
|
|
||||||
} catch(Exception $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
$test = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$test) {
|
|
||||||
Speedtest::create([
|
|
||||||
'ping' => 0,
|
|
||||||
'upload' => 0,
|
|
||||||
'download' => 0,
|
|
||||||
'failed' => true,
|
|
||||||
'scheduled' => $scheduled,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($test) || $test == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cache::flush();
|
|
||||||
|
|
||||||
return $test;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the output of executing speedtest binary.
|
|
||||||
*
|
|
||||||
* @return boolean|string
|
|
||||||
*/
|
|
||||||
public static function output()
|
|
||||||
{
|
|
||||||
$server = SettingsHelper::get('server')['value'];
|
|
||||||
|
|
||||||
$binPath = app_path() . DIRECTORY_SEPARATOR . 'Bin' . DIRECTORY_SEPARATOR . 'speedtest';
|
|
||||||
$homePrefix = config('speedtest.home') . ' && ';
|
|
||||||
|
|
||||||
if($server != '' && $server != false) {
|
|
||||||
$server = explode(',', $server);
|
|
||||||
$server = $server[array_rand($server)];
|
|
||||||
if($server == false) {
|
|
||||||
Log::error('Speedtest server undefined');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return shell_exec($homePrefix . $binPath . ' -f json -s ' . $server);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shell_exec($homePrefix . $binPath . ' -f json');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,10 +39,10 @@ class SpeedtestHelper {
|
|||||||
{
|
{
|
||||||
$t = Carbon::now()->subDay();
|
$t = Carbon::now()->subDay();
|
||||||
$s = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
$s = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
||||||
->where('created_at', '>=', $t)
|
->where('created_at', '>=', $t)
|
||||||
->where('failed', false)
|
->where('failed', false)
|
||||||
->first()
|
->first()
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
return $s;
|
return $s;
|
||||||
}
|
}
|
||||||
@@ -119,8 +53,9 @@ class SpeedtestHelper {
|
|||||||
* @param int|float $bytes
|
* @param int|float $bytes
|
||||||
* @return int|float
|
* @return int|float
|
||||||
*/
|
*/
|
||||||
public static function convert($bytes) {
|
public static function convert($bytes)
|
||||||
return ( $bytes * 8 ) / 1000000;
|
{
|
||||||
|
return ($bytes * 8) / 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,7 +67,7 @@ class SpeedtestHelper {
|
|||||||
{
|
{
|
||||||
$data = Speedtest::latest()->get();
|
$data = Speedtest::latest()->get();
|
||||||
|
|
||||||
if($data->isEmpty()) {
|
if ($data->isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,17 +77,17 @@ class SpeedtestHelper {
|
|||||||
/**
|
/**
|
||||||
* Parses network speeds and return converted to Mbps
|
* Parses network speeds and return converted to Mbps
|
||||||
*
|
*
|
||||||
* @param array $input
|
* @param string $input
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function parseUnits($input)
|
public static function parseUnits($input)
|
||||||
{
|
{
|
||||||
$input = explode(' ', $input);
|
$input = explode(' ', $input);
|
||||||
|
|
||||||
$val = $input[0];
|
$val = (float)$input[0];
|
||||||
$unit = explode('/', $input[1])[0];
|
$unit = explode('/', $input[1])[0];
|
||||||
|
|
||||||
switch($unit) {
|
switch ($unit) {
|
||||||
case 'Mbyte':
|
case 'Mbyte':
|
||||||
$val = $val * 8;
|
$val = $val * 8;
|
||||||
break;
|
break;
|
||||||
@@ -174,45 +109,125 @@ class SpeedtestHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that the speedtest JSON output is complete/valid
|
* Create a backup of the SQLite database
|
||||||
*
|
*
|
||||||
* @param array $output
|
* @return null|boolean
|
||||||
* @return boolean
|
|
||||||
*/
|
*/
|
||||||
public static function checkOutputIsComplete($output)
|
public static function dbBackup()
|
||||||
{
|
{
|
||||||
/**
|
if (env('DB_CONNECTION') === 'sqlite') {
|
||||||
* Array of indexes that must exist in $output
|
if (env('DB_DATABASE') !== null) {
|
||||||
*/
|
$current = env('DB_DATABASE');
|
||||||
$checks = [
|
try {
|
||||||
'type' => 'result',
|
if (File::copy($current, $current . '.bak')) {
|
||||||
'download' => [ 'bandwidth' => '*' ],
|
return true;
|
||||||
'upload' => [ 'bandwidth' => '*' ],
|
}
|
||||||
'ping' => [ 'latency' => '*' ],
|
} catch (Exception $e) {
|
||||||
'server' => [
|
return false;
|
||||||
'id' => '*',
|
}
|
||||||
'name' => '*',
|
}
|
||||||
'host' => '*',
|
|
||||||
'port' => '*'
|
|
||||||
],
|
|
||||||
'result' => [
|
|
||||||
'url' => '*'
|
|
||||||
],
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* Array of indexes that must not exist
|
|
||||||
*/
|
|
||||||
$checkMissing = [
|
|
||||||
'type' => 'error'
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach($checks as $key => $value) {
|
return false;
|
||||||
if(!isset($output[$key])) {
|
}
|
||||||
return false;
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all speedtests from the database
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function deleteAll()
|
||||||
|
{
|
||||||
|
Cache::flush();
|
||||||
|
|
||||||
|
SpeedtestHelper::dbBackup();
|
||||||
|
|
||||||
|
if (sizeof(Speedtest::whereNotNull('id')->get()) > 0) {
|
||||||
|
if (Speedtest::whereNotNull('id')->delete()) {
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return [
|
||||||
|
'success' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Work out if a test is lower than the threshold for historic tests
|
||||||
|
*
|
||||||
|
* @param String $type
|
||||||
|
* @param Speedtest $test
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function testIsLowerThanThreshold(String $type, Speedtest $test)
|
||||||
|
{
|
||||||
|
if ($type == 'percentage') {
|
||||||
|
$avg = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
||||||
|
->where('failed', false)
|
||||||
|
->get()
|
||||||
|
->toArray()[0];
|
||||||
|
|
||||||
|
$threshold = SettingsHelper::get('threshold_alert_percentage')->value;
|
||||||
|
|
||||||
|
if ($threshold == '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
foreach ($avg as $key => $value) {
|
||||||
|
if ($key == 'ping') {
|
||||||
|
$threshold = (float)$value * (1 + ($threshold / 100));
|
||||||
|
|
||||||
|
if ($test->$key > $threshold) {
|
||||||
|
array_push($errors, $key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$threshold = (float)$value * (1 - ($threshold / 100));
|
||||||
|
|
||||||
|
if ($test->$key < $threshold) {
|
||||||
|
array_push($errors, $key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type == 'absolute') {
|
||||||
|
$thresholds = [
|
||||||
|
'download' => SettingsHelper::get('threshold_alert_absolute_download')->value,
|
||||||
|
'upload' => SettingsHelper::get('threshold_alert_absolute_upload')->value,
|
||||||
|
'ping' => SettingsHelper::get('threshold_alert_absolute_ping')->value,
|
||||||
|
];
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
foreach ($thresholds as $key => $value) {
|
||||||
|
if ($value == '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key == 'ping') {
|
||||||
|
if ($test->$key > $value) {
|
||||||
|
array_push($errors, $key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($test->$key < $value) {
|
||||||
|
array_push($errors, $key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -228,7 +243,7 @@ class SpeedtestHelper {
|
|||||||
$range = [
|
$range = [
|
||||||
Carbon::today()
|
Carbon::today()
|
||||||
];
|
];
|
||||||
for($i = 0; $i < $days; $i++) {
|
for ($i = 0; $i < ($days - 1); $i++) {
|
||||||
$prev = end($range);
|
$prev = end($range);
|
||||||
$new = $prev->copy()->subDays(1);
|
$new = $prev->copy()->subDays(1);
|
||||||
array_push($range, $new);
|
array_push($range, $new);
|
||||||
@@ -236,7 +251,7 @@ class SpeedtestHelper {
|
|||||||
|
|
||||||
$rate = [];
|
$rate = [];
|
||||||
|
|
||||||
foreach($range as $day) {
|
foreach ($range as $day) {
|
||||||
$success = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', false)->get()[0]['rate'];
|
$success = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', false)->get()[0]['rate'];
|
||||||
$fail = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', true)->get()[0]['rate'];
|
$fail = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', true)->get()[0]['rate'];
|
||||||
|
|
||||||
@@ -252,55 +267,4 @@ class SpeedtestHelper {
|
|||||||
|
|
||||||
return $rate;
|
return $rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a backup of the SQLite database
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public static function dbBackup()
|
|
||||||
{
|
|
||||||
if(env('DB_CONNECTION') === 'sqlite') {
|
|
||||||
if(env('DB_DATABASE') !== null) {
|
|
||||||
$current = env('DB_DATABASE');
|
|
||||||
if(File::copy($current, $current . '.bak')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all speedtests from the database
|
|
||||||
*
|
|
||||||
* @return boolean|string
|
|
||||||
*/
|
|
||||||
public static function deleteAll()
|
|
||||||
{
|
|
||||||
Cache::flush();
|
|
||||||
|
|
||||||
if(SpeedtestHelper::dbBackup() !== false) {
|
|
||||||
if(sizeof(Speedtest::whereNotNull('id')->get()) > 0) {
|
|
||||||
if(Speedtest::whereNotNull('id')->delete()) {
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'msg' => 'There was an error backing up the database. No speedtests have been deleted.'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,38 +13,52 @@ class UpdateHelper {
|
|||||||
/**
|
/**
|
||||||
* URL of updates
|
* URL of updates
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|bool
|
||||||
*/
|
*/
|
||||||
public $url;
|
public $url;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current app version number
|
* Current app version number
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|bool
|
||||||
*/
|
*/
|
||||||
public $currentVersion;
|
public $currentVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Latest app version number
|
||||||
|
*
|
||||||
|
* @var string|bool
|
||||||
|
*/
|
||||||
|
public $latestVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Username of GitHub repo
|
* Username of GitHub repo
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|bool
|
||||||
*/
|
*/
|
||||||
public $user;
|
public $user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of GitHub repo
|
* Name of GitHub repo
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|bool
|
||||||
*/
|
*/
|
||||||
public $repo;
|
public $repo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Branch of GitHub repo
|
* Branch of GitHub repo
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|bool
|
||||||
*/
|
*/
|
||||||
public $branch;
|
public $branch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store download
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
public $download;
|
||||||
|
|
||||||
function __construct() {
|
function __construct() {
|
||||||
$this->currentVersion = config('speedtest.version');
|
$this->currentVersion = config('speedtest.version');
|
||||||
$this->user = config('speedtest.user');
|
$this->user = config('speedtest.user');
|
||||||
@@ -87,7 +101,7 @@ class UpdateHelper {
|
|||||||
/**
|
/**
|
||||||
* Gets the latest version number from GitHub
|
* Gets the latest version number from GitHub
|
||||||
*
|
*
|
||||||
* @return array [ repo, branch, version ]
|
* @return array|bool [ repo, branch, version ]
|
||||||
*/
|
*/
|
||||||
public function checkLatestVersion()
|
public function checkLatestVersion()
|
||||||
{
|
{
|
||||||
@@ -256,7 +270,6 @@ class UpdateHelper {
|
|||||||
$zip->open($backupZip, ZipArchive::CREATE | ZipArchive::OVERWRITE);
|
$zip->open($backupZip, ZipArchive::CREATE | ZipArchive::OVERWRITE);
|
||||||
|
|
||||||
// Create recursive directory iterator
|
// Create recursive directory iterator
|
||||||
/** @var SplFileInfo[] $files */
|
|
||||||
$files = new RecursiveIteratorIterator(
|
$files = new RecursiveIteratorIterator(
|
||||||
new RecursiveDirectoryIterator($rootPath),
|
new RecursiveDirectoryIterator($rootPath),
|
||||||
RecursiveIteratorIterator::LEAVES_ONLY
|
RecursiveIteratorIterator::LEAVES_ONLY
|
||||||
|
|||||||
@@ -2,17 +2,20 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Auth\EmailVerification;
|
use App\Models\Auth\EmailVerification;
|
||||||
use App\Auth\LoginSession as AuthLoginSession;
|
use App\Models\Auth\LoginSession as AuthLoginSession;
|
||||||
use App\Helpers\EmailVerificationHelper;
|
use App\Helpers\EmailVerificationHelper;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\LoginSession;
|
use App\LoginSession;
|
||||||
use App\User;
|
use App\Rules\CurrentPasswordMatches;
|
||||||
|
use App\Models\User;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
use Hash;
|
||||||
use Illuminate\Support\Facades\Request as RequestFacade;
|
use Illuminate\Support\Facades\Request as RequestFacade;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Log;
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
@@ -164,6 +167,10 @@ class AuthController extends Controller
|
|||||||
[ 'expires', '>', time() ]
|
[ 'expires', '>', time() ]
|
||||||
])->get();
|
])->get();
|
||||||
|
|
||||||
|
$sessions = $sessions->map(function ($item) {
|
||||||
|
return collect($item)->forget(['token']);
|
||||||
|
});
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get auth sessions',
|
'method' => 'get auth sessions',
|
||||||
'response' => $sessions
|
'response' => $sessions
|
||||||
@@ -211,4 +218,36 @@ class AuthController extends Controller
|
|||||||
'success' => true,
|
'success' => true,
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function changePassword(Request $request)
|
||||||
|
{
|
||||||
|
$rules = [
|
||||||
|
'currentPassword' => [ 'string', 'required', new CurrentPasswordMatches() ],
|
||||||
|
'newPassword' => [ 'required', 'string', 'confirmed', 'min:8' ],
|
||||||
|
'logoutDevices' => [ 'required', 'bool' ]
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), $rules);
|
||||||
|
if($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'method' => 'reset password',
|
||||||
|
'success' => false,
|
||||||
|
'error' => $validator->errors()
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
$user->password = $request->newPassword;
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
if($request->logoutDevices == true) {
|
||||||
|
AuthLoginSession::where('user_id', $user->id)->update([ 'active' => false ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'method' => 'reset password',
|
||||||
|
'success' => true
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,28 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Helpers\BackupHelper;
|
use App\Helpers\BackupHelper;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
class BackupController extends Controller
|
class BackupController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get backup of speedtests
|
* Get backup of speedtests
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return file
|
* @return mixed|JsonResponse
|
||||||
*/
|
*/
|
||||||
public function backup(Request $request)
|
public function backup(Request $request)
|
||||||
{
|
{
|
||||||
@@ -36,7 +45,7 @@ class BackupController extends Controller
|
|||||||
* Retore from a backup
|
* Retore from a backup
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function restore(Request $request)
|
public function restore(Request $request)
|
||||||
{
|
{
|
||||||
|
|||||||
35
app/Http/Controllers/HomepageDataController.php
Normal file
35
app/Http/Controllers/HomepageDataController.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\GetFailedSpeedtestData;
|
||||||
|
use App\Actions\GetLatestSpeedtestData;
|
||||||
|
use App\Actions\GetSpeedtestTimeData;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Validator;
|
||||||
|
|
||||||
|
class HomepageDataController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke($days)
|
||||||
|
{
|
||||||
|
$validator = Validator::make(
|
||||||
|
['days' => $days],
|
||||||
|
['days' => ['required', 'numeric']],
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'method' => 'get speedtests in last x days',
|
||||||
|
'error' => $validator->errors(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'latest' => run(GetLatestSpeedtestData::class),
|
||||||
|
'time' => run(GetSpeedtestTimeData::class, $days),
|
||||||
|
'fail' => run(GetFailedSpeedtestData::class, $days),
|
||||||
|
'config' => SettingsHelper::getConfig(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
84
app/Http/Controllers/IntegrationsController.php
Normal file
84
app/Http/Controllers/IntegrationsController.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use Exception;
|
||||||
|
use Healthcheck;
|
||||||
|
use Henrywhitaker3\Healthchecks\Exceptions\HealthchecksUuidNotFoundException;
|
||||||
|
use Henrywhitaker3\Healthchecks\Healthchecks;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Ramsey\Uuid\Exception\InvalidUuidStringException;
|
||||||
|
|
||||||
|
class IntegrationsController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the healthchecks config
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function testHealthchecks(String $method)
|
||||||
|
{
|
||||||
|
$methodResp = 'test healthchecks \'' . $method . '\' endpoint';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// SettingsHelper::loadIntegrationConfig();
|
||||||
|
if($method == 'success') {
|
||||||
|
Healthcheck::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($method == 'fail') {
|
||||||
|
Healthcheck::fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($method == 'start') {
|
||||||
|
Healthcheck::start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'method' => $methodResp,
|
||||||
|
'success' => true
|
||||||
|
], 200);
|
||||||
|
} catch(InvalidUuidStringException $e) {
|
||||||
|
return response()->json([
|
||||||
|
'method' => $methodResp,
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Invalid UUID'
|
||||||
|
], 422);
|
||||||
|
} catch(HealthchecksUuidNotFoundException $e) {
|
||||||
|
return response()->json([
|
||||||
|
'method' => $methodResp,
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'UUID not found'
|
||||||
|
], 404);
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return response()->json([
|
||||||
|
'method' => $methodResp,
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger a test of all notification agents
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function testNotification()
|
||||||
|
{
|
||||||
|
SettingsHelper::testNotification();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'method' => 'test notification agents'
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,18 +4,27 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Rules\Cron;
|
use App\Rules\Cron;
|
||||||
use App\Setting;
|
use App\Models\Setting;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
class SettingsController extends Controller
|
class SettingsController extends Controller
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if ((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api')
|
||||||
|
->except(['config']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all settings
|
* Return all settings
|
||||||
*
|
*
|
||||||
* @return array
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
@@ -37,26 +46,26 @@ class SettingsController extends Controller
|
|||||||
* Store/update a setting
|
* Store/update a setting
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$rule = [
|
$rule = [
|
||||||
'name' => [ 'required', 'string', 'min:1' ],
|
'name' => ['required', 'string', 'min:1'],
|
||||||
];
|
];
|
||||||
if($request->name == 'schedule') {
|
if ($request->name == 'schedule') {
|
||||||
$rule['value'] = [ 'required', new Cron ];
|
$rule['value'] = ['required', new Cron];
|
||||||
}
|
}
|
||||||
|
|
||||||
$validator = Validator::make($request->all(), $rule);
|
$validator = Validator::make($request->all(), $rule);
|
||||||
if($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'Store a setting',
|
'method' => 'Store a setting',
|
||||||
'error' => $validator->errors()
|
'error' => $validator->errors()
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isset($request->value)) {
|
if (!isset($request->value)) {
|
||||||
$request->value = '';
|
$request->value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,17 +81,17 @@ class SettingsController extends Controller
|
|||||||
* Bulk store/update a setting
|
* Bulk store/update a setting
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function bulkStore(Request $request)
|
public function bulkStore(Request $request)
|
||||||
{
|
{
|
||||||
$rule = [
|
$rule = [
|
||||||
'data' => [ 'array', 'required' ],
|
'data' => ['array', 'required'],
|
||||||
'data.*.name' => [ 'string', 'required' ],
|
'data.*.name' => ['string', 'required'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$validator = Validator::make($request->all(), $rule);
|
$validator = Validator::make($request->all(), $rule);
|
||||||
if($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'Bulk store a setting',
|
'method' => 'Bulk store a setting',
|
||||||
'error' => $validator->errors()
|
'error' => $validator->errors()
|
||||||
@@ -90,14 +99,14 @@ class SettingsController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$settings = [];
|
$settings = [];
|
||||||
foreach($request->data as $d) {
|
foreach ($request->data as $d) {
|
||||||
if(!isset($d['value']) || $d['value'] == null) {
|
if (!isset($d['value']) || $d['value'] == null) {
|
||||||
$d['value'] = '';
|
$d['value'] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if($d['name'] == 'speedtest_overview_time') {
|
if ($d['name'] == 'speedtest_overview_time') {
|
||||||
$ok = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23' ];
|
$ok = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
|
||||||
if(!in_array($d['value'], $ok)) {
|
if (!in_array($d['value'], $ok)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'Bulk store a setting',
|
'method' => 'Bulk store a setting',
|
||||||
'error' => 'Invalid speedtest_overview_time value'
|
'error' => 'Invalid speedtest_overview_time value'
|
||||||
@@ -107,9 +116,9 @@ class SettingsController extends Controller
|
|||||||
|
|
||||||
$setting = SettingsHelper::get($d['name']);
|
$setting = SettingsHelper::get($d['name']);
|
||||||
|
|
||||||
if($setting == false) {
|
if ($setting == false) {
|
||||||
$setting = SettingsHelper::set($d['name'], $d['value']);
|
$setting = SettingsHelper::set($d['name'], $d['value']);
|
||||||
} else if(SettingsHelper::settingIsEditable($setting->name)) {
|
} else if (SettingsHelper::settingIsEditable($setting->name)) {
|
||||||
$setting = SettingsHelper::set($d['name'], $d['value']);
|
$setting = SettingsHelper::set($d['name'], $d['value']);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
@@ -133,13 +142,4 @@ class SettingsController extends Controller
|
|||||||
{
|
{
|
||||||
return SettingsHelper::getConfig();
|
return SettingsHelper::getConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNotification()
|
|
||||||
{
|
|
||||||
SettingsHelper::testNotification();
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'method' => 'test notificaiton agents'
|
|
||||||
], 200);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,28 +2,41 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\GetFailedSpeedtestData;
|
||||||
|
use App\Actions\GetLatestSpeedtestData;
|
||||||
|
use App\Actions\GetSpeedtestTimeData;
|
||||||
|
use App\Actions\QueueSpeedtest;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
use App\Jobs\SpeedtestJob;
|
use App\Jobs\SpeedtestJob;
|
||||||
use App\Speedtest;
|
use App\Models\Speedtest;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
class SpeedtestController extends Controller
|
class SpeedtestController extends Controller
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if ((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api')
|
||||||
|
->only(['run', 'delete', 'deleteAll']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns paginated list of speedtests
|
* Returns paginated list of speedtests
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$data = Speedtest::orderBy('created_at', 'desc')
|
$data = Speedtest::orderBy('created_at', 'desc')
|
||||||
->paginate();
|
->paginate();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'index of speedtests',
|
'method' => 'index of speedtests',
|
||||||
@@ -35,30 +48,24 @@ class SpeedtestController extends Controller
|
|||||||
* Returns speedtest going back 'x' days
|
* Returns speedtest going back 'x' days
|
||||||
*
|
*
|
||||||
* @param int $days
|
* @param int $days
|
||||||
* @return void
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function time($days)
|
public function time($days)
|
||||||
{
|
{
|
||||||
$rule = [
|
$rule = [
|
||||||
'days' => [ 'required', 'integer' ],
|
'days' => ['required', 'integer'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$validator = Validator::make([ 'days' => $days ], $rule);
|
$validator = Validator::make(['days' => $days], $rule);
|
||||||
|
|
||||||
if($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get speedtests in last x days',
|
'method' => 'get speedtests in last x days',
|
||||||
'error' => $validator->errors(),
|
'error' => $validator->errors(),
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ttl = Carbon::now()->addDays(1);
|
$data = run(GetSpeedtestTimeData::class, $days);
|
||||||
$data = Cache::remember('speedtest-days-' . $days, $ttl, function () use ($days) {
|
|
||||||
return Speedtest::where('created_at', '>=', Carbon::now()->subDays($days))
|
|
||||||
->where('failed', false)
|
|
||||||
->orderBy('created_at', 'asc')
|
|
||||||
->get();
|
|
||||||
});
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get speedtests in last x days',
|
'method' => 'get speedtests in last x days',
|
||||||
@@ -71,24 +78,24 @@ class SpeedtestController extends Controller
|
|||||||
* Returns speedtest failure rate going back 'x' days
|
* Returns speedtest failure rate going back 'x' days
|
||||||
*
|
*
|
||||||
* @param int $days
|
* @param int $days
|
||||||
* @return void
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function fail($days)
|
public function fail($days)
|
||||||
{
|
{
|
||||||
$rule = [
|
$rule = [
|
||||||
'days' => [ 'required', 'integer' ],
|
'days' => ['required', 'integer'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$validator = Validator::make([ 'days' => $days ], $rule);
|
$validator = Validator::make(['days' => $days], $rule);
|
||||||
|
|
||||||
if($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get speedtests in last x days',
|
'method' => 'get speedtests in last x days',
|
||||||
'error' => $validator->errors(),
|
'error' => $validator->errors(),
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = SpeedtestHelper::failureRate($days);
|
$data = run(GetFailedSpeedtestData::class, $days);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get speedtests in last x days',
|
'method' => 'get speedtests in last x days',
|
||||||
@@ -100,25 +107,14 @@ class SpeedtestController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Return latest speedtest
|
* Return latest speedtest
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function latest()
|
public function latest()
|
||||||
{
|
{
|
||||||
$data = SpeedtestHelper::latest();
|
$data = run(GetLatestSpeedtestData::class);
|
||||||
$avg = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
|
||||||
->where('failed', false)
|
|
||||||
->get();
|
|
||||||
$max = Speedtest::select(DB::raw('MAX(ping) as ping, MAX(download) as download, MAX(upload) as upload'))
|
|
||||||
->where('failed', false)
|
|
||||||
->get();
|
|
||||||
|
|
||||||
if($data) {
|
if ($data['data']) {
|
||||||
return response()->json([
|
return response()->json($data, 200);
|
||||||
'method' => 'get latest speedtest',
|
|
||||||
'data' => $data,
|
|
||||||
'average' => $avg[0],
|
|
||||||
'max' => $max[0],
|
|
||||||
], 200);
|
|
||||||
} else {
|
} else {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get latest speedtest',
|
'method' => 'get latest speedtest',
|
||||||
@@ -130,17 +126,18 @@ class SpeedtestController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Queue a new speedtest
|
* Queue a new speedtest
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$data = SpeedtestJob::dispatch(false);
|
run(QueueSpeedtest::class);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'run speedtest',
|
'method' => 'run speedtest',
|
||||||
'data' => 'a new speedtest has been added to the queue'
|
'data' => 'a new speedtest has been added to the queue'
|
||||||
], 200);
|
], 200);
|
||||||
} catch(Exception $e) {
|
} catch (Exception $e) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'run speedtest',
|
'method' => 'run speedtest',
|
||||||
'error' => $e
|
'error' => $e
|
||||||
@@ -151,13 +148,13 @@ class SpeedtestController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Delete all speedtests from db
|
* Delete all speedtests from db
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function deleteAll()
|
public function deleteAll()
|
||||||
{
|
{
|
||||||
$ret = SpeedtestHelper::deleteAll();
|
$ret = SpeedtestHelper::deleteAll();
|
||||||
|
|
||||||
if($ret['success']) {
|
if ($ret['success']) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'delete all speedtests from the database',
|
'method' => 'delete all speedtests from the database',
|
||||||
'success' => true
|
'success' => true
|
||||||
@@ -175,7 +172,7 @@ class SpeedtestController extends Controller
|
|||||||
* Delete a specific speedtest from the database
|
* Delete a specific speedtest from the database
|
||||||
*
|
*
|
||||||
* @param Speedtest $speedtest
|
* @param Speedtest $speedtest
|
||||||
* @return boolean
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function delete(Speedtest $speedtest)
|
public function delete(Speedtest $speedtest)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,17 +2,25 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Updater;
|
use Updater;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
class UpdateController extends Controller
|
class UpdateController extends Controller
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if((bool)SettingsHelper::get('auth')->value === true) {
|
||||||
|
$this->middleware('auth:api');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for new update
|
* Check for new update
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function checkForUpdate()
|
public function checkForUpdate()
|
||||||
{
|
{
|
||||||
@@ -25,7 +33,7 @@ class UpdateController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Download new update
|
* Download new update
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function downloadUpdate()
|
public function downloadUpdate()
|
||||||
{
|
{
|
||||||
@@ -47,7 +55,7 @@ class UpdateController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Trigger update extraction
|
* Trigger update extraction
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function extractUpdate()
|
public function extractUpdate()
|
||||||
{
|
{
|
||||||
@@ -69,22 +77,22 @@ class UpdateController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Trigger update file move
|
* Trigger update file move
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function moveUpdate()
|
public function moveUpdate()
|
||||||
{
|
{
|
||||||
$cp = Updater::updateFiles();
|
Updater::updateFiles();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'copy latest version',
|
'method' => 'copy latest version',
|
||||||
'success' => $cp,
|
'success' => null,
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get local changelog
|
* Get local changelog
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function changelog()
|
public function changelog()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,5 +17,7 @@ class Authenticate extends Middleware
|
|||||||
if (! $request->expectsJson()) {
|
if (! $request->expectsJson()) {
|
||||||
return route('auth.login');
|
return route('auth.login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
use App\Auth\LoginSession;
|
use App\Models\Auth\LoginSession;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
|
|||||||
13
app/Interfaces/InfluxDBWrapperInterface.php
Normal file
13
app/Interfaces/InfluxDBWrapperInterface.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Interfaces;
|
||||||
|
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
|
||||||
|
interface InfluxDBWrapperInterface
|
||||||
|
{
|
||||||
|
public function testConnection(): bool;
|
||||||
|
public function doesDatabaseExist(string $database): bool;
|
||||||
|
public function createDatabase(string $database): bool;
|
||||||
|
public function store(Speedtest $speedtest): bool;
|
||||||
|
}
|
||||||
11
app/Interfaces/SpeedtestProvider.php
Normal file
11
app/Interfaces/SpeedtestProvider.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Interfaces;
|
||||||
|
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
|
||||||
|
interface SpeedtestProvider
|
||||||
|
{
|
||||||
|
public function run(): Speedtest;
|
||||||
|
public function output();
|
||||||
|
}
|
||||||
@@ -4,7 +4,13 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Events\SpeedtestCompleteEvent;
|
use App\Events\SpeedtestCompleteEvent;
|
||||||
use App\Events\SpeedtestFailedEvent;
|
use App\Events\SpeedtestFailedEvent;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Utils\OoklaTester;
|
||||||
|
use Exception;
|
||||||
|
use Healthcheck;
|
||||||
|
use Henrywhitaker3\Healthchecks\Healthchecks;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
@@ -16,16 +22,32 @@ class SpeedtestJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scheduled bool
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
private $scheduled;
|
private $scheduled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integrations config array
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
private SpeedtestProvider $speedtestProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct($scheduled = true)
|
public function __construct($scheduled = true, $config = [], SpeedtestProvider $speedtestProvider)
|
||||||
{
|
{
|
||||||
$this->scheduled = $scheduled;
|
$this->scheduled = $scheduled;
|
||||||
|
$this->config = $config;
|
||||||
|
$this->speedtestProvider = $speedtestProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,13 +57,50 @@ class SpeedtestJob implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$output = SpeedtestHelper::output();
|
if ($this->config['healthchecks_enabled'] === true) {
|
||||||
$speedtest = SpeedtestHelper::runSpeedtest($output, $this->scheduled);
|
$this->healthcheck('start');
|
||||||
if($speedtest == false) {
|
}
|
||||||
|
$output = $this->speedtestProvider->output();
|
||||||
|
$speedtest = $this->speedtestProvider->run($output, $this->scheduled);
|
||||||
|
if ($speedtest == false) {
|
||||||
|
if ($this->config['healthchecks_enabled'] === true) {
|
||||||
|
$this->healthcheck('fail');
|
||||||
|
}
|
||||||
|
|
||||||
event(new SpeedtestFailedEvent());
|
event(new SpeedtestFailedEvent());
|
||||||
} else {
|
} else {
|
||||||
|
if ($this->config['healthchecks_enabled'] === true) {
|
||||||
|
$this->healthcheck('success');
|
||||||
|
}
|
||||||
|
|
||||||
event(new SpeedtestCompleteEvent($speedtest));
|
event(new SpeedtestCompleteEvent($speedtest));
|
||||||
}
|
}
|
||||||
return $speedtest;
|
return $speedtest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper to reduce duplication of try/catch for hc
|
||||||
|
*
|
||||||
|
* @param String $method
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function healthcheck(String $method)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$hc = new Healthchecks(SettingsHelper::get('healthchecks_uuid')->value, SettingsHelper::get('healthchecks_server_url')->value);
|
||||||
|
if ($method === 'start') {
|
||||||
|
$hc->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($method === 'success') {
|
||||||
|
$hc->success();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($method === 'fail') {
|
||||||
|
$hc->fail();
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,13 @@
|
|||||||
namespace App\Listeners;
|
namespace App\Listeners;
|
||||||
|
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Notifications\SpeedtestAbsoluteThresholdNotificationSlack;
|
||||||
|
use App\Notifications\SpeedtestAbsoluteThresholdTelegram;
|
||||||
use App\Notifications\SpeedtestCompleteSlack;
|
use App\Notifications\SpeedtestCompleteSlack;
|
||||||
use App\Notifications\SpeedtestCompleteTelegram;
|
use App\Notifications\SpeedtestCompleteTelegram;
|
||||||
|
use App\Notifications\SpeedtestPercentageThresholdNotificationSlack;
|
||||||
|
use App\Notifications\SpeedtestPercentageThresholdTelegram;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
@@ -32,9 +37,63 @@ class SpeedtestCompleteListener
|
|||||||
*/
|
*/
|
||||||
public function handle($event)
|
public function handle($event)
|
||||||
{
|
{
|
||||||
|
if((bool)SettingsHelper::get('threshold_alert_percentage_notifications')->value == true) {
|
||||||
|
$data = $event->speedtest;
|
||||||
|
$errors = SpeedtestHelper::testIsLowerThanThreshold('percentage', $data);
|
||||||
|
if(sizeof($errors) > 0) {
|
||||||
|
if(SettingsHelper::get('slack_webhook')->value) {
|
||||||
|
try {
|
||||||
|
Notification::route('slack', SettingsHelper::get('slack_webhook')->value)
|
||||||
|
->notify(new SpeedtestPercentageThresholdNotificationSlack($errors));
|
||||||
|
} catch(Exception $e) {
|
||||||
|
Log::notice('Your sleck webhook is invalid');
|
||||||
|
Log::notice($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SettingsHelper::get('telegram_bot_token')->value == true && SettingsHelper::get('telegram_chat_id')->value == true) {
|
||||||
|
try {
|
||||||
|
config([ 'services.telegram-bot-api' => [ 'token' => SettingsHelper::get('telegram_bot_token')->value ] ]);
|
||||||
|
Notification::route(TelegramChannel::class, SettingsHelper::get('telegram_chat_id')->value)
|
||||||
|
->notify(new SpeedtestPercentageThresholdTelegram($errors));
|
||||||
|
} catch(Exception $e) {
|
||||||
|
Log::notice('Your telegram settings are invalid');
|
||||||
|
Log::notice($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if((bool)SettingsHelper::get('threshold_alert_absolute_notifications')->value == true) {
|
||||||
|
$data = $event->speedtest;
|
||||||
|
$errors = SpeedtestHelper::testIsLowerThanThreshold('absolute', $data);
|
||||||
|
if(sizeof($errors) > 0) {
|
||||||
|
if(SettingsHelper::get('slack_webhook')->value) {
|
||||||
|
try {
|
||||||
|
Notification::route('slack', SettingsHelper::get('slack_webhook')->value)
|
||||||
|
->notify(new SpeedtestAbsoluteThresholdNotificationSlack($errors));
|
||||||
|
} catch(Exception $e) {
|
||||||
|
Log::notice('Your sleck webhook is invalid');
|
||||||
|
Log::notice($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SettingsHelper::get('telegram_bot_token')->value == true && SettingsHelper::get('telegram_chat_id')->value == true) {
|
||||||
|
try {
|
||||||
|
config([ 'services.telegram-bot-api' => [ 'token' => SettingsHelper::get('telegram_bot_token')->value ] ]);
|
||||||
|
Notification::route(TelegramChannel::class, SettingsHelper::get('telegram_chat_id')->value)
|
||||||
|
->notify(new SpeedtestAbsoluteThresholdTelegram($errors));
|
||||||
|
} catch(Exception $e) {
|
||||||
|
Log::notice('Your telegram settings are invalid');
|
||||||
|
Log::notice($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(SettingsHelper::get('speedtest_notifications')->value == true) {
|
if(SettingsHelper::get('speedtest_notifications')->value == true) {
|
||||||
$data = $event->speedtest;
|
$data = $event->speedtest;
|
||||||
if(SettingsHelper::get('slack_webhook')) {
|
if(SettingsHelper::get('slack_webhook')->value) {
|
||||||
try {
|
try {
|
||||||
Notification::route('slack', SettingsHelper::get('slack_webhook')->value)
|
Notification::route('slack', SettingsHelper::get('slack_webhook')->value)
|
||||||
->notify(new SpeedtestCompleteSlack($data));
|
->notify(new SpeedtestCompleteSlack($data));
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Notification;
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
use NotificationChannels\Telegram\TelegramChannel;
|
||||||
|
|
||||||
class SpeedtestFailedListener
|
class SpeedtestFailedListener
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Auth;
|
namespace App\Models\Auth;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Auth;
|
namespace App\Models\Auth;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Casts\CommaSeparatedArrayCast;
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
@@ -17,4 +18,8 @@ class Setting extends Model
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $table = 'settings';
|
protected $table = 'settings';
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'value' => CommaSeparatedArrayCast::class,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
45
app/Models/Speedtest.php
Normal file
45
app/Models/Speedtest.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Speedtest extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'ping',
|
||||||
|
'download',
|
||||||
|
'upload',
|
||||||
|
'created_at',
|
||||||
|
'server_id',
|
||||||
|
'server_name',
|
||||||
|
'server_host',
|
||||||
|
'url',
|
||||||
|
'scheduled',
|
||||||
|
'failed',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $table = 'speedtests';
|
||||||
|
|
||||||
|
public function formatForInfluxDB()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => (int) $this->id,
|
||||||
|
'download' => (float) $this->download,
|
||||||
|
'upload' => (float) $this->upload,
|
||||||
|
'ping' => (float) $this->ping,
|
||||||
|
'server_id' => (int) $this->server_id,
|
||||||
|
'server_host' => $this->server_host,
|
||||||
|
'server_name' => $this->server_name,
|
||||||
|
'scheduled' => (bool) $this->scheduled,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
@@ -78,7 +78,7 @@ class User extends Authenticatable implements JWTSubject
|
|||||||
/**
|
/**
|
||||||
* Returns a user's login sessions
|
* Returns a user's login sessions
|
||||||
*
|
*
|
||||||
* @return array
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function sessions()
|
public function sessions()
|
||||||
{
|
{
|
||||||
@@ -107,7 +107,7 @@ class User extends Authenticatable implements JWTSubject
|
|||||||
|
|
||||||
public function setPasswordAttribute($password)
|
public function setPasswordAttribute($password)
|
||||||
{
|
{
|
||||||
if ( !empty($password) ) {
|
if (!empty($password)) {
|
||||||
$this->attributes['password'] = Hash::make($password);
|
$this->attributes['password'] = Hash::make($password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use App\Helpers\NotificationsHelper;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Messages\SlackMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class SpeedtestAbsoluteThresholdNotificationSlack extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
protected $errors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new notification instance.
|
||||||
|
*
|
||||||
|
* @param array $errors
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $errors)
|
||||||
|
{
|
||||||
|
$this->errors = $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification's delivery channels.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function via($notifiable)
|
||||||
|
{
|
||||||
|
return ['slack'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format slack notification
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return SlackMessage
|
||||||
|
*/
|
||||||
|
public function toSlack($notifiable)
|
||||||
|
{
|
||||||
|
$msg = NotificationsHelper::formatAbsoluteThresholdMessage($this->errors);
|
||||||
|
|
||||||
|
return (new SlackMessage)
|
||||||
|
->warning()
|
||||||
|
->attachment(function ($attachment) use ($msg) {
|
||||||
|
$attachment->title('Speedtest absolute threshold error')
|
||||||
|
->content($msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/Notifications/SpeedtestAbsoluteThresholdTelegram.php
Normal file
58
app/Notifications/SpeedtestAbsoluteThresholdTelegram.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use App\Helpers\NotificationsHelper;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use NotificationChannels\Telegram\TelegramChannel;
|
||||||
|
use NotificationChannels\Telegram\TelegramMessage;
|
||||||
|
|
||||||
|
class SpeedtestAbsoluteThresholdTelegram extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
private $errors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new notification instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($errors)
|
||||||
|
{
|
||||||
|
$this->errors = $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification's delivery channels.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function via($notifiable)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
TelegramChannel::class
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format telegram notification
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return TelegramMessage
|
||||||
|
*/
|
||||||
|
public function toTelegram($notifiable)
|
||||||
|
{
|
||||||
|
$msg = NotificationsHelper::formatAbsoluteThresholdMessage($this->errors);
|
||||||
|
|
||||||
|
return TelegramMessage::create()
|
||||||
|
->to(SettingsHelper::get('telegram_chat_id')->value)
|
||||||
|
->content($msg)
|
||||||
|
->options(['parse_mode' => 'Markdown']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@ class SpeedtestCompleteTelegram extends Notification
|
|||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
|
public $speedtest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new notification instance.
|
* Create a new notification instance.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ class SpeedtestOverviewSlack extends Notification
|
|||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
|
public $data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new notification instance.
|
* Create a new notification instance.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ class SpeedtestOverviewTelegram extends Notification
|
|||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
|
public $data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new notification instance.
|
* Create a new notification instance.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use App\Helpers\NotificationsHelper;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Messages\SlackMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class SpeedtestPercentageThresholdNotificationSlack extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
protected $errors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new notification instance.
|
||||||
|
*
|
||||||
|
* @param array $errors
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $errors)
|
||||||
|
{
|
||||||
|
$this->errors = $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification's delivery channels.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function via($notifiable)
|
||||||
|
{
|
||||||
|
return ['slack'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format slack notification
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return SlackMessage
|
||||||
|
*/
|
||||||
|
public function toSlack($notifiable)
|
||||||
|
{
|
||||||
|
$msg = NotificationsHelper::formatPercentageThresholdMessage($this->errors);
|
||||||
|
|
||||||
|
return (new SlackMessage)
|
||||||
|
->warning()
|
||||||
|
->attachment(function ($attachment) use ($msg) {
|
||||||
|
$attachment->title('Speedtest percentage threshold error')
|
||||||
|
->content($msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/Notifications/SpeedtestPercentageThresholdTelegram.php
Normal file
58
app/Notifications/SpeedtestPercentageThresholdTelegram.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use App\Helpers\NotificationsHelper;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use NotificationChannels\Telegram\TelegramChannel;
|
||||||
|
use NotificationChannels\Telegram\TelegramMessage;
|
||||||
|
|
||||||
|
class SpeedtestPercentageThresholdTelegram extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
private $errors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new notification instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($errors)
|
||||||
|
{
|
||||||
|
$this->errors = $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification's delivery channels.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function via($notifiable)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
TelegramChannel::class
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format telegram notification
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return TelegramMessage
|
||||||
|
*/
|
||||||
|
public function toTelegram($notifiable)
|
||||||
|
{
|
||||||
|
$msg = NotificationsHelper::formatAbsoluteThresholdMessage($this->errors);
|
||||||
|
|
||||||
|
return TelegramMessage::create()
|
||||||
|
->to(SettingsHelper::get('telegram_chat_id')->value)
|
||||||
|
->content($msg)
|
||||||
|
->options(['parse_mode' => 'Markdown']);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Observers/SpeedtestObserver.php
Normal file
30
app/Observers/SpeedtestObserver.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Observers;
|
||||||
|
|
||||||
|
use App\Exceptions\InfluxDBNotEnabledException;
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use App\Utils\InfluxDB\InfluxDB;
|
||||||
|
use Exception;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
class SpeedtestObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle the Speedtest "created" event.
|
||||||
|
*
|
||||||
|
* @param \App\Speedtest $speedtest
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function created(Speedtest $speedtest)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
InfluxDB::connect()
|
||||||
|
->store($speedtest);
|
||||||
|
} catch (InfluxDBNotEnabledException $e) {
|
||||||
|
// /
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ use App\Listeners\SpeedtestCompleteListener;
|
|||||||
use App\Listeners\SpeedtestFailedListener;
|
use App\Listeners\SpeedtestFailedListener;
|
||||||
use App\Listeners\SpeedtestOverviewListener;
|
use App\Listeners\SpeedtestOverviewListener;
|
||||||
use App\Listeners\TestNotificationListener;
|
use App\Listeners\TestNotificationListener;
|
||||||
|
use App\Observers\SpeedtestObserver;
|
||||||
|
use App\Models\Speedtest;
|
||||||
use Illuminate\Auth\Events\Registered;
|
use Illuminate\Auth\Events\Registered;
|
||||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||||
@@ -49,6 +51,6 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
parent::boot();
|
parent::boot();
|
||||||
|
|
||||||
//
|
Speedtest::observe(SpeedtestObserver::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
app/Providers/IntegrationsServiceProvider.php
Normal file
58
app/Providers/IntegrationsServiceProvider.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use Exception;
|
||||||
|
use File;
|
||||||
|
use Henrywhitaker3\Healthchecks\Healthchecks;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Ramsey\Uuid\Exception\InvalidUuidStringException;
|
||||||
|
use Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class updates the integrations.php config with the relevant values
|
||||||
|
* from the databse.
|
||||||
|
*/
|
||||||
|
class IntegrationsServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Register services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
if (File::exists(env('DB_DATABASE'))) {
|
||||||
|
if (Schema::hasTable('settings')) {
|
||||||
|
$setting = SettingsHelper::get('healthchecks_uuid');
|
||||||
|
|
||||||
|
if ($setting !== false) {
|
||||||
|
try {
|
||||||
|
SettingsHelper::loadIntegrationConfig();
|
||||||
|
|
||||||
|
App::bind('healthcheck', function () use ($setting) {
|
||||||
|
return new Healthchecks($setting->value, SettingsHelper::get('healthchecks_server_url')->value);
|
||||||
|
});
|
||||||
|
} catch (InvalidUuidStringException $e) {
|
||||||
|
Log::error('Invalid healthchecks UUID');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $namespace = 'App\Http\Controllers';
|
protected $namespace = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The path to the "home" route for your application.
|
* The path to the "home" route for your application.
|
||||||
|
|||||||
28
app/Providers/SpeedtestServiceProvider.php
Normal file
28
app/Providers/SpeedtestServiceProvider.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Utils\OoklaTester;
|
||||||
|
use File;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Schema;
|
||||||
|
|
||||||
|
class SpeedtestServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bootstrap services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
$this->app->singleton(
|
||||||
|
SpeedtestProvider::class,
|
||||||
|
function () {
|
||||||
|
return new OoklaTester();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
app/Rules/CurrentPasswordMatches.php
Normal file
41
app/Rules/CurrentPasswordMatches.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Rules;
|
||||||
|
|
||||||
|
use Hash;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
class CurrentPasswordMatches implements Rule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new rule instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the validation rule passes.
|
||||||
|
*
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
return Hash::check($value, auth()->user()->password);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation error message.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return 'The current password doesn\'t match.';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class Speedtest extends Model
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
|
||||||
'ping',
|
|
||||||
'download',
|
|
||||||
'upload',
|
|
||||||
'created_at',
|
|
||||||
'server_id',
|
|
||||||
'server_name',
|
|
||||||
'server_host',
|
|
||||||
'url',
|
|
||||||
'scheduled',
|
|
||||||
'failed',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $table = 'speedtests';
|
|
||||||
}
|
|
||||||
113
app/Utils/InfluxDB/InfluxDB.php
Normal file
113
app/Utils/InfluxDB/InfluxDB.php
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils\InfluxDB;
|
||||||
|
|
||||||
|
use App\Exceptions\InfluxDBConnectionErrorException;
|
||||||
|
use App\Exceptions\InfluxDBNotEnabledException;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use InfluxDB\Client as Version1;
|
||||||
|
use InfluxDB2\Client as Version2;
|
||||||
|
use App\Interfaces\InfluxDBWrapperInterface as Client;
|
||||||
|
|
||||||
|
class InfluxDB
|
||||||
|
{
|
||||||
|
private Client $client;
|
||||||
|
|
||||||
|
private string $database;
|
||||||
|
|
||||||
|
public function __construct(Client $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to influx db
|
||||||
|
*
|
||||||
|
* @param string $host
|
||||||
|
* @param integer $port
|
||||||
|
* @param string $database
|
||||||
|
* @return InfluxDB
|
||||||
|
*/
|
||||||
|
public static function connect()
|
||||||
|
{
|
||||||
|
if (!(bool) SettingsHelper::get('influx_db_enabled')->value) {
|
||||||
|
throw new InfluxDBNotEnabledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = SettingsHelper::get('influx_db_host')->value;
|
||||||
|
$port = SettingsHelper::get('influx_db_port')->value;
|
||||||
|
$username = SettingsHelper::get('influx_db_username')->value;
|
||||||
|
$password = SettingsHelper::get('influx_db_password')->value;
|
||||||
|
$database = SettingsHelper::get('influx_db_database')->value;
|
||||||
|
$version = (int) SettingsHelper::get('influx_db_version')->value;
|
||||||
|
|
||||||
|
$wrapper = $version === 1
|
||||||
|
? new InfluxDBVersion1Wrapper(
|
||||||
|
new Version1(
|
||||||
|
str_replace(['http://', 'https://'], '', $host),
|
||||||
|
$port,
|
||||||
|
$username,
|
||||||
|
$password
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: new InfluxDBVersion2Wrapper(
|
||||||
|
new Version2([
|
||||||
|
'url' => $host . ':' . $port,
|
||||||
|
'token' => '',
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
return (new self($wrapper))->setDatabase($database)
|
||||||
|
->testConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the database field
|
||||||
|
*
|
||||||
|
* @param string $database
|
||||||
|
* @return InfluxDB
|
||||||
|
*/
|
||||||
|
public function setDatabase(string $database): InfluxDB
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the connection
|
||||||
|
*
|
||||||
|
* @throws InfluxDBConnectionErrorException
|
||||||
|
* @return InfluxDB
|
||||||
|
*/
|
||||||
|
public function testConnection(): InfluxDB
|
||||||
|
{
|
||||||
|
if (!$this->client->testConnection()) {
|
||||||
|
throw new InfluxDBConnectionErrorException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->doesDatabaseExist()) {
|
||||||
|
$this->createDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doesDatabaseExist(): bool
|
||||||
|
{
|
||||||
|
return $this->client->doesDatabaseExist($this->database);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createDatabase(): bool
|
||||||
|
{
|
||||||
|
return $this->client->createDatabase($this->database);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Speedtest $speedtest): InfluxDB
|
||||||
|
{
|
||||||
|
$this->client->store($speedtest);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
app/Utils/InfluxDB/InfluxDBVersion1Wrapper.php
Normal file
74
app/Utils/InfluxDB/InfluxDBVersion1Wrapper.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils\InfluxDB;
|
||||||
|
|
||||||
|
use App\Interfaces\InfluxDBWrapperInterface;
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use Exception;
|
||||||
|
use InfluxDB\Client;
|
||||||
|
use InfluxDB\Database;
|
||||||
|
use InfluxDB\Database\RetentionPolicy;
|
||||||
|
use InfluxDB\Point;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
class InfluxDBVersion1Wrapper implements InfluxDBWrapperInterface
|
||||||
|
{
|
||||||
|
private Client $client;
|
||||||
|
private Database $database;
|
||||||
|
|
||||||
|
public function __construct(Client $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnection(): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->client->listDatabases();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error($e);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doesDatabaseExist(string $database): bool
|
||||||
|
{
|
||||||
|
$this->database = $this->client->selectDB($database);
|
||||||
|
|
||||||
|
return (bool) $this->database->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createDatabase(string $database): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->database->create(
|
||||||
|
new RetentionPolicy(
|
||||||
|
'speedtest_retention_policy',
|
||||||
|
config('services.influxdb.retention'),
|
||||||
|
1,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error($e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Speedtest $speedtest): bool
|
||||||
|
{
|
||||||
|
return $this->database->writePoints([
|
||||||
|
new Point(
|
||||||
|
'speedtest',
|
||||||
|
null,
|
||||||
|
['host' => config('services.influxdb.host')],
|
||||||
|
$speedtest->formatForInfluxDB(),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/Utils/InfluxDB/InfluxDBVersion2Wrapper.php
Normal file
37
app/Utils/InfluxDB/InfluxDBVersion2Wrapper.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils\InfluxDB;
|
||||||
|
|
||||||
|
use App\Interfaces\InfluxDBWrapperInterface;
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use InfluxDB2\Client;
|
||||||
|
|
||||||
|
class InfluxDBVersion2Wrapper implements InfluxDBWrapperInterface
|
||||||
|
{
|
||||||
|
private Client $client;
|
||||||
|
|
||||||
|
public function __construct(Client $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnection(): bool
|
||||||
|
{
|
||||||
|
return $this->client->health()->getStatus() !== 'pass';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doesDatabaseExist(string $database): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createDatabase(string $database): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Speedtest $speedtest): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
128
app/Utils/OoklaTester.php
Normal file
128
app/Utils/OoklaTester.php
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
use App\Exceptions\SpeedtestFailureException;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use Cache;
|
||||||
|
use Exception;
|
||||||
|
use JsonException;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
class OoklaTester implements SpeedtestProvider
|
||||||
|
{
|
||||||
|
public function run($output = false, $scheduled = true): Speedtest
|
||||||
|
{
|
||||||
|
if ($output === false) {
|
||||||
|
$output = $this->output();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$output = json_decode($output, true, 512, JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
if (!$this->isOutputComplete($output)) {
|
||||||
|
$test = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$test = Speedtest::create([
|
||||||
|
'ping' => $output['ping']['latency'],
|
||||||
|
'download' => SpeedtestHelper::convert($output['download']['bandwidth']),
|
||||||
|
'upload' => SpeedtestHelper::convert($output['upload']['bandwidth']),
|
||||||
|
'server_id' => $output['server']['id'],
|
||||||
|
'server_name' => $output['server']['name'],
|
||||||
|
'server_host' => $output['server']['host'] . ':' . $output['server']['port'],
|
||||||
|
'url' => $output['result']['url'],
|
||||||
|
'scheduled' => $scheduled
|
||||||
|
]);
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
Log::error('Failed to parse speedtest JSON');
|
||||||
|
Log::error($output);
|
||||||
|
$test = false;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
$test = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($test == false) {
|
||||||
|
Speedtest::create([
|
||||||
|
'ping' => 0,
|
||||||
|
'upload' => 0,
|
||||||
|
'download' => 0,
|
||||||
|
'failed' => true,
|
||||||
|
'scheduled' => $scheduled,
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw new SpeedtestFailureException(json_encode($output));
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::flush();
|
||||||
|
|
||||||
|
return $test;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function output()
|
||||||
|
{
|
||||||
|
$server = SettingsHelper::get('server')['value'];
|
||||||
|
|
||||||
|
$binPath = app_path() . DIRECTORY_SEPARATOR . 'Bin' . DIRECTORY_SEPARATOR . 'speedtest';
|
||||||
|
$homePrefix = config('speedtest.home') . ' && ';
|
||||||
|
|
||||||
|
if ($server != '' && $server != false) {
|
||||||
|
$server = explode(',', $server);
|
||||||
|
$server = $server[array_rand($server)];
|
||||||
|
if ($server == false) {
|
||||||
|
Log::error('Speedtest server undefined');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shell_exec($homePrefix . $binPath . ' -f json -s ' . $server);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shell_exec($homePrefix . $binPath . ' -f json');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the speedtest JSON output is complete/valid
|
||||||
|
*
|
||||||
|
* @param array $output
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function isOutputComplete($output)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Array of indexes that must exist in $output
|
||||||
|
*/
|
||||||
|
$checks = [
|
||||||
|
'type' => 'result',
|
||||||
|
'download' => ['bandwidth' => '*'],
|
||||||
|
'upload' => ['bandwidth' => '*'],
|
||||||
|
'ping' => ['latency' => '*'],
|
||||||
|
'server' => [
|
||||||
|
'id' => '*',
|
||||||
|
'name' => '*',
|
||||||
|
'host' => '*',
|
||||||
|
'port' => '*'
|
||||||
|
],
|
||||||
|
'result' => [
|
||||||
|
'url' => '*'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Array of indexes that must not exist
|
||||||
|
*/
|
||||||
|
$checkMissing = [
|
||||||
|
'type' => 'error'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($checks as $key => $value) {
|
||||||
|
if (!isset($output[$key])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
210
changelog.json
210
changelog.json
@@ -1,4 +1,214 @@
|
|||||||
{
|
{
|
||||||
|
"1.12.2": [
|
||||||
|
{
|
||||||
|
"description": "Fixed a bug where the latest X days widget would not update for the failure graph",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.12.1": [
|
||||||
|
{
|
||||||
|
"description": "Fixed a bug where the latest X days widget would not update (#680)",
|
||||||
|
"link": "https://github.com/henrywhitaker3/Speedtest-Tracker/pull/680"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.12.0": [
|
||||||
|
{
|
||||||
|
"description": "Added InfluxDB intergation.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.11.1": [
|
||||||
|
{
|
||||||
|
"description": "Add option to show/hide columns in the all tests table.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Add option to delete failed tests.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.11.0": [
|
||||||
|
{
|
||||||
|
"description": "Upgrade to Laravel 8.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Refactor loads of back-end stuff.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Refactor front-end stuff.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.10.4": [
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.10.3": [
|
||||||
|
{
|
||||||
|
"description": "Moved stuff into pages.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.10.2": [
|
||||||
|
{
|
||||||
|
"description": "Added option to disable scheduled tests.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.10.1": [
|
||||||
|
{
|
||||||
|
"description": "Fixed integrations config being empty causing healthchecks to not run on scheduled tests.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.10.0": [
|
||||||
|
{
|
||||||
|
"description": "Added automated build for ARM.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.10": [
|
||||||
|
{
|
||||||
|
"description": "Laravel will now log into daily files and logs will only be kept for 7 days.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.9": [
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.8": [
|
||||||
|
{
|
||||||
|
"description": "Fixed bug with minimum display setting.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.7": [
|
||||||
|
{
|
||||||
|
"description": "Added option to display minimum values on the top widgets.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "New general settings section.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.6": [
|
||||||
|
{
|
||||||
|
"description": "Clear the cache on restore.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.5": [
|
||||||
|
{
|
||||||
|
"description": "Added toggle to show failed tests on graphs.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.4": [
|
||||||
|
{
|
||||||
|
"description": "Changed integration config loading.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Added more tests.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.3": [
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.2": [
|
||||||
|
{
|
||||||
|
"description": "Authentication bugfixes.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.1": [
|
||||||
|
{
|
||||||
|
"description": "Added conditional notifications.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.9.0": [
|
||||||
|
{
|
||||||
|
"description": "Added optional authentication.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.8.1": [
|
||||||
|
{
|
||||||
|
"description": "Added healthchecks.io test buttons.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Added close buttons to modals.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Back-end config handling changes.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.8.0": [
|
||||||
|
{
|
||||||
|
"description": "Added healthchecks.io integration.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.7.19": [
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.7.18": [
|
||||||
|
{
|
||||||
|
"description": "Fixed issue with widgets not updating when switching between failed/successful tests.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Updated backup/restore functions to reflect new DB fields.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.7.17": [
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1.7.16": [
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies.",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
"1.7.15": [
|
"1.7.15": [
|
||||||
{
|
{
|
||||||
"description": "Updates for manual installs.",
|
"description": "Updates for manual installs.",
|
||||||
|
|||||||
@@ -10,23 +10,30 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2.5",
|
"php": "^7.2.5",
|
||||||
"doctrine/dbal": "^2.10",
|
"doctrine/dbal": "^2.10",
|
||||||
"dragonmantank/cron-expression": "^2",
|
"dragonmantank/cron-expression": "^3",
|
||||||
"fideloper/proxy": "^4.2",
|
"fideloper/proxy": "^4.2",
|
||||||
"fruitcake/laravel-cors": "^2.0",
|
"fruitcake/laravel-cors": "^2.0",
|
||||||
"guzzlehttp/guzzle": "^7.0",
|
"guzzlehttp/guzzle": "^7.0.1",
|
||||||
"laravel-notification-channels/telegram": "^0.4.0",
|
"henrywhitaker3/healthchecks-io": "^1.0",
|
||||||
"laravel/framework": "^7.0",
|
"henrywhitaker3/laravel-actions": "^1.0",
|
||||||
|
"influxdata/influxdb-client-php": "^1.12",
|
||||||
|
"influxdb/influxdb-php": "^1.15",
|
||||||
|
"laravel-notification-channels/telegram": "^0.5.0",
|
||||||
|
"laravel/framework": "^8.0",
|
||||||
"laravel/slack-notification-channel": "^2.0",
|
"laravel/slack-notification-channel": "^2.0",
|
||||||
"laravel/tinker": "^2.0",
|
"laravel/tinker": "^2.0",
|
||||||
"laravel/ui": "^2.0",
|
"laravel/ui": "^3.0",
|
||||||
"tymon/jwt-auth": "^1.0"
|
"tymon/jwt-auth": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"facade/ignition": "^2.0",
|
"barryvdh/laravel-ide-helper": "^2.8",
|
||||||
|
"brianium/paratest": "^6.2",
|
||||||
|
"facade/ignition": "^2.3.6",
|
||||||
"fzaninotto/faker": "^1.9.1",
|
"fzaninotto/faker": "^1.9.1",
|
||||||
"mockery/mockery": "^1.3.1",
|
"mockery/mockery": "^1.3.1",
|
||||||
"nunomaduro/collision": "^4.1",
|
"nunomaduro/collision": "^5.3",
|
||||||
"phpunit/phpunit": "^8.5"
|
"nunomaduro/larastan": "^0.7.0",
|
||||||
|
"phpunit/phpunit": "^9.5"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"optimize-autoloader": true,
|
"optimize-autoloader": true,
|
||||||
@@ -40,12 +47,10 @@
|
|||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"App\\": "app/"
|
"App\\": "app/",
|
||||||
},
|
"Database\\Factories\\": "database/factories/",
|
||||||
"classmap": [
|
"Database\\Seeders\\": "database/seeders/"
|
||||||
"database/seeds",
|
}
|
||||||
"database/factories"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|||||||
4765
composer.lock
generated
4765
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -178,6 +178,12 @@ return [
|
|||||||
App\Providers\RouteServiceProvider::class,
|
App\Providers\RouteServiceProvider::class,
|
||||||
App\Providers\UpdaterServiceProvider::class,
|
App\Providers\UpdaterServiceProvider::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Custom providers...
|
||||||
|
*/
|
||||||
|
App\Providers\IntegrationsServiceProvider::class,
|
||||||
|
App\Providers\SpeedtestServiceProvider::class,
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -230,6 +236,7 @@ return [
|
|||||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||||
'View' => Illuminate\Support\Facades\View::class,
|
'View' => Illuminate\Support\Facades\View::class,
|
||||||
'Updater' => App\Facades\UpdaterFacade::class,
|
'Updater' => App\Facades\UpdaterFacade::class,
|
||||||
|
'Healthcheck' => App\Facades\HealthchecksFacade::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ return [
|
|||||||
'providers' => [
|
'providers' => [
|
||||||
'users' => [
|
'users' => [
|
||||||
'driver' => 'eloquent',
|
'driver' => 'eloquent',
|
||||||
'model' => App\User::class,
|
'model' => App\Models\User::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
// 'users' => [
|
// 'users' => [
|
||||||
|
|||||||
409
config/clockwork.php
Normal file
409
config/clockwork.php
Normal file
@@ -0,0 +1,409 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Enable Clockwork
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork is enabled by default only when your application is in debug mode. Here you can explicitly enable or
|
||||||
|
| disable Clockwork. When disabled, no data is collected and the api and web ui are inactive.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'enable' => env('CLOCKWORK_ENABLE', null),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Features
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You can enable or disable various Clockwork features here. Some features have additional settings (eg. slow query
|
||||||
|
| threshold for database queries).
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'features' => [
|
||||||
|
|
||||||
|
// Cache usage stats and cache queries including results
|
||||||
|
'cache' => [
|
||||||
|
'enabled' => env('CLOCKWORK_CACHE_ENABLED', true),
|
||||||
|
|
||||||
|
// Collect cache queries including results (high performance impact with a high number of queries)
|
||||||
|
'collect_queries' => env('CLOCKWORK_CACHE_QUERIES', false)
|
||||||
|
],
|
||||||
|
|
||||||
|
// Database usage stats and queries
|
||||||
|
'database' => [
|
||||||
|
'enabled' => env('CLOCKWORK_DATABASE_ENABLED', true),
|
||||||
|
|
||||||
|
// Collect database queries (high performance impact with a very high number of queries)
|
||||||
|
'collect_queries' => env('CLOCKWORK_DATABASE_COLLECT_QUERIES', true),
|
||||||
|
|
||||||
|
// Collect details of models updates (high performance impact with a lot of model updates)
|
||||||
|
'collect_models_actions' => env('CLOCKWORK_DATABASE_COLLECT_MODELS_ACTIONS', true),
|
||||||
|
|
||||||
|
// Collect details of retrieved models (very high performance impact with a lot of models retrieved)
|
||||||
|
'collect_models_retrieved' => env('CLOCKWORK_DATABASE_COLLECT_MODELS_RETRIEVED', false),
|
||||||
|
|
||||||
|
// Query execution time threshold in miliseconds after which the query will be marked as slow
|
||||||
|
'slow_threshold' => env('CLOCKWORK_DATABASE_SLOW_THRESHOLD'),
|
||||||
|
|
||||||
|
// Collect only slow database queries
|
||||||
|
'slow_only' => env('CLOCKWORK_DATABASE_SLOW_ONLY', false),
|
||||||
|
|
||||||
|
// Detect and report duplicate (N+1) queries
|
||||||
|
'detect_duplicate_queries' => env('CLOCKWORK_DATABASE_DETECT_DUPLICATE_QUERIES', false)
|
||||||
|
],
|
||||||
|
|
||||||
|
// Dispatched events
|
||||||
|
'events' => [
|
||||||
|
'enabled' => env('CLOCKWORK_EVENTS_ENABLED', true),
|
||||||
|
|
||||||
|
// Ignored events (framework events are ignored by default)
|
||||||
|
'ignored_events' => [
|
||||||
|
// App\Events\UserRegistered::class,
|
||||||
|
// 'user.registered'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
// Laravel log (you can still log directly to Clockwork with laravel log disabled)
|
||||||
|
'log' => [
|
||||||
|
'enabled' => env('CLOCKWORK_LOG_ENABLED', true)
|
||||||
|
],
|
||||||
|
|
||||||
|
// Sent notifications
|
||||||
|
'notifications' => [
|
||||||
|
'enabled' => env('CLOCKWORK_NOTIFICATIONS_ENABLED', true),
|
||||||
|
],
|
||||||
|
|
||||||
|
// Performance metrics
|
||||||
|
'performance' => [
|
||||||
|
// Allow collecting of client metrics. Requires separate clockwork-browser npm package.
|
||||||
|
'client_metrics' => env('CLOCKWORK_PERFORMANCE_CLIENT_METRICS', true)
|
||||||
|
],
|
||||||
|
|
||||||
|
// Dispatched queue jobs
|
||||||
|
'queue' => [
|
||||||
|
'enabled' => env('CLOCKWORK_QUEUE_ENABLED', true)
|
||||||
|
],
|
||||||
|
|
||||||
|
// Redis commands
|
||||||
|
'redis' => [
|
||||||
|
'enabled' => env('CLOCKWORK_REDIS_ENABLED', true)
|
||||||
|
],
|
||||||
|
|
||||||
|
// Routes list
|
||||||
|
'routes' => [
|
||||||
|
'enabled' => env('CLOCKWORK_ROUTES_ENABLED', false)
|
||||||
|
],
|
||||||
|
|
||||||
|
// Rendered views
|
||||||
|
'views' => [
|
||||||
|
'enabled' => env('CLOCKWORK_VIEWS_ENABLED', true),
|
||||||
|
|
||||||
|
// Collect views including view data (high performance impact with a high number of views)
|
||||||
|
'collect_data' => env('CLOCKWORK_VIEWS_COLLECT_DATA', false),
|
||||||
|
|
||||||
|
// Use Twig profiler instead of Laravel events for apps using laravel-twigbridge (more precise, but does
|
||||||
|
// not support collecting view data)
|
||||||
|
'use_twig_profiler' => env('CLOCKWORK_VIEWS_USE_TWIG_PROFILER', false)
|
||||||
|
]
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Enable web UI
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork comes with a web UI accessibla via http://your.app/clockwork. Here you can enable or disable this
|
||||||
|
| feature. You can also set a custom path for the web UI.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'web' => env('CLOCKWORK_WEB', true),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Enable toolbar
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork can show a toolbar with basic metrics on all responses. Here you can enable or disable this feature.
|
||||||
|
| Requires a separate clockwork-browser npm library.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'toolbar' => env('CLOCKWORK_TOOLBAR', false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| HTTP requests collection
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork collects data about HTTP requests to your app. Here you can choose which requests should be collected.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'requests' => [
|
||||||
|
// With on-demand mode enabled, Clockwork will only profile requests when the browser extension is open or you
|
||||||
|
// manually pass a "clockwork-profile" cookie or get/post data key.
|
||||||
|
// Optionally you can specify a "secret" that has to be passed as the value to enable profiling.
|
||||||
|
'on_demand' => env('CLOCKWORK_REQUESTS_ON_DEMAND', false),
|
||||||
|
|
||||||
|
// Collect only errors (requests with HTTP 4xx and 5xx responses)
|
||||||
|
'errors_only' => env('CLOCKWORK_REQUESTS_ERRORS_ONLY', false),
|
||||||
|
|
||||||
|
// Response time threshold in miliseconds after which the request will be marked as slow
|
||||||
|
'slow_threshold' => env('CLOCKWORK_REQUESTS_SLOW_THRESHOLD'),
|
||||||
|
|
||||||
|
// Collect only slow requests
|
||||||
|
'slow_only' => env('CLOCKWORK_REQUESTS_SLOW_ONLY', false),
|
||||||
|
|
||||||
|
// Sample the collected requests (eg. set to 100 to collect only 1 in 100 requests)
|
||||||
|
'sample' => env('CLOCKWORK_REQUESTS_SAMPLE', false),
|
||||||
|
|
||||||
|
// List of URIs that should not be collected
|
||||||
|
'except' => [
|
||||||
|
'/horizon/.*', // Laravel Horizon requests
|
||||||
|
'/telescope/.*', // Laravel Telescope requests
|
||||||
|
'/files/*'
|
||||||
|
],
|
||||||
|
|
||||||
|
// List of URIs that should be collected, any other URI will not be collected if not empty
|
||||||
|
'only' => [
|
||||||
|
// '/api/.*'
|
||||||
|
],
|
||||||
|
|
||||||
|
// Don't collect OPTIONS requests, mostly used in the CSRF pre-flight requests and are rarely of interest
|
||||||
|
'except_preflight' => env('CLOCKWORK_REQUESTS_EXCEPT_PREFLIGHT', true)
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Artisan commands collection
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork can collect data about executed artisan commands. Here you can enable and configure which commands
|
||||||
|
| should be collected.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'artisan' => [
|
||||||
|
// Enable or disable collection of executed Artisan commands
|
||||||
|
'collect' => env('CLOCKWORK_ARTISAN_COLLECT', false),
|
||||||
|
|
||||||
|
// List of commands that should not be collected (built-in commands are not collected by default)
|
||||||
|
'except' => [
|
||||||
|
// 'inspire'
|
||||||
|
],
|
||||||
|
|
||||||
|
// List of commands that should be collected, any other command will not be collected if not empty
|
||||||
|
'only' => [
|
||||||
|
// 'inspire'
|
||||||
|
],
|
||||||
|
|
||||||
|
// Enable or disable collection of command output
|
||||||
|
'collect_output' => env('CLOCKWORK_ARTISAN_COLLECT_OUTPUT', false),
|
||||||
|
|
||||||
|
// Enable or disable collection of built-in Laravel commands
|
||||||
|
'except_laravel_commands' => env('CLOCKWORK_ARTISAN_EXCEPT_LARAVEL_COMMANDS', true)
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Queue jobs collection
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork can collect data about executed queue jobs. Here you can enable and configure which queue jobs should
|
||||||
|
| be collected.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'queue' => [
|
||||||
|
// Enable or disable collection of executed queue jobs
|
||||||
|
'collect' => env('CLOCKWORK_QUEUE_COLLECT', false),
|
||||||
|
|
||||||
|
// List of queue jobs that should not be collected
|
||||||
|
'except' => [
|
||||||
|
// App\Jobs\ExpensiveJob::class
|
||||||
|
],
|
||||||
|
|
||||||
|
// List of queue jobs that should be collected, any other queue job will not be collected if not empty
|
||||||
|
'only' => [
|
||||||
|
// App\Jobs\BuggyJob::class
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Tests collection
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork can collect data about executed tests. Here you can enable and configure which tests should be
|
||||||
|
| collected.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'tests' => [
|
||||||
|
// Enable or disable collection of ran tests
|
||||||
|
'collect' => env('CLOCKWORK_TESTS_COLLECT', false),
|
||||||
|
|
||||||
|
// List of tests that should not be collected
|
||||||
|
'except' => [
|
||||||
|
// Tests\Unit\ExampleTest::class
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Enable data collection when Clockwork is disabled
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You can enable this setting to collect data even when Clockwork is disabled. Eg. for future analysis.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'collect_data_always' => env('CLOCKWORK_COLLECT_DATA_ALWAYS', false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Metadata storage
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Configure how is the metadata collected by Clockwork stored. Two options are available:
|
||||||
|
| - files - A simple fast storage implementation storing data in one-per-request files.
|
||||||
|
| - sql - Stores requests in a sql database. Supports MySQL, Postgresql, Sqlite and requires PDO.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'storage' => env('CLOCKWORK_STORAGE', 'files'),
|
||||||
|
|
||||||
|
// Path where the Clockwork metadata is stored
|
||||||
|
'storage_files_path' => env('CLOCKWORK_STORAGE_FILES_PATH', storage_path('clockwork')),
|
||||||
|
|
||||||
|
// Compress the metadata files using gzip, trading a little bit of performance for lower disk usage
|
||||||
|
'storage_files_compress' => env('CLOCKWORK_STORAGE_FILES_COMPRESS', false),
|
||||||
|
|
||||||
|
// SQL database to use, can be a name of database configured in database.php or a path to a sqlite file
|
||||||
|
'storage_sql_database' => env('CLOCKWORK_STORAGE_SQL_DATABASE', storage_path('clockwork.sqlite')),
|
||||||
|
|
||||||
|
// SQL table name to use, the table is automatically created and udpated when needed
|
||||||
|
'storage_sql_table' => env('CLOCKWORK_STORAGE_SQL_TABLE', 'clockwork'),
|
||||||
|
|
||||||
|
// Maximum lifetime of collected metadata in minutes, older requests will automatically be deleted, false to disable
|
||||||
|
'storage_expiration' => env('CLOCKWORK_STORAGE_EXPIRATION', 60 * 24 * 7),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Authentication
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork can be configured to require authentication before allowing access to the collected data. This might be
|
||||||
|
| useful when the application is publicly accessible. Setting to true will enable a simple authentication with a
|
||||||
|
| pre-configured password. You can also pass a class name of a custom implementation.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'authentication' => env('CLOCKWORK_AUTHENTICATION', false),
|
||||||
|
|
||||||
|
// Password for the simple authentication
|
||||||
|
'authentication_password' => env('CLOCKWORK_AUTHENTICATION_PASSWORD', 'VerySecretPassword'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Stack traces collection
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork can collect stack traces for log messages and certain data like database queries. Here you can set
|
||||||
|
| whether to collect stack traces, limit the number of collected frames and set further configuration. Collecting
|
||||||
|
| long stack traces considerably increases metadata size.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'stack_traces' => [
|
||||||
|
// Enable or disable collecting of stack traces
|
||||||
|
'enabled' => env('CLOCKWORK_STACK_TRACES_ENABLED', true),
|
||||||
|
|
||||||
|
// Limit the number of frames to be collected
|
||||||
|
'limit' => env('CLOCKWORK_STACK_TRACES_LIMIT', 10),
|
||||||
|
|
||||||
|
// List of vendor names to skip when determining caller, common vendors are automatically added
|
||||||
|
'skip_vendors' => [
|
||||||
|
// 'phpunit'
|
||||||
|
],
|
||||||
|
|
||||||
|
// List of namespaces to skip when determining caller
|
||||||
|
'skip_namespaces' => [
|
||||||
|
// 'Laravel'
|
||||||
|
],
|
||||||
|
|
||||||
|
// List of class names to skip when determining caller
|
||||||
|
'skip_classes' => [
|
||||||
|
// App\CustomLog::class
|
||||||
|
]
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Serialization
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork serializes the collected data to json for storage and transfer. Here you can configure certain aspects
|
||||||
|
| of serialization. Serialization has a large effect on the cpu time and memory usage.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Maximum depth of serialized multi-level arrays and objects
|
||||||
|
'serialization_depth' => env('CLOCKWORK_SERIALIZATION_DEPTH', 10),
|
||||||
|
|
||||||
|
// A list of classes that will never be serialized (eg. a common service container class)
|
||||||
|
'serialization_blackbox' => [
|
||||||
|
\Illuminate\Container\Container::class,
|
||||||
|
\Illuminate\Foundation\Application::class,
|
||||||
|
\Laravel\Lumen\Application::class
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Register helpers
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork comes with a "clock" global helper function. You can use this helper to quickly log something and to
|
||||||
|
| access the Clockwork instance.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'register_helpers' => env('CLOCKWORK_REGISTER_HELPERS', true),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Send Headers for AJAX request
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When trying to collect data the AJAX method can sometimes fail if it is missing required headers. For example, an
|
||||||
|
| API might require a version number using Accept headers to route the HTTP request to the correct codebase.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'headers' => [
|
||||||
|
// 'Accept' => 'application/vnd.com.whatever.v1+json',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
| Server-Timing
|
||||||
|
|------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Clockwork supports the W3C Server Timing specification, which allows for collecting a simple performance metrics
|
||||||
|
| in a cross-browser way. Eg. in Chrome, your app, database and timeline event timings will be shown in the Dev
|
||||||
|
| Tools network tab. This setting specifies the max number of timeline events that will be sent. Setting to false
|
||||||
|
| will disable the feature.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'server_timing' => env('CLOCKWORK_SERVER_TIMING', 10)
|
||||||
|
|
||||||
|
];
|
||||||
59
config/integrations.php
Normal file
59
config/integrations.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Healthchecks enabled
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option defines whether healthchecks integrations are enabled
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'healthchecks_enabled' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Healthchecks UUID
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option defines the UUID for healthchecks
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'healthchecks_uuid' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Slack webhook
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option defines the slack webhook url
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'slack_webhook' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telegram bot token
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option defines the telegram bot token
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'telegram_bot_token' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telegram chat id
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option defines the telegram chat id
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'telegram_chat_id' => null,
|
||||||
|
];
|
||||||
@@ -37,7 +37,7 @@ return [
|
|||||||
'channels' => [
|
'channels' => [
|
||||||
'stack' => [
|
'stack' => [
|
||||||
'driver' => 'stack',
|
'driver' => 'stack',
|
||||||
'channels' => ['single'],
|
'channels' => ['daily'],
|
||||||
'ignore_exceptions' => false,
|
'ignore_exceptions' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ return [
|
|||||||
'driver' => 'daily',
|
'driver' => 'daily',
|
||||||
'path' => storage_path('logs/laravel.log'),
|
'path' => storage_path('logs/laravel.log'),
|
||||||
'level' => 'debug',
|
'level' => 'debug',
|
||||||
'days' => 14,
|
'days' => 7,
|
||||||
],
|
],
|
||||||
|
|
||||||
'slack' => [
|
'slack' => [
|
||||||
|
|||||||
@@ -34,4 +34,9 @@ return [
|
|||||||
'token' => env('TELEGRAM_BOT_TOKEN', 'YOUR BOT TOKEN HERE')
|
'token' => env('TELEGRAM_BOT_TOKEN', 'YOUR BOT TOKEN HERE')
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'influxdb' => [
|
||||||
|
'retention' => env('INFLUXDB_RETENTION', '30d'),
|
||||||
|
'host' => env('INFLUXDB_HOST_TAG', 'speedtest'),
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'version' => '1.7.15',
|
'version' => '1.12.2',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -15,7 +15,7 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'install' => 'manual',
|
'install' => 'docker',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -23,7 +23,7 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'home' => 'HOME=' . base_path() . DIRECTORY_SEPARATOR,
|
'home' => 'HOME=/config',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|||||||
1
database/.gitignore
vendored
1
database/.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite-journal
|
*.sqlite-journal
|
||||||
*.bak
|
*.bak
|
||||||
|
*.db_test*
|
||||||
|
|||||||
32
database/factories/SpeedtestFactory.php
Normal file
32
database/factories/SpeedtestFactory.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Speedtest;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
class SpeedtestFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name of the factory's corresponding model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $model = Speedtest::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'download' => rand(15, 900),
|
||||||
|
'upload' => rand(15, 900),
|
||||||
|
'ping' => rand(1, 25),
|
||||||
|
'scheduled' => (bool) rand(0, 1),
|
||||||
|
'failed' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
/** @var \Illuminate\Database\Eloquent\Factory $factory */
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */
|
||||||
|
|
||||||
use App\User;
|
use App\Models\User;
|
||||||
use Faker\Generator as Faker;
|
use Faker\Generator as Faker;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Setting;
|
use App\Models\Setting;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Setting;
|
use App\Models\Setting;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Setting;
|
use App\Models\Setting;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Setting;
|
use App\Models\Setting;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddHealthchecksSettings extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if(!SettingsHelper::get('healthchecks_enabled')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'healthchecks_enabled',
|
||||||
|
'value' => false,
|
||||||
|
'description' => 'Enable the healthchecks.io integration for speedtests.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SettingsHelper::get('healthchecks_uuid')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'healthchecks_uuid',
|
||||||
|
'value' => '',
|
||||||
|
'description' => ''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'healthchecks_enabled',
|
||||||
|
'healthchecks_uuid',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddAuthenticationSettings extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if(!SettingsHelper::get('auth')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'auth',
|
||||||
|
'value' => false,
|
||||||
|
'description' => 'Enable authentication.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'auth',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddConditionalNotificationsSettings extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if(!SettingsHelper::get('threshold_alert_percentage_notifications')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'threshold_alert_percentage_notifications',
|
||||||
|
'value' => false,
|
||||||
|
'description' => 'Enable/disable theshold percentage notifications'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SettingsHelper::get('threshold_alert_percentage')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'threshold_alert_percentage',
|
||||||
|
'value' => 15,
|
||||||
|
'description' => 'When any value of a speedtest is x percent lower than the average, a notification will be sent.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SettingsHelper::get('threshold_alert_absolute_notifications')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'threshold_alert_absolute_notifications',
|
||||||
|
'value' => false,
|
||||||
|
'description' => 'Enable/disable absolute theshold notifications'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SettingsHelper::get('threshold_alert_absolute_download')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'threshold_alert_absolute_download',
|
||||||
|
'value' => '',
|
||||||
|
'description' => 'When the download is lower than this value, a notification will be sent. Leave blank to disable'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SettingsHelper::get('threshold_alert_absolute_upload')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'threshold_alert_absolute_upload',
|
||||||
|
'value' => '',
|
||||||
|
'description' => 'When the upload is lower than this value, a notification will be sent. Leave blank to disable'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SettingsHelper::get('threshold_alert_absolute_ping')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'threshold_alert_absolute_ping',
|
||||||
|
'value' => '',
|
||||||
|
'description' => 'When the ping is higher than this value, a notification will be sent. Leave blank to disable'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'threshold_alert_percentage',
|
||||||
|
'threshold_alert_absolute_download',
|
||||||
|
'threshold_alert_absolute_upload',
|
||||||
|
'threshold_alert_absolute_ping',
|
||||||
|
'threshold_alert_percentage_notifications',
|
||||||
|
'threshold_alert_absolute_notifications'
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddShowFailedTestsSetting extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if(!SettingsHelper::get('show_failed_tests_on_graph')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'show_failed_tests_on_graph',
|
||||||
|
'value' => true,
|
||||||
|
'description' => 'If enabled, failed tests will appear on the graphs as 0.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'show_failed_tests_on_graph',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddWidgetCardSettings extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (!SettingsHelper::get('show_average')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'show_average',
|
||||||
|
'value' => true,
|
||||||
|
'description' => 'If enabled, the average value for speedtests will be shown in the widgets.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SettingsHelper::get('show_max')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'show_max',
|
||||||
|
'value' => true,
|
||||||
|
'description' => 'If enabled, the maximum value for speedtests will be shown in the widgets.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SettingsHelper::get('show_min')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'show_min',
|
||||||
|
'value' => true,
|
||||||
|
'description' => 'If enabled, the minimum value for speedtests will be shown in the widgets.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'show_average',
|
||||||
|
'show_max',
|
||||||
|
'show_min',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddScheduleEnabledSetting extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (!SettingsHelper::get('schedule_enabled')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'schedule_enabled',
|
||||||
|
'value' => true,
|
||||||
|
'description' => 'Enable/disable the schedule worker'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'schedule_enabled',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddAppNameSetting extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (!SettingsHelper::get('app_name')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'app_name',
|
||||||
|
'value' => 'Speedtest Tracker',
|
||||||
|
'description' => 'Set a custom app name'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'app_name',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddCustomHealthchecksSetting extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (!SettingsHelper::get('healthchecks_server_url')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'healthchecks_server_url',
|
||||||
|
'value' => 'https://hc-ping.com/',
|
||||||
|
'description' => 'The URL of the healthchecks.io server. Change this to use a self-hosted server.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'healthchecks_server_url',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user