Compare commits

..

2 commits
2.x ... master

Author SHA1 Message Date
KodeStar
1750da1345
Merge pull request #630 from divendres/catala
Catalan translation
2022-11-14 15:02:46 +00:00
Albert
d0e3a2faf5 Catalan translation 2021-11-23 13:38:01 +01:00
6247 changed files with 145845 additions and 445712 deletions

View file

@ -9,13 +9,6 @@ LOG_CHANNEL=daily
DB_CONNECTION=sqlite DB_CONNECTION=sqlite
DB_DATABASE=app.sqlite DB_DATABASE=app.sqlite
#DB_CONNECTION=<mysql | pgsql>
#DB_HOST=<hostname | ip>
#DB_PORT=<port number>
#DB_DATABASE=<database>
#DB_USERNAME=<user>
#DB_PASSWORD=<password>
BROADCAST_DRIVER=log BROADCAST_DRIVER=log
CACHE_DRIVER=file CACHE_DRIVER=file
QUEUE_CONNECTION=sync QUEUE_CONNECTION=sync

View file

@ -1,3 +0,0 @@
huebee.js
jquery-ui.min.js
bootstrap.js

View file

@ -1,13 +0,0 @@
{
"extends": ["airbnb-base", "prettier"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": ["error"]
},
"env": {
"browser": true
},
"globals": {
"$": true
}
}

View file

@ -1,16 +0,0 @@
name: Issue & PR Tracker
on:
issues:
types: [opened,reopened,labeled,unlabeled,closed]
pull_request_target:
types: [opened,reopened,review_requested,review_request_removed,labeled,unlabeled,closed]
pull_request_review:
types: [submitted,edited,dismissed]
jobs:
manage-project:
permissions:
issues: write
uses: linuxserver/github-workflows/.github/workflows/issue-pr-tracker.yml@v1
secrets: inherit

View file

@ -1,13 +0,0 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: '35 15 * * *'
workflow_dispatch:
jobs:
stale:
permissions:
issues: write
pull-requests: write
uses: linuxserver/github-workflows/.github/workflows/issues-cron.yml@v1
secrets: inherit

View file

@ -1,59 +0,0 @@
name: Tests (PHP)
on: [pull_request]
jobs:
tests:
name: Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php
with:
php-version: '7.4'
extensions: mbstring, dom, fileinfo, mysql, libxml, xml, xmlwriter, dom, tokenizer, filter, json, phar, pcre, openssl, pdo, intl, curl
- name: Cache composer dependencies
uses: actions/cache@v1
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- name: Run composer install
run: composer install -n --prefer-dist
env:
APP_ENV: testing
- name: Prepare Laravel Application
run: |
cp .env.example .env
php artisan key:generate
- name: Cache yarn dependencies
uses: actions/cache@v1
with:
path: node_modules
key: yarn-${{ hashFiles('yarn.lock') }}
- name: Run yarn
run: yarn && yarn dev
- name: Run ESLint
run: yarn lint
- name: Run tests
run: php artisan test
env:
APP_ENV: testing
- name: Php code sniffer
run: ./vendor/bin/phpcs
- name: Upload artifacts
uses: actions/upload-artifact@master
if: failure()
with:
name: Logs
path: ./storage/logs

2
.gitignore vendored
View file

@ -3,7 +3,6 @@
/public/hot /public/hot
/public/storage /public/storage
/storage/*.key /storage/*.key
/storage/debugbar
/.idea /.idea
/.vagrant /.vagrant
Homestead.json Homestead.json
@ -28,4 +27,3 @@ yarn-error.log
.VolumeIcon.icns .VolumeIcon.icns
storage/app/public/avatars/* storage/app/public/avatars/*
.env .env
.phpunit.result.cache

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,218 +2,126 @@
namespace App; namespace App;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* App\Application
*
* @property string $appid
* @property string $name
* @property string|null $sha
* @property string|null $icon
* @property string|null $website
* @property string|null $license
* @property string|null $description
* @property int $enhanced
* @property string $tile_background
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $class
* @method static Builder|Application newModelQuery()
* @method static Builder|Application newQuery()
* @method static Builder|Application query()
* @method static Builder|Application whereAppid($value)
* @method static Builder|Application whereClass($value)
* @method static Builder|Application whereCreatedAt($value)
* @method static Builder|Application whereDescription($value)
* @method static Builder|Application whereEnhanced($value)
* @method static Builder|Application whereIcon($value)
* @method static Builder|Application whereLicense($value)
* @method static Builder|Application whereName($value)
* @method static Builder|Application whereSha($value)
* @method static Builder|Application whereTileBackground($value)
* @method static Builder|Application whereUpdatedAt($value)
* @method static Builder|Application whereWebsite($value)
*/
class Application extends Model class Application extends Model
{ {
/**
* @var bool
*/
public $incrementing = false; public $incrementing = false;
/**
* @var string
*/
protected $primaryKey = 'appid'; protected $primaryKey = 'appid';
/** //
* @return mixed
*/
public function icon() public function icon()
{ {
if (! file_exists(storage_path('app/public/'.$this->icon))) { if(!file_exists(storage_path('app/public/'.$this->icon))) {
$img_src = app_path('SupportedApps/'.$this->name.'/'.str_replace('icons/', '', $this->icon)); $img_src = app_path('SupportedApps/'.$this->name.'/'.str_replace('icons/', '', $this->icon));
$img_dest = storage_path('app/public/'.$this->icon); $img_dest = storage_path('app/public/'.$this->icon);
//die("i: ".$img_src); //die("i: ".$img_src);
@copy($img_src, $img_dest); @copy($img_src, $img_dest);
} }
return $this->icon; return $this->icon;
} }
/** public function iconView()
* @return string
*/
public function iconView(): string
{ {
return asset('storage/'.$this->icon); return asset('storage/'.$this->icon);
} }
/** public function defaultColour()
* @return string
*/
public function defaultColour(): string
{ {
// check if light or dark // check if light or dark
if ($this->tile_background == 'light') { if($this->tile_background == 'light') return '#fafbfc';
return '#fafbfc';
}
return '#161b1f'; return '#161b1f';
} }
/** public function class()
* @return string
*/
public function class(): string
{ {
$name = $this->name; $name = $this->name;
$name = preg_replace('/[^\p{L}\p{N}]/u', '', $name); $name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
return '\App\SupportedApps\\'.$name.'\\'.$name;
}
/**
* @param $name
* @return string
*/
public static function classFromName($name): string
{
$name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
$class = '\App\SupportedApps\\'.$name.'\\'.$name; $class = '\App\SupportedApps\\'.$name.'\\'.$name;
return $class; return $class;
} }
/** public static function classFromName($name)
* @return Collection {
*/ $name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
public static function apps(): Collection
$class = '\App\SupportedApps\\'.$name.'\\'.$name;
return $class;
}
public static function apps()
{ {
$json = json_decode(file_get_contents(storage_path('app/supportedapps.json'))) ?? []; $json = json_decode(file_get_contents(storage_path('app/supportedapps.json'))) ?? [];
$apps = collect($json->apps); $apps = collect($json->apps);
$sorted = $apps->sortBy('name', SORT_NATURAL|SORT_FLAG_CASE);
return $apps->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE); return $sorted;
} }
/** public static function autocomplete()
* @return array
*/
public static function autocomplete(): array
{ {
$apps = self::apps(); $apps = self::apps();
$list = []; $list = [];
foreach ($apps as $app) { foreach($apps as $app) {
$list[] = (object) [ $list[] = (object)[
'label' => $app->name, 'label' => $app->name,
'value' => $app->appid, 'value' => $app->appid
]; ];
} }
return $list; return $list;
} }
/**
* @param $appid
* @return mixed|null
* @throws GuzzleException
*/
public static function getApp($appid) public static function getApp($appid)
{ {
Log::debug("Get app triggered for: $appid"); $localapp = Application::where('appid', $appid)->first();
$localapp = self::where('appid', $appid)->first();
$app = self::single($appid); $app = self::single($appid);
$application = ($localapp) ? $localapp : new self; $application = ($localapp) ? $localapp : new Application;
// Files missing? || app not in db || old sha version if(!file_exists(app_path('SupportedApps/'.className($app->name)))) {
if (! file_exists(app_path('SupportedApps/'.className($app->name))) || SupportedApps::getFiles($app);
! $localapp || SupportedApps::saveApp($app, $application);
$localapp->sha !== $app->sha } else {
) { // check if there has been an update for this app
$gotFiles = SupportedApps::getFiles($app); if($localapp) {
if ($gotFiles) { if($localapp->sha !== $app->sha) {
SupportedApps::getFiles($app);
$app = SupportedApps::saveApp($app, $application);
}
} else {
SupportedApps::getFiles($app);
$app = SupportedApps::saveApp($app, $application); $app = SupportedApps::saveApp($app, $application);
} }
} }
return $app; return $app;
} }
/**
* @param $appid
* @return mixed|null
*/
public static function single($appid) public static function single($appid)
{ {
$apps = self::apps(); $apps = self::apps();
$app = $apps->where('appid', $appid)->first(); $app = $apps->where('appid', $appid)->first();
if ($app === null) return null;
if ($app === null) { $classname = preg_replace('/[^\p{L}\p{N}]/u', '', $app->name);
// Try in db for Private App
$appModel = self::where('appid', $appid)->first();
if ($appModel) {
$app = json_decode($appModel->toJson());
}
}
if ($app === null) {
return null;
}
$classname = preg_replace('/[^\p{L}\p{N}]/u', '', $app->name);
$app->class = '\App\SupportedApps\\'.$classname.'\\'.$classname; $app->class = '\App\SupportedApps\\'.$classname.'\\'.$classname;
return $app; return $app;
} }
/** public static function applist()
* @return array
*/
public static function applist(): array
{ {
$list = []; $list = [];
$list['null'] = 'None'; $list['null'] = 'None';
$apps = self::apps(); $apps = self::apps();
foreach ($apps as $app) { foreach($apps as $app) {
$list[$app->appid] = $app->name; $list[$app->appid] = $app->name;
} }
// Check for private apps in the db
$appsListFromDB = self::all(['appid', 'name']);
foreach ($appsListFromDB as $app) {
// Already existing keys are overwritten,
// only private apps should be added at the end
$list[$app->appid] = $app->name;
}
return $list; return $list;
} }
} }

View file

@ -2,10 +2,9 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Application; use App\Application;
use App\SupportedApps; use App\SupportedApps;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class RegisterApp extends Command class RegisterApp extends Command
{ {
@ -14,7 +13,7 @@ class RegisterApp extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'register:app {folder} {--remove}'; protected $signature = 'register:app {folder}';
/** /**
* The console command description. * The console command description.
@ -36,72 +35,45 @@ class RegisterApp extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @return void * @return mixed
*/ */
public function handle() public function handle()
{ {
$folder = $this->argument('folder'); $folder = $this->argument('folder');
if ($folder == 'all') { if($folder == 'all') {
$apps = scandir(app_path('SupportedApps')); $apps = scandir(app_path('SupportedApps'));
foreach ($apps as $folder) { foreach($apps as $folder) {
if ($folder == '.' || $folder == '..') { if($folder == '.' || $folder == '..') continue;
continue;
}
$this->addApp($folder); $this->addApp($folder);
} }
} else { } else {
$this->addApp($folder, $this->option('remove')); $this->addApp($folder);
} }
} }
/** public function addApp($folder)
* @param $folder
* @param bool $remove
* @return void
*/
public function addApp($folder, bool $remove = false)
{ {
$json = app_path('SupportedApps/'.$folder.'/app.json'); $json = app_path('SupportedApps/'.$folder.'/app.json');
if(file_exists($json)) {
if (!file_exists($json)) { $app = json_decode(file_get_contents($json));
$this->error('Could not find ' . $json); if(isset($app->appid)) {
return; $exists = Application::find($app->appid);
} if($exists) {
$this->error('Application already registered - '.$exists->name." - ".$exists->appid);
$app = json_decode(file_get_contents($json)); } else {
// Doesn't exist so add it
if (!isset($app->appid)) { SupportedApps::saveApp($app, new Application);
$this->error('No App ID for - ' . $folder); $this->info("Application Added - ".$app->name." - ".$app->appid);
return; }
} } else {
$this->error('No App ID for - '.$folder);
$exists = Application::find($app->appid);
if ($exists) {
if ($remove) {
$exists->delete();
$this->info('Application Removed - ' . $app->name . ' - ' . $app->appid);
return;
} }
$this->error('Application already registered - ' . $exists->name . ' - ' . $exists->appid);
return; } else {
$this->error('Could not find '.$json);
} }
// Doesn't exist so add it
SupportedApps::saveApp($app, new Application);
$this->saveIcon($folder, $app->icon);
$this->info('Application Added - ' . $app->name . ' - ' . $app->appid);
}
/**
* @param $appFolder
* @param $icon
* @return void
*/
private function saveIcon($appFolder, $icon)
{
$iconPath = app_path('SupportedApps/' . $appFolder . '/' . $icon);
$contents = file_get_contents($iconPath);
Storage::disk('public')->put('icons/'.$icon, $contents);
} }
} }

View file

@ -19,7 +19,7 @@ class Kernel extends ConsoleKernel
/** /**
* Define the application's command schedule. * Define the application's command schedule.
* *
* @param Schedule $schedule * @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void * @return void
*/ */
protected function schedule(Schedule $schedule) protected function schedule(Schedule $schedule)

View file

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

View file

@ -2,137 +2,78 @@
use Illuminate\Support\Str; use Illuminate\Support\Str;
/** function format_bytes($bytes, $is_drive_size = true, $beforeunit = '', $afterunit = '')
* @param $bytes
* @param bool $is_drive_size
* @param string $beforeunit
* @param string $afterunit
* @return string
*/
function format_bytes($bytes, bool $is_drive_size = true, string $beforeunit = '', string $afterunit = ''): string
{ {
$btype = ($is_drive_size === true) ? 1000 : 1024; $btype = ($is_drive_size === true) ? 1000 : 1024;
$labels = ['B', 'KB', 'MB', 'GB', 'TB']; $labels = array('B','KB','MB','GB','TB');
// use 1000 rather than 1024 to simulate HD size not real size for($x = 0; $bytes >= $btype && $x < (count($labels) - 1); $bytes /= $btype, $x++); // use 1000 rather than 1024 to simulate HD size not real size
for ($x = 0; $bytes >= $btype && $x < (count($labels) - 1); $bytes /= $btype, $x++) ; if($labels[$x] == "TB") return(round($bytes, 3).$beforeunit.$labels[$x].$afterunit);
if ($labels[$x] == 'TB') { elseif($labels[$x] == "GB") return(round($bytes, 2).$beforeunit.$labels[$x].$afterunit);
return round($bytes, 3) . $beforeunit . $labels[$x] . $afterunit; elseif($labels[$x] == "MB") return(round($bytes, 2).$beforeunit.$labels[$x].$afterunit);
} elseif ($labels[$x] == 'GB') { else return(round($bytes, 0).$beforeunit.$labels[$x].$afterunit);
return round($bytes, 2) . $beforeunit . $labels[$x] . $afterunit;
} elseif ($labels[$x] == 'MB') {
return round($bytes, 2) . $beforeunit . $labels[$x] . $afterunit;
} else {
return round($bytes, 0) . $beforeunit . $labels[$x] . $afterunit;
}
} }
/** function str_slug($title, $separator = '-', $language = 'en')
* @param $title
* @param string $separator
* @param string $language
* @return string
*/
function str_slug($title, string $separator = '-', string $language = 'en'): string
{ {
return Str::slug($title, $separator, $language); return Str::slug($title, $separator, $language);
} }
if (!function_exists('str_is')) { if (! function_exists('str_is')) {
/** /**
* Determine if a given string matches a given pattern. * Determine if a given string matches a given pattern.
* *
* @param string|array $pattern * @param string|array $pattern
* @param string $value * @param string $value
* @return bool * @return bool
* *
* @deprecated Str::is() should be used directly instead. Will be removed in Laravel 6.0. * @deprecated Str::is() should be used directly instead. Will be removed in Laravel 6.0.
*/ */
function str_is($pattern, string $value): bool function str_is($pattern, $value)
{ {
return Str::is($pattern, $value); return Str::is($pattern, $value);
} }
} }
/** function get_brightness($hex) {
* @param $hex
* @return float|int
*/
function get_brightness($hex)
{
// returns brightness value from 0 to 255 // returns brightness value from 0 to 255
// strip off any leading # // strip off any leading #
// $hex = str_replace('#', '', $hex); $hex = str_replace('#', '', $hex);
$hex = preg_replace("/[^0-9A-Fa-f]/", '', $hex); if(strlen($hex) == 3) {
if (strlen($hex) == 3) { $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
} }
$c_r = hexdec(substr($hex, 0, 2)); $c_r = hexdec(substr($hex, 0, 2));
$c_g = hexdec(substr($hex, 2, 2)); $c_g = hexdec(substr($hex, 2, 2));
$c_b = hexdec(substr($hex, 4, 2)); $c_b = hexdec(substr($hex, 4, 2));
return (($c_r * 299) + ($c_g * 587) + ($c_b * 114)) / 1000; return (($c_r * 299) + ($c_g * 587) + ($c_b * 114)) / 1000;
} }
/** function title_color($hex)
* @param $hex
* @return string
*/
function title_color($hex): string
{ {
if (get_brightness($hex) > 130) { if(get_brightness($hex) > 130) {
return ' black'; return ' black';
} else { } else {
return ' white'; return ' white';
} }
} }
/** function getLinkTargetAttribute()
* @return string
*/
function getLinkTargetAttribute(): string
{ {
$target = \App\Setting::fetch('window_target'); $target = \App\Setting::fetch('window_target');
if ($target === 'current') { if($target === 'current') {
return ''; return '';
} else { } else {
return ' target="' . $target . '"'; return ' target="' . $target . '"';
} }
} }
/**
* @param $name
* @return array|string|string[]|null
*/
function className($name) function className($name)
{ {
return preg_replace('/[^\p{L}\p{N}]/u', '', $name); $name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
return $name;
} }
/**
* @param string $file
* @param string $extension
* @return bool
*/
function isImage(string $file, string $extension): bool
{
$allowedExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp'];
if (!in_array($extension, $allowedExtensions)) {
return false;
}
$tempFileName = @tempnam("/tmp", "image-check-");
$handle = fopen($tempFileName, "w");
fwrite($handle, $file);
fclose($handle);
if ($extension == 'svg') {
return 'image/svg+xml' === mime_content_type($tempFileName);
}
$size = @getimagesize($tempFileName);
return is_array($size) && str_starts_with($size['mime'], 'image');
}

View file

@ -3,18 +3,12 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\RedirectResponse; use App\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\URL;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpFoundation\Response;
class LoginController extends Controller class LoginController extends Controller
{ {
@ -36,7 +30,7 @@ class LoginController extends Controller
* *
* @var string * @var string
*/ */
protected string $redirectTo = '/'; protected $redirectTo = '/';
/** /**
* Create a new controller instance. * Create a new controller instance.
@ -46,13 +40,10 @@ class LoginController extends Controller
public function __construct() public function __construct()
{ {
Session::put('backUrl', URL::previous()); Session::put('backUrl', URL::previous());
$this->middleware('guest')->except(['logout','autologin']); $this->middleware('guest')->except('logout');
} }
/** public function username()
* @return string
*/
public function username(): string
{ {
return 'username'; return 'username';
} }
@ -60,12 +51,12 @@ class LoginController extends Controller
/** /**
* Handle a login request to the application. * Handle a login request to the application.
* *
* @param Request $request * @param \Illuminate\Http\Request $request
* @return Response * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
* *
* @throws ValidationException * @throws \Illuminate\Validation\ValidationException
*/ */
public function login(Request $request): Response public function login(Request $request)
{ {
$current_user = User::currentUser(); $current_user = User::currentUser();
$request->merge(['username' => $current_user->username, 'remember' => true]); $request->merge(['username' => $current_user->username, 'remember' => true]);
@ -97,64 +88,39 @@ class LoginController extends Controller
{ {
} }
/** public function setUser(User $user)
* @param User $user
* @return RedirectResponse
*/
public function setUser(User $user): RedirectResponse
{ {
Auth::logout(); Auth::logout();
session(['current_user' => $user]); session(['current_user' => $user]);
return redirect()->route('dash'); return redirect()->route('dash');
} }
/** public function autologin($uuid)
* @param $uuid
* @return RedirectResponse
*/
public function autologin($uuid): RedirectResponse
{ {
Auth::logout();
$user = User::where('autologin', $uuid)->first(); $user = User::where('autologin', $uuid)->first();
if (!$user) {
return redirect()->route('dash');
}
Auth::login($user, true); Auth::login($user, true);
session(['current_user' => $user]); session(['current_user' => $user]);
return redirect()->route('dash'); return redirect()->route('dash');
} }
/** /**
* Show the application's login form. * Show the application's login form.
* *
* @return Application|Factory|View * @return \Illuminate\Http\Response
*/ */
public function showLoginForm() public function showLoginForm()
{ {
return view('auth.login'); return view('auth.login');
} }
/** protected function authenticated(Request $request, $user)
* @param Request $request
* @param $user
* @return RedirectResponse
*/
protected function authenticated(Request $request, $user): RedirectResponse
{ {
return back(); return back();
} }
/**
* @return mixed|string
*/
public function redirectTo() public function redirectTo()
{ {
return Session::get('url.intended') ? Session::get('url.intended') : $this->redirectTo; return Session::get('url.intended') ? Session::get('url.intended') : $this->redirectTo;
} }
} }

View file

@ -2,10 +2,10 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\User; use App\User;
use Illuminate\Foundation\Auth\RegistersUsers; use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
class RegisterController extends Controller class RegisterController extends Controller
{ {
@ -27,7 +27,7 @@ class RegisterController extends Controller
* *
* @var string * @var string
*/ */
protected string $redirectTo = '/'; protected $redirectTo = '/';
/** /**
* Create a new controller instance. * Create a new controller instance.
@ -45,7 +45,7 @@ class RegisterController extends Controller
* @param array $data * @param array $data
* @return \Illuminate\Contracts\Validation\Validator * @return \Illuminate\Contracts\Validation\Validator
*/ */
protected function validator(array $data): \Illuminate\Contracts\Validation\Validator protected function validator(array $data)
{ {
return Validator::make($data, [ return Validator::make($data, [
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
@ -58,7 +58,7 @@ class RegisterController extends Controller
* Create a new user instance after a valid registration. * Create a new user instance after a valid registration.
* *
* @param array $data * @param array $data
* @return User * @return \App\User
*/ */
protected function create(array $data) protected function create(array $data)
{ {

View file

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

View file

@ -2,11 +2,12 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController; 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 class Controller extends BaseController
{ {
@ -21,6 +22,7 @@ class Controller extends BaseController
//print_r($this->user); //print_r($this->user);
return $next($request); return $next($request);
}); });
} }
public function user() public function user()

View file

@ -1,58 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Item;
use App\User;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\RateLimiter;
class HealthController extends Controller
{
/**
* @return int
*/
private static function getUsers(): int
{
return User::count();
}
/**
* @return int
*/
private static function getItems(): int
{
return Item::select('id')
->where('deleted_at', null)
->where('type', '0')
->count();
}
/**
* Handle the incoming request.
*
* @param Request $request
* @return JsonResponse|Response
* @throws BindingResolutionException
*/
public function __invoke(Request $request)
{
$REQUESTS_MAX_PER_MIN = 30;
$STATUS_TOO_MANY_REQUESTS = 429;
if (RateLimiter::remaining('health', $REQUESTS_MAX_PER_MIN) < 1) {
return response()->make('Too many attempts.', $STATUS_TOO_MANY_REQUESTS);
}
RateLimiter::hit('health');
return response()->json([
'status' => 'ok',
'items' => self::getItems(),
'users' => self::getUsers(),
]);
}
}

View file

@ -2,7 +2,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request;
class HomeController extends Controller class HomeController extends Controller
{ {
@ -13,16 +13,15 @@ class HomeController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
$this->middleware('auth'); $this->middleware('auth');
} }
/** /**
* Show the application dashboard. * Show the application dashboard.
* *
* @return RedirectResponse * @return \Illuminate\Http\Response
*/ */
public function index(): RedirectResponse public function index()
{ {
return redirect()->route('dash'); return redirect()->route('dash');
} }

View file

@ -1,31 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\View\View;
class ImportController extends Controller
{
/**
* Instantiate a new controller instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->middleware('allowed');
}
/**
* Handle the incoming request.
*
* @param Request $request
* @return View
*/
public function __invoke(Request $request): View
{
return view('items.import');
}
}

View file

@ -2,40 +2,36 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Artisan;
use App\Application; use App\Application;
use App\Item; use App\Item;
use App\Jobs\ProcessApps; use App\Setting;
use App\User; use App\User;
use GuzzleHttp\Client; use GrahamCampbell\GitHub\Facades\GitHub;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ServerException;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL; use App\SupportedApps;
use Illuminate\Validation\ValidationException; use App\Jobs\ProcessApps;
use Psr\Http\Message\ResponseInterface; use App\Search;
use Psr\Http\Message\StreamInterface; use Illuminate\Support\Facades\Route;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
class ItemController extends Controller class ItemController extends Controller
{ {
public function __construct() public function __construct()
{ {
parent::__construct();
$this->middleware('allowed'); $this->middleware('allowed');
} }
/**
/**
* Display a listing of the resource on the dashboard. * Display a listing of the resource on the dashboard.
* *
* @return View * @return \Illuminate\Http\Response
*/ */
public function dash(): View public function dash()
{ {
$data['apps'] = Item::whereHas('parents', function ($query) { $data['apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0); $query->where('id', 0);
@ -50,15 +46,15 @@ class ItemController extends Controller
return view('welcome', $data); return view('welcome', $data);
} }
/** /**
* Set order on the dashboard. * Set order on the dashboard.
* *
* @return void * @return \Illuminate\Http\Response
*/ */
public function setOrder(Request $request) public function setOrder(Request $request)
{ {
$order = array_filter($request->input('order')); $order = array_filter($request->input('order'));
foreach ($order as $o => $id) { foreach($order as $o => $id) {
$item = Item::find($id); $item = Item::find($id);
$item->order = $o; $item->order = $o;
$item->save(); $item->save();
@ -68,118 +64,193 @@ class ItemController extends Controller
/** /**
* Pin item on the dashboard. * Pin item on the dashboard.
* *
* @param $id * @return \Illuminate\Http\Response
* @return RedirectResponse
*/ */
public function pin($id): RedirectResponse public function pin($id)
{ {
$item = Item::findOrFail($id); $item = Item::findOrFail($id);
$item->pinned = true; $item->pinned = true;
$item->save(); $item->save();
$route = route('dash', []); $route = route('dash', []);
return redirect($route); return redirect($route);
} }
/** /**
* Unpin item on the dashboard. * Unpin item on the dashboard.
* *
* @param $id * @return \Illuminate\Http\Response
* @return RedirectResponse
*/ */
public function unpin($id): RedirectResponse public function unpin($id)
{ {
$item = Item::findOrFail($id); $item = Item::findOrFail($id);
$item->pinned = false; $item->pinned = false;
$item->save(); $item->save();
$route = route('dash', []); $route = route('dash', []);
return redirect($route); return redirect($route);
} }
/** /**
* Unpin item on the dashboard. * Unpin item on the dashboard.
* *
* @return RedirectResponse|View * @return \Illuminate\Http\Response
*/ */
public function pinToggle($id, $ajax = false, $tag = false) public function pinToggle($id, $ajax=false, $tag=false)
{ {
$item = Item::findOrFail($id); $item = Item::findOrFail($id);
$new = !(((bool)$item->pinned === true)); $new = ((bool)$item->pinned === true) ? false : true;
$item->pinned = $new; $item->pinned = $new;
$item->save(); $item->save();
if($ajax) {
if ($ajax) { if(is_numeric($tag) && $tag > 0) {
$item = Item::whereId($tag)->first(); $item = Item::whereId($tag)->first();
$data['apps'] = $item->children()->pinned()->orderBy('order', 'asc')->get();
$data['apps'] = new Collection; } else {
$data['apps'] = Item::pinned()->orderBy('order', 'asc')->get();
if ((int)$tag === 0) {
$tags = Item::where('type', 1)->pinned()->orderBy('order', 'asc')->get();
$data['apps'] = $data['apps']->merge($tags);
} }
$apps = $item->children()->pinned()->orderBy('order', 'asc')->get();
$data['apps'] = $data['apps']->merge($apps);
$data['ajax'] = true; $data['ajax'] = true;
return view('sortable', $data); return view('sortable', $data);
} else { } else {
$route = route('dash', []); $route = route('dash', []);
return redirect($route); return redirect($route);
} }
} }
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request * @return \Illuminate\Http\Response
* @return View
*/ */
public function index(Request $request): View public function index(Request $request)
{ {
$trash = (bool)$request->input('trash'); $trash = (bool)$request->input('trash');
$data['apps'] = Item::ofType('item')->orderBy('title', 'asc')->get(); $data['apps'] = Item::ofType('item')->orderBy('title', 'asc')->get();
$data['trash'] = Item::ofType('item')->onlyTrashed()->get(); $data['trash'] = Item::ofType('item')->onlyTrashed()->get();
if ($trash) { if($trash) {
return view('items.trash', $data); return view('items.trash', $data);
} else { } else {
return view('items.list', $data); return view('items.list', $data);
} }
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
* *
* @return View * @return \Illuminate\Http\Response
*/ */
public function create(): View public function create()
{ {
// //
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id'); $data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
$data['tags']->prepend(__('app.dashboard'), 0); $data['tags']->prepend(__('app.dashboard'), 0);
$data['current_tags'] = '0'; $data['current_tags'] = collect([0 => __('app.dashboard')]);
return view('items.create', $data); return view('items.create', $data);
}
public function storelogic($request, $id = null)
{
$application = Application::single($request->input('appid'));
$validatedData = $request->validate([
'title' => 'required|max:255',
'url' => 'required',
]);
if($request->hasFile('file')) {
$path = $request->file('file')->store('icons');
$request->merge([
'icon' => $path
]);
} elseif(strpos($request->input('icon'), 'http') === 0) {
$contents = file_get_contents($request->input('icon'));
if ($application) {
$icon = $application->icon;
} else {
$file = $request->input('icon');
$path_parts = pathinfo($file);
$icon = md5($contents);
$icon .= '.'.$path_parts['extension'];
}
$path = 'icons/'.$icon;
Storage::disk('public')->put($path, $contents);
$request->merge([
'icon' => $path
]);
}
$config = Item::checkConfig($request->input('config'));
$current_user = User::currentUser();
$request->merge([
'description' => $config,
'user_id' => $current_user->id
]);
if($request->input('appid') === 'null') {
$request->merge([
'class' => null,
]);
} else {
$request->merge([
'class' => Application::classFromName($application->name),
]);
}
if($id === null) {
$item = Item::create($request->all());
} else {
$item = Item::find($id);
$item->update($request->all());
}
$item->parents()->sync($request->tags);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->storelogic($request);
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.item_created'));
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
* *
* @param int $id * @param int $id
* @return View * @return \Illuminate\Http\Response
*/ */
public function edit(int $id): View public function edit($id)
{ {
// Get the item // Get the item
$item = Item::find($id); $item = Item::find($id);
if ($item->appid === null && $item->class !== null) { // old apps wont have an app id so set it if($item->appid === null && $item->class !== null) { // old apps wont have an app id so set it
$app = Application::where('class', $item->class)->first(); $app = Application::where('class', $item->class)->first();
if ($app) { if($app) {
$item->appid = $app->appid; $item->appid = $app->appid;
} }
} }
@ -190,156 +261,35 @@ class ItemController extends Controller
//$data['current_tags'] = $data['item']->parent; //$data['current_tags'] = $data['item']->parent;
//die(print_r($data['current_tags'])); //die(print_r($data['current_tags']));
// show the edit form and pass the nerd // show the edit form and pass the nerd
return view('items.edit', $data); return view('items.edit', $data);
}
/**
* @param Request $request
* @param null $id
* @return Item
* @throws ValidationException
*/
public static function storelogic(Request $request, $id = null): Item
{
$application = Application::single($request->input('appid'));
$validatedData = $request->validate([
'title' => 'required|max:255',
'url' => 'required',
'file' => 'image'
]);
if ($request->hasFile('file')) {
$path = $request->file('file')->store('icons');
$request->merge([
'icon' => $path,
]);
} elseif (strpos($request->input('icon'), 'http') === 0) {
$options = array(
"ssl" => array(
"verify_peer" => false,
"verify_peer_name" => false,
),
);
$file = $request->input('icon');
$path_parts = pathinfo($file);
$extension = $path_parts['extension'];
$contents = file_get_contents($request->input('icon'), false, stream_context_create($options));
if (!isImage($contents, $extension)) {
throw ValidationException::withMessages(['file' => 'Icon must be an image.']);
}
$path = 'icons/' . ($application ? $application->icon : md5($contents) . '.' . $extension);
// Private apps could have here duplicated icons folder
if (strpos($path, 'icons/icons/') !== false) {
$path = str_replace('icons/icons/', 'icons/', $path);
}
if (!Storage::disk('public')->exists($path)) {
Storage::disk('public')->put($path, $contents);
}
$request->merge([
'icon' => $path,
]);
}
$config = Item::checkConfig($request->input('config'));
// Don't overwrite the stored password if it wasn't submitted when updating the item
if ($id !== null && strpos($config, '"password":null') !== false) {
$storedItem = Item::find($id);
$storedConfigObject = json_decode($storedItem->getAttribute('description'));
$configObject = json_decode($config);
$configObject->password = $storedConfigObject->password;
$config = json_encode($configObject);
}
$current_user = User::currentUser();
$request->merge([
'description' => $config,
'user_id' => $current_user->getId(),
]);
if ($request->input('appid') === 'null') {
$request->merge([
'class' => null,
]);
} else {
$request->merge([
'class' => Application::classFromName($application->name),
]);
}
if ($id === null) {
$item = Item::create($request->all());
} else {
$item = Item::find($id);
$item->update($request->all());
}
$item->parents()->sync($request->tags);
return $item;
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return RedirectResponse
*/
public function store(Request $request): RedirectResponse
{
self::storelogic($request);
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.item_created'));
}
/**
* Display the specified resource.
*
* @param int $id
* @return void
*/
public function show(int $id): void
{
//
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
* @param Request $request * @param \Illuminate\Http\Request $request
* @param int $id * @param int $id
* @return RedirectResponse * @return \Illuminate\Http\Response
*/ */
public function update(Request $request, int $id): RedirectResponse public function update(Request $request, $id)
{ {
self::storelogic($request, $id); $this->storelogic($request, $id);
$route = route('dash', []); $route = route('dash', []);
return redirect($route) return redirect($route)
->with('success', __('app.alert.success.item_updated')); ->with('success',__('app.alert.success.item_updated'));
} }
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
* @param Request $request * @param int $id
* @param int $id * @return \Illuminate\Http\Response
* @return RedirectResponse
*/ */
public function destroy(Request $request, int $id): RedirectResponse public function destroy(Request $request, $id)
{ {
// //
$force = (bool)$request->input('force'); $force = (bool)$request->input('force');
if ($force) { if($force) {
Item::withTrashed() Item::withTrashed()
->where('id', $id) ->where('id', $id)
->forceDelete(); ->forceDelete();
@ -348,45 +298,59 @@ class ItemController extends Controller
} }
$route = route('items.index', []); $route = route('items.index', []);
return redirect($route)
return redirect($route) ->with('success',__('app.alert.success.item_deleted'));
->with('success', __('app.alert.success.item_deleted'));
} }
/** /**
* Restore the specified resource from soft deletion. * Restore the specified resource from soft deletion.
* *
* @param int $id * @param int $id
* @return RedirectResponse * @return \Illuminate\Http\Response
*/ */
public function restore(int $id): RedirectResponse public function restore($id)
{ {
// //
Item::withTrashed() Item::withTrashed()
->where('id', $id) ->where('id', $id)
->restore(); ->restore();
$route = route('items.index', []); $route = route('items.index', []);
return redirect($route) return redirect($route)
->with('success', __('app.alert.success.item_restored')); ->with('success',__('app.alert.success.item_restored'));
} }
/** /**
* Return details for supported apps * Return details for supported apps
* *
* @param Request $request * @return Json
* @return string|null
* @throws GuzzleException
*/ */
public function appload(Request $request): ?string public function appload(Request $request)
{ {
$output = []; $output = [];
$appid = $request->input('app'); $appid = $request->input('app');
if ($appid === 'null') { if($appid === "null") return null;
return null; /*$appname = $request->input('app');
} //die($appname);
$app_details = Application::where('name', $appname)->firstOrFail();
$appclass = $app_details->class();
$app = new $appclass;
// 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;
}*/
$output['config'] = null; $output['config'] = null;
$output['custom'] = null; $output['custom'] = null;
@ -394,129 +358,94 @@ class ItemController extends Controller
$app = Application::single($appid); $app = Application::single($appid);
$output = (array)$app; $output = (array)$app;
$appdetails = Application::getApp($appid); if((boolean)$app->enhanced === true) {
if ((bool)$app->enhanced === true) {
// if(!isset($app->config)) { // class based config // if(!isset($app->config)) { // class based config
$output['custom'] = className($appdetails->name) . '.config'; $appdetails = Application::getApp($appid);
$output['custom'] = className($appdetails->name).'.config';
// } // }
} }
$output['colour'] = ($app->tile_background == 'light') ? '#fafbfc' : '#161b1f'; $output['colour'] = ($app->tile_background == 'light') ? '#fafbfc' : '#161b1f';
$output['iconview'] = config('app.appsource').'icons/' . $app->icon;
if (strpos($app->icon, '://') !== false) { ;
$output['iconview'] = $app->icon;
} elseif (strpos($app->icon, 'icons/') !== false) {
// Private apps have the icon locally
$output['iconview'] = URL::to('/') . '/storage/' . $app->icon;
$output['icon'] = str_replace('icons/', '', $output['icon']);
} else {
$output['iconview'] = config('app.appsource') . 'icons/' . $app->icon;
}
return json_encode($output); return json_encode($output);
} }
/**
* @param Request $request
* @return void
*/
public function testConfig(Request $request) public function testConfig(Request $request)
{ {
$data = $request->input('data'); $data = $request->input('data');
//$url = $data[array_search('url', array_column($data, 'name'))]['value']; //$url = $data[array_search('url', array_column($data, 'name'))]['value'];
$single = Application::single($data['type']);
$app = $single->class; $app = $data['type'];
// If password is not resubmitted fill it from the database when in edit mode
if (array_key_exists('password', $data) &&
$data['password'] === null &&
array_key_exists('id', $data)
) {
$item = Item::find($data['id']);
if ($item) {
$itemConfig = $item->getConfig();
$data['password'] = $itemConfig->password;
}
}
$app_details = new $app(); $app_details = new $app();
$app_details->config = (object)$data; $app_details->config = (object)$data;
$app_details->test(); $app_details->test();
} }
/** public function execute($url, $attrs = [], $overridevars=false)
* @param $url
* @param array $attrs
* @param array|bool $overridevars
* @return ResponseInterface|null
* @throws GuzzleException
*/
public function execute($url, array $attrs = [], $overridevars = false): ?ResponseInterface
{ {
$res = null;
$vars = ($overridevars !== false) ? $vars = ($overridevars !== false) ?
$overridevars : [ $overridevars : [
'http_errors' => false, 'http_errors' => false,
'timeout' => 15, 'timeout' => 15,
'connect_timeout' => 15, 'connect_timeout' => 15,
'verify' => false, 'verify' => false
]; ];
$client = new Client($vars); $client = new Client($vars);
$method = 'GET'; $method = 'GET';
try { try {
return $client->request($method, $url, $attrs); return $client->request($method, $url, $attrs);
} catch (ConnectException $e) { } catch (\GuzzleHttp\Exception\ConnectException $e) {
Log::error('Connection refused'); Log::error("Connection refused");
Log::debug($e->getMessage()); Log::debug($e->getMessage());
} catch (ServerException $e) { } catch (\GuzzleHttp\Exception\ServerException $e) {
Log::debug($e->getMessage()); Log::debug($e->getMessage());
} }
return $res;
return null;
} }
/**
* @param $url
* @return StreamInterface
* @throws GuzzleException
*/
public function websitelookup($url): StreamInterface
{
$url = base64_decode($url);
$data = $this->execute($url);
public function websitelookup($url)
{
$url = \base64_decode($url);
$data = $this->execute($url);
return $data->getBody(); return $data->getBody();
} }
/**
* @param $id
* @return void
*/
public function getStats($id) public function getStats($id)
{ {
$item = Item::find($id); $item = Item::find($id);
$config = $item->getconfig(); $config = $item->getconfig();
if (isset($item->class)) { if(isset($item->class)) {
$application = new $item->class; $application = new $item->class;
$application->config = $config; $application->config = $config;
echo $application->livestats(); echo $application->livestats();
} }
} }
/**
* @return \Illuminate\Contracts\Foundation\Application|RedirectResponse|Redirector
*/
public function checkAppList() public function checkAppList()
{ {
ProcessApps::dispatch(); ProcessApps::dispatch();
$route = route('items.index'); $route = route('items.index');
return redirect($route) return redirect($route)
->with('success', __('app.alert.success.updating')); ->with('success', __('app.alert.success.updating'));
} }
} }

View file

@ -1,111 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Item;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Collection;
class ItemRestController extends Controller
{
public function __construct()
{
parent::__construct();
$this->middleware('allowed');
}
/**
* Display a listing of the resource.
*
* @return Collection
*/
public function index()
{
$columns = [
'title',
'colour',
'url',
'description',
'appid',
'appdescription',
];
return Item::select($columns)
->where('deleted_at', null)
->where('type', '0')
->orderBy('order', 'asc')
->get();
}
/**
* Show the form for creating a new resource.
*
* @return void
*/
public function create()
{
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return object
*/
public function store(Request $request): object
{
$item = ItemController::storelogic($request);
if ($item) {
return (object) ['status' => 'OK'];
}
return (object) ['status' => 'FAILED'];
}
/**
* Display the specified resource.
*
* @param Item $item
* @return Response
*/
public function show(Item $item)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param Item $item
* @return Response
*/
public function edit(Item $item)
{
//
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param Item $item
* @return Response
*/
public function update(Request $request, Item $item)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param Item $item
* @return Response
*/
public function destroy(Item $item)
{
//
}
}

View file

@ -2,18 +2,12 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Search;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Routing\Redirector; use App\Http\Controllers\Controller;
use App\Search;
class SearchController extends Controller class SearchController extends Controller
{ {
/**
* @param Request $request
* @return Application|RedirectResponse|Redirector|mixed|void
*/
public function index(Request $request) public function index(Request $request)
{ {
$requestprovider = $request->input('provider'); $requestprovider = $request->input('provider');
@ -21,9 +15,9 @@ class SearchController extends Controller
$provider = Search::providerDetails($requestprovider); $provider = Search::providerDetails($requestprovider);
if ($provider->type == 'standard') { if($provider->type == 'standard') {
return redirect($provider->url.'?'.$provider->query.'='.urlencode($query)); return redirect($provider->url.'?'.$provider->query.'='.urlencode($query));
} elseif ($provider->type == 'external') { } elseif($provider->type == 'external') {
$class = new $provider->class; $class = new $provider->class;
//print_r($provider); //print_r($provider);
return $class->getResults($query, $provider); return $class->getResults($query, $provider);

View file

@ -2,25 +2,24 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Setting; use App\Setting;
use App\SettingGroup; use App\SettingGroup;
use Exception; use App\User;
use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Auth;
use Illuminate\Http\RedirectResponse; use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class SettingsController extends Controller class SettingsController extends Controller
{ {
public function __construct() public function __construct()
{ {
parent::__construct();
$this->middleware('allowed'); $this->middleware('allowed');
} }
/** /**
* @return View * @return \Illuminate\View\View
*/ */
public function index(): View public function index()
{ {
$settings = SettingGroup::with([ $settings = SettingGroup::with([
'settings', 'settings',
@ -34,108 +33,95 @@ class SettingsController extends Controller
/** /**
* @param int $id * @param int $id
* *
* @return RedirectResponse|View * @return \Illuminate\Http\RedirectResponse
*/ */
public function edit(int $id) public function edit($id)
{ {
$setting = Setting::find($id); $setting = Setting::find($id);
//die("s: ".$setting->label); //die("s: ".$setting->label);
if ((bool) $setting->system === true) { if((bool)$setting->system === true) return abort(404);
return abort(404);
}
if (! is_null($setting)) { if (!is_null($setting)) {
return view('settings.edit')->with([ return view('settings.edit')->with([
'setting' => $setting, 'setting' => $setting,
]); ]);
} else { } else {
$route = route('settings.list', []); $route = route('settings.list', []);
return redirect($route)
return redirect($route)
->with([ ->with([
'errors' => collect([__('app.alert.error.not_exist')]), 'error' => __('app.alert.error.not_exist'),
]); ]);
} }
} }
/** /**
* @param Request $request
* @param int $id * @param int $id
* *
* @return RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function update(Request $request, int $id): RedirectResponse public function update(Request $request, $id)
{ {
$setting = Setting::find($id); $setting = Setting::find($id);
$user = $this->user(); $user = $this->user();
$route = route('settings.index', []);
try { if (!is_null($setting)) {
if (is_null($setting)) { $data = Setting::getInput($request);
throw new Exception('not_exists');
}
if ($setting->type === 'image') { $setting_value = null;
$validatedData = $request->validate([
'value' => 'image'
]);
if (!$request->hasFile('value')) { if ($setting->type == 'image') {
throw new \Exception(
'file_too_big'
); if($request->hasFile('value')) {
$path = $request->file('value')->store('backgrounds');
$setting_value = $path;
} }
$path = $request->file('value')->store('backgrounds');
if ($path === null) {
throw new \Exception('file_not_stored');
}
$setting_value = $path;
} else { } else {
$data = Setting::getInput($request);
$setting_value = $data->value; $setting_value = $data->value;
} }
$user->settings()->detach($setting->id); $user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => $setting_value]); $user->settings()->save($setting, ['uservalue' => $setting_value]);
return redirect($route) $route = route('settings.index', []);
->with([ return redirect($route)
'success' => __('app.alert.success.setting_updated'), ->with([
]); 'success' => __('app.alert.success.setting_updated'),
} catch (Exception $e) { ]);
return redirect($route) } else {
->with([ $route = route('settings.index', []);
'errors' => collect([__('app.alert.error.'.$e->getMessage())]), return redirect($route)
]); ->with([
'error' => __('app.alert.error.not_exist'),
]);
} }
} }
/** /**
* @param int $id * @param int $id
* *
* @return RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function clear(int $id): RedirectResponse public function clear($id)
{ {
$user = $this->user(); $user = $this->user();
$setting = Setting::find($id); $setting = Setting::find($id);
if ((bool) $setting->system !== true) { if((bool)$setting->system !== true) {
$user->settings()->detach($setting->id); $user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => '']); $user->settings()->save($setting, ['uservalue' => '']);
} }
$route = route('settings.index', []); $route = route('settings.index', []);
return redirect($route)
return redirect($route)
->with([ ->with([
'success' => __('app.alert.success.setting_updated'), 'success' => __('app.alert.success.setting_updated'),
]); ]);
} }
public function search(Request $request) public function search(Request $request)
{ {
} }
} }

View file

@ -2,13 +2,10 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Item; use App\Item;
use App\User; use App\User;
use Illuminate\Contracts\Foundation\Application; use DB;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class TagController extends Controller class TagController extends Controller
{ {
@ -16,19 +13,18 @@ class TagController extends Controller
{ {
$this->middleware('allowed'); $this->middleware('allowed');
} }
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @return Application|Factory|View * @return \Illuminate\Http\Response
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$trash = (bool) $request->input('trash'); $trash = (bool)$request->input('trash');
$data['apps'] = Item::ofType('tag')->where('id', '>', 0)->orderBy('title', 'asc')->get(); $data['apps'] = Item::ofType('tag')->where('id', '>', 0)->orderBy('title', 'asc')->get();
$data['trash'] = Item::ofType('tag')->where('id', '>', 0)->onlyTrashed()->get(); $data['trash'] = Item::ofType('tag')->where('id', '>', 0)->onlyTrashed()->get();
if ($trash) { if($trash) {
return view('tags.trash', $data); return view('tags.trash', $data);
} else { } else {
return view('tags.list', $data); return view('tags.list', $data);
@ -38,36 +34,34 @@ class TagController extends Controller
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
* *
* @return Application|Factory|View * @return \Illuminate\Http\Response
*/ */
public function create() public function create()
{ {
$data = []; $data = [];
return view('tags.create', $data); return view('tags.create', $data);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
* @param Request $request * @param \Illuminate\Http\Request $request
* @return RedirectResponse * @return \Illuminate\Http\Response
*/ */
public function store(Request $request): RedirectResponse public function store(Request $request)
{ {
$validatedData = $request->validate([ $validatedData = $request->validate([
'title' => 'required|max:255', 'title' => 'required|max:255',
'file' => 'image'
]); ]);
if ($request->hasFile('file')) { if($request->hasFile('file')) {
$path = $request->file('file')->store('icons'); $path = $request->file('file')->store('icons');
$request->merge([ $request->merge([
'icon' => $path, 'icon' => $path
]); ]);
} }
$slug = str_slug($request->title, '-', 'en_US'); $slug = str_slug($request->title, '-');
$current_user = User::currentUser(); $current_user = User::currentUser();
@ -75,13 +69,12 @@ class TagController extends Controller
$request->merge([ $request->merge([
'type' => '1', 'type' => '1',
'url' => $slug, 'url' => $slug,
'user_id' => $current_user->getId(), 'user_id' => $current_user->id
]); ]);
//die(print_r($request->all())); //die(print_r($request->all()));
Item::create($request->all()); Item::create($request->all());
$route = route('dash', []); $route = route('dash', []);
return redirect($route) return redirect($route)
->with('success', __('app.alert.success.tag_created')); ->with('success', __('app.alert.success.tag_created'));
} }
@ -89,134 +82,121 @@ class TagController extends Controller
/** /**
* Display the specified resource. * Display the specified resource.
* *
* @param $slug * @param int $id
* @return View * @return \Illuminate\Http\Response
*/ */
public function show($slug): View public function show($slug)
{ {
$item = Item::whereUrl($slug)->first(); $item = Item::whereUrl($slug)->first();
//print_r($item); //print_r($item);
$data['apps'] = $item->children()->pinned()->orderBy('order', 'asc')->get(); $data['apps'] = $item->children()->pinned()->orderBy('order', 'asc')->get();
$data['tag'] = $item->id; $data['tag'] = $item->id;
$data['all_apps'] = $item->children; $data['all_apps'] = $item->children;
return view('welcome', $data); return view('welcome', $data);
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
* *
* @param int $id * @param int $id
* @return View * @return \Illuminate\Http\Response
*/ */
public function edit(int $id): View public function edit($id)
{ {
// Get the item // Get the item
$item = Item::find($id); $item = Item::find($id);
// show the edit form and pass the nerd // show the edit form and pass the nerd
return view('tags.edit') return view('tags.edit')
->with('item', $item); ->with('item', $item);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
* @param Request $request * @param \Illuminate\Http\Request $request
* @param int $id * @param int $id
* @return RedirectResponse * @return \Illuminate\Http\Response
*/ */
public function update(Request $request, int $id): RedirectResponse public function update(Request $request, $id)
{ {
$validatedData = $request->validate([ $validatedData = $request->validate([
'title' => 'required|max:255', 'title' => 'required|max:255',
'file' => 'image'
]); ]);
if ($request->hasFile('file')) { if($request->hasFile('file')) {
$path = $request->file('file')->store('icons'); $path = $request->file('file')->store('icons');
$request->merge([ $request->merge([
'icon' => $path, 'icon' => $path
]); ]);
} }
$slug = str_slug($request->title, '-', 'en_US'); $slug = str_slug($request->title, '-');
// set item type to tag // set item type to tag
$request->merge([ $request->merge([
'url' => $slug, 'url' => $slug
]); ]);
Item::find($id)->update($request->all()); Item::find($id)->update($request->all());
$route = route('dash', []); $route = route('dash', []);
return redirect($route) return redirect($route)
->with('success', __('app.alert.success.tag_updated')); ->with('success',__('app.alert.success.tag_updated'));
} }
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
* @param Request $request * @param int $id
* @param int $id * @return \Illuminate\Http\Response
* @return RedirectResponse
*/ */
public function destroy(Request $request, int $id): RedirectResponse public function destroy(Request $request, $id)
{ {
// //
$force = (bool) $request->input('force'); $force = (bool)$request->input('force');
if ($force) { if($force) {
Item::withTrashed() Item::withTrashed()
->where('id', $id) ->where('id', $id)
->forceDelete(); ->forceDelete();
} else { } else {
Item::find($id)->delete(); Item::find($id)->delete();
} }
$route = route('tags.index', []); $route = route('tags.index', []);
return redirect($route) return redirect($route)
->with('success', __('app.alert.success.item_deleted')); ->with('success',__('app.alert.success.item_deleted'));
} }
/** /**
* Restore the specified resource from soft deletion. * Restore the specified resource from soft deletion.
* *
* @param int $id * @param int $id
* @return RedirectResponse * @return \Illuminate\Http\Response
*/ */
public function restore(int $id): RedirectResponse public function restore($id)
{ {
// //
Item::withTrashed() Item::withTrashed()
->where('id', $id) ->where('id', $id)
->restore(); ->restore();
$route = route('tags.index', []); $route = route('tags.index', []);
return redirect($route) return redirect($route)
->with('success', __('app.alert.success.item_restored')); ->with('success',__('app.alert.success.item_restored'));
} }
/** public function add($tag, $item)
* Add item to tag
*
* @param $tag
* @param $item
* @return int 1|0
*/
public function add($tag, $item): int
{ {
$output = 0;
$tag = Item::find($tag); $tag = Item::find($tag);
$item = Item::find($item); $item = Item::find($item);
if ($tag && $item) { if($tag && $item) {
// only add items, not cats // only add items, not cats
if ((int) $item->type === 0) { if((int)$item->type === 0) {
$tag->children()->attach($item); $tag->children()->attach($item);
return 1; return 1;
} }
} }
return $output;
return 0;
} }
} }

View file

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

View file

@ -2,11 +2,9 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use App\User;
use Closure; use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use App\User;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Session; use Session;
@ -15,45 +13,36 @@ class CheckAllowed
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
* @param Request $request * @param \Illuminate\Http\Request $request
* @param Closure $next * @param \Closure $next
* @return mixed * @return mixed
* @throws AuthenticationException
*/ */
public function handle(Request $request, Closure $next) public function handle($request, Closure $next)
{ {
$route = Route::currentRouteName(); $route = Route::currentRouteName();
$current_user = User::currentUser(); $current_user = User::currentUser();
// Non admin users can't access users management if(str_is('users*', $route)) {
if (str_is('users*', $route)) { if($current_user->id !== 1) {
if ($current_user->getId() !== 1) {
return redirect()->route('dash'); return redirect()->route('dash');
} }
} }
// Public access to frontpage if($route == 'dash') {
if ($route === 'dash' || $route === 'tags.show') { //print_r(User::all());
if ((bool)$current_user->public_front === true) { //die("here".var_dump($current_user->password));
return $next($request); if((bool)$current_user->public_front === true) return $next($request);
}
} }
// Continue with passwordless user if(empty($current_user->password)) return $next($request);
if (empty($current_user->password)) {
return $next($request);
}
// Check if user is logged in as $current_user // Check if user is logged in as $current_user
if (Auth::check()) { if (Auth::check()) {
$loggedin_user = Auth::user(); $loggedin_user = Auth::user();
if ($loggedin_user->id === $current_user->getId()) { if($loggedin_user->id === $current_user->id) return $next($request);
return $next($request);
}
} }
// Redirect to login return Auth::authenticate();
Auth::authenticate();
return redirect()->route('user.select');
} }
} }

View file

@ -3,7 +3,6 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Closure; use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated class RedirectIfAuthenticated
@ -11,12 +10,12 @@ class RedirectIfAuthenticated
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
* @param Request $request * @param \Illuminate\Http\Request $request
* @param Closure $next * @param \Closure $next
* @param string|null $guard * @param string|null $guard
* @return mixed * @return mixed
*/ */
public function handle(Request $request, Closure $next, string $guard = null) public function handle($request, Closure $next, $guard = null)
{ {
if (Auth::guard($guard)->check()) { if (Auth::guard($guard)->check()) {
return redirect()->intended(); return redirect()->intended();

View file

@ -2,8 +2,8 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware class TrustProxies extends Middleware
{ {
@ -12,7 +12,7 @@ class TrustProxies extends Middleware
* *
* @var array * @var array
*/ */
protected $proxies = ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8', '127.0.0.1']; 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. * The current proxy header mappings.

View file

@ -3,6 +3,7 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
use Symfony\Component\HttpFoundation\Cookie;
class VerifyCsrfToken extends Middleware class VerifyCsrfToken extends Middleware
{ {
@ -18,4 +19,40 @@ class VerifyCsrfToken extends Middleware
'test_config', 'test_config',
//'get_stats' //'get_stats'
]; ];
/**
* Add the CSRF token to the response cookies.
*
* @param \Illuminate\Http\Request $request
* @param \Symfony\Component\HttpFoundation\Response $response
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function addCookieToResponse($request, $response)
{
$config = config('session');
if ($response instanceof Responsable) {
$response = $response->toResponse($request);
}
$response->headers->setCookie(
new Cookie(
'HEIMDALL-XSRF-TOKEN', $request->session()->token(), $this->availableAt(60 * $config['lifetime']),
$config['path'], $config['domain'], $config['secure'], false, false, $config['same_site'] ?? null
)
);
return $response;
}
/**
* Determine if the cookie contents should be serialized.
*
* @return bool
*/
public static function serialized()
{
return EncryptCookies::serialized('HEIMDALL-XSRF-TOKEN');
}
} }

View file

@ -2,122 +2,51 @@
namespace App; namespace App;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use stdClass;
use Symfony\Component\ClassLoader\ClassMapGenerator; use Symfony\Component\ClassLoader\ClassMapGenerator;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;
use App\User;
use App\ItemTag;
use App\Application;
// @codingStandardsIgnoreStart
/**
* App\Item
*
* @property int $id
* @property string $title
* @property string|null $colour
* @property string|null $icon
* @property string $url
* @property string|null $description
* @property int $pinned
* @property int $order
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property int $type
* @property int $user_id
* @property string|null $class
* @property string|null $appid
* @property string|null $appdescription
* @property-read \Illuminate\Database\Eloquent\Collection|Item[] $children
* @property-read int|null $children_count
* @property-read string $droppable
* @property-read \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\UrlGenerator|mixed|string $link
* @property-read string $link_icon
* @property-read string $link_target
* @property-read string $link_type
* @property-read \Illuminate\Database\Eloquent\Collection|Item[] $parents
* @property-read int|null $parents_count
* @property-read \App\User|null $user
* @method static \Database\Factories\ItemFactory factory(...$parameters)
* @method static Builder|Item newModelQuery()
* @method static Builder|Item newQuery()
* @method static Builder|Item ofType($type)
* @method static \Illuminate\Database\Query\Builder|Item onlyTrashed()
* @method static Builder|Item pinned()
* @method static Builder|Item query()
* @method static Builder|Item whereAppdescription($value)
* @method static Builder|Item whereAppid($value)
* @method static Builder|Item whereClass($value)
* @method static Builder|Item whereColour($value)
* @method static Builder|Item whereCreatedAt($value)
* @method static Builder|Item whereDeletedAt($value)
* @method static Builder|Item whereDescription($value)
* @method static Builder|Item whereIcon($value)
* @method static Builder|Item whereId($value)
* @method static Builder|Item whereOrder($value)
* @method static Builder|Item wherePinned($value)
* @method static Builder|Item whereTitle($value)
* @method static Builder|Item whereType($value)
* @method static Builder|Item whereUpdatedAt($value)
* @method static Builder|Item whereUrl($value)
* @method static Builder|Item whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|Item withTrashed()
* @method static \Illuminate\Database\Query\Builder|Item withoutTrashed()
* @mixin \Eloquent
*/
// @codingStandardsIgnoreEnd
class Item extends Model class Item extends Model
{ {
use SoftDeletes; use SoftDeletes;
use HasFactory;
/**
* @return void
*/
protected static function boot() protected static function boot()
{ {
parent::boot(); parent::boot();
static::addGlobalScope('user_id', function (Builder $builder) { static::addGlobalScope('user_id', function (Builder $builder) {
$current_user = User::currentUser(); $current_user = User::currentUser();
if ($current_user) { if($current_user) {
$builder->where('user_id', $current_user->getId())->orWhere('user_id', 0); $builder->where('user_id', $current_user->id)->orWhere('user_id', 0);
} else { } else {
$builder->where('user_id', 0); $builder->where('user_id', 0);
} }
}); });
} }
//
protected $fillable = [ protected $fillable = [
'title', 'title', 'url', 'colour', 'icon', 'appdescription', 'description', 'pinned', 'order', 'type', 'class', 'user_id', 'appid'
'url',
'colour',
'icon',
'appdescription',
'description',
'pinned',
'order',
'type',
'class',
'user_id',
'tag_id',
'appid',
]; ];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['deleted_at'];
/** /**
* Scope a query to only include pinned items. * Scope a query to only include pinned items.
* *
* @param Builder $query * @param \Illuminate\Database\Eloquent\Builder $query
* @return Builder * @return \Illuminate\Database\Eloquent\Builder
*/ */
public function scopePinned(Builder $query): Builder public function scopePinned($query)
{ {
return $query->where('pinned', 1); return $query->where('pinned', 1);
} }
@ -125,149 +54,100 @@ class Item extends Model
public static function checkConfig($config) public static function checkConfig($config)
{ {
// die(print_r($config)); // die(print_r($config));
if (empty($config)) { if(empty($config)) {
$config = null; $config = null;
} else { } else {
$config = json_encode($config); $config = json_encode($config);
} }
return $config; return $config;
} }
public function tags() public function tags()
{ {
$id = $this->id; $id = $this->id;
$tags = ItemTag::select('tag_id')->where('item_id', $id)->pluck('tag_id')->toArray(); $tags = ItemTag::select('tag_id')->where('item_id', $id)->pluck('tag_id')->toArray();
$tagdetails = self::select('id', 'title', 'url', 'pinned')->whereIn('id', $tags)->get(); $tagdetails = Item::select('id', 'title', 'url', 'pinned')->whereIn('id', $tags)->get();
//print_r($tags); //print_r($tags);
if (in_array(0, $tags)) { if(in_array(0, $tags)) {
$details = new self([ $details = new Item([
'id' => 0, "id" => 0,
'title' => __('app.dashboard'), "title" => __('app.dashboard'),
'url' => '', "url" => '',
'pinned' => 0, "pinned" => 0
]); ]);
$tagdetails->prepend($details); $tagdetails->prepend($details);
} }
return $tagdetails; return $tagdetails;
} }
/** public function parents()
* @return string
*/
public function getTagClass(): string
{ {
$tags = $this->tags(); return $this->belongsToMany('App\Item', 'item_tag', 'item_id', 'tag_id');
$slugs = []; }
public function children()
foreach ($tags as $tag) { {
if ($tag->url) { return $this->belongsToMany('App\Item', 'item_tag', 'tag_id', 'item_id');
$slugs[] = 'tag-'.$tag->url;
}
}
return implode(' ', $slugs);
} }
/**
* @return BelongsToMany
*/
public function parents(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_tag', 'item_id', 'tag_id');
}
/**
* @return BelongsToMany
*/
public function children(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_tag', 'tag_id', 'item_id');
}
/**
* @return \Illuminate\Contracts\Foundation\Application|UrlGenerator|mixed|string
*/
public function getLinkAttribute() public function getLinkAttribute()
{ {
if ((int) $this->type === 1) { if((int)$this->type === 1) {
return url('tag/'.$this->url); return url('tag/'.$this->url);
} else { } else {
return $this->url; return $this->url;
} }
} }
/** public function getDroppableAttribute()
* @return string
*/
public function getDroppableAttribute(): string
{ {
if ((int) $this->type === 1) { if((int)$this->type === 1) {
return ' droppable'; return ' droppable';
} else { } else {
return ''; return '';
} }
} }
/** public function getLinkTargetAttribute()
* @return string
*/
public function getLinkTargetAttribute(): string
{ {
$target = Setting::fetch('window_target'); $target = Setting::fetch('window_target');
if ((int) $this->type === 1 || $target === 'current') { if((int)$this->type === 1 || $target === 'current') {
return ''; return '';
} else { } else {
return ' target="'.$target.'"'; return ' target="' . $target . '"';
} }
} }
/** public function getLinkIconAttribute()
* @return string
*/
public function getLinkIconAttribute(): string
{ {
if ((int) $this->type === 1) { if((int)$this->type === 1) {
return 'fa-tag'; return 'fa-tag';
} else { } else {
return 'fa-arrow-alt-to-right'; return 'fa-arrow-alt-to-right';
} }
} }
public function getLinkTypeAttribute()
/**
* @return string
*/
public function getLinkTypeAttribute(): string
{ {
if ((int) $this->type === 1) { if((int)$this->type === 1) {
return 'tags'; return 'tags';
} else { } else {
return 'items'; return 'items';
} }
} }
/**
* @param $class
* @return false|mixed|string
*/
public static function nameFromClass($class) public static function nameFromClass($class)
{ {
$explode = explode('\\', $class); $explode = explode('\\', $class);
$name = end($explode); $name = end($explode);
return $name; return $name;
} }
/**
* @param $query
* @param $type
* @return mixed
*/
public function scopeOfType($query, $type) public function scopeOfType($query, $type)
{ {
switch ($type) { switch($type) {
case 'item': case 'item':
$typeid = 0; $typeid = 0;
break; break;
@ -279,10 +159,7 @@ class Item extends Model
return $query->where('type', $typeid); return $query->where('type', $typeid);
} }
/** public function enhanced()
* @return bool
*/
public function enhanced(): bool
{ {
/*if(isset($this->class) && !empty($this->class)) { /*if(isset($this->class) && !empty($this->class)) {
$app = new $this->class; $app = new $this->class;
@ -293,118 +170,89 @@ class Item extends Model
return $this->description !== null; return $this->description !== null;
} }
/** public static function isEnhanced($class)
* @param $class
* @return bool
*/
public static function isEnhanced($class): bool
{ {
if (!class_exists($class, false) || $class === null || $class === 'null') { if($class === null || $class === 'null') return false;
return false;
}
$app = new $class; $app = new $class;
return (bool)($app instanceof \App\EnhancedApps);
return (bool) ($app instanceof EnhancedApps);
} }
/**
* @param $class
* @return false|mixed
*/
public static function isSearchProvider($class) public static function isSearchProvider($class)
{ {
if (!class_exists($class, false) || $class === null || $class === 'null') {
return false;
}
$app = new $class; $app = new $class;
return ((bool)($app instanceof \App\SearchInterface)) ? $app : false;
return ((bool) ($app instanceof SearchInterface)) ? $app : false;
} }
/** public function enabled()
* @return bool
*/
public function enabled(): bool
{ {
if ($this->enhanced()) { if($this->enhanced()) {
$config = $this->getconfig(); $config = $this->getconfig();
if ($config) { if($config) {
return (bool) $config->enabled; return (bool) $config->enabled;
} }
} }
return false; return false;
} }
/**
* @return mixed|stdClass
*/
public function getconfig() public function getconfig()
{ {
// $explode = explode('\\', $this->class); // $explode = explode('\\', $this->class);
if (! isset($this->description) || empty($this->description)) { if(!isset($this->description) || empty($this->description)) {
$config = new stdClass; $config = new \stdClass;
// $config->name = end($explode); // $config->name = end($explode);
$config->enabled = false; $config->enabled = false;
$config->override_url = null; $config->override_url = null;
$config->apikey = null; $config->apikey = null;
return $config; return $config;
} }
$config = json_decode($this->description); $config = json_decode($this->description);
// $config->name = end($explode); // $config->name = end($explode);
$config->url = $this->url; $config->url = $this->url;
if (isset($config->override_url) && ! empty($config->override_url)) { if(isset($config->override_url) && !empty($config->override_url)) {
$config->url = $config->override_url; $config->url = $config->override_url;
} else { } else {
$config->override_url = null; $config->override_url = null;
} }
return $config; return $config;
} }
/** public static function applicationDetails($class)
* @param $class
* @return Application|null
*/
public static function applicationDetails($class): ?Application
{ {
if (! empty($class)) { if(!empty($class)) {
$name = self::nameFromClass($class); $name = self::nameFromClass($class);
$application = Application::where('name', $name)->first(); $application = Application::where('name', $name)->first();
if ($application) { if($application) return $application;
return $application;
}
} }
return null; return false;
} }
/** public static function getApplicationDescription($class)
* @param $class
* @return string
*/
public static function getApplicationDescription($class): string
{ {
$details = self::applicationDetails($class); $details = self::applicationDetails($class);
if ($details !== null) { if($details !== false) {
return $details->description.' - '.$details->license; return $details->description.' - '.$details->license;
} }
return ''; return '';
} }
/** /**
* Get the user that owns the item. * Get the user that owns the item.
*
* @return BelongsTo
*/ */
public function user(): BelongsTo public function user()
{ {
return $this->belongsTo(User::class); return $this->belongsTo('App\User');
} }
} }

View file

@ -2,26 +2,9 @@
namespace App; namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Eloquent\Relations\Pivot;
/**
* App\ItemTag
*
* @property int $item_id
* @property int $tag_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag query()
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereItemId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereTagId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereUpdatedAt($value)
* @mixin \Eloquent
*/
class ItemTag extends Pivot class ItemTag extends Pivot
{ {
use HasFactory;
} }

View file

@ -2,20 +2,16 @@
namespace App\Jobs; namespace App\Jobs;
use App\Application;
use App\Item;
use App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use App\Application;
use App\SupportedApps;
class ProcessApps implements ShouldQueue, ShouldBeUnique class ProcessApps implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -33,29 +29,18 @@ class ProcessApps implements ShouldQueue, ShouldBeUnique
* Execute the job. * Execute the job.
* *
* @return void * @return void
* @throws GuzzleException
*/ */
public function handle() public function handle()
{ {
Log::debug('Process Apps dispatched');
$localapps = Application::whereNull('class')->get(); $localapps = Application::whereNull('class')->get();
$json = SupportedApps::getList()->getBody(); $json = SupportedApps::getList()->getBody();
Storage::disk('local')->put('supportedapps.json', $json); Storage::disk('local')->put('supportedapps.json', $json);
foreach ($localapps as $app) { foreach($localapps as $app) {
$app->class = $app->class(); $app->class = $app->class();
$app->save(); $app->save();
} }
$items = Item::whereNotNull('class')->get();
foreach ($items as $item) {
if (! file_exists(app_path('SupportedApps/'.Item::nameFromClass($item->class)))) {
$app = Application::where('class', $item->class)->first();
if ($app) {
Application::getApp($app->appid);
}
}
}
} }
} }

View file

@ -1,60 +0,0 @@
<?php
namespace App\Jobs;
use App\Application;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class UpdateApps implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
* @throws GuzzleException
*/
public function handle()
{
Log::debug('Update of all apps triggered!');
$apps = Application::all('appid')->toArray();
// We onl update the apps that are actually in use by items
// 1 sec delay after each update to throttle the requests
foreach ($apps as $appKey => $app) {
Application::getApp($app['appid']);
sleep(1);
}
Log::debug('Update of all apps finished!');
Cache::lock('updateApps')->forceRelease();
}
/**
* @return void
*/
public function failed($exception)
{
Cache::lock('updateApps')->forceRelease();
}
}

View file

@ -2,18 +2,13 @@
namespace App\Providers; namespace App\Providers;
use App\Application; use Illuminate\Support\ServiceProvider;
use App\Jobs\ProcessApps; use Artisan;
use App\Jobs\UpdateApps; use Schema;
use App\Setting; use App\Setting;
use App\User; use App\User;
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider; use App\Application;
use Illuminate\Support\Facades\Artisan; use App\Jobs\ProcessApps;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
@ -24,46 +19,75 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
if (! class_exists('ZipArchive')) {
die('You are missing php-zip'); if(!is_file(base_path('.env'))) {
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'));
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
//Cache
//Artisan::call('config:cache');
//Artisan::call('route:cache');
}
if(is_file(database_path('app.sqlite'))) {
if(Schema::hasTable('settings')) {
// check version to see if an upgrade is needed
$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));
}
} }
$this->createEnvFile(); if(!is_file(public_path('storage/.gitignore'))) {
$this->setupDatabase();
if (! is_file(public_path('storage/.gitignore'))) {
Artisan::call('storage:link'); Artisan::call('storage:link');
\Session::put('current_user', null); \Session::put('current_user', null);
} }
$applications = Application::all();
if ($applications->count() <= 0) {
ProcessApps::dispatch();
}
$lang = Setting::fetch('language'); $lang = Setting::fetch('language');
\App::setLocale($lang); \App::setLocale($lang);
$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 // User specific settings need to go here as session isn't available at this point in the app
view()->composer('*', function ($view) { view()->composer('*', function ($view)
if (isset($_SERVER['HTTP_AUTHORIZATION']) && ! empty($_SERVER['HTTP_AUTHORIZATION'])) { {
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
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))); explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
} }
if (! \Auth::check()) { if(!\Auth::check()) {
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])
&& ! empty($_SERVER['PHP_AUTH_USER']) && ! empty($_SERVER['PHP_AUTH_PW'])) { && !empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
$credentials = ['username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']]; $credentials = ['username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']];
if (\Auth::attempt($credentials, true)) { if (\Auth::attempt($credentials, true)) {
// Authentication passed... // Authentication passed...
$user = \Auth::user(); $user = \Auth::user();
//\Session::put('current_user', $user); //\Session::put('current_user', $user);
session(['current_user' => $user]); session(['current_user' => $user]);
} }
} elseif (isset($_SERVER['REMOTE_USER']) && ! empty($_SERVER['REMOTE_USER'])) { }
elseif(isset($_SERVER['REMOTE_USER']) && !empty($_SERVER['REMOTE_USER'])) {
$user = User::where('username', $_SERVER['REMOTE_USER'])->first(); $user = User::where('username', $_SERVER['REMOTE_USER'])->first();
if ($user) { if ($user) {
\Auth::login($user, true); \Auth::login($user, true);
@ -72,51 +96,52 @@ class AppServiceProvider extends ServiceProvider
} }
} }
$alt_bg = ''; $alt_bg = '';
$trianglify = 'false'; if($bg_image = Setting::fetch('background_image')) {
$trianglify_seed = null;
if (Setting::fetch('trianglify')) {
$trianglify = 'true';
$trianglify_seed = Setting::fetch('trianglify_seed');
} elseif ($bg_image = Setting::fetch('background_image')) {
$alt_bg = ' style="background-image: url(storage/'.$bg_image.')"'; $alt_bg = ' style="background-image: url(storage/'.$bg_image.')"';
} }
$allusers = User::all(); $allusers = User::all();
$current_user = User::currentUser(); $current_user = User::currentUser();
$view->with('alt_bg', $alt_bg); $view->with('alt_bg', $alt_bg );
$view->with('trianglify', $trianglify); $view->with('allusers', $allusers );
$view->with('trianglify_seed', $trianglify_seed); $view->with('current_user', $current_user );
$view->with('allusers', $allusers);
$view->with('current_user', $current_user);
});
});
$this->app['view']->addNamespace('SupportedApps', app_path('SupportedApps')); $this->app['view']->addNamespace('SupportedApps', app_path('SupportedApps'));
if (env('FORCE_HTTPS') === true) { if (env('FORCE_HTTPS') === true) {
\URL::forceScheme('https'); \URL::forceScheme('https');
} }
if (env('APP_URL') != 'http://localhost') { if(env('APP_URL') != 'http://localhost') {
\URL::forceRootUrl(env('APP_URL')); \URL::forceRootUrl(env('APP_URL'));
} }
} }
/** /**
* Generate app key if missing and .env exists * Generate app key if missing and .env exists
* *
* @return void * @return void
*/ */
public function genKey() public function genKey()
{ {
if (is_file(base_path('.env'))) { if(is_file(base_path('.env'))) {
if (empty(env('APP_KEY'))) { if(empty(env('APP_KEY'))) {
Artisan::call('key:generate', ['--force' => true, '--no-interaction' => true]); Artisan::call('key:generate', array('--force' => true, '--no-interaction' => true));
} }
} }
} }
/** /**
* Register any application services. * Register any application services.
* *
@ -124,78 +149,8 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function register() public function register()
{ {
if ($this->app->isLocal()) {
$this->app->register(IdeHelperServiceProvider::class);
}
$this->app->singleton('settings', function () { $this->app->singleton('settings', function () {
return new Setting(); return new Setting();
}); });
} }
/**
* Check if database needs an update or do first time database setup
*
* @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function setupDatabase(): void
{
$db_type = config()->get('database.default');
if ($db_type == 'sqlite') {
$db_file = database_path(env('DB_DATABASE', 'app.sqlite'));
if (! is_file($db_file)) {
touch($db_file);
}
}
if ($this->needsDBUpdate()) {
Artisan::call('migrate', ['--path' => 'database/migrations', '--force' => true, '--seed' => true]);
ProcessApps::dispatchSync();
$this->updateApps();
}
}
/**
* @return void
*/
public function createEnvFile(): void
{
if (!is_file(base_path('.env'))) {
copy(base_path('.env.example'), base_path('.env'));
}
$this->genKey();
}
/**
* @return bool
*/
private function needsDBUpdate(): bool
{
if (!Schema::hasTable('settings')) {
return true;
}
$db_version = Setting::_fetch('version');
$app_version = config('app.version');
return version_compare($app_version, $db_version) === 1;
}
/**
* @return void
*/
private function updateApps()
{
// This lock ensures that the job is not invoked multiple times.
// In 5 minutes all app updates should be finished.
$lock = Cache::lock('updateApps', 5*60);
if ($lock->get()) {
UpdateApps::dispatchAfterResponse();
}
}
} }

View file

@ -2,6 +2,7 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider class AuthServiceProvider extends ServiceProvider

View file

@ -2,8 +2,8 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Broadcast;
class BroadcastServiceProvider extends ServiceProvider class BroadcastServiceProvider extends ServiceProvider
{ {

View file

@ -2,6 +2,7 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider class EventServiceProvider extends ServiceProvider

View file

@ -2,8 +2,8 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider class RouteServiceProvider extends ServiceProvider
{ {
@ -12,9 +12,9 @@ class RouteServiceProvider extends ServiceProvider
* *
* In addition, it is set as the URL generator's root namespace. * In addition, it is set as the URL generator's root namespace.
* *
* REMOVED WITH LARAVEL 8 UPGRADE * @var string
*/ */
// protected $namespace = 'App\Http\Controllers'; protected $namespace = 'App\Http\Controllers';
/** /**
* Define your route model bindings, pattern filters, etc. * Define your route model bindings, pattern filters, etc.

View file

@ -1,54 +1,52 @@
<?php <?php namespace App;
namespace App; use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Cache; use App\Item;
use App\Setting;
use Form; use Form;
use Illuminate\Support\Collection; use Cache;
use Yaml; use Yaml;
abstract class Search abstract class Search
{ {
/** /**
* List of all search providers * List of all search providers
* *
* @return Collection * @return Array
*/ */
public static function providers(): Collection public static function providers()
{ {
$providers = self::standardProviders(); $providers = self::standardProviders();
$providers = $providers + self::appProviders(); $providers = $providers + self::appProviders();
return collect($providers); return collect($providers);
} }
/** /**
* Gets details for a single provider * Gets details for a single provider
* *
* @return false|object * @return Object
*/ */
public static function providerDetails($provider) public static function providerDetails($provider)
{ {
$providers = self::providers(); $providers = self::providers();
if (! isset($providers[$provider])) { if(!isset($providers[$provider])) return false;
return false; return (object)$providers[$provider] ?? false;
}
return (object) $providers[$provider] ?? false;
} }
/** /**
* Array of the standard providers * Array of the standard providers
* *
* @return array * @return Array
*/ */
public static function standardProviders(): array public static function standardProviders()
{ {
// $providers = json_decode(file_get_contents(storage_path('app/searchproviders.json'))); // $providers = json_decode(file_get_contents(storage_path('app/searchproviders.json')));
// print_r($providers); // print_r($providers);
$providers = Yaml::parseFile(storage_path('app/searchproviders.yaml')); $providers = Yaml::parseFile(storage_path('app/searchproviders.yaml'));
$all = []; $all = [];
foreach ($providers as $key => $provider) { foreach($providers as $key => $provider) {
$all[$key] = $provider; $all[$key] = $provider;
$all[$key]['type'] = 'standard'; $all[$key]['type'] = 'standard';
} }
@ -59,18 +57,16 @@ abstract class Search
/** /**
* Loops through users apps to see if app is a search provider, might be worth * Loops through users apps to see if app is a search provider, might be worth
* looking into caching this at some point * looking into caching this at some point
* *
* @return array * @return Array
*/ */
public static function appProviders(): array public static function appProviders()
{ {
$providers = []; $providers = [];
$userapps = Item::all(); $userapps = Item::all();
foreach ($userapps as $app) { foreach($userapps as $app) {
if (empty($app->class)) { if(empty($app->class)) continue;
continue; if(($provider = Item::isSearchProvider($app->class)) !== false) {
}
if (($provider = Item::isSearchProvider($app->class)) !== false) {
$name = Item::nameFromClass($app->class); $name = Item::nameFromClass($app->class);
$providers[$app->id] = [ $providers[$app->id] = [
'id' => $app->id, 'id' => $app->id,
@ -80,36 +76,35 @@ abstract class Search
'name' => $app->title, 'name' => $app->title,
'colour' => $app->colour, 'colour' => $app->colour,
'icon' => $app->icon, 'icon' => $app->icon,
'description' => $app->description, 'description' => $app->description
]; ];
} }
} }
return $providers; return $providers;
} }
/** /**
* Outputs the search form * Outputs the search form
* *
* @return string * @return html
*/ */
public static function form(): string public static function form()
{ {
$output = ''; $output = '';
$homepage_search = Setting::fetch('homepage_search'); $homepage_search = Setting::fetch('homepage_search');
$search_provider = Setting::where('key', '=', 'search_provider')->first(); $search_provider = Setting::where('key', '=', 'search_provider')->first();
$user_search_provider = Setting::fetch('search_provider'); $user_search_provider = Setting::fetch('search_provider');
//die(print_r($search_provider)); //die(print_r($search_provider));
//die(var_dump($user_search_provider)); //die(var_dump($user_search_provider));
// return early if search isn't applicable // return early if search isn't applicable
if ((bool) $homepage_search !== true) { if((bool)$homepage_search !== true) return $output;
return $output;
}
$user_search_provider = $user_search_provider ?? 'none'; $user_search_provider = $user_search_provider ?? 'none';
if ((bool) $search_provider) { if((bool)$homepage_search && (bool)$search_provider) {
if ((bool) $user_search_provider) {
if((bool)$user_search_provider) {
$name = 'app.options.'.$user_search_provider; $name = 'app.options.'.$user_search_provider;
$provider = self::providerDetails($user_search_provider); $provider = self::providerDetails($user_search_provider);
@ -117,27 +112,20 @@ abstract class Search
$output .= '<form action="'.url('search').'"'.getLinkTargetAttribute().' method="get">'; $output .= '<form action="'.url('search').'"'.getLinkTargetAttribute().' method="get">';
$output .= '<div id="search-container" class="input-container">'; $output .= '<div id="search-container" class="input-container">';
$output .= '<select name="provider">'; $output .= '<select name="provider">';
foreach (self::providers() as $key => $searchprovider) { foreach(self::providers() as $key => $searchprovider) {
$selected = ((string) $key === (string) $user_search_provider) ? ' selected="selected"' : ''; $selected = ((string)$key === (string)$user_search_provider) ? ' selected="selected"' : '';
$output .= '<option value="'.$key.'"'.$selected.'>'.$searchprovider['name'].'</option>'; $output .= '<option value="'.$key.'"'.$selected.'>'.$searchprovider['name'].'</option>';
} }
$output .= '</select>'; $output .= '</select>';
$output .= Form::text( $output .= Form::text('q', null, ['class' => 'homesearch', 'autofocus' => 'autofocus', 'placeholder' => __('app.settings.search').'...']);
'q',
null,
[
'class' => 'homesearch',
'autofocus' => 'autofocus',
'placeholder' => __('app.settings.search').'...'
]
);
$output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>'; $output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>';
$output .= '</div>'; $output .= '</div>';
$output .= '</form>'; $output .= '</form>';
$output .= '</div>'; $output .= '</div>';
} }
} }
return $output; return $output;
} }
} }

View file

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

View file

@ -2,47 +2,14 @@
namespace App; namespace App;
use Form;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Http\Request;
use Illuminate\Session\SessionManager;
use Illuminate\Session\Store;
use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Input;
use Form;
use Illuminate\Support\Facades\Auth;
use App\User;
use App\Search;
use Illuminate\Http\Request;
/**
* App\Setting
*
* @mixin Builder
* @property int $id
* @property int $group_id
* @property string $key
* @property string $type
* @property string|null $options
* @property string $label
* @property string|null $value
* @property string $order
* @property int $system
* @property-read mixed $edit_value
* @property-read mixed $list_value
* @property-read \App\SettingGroup|null $group
* @property-read \Illuminate\Database\Eloquent\Collection|\App\User[] $users
* @property-read int|null $users_count
* @method static Builder|Setting newModelQuery()
* @method static Builder|Setting newQuery()
* @method static Builder|Setting query()
* @method static Builder|Setting whereGroupId($value)
* @method static Builder|Setting whereId($value)
* @method static Builder|Setting whereKey($value)
* @method static Builder|Setting whereLabel($value)
* @method static Builder|Setting whereOptions($value)
* @method static Builder|Setting whereOrder($value)
* @method static Builder|Setting whereSystem($value)
* @method static Builder|Setting whereType($value)
* @method static Builder|Setting whereValue($value)
*/
class Setting extends Model class Setting extends Model
{ {
/** /**
@ -53,7 +20,7 @@ class Setting extends Model
protected $table = 'settings'; protected $table = 'settings';
protected $fillable = [ protected $fillable = [
'id', 'group_id', 'key', 'type', 'options', 'label', 'value', 'order', 'system', 'id', 'group_id', 'key', 'type', 'options', 'label', 'value', 'order', 'system'
]; ];
/** /**
@ -71,10 +38,9 @@ class Setting extends Model
protected static $cache = []; protected static $cache = [];
/** /**
* @param Request $request * @return array
* @return object
*/ */
public static function getInput(Request $request): object public static function getInput(Request $request)
{ {
return (object) [ return (object) [
'value' => $request->input('value'), 'value' => $request->input('value'),
@ -84,43 +50,37 @@ class Setting extends Model
public function getListValueAttribute() public function getListValueAttribute()
{ {
if ((bool) $this->system === true) { if((bool)$this->system === true) {
$value = self::_fetch($this->key); $value = self::_fetch($this->key);
} else { } else {
$value = self::fetch($this->key); $value = self::fetch($this->key);
} }
$this->value = $value; $this->value = $value;
switch ($this->type) { switch($this->type) {
case 'image': case 'image':
if (! empty($this->value)) { if(!empty($this->value)) {
$value = '<a href="'.asset('storage/'.$this->value).'" title="'. $value = '<a href="'.asset('storage/'.$this->value).'" title="'.__('app.settings.view').'" target="_blank">'.__('app.settings.view').'</a>';
__('app.settings.view').
'" target="_blank">'.
__('app.settings.view').
'</a>';
} else { } else {
$value = __('app.options.none'); $value = __('app.options.none');
} }
break; break;
case 'boolean': case 'boolean':
if ((bool) $this->value === true) { if((bool)$this->value === true) {
$value = __('app.options.yes'); $value = __('app.options.yes');
} else { } else {
$value = __('app.options.no'); $value = __('app.options.no');
} }
break; break;
case 'select': case 'select':
if (! empty($this->value) && $this->value !== 'none') { if(!empty($this->value) && $this->value !== 'none') {
$options = (array) json_decode($this->options); $options = (array)json_decode($this->options);
if ($this->key === 'search_provider') { if($this->key === 'search_provider') {
$options = Search::providers()->pluck('name', 'id')->toArray(); $options = Search::providers()->pluck('name', 'id')->toArray();
} }
$value = (array_key_exists($this->value, $options)) $value = __($options[$this->value]);
? __($options[$this->value])
: __('app.options.none');
} else { } else {
$value = __('app.options.none'); $value = __('app.options.none');
} }
break; break;
default: default:
$value = __($this->value); $value = __($this->value);
@ -128,46 +88,32 @@ class Setting extends Model
} }
return $value; return $value;
} }
public function getEditValueAttribute() public function getEditValueAttribute()
{ {
if ((bool) $this->system === true) { if((bool)$this->system === true) {
$value = self::_fetch($this->key); $value = self::_fetch($this->key);
} else { } else {
$value = self::fetch($this->key); $value = self::fetch($this->key);
} }
$this->value = $value; $this->value = $value;
switch ($this->type) { switch($this->type) {
case 'image': case 'image':
$value = ''; $value = '';
if (isset($this->value) && ! empty($this->value)) { if(isset($this->value) && !empty($this->value)) {
$value .= '<a class="setting-view-image" href="'. $value .= '<a class="setting-view-image" href="'.asset('storage/'.$this->value).'" title="'.__('app.settings.view').'" target="_blank"><img src="'.asset('storage/'.$this->value).'" /></a>';
asset('storage/'.$this->value).
'" title="'.
__('app.settings.view').
'" target="_blank"><img src="'.
asset('storage/'.
$this->value).
'" /></a>';
} }
$value .= Form::file('value', ['class' => 'form-control']); $value .= Form::file('value', ['class' => 'form-control']);
if (isset($this->value) && ! empty($this->value)) { if(isset($this->value) && !empty($this->value)) {
$value .= '<a class="settinglink" href="'. $value .= '<a class="settinglink" href="'.route('settings.clear', $this->id).'" title="'.__('app.settings.remove').'">'.__('app.settings.reset').'</a>';
route('settings.clear', $this->id).
'" title="'.
__('app.settings.remove').
'">'.
__('app.settings.reset').
'</a>';
} }
break; break;
case 'boolean': case 'boolean':
$checked = false; $checked = false;
if (isset($this->value) && (bool) $this->value === true) { if(isset($this->value) && (bool)$this->value === true) $checked = true;
$checked = true;
}
$set_checked = ($checked) ? ' checked="checked"' : ''; $set_checked = ($checked) ? ' checked="checked"' : '';
$value = ' $value = '
<input type="hidden" name="value" value="0" /> <input type="hidden" name="value" value="0" />
@ -179,10 +125,10 @@ class Setting extends Model
break; break;
case 'select': case 'select':
$options = json_decode($this->options); $options = json_decode($this->options);
if ($this->key === 'search_provider') { if($this->key === 'search_provider') {
$options = Search::providers()->pluck('name', 'id'); $options = Search::providers()->pluck('name', 'id');
} }
foreach ($options as $key => $opt) { foreach($options as $key => $opt) {
$options->$key = __($opt); $options->$key = __($opt);
} }
$value = Form::select('value', $options, null, ['class' => 'form-control']); $value = Form::select('value', $options, null, ['class' => 'form-control']);
@ -196,74 +142,70 @@ class Setting extends Model
} }
return $value; return $value;
} }
/** public function group()
* @return BelongsTo
*/
public function group(): BelongsTo
{ {
return $this->belongsTo(\App\SettingGroup::class, 'group_id'); return $this->belongsTo('App\SettingGroup', 'group_id');
} }
/** /**
* @param string $key * @param string $key
* *
* @return mixed * @return mixed
*/ */
public static function fetch(string $key) public static function fetch($key)
{ {
$user = self::user(); $user = self::user();
return self::_fetch($key, $user); return self::_fetch($key, $user);
} }
// @codingStandardsIgnoreStart
/** /**
* @param string $key * @param string $key
* *
* @return mixed * @return mixed
*/ */
public static function _fetch($key, $user = null) public static function _fetch($key, $user=null)
{ {
// @codingStandardsIgnoreEnd #$cachekey = ($user === null) ? $key : $key.'-'.$user->id;
//$cachekey = ($user === null) ? $key : $key.'-'.$user->id; #if (Setting::cached($cachekey)) {
//if (Setting::cached($cachekey)) { # return Setting::$cache[$cachekey];
// return Setting::$cache[$cachekey]; #} else {
//} else { $find = self::where('key', '=', $key)->first();
$find = self::where('key', '=', $key)->first();
if (! is_null($find)) { if (!is_null($find)) {
if ((bool) $find->system === true) { // if system variable use global 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; $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);
//Setting::add($cachekey, $value);
return $value; return $value;
} else { } else {
return false; return false;
} }
//} #}
} }
/** /**
@ -272,7 +214,7 @@ class Setting extends Model
*/ */
public static function add($key, $value) public static function add($key, $value)
{ {
self::$cache[$key] = $value; Setting::$cache[$key] = $value;
} }
/** /**
@ -280,24 +222,24 @@ class Setting extends Model
* *
* @return bool * @return bool
*/ */
public static function cached($key): bool public static function cached($key)
{ {
return array_key_exists($key, self::$cache); return array_key_exists($key, Setting::$cache);
} }
/** /**
* The users that belong to the setting. * The users that belong to the setting.
*/ */
public function users(): BelongsToMany public function users()
{ {
return $this->belongsToMany(\App\User::class)->using(\App\SettingUser::class)->withPivot('uservalue'); return $this->belongsToMany('App\User')->using('App\SettingUser')->withPivot('uservalue');
} }
/**
* @return \Illuminate\Contracts\Foundation\Application|SessionManager|Store|mixed
*/
public static function user() public static function user()
{ {
return User::currentUser(); return User::currentUser();
} }
} }

View file

@ -3,24 +3,7 @@
namespace App; namespace App;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* App\SettingGroup
*
* @property int $id
* @property string $title
* @property int $order
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Setting[] $settings
* @property-read int|null $settings_count
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup query()
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereOrder($value)
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereTitle($value)
* @mixin \Eloquent
*/
class SettingGroup extends Model class SettingGroup extends Model
{ {
/** /**
@ -37,11 +20,8 @@ class SettingGroup extends Model
*/ */
public $timestamps = false; public $timestamps = false;
/** public function settings()
* @return HasMany
*/
public function settings(): HasMany
{ {
return $this->hasMany(\App\Setting::class, 'group_id'); return $this->hasMany('App\Setting', 'group_id');
} }
} }

View file

@ -4,20 +4,6 @@ namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Eloquent\Relations\Pivot;
/**
* App\SettingUser
*
* @property int $setting_id
* @property int $user_id
* @property string|null $uservalue
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser query()
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereSettingId($value)
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereUserId($value)
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereUservalue($value)
* @mixin \Eloquent
*/
class SettingUser extends Pivot class SettingUser extends Pivot
{ {
// //

View file

@ -1,46 +1,34 @@
<?php <?php namespace App;
namespace App;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ServerException; use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Psr\Http\Message\ResponseInterface;
abstract class SupportedApps abstract class SupportedApps
{ {
protected $jar = false; protected $jar = false;
protected $method = 'GET'; protected $method = 'GET';
protected $error; protected $error;
/** public function appTest($url, $attrs = [], $overridevars=false)
* @param $url
* @param array $attrs
* @return object
* @throws GuzzleException
*/
public function appTest($url, array $attrs = []): object
{ {
if (empty($this->config->url)) { if(empty($this->config->url)) {
return (object) [ return (object)[
'code' => 404, 'code' => 404,
'status' => 'No URL has been specified', 'status' => 'No URL has been specified',
'response' => 'No URL has been specified', 'response' => 'No URL has been specified',
]; ];
} }
$res = $this->execute($url, $attrs); $res = $this->execute($url, $attrs);
if ($res == null) { if($res == null) {
return (object) [ return (object)[
'code' => null, 'code' => null,
'status' => $this->error, 'status' => $this->error,
'response' => 'Connection failed', 'response' => 'Connection failed',
]; ];
} }
switch ($res->getStatusCode()) { switch($res->getStatusCode()) {
case 200: case 200:
$status = 'Successfully communicated with the API'; $status = 'Successfully communicated with the API';
break; break;
@ -54,84 +42,58 @@ abstract class SupportedApps
$status = 'Something went wrong... Code: '.$res->getStatusCode(); $status = 'Something went wrong... Code: '.$res->getStatusCode();
break; break;
} }
return (object)[
return (object) [
'code' => $res->getStatusCode(), 'code' => $res->getStatusCode(),
'status' => $status, 'status' => $status,
'response' => $res->getBody(), 'response' => $res->getBody(),
]; ];
} }
/** public function execute($url, $attrs = [], $overridevars=false, $overridemethod=false)
* @param $url {
* @param array $attrs
* @param array|bool|null $overridevars
* @param string|bool|null $overridemethod
* @return ResponseInterface|null
* @throws GuzzleException
*/
public function execute(
$url,
array $attrs = [],
$overridevars = null,
$overridemethod = null
): ?ResponseInterface {
$res = null; $res = null;
$vars = ($overridevars === null || $overridevars === false) ? $vars = ($overridevars !== false) ?
[ $overridevars : [
'http_errors' => false, 'http_errors' => false,
'timeout' => 15, 'timeout' => 15,
'connect_timeout' => 15, 'connect_timeout' => 15,
] : $overridevars; ];
$client = new Client($vars); $client = new Client($vars);
$method = ($overridemethod === null || $overridemethod === false) ? $this->method : $overridemethod; $method = ($overridemethod !== false) ? $overridemethod : $this->method;
try { try {
return $client->request($method, $url, $attrs); return $client->request($method, $url, $attrs);
} catch (ConnectException $e) { } catch (\GuzzleHttp\Exception\ConnectException $e) {
Log::error('Connection refused'); Log::error("Connection refused");
Log::debug($e->getMessage()); Log::debug($e->getMessage());
$this->error = 'Connection refused - '.(string) $e->getMessage(); $this->error = "Connection refused - ".(string) $e->getMessage();
} catch (ServerException $e) { } catch (\GuzzleHttp\Exception\ServerException $e) {
Log::debug($e->getMessage()); Log::debug($e->getMessage());
$this->error = (string) $e->getResponse()->getBody(); $this->error = (string) $e->getResponse()->getBody();
} }
$this->error = 'General error connecting with API'; $this->error = 'General error connecting with API';
return $res; return $res;
} }
/**
* @return void
*/
public function login() public function login()
{ {
} }
/** public function normaliseurl($url, $addslash=true)
* @param string $url
* @param bool $addslash
* @return string
*/
public function normaliseurl(string $url, bool $addslash = true): string
{ {
$url = rtrim($url, '/'); $url = rtrim($url, '/');
if ($addslash) { if($addslash) $url .= '/';
$url .= '/';
}
return $url; return $url;
} }
/**
* @param $status
* @param $data
* @return false|string
*/
public function getLiveStats($status, $data) public function getLiveStats($status, $data)
{ {
$className = get_class($this); $className = get_class($this);
@ -139,53 +101,33 @@ abstract class SupportedApps
$name = end($explode); $name = end($explode);
$html = view('SupportedApps::'.$name.'.livestats', $data)->with('data', $data)->render(); $html = view('SupportedApps::'.$name.'.livestats', $data)->with('data', $data)->render();
return json_encode(['status' => $status, 'html' => $html]); return json_encode(['status' => $status, 'html' => $html]);
//return //return
} }
/** public static function getList()
* @return ResponseInterface
* @throws GuzzleException
*/
public static function getList(): ResponseInterface
{ {
// $list_url = 'https://apps.heimdall.site/list'; // $list_url = 'https://apps.heimdall.site/list';
$list_url = config('app.appsource').'list.json'; $list_url = config('app.appsource').'list.json';
$client = new Client(['http_errors' => false, 'verify' => false, 'timeout' => 15, 'connect_timeout' => 15]); $client = new Client(['http_errors' => false, 'timeout' => 15, 'connect_timeout' => 15]);
return $client->request('GET', $list_url); return $client->request('GET', $list_url);
} }
public static function configValue($item = null, $key = null) public static function configValue($item=null, $key=null)
{ {
if (isset($item) && ! empty($item)) { if(isset($item) && !empty($item)) {
return $item->getconfig()->$key; return $item->getconfig()->$key;
} else { } else return null;
return null;
}
} }
/** public static function getFiles($app)
* @param $app
* @return bool|false
* @throws GuzzleException
*/
public static function getFiles($app): bool
{ {
Log::debug("Download triggered for ".print_r($app, true));
$zipurl = config('app.appsource').'files/'.$app->sha.'.zip'; $zipurl = config('app.appsource').'files/'.$app->sha.'.zip';
$client = new Client(['http_errors' => false, 'timeout' => 60, 'connect_timeout' => 15, 'verify' => false]); $client = new Client(['http_errors' => false, 'timeout' => 60, 'connect_timeout' => 15]);
$res = $client->request('GET', $zipurl); $res = $client->request('GET', $zipurl);
// Something went wrong? if(!file_exists(app_path('SupportedApps'))) {
if ($res->getStatusCode() !== 200) {
return false;
}
if (! file_exists(app_path('SupportedApps'))) {
mkdir(app_path('SupportedApps'), 0777, true); mkdir(app_path('SupportedApps'), 0777, true);
} }
@ -200,18 +142,11 @@ abstract class SupportedApps
unlink($src); //Deleting the Zipped file unlink($src); //Deleting the Zipped file
} else { } else {
var_dump($x); var_dump($x);
return false;
} }
return true;
} }
/**
* @param $details
* @param $app
* @return mixed
*/
public static function saveApp($details, $app) public static function saveApp($details, $app)
{ {
$app->appid = $details->appid; $app->appid = $details->appid;
$app->name = $details->name; $app->name = $details->name;
$app->sha = $details->sha ?? null; $app->sha = $details->sha ?? null;
@ -221,12 +156,12 @@ abstract class SupportedApps
$appclass = $app->class(); $appclass = $app->class();
$application = new $appclass; $application = new $appclass;
$enhanced = (bool) ($application instanceof \App\EnhancedApps); $enhanced = (bool)($application instanceof \App\EnhancedApps);
$app->class = $appclass; $app->class = $appclass;
$app->enhanced = $enhanced; $app->enhanced = $enhanced;
$app->tile_background = $details->tile_background; $app->tile_background = $details->tile_background;
$app->save(); $app->save();
return $app;
return $app;
} }
}
}

View file

@ -2,54 +2,13 @@
namespace App; namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
// @codingStandardsIgnoreStart
/**
* App\User
*
* @property int $id
* @property string $username
* @property string $email
* @property string|null $avatar
* @property string|null $password
* @property string|null $autologin
* @property int $public_front
* @property string|null $remember_token
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Item[] $items
* @property-read int|null $items_count
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications
* @property-read int|null $notifications_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Setting[] $settings
* @property-read int|null $settings_count
* @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User query()
* @method static \Illuminate\Database\Eloquent\Builder|User whereAutologin($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereAvatar($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|User wherePassword($value)
* @method static \Illuminate\Database\Eloquent\Builder|User wherePublicFront($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereUsername($value)
* @mixin \Eloquent
*/
// @codingStandardsIgnoreEnd
class User extends Authenticatable class User extends Authenticatable
{ {
use Notifiable; use Notifiable;
use HasFactory;
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
@ -68,28 +27,20 @@ class User extends Authenticatable
'password', 'remember_token', 'password', 'remember_token',
]; ];
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/** /**
* Get the items for the user. * Get the items for the user.
*/ */
public function items(): HasMany public function items()
{ {
return $this->hasMany(Item::class); return $this->hasMany('App\Item');
} }
/** /**
* The settings that belong to the user. * The settings that belong to the user.
*/ */
public function settings(): BelongsToMany public function settings()
{ {
return $this->belongsToMany(Setting::class)->withPivot('uservalue'); return $this->belongsToMany('App\Setting')->withPivot('uservalue');
} }
public static function currentUser() public static function currentUser()
@ -98,13 +49,15 @@ class User extends Authenticatable
if ($current_user) { // if logged in, set this user if ($current_user) { // if logged in, set this user
return $current_user; return $current_user;
} else { // not logged in, get first user } else { // not logged in, get first user
$user = self::where('public_front', true)->first(); $user = User::where('public_front',true)->first();
if (! $user) { if(!$user) {
$user = self::first(); $user = User::first();
} }
session(['current_user' => $user]); session(['current_user' => $user]);
return $user; return $user;
} }
} }
} }

View file

@ -12,7 +12,7 @@
*/ */
$app = new Illuminate\Foundation\Application( $app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) realpath(__DIR__.'/../')
); );
/* /*

View file

@ -1,48 +1,37 @@
{ {
"name": "laravel/laravel", "name": "laravel/laravel",
"description": "The Laravel Framework.", "description": "The Laravel Framework.",
"keywords": [ "keywords": ["framework", "laravel"],
"framework",
"laravel"
],
"license": "MIT", "license": "MIT",
"type": "project", "type": "project",
"require": { "require": {
"php": ">=7.4.32", "php": ">=7.2.5",
"facade/ignition": "^2.3.6",
"fideloper/proxy": "^4.0", "fideloper/proxy": "^4.0",
"graham-campbell/github": "^10.5", "graham-campbell/github": "^10.5",
"guzzlehttp/guzzle": "^7.4", "guzzlehttp/guzzle": "^7.4",
"laravel/framework": "^8.0", "laravel/framework": "^7.0",
"laravel/tinker": "^2.0", "laravel/tinker": "^2.0",
"laravel/ui": "^3.0", "laravel/ui": "^2.4",
"laravelcollective/html": "^6.0", "laravelcollective/html": "^6.0",
"nunomaduro/collision": "^5.0", "symfony/yaml": "^5.4"
"symfony/yaml": "^5.4",
"ext-json": "*",
"ext-intl": "*"
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-ide-helper": "^2.12",
"filp/whoops": "~2.0", "filp/whoops": "~2.0",
"fzaninotto/faker": "~1.4", "fzaninotto/faker": "~1.4",
"mockery/mockery": "~1.0", "mockery/mockery": "~1.0",
"phpunit/phpunit": "~9.0", "phpunit/phpunit": "~6.0",
"squizlabs/php_codesniffer": "3.*",
"symfony/thanks": "^1.0" "symfony/thanks": "^1.0"
}, },
"autoload": { "autoload": {
"classmap": [ "classmap": [
"database/seeders", "database/seeds",
"database/factories" "database/factories"
], ],
"files": [ "files": [
"app/Helper.php" "app/Helper.php"
], ],
"psr-4": { "psr-4": {
"App\\": "app/", "App\\": "app/"
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
} }
}, },
"autoload-dev": { "autoload-dev": {
@ -53,7 +42,6 @@
"extra": { "extra": {
"laravel": { "laravel": {
"dont-discover": [ "dont-discover": [
"barryvdh/laravel-ide-helper"
] ]
} }
}, },
@ -67,11 +55,6 @@
"post-autoload-dump": [ "post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover" "@php artisan package:discover"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"@php artisan ide-helper:generate",
"@php artisan ide-helper:meta"
] ]
}, },
"config": { "config": {

3689
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -13,8 +13,8 @@ return [
| |
*/ */
'name' => env('APP_NAME', 'Heimdall'), 'name' => env('APP_NAME', 'Heimdall'),
'version' => '2.5.8', 'version' => '2.4.0',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -40,7 +40,7 @@ return [
| |
*/ */
'debug' => (bool) env('APP_DEBUG', false), 'debug' => env('APP_DEBUG', false),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -54,10 +54,9 @@ return [
*/ */
'url' => env('APP_URL', 'http://localhost'), 'url' => env('APP_URL', 'http://localhost'),
'asset_url' => env('ASSET_URL', null),
'appsource' => env('APP_SOURCE', 'https://appslist.heimdall.site/'), 'appsource' => env('APP_SOURCE', 'https://appslist.heimdall.site/'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Application Timezone | Application Timezone
@ -97,19 +96,6 @@ return [
'fallback_locale' => 'en', 'fallback_locale' => 'en',
/*
|--------------------------------------------------------------------------
| Faker Locale
|--------------------------------------------------------------------------
|
| This locale will be used by the Faker PHP library when generating fake
| data for your database seeds. For example, this will be used to get
| localized telephone numbers, street address information and more.
|
*/
'faker_locale' => 'en_US',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Encryption Key | Encryption Key
@ -125,6 +111,23 @@ return [
'cipher' => 'AES-256-CBC', 'cipher' => 'AES-256-CBC',
/*
|--------------------------------------------------------------------------
| Logging Configuration
|--------------------------------------------------------------------------
|
| Here you may configure the log settings for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Settings: "single", "daily", "syslog", "errorlog"
|
*/
'log' => env('APP_LOG', 'single'),
'log_level' => env('APP_LOG_LEVEL', 'debug'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Autoloaded Service Providers | Autoloaded Service Providers
@ -193,7 +196,6 @@ return [
'aliases' => [ 'aliases' => [
'App' => Illuminate\Support\Facades\App::class, 'App' => Illuminate\Support\Facades\App::class,
'Arr' => Illuminate\Support\Arr::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class, 'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class, 'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class, 'Blade' => Illuminate\Support\Facades\Blade::class,
@ -211,7 +213,6 @@ return [
'Gate' => Illuminate\Support\Facades\Gate::class, 'Gate' => Illuminate\Support\Facades\Gate::class,
'Hash' => Illuminate\Support\Facades\Hash::class, 'Hash' => Illuminate\Support\Facades\Hash::class,
'Html' => Collective\Html\HtmlFacade::class, 'Html' => Collective\Html\HtmlFacade::class,
'Http' => Illuminate\Support\Facades\Http::class,
'Lang' => Illuminate\Support\Facades\Lang::class, 'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class, 'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class, 'Mail' => Illuminate\Support\Facades\Mail::class,
@ -226,7 +227,6 @@ return [
'Schema' => Illuminate\Support\Facades\Schema::class, 'Schema' => Illuminate\Support\Facades\Schema::class,
'Session' => Illuminate\Support\Facades\Session::class, 'Session' => Illuminate\Support\Facades\Session::class,
'Storage' => Illuminate\Support\Facades\Storage::class, 'Storage' => Illuminate\Support\Facades\Storage::class,
'Str' => Illuminate\Support\Str::class,
'URL' => Illuminate\Support\Facades\URL::class, 'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class, 'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class, 'View' => Illuminate\Support\Facades\View::class,

View file

@ -44,7 +44,6 @@ return [
'api' => [ 'api' => [
'driver' => 'token', 'driver' => 'token',
'provider' => 'users', 'provider' => 'users',
'hash' => false,
], ],
], ],
@ -97,21 +96,7 @@ return [
'provider' => 'users', 'provider' => 'users',
'table' => 'password_resets', 'table' => 'password_resets',
'expire' => 60, 'expire' => 60,
'throttle' => 60,
], ],
], ],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
]; ];

View file

@ -37,7 +37,7 @@ return [
'app_id' => env('PUSHER_APP_ID'), 'app_id' => env('PUSHER_APP_ID'),
'options' => [ 'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'), 'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => true, 'encrypted' => true,
], ],
], ],

View file

@ -1,7 +1,5 @@
<?php <?php
use Illuminate\Support\Str;
return [ return [
/* /*
@ -38,7 +36,6 @@ return [
'array' => [ 'array' => [
'driver' => 'array', 'driver' => 'array',
'serialize' => false,
], ],
'database' => [ 'database' => [
@ -76,15 +73,6 @@ return [
'connection' => 'default', 'connection' => 'default',
], ],
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
], ],
/* /*
@ -98,6 +86,9 @@ return [
| |
*/ */
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'), 'prefix' => env(
'CACHE_PREFIX',
str_slug(env('APP_NAME', 'laravel'), '_').'_cache'
),
]; ];

View file

@ -44,7 +44,6 @@ return [
'mysql' => [ 'mysql' => [
'driver' => 'mysql', 'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'), 'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'), 'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'), 'database' => env('DB_DATABASE', 'forge'),
@ -54,17 +53,12 @@ return [
'charset' => 'utf8mb4', 'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci', 'collation' => 'utf8mb4_unicode_ci',
'prefix' => '', 'prefix' => '',
'prefix_indexes' => true,
'strict' => true, 'strict' => true,
'engine' => null, 'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
], ],
'pgsql' => [ 'pgsql' => [
'driver' => 'pgsql', 'driver' => 'pgsql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'), 'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'), 'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'), 'database' => env('DB_DATABASE', 'forge'),
@ -72,14 +66,12 @@ return [
'password' => env('DB_PASSWORD', ''), 'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8', 'charset' => 'utf8',
'prefix' => '', 'prefix' => '',
'prefix_indexes' => true,
'schema' => 'public', 'schema' => 'public',
'sslmode' => 'prefer', 'sslmode' => 'prefer',
], ],
'sqlsrv' => [ 'sqlsrv' => [
'driver' => 'sqlsrv', 'driver' => 'sqlsrv',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', 'localhost'), 'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '1433'), 'port' => env('DB_PORT', '1433'),
'database' => env('DB_DATABASE', 'forge'), 'database' => env('DB_DATABASE', 'forge'),
@ -87,9 +79,8 @@ return [
'password' => env('DB_PASSWORD', ''), 'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8', 'charset' => 'utf8',
'prefix' => '', 'prefix' => '',
'prefix_indexes' => true,
], ],
], ],
/* /*

View file

@ -61,10 +61,8 @@ return [
'secret' => env('AWS_SECRET_ACCESS_KEY'), 'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'), 'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'), 'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
], ],
], ],
]; ];

View file

@ -1,52 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Hash Driver
|--------------------------------------------------------------------------
|
| This option controls the default hash driver that will be used to hash
| passwords for your application. By default, the bcrypt algorithm is
| used; however, you remain free to modify this option if you wish.
|
| Supported: "bcrypt", "argon", "argon2id"
|
*/
'driver' => 'bcrypt',
/*
|--------------------------------------------------------------------------
| Bcrypt Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Bcrypt algorithm. This will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 10),
],
/*
|--------------------------------------------------------------------------
| Argon Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Argon algorithm. These will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'argon' => [
'memory' => 1024,
'threads' => 2,
'time' => 2,
],
];

View file

@ -17,7 +17,7 @@ return [
| |
*/ */
'default' => env('LOG_CHANNEL', 'daily'), 'default' => env('LOG_CHANNEL', 'stack'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -101,4 +101,4 @@ return [
], ],
], ],
]; ];

View file

@ -4,16 +4,18 @@ return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Default Queue Connection Name | Default Queue Driver
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Laravel's queue API supports an assortment of back-ends via a single | Laravel's queue API supports an assortment of back-ends via a single
| API, giving you convenient access to each back-end using the same | API, giving you convenient access to each back-end using the same
| syntax for every one. Here you may define a default connection. | syntax for each one. Here you may set the default queue driver.
|
| Supported: "sync", "database", "beanstalkd", "sqs", "redis", "null"
| |
*/ */
'default' => env('QUEUE_CONNECTION', 'sync'), 'default' => env('QUEUE_DRIVER', 'sync'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -24,8 +26,6 @@ return [
| is used by your application. A default configuration has been added | is used by your application. A default configuration has been added
| for each back-end shipped with Laravel. You are free to add more. | for each back-end shipped with Laravel. You are free to add more.
| |
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
*/ */
'connections' => [ 'connections' => [
@ -46,25 +46,22 @@ return [
'host' => 'localhost', 'host' => 'localhost',
'queue' => 'default', 'queue' => 'default',
'retry_after' => 90, 'retry_after' => 90,
'block_for' => 0,
], ],
'sqs' => [ 'sqs' => [
'driver' => 'sqs', 'driver' => 'sqs',
'key' => env('AWS_ACCESS_KEY_ID'), 'key' => env('SQS_KEY', 'your-public-key'),
'secret' => env('AWS_SECRET_ACCESS_KEY'), 'secret' => env('SQS_SECRET', 'your-secret-key'),
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'your-queue-name'), 'queue' => env('SQS_QUEUE', 'your-queue-name'),
'suffix' => env('SQS_SUFFIX'), 'region' => env('SQS_REGION', 'us-east-1'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
], ],
'redis' => [ 'redis' => [
'driver' => 'redis', 'driver' => 'redis',
'connection' => 'default', 'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'), 'queue' => 'default',
'retry_after' => 90, 'retry_after' => 90,
'block_for' => null,
], ],
], ],
@ -81,7 +78,6 @@ return [
*/ */
'failed' => [ 'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database'),
'database' => env('DB_CONNECTION', 'mysql'), 'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'failed_jobs', 'table' => 'failed_jobs',
], ],

View file

@ -8,26 +8,31 @@ return [
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| This file is for storing the credentials for third party services such | This file is for storing the credentials for third party services such
| as Mailgun, Postmark, AWS and more. This file provides the de facto | as Stripe, Mailgun, SparkPost and others. This file provides a sane
| location for this type of information, allowing packages to have | default location for this type of information, allowing packages
| a conventional file to locate the various service credentials. | to have a conventional place to find your various credentials.
| |
*/ */
'mailgun' => [ 'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'), 'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'), 'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
],
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
], ],
'ses' => [ 'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'), 'key' => env('SES_KEY'),
'secret' => env('AWS_SECRET_ACCESS_KEY'), 'secret' => env('SES_SECRET'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'region' => 'us-east-1',
], ],
'sparkpost' => [
'secret' => env('SPARKPOST_SECRET'),
],
'stripe' => [
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
]; ];

View file

@ -1,7 +1,5 @@
<?php <?php
use Illuminate\Support\Str;
return [ return [
/* /*
@ -72,7 +70,7 @@ return [
| |
*/ */
'connection' => env('SESSION_CONNECTION', null), 'connection' => null,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -98,7 +96,7 @@ return [
| |
*/ */
'store' => env('SESSION_STORE', null), 'store' => null,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -126,7 +124,7 @@ return [
'cookie' => env( 'cookie' => env(
'SESSION_COOKIE', 'SESSION_COOKIE',
Str::slug(env('APP_NAME', 'laravel'), '_').'_session' str_slug(env('APP_NAME', 'laravel'), '_').'_session'
), ),
/* /*
@ -166,7 +164,7 @@ return [
| |
*/ */
'secure' => env('SESSION_SECURE_COOKIE'), 'secure' => env('SESSION_SECURE_COOKIE', null),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View file

@ -28,9 +28,6 @@ return [
| |
*/ */
'compiled' => env( 'compiled' => realpath(storage_path('framework/views')),
'VIEW_COMPILED_PATH',
realpath(storage_path('framework/views'))
),
]; ];

View file

@ -1,29 +0,0 @@
<?php
namespace Database\Factories;
use App\Item;
use Illuminate\Database\Eloquent\Factories\Factory;
class ItemFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Item::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition(): array
{
return [
'title' => $this->faker->unique()->text(),
'url' => $this->faker->unique()->url(),
];
}
}

View file

@ -1,27 +0,0 @@
<?php
namespace Database\Factories;
use App\Item;
use App\ItemTag;
use Illuminate\Database\Eloquent\Factories\Factory;
class ItemTagFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = ItemTag::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition(): array
{
return [];
}
}

View file

@ -1,47 +1,23 @@
<?php <?php
namespace Database\Factories; use Faker\Generator as Faker;
use App\User; /*
use Illuminate\Database\Eloquent\Factories\Factory; |--------------------------------------------------------------------------
use Illuminate\Support\Str; | Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
class UserFactory extends Factory $factory->define(App\User::class, function (Faker $faker) {
{ return [
/** 'name' => $faker->name,
* The name of the factory's corresponding model. 'email' => $faker->unique()->safeEmail,
* 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
* @var string 'remember_token' => str_random(10),
*/ ];
protected $model = User::class; });
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'username' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'public_front' => 1,
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function unverified()
{
return $this->state(function (array $attributes) {
return [
'email_verified_at' => null,
];
});
}
}

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateItemsTable extends Migration class CreateItemsTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateSettingsTable extends Migration class CreateSettingsTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateSettingGroupsTable extends Migration class CreateSettingGroupsTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddColumnsToItemsForGroups extends Migration class AddColumnsToItemsForGroups extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class ItemTag extends Migration class ItemTag extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration class CreateUsersTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePasswordResetsTable extends Migration class CreatePasswordResetsTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddUserIdToItemsTable extends Migration class AddUserIdToItemsTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateSettingUserPivotTable extends Migration class CreateSettingUserPivotTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateApplicationsTable extends Migration class CreateApplicationsTable extends Migration
{ {
@ -14,6 +14,7 @@ class CreateApplicationsTable extends Migration
public function up() public function up()
{ {
Schema::create('applications', function (Blueprint $table) { Schema::create('applications', function (Blueprint $table) {
$table->string('appid')->unique(); $table->string('appid')->unique();
$table->string('name')->unique(); $table->string('name')->unique();
$table->string('sha')->unique()->nullable(); $table->string('sha')->unique()->nullable();

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddClassToItemsTable extends Migration class AddClassToItemsTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateJobsTable extends Migration class CreateJobsTable extends Migration
{ {

View file

@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateFailedJobsTable extends Migration class CreateFailedJobsTable extends Migration
{ {

View file

@ -1,19 +0,0 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->call(SettingsSeeder::class);
$this->call(UsersSeeder::class);
}
}

View file

@ -1,333 +0,0 @@
<?php
namespace Database\Seeders;
use App\Setting;
use App\SettingGroup;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Locale;
class SettingsSeeder extends Seeder
{
/**
* @return false|string
*/
public static function getSupportedLanguageMap()
{
if (! class_exists('Locale')) {
Log::info('PHP Extension Intl not found. Falling back to English language support only.');
return json_encode(['en' => 'English']);
}
$languageDirectories = array_filter(glob(resource_path().'/lang/*'), 'is_dir');
$result = [];
foreach ($languageDirectories as $languageDirectory) {
$language = self::getLanguageFromDirectory($languageDirectory);
$resultNative = mb_convert_case(
Locale::getDisplayLanguage($language.'-', $language),
MB_CASE_TITLE,
'UTF-8'
);
$resultEn = ucfirst(Locale::getDisplayLanguage($language, 'en'));
$result[$language] = "$resultNative ($resultEn)";
}
return json_encode($result);
}
/**
* @param $languageDirectory
* @return false|string[]
*/
public static function getLanguageFromDirectory($languageDirectory)
{
$directories = explode('/', $languageDirectory);
return $directories[array_key_last($directories)];
}
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Groups
if (! $setting_group = SettingGroup::find(1)) {
$setting_group = new SettingGroup;
$setting_group->id = 1;
$setting_group->title = 'app.settings.system';
$setting_group->order = 0;
$setting_group->save();
} else {
$setting_group->title = 'app.settings.system';
$setting_group->save();
}
if (! $setting_group = SettingGroup::find(2)) {
$setting_group = new SettingGroup;
$setting_group->id = 2;
$setting_group->title = 'app.settings.appearance';
$setting_group->order = 1;
$setting_group->save();
} else {
$setting_group->title = 'app.settings.appearance';
$setting_group->save();
}
if (! $setting_group = SettingGroup::find(3)) {
$setting_group = new SettingGroup;
$setting_group->id = 3;
$setting_group->title = 'app.settings.miscellaneous';
$setting_group->order = 2;
$setting_group->save();
} else {
$setting_group->title = 'app.settings.miscellaneous';
$setting_group->save();
}
if (! $setting_group = SettingGroup::find(4)) {
$setting_group = new SettingGroup;
$setting_group->id = 4;
$setting_group->title = 'app.settings.advanced';
$setting_group->order = 3;
$setting_group->save();
} else {
$setting_group->title = 'app.settings.advanced';
$setting_group->save();
}
if ($version = Setting::find(1)) {
$version->label = 'app.settings.version';
$version->value = config('app.version');
$version->save();
} else {
$setting = new Setting;
$setting->id = 1;
$setting->group_id = 1;
$setting->key = 'version';
$setting->type = 'text';
$setting->label = 'app.settings.version';
$setting->value = config('app.version');
$setting->system = true;
$setting->save();
}
if (! $setting = Setting::find(2)) {
$setting = new Setting;
$setting->id = 2;
$setting->group_id = 2;
$setting->key = 'background_image';
$setting->type = 'image';
$setting->label = 'app.settings.background_image';
$setting->save();
} else {
$setting->label = 'app.settings.background_image';
$setting->save();
}
if (! $setting = Setting::find(3)) {
$setting = new Setting;
$setting->id = 3;
$setting->group_id = 3;
$setting->key = 'homepage_search';
$setting->type = 'boolean';
$setting->label = 'app.settings.homepage_search';
$setting->save();
} else {
$setting->label = 'app.settings.homepage_search';
$setting->save();
}
$options = json_encode([
'none' => 'app.options.none',
'google' => 'app.options.google',
'ddg' => 'app.options.ddg',
'qwant' => 'app.options.qwant',
'bing' => 'app.options.bing',
'startpage' => 'app.options.startpage',
]);
if (! $setting = Setting::find(4)) {
$setting = new Setting;
$setting->id = 4;
$setting->group_id = 3;
$setting->key = 'search_provider';
$setting->type = 'select';
$setting->options = $options;
$setting->label = 'app.settings.search_provider';
$setting->save();
} else {
$setting->options = $options;
$setting->label = 'app.settings.search_provider';
$setting->save();
}
$language_options = SettingsSeeder::getSupportedLanguageMap();
if ($languages = Setting::find(5)) {
$languages->options = $language_options;
$languages->save();
} else {
$setting = new Setting;
$setting->id = 5;
$setting->group_id = 1;
$setting->key = 'language';
$setting->type = 'select';
$setting->label = 'app.settings.language';
$setting->options = $language_options;
$setting->value = 'en';
$setting->save();
}
if (! $setting = Setting::find(12)) {
$setting = new Setting;
$setting->id = 12;
$setting->group_id = 2;
$setting->key = 'trianglify';
$setting->type = 'boolean';
$setting->label = 'app.settings.trianglify';
$setting->save();
} else {
$setting->label = 'app.settings.trianglify';
$setting->save();
}
if (! $setting = Setting::find(13)) {
$setting = new Setting;
$setting->id = 13;
$setting->group_id = 2;
$setting->key = 'trianglify_seed';
$setting->type = 'text';
$setting->value = 'heimdall';
$setting->label = 'app.settings.trianglify_seed';
$setting->save();
} else {
$setting->label = 'app.settings.trianglify_seed';
$setting->save();
}
$window_target_options = json_encode([
'current' => 'app.settings.window_target.current',
'heimdall' => 'app.settings.window_target.one',
'_blank' => 'app.settings.window_target.new',
]);
if (! $setting = Setting::find(7)) {
$setting = new Setting;
$setting->id = 7;
$setting->group_id = 3;
$setting->key = 'window_target';
$setting->type = 'select';
$setting->options = $window_target_options;
$setting->label = 'app.settings.window_target';
$setting->value = 'heimdall';
$setting->save();
} else {
$setting->options = $window_target_options;
$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();
}
if (! $setting = Setting::find(10)) {
$setting = new Setting;
$setting->id = 10;
$setting->group_id = 4;
$setting->key = 'custom_css';
$setting->type = 'textarea';
$setting->label = 'app.settings.custom_css';
$setting->value = '';
$setting->save();
} else {
$setting->type = 'textarea';
$setting->group_id = 4;
$setting->label = 'app.settings.custom_css';
$setting->save();
}
if (! $setting = Setting::find(11)) {
$setting = new Setting;
$setting->id = 11;
$setting->group_id = 4;
$setting->key = 'custom_js';
$setting->type = 'textarea';
$setting->label = 'app.settings.custom_js';
$setting->value = '';
$setting->save();
} else {
$setting->type = 'textarea';
$setting->group_id = 4;
$setting->label = 'app.settings.custom_js';
$setting->save();
}
if (! $home_tag = \App\Item::find(0)) {
$home_tag = new \App\Item;
$home_tag->id = 0;
$home_tag->title = 'app.dashboard';
$home_tag->pinned = 0;
$home_tag->url = '';
$home_tag->type = 1;
$home_tag->user_id = 0;
$home_tag->save();
$home_tag_id = $home_tag->id;
if ($home_tag_id != 0) {
Log::info("Home Tag returned with id $home_tag_id from db! Changing to 0.");
DB::update('update items set id = 0 where id = ?', [$home_tag_id]);
}
$homeapps = \App\Item::withoutGlobalScope('user_id')->doesntHave('parents')->get();
foreach ($homeapps as $app) {
if ($app->id === 0) {
continue;
}
$app->parents()->attach(0);
}
}
}
}

View file

@ -1,34 +0,0 @@
<?php
namespace Database\Seeders;
use App\User;
use Illuminate\Database\Seeder;
class UsersSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Groups
if (!User::find(1)) {
$user = new User;
$user->username = 'admin';
$user->email = 'admin@test.com';
$user->password = null;
$user->save();
$user_id = $user->id;
if ($user_id != 1) {
Log::info("First User returned with id $user_id from db! Changing to 1.");
DB::update('update users set id = 1 where id = ?', [$user_id]);
}
}
}
}

View file

@ -0,0 +1,17 @@
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->call(SettingsSeeder::class);
$this->call(UsersSeeder::class);
}
}

View file

@ -0,0 +1,258 @@
<?php
use Illuminate\Database\Seeder;
use App\Setting;
use App\SettingGroup;
class SettingsSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Groups
if(!$setting_group = SettingGroup::find(1)) {
$setting_group = new SettingGroup;
$setting_group->id = 1;
$setting_group->title = 'app.settings.system';
$setting_group->order = 0;
$setting_group->save();
} else {
$setting_group->title = 'app.settings.system';
$setting_group->save();
}
if(!$setting_group = SettingGroup::find(2)) {
$setting_group = new SettingGroup;
$setting_group->id = 2;
$setting_group->title = 'app.settings.appearance';
$setting_group->order = 1;
$setting_group->save();
} else {
$setting_group->title = 'app.settings.appearance';
$setting_group->save();
}
if(!$setting_group = SettingGroup::find(3)) {
$setting_group = new SettingGroup;
$setting_group->id = 3;
$setting_group->title = 'app.settings.miscellaneous';
$setting_group->order = 2;
$setting_group->save();
} else {
$setting_group->title = 'app.settings.miscellaneous';
$setting_group->save();
}
if(!$setting_group = SettingGroup::find(4)) {
$setting_group = new SettingGroup;
$setting_group->id = 4;
$setting_group->title = 'app.settings.advanced';
$setting_group->order = 3;
$setting_group->save();
} else {
$setting_group->title = 'app.settings.advanced';
$setting_group->save();
}
if($version = Setting::find(1)) {
$version->label = 'app.settings.version';
$version->value = config('app.version');
$version->save();
} else {
$setting = new Setting;
$setting->id = 1;
$setting->group_id = 1;
$setting->key = 'version';
$setting->type = 'text';
$setting->label = 'app.settings.version';
$setting->value = config('app.version');
$setting->system = true;
$setting->save();
}
if(!$setting = Setting::find(2)) {
$setting = new Setting;
$setting->id = 2;
$setting->group_id = 2;
$setting->key = 'background_image';
$setting->type = 'image';
$setting->label = 'app.settings.background_image';
$setting->save();
} else {
$setting->label = 'app.settings.background_image';
$setting->save();
}
if(!$setting = Setting::find(3)) {
$setting = new Setting;
$setting->id = 3;
$setting->group_id = 3;
$setting->key = 'homepage_search';
$setting->type = 'boolean';
$setting->label = 'app.settings.homepage_search';
$setting->save();
} else {
$setting->label = 'app.settings.homepage_search';
$setting->save();
}
$options = json_encode([
'none' => 'app.options.none',
'google' => 'app.options.google',
'ddg' => 'app.options.ddg',
'qwant' => 'app.options.qwant',
'bing' => 'app.options.bing',
'startpage' => 'app.options.startpage',
]);
if(!$setting = Setting::find(4)) {
$setting = new Setting;
$setting->id = 4;
$setting->group_id = 3;
$setting->key = 'search_provider';
$setting->type = 'select';
$setting->options = $options;
$setting->label = 'app.settings.search_provider';
$setting->save();
} else {
$setting->options = $options;
$setting->label = 'app.settings.search_provider';
$setting->save();
}
$language_options = json_encode([
'de' => 'Deutsch (German)',
'en' => 'English',
'fi' => 'Suomi (Finnish)',
'fr' => 'Français (French)',
'el' => 'Ελληνικά (Greek)',
'it' => 'Italiano (Italian)',
'no' => 'Norsk (Norwegian)',
'pl' => 'Polski (Polish)',
'sv' => 'Svenska (Swedish)',
'es' => 'Español (Spanish)',
'tr' => 'Türkçe (Turkish)',
]);
if($languages = Setting::find(5)) {
$languages->options = $language_options;
$languages->save();
} else {
$setting = new Setting;
$setting->id = 5;
$setting->group_id = 1;
$setting->key = 'language';
$setting->type = 'select';
$setting->label = 'app.settings.language';
$setting->options = $language_options;
$setting->value = 'en';
$setting->save();
}
$window_target_options = json_encode([
'current' => 'app.settings.window_target.current',
'heimdall' => 'app.settings.window_target.one',
'_blank' => 'app.settings.window_target.new',
]);
if(!$setting = Setting::find(7)) {
$setting = new Setting;
$setting->id = 7;
$setting->group_id = 3;
$setting->key = 'window_target';
$setting->type = 'select';
$setting->options = $window_target_options;
$setting->label = 'app.settings.window_target';
$setting->value = 'heimdall';
$setting->save();
} else {
$setting->options = $window_target_options;
$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();
}
if(!$setting = Setting::find(10)) {
$setting = new Setting;
$setting->id = 10;
$setting->group_id = 4;
$setting->key = 'custom_css';
$setting->type = 'textarea';
$setting->label = 'app.settings.custom_css';
$setting->value = '';
$setting->save();
} else {
$setting->type = 'textarea';
$setting->group_id = 4;
$setting->label = 'app.settings.custom_css';
$setting->save();
}
if(!$setting = Setting::find(11)) {
$setting = new Setting;
$setting->id = 11;
$setting->group_id = 4;
$setting->key = 'custom_js';
$setting->type = 'textarea';
$setting->label = 'app.settings.custom_js';
$setting->value = '';
$setting->save();
} else {
$setting->type = 'textarea';
$setting->group_id = 4;
$setting->label = 'app.settings.custom_js';
$setting->save();
}
if(!$home_tag = \App\Item::find(0)) {
$home_tag = new \App\Item;
$home_tag->id = 0;
$home_tag->title = 'app.dashboard';
$home_tag->pinned = 0;
$home_tag->url = '';
$home_tag->type = 1;
$home_tag->user_id = 0;
$home_tag->save();
$homeapps = \App\Item::withoutGlobalScope('user_id')->doesntHave('parents')->get();
foreach($homeapps as $app) {
if($app->id === 0) continue;
$app->parents()->attach(0);
}
}
}
}

View file

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Seeder;
use App\User;
class UsersSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Groups
if(!$user = User::find(1)) {
$user = new User;
$user->id = 1;
$user->username = 'admin';
$user->email = 'admin@test.com';
$user->password = null;
$user->save();
} else {
//$user->save();
}
}
}

16237
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,29 +2,22 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "npm run development", "dev": "npm run development",
"development": "mix", "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "mix watch", "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "mix watch -- --watch-options-poll=1000", "watch-poll": "npm run watch -- --watch-poll",
"hot": "mix watch --hot", "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production", "prod": "npm run production",
"production": "mix --production", "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
"lint": "eslint 'resources/assets/js/*'"
}, },
"devDependencies": { "devDependencies": {
"bootstrap-sass": "^3.4.3", "bootstrap-sass": "^3.4.1",
"eslint": "^8.28.0", "cross-env": "^5.2.0",
"eslint-config-airbnb-base": "^15.0.0", "jquery": "^3.4.1",
"eslint-config-prettier": "^8.5.0", "laravel-mix": "^4.0.16",
"eslint-plugin-import": "^2.26.0", "sass": "^1.21.0",
"eslint-plugin-prettier": "^4.2.1", "sass-loader": "7.*"
"jquery": "^3.6.3",
"laravel-mix": "^6.0.49",
"prettier": "^2.8.1",
"sass": "^1.56.1",
"sass-loader": "13.*"
}, },
"dependencies": { "dependencies": {
"select2": "^4.0.13", "select2": "^4.0.7"
"sortablejs": "^1.15.0"
} }
} }

View file

@ -1,24 +0,0 @@
<?xml version="1.0"?>
<ruleset name="PHP_CodeSniffer">
<description>The coding standard for our project.</description>
<rule ref="PSR2"/>
<file>app</file>
<file>bootstrap</file>
<file>config</file>
<file>database</file>
<file>resources</file>
<file>routes</file>
<exclude-pattern>bootstrap/cache/*</exclude-pattern>
<exclude-pattern>app/SupportedApps/*</exclude-pattern>
<exclude-pattern>resources/lang/*</exclude-pattern>
<exclude-pattern>bootstrap/autoload.php</exclude-pattern>
<exclude-pattern>*/migrations/*</exclude-pattern>
<exclude-pattern>*/seeds/*</exclude-pattern>
<exclude-pattern>*.blade.php</exclude-pattern>
<exclude-pattern>*.js</exclude-pattern>
<!-- Show progression -->
<arg value="p"/>
</ruleset>

View file

@ -1,27 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true"> <phpunit backupGlobals="false"
<coverage processUncoveredFiles="true"> backupStaticAttributes="false"
<include> bootstrap="vendor/autoload.php"
<directory suffix=".php">./app</directory> colors="true"
</include> convertErrorsToExceptions="true"
</coverage> convertNoticesToExceptions="true"
<testsuites> convertWarningsToExceptions="true"
<testsuite name="Unit"> processIsolation="false"
<directory suffix="Test.php">./tests/Unit</directory> stopOnFailure="false">
</testsuite> <testsuites>
<testsuite name="Feature"> <testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory> <directory suffix="Test.php">./tests/Feature</directory>
</testsuite> </testsuite>
</testsuites>
<php> <testsuite name="Unit">
<server name="APP_ENV" value="testing"/> <directory suffix="Test.php">./tests/Unit</directory>
<server name="BCRYPT_ROUNDS" value="4"/> </testsuite>
<server name="CACHE_DRIVER" value="array"/> </testsuites>
<!-- <server name="DB_CONNECTION" value="sqlite"/> --> <filter>
<!-- <server name="DB_DATABASE" value=":memory:"/> --> <whitelist processUncoveredFilesFromWhitelist="true">
<server name="MAIL_MAILER" value="array"/> <directory suffix=".php">./app</directory>
<server name="QUEUE_CONNECTION" value="sync"/> </whitelist>
<server name="SESSION_DRIVER" value="array"/> </filter>
<server name="TELESCOPE_ENABLED" value="false"/> <php>
</php> <env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
</php>
</phpunit> </phpunit>

2069
public/css/app.css vendored

File diff suppressed because one or more lines are too long

749
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View file

@ -483,14 +483,10 @@ var icons = {
"arrow-rotate-right": [512, 512, [8635, "arrow-right-rotate", "arrow-rotate-forward", "redo"], "f01e", "M496 48V192c0 17.69-14.31 32-32 32H320c-17.69 0-32-14.31-32-32s14.31-32 32-32h63.39c-29.97-39.7-77.25-63.78-127.6-63.78C167.7 96.22 96 167.9 96 256s71.69 159.8 159.8 159.8c34.88 0 68.03-11.03 95.88-31.94c14.22-10.53 34.22-7.75 44.81 6.375c10.59 14.16 7.75 34.22-6.375 44.81c-39.03 29.28-85.36 44.86-134.2 44.86C132.5 479.9 32 379.4 32 256s100.5-223.9 223.9-223.9c69.15 0 134 32.47 176.1 86.12V48c0-17.69 14.31-32 32-32S496 30.31 496 48z"], "arrow-rotate-right": [512, 512, [8635, "arrow-right-rotate", "arrow-rotate-forward", "redo"], "f01e", "M496 48V192c0 17.69-14.31 32-32 32H320c-17.69 0-32-14.31-32-32s14.31-32 32-32h63.39c-29.97-39.7-77.25-63.78-127.6-63.78C167.7 96.22 96 167.9 96 256s71.69 159.8 159.8 159.8c34.88 0 68.03-11.03 95.88-31.94c14.22-10.53 34.22-7.75 44.81 6.375c10.59 14.16 7.75 34.22-6.375 44.81c-39.03 29.28-85.36 44.86-134.2 44.86C132.5 479.9 32 379.4 32 256s100.5-223.9 223.9-223.9c69.15 0 134 32.47 176.1 86.12V48c0-17.69 14.31-32 32-32S496 30.31 496 48z"],
"ban": [512, 512, [], "f05e", "M256 8C119.034 8 8 119.033 8 256s111.034 248 248 248 248-111.034 248-248S392.967 8 256 8zm130.108 117.892c65.448 65.448 70 165.481 20.677 235.637L150.47 105.216c70.204-49.356 170.226-44.735 235.638 20.676zM125.892 386.108c-65.448-65.448-70-165.481-20.677-235.637L361.53 406.784c-70.203 49.356-170.226 44.736-235.638-20.676z"], "ban": [512, 512, [], "f05e", "M256 8C119.034 8 8 119.033 8 256s111.034 248 248 248 248-111.034 248-248S392.967 8 256 8zm130.108 117.892c65.448 65.448 70 165.481 20.677 235.637L150.47 105.216c70.204-49.356 170.226-44.735 235.638 20.676zM125.892 386.108c-65.448-65.448-70-165.481-20.677-235.637L361.53 406.784c-70.203 49.356-170.226 44.736-235.638-20.676z"],
"check": [512, 512, [], "f00c", "M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"], "check": [512, 512, [], "f00c", "M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"],
"circle-check": [512, 512, [61533, "check-circle"], "f058", ["M0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256C512 397.4 397.4 512 256 512C114.6 512 0 397.4 0 256zM371.8 211.8C382.7 200.9 382.7 183.1 371.8 172.2C360.9 161.3 343.1 161.3 332.2 172.2L224 280.4L179.8 236.2C168.9 225.3 151.1 225.3 140.2 236.2C129.3 247.1 129.3 264.9 140.2 275.8L204.2 339.8C215.1 350.7 232.9 350.7 243.8 339.8L371.8 211.8z", "M371.8 172.2C382.7 183.1 382.7 200.9 371.8 211.8L243.8 339.8C232.9 350.7 215.1 350.7 204.2 339.8L140.2 275.8C129.3 264.9 129.3 247.1 140.2 236.2C151.1 225.3 168.9 225.3 179.8 236.2L224 280.4L332.2 172.2C343.1 161.3 360.9 161.3 371.8 172.2V172.2z"]],
"circle-xmark": [512, 512, [61532, "times-circle", "xmark-circle"], "f057", ["M0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256C512 397.4 397.4 512 256 512C114.6 512 0 397.4 0 256zM175 208.1L222.1 255.1L175 303C165.7 312.4 165.7 327.6 175 336.1C184.4 346.3 199.6 346.3 208.1 336.1L255.1 289.9L303 336.1C312.4 346.3 327.6 346.3 336.1 336.1C346.3 327.6 346.3 312.4 336.1 303L289.9 255.1L336.1 208.1C346.3 199.6 346.3 184.4 336.1 175C327.6 165.7 312.4 165.7 303 175L255.1 222.1L208.1 175C199.6 165.7 184.4 165.7 175 175C165.7 184.4 165.7 199.6 175 208.1V208.1z", "M255.1 222.1L303 175C312.4 165.7 327.6 165.7 336.1 175C346.3 184.4 346.3 199.6 336.1 208.1L289.9 255.1L336.1 303C346.3 312.4 346.3 327.6 336.1 336.1C327.6 346.3 312.4 346.3 303 336.1L255.1 289.9L208.1 336.1C199.6 346.3 184.4 346.3 175 336.1C165.7 327.6 165.7 312.4 175 303L222.1 255.1L175 208.1C165.7 199.6 165.7 184.4 175 175C184.4 165.7 199.6 165.7 208.1 175L255.1 222.1z"]],
"cloud-download": [640, 512, [], "f0ed", "M537.6 226.6c4.1-10.7 6.4-22.4 6.4-34.6 0-53-43-96-96-96-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32c-88.4 0-160 71.6-160 160 0 2.7.1 5.4.2 8.1C40.2 219.8 0 273.2 0 336c0 79.5 64.5 144 144 144h368c70.7 0 128-57.3 128-128 0-61.9-44-113.6-102.4-125.4zm-139.9 93L305 412.3c-9.4 9.4-24.6 9.4-33.9 0l-92.7-92.7c-9.4-9.4-9.4-24.6 0-33.9l10.8-10.8c9.6-9.6 25.2-9.3 34.5.5l32.4 34.5V184c0-13.3 10.7-24 24-24h16c13.3 0 24 10.7 24 24v125.9l32.4-34.5c9.3-9.9 24.9-10.1 34.5-.5l10.8 10.8c9.2 9.3 9.2 24.5-.1 33.9z"], "cloud-download": [640, 512, [], "f0ed", "M537.6 226.6c4.1-10.7 6.4-22.4 6.4-34.6 0-53-43-96-96-96-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32c-88.4 0-160 71.6-160 160 0 2.7.1 5.4.2 8.1C40.2 219.8 0 273.2 0 336c0 79.5 64.5 144 144 144h368c70.7 0 128-57.3 128-128 0-61.9-44-113.6-102.4-125.4zm-139.9 93L305 412.3c-9.4 9.4-24.6 9.4-33.9 0l-92.7-92.7c-9.4-9.4-9.4-24.6 0-33.9l10.8-10.8c9.6-9.6 25.2-9.3 34.5.5l32.4 34.5V184c0-13.3 10.7-24 24-24h16c13.3 0 24 10.7 24 24v125.9l32.4-34.5c9.3-9.9 24.9-10.1 34.5-.5l10.8 10.8c9.2 9.3 9.2 24.5-.1 33.9z"],
"cogs": [640, 512, [], "f085", "M512.1 191l-8.2 14.3c-3 5.3-9.4 7.5-15.1 5.4-11.8-4.4-22.6-10.7-32.1-18.6-4.6-3.8-5.8-10.5-2.8-15.7l8.2-14.3c-6.9-8-12.3-17.3-15.9-27.4h-16.5c-6 0-11.2-4.3-12.2-10.3-2-12-2.1-24.6 0-37.1 1-6 6.2-10.4 12.2-10.4h16.5c3.6-10.1 9-19.4 15.9-27.4l-8.2-14.3c-3-5.2-1.9-11.9 2.8-15.7 9.5-7.9 20.4-14.2 32.1-18.6 5.7-2.1 12.1.1 15.1 5.4l8.2 14.3c10.5-1.9 21.2-1.9 31.7 0L552 6.3c3-5.3 9.4-7.5 15.1-5.4 11.8 4.4 22.6 10.7 32.1 18.6 4.6 3.8 5.8 10.5 2.8 15.7l-8.2 14.3c6.9 8 12.3 17.3 15.9 27.4h16.5c6 0 11.2 4.3 12.2 10.3 2 12 2.1 24.6 0 37.1-1 6-6.2 10.4-12.2 10.4h-16.5c-3.6 10.1-9 19.4-15.9 27.4l8.2 14.3c3 5.2 1.9 11.9-2.8 15.7-9.5 7.9-20.4 14.2-32.1 18.6-5.7 2.1-12.1-.1-15.1-5.4l-8.2-14.3c-10.4 1.9-21.2 1.9-31.7 0zm-10.5-58.8c38.5 29.6 82.4-14.3 52.8-52.8-38.5-29.7-82.4 14.3-52.8 52.8zM386.3 286.1l33.7 16.8c10.1 5.8 14.5 18.1 10.5 29.1-8.9 24.2-26.4 46.4-42.6 65.8-7.4 8.9-20.2 11.1-30.3 5.3l-29.1-16.8c-16 13.7-34.6 24.6-54.9 31.7v33.6c0 11.6-8.3 21.6-19.7 23.6-24.6 4.2-50.4 4.4-75.9 0-11.5-2-20-11.9-20-23.6V418c-20.3-7.2-38.9-18-54.9-31.7L74 403c-10 5.8-22.9 3.6-30.3-5.3-16.2-19.4-33.3-41.6-42.2-65.7-4-10.9.4-23.2 10.5-29.1l33.3-16.8c-3.9-20.9-3.9-42.4 0-63.4L12 205.8c-10.1-5.8-14.6-18.1-10.5-29 8.9-24.2 26-46.4 42.2-65.8 7.4-8.9 20.2-11.1 30.3-5.3l29.1 16.8c16-13.7 34.6-24.6 54.9-31.7V57.1c0-11.5 8.2-21.5 19.6-23.5 24.6-4.2 50.5-4.4 76-.1 11.5 2 20 11.9 20 23.6v33.6c20.3 7.2 38.9 18 54.9 31.7l29.1-16.8c10-5.8 22.9-3.6 30.3 5.3 16.2 19.4 33.2 41.6 42.1 65.8 4 10.9.1 23.2-10 29.1l-33.7 16.8c3.9 21 3.9 42.5 0 63.5zm-117.6 21.1c59.2-77-28.7-164.9-105.7-105.7-59.2 77 28.7 164.9 105.7 105.7zm243.4 182.7l-8.2 14.3c-3 5.3-9.4 7.5-15.1 5.4-11.8-4.4-22.6-10.7-32.1-18.6-4.6-3.8-5.8-10.5-2.8-15.7l8.2-14.3c-6.9-8-12.3-17.3-15.9-27.4h-16.5c-6 0-11.2-4.3-12.2-10.3-2-12-2.1-24.6 0-37.1 1-6 6.2-10.4 12.2-10.4h16.5c3.6-10.1 9-19.4 15.9-27.4l-8.2-14.3c-3-5.2-1.9-11.9 2.8-15.7 9.5-7.9 20.4-14.2 32.1-18.6 5.7-2.1 12.1.1 15.1 5.4l8.2 14.3c10.5-1.9 21.2-1.9 31.7 0l8.2-14.3c3-5.3 9.4-7.5 15.1-5.4 11.8 4.4 22.6 10.7 32.1 18.6 4.6 3.8 5.8 10.5 2.8 15.7l-8.2 14.3c6.9 8 12.3 17.3 15.9 27.4h16.5c6 0 11.2 4.3 12.2 10.3 2 12 2.1 24.6 0 37.1-1 6-6.2 10.4-12.2 10.4h-16.5c-3.6 10.1-9 19.4-15.9 27.4l8.2 14.3c3 5.2 1.9 11.9-2.8 15.7-9.5 7.9-20.4 14.2-32.1 18.6-5.7 2.1-12.1-.1-15.1-5.4l-8.2-14.3c-10.4 1.9-21.2 1.9-31.7 0zM501.6 431c38.5 29.6 82.4-14.3 52.8-52.8-38.5-29.6-82.4 14.3-52.8 52.8z"], "cogs": [640, 512, [], "f085", "M512.1 191l-8.2 14.3c-3 5.3-9.4 7.5-15.1 5.4-11.8-4.4-22.6-10.7-32.1-18.6-4.6-3.8-5.8-10.5-2.8-15.7l8.2-14.3c-6.9-8-12.3-17.3-15.9-27.4h-16.5c-6 0-11.2-4.3-12.2-10.3-2-12-2.1-24.6 0-37.1 1-6 6.2-10.4 12.2-10.4h16.5c3.6-10.1 9-19.4 15.9-27.4l-8.2-14.3c-3-5.2-1.9-11.9 2.8-15.7 9.5-7.9 20.4-14.2 32.1-18.6 5.7-2.1 12.1.1 15.1 5.4l8.2 14.3c10.5-1.9 21.2-1.9 31.7 0L552 6.3c3-5.3 9.4-7.5 15.1-5.4 11.8 4.4 22.6 10.7 32.1 18.6 4.6 3.8 5.8 10.5 2.8 15.7l-8.2 14.3c6.9 8 12.3 17.3 15.9 27.4h16.5c6 0 11.2 4.3 12.2 10.3 2 12 2.1 24.6 0 37.1-1 6-6.2 10.4-12.2 10.4h-16.5c-3.6 10.1-9 19.4-15.9 27.4l8.2 14.3c3 5.2 1.9 11.9-2.8 15.7-9.5 7.9-20.4 14.2-32.1 18.6-5.7 2.1-12.1-.1-15.1-5.4l-8.2-14.3c-10.4 1.9-21.2 1.9-31.7 0zm-10.5-58.8c38.5 29.6 82.4-14.3 52.8-52.8-38.5-29.7-82.4 14.3-52.8 52.8zM386.3 286.1l33.7 16.8c10.1 5.8 14.5 18.1 10.5 29.1-8.9 24.2-26.4 46.4-42.6 65.8-7.4 8.9-20.2 11.1-30.3 5.3l-29.1-16.8c-16 13.7-34.6 24.6-54.9 31.7v33.6c0 11.6-8.3 21.6-19.7 23.6-24.6 4.2-50.4 4.4-75.9 0-11.5-2-20-11.9-20-23.6V418c-20.3-7.2-38.9-18-54.9-31.7L74 403c-10 5.8-22.9 3.6-30.3-5.3-16.2-19.4-33.3-41.6-42.2-65.7-4-10.9.4-23.2 10.5-29.1l33.3-16.8c-3.9-20.9-3.9-42.4 0-63.4L12 205.8c-10.1-5.8-14.6-18.1-10.5-29 8.9-24.2 26-46.4 42.2-65.8 7.4-8.9 20.2-11.1 30.3-5.3l29.1 16.8c16-13.7 34.6-24.6 54.9-31.7V57.1c0-11.5 8.2-21.5 19.6-23.5 24.6-4.2 50.5-4.4 76-.1 11.5 2 20 11.9 20 23.6v33.6c20.3 7.2 38.9 18 54.9 31.7l29.1-16.8c10-5.8 22.9-3.6 30.3 5.3 16.2 19.4 33.2 41.6 42.1 65.8 4 10.9.1 23.2-10 29.1l-33.7 16.8c3.9 21 3.9 42.5 0 63.5zm-117.6 21.1c59.2-77-28.7-164.9-105.7-105.7-59.2 77 28.7 164.9 105.7 105.7zm243.4 182.7l-8.2 14.3c-3 5.3-9.4 7.5-15.1 5.4-11.8-4.4-22.6-10.7-32.1-18.6-4.6-3.8-5.8-10.5-2.8-15.7l8.2-14.3c-6.9-8-12.3-17.3-15.9-27.4h-16.5c-6 0-11.2-4.3-12.2-10.3-2-12-2.1-24.6 0-37.1 1-6 6.2-10.4 12.2-10.4h16.5c3.6-10.1 9-19.4 15.9-27.4l-8.2-14.3c-3-5.2-1.9-11.9 2.8-15.7 9.5-7.9 20.4-14.2 32.1-18.6 5.7-2.1 12.1.1 15.1 5.4l8.2 14.3c10.5-1.9 21.2-1.9 31.7 0l8.2-14.3c3-5.3 9.4-7.5 15.1-5.4 11.8 4.4 22.6 10.7 32.1 18.6 4.6 3.8 5.8 10.5 2.8 15.7l-8.2 14.3c6.9 8 12.3 17.3 15.9 27.4h16.5c6 0 11.2 4.3 12.2 10.3 2 12 2.1 24.6 0 37.1-1 6-6.2 10.4-12.2 10.4h-16.5c-3.6 10.1-9 19.4-15.9 27.4l8.2 14.3c3 5.2 1.9 11.9-2.8 15.7-9.5 7.9-20.4 14.2-32.1 18.6-5.7 2.1-12.1-.1-15.1-5.4l-8.2-14.3c-10.4 1.9-21.2 1.9-31.7 0zM501.6 431c38.5 29.6 82.4-14.3 52.8-52.8-38.5-29.6-82.4 14.3-52.8 52.8z"],
"edit": [576, 512, [], "f044", "M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"], "edit": [576, 512, [], "f044", "M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"],
"exchange": [512, 512, [], "f0ec", "M0 168v-16c0-13.255 10.745-24 24-24h381.97l-30.467-27.728c-9.815-9.289-10.03-24.846-.474-34.402l10.84-10.84c9.373-9.373 24.568-9.373 33.941 0l82.817 82.343c12.497 12.497 12.497 32.758 0 45.255l-82.817 82.343c-9.373 9.373-24.569 9.373-33.941 0l-10.84-10.84c-9.556-9.556-9.341-25.114.474-34.402L405.97 192H24c-13.255 0-24-10.745-24-24zm488 152H106.03l30.467-27.728c9.815-9.289 10.03-24.846.474-34.402l-10.84-10.84c-9.373-9.373-24.568-9.373-33.941 0L9.373 329.373c-12.497 12.497-12.497 32.758 0 45.255l82.817 82.343c9.373 9.373 24.569 9.373 33.941 0l10.84-10.84c9.556-9.556 9.341-25.113-.474-34.402L106.03 384H488c13.255 0 24-10.745 24-24v-16c0-13.255-10.745-24-24-24z"], "exchange": [512, 512, [], "f0ec", "M0 168v-16c0-13.255 10.745-24 24-24h381.97l-30.467-27.728c-9.815-9.289-10.03-24.846-.474-34.402l10.84-10.84c9.373-9.373 24.568-9.373 33.941 0l82.817 82.343c12.497 12.497 12.497 32.758 0 45.255l-82.817 82.343c-9.373 9.373-24.569 9.373-33.941 0l-10.84-10.84c-9.556-9.556-9.341-25.114.474-34.402L405.97 192H24c-13.255 0-24-10.745-24-24zm488 152H106.03l30.467-27.728c9.815-9.289 10.03-24.846.474-34.402l-10.84-10.84c-9.373-9.373-24.568-9.373-33.941 0L9.373 329.373c-12.497 12.497-12.497 32.758 0 45.255l82.817 82.343c9.373 9.373 24.569 9.373 33.941 0l10.84-10.84c9.556-9.556 9.341-25.113-.474-34.402L106.03 384H488c13.255 0 24-10.745 24-24v-16c0-13.255-10.745-24-24-24z"],
"file-arrow-down": [384, 512, ["file-download"], "f56d", ["M256 128V0H48C21.49 0 0 21.49 0 48v416C0 490.5 21.49 512 48 512h288c26.51 0 48-21.49 48-48V128H256zM288.1 360.1l-80 80c-9.375 9.375-24.56 9.375-33.94 0l-80-80c-9.375-9.375-9.375-24.56 0-33.94C99.72 322.3 105.8 320 112 320s12.28 2.344 16.97 7.031L168 366.1V248C168 234.8 178.8 224 192 224s24 10.75 24 24v118.1l39.03-39.03c9.375-9.375 24.56-9.375 33.94 0S298.3 351.6 288.1 360.1z", "M256 0v128h128L256 0zM255 327L216 366.1V248C216 234.8 205.3 224 192 224S168 234.8 168 248v118.1l-39.03-39.03C124.3 322.3 118.2 320 112 320s-12.28 2.344-16.97 7.031c-9.375 9.375-9.375 24.56 0 33.94l80 80c9.375 9.375 24.56 9.375 33.94 0l80-80c9.375-9.375 9.375-24.56 0-33.94S264.4 317.7 255 327z"]],
"file-arrow-up": [384, 512, ["file-upload"], "f574", ["M256 128V0H48C21.49 0 0 21.49 0 48v416C0 490.5 21.49 512 48 512h288c26.51 0 48-21.49 48-48V128H256zM288.1 344.1C284.3 349.7 278.2 352 272 352s-12.28-2.344-16.97-7.031L216 305.9V424c0 13.25-10.75 24-24 24s-24-10.75-24-24V305.9l-39.03 39.03c-9.375 9.375-24.56 9.375-33.94 0s-9.375-24.56 0-33.94l80-80c9.375-9.375 24.56-9.375 33.94 0l80 80C298.3 320.4 298.3 335.6 288.1 344.1z", "M256 0v128h128L256 0zM208.1 231c-9.375-9.375-24.56-9.375-33.94 0l-80 80c-9.375 9.375-9.375 24.56 0 33.94s24.56 9.375 33.94 0L168 305.9V424C168 437.3 178.8 448 192 448s24-10.75 24-24V305.9l39.03 39.03C259.7 349.7 265.8 352 272 352s12.28-2.344 16.97-7.031c9.375-9.375 9.375-24.56 0-33.94L208.1 231z"]],
"list": [512, 512, [], "f03a", "M128 116V76c0-8.837 7.163-16 16-16h352c8.837 0 16 7.163 16 16v40c0 8.837-7.163 16-16 16H144c-8.837 0-16-7.163-16-16zm16 176h352c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H144c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h352c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H144c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zM16 144h64c8.837 0 16-7.163 16-16V64c0-8.837-7.163-16-16-16H16C7.163 48 0 55.163 0 64v64c0 8.837 7.163 16 16 16zm0 160h64c8.837 0 16-7.163 16-16v-64c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v64c0 8.837 7.163 16 16 16zm0 160h64c8.837 0 16-7.163 16-16v-64c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v64c0 8.837 7.163 16 16 16z"], "list": [512, 512, [], "f03a", "M128 116V76c0-8.837 7.163-16 16-16h352c8.837 0 16 7.163 16 16v40c0 8.837-7.163 16-16 16H144c-8.837 0-16-7.163-16-16zm16 176h352c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H144c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h352c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H144c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zM16 144h64c8.837 0 16-7.163 16-16V64c0-8.837-7.163-16-16-16H16C7.163 48 0 55.163 0 64v64c0 8.837 7.163 16 16 16zm0 160h64c8.837 0 16-7.163 16-16v-64c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v64c0 8.837 7.163 16 16 16zm0 160h64c8.837 0 16-7.163 16-16v-64c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v64c0 8.837 7.163 16 16 16z"],
"pencil": [512, 512, [], "f040", "M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"], "pencil": [512, 512, [], "f040", "M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"],
"plus": [448, 512, [], "f067", "M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"], "plus": [448, 512, [], "f067", "M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"],

2
public/js/jquery-3.6.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

13
public/js/jquery-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
{ {
"/css/app.css": "/css/app.css?id=55e02812d34a73b4386802d27fbcd6e8", "/css/app.css": "/css/app.css?id=bc18c3a0e8475fe00a5a",
"/js/app.js": "/js/app.js?id=3377b9b80073713e4dc54937c94aa6ad" "/js/app.js": "/js/app.js?id=c95edea425171c6ce8f6"
} }

View file

@ -22,10 +22,10 @@ Why not use it as your browser start page? It even has the ability to include a
![Heimdall demo animation](https://i.imgur.com/MrC4QpN.gif) ![Heimdall demo animation](https://i.imgur.com/MrC4QpN.gif)
## Video ## Video
If you want to see a quick video of Heimdall in use, go to https://youtu.be/GXnnMAxPzMc If you want to see a quick video of it in use, go to https://youtu.be/GXnnMAxPzMc
## Supported applications ## Supported applications
You can use the app to link to any site or application, but Foundation apps will auto fill in the icon for the app and supply a default color for the tile. In addition, Enhanced apps allow you provide details to an apps API, allowing you to view live stats directly on the dashboad. For example, the NZBGet and Sabnzbd Enhanced apps will display the queue size, and download speed while something is downloading. You can use the app to link to any site or application, but Foundation apps will auto fill in the icon for the app and supply a default color for the tile. In addition Enhanced apps allow you provide details to an apps API, allowing you to view live stats directly on the dashboad. For example, the NZBGet and Sabnzbd Enhanced apps will display the queue size and download speed while something is downloading.
Supported applications are recognized by the title of the application as entered in the title field when adding an application. For example, to add a link to pfSense, begin by typing "p" in the title field and then select "pfSense" from the list of supported applications. Supported applications are recognized by the title of the application as entered in the title field when adding an application. For example, to add a link to pfSense, begin by typing "p" in the title field and then select "pfSense" from the list of supported applications.
@ -34,9 +34,9 @@ Supported applications are recognized by the title of the application as entered
[![foundationapps](https://img.shields.io/badge/dynamic/json.svg?label=Foundation%20Apps&url=https%3A%2F%2Fapps.heimdall.site%2Fstats&query=foundation_apps&colorB=3f8483&style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAjCAMAAACw/5reAAAAnFBMVEUAAADu7u7u7u7u7u7u7u7x8fHu7u7u7u7u7u7u7u7u7u7u7u7r6+vu7u7v7+/u7u7t7e3v7+/v7+/u7u7u7u7u7u7u7u7u7u7u7u7u7u7v7+/u7u7p6ent7e3v7+/v7+/v7+/u7u7u7u7u7u7u7u7t7e3////u7u7u7u7u7u7u7u7w8PDw8PDt7e3u7u7t7e3s7Ozu7u7t7e3u7u4TnCP6AAAAM3RSTlMA+9n3phHw3czC088M5Y5zG6mflWdJFumyfj4sB2NeTi7hiWlDOQPGt5lsMiG9hFQntpFqxQJtAAABnElEQVQoz2WRh3KrQAxFtYWO6ZhucItrynv6/3/LFnA24c6wurpnYBkJZvXduNix6+GXTo8qWnxUPU4m2w0O1ktTozPsftiZpejGlm7C2MWUnRcWOohIo36+PaKyDZdLUOgDXvqQfaT9kwkfvP3AN18E7Kl8hkJHMHSXSSadxaTtTNjJhMkfjFHKMqGlolg4T7mtCbcq8gBCotxkwklFLIQSlQoTHnVWQqzNxYQuzpfmqGVMc5ijHK5yAuIhxbZ5p/S92RZkjv5BKs6aosSIr0JrcXBo1FtICVINKRKK6u0GnraoN84O5KbhjRwYzxCJnQCMtotkdNxjq2F7dJ2RoGuXIBTvc3ROthdmat6hZ7cOyfcxKGV+wTxBkxQxTQTzWOFny/7qS2nzx37T7nbtZj9xu7zUr/323nVy0sQnhwMJktSZrl5v7CjgSQmWi+haUCY8sH4tyc/FGSKGouS+WqBJm8U2NIE/+nLu2tzpF/xVNGy02QzRClafC/ysVpDzQJuA8xXsKl8bv+pgpXz57H9Yy3J1lQNY62wUrW+mdzrylWS0QwAAAABJRU5ErkJggg==)](https://apps.heimdall.site/applications/foundation) [![foundationapps](https://img.shields.io/badge/dynamic/json.svg?label=Foundation%20Apps&url=https%3A%2F%2Fapps.heimdall.site%2Fstats&query=foundation_apps&colorB=3f8483&style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAjCAMAAACw/5reAAAAnFBMVEUAAADu7u7u7u7u7u7u7u7x8fHu7u7u7u7u7u7u7u7u7u7u7u7r6+vu7u7v7+/u7u7t7e3v7+/v7+/u7u7u7u7u7u7u7u7u7u7u7u7u7u7v7+/u7u7p6ent7e3v7+/v7+/v7+/u7u7u7u7u7u7u7u7t7e3////u7u7u7u7u7u7u7u7w8PDw8PDt7e3u7u7t7e3s7Ozu7u7t7e3u7u4TnCP6AAAAM3RSTlMA+9n3phHw3czC088M5Y5zG6mflWdJFumyfj4sB2NeTi7hiWlDOQPGt5lsMiG9hFQntpFqxQJtAAABnElEQVQoz2WRh3KrQAxFtYWO6ZhucItrynv6/3/LFnA24c6wurpnYBkJZvXduNix6+GXTo8qWnxUPU4m2w0O1ktTozPsftiZpejGlm7C2MWUnRcWOohIo36+PaKyDZdLUOgDXvqQfaT9kwkfvP3AN18E7Kl8hkJHMHSXSSadxaTtTNjJhMkfjFHKMqGlolg4T7mtCbcq8gBCotxkwklFLIQSlQoTHnVWQqzNxYQuzpfmqGVMc5ijHK5yAuIhxbZ5p/S92RZkjv5BKs6aosSIr0JrcXBo1FtICVINKRKK6u0GnraoN84O5KbhjRwYzxCJnQCMtotkdNxjq2F7dJ2RoGuXIBTvc3ROthdmat6hZ7cOyfcxKGV+wTxBkxQxTQTzWOFny/7qS2nzx37T7nbtZj9xu7zUr/323nVy0sQnhwMJktSZrl5v7CjgSQmWi+haUCY8sH4tyc/FGSKGouS+WqBJm8U2NIE/+nLu2tzpF/xVNGy02QzRClafC/ysVpDzQJuA8xXsKl8bv+pgpXz57H9Yy3J1lQNY62wUrW+mdzrylWS0QwAAAABJRU5ErkJggg==)](https://apps.heimdall.site/applications/foundation)
## Installing ## Installing
Apart from the Laravel 8 dependencies, namely PHP >= 7.4.32, BCMath PHP Extension, INTL PHP Extension, Ctype PHP Extension, Fileinfo PHP extension, JSON PHP Extension, Mbstring PHP Extension, OpenSSL PHP Extension, PDO PHP Extension, Tokenizer PHP Extension, XML PHP Extension, the only other thing Heimdall needs is sqlite support and zip support (php-zip). Apart from the Laravel dependencies, namely PHP >= 7.2.5, BCMath PHP Extension, Ctype PHP Extension, Fileinfo PHP extension, JSON PHP Extension, Mbstring PHP Extension, OpenSSL PHP Extension, PDO PHP Extension, Tokenizer PHP Extension, XML PHP Extension, the only other thing Heimdall needs is sqlite support and zip support (php-zip).
If you find you can't change the background make sure `php_fileinfo` is enabled in your php.ini. I believe `php_fileinfo` should be enabled by default, but one user came across the issue on a windows system. If you find you can't change the background make sure `php_fileinfo` is enabled in your php.ini. I believe it should be by default, but one user came across the issue on a windows system.
Installation is as simple as cloning the repository somewhere, or downloading and extracting the zip/tar and pointing your httpd document root to the `/public` folder then creating the .env file and generating an encryption key (this is all taken care of for you with the docker). Installation is as simple as cloning the repository somewhere, or downloading and extracting the zip/tar and pointing your httpd document root to the `/public` folder then creating the .env file and generating an encryption key (this is all taken care of for you with the docker).
@ -62,10 +62,10 @@ Options are stored in `/storage/app/searchproviders.yaml` (`/config/www/searchpr
Consider contributing to https://github.com/linuxserver/Heimdall/discussions/categories/search-providers to help others add new ones. Consider contributing to https://github.com/linuxserver/Heimdall/discussions/categories/search-providers to help others add new ones.
The item at the top of the list `Tiles` allows you to search for apps on your dashboard by name, this can be helpful when you have lots of icons. The item at the top of the list `Tiles` allows you to search for apps on your dashboard by name, helpful when you have lots of icons.
## New background image not being set ## New background image not being set
If you are using the docker image or a default php install you may find images over 2MB won't get set as the background image, you just need to change the `upload_max_filesize` in the php.ini. If you are using the docker image or a default php install you may find images over 2MB wont get set as the background image, you just need to change the `upload_max_filesize` in the php.ini.
If you are using the linuxserver.io docker image simply edit `/path/to/config/php/php-local.ini` and add `upload_max_filesize = 30M` to the end. If you are using the linuxserver.io docker image simply edit `/path/to/config/php/php-local.ini` and add `upload_max_filesize = 30M` to the end.
@ -75,7 +75,7 @@ If you are running the docker and the EnhancedApps you are using are also in doc
You can do this by using `http(s)://docker_name:port` in the config section. Instead of the name you can use the internal docker ip, this usually starts with `172.` You can do this by using `http(s)://docker_name:port` in the config section. Instead of the name you can use the internal docker ip, this usually starts with `172.`
## Languages ## Languages
The app has been translated into several languages; however, the quality of the translations could benefit from some work. If you would like to improve them, or help with other translations, they are stored in `/resources/lang/`. The app has been translated into several languages; however, the quality of the translations could do with work. If you would like to improve them, or help with other translations, they are stored in `/resources/lang/`.
To create a new language translation, make a new folder with the ISO 3166-1 alpha-2 code as the name, copy `app.php` from `/resources/lang/en/app.php` into your new folder and replace the English strings. To create a new language translation, make a new folder with the ISO 3166-1 alpha-2 code as the name, copy `app.php` from `/resources/lang/en/app.php` into your new folder and replace the English strings.
@ -83,28 +83,14 @@ When you are finished, create a pull request.
Currently added languages are Currently added languages are
- Breton
- Chinese
- Danish
- Dutch
- English - English
- German
- Finnish - Finnish
- French - French
- German
- Greek
- Hungarian
- Italian
- Japanese
- Korean
- Lombard
- Norwegian
- Polish
- Portuguese
- Russian
- Slovenian
- Spanish
- Swedish - Swedish
- Spanish
- Turkish - Turkish
- Russian
## Web Server Configuration ## Web Server Configuration
@ -145,15 +131,15 @@ location / {
try_files $uri $uri/ /index.php?$query_string; try_files $uri $uri/ /index.php?$query_string;
} }
``` ```
Someone was using the same Nginx setup to both run this and reverse proxy Plex. Plex is served from `/web` so their location was interfering with the `/webfonts`. Someone was using the same nginx setup to both run this and reverse proxy Plex, Plex is served from `/web` so their location was interfering with the `/webfonts`.
Therefore, if your fonts aren't showing because you have a location for `/web`, add the following: Therefore, if your fonts aren't showing because you have a location for `/web`, add the following
``` ```
location /webfonts { location /webfonts {
try_files $uri $uri/; try_files $uri $uri/;
} }
``` ```
If there are any other locations that might interfere with any of the folders in the `/public` folder, you might have to do the same for those as well, however it's a super fringe case. If there are any other locations which might interfere with any of the folders in the `/public` folder, you might have to do the same for those as well, but it's a super fringe case.
### Reverse proxy ### Reverse proxy
If you'd like to reverse proxy this app, we recommend using our letsencrypt/nginx docker image: [SWAG - Secure Web Application Gateway](https://hub.docker.com/r/linuxserver/swag) If you'd like to reverse proxy this app, we recommend using our letsencrypt/nginx docker image: [SWAG - Secure Web Application Gateway](https://hub.docker.com/r/linuxserver/swag)
@ -181,15 +167,7 @@ Per default Heimdall uses the standard certificate bundle file (`ca-certificates
openssl.cafile = /config/heimdall.pem openssl.cafile = /config/heimdall.pem
``` ```
Restart the container and the Enhanced apps should now be able to access your local HTTP websites. This configuration will survive updating or recreating the Heimdall container. Restart the container and the enhanced apps should now be able to access your local HTTP websites. This configuration will survive updating or recreating the Heimdall container.
## Running offline
The apps list is hosted on github, you have a couple of options if you want to run without a connection to the outside world:
1) Clone the repository and host it yourself, look at the .github actions file to see how to generate the apps list.
2) Download the apps list and store it as a JSON accessible to Heimdall named `list.json`
With both options all you need to do is add the following to your `.env`
`APP_SOURCE=http://localhost/` Where `http://localhost/` is the path to the apps list without the name of the file, so if your file is stored at `https://heimdall.local/list.json` you would put `APP_SOURCE=https://heimdall.local/`
## Support ## Support
https://discord.gg/CCjHKn4 or through GitHub issues https://discord.gg/CCjHKn4 or through GitHub issues
@ -205,13 +183,12 @@ If you would like to show your appreciation, feel free to use the link below.
- JavaScript - [jQuery](https://jquery.com/) - JavaScript - [jQuery](https://jquery.com/)
- Colour picker - [Huebee](http://huebee.buzz/) - Colour picker - [Huebee](http://huebee.buzz/)
- Background image - [pexels](https://www.pexels.com) - Background image - [pexels](https://www.pexels.com)
- Trianglify library - [Trianglify](https://github.com/qrohlf/trianglify)
- Everyone at Linuxserver.io that has helped with the app and let's not forget IronicBadger for the following question that started it all: - Everyone at Linuxserver.io that has helped with the app and let's not forget IronicBadger for the following question that started it all:
``` ```
You know, I would love something like this landing page for all my servers' apps you know, i would love something like this landing page for all my servers apps
that gives me the ability to pin favourites that gives me the ability to pin favourites
and / or search and / or search
@Stark @Kode do either of you think you'd be able to rustle something like this up? @Stark @Kode do either of you think you'd be able to rustle something like this up ?
``` ```
## License ## License

View file

@ -1,41 +1,103 @@
/* eslint-disable func-names */ $.when( $.ready ).then(function() {
$.when($.ready).then(() => {
const base = (document.querySelector("base") || {}).href;
const itemID = $("form[data-item-id]").data("item-id"); var base = (document.querySelector('base') || {}).href;
const fakePassword = "*****";
// If in edit mode and password field is present, fill it with stars if($('.message-container').length) {
if (itemID) { setTimeout(
const passwordField = $('input[name="config[password]"]').first(); function()
{
if (passwordField.length > 0) { $('.message-container').fadeOut();
passwordField.attr("value", fakePassword); }, 3500);
} }
}
if ($(".message-container").length) { // from https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
setTimeout(() => { // Set the name of the hidden property and the change event for visibility
$(".message-container").fadeOut(); var hidden, visibilityChange;
}, 3500); if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
} hidden = "hidden";
visibilityChange = "visibilitychange";
function readURL(input) { } else if (typeof document.msHidden !== "undefined") {
if (input.files && input.files[0]) { hidden = "msHidden";
const reader = new FileReader(); visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
reader.onload = function (e) { hidden = "webkitHidden";
$("#appimage img").attr("src", e.target.result); visibilityChange = "webkitvisibilitychange";
};
reader.readAsDataURL(input.files[0]);
} }
}
$("#upload").change(function () { var livestatsRefreshTimeouts = [];
readURL(this); var livestatsFuncs = [];
}); var livestatsContainers = $('.livestats-container');
/* $(".droppable").droppable({ function stopLivestatsRefresh() {
for (var timeoutId of livestatsRefreshTimeouts) {
window.clearTimeout(timeoutId);
}
}
function startLivestatsRefresh() {
for (var fun of livestatsFuncs) {
fun();
}
}
if (livestatsContainers.length > 0) {
if (typeof document.addEventListener === "undefined" || hidden === undefined) {
console.log("This browser does not support visibilityChange");
} else {
document.addEventListener(visibilityChange, function() {
if (document[hidden]) {
stopLivestatsRefresh();
} else {
startLivestatsRefresh();
}
}, false);
}
livestatsContainers.each(function(index){
var id = $(this).data('id');
var dataonly = $(this).data('dataonly');
var increaseby = (dataonly == 1) ? 20000 : 1000;
var container = $(this);
var max_timer = 30000;
var timer = 5000;
var fun = function worker() {
$.ajax({
url: base+'get_stats/'+id,
dataType: 'json',
success: function(data) {
container.html(data.html);
if(data.status == 'active') timer = increaseby;
else {
if(timer < max_timer) timer += 2000;
}
},
complete: function() {
// Schedule the next request when the current one's complete
livestatsRefreshTimeouts[index] = window.setTimeout(worker, timer);
}
});
};
livestatsFuncs[index] = fun;
fun();
});
}
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('#appimage img').attr('src', e.target.result);
};
reader.readAsDataURL(input.files[0]);
}
}
$('#upload').change(function() {
readURL(this);
});
/*$(".droppable").droppable({
tolerance: "intersect", tolerance: "intersect",
drop: function( event, ui ) { drop: function( event, ui ) {
var tag = $( this ).data('id'); var tag = $( this ).data('id');
@ -50,195 +112,126 @@ $.when($.ready).then(() => {
}); });
} }
}); */ });*/
const sortableEl = document.getElementById("sortable"); $( '#sortable' ).sortable({
let sortable; stop: function (event, ui) {
if (sortableEl !== null) { var idsInOrder = $('#sortable').sortable('toArray', {
// eslint-disable-next-line no-undef attribute: 'data-id'
sortable = Sortable.create(sortableEl, { });
disabled: true, $.post(
animation: 150, base+'order',
forceFallback: !( { order:idsInOrder }
navigator.userAgent.toLowerCase().indexOf("firefox") > -1 );
),
draggable: ".item-container",
onEnd() {
const idsInOrder = sortable.toArray();
$.post(`${base}order`, { order: idsInOrder });
},
});
// prevent Firefox drag behavior
if (navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
sortable.option("setData", (dataTransfer) => {
dataTransfer.setData("Text", "");
});
sortableEl.addEventListener("dragstart", (event) => {
const { target } = event;
if (target.nodeName.toLowerCase() === "a") {
event.preventDefault();
event.stopPropagation();
event.dataTransfer.setData("Text", "");
} }
});
}
}
$("#main")
.on("mouseenter", "#sortable .item", function () {
$(this).siblings(".tooltip").addClass("active");
$(".refresh", this).addClass("active");
})
.on("mouseleave", ".item", function () {
$(this).siblings(".tooltip").removeClass("active");
$(".refresh", this).removeClass("active");
}); });
$("#config-buttons") $('#sortable').sortable('disable');
.on("mouseenter", "a", function () {
$(".tooltip", this).addClass("active"); $('#sortable').on('mouseenter', '.item', function () {
$(this).siblings('.tooltip').addClass('active')
$('.refresh', this).addClass('active')
}).on('mouseleave', '.item', function () {
$(this).siblings('.tooltip').removeClass('active')
$('.refresh', this).removeClass('active')
}) })
.on("mouseleave", "a", function () {
$(".tooltip", this).removeClass("active");
});
$(".searchform > form").on("submit", (event) => { $('#search-container').on('input', 'input[name=q]', function () {
if ($("#search-container select[name=provider]").val() === "tiles") { const search = this.value
event.preventDefault(); const items = $('#sortable').children('.item-container')
} if($('#search-container select[name=provider]').val() === 'tiles') {
}); if(search.length > 0) {
items.hide()
$("#search-container") items.filter(function () {
.on("input", "input[name=q]", function () { const name = $(this).data('name').toLowerCase();
const search = this.value; return name.includes(search.toLowerCase())
const items = $("#sortable").children(".item-container"); }).show()
if ($("#search-container select[name=provider]").val() === "tiles") { } else {
if (search.length > 0) { items.show()
items.hide(); }
items
.filter(function () {
const name = $(this).data("name").toLowerCase();
return name.includes(search.toLowerCase());
})
.show();
} else { } else {
items.show(); items.show()
} }
} else { }).on('change', 'select[name=provider]', function () {
items.show(); const items = $('#sortable').children('.item-container')
} if($(this).val() === 'tiles') {
}) $('#search-container button').hide()
.on("change", "select[name=provider]", function () { const search = $('#search-container input[name=q]').val()
const items = $("#sortable").children(".item-container"); if(search.length > 0) {
if ($(this).val() === "tiles") { items.hide()
$("#search-container button").hide(); items.filter(function () {
const search = $("#search-container input[name=q]").val(); const name = $(this).data('name').toLowerCase();
if (search.length > 0) { return name.includes(search.toLowerCase())
items.hide(); }).show()
items } else {
.filter(function () { items.show()
const name = $(this).data("name").toLowerCase(); }
return name.includes(search.toLowerCase());
})
.show();
} else { } else {
items.show(); $('#search-container button').show()
items.show()
} }
} else { })
$("#search-container button").show();
items.show(); $('#app').on('click', '#config-button', function(e) {
} e.preventDefault();
var app = $('#app');
var active = (app.hasClass('header'));
app.toggleClass('header');
if(active) {
$('.add-item').hide();
$('.item-edit').hide();
$('#app').removeClass('sidebar');
$('#sortable').sortable('disable');
} else {
$('#sortable').sortable('enable');
setTimeout(function() {
$('.add-item').fadeIn();
$('.item-edit').fadeIn();
}, 350);
}
}).on('click', '#add-item, #pin-item', function(e) {
e.preventDefault();
var app = $('#app');
var active = (app.hasClass('sidebar'));
app.toggleClass('sidebar');
}).on('click', '.close-sidenav', function(e) {
e.preventDefault();
var app = $('#app');
app.removeClass('sidebar');
}).on('click', '#test_config', function(e) {
e.preventDefault();
var apiurl = $('#create input[name=url]').val();
var override_url = $('#create input[name="config[override_url]"]').val();
if(override_url.length && override_url != '') {
apiurl = override_url;
}
var data = {};
data['url'] = apiurl;
$('.config-item').each(function(index){
var config = $(this).data('config');
data[config] = $(this).val();
});
$.post(base+'test_config', { data: data }, function(data) {
alert(data);
});
}); });
$('#pinlist').on('click', 'a', function(e) {
$("#app") e.preventDefault();
.on("click", "#config-button", (e) => { var current = $(this);
e.preventDefault(); var id = current.data('id');
const app = $("#app"); var tag = current.data('tag');
const active = app.hasClass("header"); $.get(base+'items/pintoggle/'+id+'/true/'+tag, function(data) {
app.toggleClass("header"); var inner = $(data).filter('#sortable').html();
if (active) { $('#sortable').html(inner);
$(".add-item").hide(); current.toggleClass('active');
$(".item-edit").hide();
$("#app").removeClass("sidebar");
$("#sortable .tooltip").css("display", "");
if (sortable !== undefined) sortable.option("disabled", true);
} else {
$("#sortable .tooltip").css("display", "none");
if (sortable !== undefined) sortable.option("disabled", false);
setTimeout(() => {
$(".add-item").fadeIn();
$(".item-edit").fadeIn();
}, 350);
}
})
.on("click", "#add-item, #pin-item", (e) => {
e.preventDefault();
const app = $("#app");
// const active = app.hasClass("sidebar");
app.toggleClass("sidebar");
})
.on("click", ".close-sidenav", (e) => {
e.preventDefault();
const app = $("#app");
app.removeClass("sidebar");
})
.on("click", "#test_config", (e) => {
e.preventDefault();
let apiurl = $("#create input[name=url]").val();
const overrideUrl = $(
'#sapconfig input[name="config[override_url]"]'
).val();
if (typeof overrideUrl === "string" && overrideUrl !== "") {
apiurl = overrideUrl;
}
const data = {};
data.url = apiurl;
$(".config-item").each(function () {
const config = $(this).data("config");
data[config] = $(this).val();
});
data.id = $("form[data-item-id]").data("item-id");
if (data.password && data.password === fakePassword) {
data.password = "";
}
$.post(`${base}test_config`, { data })
.done((responseData) => {
// eslint-disable-next-line no-alert
alert(responseData);
})
.fail((responseData) => {
// eslint-disable-next-line no-alert
alert(
`Something went wrong: ${responseData.responseText.substring(
0,
100
)}`
);
}); });
}); });
$("#pinlist").on("click", "a", function (e) {
e.preventDefault();
const current = $(this);
const id = current.data("id");
const tag = current.data("tag");
$.get(`${base}items/pintoggle/${id}/true/${tag}`, (data) => {
const inner = $(data).filter("#sortable").html();
$("#sortable").html(inner);
current.toggleClass("active");
});
});
$("#itemform").on("submit", () => {
const passwordField = $('input[name="config[password]"]').first();
if (passwordField.length > 0) {
if (passwordField.attr("value") === fakePassword) {
passwordField.attr("value", "");
}
}
});
}); });

View file

@ -1,49 +0,0 @@
const EXPORT_FILE_NAME = "HeimdallExport.json";
const EXPORT_API_URL = "api/item";
/**
*
* @param {string} fileName
* @param {string} data
*/
function triggerFileDownload(fileName, data) {
const a = document.createElement("a");
const file = new Blob([data], {
type: "text/plain",
});
a.href = URL.createObjectURL(file);
a.download = EXPORT_FILE_NAME;
a.click();
}
/**
*
* @param {Event} event
*/
const exportItems = (event) => {
event.preventDefault();
fetch(EXPORT_API_URL)
.then((response) => {
if (response.status !== 200) {
// eslint-disable-next-line no-alert
window.alert("An error occurred while exporting...");
}
return response.json();
})
.then((data) => {
const exportedJson = JSON.stringify(data, null, 2);
triggerFileDownload(EXPORT_FILE_NAME, exportedJson);
});
};
const exportButton = document.querySelector("#item-export");
if (exportButton) {
exportButton.addEventListener("click", exportItems);
}

View file

@ -1,178 +0,0 @@
const IMPORT_API_URL = "api/item";
const APP_LOAD_URL = "appload";
/**
*
* @param {object} item
* @param {array} errors
*/
const updateStatus = ({ item, errors }) => {
// eslint-disable-next-line no-console
console.log(item, errors);
let statusLine;
if (errors.length === 0) {
statusLine = `<li class="success"><i class="fas fa-circle-check"></i> Imported: ${item.title} </li>`;
} else {
statusLine = `<li class="fail"><i class="fas fa-circle-xmark"></i> Failed: ${item.title} - ${errors[0]} </li>`;
}
document.querySelector(".import-status").innerHTML += statusLine;
};
/**
*
*/
function clearStatus() {
const statusContainer = document.querySelector(".import-status");
statusContainer.innerHTML = "";
}
/**
*
* @param {object} data
* @param {string} csrfToken
*/
const postToApi = (data, csrfToken) =>
fetch(IMPORT_API_URL, {
method: "POST",
cache: "no-cache",
redirect: "follow",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": csrfToken,
},
body: JSON.stringify(data),
});
/**
*
* @returns {string}
*/
const getCSRFToken = () => {
const tokenSelector = 'input[name="_token"]';
return document.querySelector(tokenSelector).value;
};
/**
*
* @param {object} item
* @param {object} appDetails
* @returns {object}
*/
const mergeItemWithAppDetails = (item, appDetails) => ({
pinned: 1,
tags: [0],
appid: item.appid,
title: item.title,
colour: item.colour,
url: item.url,
appdescription: item.appdescription
? item.appdescription
: appDetails.description,
website: appDetails.website,
icon: appDetails.iconview,
config: item.description ? JSON.parse(item.description) : null,
});
/**
*
* @param {string|null} appId
* @returns {Promise<{}>|Promise<any>}
*/
const fetchAppDetails = (appId) => {
if (appId === null || appId === "null") {
return Promise.resolve({});
}
return fetch(APP_LOAD_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ app: appId }),
}).then((response) => response.json());
};
/**
*
* @param {array} items
*/
const importItems = (items) => {
items.forEach((item) => {
const errors = [];
fetchAppDetails(item.appid)
.catch(() =>
errors.push(new Error(`Failed to find app id: ${item.appid}`))
)
.then((appDetails) => {
const itemWithAppDetails = mergeItemWithAppDetails(item, appDetails);
const csrfToken = getCSRFToken();
return postToApi(itemWithAppDetails, csrfToken);
})
.catch(() =>
errors.push(new Error(`Failed to create item: ${item.title}`))
)
.finally(() => {
updateStatus({
item,
errors,
});
});
});
};
/**
*
* @param {Blob} file
* @returns {Promise<unknown>}
*/
const readJSON = (file) =>
new Promise((resolve, reject) => {
try {
const reader = new FileReader();
reader.onload = (event) => {
const contents = event.target.result;
resolve(JSON.parse(contents));
};
reader.readAsText(file);
} catch (e) {
reject(new Error("Unable to read file"));
}
});
/**
*
* @param {Blob} file
*/
const openFileForImport = (file) => {
clearStatus();
return readJSON(file)
.catch((error) => {
// eslint-disable-next-line no-console
console.error(error);
})
.then(importItems);
};
const fileInput = document.querySelector("input[name='import']");
const importButtons = document.querySelectorAll(".import-button");
if (fileInput && importButtons) {
importButtons.forEach((importButton) => {
importButton.addEventListener("click", () => {
const file = fileInput.files[0];
if (!file) {
return;
}
openFileForImport(file);
});
});
fileInput.addEventListener("change", openFileForImport, false);
}

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