Compare commits

...

103 Commits

Author SHA1 Message Date
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
2955 changed files with 113748 additions and 36550 deletions

3
.gitignore vendored
View File

@@ -23,4 +23,5 @@ yarn-error.log
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.VolumeIcon.icns
storage/app/public/avatars/*

55
app/Application.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Application extends Model
{
public $incrementing = false;
protected $primaryKey = 'appid';
//
public function icon()
{
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();
$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,61 @@
<?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');
$json = app_path('SupportedApps/'.$folder.'/app.json');
if(file_exists($json)) {
$app = json_decode(file_get_contents($json));
$exists = Application::find($app->appid);
if($exists) {
$this->error('This app is already registered');
exit;
}
// Doesn't exist so add it
SupportedApps::saveApp($app, new Application);
$this->info("Application Added - ".$app->name." - ".$app->appid);
} 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,12 @@ function title_color($hex)
} else {
return ' white';
}
}
}
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]);
//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);
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,23 @@
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;
class ItemController extends Controller
{
public function __construct()
{
$this->middleware('allowed');
}
/**
* Display a listing of the resource on the dashboard.
*
@@ -37,9 +45,8 @@ class ItemController extends Controller
$item->save();
}
}
/**
/**
* Pin item on the dashboard.
*
* @return \Illuminate\Http\Response
@@ -144,10 +151,19 @@ 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());
@@ -209,10 +225,19 @@ 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());
@@ -264,16 +289,6 @@ class ItemController extends Controller
->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 +297,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 +330,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

@@ -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);
@@ -55,6 +63,7 @@ class SettingsController extends Controller
public function update(Request $request, $id)
{
$setting = Setting::find($id);
$user = $this->user();
if (!is_null($setting)) {
$data = Setting::getInput();
@@ -64,17 +73,16 @@ class SettingsController extends Controller
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();
$user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => $setting_value]);
$route = route('settings.index', [], false);
return redirect($route)
->with([
@@ -95,10 +103,11 @@ 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);
return redirect($route)

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.
*
@@ -58,10 +63,13 @@ 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());

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', [], false);
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', [], false);
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', [], false);
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

@@ -19,11 +19,5 @@ class TrustProxies extends Middleware
*
* @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,26 @@ 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;
class Item extends Model
{
use SoftDeletes;
protected static function boot()
{
parent::boot();
static::addGlobalScope('user_id', function (Builder $builder) {
$current_user = User::currentUser();
$builder->where('user_id', $current_user->id);
});
}
//
protected $fillable = [
'title', 'url', 'colour', 'icon', 'description', 'pinned', 'order', 'type'
'title', 'url', 'colour', 'icon', 'description', 'pinned', 'order', 'type', 'class', 'user_id'
];
/**
@@ -23,77 +34,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 +45,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)) {
@@ -216,5 +138,63 @@ 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 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;
}
/**
* Get the user that owns the item.
*/
public function user()
{
return $this->belongsTo('App\User');
}
}

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

@@ -0,0 +1,63 @@
<?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;
if(!file_exists(app_path('SupportedApps/'.className($app->name)))) {
SupportedApps::getFiles($app);
$application = new Application;
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, $localapp);
}
} else {
SupportedApps::getFiles($app);
$application = new Application;
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,7 +19,6 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
$alt_bg = '';
if(!is_file(base_path('.env'))) {
touch(base_path('.env'));
@@ -32,34 +34,87 @@ 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));
}
}
$applications = Application::all();
if($applications->count() <= 0) {
if (class_exists('ZipArchive')) {
ProcessApps::dispatch();
} else {
die("You are missing php-zip");
}
}
if(!is_file(public_path('storage'))) {
Artisan::call('storage:link');
\Session::put('current_user', null);
}
// 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)) {
// Authentication passed...
$user = \Auth::user();
//\Session::put('current_user', $user);
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'));
}
}
/**

View File

@@ -5,6 +5,8 @@ namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Input;
use Form;
use Illuminate\Support\Facades\Auth;
use App\User;
class Setting extends Model
{
@@ -46,6 +48,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 +88,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 +139,7 @@ class Setting extends Model
return $this->belongsTo('App\SettingGroup', 'group_id');
}
/**
* @param string $key
*
@@ -132,20 +147,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;
}
}
#}
}
/**
@@ -175,21 +224,23 @@ class Setting extends Model
$output = '';
$homepage_search = self::fetch('homepage_search');
$search_provider = self::where('key', '=', 'search_provider')->first();
//die(var_dump($search_provider->value));
$user_search_provider = self::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;
if($search_provider->value === 'none') return $output;
if(empty($search_provider->value)) return $output;
if(is_null($search_provider->value)) return $output;
if($user_search_provider === 'none') return $output;
if(empty($user_search_provider)) return $output;
if(is_null($user_search_provider)) 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) {
$name = $options[$user_search_provider];
if((bool)$user_search_provider) {
switch($user_search_provider) {
case 'google':
$url = 'https://www.google.com/search';
$var = 'q';
@@ -218,4 +269,19 @@ class Setting extends Model
}
return $output;
}
/**
* The users that belong to the setting.
*/
public function users()
{
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
{
//
}

154
app/SupportedApps.php Normal file
View File

@@ -0,0 +1,154 @@
<?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)
{
$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 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)
{
$img_src = app_path('SupportedApps/'.className($details->name).'/'.$details->icon);
$img_dest = public_path('storage/supportedapps/'.$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 = 'supportedapps/'.$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",

1763
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.1.3',
/*
|--------------------------------------------------------------------------
@@ -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']);
});
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateSettingUserPivotTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('setting_user', function (Blueprint $table) {
$table->integer('setting_id')->unsigned()->index();
$table->foreign('setting_id')->references('id')->on('settings')->onDelete('cascade');
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->primary(['setting_id', 'user_id']);
$table->string('uservalue')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('setting_user');
}
}

View File

@@ -0,0 +1,41 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateApplicationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('applications', function (Blueprint $table) {
$table->string('appid')->unique();
$table->string('name')->unique();
$table->string('sha')->unique()->nullable();
$table->string('icon')->nullable();
$table->string('website')->nullable();
$table->string('license')->nullable();
$table->mediumText('description')->nullable();
$table->boolean('enhanced')->default(false);
$table->string('tile_background')->default('dark');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('applications');
}
}

View File

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

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateJobsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('jobs');
}
}

View File

@@ -12,5 +12,6 @@ class DatabaseSeeder extends Seeder
public function run()
{
$this->call(SettingsSeeder::class);
$this->call(UsersSeeder::class);
}
}

View File

@@ -160,5 +160,38 @@ class SettingsSeeder extends Seeder
$setting->label = 'app.settings.window_target';
$setting->save();
}
if($support = Setting::find(8)) {
$support->label = 'app.settings.support';
$support->value = '<a rel="noopener" target="_blank" href="https://discord.gg/CCjHKn4">Discord</a> | <a rel="noopener" target="_blank" href="https://github.com/linuxserver/Heimdall">Github</a> | <a rel="noopener" target="_blank" href="https://blog.heimdall.site/">Blog</a>';
$support->save();
} else {
$setting = new Setting;
$setting->id = 8;
$setting->group_id = 1;
$setting->key = 'support';
$setting->type = 'text';
$setting->label = 'app.settings.support';
$setting->value = '<a rel="noopener" target="_blank" href="https://discord.gg/CCjHKn4">Discord</a> | <a rel="noopener" target="_blank" href="https://github.com/linuxserver/Heimdall">Github</a> | <a rel="noopener" target="_blank" href="https://blog.heimdall.site/">Blog</a>';
$setting->system = true;
$setting->save();
}
if($donate = Setting::find(9)) {
$donate->label = 'app.settings.donate';
$donate->value = '<a rel="noopener" target="_blank" href="https://www.paypal.me/heimdall">Paypal</a>';
$donate->save();
} else {
$setting = new Setting;
$setting->id = 9;
$setting->group_id = 1;
$setting->key = 'donate';
$setting->type = 'text';
$setting->label = 'app.settings.donate';
$setting->value = '<a rel="noopener" target="_blank" href="https://www.paypal.me/heimdall">Paypal</a>';
$setting->system = true;
$setting->save();
}
}
}

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