Compare commits

...

200 Commits

Author SHA1 Message Date
Kode
9e80c3fe40 2.2.2 hasn't been released yet 2019-06-22 17:05:05 +01:00
Kode
f272dd13ce Fix for tags not showing 2019-06-22 17:03:01 +01:00
Chris
9f26de89a4 add remember user and missing check icon 2019-06-19 12:11:55 +01:00
Chris
e23964ebad set session and remember user when logged in via remote means 2019-06-19 12:01:17 +01:00
Chris
d0584ac941 Update app version ready for next release 2019-06-19 11:49:17 +01:00
Chris
1410c41f48 Add REMOTE_USER auth 2019-06-19 11:32:41 +01:00
Chris
5f524563cf Merge branch 'master' of https://github.com/linuxserver/Heimdall 2019-06-18 13:41:06 +01:00
Chris
3d181623c3 fix small issue 2019-06-18 13:41:02 +01:00
KodeStar
1919dbfd96 Update readme.md 2019-06-18 13:22:14 +01:00
Chris
8e1615ac5f fix search tab behaviour and tag link in subfolder 2019-06-18 12:25:05 +01:00
Chris
7966f07fdb update scripts + add home dash as default tag 2019-06-18 11:51:07 +01:00
Chris
ac8fe7012b remove false from routes 2019-06-18 10:51:51 +01:00
Chris
51d89dae82 Possible fix for folder based RP 2019-06-18 08:46:57 +01:00
KodeStar
ef21ac0fe7 Merge pull request #359 from pahakalle/patch-1
Fix typo in Finnish translation
2019-06-13 19:10:04 +01:00
KodeStar
02697687cc Merge pull request #364 from linuxserver/hometags
Allow apps to be tagged to the homepage
2019-06-13 19:09:19 +01:00
Kode
fd2f7f27a6 Make home tag translatable 2019-06-13 19:07:38 +01:00
Kode
21b1ef5e4b fix pin toggle and get homepage dash working 2019-06-13 18:59:01 +01:00
Chris
e452d3b5f6 add seed 2019-06-13 15:57:54 +01:00
Chris
1419882455 initial 2019-06-13 15:40:26 +01:00
KodeStar
d79202ed87 Update readme.md 2019-06-12 11:02:19 +01:00
KodeStar
aa05c947a9 Remove url validation 2019-06-11 14:11:13 +01:00
Chris
eb69ea05fa Update package-lock.json 2019-06-11 12:35:48 +01:00
Chris
244b0998f9 update bootstrap-sass 2019-06-11 12:34:28 +01:00
Chris
1f608b1c21 Update composer dependencies 2019-06-11 12:29:32 +01:00
Chris
7d6df3843b update node deps 2019-06-11 12:20:04 +01:00
Kalle Laine
dd4b94ba71 Fix typo in Finnish translation 2019-06-11 14:18:08 +03:00
Chris
edb9397a47 Add startpage back in 2019-06-11 12:02:18 +01:00
KodeStar
66815a8487 readd startpage 2019-06-11 11:45:13 +01:00
KodeStar
5ee19bceb6 Merge pull request #357 from Agurato/master
Add Qwant as search engine
2019-06-11 11:32:31 +01:00
KodeStar
deba7d0279 Merge branch 'master' into master 2019-06-11 11:32:11 +01:00
KodeStar
38e93d33f1 remove startpage 2019-06-11 11:30:33 +01:00
KodeStar
f182f305fc remove startpage 2019-06-11 11:30:17 +01:00
KodeStar
03c675ed57 remove startpage 2019-06-11 11:29:54 +01:00
KodeStar
3916b2d4dc remove startpage 2019-06-11 11:29:38 +01:00
KodeStar
698a7a9bbc remove startpage 2019-06-11 11:28:07 +01:00
KodeStar
9954d1b6bf Merge pull request #342 from dmbekker/master
Dutch Translation update
2019-06-11 11:04:52 +01:00
KodeStar
a98b981efe Merge pull request #348 from Aerion/toggle-livestats-refresh-active-tab
turn off livestats update when the tab isn't active
2019-06-11 11:03:14 +01:00
KodeStar
dd2ca62eaf Add private subnets to trusted proxies for reverse proxy use 2019-06-11 11:00:44 +01:00
Agurato
dc2a42ad6d Add Qwant as search engine 2019-06-08 13:02:12 +02:00
Guillaume Taquet Gasperini
b5b25458db turn off livestats update when the tab isn't active
Fixes #330: Turning off the updating when the tab isn't active

When the tab is not visible from the user, the livestats refresh are
stopped. As soon as its visibility changes (from visibilitychange
API), the livestats are refreshed as usual.

This will prevent unnecessary calls to the different services when the
tab is hidden from the user.
2019-05-20 21:58:03 +02:00
David
ac4bbceb2f New Translations, Fixes for current translation
Added new translations

Minor language-issues fixed
- Changed 'Toepassing' to 'Applicatie' (Both Acronyms were used)
- Changed 'Reeks' to 'Tekenreeks'
2019-04-22 16:38:07 +02:00
Kode
4980bfab12 fix not protocol breaking adding for some reason 2019-03-30 23:10:00 +00:00
Chris
63e0d07d50 remove unused storeSearchProvider 2019-02-04 08:51:49 +00:00
Kode
2b76520e3a change version number and update lang value 2019-01-18 18:42:17 +00:00
Kode
6631b8a23b update dev npm deps 2019-01-18 18:37:51 +00:00
Kode
04d34017c1 Composer deps update 2019-01-18 18:33:28 +00:00
KodeStar
6dcbcb452e Merge pull request #316 from linuxserver/search-enhancements
Search enhancements
2019-01-18 18:27:19 +00:00
Kode
7c74b3bb13 update manifest 2019-01-18 18:26:07 +00:00
Kode
b76a9ee118 Merge branch 'master' into search-enhancements 2019-01-18 18:25:00 +00:00
Kode
40da649b10 Changes to search 2019-01-18 18:21:44 +00:00
Chris
cd64d762e7 additions to search 2019-01-18 15:21:50 +00:00
Chris
b91c52820a replace fontawesome with js version 2019-01-18 14:07:17 +00:00
KodeStar
1204ffd306 Update app.php 2019-01-16 17:24:31 +00:00
Chris
978267dd14 Update Search.php 2019-01-16 15:00:42 +00:00
Chris
f935fd94c6 initial work on seach changes 2019-01-16 14:47:32 +00:00
Chris
fb2428e21b fixes #109 2019-01-16 14:20:33 +00:00
Chris
e36a126c01 fixes #286 2019-01-16 12:02:38 +00:00
Chris
b186978624 fixes #299 2019-01-16 11:39:39 +00:00
Chris
49773d7654 Fixes #302 2019-01-16 10:37:25 +00:00
Chris
53c03751a1 ignore supported apps folder 2019-01-16 10:35:55 +00:00
Chris
9d2908449a Merge branch 'master' of https://github.com/linuxserver/Heimdall 2019-01-15 15:27:29 +00:00
KodeStar
caf92bcf6d check for gitignore in public storage and symlink if missing 2019-01-15 15:25:53 +00:00
KodeStar
cbef469e02 Update AppServiceProvider.php 2019-01-15 15:23:10 +00:00
Chris
a6bfc1d76c ignore .env 2019-01-15 14:27:19 +00:00
KodeStar
0446a74de9 Merge pull request #308 from openseabrus/master
Portuguese (PT-PT) translation.
2019-01-07 08:27:46 +00:00
KodeStar
1a14079b39 add APP_KEY 2019-01-04 14:00:15 +00:00
KodeStar
d9c1919ddc Merge pull request #313 from linuxserver/Fix-default-appkey
Update .env.example
2019-01-04 11:52:07 +00:00
KodeStar
f42ac0c5ba if .env is missing copy example and generate key 2019-01-04 10:38:07 +00:00
KodeStar
c073929895 Delete .env 2019-01-04 10:36:04 +00:00
KodeStar
02df7844a4 Update .env.example 2019-01-04 10:34:12 +00:00
l1904l
e43aba0111 Portuguese (PT-PT) translation. 2018-12-26 15:56:34 +00:00
Kode
ac7446ffe6 update version 2018-11-14 18:14:23 +00:00
Kode
e45d3ca6ec remove apps 2018-11-14 18:10:55 +00:00
Chris
603550a453 autofocus password 2018-11-13 12:38:19 +00:00
Kode
4463ef4a9a add right to livestats 2018-11-10 21:37:21 +00:00
Kode
49b8dc0079 Update version number 2018-11-10 18:05:19 +00:00
Kode
ab83c3a551 Small changes
Copy icon if missing on new app add.
Change order of create symlink and load apps
2018-11-09 23:07:01 +00:00
Chris
98c6093674 Check url isn't missing when testing 2018-11-07 11:24:26 +00:00
Chris
e1f51521bf Update version 2018-11-07 08:57:18 +00:00
Chris
e85bc98dcc Fix ProcessJobs not working if folder is empty but table isn't 2018-11-07 08:54:00 +00:00
Chris
4ba52baa5c Remove old code from tags form 2018-11-06 15:44:09 +00:00
Chris
a82077b4de Update version 2018-11-06 15:15:55 +00:00
Chris
b8f04f3d11 Fix preview and config not retaining values 2018-11-06 14:52:45 +00:00
Chris
e91f65f833 Ignore copy errors 2018-11-06 11:53:25 +00:00
Chris
575ab9be2d update version 2018-11-06 11:29:33 +00:00
Chris
64071bb60f Add migration and change path 2018-11-06 11:28:22 +00:00
Chris
44621a1a61 Change version + replace supported with icons 2018-11-06 09:48:03 +00:00
Chris
c56043e1f9 Add default value to null rather than not set 2018-11-06 09:20:53 +00:00
Kode
e8e4cbfd41 Change sort order for applist 2018-11-05 21:03:42 +00:00
Kode
4b2bbe0614 update version 2018-11-05 19:03:26 +00:00
Kode
9adfa14e62 fix light colour livestats 2018-11-05 19:02:19 +00:00
Kode
067f82b632 fix copy failing 2018-11-05 18:47:56 +00:00
Chris
e24e7979be update version number 2018-11-05 15:28:17 +00:00
Chris
c7c2b6e6f2 change location of icons so it's persistant 2018-11-05 15:27:29 +00:00
Chris
96798963d6 Update register app to allow all 2018-11-05 12:31:43 +00:00
Chris
75508a81ef Update version 2018-11-05 09:44:11 +00:00
Chris
f7f4efadb7 Change html comment to php comment 2018-11-05 09:40:44 +00:00
Kode
4cc80b98db inconsequential update to try and fix broken build 2018-11-04 22:33:44 +00:00
Kode
b07efaa7d0 Merge branch 'master' of https://github.com/linuxserver/ifai 2018-11-04 21:43:16 +00:00
Kode
7ba8ea6dd4 die if missing php-zip 2018-11-04 21:43:12 +00:00
KodeStar
479461821f Update readme to use shields for apps 2018-11-04 18:30:27 +00:00
Kode
d25aea38fb update app version to correct number 2018-11-04 16:26:08 +00:00
Kode
0067502b37 Initial application process 2018-11-04 16:20:12 +00:00
Kode
d321af6085 update app type via dropdown 2018-11-04 16:03:55 +00:00
Kode
b30f1730e4 order applist in dropdown by name 2018-11-04 15:27:57 +00:00
KodeStar
7a02986982 Merge pull request #282 from linuxserver/2.1.0
2.1.0
2018-11-04 14:54:13 +00:00
Kode
a4107cba25 Fix tags 2018-11-04 14:33:39 +00:00
KodeStar
ba187aa345 Merge pull request #281 from BiohZn/master
Finnish and Swedish translation
2018-11-04 13:24:39 +00:00
Conny Sjöblom
b400cef734 Update Finnish translation 2018-11-04 15:20:53 +02:00
Conny Sjöblom
03b72b8d2e Update Swedish translation 2018-11-04 15:19:48 +02:00
Conny Sjöblom
0c7e20dee0 Finnish translation 2018-11-04 15:06:38 +02:00
Conny Sjöblom
aa42c71a06 Swedish translation 2018-11-04 14:53:20 +02:00
Kode
041c0b81a3 updates 2018-11-01 08:55:21 +00:00
Chris
fe6776ee9d Some changes to SupportedApps.php 2018-10-31 15:42:34 +00:00
Chris
125b9f4160 Some fixes and changes to run css 2018-10-30 14:58:45 +00:00
Kode
488fee7b4b fixes 2018-10-29 19:43:08 +00:00
Kode
4fba596909 add enable button 2018-10-29 19:01:25 +00:00
Chris
af04781830 remove class name from icon name 2018-10-29 15:24:47 +00:00
Chris
2507cda94c Normalise classname to remove non alpha chars 2018-10-29 15:12:47 +00:00
Kode
21c1401859 fixes 2018-10-28 20:41:46 +00:00
Kode
4351f55225 updates 2018-10-28 10:59:59 +00:00
Kode
d1956c4e88 updates 2018-10-26 19:55:40 +01:00
Chris
d76d056ed1 changes 2018-10-25 14:42:14 +01:00
Chris
e081cc31a2 small update 2018-10-25 12:14:18 +01:00
Chris
88153b0e32 Redo app form page 2018-10-25 09:44:40 +01:00
Chris
2b2d51cb6f changes 2018-10-23 15:53:56 +01:00
Kode
abd8c7227d Merge branch 'master' into 2.1.0 2018-10-21 14:09:39 +01:00
Kode
50d6dc7b71 change to find first user with public front 2018-10-21 13:47:03 +01:00
Kode
b4a1ecc305 update EnhancedApps 2018-10-21 13:32:58 +01:00
Kode
540bead0db Fix test button 2018-10-21 13:23:23 +01:00
Kode
6e9f25d680 Fixes 2018-10-21 12:39:12 +01:00
Kode
268afe7006 Get working 2018-10-21 00:17:36 +01:00
Kode
7b9d3f0ec6 replace text input with password input and swap input for Form::password 2018-10-20 20:08:36 +01:00
Kode
de116030bc change password field on nzbget blade 2018-10-20 13:55:56 +01:00
Chris
4fb59385b7 work on applications 2018-10-19 15:10:05 +01:00
Chris
40d6808067 Merge branch 'master' into 2.1.0 2018-10-19 08:47:58 +01:00
Chris
3572bd8068 update laravel requirements 2018-10-19 08:13:00 +01:00
Chris
380a0e8623 changes 2018-10-18 15:59:38 +01:00
Chris
4f6a0cb7c6 Move current apps out 2018-10-18 10:37:18 +01:00
Chris
56cce8233b Replace svgs 2018-10-18 09:53:13 +01:00
KodeStar
af8afcf931 Update readme.md 2018-10-18 08:41:13 +01:00
Kode
7f997d60cd Update version 2018-10-17 22:17:27 +01:00
Kode
d45f5a805e wrap asset() round root assets 2018-10-17 22:16:23 +01:00
Kode
fc9c624509 update donate link 2018-10-17 19:53:27 +01:00
KodeStar
33372509eb Merge pull request #266 from linuxserver/Multi-user
v2.0.0
2018-10-17 18:41:51 +01:00
Kode
bca0b02925 update version number to 2.0.0 2018-10-17 18:37:22 +01:00
Kode
24995135e6 update dependency 2018-10-17 18:35:09 +01:00
Chris
c1e4103edd Add support for pinned tabs in safari (potentially) 2018-10-17 11:31:42 +01:00
Chris
6eda423156 Added mailcow, webmin and virtualmin 2018-10-17 10:27:05 +01:00
Chris
7dc72d3519 style switch user icon same as user list page 2018-10-17 09:59:39 +01:00
Chris
907c22179b rearrange order 2018-10-17 09:49:23 +01:00
Chris
cdafbab7b1 possible fix for basic auth 2018-10-17 09:41:20 +01:00
Chris
e0064504e7 Merge branch 'master' into Multi-user 2018-10-17 08:39:10 +01:00
KodeStar
b22117dd01 Add missing override url 2018-10-17 08:36:06 +01:00
KodeStar
c88efa8dbc Fix broken config 2018-10-17 08:33:42 +01:00
Kode
7d060ff803 Merge branch 'master' into Multi-user 2018-10-16 19:05:04 +01:00
KodeStar
d5f8b6aae0 Merge pull request #257 from ABOTlegacy/Monica
Monica
2018-10-16 19:01:52 +01:00
KodeStar
2416993c5e Merge pull request #258 from ABOTlegacy/Bitwarden
Added Bitwarden to foundation apps.
2018-10-16 19:01:10 +01:00
KodeStar
494165a03a Merge pull request #259 from dgw/readme-polishing
readme: formatting, typo fixes, spit & polish
2018-10-16 19:00:20 +01:00
KodeStar
88e607533c Change to multiarch docker 2018-10-16 18:58:33 +01:00
KodeStar
035d2f9209 Merge pull request #265 from alxlaxv/master
Updating french translations
2018-10-16 18:53:05 +01:00
alxlaxv
53903daa87 Update app.php 2018-10-16 19:32:37 +02:00
alxlaxv
567994be1a Merge pull request #1 from alxlaxv/patch-1
Update app.php
2018-10-16 19:21:03 +02:00
alxlaxv
32fa27337a Update app.php
French translations improved
2018-10-16 17:27:53 +02:00
Chris
e1bb7646ac small fixes 2018-10-16 15:44:23 +01:00
KodeStar
108b636def Merge pull request #260 from keranoz/patch-1
Update app.php
2018-10-16 15:20:03 +01:00
keranoz
4abb14bf54 Update app.php
Improved some translations that seemed machine translated and added missing entries.
2018-10-16 14:08:19 +00:00
Chris
f8f96593c1 Update userseeder 2018-10-16 15:04:53 +01:00
Chris
cb21b0f8f1 add basic auth support and put username in switch user section 2018-10-16 14:57:21 +01:00
Chris
4c8c5fa27f Replace name for username in list view 2018-10-16 14:28:12 +01:00
Chris
6093119dde Change name to username 2018-10-16 14:14:14 +01:00
Chris
15755a3fd1 move all assets locally 2018-10-16 12:58:56 +01:00
Chris
0213c81e0d muted the switch user button until it's hovered over 2018-10-16 11:24:21 +01:00
Chris
c47f296f17 Change location of switch user 2018-10-16 10:59:35 +01:00
Chris
75133474f7 Added support and br translation 2018-10-16 09:33:24 +01:00
Kode
ddbe171f3a minor change to user avatar size to cut down whitespace a bit 2018-10-15 20:43:27 +01:00
Kode
12e109f82c add unique validation for name and don't fail validation when already the owner of data 2018-10-15 20:34:21 +01:00
Kode
e095589172 fix validation on email 2018-10-15 20:25:21 +01:00
Kode
d0293c785b Get settings working and autologin 2018-10-15 19:56:45 +01:00
Chris
aa351e31bf trying to get session settings to work 2018-10-15 16:00:20 +01:00
Chris
aceed3d13b fix settings edit not working 2018-10-15 14:35:14 +01:00
Chris
10b70d4a09 changes 2018-10-15 13:02:16 +01:00
Chris
cb9e014cf3 Autologin togles working 2018-10-15 09:52:36 +01:00
Chris
99017d834e Merge branch 'master' into Multi-user 2018-10-15 09:08:42 +01:00
Chris
fb73f5ca24 Attempt to allow changing root URL 2018-10-15 09:03:54 +01:00
dgw
4369f1aeda readme: formatting, typo fixes, spit & polish
Includes making inter-sentence spaces consistent, `pre-formatting`
values where it seemed appropriate, correcting typos, and other
miscellaneous niceties to help the readme make a good first impression.
2018-10-14 17:38:05 -05:00
Kode
6501aacb1b update to laravel 5.7 and try getting autologin saved 2018-10-14 20:50:32 +01:00
Kode
c3da17befc User specific items 2018-10-14 17:27:28 +01:00
Kode
46bb073001 Working on multi user 2018-10-14 16:17:55 +01:00
Kode
e8b47776ce Merge branch 'master' into Multi-user 2018-10-13 15:52:42 +01:00
Kode
6941fd3e2d Fixes for folder installs 2018-10-13 15:32:15 +01:00
Andy Bottom
f5937879df Added Bitwarden to foundation apps. 2018-10-12 14:59:19 -05:00
Andy Bottom
93877b7025 Fixed spaces vs tabs. 2018-10-12 14:35:59 -05:00
Andy Bottom
6612631cc3 Added Monica to foundation apps. 2018-10-12 14:32:34 -05:00
Chris
30c3079a90 added setting user many to many relationship 2018-10-12 15:10:40 +01:00
Chris
e86e681c53 Initial commit of multi user 2018-10-12 14:57:46 +01:00
Chris
48f72652da Merge branch 'master' of https://github.com/linuxserver/Heimdall 2018-10-12 11:33:43 +01:00
Chris
a3f7bafedb change css and js links to asset links 2018-10-12 11:33:39 +01:00
KodeStar
d30d610042 Update readme.md 2018-10-12 11:13:51 +01:00
Kode
882e406266 remove var dump 2018-09-30 15:47:44 +01:00
4383 changed files with 228068 additions and 66700 deletions

31
.env
View File

@@ -1,31 +0,0 @@
APP_NAME=Heimdall
APP_ENV=local
APP_KEY=base64:I206O8ibx+GQyRE7BeOxDobn04Mfmyyc5Ptzns/C0mY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost
DB_CONNECTION=sqlite
DB_DATABASE=app.sqlite
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_LIFETIME=120
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1

View File

@@ -1,16 +1,12 @@
APP_NAME=Laravel
APP_NAME=Heimdall
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
DB_CONNECTION=sqlite
DB_DATABASE=app.sqlite
BROADCAST_DRIVER=log
CACHE_DRIVER=file

5
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/app/SupportedApps
/node_modules
/public/hot
/public/storage
@@ -23,4 +24,6 @@ yarn-error.log
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.VolumeIcon.icns
storage/app/public/avatars/*
.env

63
app/Application.php Normal file
View File

@@ -0,0 +1,63 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Application extends Model
{
public $incrementing = false;
protected $primaryKey = 'appid';
//
public function icon()
{
if(!file_exists(storage_path('app/public/'.$this->icon))) {
$img_src = app_path('SupportedApps/'.$this->name.'/'.str_replace('icons/', '', $this->icon));
$img_dest = storage_path('app/public/'.$this->icon);
//die("i: ".$img_src);
@copy($img_src, $img_dest);
}
return $this->icon;
}
public function iconView()
{
return asset('storage/'.$this->icon);
}
public function defaultColour()
{
// check if light or dark
if($this->tile_background == 'light') return '#fafbfc';
return '#161b1f';
}
public function class()
{
$name = $this->name;
$name = preg_replace('/\PL/u', '', $name);
$class = '\App\SupportedApps\\'.$name.'\\'.$name;
return $class;
}
public static function applist()
{
$list = [];
$all = self::orderBy('name')->get()->sortBy('name', SORT_NATURAL|SORT_FLAG_CASE);
$list['null'] = 'None';
foreach($all as $app) {
$name = $app->name;
$name = preg_replace('/\PL/u', '', $name);
$list['\App\SupportedApps\\'.$name.'\\'.$name] = $app->name;
}
return $list;
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Application;
use App\SupportedApps;
class RegisterApp extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'register:app {folder}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Add a local app to the registry';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$folder = $this->argument('folder');
if($folder == 'all') {
$apps = scandir(app_path('SupportedApps'));
foreach($apps as $folder) {
if($folder == '.' || $folder == '..') continue;
$this->addApp($folder);
}
} else {
$this->addApp($folder);
}
}
public function addApp($folder)
{
$json = app_path('SupportedApps/'.$folder.'/app.json');
if(file_exists($json)) {
$app = json_decode(file_get_contents($json));
if(isset($app->appid)) {
$exists = Application::find($app->appid);
if($exists) {
$this->error('Application already registered - '.$exists->name." - ".$exists->appid);
} else {
// Doesn't exist so add it
SupportedApps::saveApp($app, new Application);
$this->info("Application Added - ".$app->name." - ".$app->appid);
}
} else {
$this->error('No App ID for - '.$folder);
}
} else {
$this->error('Could not find '.$json);
}
}
}

12
app/EnhancedApps.php Normal file
View File

@@ -0,0 +1,12 @@
<?php namespace App;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
interface EnhancedApps
{
public function test();
public function livestats();
public function url($endpoint);
}

View File

@@ -33,4 +33,24 @@ function title_color($hex)
} else {
return ' white';
}
}
}
function getLinkTargetAttribute()
{
$target = \App\Setting::fetch('window_target');
if($target === 'current') {
return '';
} else {
return ' target="' . $target . '"';
}
}
function className($name)
{
$name = preg_replace('/\PL/u', '', $name);
return $name;
}

View File

@@ -4,6 +4,11 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL;
class LoginController extends Controller
{
@@ -25,7 +30,7 @@ class LoginController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected $redirectTo = '/';
/**
* Create a new controller instance.
@@ -34,6 +39,88 @@ class LoginController extends Controller
*/
public function __construct()
{
Session::put('backUrl', URL::previous());
$this->middleware('guest')->except('logout');
}
public function username()
{
return 'username';
}
/**
* Handle a login request to the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function login(Request $request)
{
$current_user = User::currentUser();
$request->merge(['username' => $current_user->username, 'remember' => true]);
//die(print_r($request->all()));
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
public function index()
{
}
public function setUser(User $user)
{
Auth::logout();
session(['current_user' => $user]);
return redirect()->route('dash');
}
public function autologin($uuid)
{
$user = User::where('autologin', $uuid)->first();
Auth::login($user, true);
session(['current_user' => $user]);
return redirect()->route('dash');
}
/**
* Show the application's login form.
*
* @return \Illuminate\Http\Response
*/
public function showLoginForm()
{
return view('auth.login');
}
protected function authenticated(Request $request, $user)
{
return back();
}
public function redirectTo()
{
return Session::get('url.intended') ? Session::get('url.intended') : $this->redirectTo;
}
}

View File

@@ -27,7 +27,7 @@ class RegisterController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected $redirectTo = '/';
/**
* Create a new controller instance.

View File

@@ -25,7 +25,7 @@ class ResetPasswordController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected $redirectTo = '/';
/**
* Create a new controller instance.

View File

@@ -6,8 +6,27 @@ use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\Auth;
use App\User;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
protected $user;
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = $this->user();
//print_r($this->user);
return $next($request);
});
}
public function user()
{
return User::currentUser();
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return redirect()->route('dash');
}
}

View File

@@ -2,15 +2,25 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Artisan;
use App\Application;
use App\Item;
use App\Setting;
use App\SupportedApps\Nzbget;
use App\User;
use GrahamCampbell\GitHub\Facades\GitHub;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\SupportedApps;
use App\Jobs\ProcessApps;
use App\Search;
use Illuminate\Support\Facades\Route;
class ItemController extends Controller
{
public function __construct()
{
$this->middleware('allowed');
}
/**
* Display a listing of the resource on the dashboard.
*
@@ -18,8 +28,16 @@ class ItemController extends Controller
*/
public function dash()
{
$data['apps'] = Item::doesntHave('parents')->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::doesntHave('parents')->get();
$data['apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere('type', 1)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere('type', 1)->orderBy('order', 'asc')->get();
//$data['all_apps'] = Item::doesntHave('parents')->get();
//die(print_r($data['apps']));
return view('welcome', $data);
}
@@ -37,9 +55,8 @@ class ItemController extends Controller
$item->save();
}
}
/**
/**
* Pin item on the dashboard.
*
* @return \Illuminate\Http\Response
@@ -49,7 +66,7 @@ class ItemController extends Controller
$item = Item::findOrFail($id);
$item->pinned = true;
$item->save();
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route);
}
@@ -63,7 +80,7 @@ class ItemController extends Controller
$item = Item::findOrFail($id);
$item->pinned = false;
$item->save();
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route);
}
@@ -72,20 +89,25 @@ class ItemController extends Controller
*
* @return \Illuminate\Http\Response
*/
public function pinToggle($id, $ajax=false)
public function pinToggle($id, $ajax=false, $tag=false)
{
$item = Item::findOrFail($id);
$new = ((bool)$item->pinned === true) ? false : true;
$item->pinned = $new;
$item->save();
if($ajax) {
$data['apps'] = Item::pinned()->get();
if(is_numeric($tag) && $tag > 0) {
$item = Item::whereId($tag)->first();
$data['apps'] = $item->children()->pinned()->orderBy('order', 'asc')->get();
} else {
$data['apps'] = Item::pinned()->orderBy('order', 'asc')->get();
}
$data['ajax'] = true;
return view('sortable', $data);
} else {
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route);
}
}
}
@@ -117,7 +139,8 @@ class ItemController extends Controller
{
//
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
$data['current_tags'] = [];
$data['tags']->prepend(__('app.dashboard'), 0);
$data['current_tags'] = collect([0 => __('app.dashboard')]);
return view('items.create', $data);
}
@@ -133,7 +156,7 @@ class ItemController extends Controller
//
$validatedData = $request->validate([
'title' => 'required|max:255',
'url' => 'required|url',
'url' => 'required',
]);
if($request->hasFile('file')) {
@@ -144,17 +167,28 @@ class ItemController extends Controller
}
$config = Item::checkConfig($request->input('config'));
$current_user = User::currentUser();
$request->merge([
'description' => $config
'description' => $config,
'user_id' => $current_user->id
]);
if($request->input('class') === 'null') {
$request->merge([
'class' => null,
]);
}
//die(print_r($request->input('config')));
$item = Item::create($request->all());
//Search::storeSearchProvider($request->input('class'), $item);
$item->parents()->sync($request->tags);
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.item_created'));
}
@@ -181,8 +215,10 @@ class ItemController extends Controller
// Get the item
$data['item'] = Item::find($id);
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
$data['current_tags'] = $data['item']->parents;
$data['tags']->prepend(__('app.dashboard'), 0);
$data['current_tags'] = $data['item']->tags();
//$data['current_tags'] = $data['item']->parent;
//die(print_r($data['current_tags']));
// show the edit form and pass the nerd
return view('items.edit', $data);
}
@@ -198,7 +234,7 @@ class ItemController extends Controller
{
$validatedData = $request->validate([
'title' => 'required|max:255',
'url' => 'required|url',
'url' => 'required',
]);
//die(print_r($request->all()));
if($request->hasFile('file')) {
@@ -209,16 +245,27 @@ class ItemController extends Controller
}
$config = Item::checkConfig($request->input('config'));
$current_user = User::currentUser();
$request->merge([
'description' => $config
'description' => $config,
'user_id' => $current_user->id
]);
if($request->input('class') === 'null') {
$request->merge([
'class' => null,
]);
}
$item = Item::find($id);
$item->update($request->all());
//Search::storeSearchProvider($request->input('class'), $item);
$item->parents()->sync($request->tags);
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route)
->with('success',__('app.alert.success.item_updated'));
}
@@ -241,7 +288,7 @@ class ItemController extends Controller
Item::find($id)->delete();
}
$route = route('items.index', [], false);
$route = route('items.index', []);
return redirect($route)
->with('success',__('app.alert.success.item_deleted'));
}
@@ -259,21 +306,11 @@ class ItemController extends Controller
->where('id', $id)
->restore();
$route = route('items.inded', [], false);
$route = route('items.inded', []);
return redirect($route)
->with('success',__('app.alert.success.item_restored'));
}
public function isSupportedAppByKey($app)
{
$output = false;
$all_supported = Item::supportedList();
if(array_key_exists($app, $all_supported)) {
$output = new $all_supported[$app];
}
return $output;
}
/**
* Return details for supported apps
*
@@ -282,19 +319,25 @@ class ItemController extends Controller
public function appload(Request $request)
{
$output = [];
$app = $request->input('app');
$appname = $request->input('app');
//die($appname);
if(($app_details = $this->isSupportedAppByKey($app)) !== false) {
// basic details
$output['icon'] = $app_details->icon();
$output['colour'] = $app_details->defaultColour();
$app_details = Application::where('name', $appname)->firstOrFail();
$appclass = $app_details->class();
$app = new $appclass;
// live details
if($app_details instanceof \App\SupportedApps\Contracts\Livestats) {
$output['config'] = $app_details->configDetails();
} else {
$output['config'] = null;
}
// basic details
$output['icon'] = $app_details->icon();
$output['name'] = $app_details->name;
$output['iconview'] = $app_details->iconView();
$output['colour'] = $app_details->defaultColour();
$output['class'] = $appclass;
// live details
if($app instanceof \App\EnhancedApps) {
$output['config'] = className($app_details->name).'.config';
} else {
$output['config'] = null;
}
return json_encode($output);
@@ -309,25 +352,34 @@ class ItemController extends Controller
$app_details = new $app();
$app_details->config = (object)$data;
$app_details->testConfig();
$app_details->test();
}
public function getStats($id)
{
$item = Item::find($id);
$config = json_decode($item->description);
if(isset($config->type)) {
$config->url = $item->url;
if(isset($config->override_url) && !empty($config->override_url)) {
$config->url = $config->override_url;
}
$app_details = new $config->type;
$app_details->config = $config;
echo $app_details->executeConfig();
$config = $item->getconfig();
if(isset($item->class)) {
$application = new $item->class;
$application->config = $config;
echo $application->livestats();
}
}
public function checkAppList()
{
ProcessApps::dispatch();
$route = route('items.index');
return redirect($route)
->with('success', __('app.alert.success.updating'));
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Search;
class SearchController extends Controller
{
public function index(Request $request)
{
$requestprovider = $request->input('provider');
$query = $request->input('q');
$provider = Search::providerDetails($requestprovider);
if($provider->type == 'standard') {
return redirect($provider->url.'?'.$provider->var.'='.urlencode($query));
} elseif($provider->type == 'external') {
$class = new $provider->class;
//print_r($provider);
return $class->getResults($query, $provider);
}
//print_r($provider);
}
}

View File

@@ -5,10 +5,17 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Setting;
use App\SettingGroup;
use App\User;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class SettingsController extends Controller
{
public function __construct()
{
$this->middleware('allowed');
}
/**
* @return \Illuminate\View\View
*/
@@ -31,6 +38,7 @@ class SettingsController extends Controller
public function edit($id)
{
$setting = Setting::find($id);
//die("s: ".$setting->label);
if((bool)$setting->system === true) return abort(404);
@@ -39,7 +47,7 @@ class SettingsController extends Controller
'setting' => $setting,
]);
} else {
$route = route('settings.list', [], false);
$route = route('settings.list', []);
return redirect($route)
->with([
'error' => __('app.alert.error.not_exist'),
@@ -55,33 +63,35 @@ class SettingsController extends Controller
public function update(Request $request, $id)
{
$setting = Setting::find($id);
$user = $this->user();
if (!is_null($setting)) {
$data = Setting::getInput();
$setting_value = null;
if ($setting->type == 'image') {
if($request->hasFile('value')) {
$path = $request->file('value')->store('backgrounds');
$setting->value = $path;
$setting_value = $path;
}
} else {
$setting->value = $data->value;
$setting_value = $data->value;
}
$setting->save();
$route = route('settings.index', [], false);
$user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => $setting_value]);
$route = route('settings.index', []);
return redirect($route)
->with([
'success' => __('app.alert.success.setting_updated'),
]);
} else {
$route = route('settings.index', [], false);
$route = route('settings.index', []);
return redirect($route)
->with([
'error' => __('app.alert.error.not_exist'),
@@ -95,16 +105,23 @@ class SettingsController extends Controller
*/
public function clear($id)
{
$user = $this->user();
$setting = Setting::find($id);
if((bool)$setting->system !== true) {
$setting->value = '';
$setting->save();
$user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => '']);
}
$route = route('settings.index', [], false);
$route = route('settings.index', []);
return redirect($route)
->with([
'success' => __('app.alert.success.setting_updated'),
]);
}
public function search(Request $request)
{
}
}

View File

@@ -4,10 +4,15 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Item;
use App\User;
use DB;
class TagController extends Controller
{
public function __construct()
{
$this->middleware('allowed');
}
/**
* Display a listing of the resource.
*
@@ -17,8 +22,8 @@ class TagController extends Controller
{
$trash = (bool)$request->input('trash');
$data['apps'] = Item::ofType('tag')->orderBy('title', 'asc')->get();
$data['trash'] = Item::ofType('tag')->onlyTrashed()->get();
$data['apps'] = Item::ofType('tag')->where('id', '>', 0)->orderBy('title', 'asc')->get();
$data['trash'] = Item::ofType('tag')->where('id', '>', 0)->onlyTrashed()->get();
if($trash) {
return view('tags.trash', $data);
} else {
@@ -58,15 +63,18 @@ class TagController extends Controller
$slug = str_slug($request->title, '-');
$current_user = User::currentUser();
// set item type to tag
$request->merge([
'type' => '1',
'url' => $slug
'url' => $slug,
'user_id' => $current_user->id
]);
//die(print_r($request->all()));
Item::create($request->all());
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.tag_created'));
}
@@ -82,6 +90,7 @@ class TagController extends Controller
$item = Item::whereUrl($slug)->first();
//print_r($item);
$data['apps'] = $item->children()->pinned()->orderBy('order', 'asc')->get();
$data['tag'] = $item->id;
$data['all_apps'] = $item->children;
return view('welcome', $data);
}
@@ -130,7 +139,7 @@ class TagController extends Controller
Item::find($id)->update($request->all());
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route)
->with('success',__('app.alert.success.tag_updated'));
}
@@ -153,7 +162,7 @@ class TagController extends Controller
Item::find($id)->delete();
}
$route = route('tags.index', [], false);
$route = route('tags.index', []);
return redirect($route)
->with('success',__('app.alert.success.item_deleted'));
}
@@ -170,7 +179,7 @@ class TagController extends Controller
Item::withTrashed()
->where('id', $id)
->restore();
$route = route('tags.index', [], false);
$route = route('tags.index', []);
return redirect($route)
->with('success',__('app.alert.success.item_restored'));
}

View File

@@ -0,0 +1,175 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;
class UserController extends Controller
{
public function __construct()
{
$this->middleware('allowed')->except(['selectUser']);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$data['users'] = User::all();
return view('users.index', $data);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$data = [];
return view('users.create', $data);
}
public function selectUser()
{
Auth::logout();
$data['users'] = User::all();
return view('userselect', $data);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$validatedData = $request->validate([
'username' => 'required|max:255|unique:users',
'email' => 'required|email',
'password' => 'nullable|confirmed',
'password_confirmation' => 'nullable'
]);
$user = new User;
$user->username = $request->input('username');
$user->email = $request->input('email');
$user->public_front = $request->input('public_front');
$password = $request->input('password');
if(!empty($password)) {
$user->password = bcrypt($password);
}
if($request->hasFile('file')) {
$path = $request->file('file')->store('avatars');
$user->avatar = $path;
}
if ((bool)$request->input('autologin_allow') === true) {
$user->autologin = (string)Str::uuid();
}
$user->save();
$route = route('dash', []);
return redirect($route)
->with('success',__('app.alert.success.user_updated'));
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit(User $user)
{
$data['user'] = $user;
return view('users.edit', $data);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, User $user)
{
$validatedData = $request->validate([
'username' => 'required|max:255|unique:users,username,'.$user->id,
'email' => 'required|email',
'password' => 'nullable|confirmed',
'password_confirmation' => 'nullable'
]);
//die(print_r($request->all()));
$user->username = $request->input('username');
$user->email = $request->input('email');
$user->public_front = $request->input('public_front');
$password = $request->input('password');
if(!empty($password)) {
$user->password = bcrypt($password);
} elseif($password == 'null') {
$user->password = null;
}
if($request->hasFile('file')) {
$path = $request->file('file')->store('avatars');
$user->avatar = $path;
}
if ((bool)$request->input('autologin_allow') === true) {
$user->autologin = (is_null($user->autologin)) ? (string)Str::uuid() : $user->autologin;
} else {
$user->autologin = null;
}
$user->save();
$route = route('dash', []);
return redirect($route)
->with('success',__('app.alert.success.user_updated'));
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(User $user)
{
if($user->id !== 1) {
$user->delete();
$route = route('dash', []);
return redirect($route)
->with('success',__('app.alert.success.user_deleted'));
}
}
}

View File

@@ -51,6 +51,7 @@ class Kernel extends HttpKernel
* @var array
*/
protected $routeMiddleware = [
'allowed' => \App\Http\Middleware\CheckAllowed::class,
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use App\User;
use Illuminate\Support\Facades\Route;
use Session;
class CheckAllowed
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$route = Route::currentRouteName();
$current_user = User::currentUser();
if(str_is('users*', $route)) {
if($current_user->id !== 1) {
return redirect()->route('dash');
}
}
if($route == 'dash') {
//print_r(User::all());
//die("here".var_dump($current_user->password));
if((bool)$current_user->public_front === true) return $next($request);
}
if(empty($current_user->password)) return $next($request);
// Check if user is logged in as $current_user
if (Auth::check()) {
$loggedin_user = Auth::user();
if($loggedin_user->id === $current_user->id) return $next($request);
}
return Auth::authenticate();
}
}

View File

@@ -18,7 +18,7 @@ class RedirectIfAuthenticated
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/home');
return redirect()->intended();
}
return $next($request);

View File

@@ -12,18 +12,12 @@ class TrustProxies extends Middleware
*
* @var array
*/
protected $proxies;
protected $proxies = ['192.168.0.0/16', '172.16.0.0/12','10.0.0.0/8', '127.0.0.1'];
/**
* The current proxy header mappings.
*
* @var array
*/
protected $headers = [
Request::HEADER_FORWARDED => 'FORWARDED',
Request::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR',
Request::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST',
Request::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT',
Request::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO',
];
protected $headers = Request::HEADER_X_FORWARDED_ALL;
}

View File

@@ -5,15 +5,32 @@ namespace App;
use Illuminate\Database\Eloquent\Model;
use Symfony\Component\ClassLoader\ClassMapGenerator;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;
use App\User;
use App\ItemTag;
use App\Application;
class Item extends Model
{
use SoftDeletes;
protected static function boot()
{
parent::boot();
static::addGlobalScope('user_id', function (Builder $builder) {
$current_user = User::currentUser();
if($current_user) {
$builder->where('user_id', $current_user->id)->orWhere('user_id', 0);
} else {
$builder->where('user_id', 0);
}
});
}
//
protected $fillable = [
'title', 'url', 'colour', 'icon', 'description', 'pinned', 'order', 'type'
'title', 'url', 'colour', 'icon', 'description', 'pinned', 'order', 'type', 'class', 'user_id'
];
/**
@@ -23,77 +40,6 @@ class Item extends Model
*/
protected $dates = ['deleted_at'];
public static function supportedList()
{
return [
'AirSonic' => \App\SupportedApps\AirSonic::class,
'Cardigann' => \App\SupportedApps\Cardigann::class,
'CouchPotato' => \App\SupportedApps\CouchPotato::class,
'Bazarr' => \App\SupportedApps\Bazarr::class,
'Booksonic' => \App\SupportedApps\Booksonic::class,
'BookStack' => \App\SupportedApps\BookStack::class,
'Deluge' => \App\SupportedApps\Deluge::class,
'Dokuwiki' => \App\SupportedApps\Dokuwiki::class,
'Duplicati' => \App\SupportedApps\Duplicati::class,
'Emby' => \App\SupportedApps\Emby::class,
'Flood' => \App\SupportedApps\Flood::class,
'FreshRSS' => \App\SupportedApps\FreshRSS::class,
'Gitea' => \App\SupportedApps\Gitea::class,
'Glances' => \App\SupportedApps\Glances::class,
'Grafana' => \App\SupportedApps\Grafana::class,
'Graylog' => \App\SupportedApps\Graylog::class,
'Headphones' => \App\SupportedApps\Headphones::class,
'Home Assistant' => \App\SupportedApps\HomeAssistant::class,
'Jackett' => \App\SupportedApps\Jackett::class,
'Jdownloader' => \App\SupportedApps\Jdownloader::class,
'Krusader' => \App\SupportedApps\Krusader::class,
'LibreNMS' => \App\SupportedApps\LibreNMS::class,
'Lidarr' => \App\SupportedApps\Lidarr::class,
'Mcmyadmin' => \App\SupportedApps\Mcmyadmin::class,
'Medusa' => \App\SupportedApps\Medusa::class,
'MusicBrainz' => \App\SupportedApps\MusicBrainz::class,
'Mylar' => \App\SupportedApps\Mylar::class,
'NZBGet' => \App\SupportedApps\Nzbget::class,
'Netdata' => \App\SupportedApps\Netdata::class,
'Nextcloud' => \App\SupportedApps\Nextcloud::class,
'Now Showing' => \App\SupportedApps\NowShowing::class,
'Nzbhydra' => \App\SupportedApps\Nzbhydra::class,
'OPNSense' => \App\SupportedApps\Opnsense::class,
'Ombi' => \App\SupportedApps\Ombi::class,
'Openhab' => \App\SupportedApps\Openhab::class,
'OpenMediaVault' => \App\SupportedApps\OpenMediaVault::class,
'Pihole' => \App\SupportedApps\Pihole::class,
'Plex' => \App\SupportedApps\Plex::class,
'Plexpy' => \App\SupportedApps\Plexpy::class,
'Plexrequests' => \App\SupportedApps\Plexrequests::class,
'Portainer' => \App\SupportedApps\Portainer::class,
'Proxmox' => \App\SupportedApps\Proxmox::class,
'Radarr' => \App\SupportedApps\Radarr::class,
'Rancher' => \App\SupportedApps\Rancher::class,
'Runeaudio' => \App\SupportedApps\Runeaudio::class,
'Sabnzbd' => \App\SupportedApps\Sabnzbd::class,
'Sickrage' => \App\SupportedApps\Sickrage::class,
'Sonarr' => \App\SupportedApps\Sonarr::class,
'Syncthing' => \App\SupportedApps\Syncthing::class,
'Tautulli' => \App\SupportedApps\Tautulli::class,
'Transmission' => \App\SupportedApps\Transmission::class,
'Traefik' => \App\SupportedApps\Traefik::class,
'tt-rss' => \App\SupportedApps\Ttrss::class,
'TVheadend' => \App\SupportedApps\TVheadend::class,
'UniFi' => \App\SupportedApps\Unifi::class,
'unRAID' => \App\SupportedApps\Unraid::class,
'pfSense' => \App\SupportedApps\Pfsense::class,
'pyLoad' => \App\SupportedApps\pyLoad::class,
'ruTorrent' => \App\SupportedApps\ruTorrent::class,
'Watcher3' => \App\SupportedApps\Watcher3::class,
'WebTools' => \App\SupportedApps\WebTools::class,
];
}
public static function supportedOptions()
{
return array_keys(self::supportedList());
}
/**
* Scope a query to only include pinned items.
*
@@ -105,24 +51,6 @@ class Item extends Model
return $query->where('pinned', 1);
}
public function getConfigAttribute()
{
$output = null;
$view = null;
if(isset($this->description) && !empty($this->description)){
$output = json_decode($this->description);
$output = is_object($output) ? $output : new \stdClass();
if(isset($output->type) && !empty($output->type)) {
$class = $output->type;
$sap = new $class();
$view = $sap->configDetails();
$output->view = $view;
}
if(!isset($output->dataonly)) $output->dataonly = '0';
}
return (object)$output;
}
public static function checkConfig($config)
{
if(empty($config)) {
@@ -147,6 +75,25 @@ class Item extends Model
}
public function tags()
{
$id = $this->id;
$tags = ItemTag::select('tag_id')->where('item_id', $id)->pluck('tag_id')->toArray();
$tagdetails = Item::select('id', 'title', 'url', 'pinned')->whereIn('id', $tags)->get();
//print_r($tags);
if(in_array(0, $tags)) {
$details = new Item([
"id" => 0,
"title" => __('app.dashboard'),
"url" => '',
"pinned" => 0
]);
$tagdetails->prepend($details);
}
return $tagdetails;
}
public function parents()
{
return $this->belongsToMany('App\Item', 'item_tag', 'item_id', 'tag_id');
@@ -202,6 +149,14 @@ class Item extends Model
}
}
public static function nameFromClass($class)
{
$explode = explode('\\', $class);
$name = end($explode);
return $name;
}
public function scopeOfType($query, $type)
{
switch($type) {
@@ -216,5 +171,95 @@ class Item extends Model
return $query->where('type', $typeid);
}
public function enhanced()
{
if(isset($this->class) && !empty($this->class)) {
$app = new $this->class;
} else {
return false;
}
return (bool)($app instanceof \App\EnhancedApps);
}
public static function isEnhanced($class)
{
if($class === null || $class === 'null') return false;
$app = new $class;
return (bool)($app instanceof \App\EnhancedApps);
}
public static function isSearchProvider($class)
{
$app = new $class;
return ((bool)($app instanceof \App\SearchInterface)) ? $app : false;
}
public function enabled()
{
if($this->enhanced()) {
$config = $this->getconfig();
if($config) {
return (bool) $config->enabled;
}
}
return false;
}
public function getconfig()
{
$explode = explode('\\', $this->class);
if(!isset($this->description) || empty($this->description)) {
$config = new \stdClass;
$config->name = end($explode);
$config->enabled = false;
return $config;
}
$config = json_decode($this->description);
$config->name = end($explode);
$config->url = $this->url;
if(isset($config->override_url) && !empty($config->override_url)) {
$config->url = $config->override_url;
}
return $config;
}
public static function applicationDetails($class)
{
if(!empty($class)) {
$name = self::nameFromClass($class);
$application = Application::where('name', $name)->first();
if($application) return $application;
}
return false;
}
public static function getApplicationDescription($class)
{
$details = self::applicationDetails($class);
if($details !== false) {
return $details->description.' - '.$details->license;
}
return '';
}
/**
* Get the user that owns the item.
*/
public function user()
{
return $this->belongsTo('App\User');
}
}

10
app/ItemTag.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ItemTag extends Pivot
{
}

66
app/Jobs/ProcessApps.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Application;
use App\SupportedApps;
class ProcessApps implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$localapps = Application::all();
$list = json_decode(SupportedApps::getList()->getBody());
$validapps = [];
foreach($list->apps as $app) {
$validapps[] = $app->appid;
$localapp = $localapps->where('appid', $app->appid)->first();
$application = ($localapp) ? $localapp : new Application;
if(!file_exists(app_path('SupportedApps/'.className($app->name)))) {
SupportedApps::getFiles($app);
SupportedApps::saveApp($app, $application);
} else {
// check if there has been an update for this app
$localapp = $localapps->where('appid', $app->appid)->first();
if($localapp) {
if($localapp->sha !== $app->sha) {
SupportedApps::getFiles($app);
SupportedApps::saveApp($app, $application);
}
} else {
SupportedApps::getFiles($app);
SupportedApps::saveApp($app, $application);
}
}
}
//$delete = Application::whereNotIn('appid', $validapps)->delete(); // delete any apps not in list
// removed the delete so local apps can be added
}
}

View File

@@ -6,6 +6,9 @@ use Illuminate\Support\ServiceProvider;
use Artisan;
use Schema;
use App\Setting;
use App\User;
use App\Application;
use App\Jobs\ProcessApps;
class AppServiceProvider extends ServiceProvider
{
@@ -16,12 +19,11 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
$alt_bg = '';
if(!is_file(base_path('.env'))) {
touch(base_path('.env'));
Artisan::call('key:generate');
copy(base_path('.env.example'), base_path('.env'));
}
$this->genKey();
if(!is_file(database_path('app.sqlite'))) {
// first time setup
touch(database_path('app.sqlite'));
@@ -32,36 +34,109 @@ class AppServiceProvider extends ServiceProvider
}
if(is_file(database_path('app.sqlite'))) {
if(Schema::hasTable('settings')) {
if($bg_image = Setting::fetch('background_image')) {
$alt_bg = ' style="background-image: url(/storage/'.$bg_image.')"';
}
// check version to see if an upgrade is needed
$db_version = Setting::fetch('version');
$db_version = Setting::_fetch('version');
$app_version = config('app.version');
if(version_compare($app_version, $db_version) == 1) { // app is higher than db, so need to run migrations etc
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
}
} else {
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
}
}
if(!is_file(public_path('storage/.gitignore'))) {
Artisan::call('storage:link');
\Session::put('current_user', null);
}
$applications = Application::all();
if($applications->count() <= 0) {
if (class_exists('ZipArchive')) {
ProcessApps::dispatch();
} else {
die("You are missing php-zip");
}
}
// User specific settings need to go here as session isn't available at this point in the app
view()->composer('*', function ($view)
{
if(isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
}
if(!\Auth::check()) {
if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
$credentials = ['username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']];
if (\Auth::attempt($credentials, true)) {
// Authentication passed...
$user = \Auth::user();
//\Session::put('current_user', $user);
session(['current_user' => $user]);
}
}
elseif(isset($_SERVER['REMOTE_USER']) && !empty($_SERVER['REMOTE_USER'])) {
$user = User::where('username', $_SERVER['REMOTE_USER'])->first();
\Auth::login($user, true);
session(['current_user' => $user]);
}
}
$alt_bg = '';
if($bg_image = Setting::fetch('background_image')) {
$alt_bg = ' style="background-image: url(/storage/'.$bg_image.')"';
}
$lang = Setting::fetch('language');
\App::setLocale($lang);
}
if(!is_file(public_path('storage'))) {
Artisan::call('storage:link');
}
view()->share('alt_bg', $alt_bg);
$allusers = User::all();
$current_user = User::currentUser();
$view->with('alt_bg', $alt_bg );
$view->with('allusers', $allusers );
$view->with('current_user', $current_user );
});
$this->app['view']->addNamespace('SupportedApps', app_path('SupportedApps'));
var_dump(env('FORCE_HTTPS'));
if (env('FORCE_HTTPS') === true) {
\URL::forceScheme('https');
}
if(env('APP_URL') != 'http://localhost') {
\URL::forceRootUrl(env('APP_URL'));
}
}
/**
* Generate app key if missing and .env exists
*
* @return void
*/
public function genKey()
{
if(is_file(base_path('.env'))) {
if(empty(env('APP_KEY'))) {
Artisan::call('key:generate', array('--force' => true, '--no-interaction' => true));
}
}
}
/**
* Register any application services.
*

147
app/Search.php Normal file
View File

@@ -0,0 +1,147 @@
<?php namespace App;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use App\Item;
use App\Setting;
use Form;
use Cache;
abstract class Search
{
/**
* List of all search providers
*
* @return Array
*/
public static function providers()
{
$providers = self::standardProviders();
$providers = $providers + self::appProviders();
return $providers;
}
/**
* Gets details for a single provider
*
* @return Object
*/
public static function providerDetails($provider)
{
$providers = self::providers();
if(!isset($providers[$provider])) return false;
return (object)$providers[$provider] ?? false;
}
/**
* Array of the standard providers
*
* @return Array
*/
public static function standardProviders()
{
return [
'google' => [
'url' => 'https://www.google.com/search',
'var' => 'q',
'method' => 'get',
'type' => 'standard',
],
'ddg' => [
'url' => 'https://duckduckgo.com/',
'var' => 'q',
'method' => 'get',
'type' => 'standard',
],
'bing' => [
'url' => 'https://www.bing.com/search',
'var' => 'q',
'method' => 'get',
'type' => 'standard',
],
'qwant' => [
'url' => 'https://www.qwant.com/',
'var' => 'q',
'method' => 'get',
'type' => 'standard',
],
'startpage' => [
'url' => 'https://www.startpage.com/do/dsearch',
'var' => 'query',
'method' => 'get',
'type' => 'standard',
],
];
}
/**
* Loops through users apps to see if app is a search provider, might be worth
* looking into caching this at some point
*
* @return Array
*/
public static function appProviders()
{
$providers = [];
$userapps = Item::all();
foreach($userapps as $app) {
if(empty($app->class)) continue;
if(($provider = Item::isSearchProvider($app->class)) !== false) {
$name = Item::nameFromClass($app->class);
$providers[strtolower($name)] = [
'type' => $provider->type,
'class' => $app->class,
'url' => $app->url,
];
}
}
return $providers;
}
/**
* Outputs the search form
*
* @return html
*/
public static function form()
{
$output = '';
$homepage_search = Setting::fetch('homepage_search');
$search_provider = Setting::where('key', '=', 'search_provider')->first();
$user_search_provider = Setting::fetch('search_provider');
//die(print_r($search_provider));
//die(var_dump($user_search_provider));
// return early if search isn't applicable
if((bool)$homepage_search !== true) return $output;
$user_search_provider = $user_search_provider ?? 'none';
if((bool)$homepage_search && (bool)$search_provider) {
if((bool)$user_search_provider) {
$name = 'app.options.'.$user_search_provider;
$provider = self::providerDetails($user_search_provider);
$output .= '<div class="searchform">';
$output .= '<form action="'.url('search').'"'.getLinkTargetAttribute().' method="get">';
$output .= '<div id="search-container" class="input-container">';
$output .= '<select name="provider">';
foreach(self::providers() as $key => $searchprovider) {
$selected = ($key === $user_search_provider) ? ' selected="selected"' : '';
$output .= '<option value="'.$key.'"'.$selected.'>'.__('app.options.'.$key).'</option>';
}
$output .= '</select>';
$output .= Form::text('q', null, ['class' => 'homesearch', 'autofocus' => 'autofocus', 'placeholder' => __('app.settings.search').'...']);
$output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>';
$output .= '</div>';
$output .= '</form>';
$output .= '</div>';
}
}
return $output;
}
}

10
app/SearchInterface.php Normal file
View File

@@ -0,0 +1,10 @@
<?php namespace App;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
interface SearchInterface
{
public function getResults($query, $providerdetails);
}

View File

@@ -5,6 +5,9 @@ namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Input;
use Form;
use Illuminate\Support\Facades\Auth;
use App\User;
use App\Search;
class Setting extends Model
{
@@ -46,6 +49,12 @@ class Setting extends Model
public function getListValueAttribute()
{
if((bool)$this->system === true) {
$value = self::_fetch($this->key);
} else {
$value = self::fetch($this->key);
}
$this->value = $value;
switch($this->type) {
case 'image':
if(!empty($this->value)) {
@@ -80,6 +89,12 @@ class Setting extends Model
public function getEditValueAttribute()
{
if((bool)$this->system === true) {
$value = self::_fetch($this->key);
} else {
$value = self::fetch($this->key);
}
$this->value = $value;
switch($this->type) {
case 'image':
$value = '';
@@ -125,6 +140,7 @@ class Setting extends Model
return $this->belongsTo('App\SettingGroup', 'group_id');
}
/**
* @param string $key
*
@@ -132,20 +148,54 @@ class Setting extends Model
*/
public static function fetch($key)
{
if (Setting::cached($key)) {
return Setting::$cache[$key];
} else {
$user = self::user();
return self::_fetch($key, $user);
}
/**
* @param string $key
*
* @return mixed
*/
public static function _fetch($key, $user=null)
{
#$cachekey = ($user === null) ? $key : $key.'-'.$user->id;
#if (Setting::cached($cachekey)) {
# return Setting::$cache[$cachekey];
#} else {
$find = self::where('key', '=', $key)->first();
if (!is_null($find)) {
$value = $find->value;
Setting::add($key, $value);
if((bool)$find->system === true) { // if system variable use global value
$value = $find->value;
} else { // not system variable so use user specific value
// check if user specified value has been set
//print_r($user);
$usersetting = $user->settings()->where('id', $find->id)->first();
//print_r($user->settings);
//die(var_dump($usersetting));
//->pivot->value;
//echo "user: ".$user->id." --- ".$usersettings;
if(isset($usersetting) && !empty($usersetting)) {
$value = $usersetting->pivot->uservalue;
} else { // if not get default from base setting
//$user->settings()->save($find, ['value' => $find->value]);
#$has_setting = $user->settings()->where('id', $find->id)->exists();
#if($has_setting) {
# $user->settings()->updateExistingPivot($find->id, ['uservalue' => (string)$find->value]);
#} else {
# $user->settings()->save($find, ['uservalue' => (string)$find->value]);
#}
$value = $find->value;
}
}
#Setting::add($cachekey, $value);
return $value;
} else {
return false;
}
}
#}
}
/**
@@ -167,55 +217,19 @@ class Setting extends Model
return array_key_exists($key, Setting::$cache);
}
/**
* @return html
* The users that belong to the setting.
*/
public static function search()
public function users()
{
$output = '';
$homepage_search = self::fetch('homepage_search');
$search_provider = self::where('key', '=', 'search_provider')->first();
//die(var_dump($search_provider->value));
// return early if search isn't applicable
if((bool)$homepage_search !== true) return $output;
if($search_provider->value === 'none') return $output;
if(empty($search_provider->value)) return $output;
if(is_null($search_provider->value)) return $output;
if((bool)$homepage_search && (bool)$search_provider) {
$options = (array)json_decode($search_provider->options);
$name = $options[$search_provider->value];
if((bool)$search_provider->value) {
switch($search_provider->value) {
case 'google':
$url = 'https://www.google.com/search';
$var = 'q';
break;
case 'ddg':
$url = 'https://duckduckgo.com/';
$var = 'q';
break;
case 'bing':
$url = 'https://www.bing.com/search';
$var = 'q';
break;
case 'startpage':
$url = 'https://www.startpage.com/';
$var = 'q';
}
$output .= '<div class="searchform">';
$output .= Form::open(['url' => $url, 'method' => 'get']);
$output .= '<div class="input-container">';
$output .= Form::text($var, null, ['class' => 'homesearch', 'autofocus' => 'autofocus', 'placeholder' => __($name).' '.__('app.settings.search').'...']);
$output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>';
$output .= '</div>';
$output .= Form::close();
$output .= '</div>';
}
}
return $output;
return $this->belongsToMany('App\User')->using('App\SettingUser')->withPivot('uservalue');
}
public static function user()
{
return User::currentUser();
}
}

10
app/SettingUser.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class SettingUser extends Pivot
{
//
}

172
app/SupportedApps.php Normal file
View File

@@ -0,0 +1,172 @@
<?php namespace App;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
abstract class SupportedApps
{
protected $jar = false;
protected $method = 'GET';
protected $error;
public function appTest($url, $attrs = [], $overridevars=false)
{
if(empty($this->config->url)) {
return (object)[
'code' => 404,
'status' => 'No URL has been specified',
'response' => 'No URL has been specified',
];
}
$res = $this->execute($url, $attrs);
if($res == null) {
return (object)[
'code' => null,
'status' => $this->error,
'response' => 'Connection failed',
];
}
switch($res->getStatusCode()) {
case 200:
$status = 'Successfully communicated with the API';
break;
case 401:
$status = 'Failed: Invalid credentials';
break;
case 404:
$status = 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
$status = 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
return (object)[
'code' => $res->getStatusCode(),
'status' => $status,
'response' => $res->getBody(),
];
}
public function execute($url, $attrs = [], $overridevars=false, $overridemethod=false)
{
$res = null;
$vars = ($overridevars !== false) ?
$overridevars : [
'http_errors' => false,
'timeout' => 15,
'connect_timeout' => 15,
];
$client = new Client($vars);
$method = ($overridemethod !== false) ? $overridemethod : $this->method;
try {
return $client->request($method, $url, $attrs);
} catch (\GuzzleHttp\Exception\ConnectException $e) {
Log::error("Connection refused");
Log::debug($e->getMessage());
$this->error = "Connection refused - ".(string) $e->getMessage();
} catch (\GuzzleHttp\Exception\ServerException $e) {
Log::debug($e->getMessage());
$this->error = (string) $e->getResponse()->getBody();
}
$this->error = 'General error connecting with API';
return $res;
}
public function login()
{
}
public function normaliseurl($url, $addslash=true)
{
$url = rtrim($url, '/');
if($addslash) $url .= '/';
return $url;
}
public function getLiveStats($status, $data)
{
$className = get_class($this);
$explode = explode('\\', $className);
$name = end($explode);
$html = view('SupportedApps::'.$name.'.livestats', $data)->with('data', $data)->render();
return json_encode(['status' => $status, 'html' => $html]);
//return
}
public static function getList()
{
$list_url = 'https://apps.heimdall.site/list';
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
return $client->request('GET', $list_url);
}
public static function configValue($item=null, $key=null)
{
if(isset($item) && !empty($item)) {
return $item->getconfig()->$key;
} else return null;
}
public static function getFiles($app)
{
$zipurl = $app->files;
$client = new Client(['http_errors' => false, 'timeout' => 60, 'connect_timeout' => 15]);
$res = $client->request('GET', $zipurl);
if(!file_exists(app_path('SupportedApps'))) {
mkdir(app_path('SupportedApps'), 0777, true);
}
$src = app_path('SupportedApps/'.className($app->name).'.zip');
file_put_contents($src, $res->getBody());
$zip = new \ZipArchive();
$x = $zip->open($src); // open the zip file to extract
if ($x === true) {
$zip->extractTo(app_path('SupportedApps')); // place in the directory with same name
$zip->close();
unlink($src); //Deleting the Zipped file
}
}
public static function saveApp($details, $app)
{
if(!file_exists(storage_path('app/public/icons'))) {
mkdir(storage_path('app/public/icons'), 0777, true);
}
$img_src = app_path('SupportedApps/'.className($details->name).'/'.$details->icon);
$img_dest = storage_path('app/public/icons/'.$details->icon);
//die("i: ".$img_src);
@copy($img_src, $img_dest);
$app->appid = $details->appid;
$app->name = $details->name;
$app->sha = $details->sha ?? null;
$app->icon = 'icons/'.$details->icon;
$app->website = $details->website;
$app->license = $details->license;
$app->description = $details->description;
$appclass = $app->class();
$application = new $appclass;
$enhanced = (bool)($application instanceof \App\EnhancedApps);
$app->enhanced = $enhanced;
$app->tile_background = $details->tile_background;
$app->save();
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class AirSonic implements Contracts\Applications {
public function defaultColour()
{
return '#08F';
}
public function icon()
{
return 'supportedapps/airsonic.png';
}
}

View File

@@ -1,14 +0,0 @@
<?php namespace App\SupportedApps;
class Bazarr implements Contracts\Applications {
public function defaultColour()
{
return '#222';
}
public function icon()
{
return 'supportedapps/bazarr.png';
}
}

View File

@@ -1,14 +0,0 @@
<?php namespace App\SupportedApps;
class BookStack implements Contracts\Applications {
public function defaultColour()
{
return '#02679E';
}
public function icon()
{
return 'supportedapps/bookstack.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Booksonic implements Contracts\Applications {
public function defaultColour()
{
return '#58a';
}
public function icon()
{
return 'supportedapps/booksonic.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class Cardigann implements Contracts\Applications {
public function defaultColour()
{
return '#753';
}
public function icon()
{
return 'supportedapps/cardigann.png';
}
}

View File

@@ -1,9 +0,0 @@
<?php namespace App\SupportedApps\Contracts;
interface Applications {
public function defaultColour();
public function icon();
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps\Contracts;
interface Livestats {
public function configDetails();
public function testConfig();
public function executeConfig();
}

View File

@@ -1,122 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
class CouchPotato implements Contracts\Applications, Contracts\Livestats
{
private $_client;
public function __construct()
{
$this->_client = new Client(
['http_errors' => false,
'timeout' => 10]
);
}
public function defaultColour()
{
return '#363840';
}
public function icon()
{
return 'supportedapps/couchpotato.png';
}
public function configDetails()
{
return 'couchpotato';
}
public function testConfig()
{
$res = $this->sendRequest();
if ($res == null) {
echo 'CouchPotato connection failed';
return;
}
switch($res->getStatusCode()) {
case 200:
echo "Successfully connected to CouchPotato";
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and includes the port';
break;
case 409:
echo 'Failed: Incorrect session id';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$res = $this->sendRequest();
if ($res == null) {
Log::debug('CouchPotato connection failed');
return '';
}
$data = json_decode($res->getBody());
if (! isset($data->movies)) {
Log::debug('Failed to fetch data from CouchPotato');
return '';
}
$movies = $data->movies;
$wantedMovies = $availableMovies = 0;
foreach ($movies as $v) {
switch ($v->status) {
case 'active':
$wantedMovies++;
break;
case 'done':
$availableMovies++;
break;
default:
Log::warning('Unexpected CouchPotato status received: '.$v['status']);
break;
}
}
$html = '
<ul class="livestats">
<li><span class="title">Wanted</span><sub>'.$wantedMovies.'</sub></li>
<li><span class="title">Available</span><sub>'.$availableMovies.'</sub></li>
</ul>
';
return json_encode(['status' => 'inactive', 'html' => $html]);
}
private function sendRequest()
{
$res = null;
try{
$res = $this->_client->request(
'GET',
$this->getApiUrl()
);
}catch(\GuzzleHttp\Exception\BadResponseException $e){
Log::error("Connection to {$e->getRequest()->getUrl()} failed");
Log::debug($e->getMessage());
$res = $e->getRequest();
}catch(\GuzzleHttp\Exception\ConnectException $e) {
Log::error("CouchPotato connection refused");
Log::debug($e->getMessage());
}
return $res;
}
private function getApiUrl()
{
$url = $this->config->url;
$url = rtrim($url, '/');
$apiUrl = $url.'/api/'.$this->config->apikey.'/movie.list';
return $apiUrl;
}
}

View File

@@ -1,109 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Deluge implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#357';
}
public function icon()
{
return 'supportedapps/deluge.png';
}
public function configDetails()
{
return 'deluge';
}
public function testConfig()
{
$res = $this->login()[0];
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(!isset($data->result) || is_null($data->result) || $data->result == false) {
echo 'Failed: Invalid Credentials';
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$jar = $this->login()[1];
$res = $this->getDetails($jar);
$data = json_decode($res->getBody());
$download_rate = $data->result->stats->download_rate;
$upload_rate = $data->result->stats->upload_rate;
$seed_count = $data->result->filters->state[2];
$leech_count = $data->result->filters->state[1];
$html = '
<ul class="livestats">
<li><span class="title"><i class="fas fa-arrow-down"></i> '.$this->formatBytes($download_rate).'</span></li>
<li><span class="title"><i class="fas fa-arrow-up"></i> '.$this->formatBytes($upload_rate).'</span></li>
</ul>
<ul class="livestats">
<li><span class="title">Leech: '.$leech_count[1].'</span></li>
<li><span class="title">Seed: '.$seed_count[1].'</span></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);
}
public function getDetails($jar)
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/json';
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('POST', $api_url, [
'body' => '{"method": "web.update_ui", "params": [["none"], {}], "id": 1}',
'cookies' => $jar,
'headers' => ['content-type' => 'application/json', 'Accept' => 'application/json']
]);
return $res;
}
public function login()
{
$config = $this->config;
$url = $config->url;
$password = $config->password;
$url = rtrim($url, '/');
$api_url = $url.'/json';
$jar = new \GuzzleHttp\Cookie\CookieJar();
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('POST', $api_url, [
'body' => '{"method": "auth.login", "params": ["'.$password.'"], "id": 1}',
'cookies' => $jar,
'headers' => ['content-type' => 'application/json', 'Accept' => 'application/json']
]);
return array($res,$jar);
}
function formatBytes($bytes, $precision = 2) {
$units = array('B', 'KB', 'MB', 'GB', 'TB');
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
// Uncomment one of the following alternatives
$bytes /= pow(1024, $pow);
// $bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow] . 'ps';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Dokuwiki implements Contracts\Applications {
public function defaultColour()
{
return '#9d7056';
}
public function icon()
{
return 'supportedapps/dokuwiki.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Duplicati implements Contracts\Applications {
public function defaultColour()
{
return '#2c3744';
}
public function icon()
{
return 'supportedapps/duplicati.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Emby implements Contracts\Applications {
public function defaultColour()
{
return '#222';
}
public function icon()
{
return 'supportedapps/emby.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Flood implements Contracts\Applications {
public function defaultColour()
{
return '##00D091';
}
public function icon()
{
return 'supportedapps/Flood.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class FreshRSS implements Contracts\Applications {
public function defaultColour()
{
return '#003B73';
}
public function icon()
{
return 'supportedapps/freshrss.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Gitea implements Contracts\Applications {
public function defaultColour()
{
return '#585e52';
}
public function icon()
{
return 'supportedapps/gitea.png';
}
}

View File

@@ -1,14 +0,0 @@
<?php namespace App\SupportedApps;
class Glances implements Contracts\Applications {
public function defaultColour()
{
return '#2c363f';
}
public function icon()
{
return 'supportedapps/glances.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Grafana implements Contracts\Applications {
public function defaultColour()
{
return '#a56e4d';
}
public function icon()
{
return 'supportedapps/grafana.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Graylog implements Contracts\Applications {
public function defaultColour()
{
return '#158';
}
public function icon()
{
return 'supportedapps/graylog.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Headphones implements Contracts\Applications {
public function defaultColour()
{
return '#185';
}
public function icon()
{
return 'supportedapps/headphones.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class HomeAssistant implements Contracts\Applications {
public function defaultColour()
{
return '#073c52';
}
public function icon()
{
return 'supportedapps/homeassistant.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Jackett implements Contracts\Applications {
public function defaultColour()
{
return '#484814';
}
public function icon()
{
return 'supportedapps/jackett.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Jdownloader implements Contracts\Applications {
public function defaultColour()
{
return '#2b494f';
}
public function icon()
{
return 'supportedapps/jdownloader.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class Krusader implements Contracts\Applications {
public function defaultColour()
{
return '#5A5';
}
public function icon()
{
return 'supportedapps/krusader.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Lidarr implements Contracts\Applications {
public function defaultColour()
{
return '#183c18';
}
public function icon()
{
return 'supportedapps/lidarr.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Mcmyadmin implements Contracts\Applications {
public function defaultColour()
{
return '#30404b';
}
public function icon()
{
return 'supportedapps/mcmyadmin.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Medusa implements Contracts\Applications {
public function defaultColour()
{
return '#4b5e55';
}
public function icon()
{
return 'supportedapps/medusa.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class MusicBrainz implements Contracts\Applications {
public function defaultColour()
{
return '#a0a';
}
public function icon()
{
return 'supportedapps/musicbrainz.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Mylar implements Contracts\Applications {
public function defaultColour()
{
return '#aa0';
}
public function icon()
{
return 'supportedapps/mylar.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Netdata implements Contracts\Applications {
public function defaultColour()
{
return '#543737';
}
public function icon()
{
return 'supportedapps/netdata.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Nextcloud implements Contracts\Applications {
public function defaultColour()
{
return '#0e2c3e';
}
public function icon()
{
return 'supportedapps/nextcloud.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class NowShowing implements Contracts\Applications {
public function defaultColour()
{
return '#690000';
}
public function icon()
{
return 'supportedapps/nowshowing.png';
}
}

View File

@@ -1,84 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Nzbget implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#253827';
}
public function icon()
{
return 'supportedapps/nzbget.png';
}
public function configDetails()
{
return 'nzbget';
}
public function testConfig()
{
$res = $this->buildRequest('status');
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'inactive';
$res = $this->buildRequest('status');
$data = json_decode($res->getBody());
//$data->result->RemainingSizeMB = '10000000';
//$data->result->DownloadRate = '100000000';
if($data) {
$size = $data->result->RemainingSizeMB;
$rate = $data->result->DownloadRate;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate, false, ' <span>');
$active = ($size > 0 || $rate > 0) ? 'active' : 'inactive';
$html = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
';
}
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$username = $config->username;
$password = $config->password;
$rebuild_url = str_replace('http://', 'http://'.$username.':'.$password.'@', $url);
$rebuild_url = str_replace('https://', 'https://'.$username.':'.$password.'@', $rebuild_url);
$rebuild_url = rtrim($rebuild_url, '/');
$api_url = $rebuild_url.'/jsonrpc/'.$endpoint;
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Nzbhydra implements Contracts\Applications {
public function defaultColour()
{
return '#53644d';
}
public function icon()
{
return 'supportedapps/nzbhydra.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Ombi implements Contracts\Applications {
public function defaultColour()
{
return '#150f09';
}
public function icon()
{
return 'supportedapps/ombi.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class OpenMediaVault implements Contracts\Applications {
public function defaultColour()
{
return '#5AF';
}
public function icon()
{
return 'supportedapps/openmediavault.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Openhab implements Contracts\Applications {
public function defaultColour()
{
return '#2b2525';
}
public function icon()
{
return 'supportedapps/openhab.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Opnsense implements Contracts\Applications {
public function defaultColour()
{
return '#211914';
}
public function icon()
{
return 'supportedapps/opnsense.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Pfsense implements Contracts\Applications {
public function defaultColour()
{
return '#243699';
}
public function icon()
{
return 'supportedapps/pfsense.png';
}
}

View File

@@ -1,73 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Pihole implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#352222';
}
public function icon()
{
return 'supportedapps/pihole.png';
}
public function configDetails()
{
return 'pihole';
}
public function testConfig()
{
$res = $this->buildRequest();
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->buildRequest();
$data = json_decode($res->getBody());
$html = '
<ul class="livestats">
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest()
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/api.php';
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Plex implements Contracts\Applications {
public function defaultColour()
{
return '#222';
}
public function icon()
{
return 'supportedapps/plex.png';
}
}

View File

@@ -1,77 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Plexpy implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#2d2208';
}
public function icon()
{
return 'supportedapps/plexpy.png';
}
public function configDetails()
{
return 'plexpy';
}
public function testConfig()
{
$res = $this->buildRequest('arnold');
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(isset($data->error) && !empty($data->error)) {
echo 'Failed: '.$data->error;
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->buildRequest('get_activity');
$data = json_decode($res->getBody());
$stream_count = $data->response->data->stream_count;
$html = '
<ul class="livestats">
<li><span class="title">Stream Count</span><strong>'.$stream_count.'</strong></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$apikey = $config->apikey;
$url = rtrim($url, '/');
$api_url = $url.'/api/v2?apikey='.$apikey.'&cmd='.$endpoint;
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Plexrequests implements Contracts\Applications {
public function defaultColour()
{
return '#3c2d1c';
}
public function icon()
{
return 'supportedapps/plexrequests.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Portainer implements Contracts\Applications {
public function defaultColour()
{
return '#283f44';
}
public function icon()
{
return 'supportedapps/portainer.png';
}
}

View File

@@ -1,80 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Proxmox implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#542e0a';
}
public function icon()
{
return 'supportedapps/proxmox.png';
}
public function configDetails()
{
//return 'proxmox';
return null;
}
public function testConfig()
{
/*$res = $this->buildRequest();
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}*/
return null;
}
public function executeConfig()
{
/*
$output = '';
$res = $this->buildRequest();
$data = json_decode($res->getBody());
$output = '
<ul class="livestats">
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
</ul>
';
return $output;
*/
return null;
}
public function buildRequest($endpoint='')
{
$config = $this->config;
$username = $config->username;
$password = $config->password;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/api2/json/'.$endpoint.'?username='.$username.'&password='.$password;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'verify' => false ]);
$res = $client->request('GET', $api_url);
return $res;
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Radarr implements Contracts\Applications {
public function defaultColour()
{
return '#3e3726';
}
public function icon()
{
return 'supportedapps/radarr.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Rancher implements Contracts\Applications {
public function defaultColour()
{
return '#78c9cf';
}
public function icon()
{
return 'supportedapps/rancher.png';
}
}

View File

@@ -1,95 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Runeaudio implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#05A';
}
public function icon()
{
return 'supportedapps/runeaudio.png';
}
public function configDetails()
{
return 'runeaudio';
}
public function testConfig()
{
$res = $this->buildRequest('status');
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$output = '';
$active = 'active';
$artist = '';
$song_title = '';
$res = $this->buildRequest('currentsong');
$array = explode("\n", $res->getBody());
foreach($array as $item) {
$item_array = explode(": ", $item);
if ($item_array[0] == 'Artist') {
$artist = $item_array[1];
} elseif ($item_array[0] == 'Title') {
$song_title = $item_array[1];
}
}
$output = '<ul class="livestats">';
if (strlen($artist) > 12) {
$output = $output.'<li><span class="title-marquee"><span>'.$artist.'</span></span></li>';
} else {
$output = $output.'<li><span class="title">'.$artist.'</span></li>';
}
$output = $output.'</ul><ul class="livestats">';
if (strlen($song_title) > 12) {
$output = $output.'<li><span class="title-marquee"><span>'.$song_title.'</span></span></li>';
} else {
$output = $output.'<li><span class="title">'.$song_title.'</span></li>';
}
$output = $output.'</ul>';
return json_encode(['status' => $active, 'html' => $output]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/command/?cmd='.$endpoint;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

View File

@@ -1,89 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Sabnzbd implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#3e3924';
}
public function icon()
{
return 'supportedapps/sabnzbd.png';
}
public function configDetails()
{
return 'sabnzbd';
}
public function testConfig()
{
$res = $this->buildRequest('queue');
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(isset($data->error) && !empty($data->error)) {
echo 'Failed: '.$data->error;
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'inactive';
$res = $this->buildRequest('queue');
$data = json_decode($res->getBody());
//$data->result->RemainingSizeMB = '10000000';
//$data->result->DownloadRate = '100000000';
if($data) {
$size = $data->queue->mbleft;
$rate = $data->queue->kbpersec;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate*1000, false, ' <span>');
$active = ($size > 0 || $rate > 0) ? 'active' : 'inactive';
$html = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
';
}
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$apikey = $config->apikey;
//print_r($config);
//die();
$url = rtrim($url, '/');
$api_url = $url.'/api?output=json&apikey='.$apikey.'&mode='.$endpoint;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Sickrage implements Contracts\Applications {
public function defaultColour()
{
return '#6185a6';
}
public function icon()
{
return 'supportedapps/sickrage.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Sonarr implements Contracts\Applications {
public function defaultColour()
{
return '#163740';
}
public function icon()
{
return 'supportedapps/sonarr.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class Syncthing implements Contracts\Applications {
public function defaultColour()
{
return '#888';
}
public function icon()
{
return 'supportedapps/syncthing.png';
}
}

View File

@@ -1,14 +0,0 @@
<?php namespace App\SupportedApps;
class TVheadend implements Contracts\Applications {
public function defaultColour()
{
return '#006080';
}
public function icon()
{
return 'supportedapps/tvheadend.png';
}
}

View File

@@ -1,77 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Tautulli implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#2d2208';
}
public function icon()
{
return 'supportedapps/tautulli.png';
}
public function configDetails()
{
return 'tautulli';
}
public function testConfig()
{
$res = $this->buildRequest('arnold');
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(isset($data->error) && !empty($data->error)) {
echo 'Failed: '.$data->error;
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->buildRequest('get_activity');
$data = json_decode($res->getBody());
$stream_count = $data->response->data->stream_count;
$html = '
<ul class="livestats">
<li><span class="title">Stream Count</span><strong>'.$stream_count.'</strong></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$apikey = $config->apikey;
$url = rtrim($url, '/');
$api_url = $url.'/api/v2?apikey='.$apikey.'&cmd='.$endpoint;
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

View File

@@ -1,78 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class Traefik implements Contracts\Applications, Contracts\Livestats
{
public function defaultColour()
{
return '#28434a';
}
public function icon()
{
return 'supportedapps/traefik.png';
}
public function configDetails()
{
return 'traefik';
}
public function testConfig()
{
$res = $this->sendRequest();
if ($res == null) {
echo 'Traefik connection failed';
return;
}
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
echo "Successfully connected with status: ".$data->result."\n";
break;
case 404:
echo 'Failed: Please make sure your URL is correct and includes the port';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'inactive';
$res = $this->sendRequest();
$data = json_decode($res->getBody());
if ($data) {
$avg_response_time = $data->average_response_time_sec;
$time = $avg_response_time*1000;
$time_output = number_format($time, 2);
$active = ($time > 0) ? 'active' : 'inactive';
$html = '
<ul class="livestats">
<li><span class="title">Avg. Response Time</span><sub><i class="fas fa-heartbeat"></i> '.$time_output.' ms</sub></li>
</ul>
';
}
return json_encode(['status' => $active, 'html' => $html]);
}
public function sendRequest()
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/health';
$client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
$res = $client->request('GET', $api_url);
return $res;
}
}

View File

@@ -1,169 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
class Transmission implements Contracts\Applications, Contracts\Livestats
{
private $_client;
private $_clientOptions = array();
public function __construct()
{
$body = array();
$body["method"] = "torrent-get";
$body["arguments"] = array("fields" => ["percentDone","status","rateDownload","rateUpload"]);
$this->_client = new Client(
['http_errors' => false,
'timeout' => 10,
'body' => json_encode($body)]
);
}
public function defaultColour()
{
return '#950003';
}
public function icon()
{
return 'supportedapps/transmission.png';
}
public function configDetails()
{
return 'transmission';
}
public function testConfig()
{
$res = $this->sendRequest();
if ($res == null) {
echo 'Transmission connection failed';
return;
}
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
echo "Successfully connected with status: ".$data->result."\n";
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and includes the port';
break;
case 409:
echo 'Failed: Incorrect session id';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$html = '';
$active = 'active';
$res = $this->sendRequest();
if ($res == null) {
Log::debug('Transmission connection failed');
return '';
}
$data = json_decode($res->getBody());
if (! isset($data->arguments)) {
Log::debug('Failed to fetch data from Transmission');
return '';
}
$torrents = $data->arguments->torrents;
$torrentCount = count($torrents);
$rateDownload = $rateUpload = $completedTorrents = 0;
foreach ($torrents as $thisTorrent) {
$rateDownload += $thisTorrent->rateDownload;
$rateUpload += $thisTorrent->rateUpload;
if ($thisTorrent->percentDone == 1) {
$completedTorrents += 1;
}
}
if ($torrentCount - $completedTorrents == 0) {
// Don't poll as frequently if we don't have any active torrents
$active = 'inactive';
}
$html = '
<ul class="livestats">
<li><span class="title">Done</span><sub>'.$completedTorrents.' / '.$torrentCount.'</sub></li>
<li><span class="title">Down</span><sub>'.format_bytes($rateDownload).'</sub></li>
<li><span class="title">Up</span><sub>'.format_bytes($rateUpload).'</sub></li>
</ul>
';
return json_encode(['status' => $active, 'html' => $html]);;
}
private function sendRequest()
{
$optionsSet = $this->setClientOptions();
if (! $optionsSet) {
// Pass the failed response back up the chain
return null;
}
$res = $this->torrentGet();
if ($res->getStatusCode() == 409) {
$this->setClientOptions();
$res = $this->torrentGet();
}
return $res;
}
private function torrentGet()
{
$res = null;
try{
$res = $this->_client->request(
'POST',
$this->getApiUrl(),
$this->_clientOptions
);
}catch(\GuzzleHttp\Exception\BadResponseException $e){
Log::error("Connection to {$e->getRequest()->getUrl()} failed");
Log::debug($e->getMessage());
$res = $e->getRequest();
}catch(\GuzzleHttp\Exception\ConnectException $e) {
Log::error("Transmission connection refused");
Log::debug($e->getMessage());
}
return $res;
}
private function setClientOptions()
{
if ($this->config->username != '' || $this->config->password != '') {
$this->_clientOptions = ['auth'=> [$this->config->username, $this->config->password, 'Basic']];
}
try{
$res = $this->_client->request('HEAD', $this->getApiUrl(), $this->_clientOptions);
$xtId = $res->getHeaderLine('X-Transmission-Session-Id');
if ($xtId != null) {
$this->_clientOptions['headers'] = ['X-Transmission-Session-Id' => $xtId];
} else {
Log::error("Unable to get Transmission session information");
Log::debug("Status Code: ".$res->getStatusCode());
}
}catch(\GuzzleHttp\Exception\ConnectException $e){
Log::error("Failed connection to Transmission");
return false;
}
return true;
}
private function getApiUrl()
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/transmission/rpc';
return $api_url;
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Ttrss implements Contracts\Applications {
public function defaultColour()
{
return '#9d704c';
}
public function icon()
{
return 'supportedapps/tt-rss.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Unifi implements Contracts\Applications {
public function defaultColour()
{
return '#363840';
}
public function icon()
{
return 'supportedapps/unifi.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Unraid implements Contracts\Applications {
public function defaultColour()
{
return '#A12624';
}
public function icon()
{
return 'supportedapps/unraid.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class Watcher3 implements Contracts\Applications {
public function defaultColour()
{
return '#500';
}
public function icon()
{
return 'supportedapps/watcher3.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class WebTools implements Contracts\Applications {
public function defaultColour()
{
return '#555';
}
public function icon()
{
return 'supportedapps/webtools.png';
}
}

View File

@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps;
class pyLoad implements Contracts\Applications {
public function defaultColour()
{
return '#881';
}
public function icon()
{
return 'supportedapps/pyload.png';
}
}

View File

@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class ruTorrent implements Contracts\Applications {
public function defaultColour()
{
return '#004';
}
public function icon()
{
return 'supportedapps/rutorrent.png';
}
}

View File

@@ -15,7 +15,7 @@ class User extends Authenticatable
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
'username', 'email', 'password',
];
/**
@@ -26,4 +26,38 @@ class User extends Authenticatable
protected $hidden = [
'password', 'remember_token',
];
/**
* Get the items for the user.
*/
public function items()
{
return $this->hasMany('App\Item');
}
/**
* The settings that belong to the user.
*/
public function settings()
{
return $this->belongsToMany('App\Setting')->withPivot('uservalue');
}
public static function currentUser()
{
$current_user = session('current_user');
if ($current_user) { // if logged in, set this user
return $current_user;
} else { // not logged in, get first user
$user = User::where('public_front',true)->first();
if(!$user) {
$user = User::first();
}
session(['current_user' => $user]);
return $user;
}
}
}

View File

@@ -6,11 +6,13 @@
"type": "project",
"require": {
"php": ">=7.0.0",
"fideloper/proxy": "~3.3",
"fideloper/proxy": "^4.0",
"graham-campbell/github": "^7.5",
"guzzlehttp/guzzle": "^6.3",
"laravel/framework": "5.5.*",
"laravel/framework": "5.7.*",
"laravel/tinker": "~1.0",
"laravelcollective/html": "^5.5"
"laravelcollective/html": "^5.5",
"php-http/guzzle6-adapter": "^1.1"
},
"require-dev": {
"filp/whoops": "~2.0",

2831
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ return [
*/
'name' => env('APP_NAME', 'Heimdall'),
'version' => '1.4.14',
'version' => '2.2.2',
/*
|--------------------------------------------------------------------------
@@ -229,6 +229,9 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'SupportedApps' => App\SupportedApps::class,
'EnhancedApps' => App\EnhancedApps::class,
],
];

91
config/github.php Normal file
View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
/*
* This file is part of Laravel GitHub.
*
* (c) Graham Campbell <graham@alt-three.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
/*
|--------------------------------------------------------------------------
| Default Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the connections below you wish to use as
| your default connection for all work. Of course, you may use many
| connections at once using the manager class.
|
*/
'default' => 'main',
/*
|--------------------------------------------------------------------------
| GitHub Connections
|--------------------------------------------------------------------------
|
| Here are each of the connections setup for your application. Example
| configuration has been included, but you may add as many connections as
| you would like. Note that the 5 supported authentication methods are:
| "application", "jwt", "none", "password", and "token".
|
*/
'connections' => [
'main' => [
'token' => 'your-token',
'method' => 'token',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
'app' => [
'clientId' => 'your-client-id',
'clientSecret' => 'your-client-secret',
'method' => 'application',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
'jwt' => [
'token' => 'your-jwt-token',
'method' => 'jwt',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
'other' => [
'username' => 'your-username',
'password' => 'your-password',
'method' => 'password',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
'none' => [
'method' => 'none',
// 'backoff' => false,
// 'cache' => false,
// 'version' => 'v3',
// 'enterprise' => false,
],
],
];

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('username')->unique();
$table->string('email');
$table->string('avatar')->nullable();
$table->string('password')->nullable();
$table->string('autologin')->nullable()->index();
$table->boolean('public_front')->default(false);
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePasswordResetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddUserIdToItemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('items', function (Blueprint $table) {
$table->integer('user_id')->default(1)->index(); // 0 = item, 1 = category
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('items', function (Blueprint $table) {
$table->dropColumn(['user_id']);
});
}
}

Some files were not shown because too many files have changed in this diff Show More