Compare commits

..

20 Commits

Author SHA1 Message Date
Roxedus
ad473732ef Merge pull request #1489 from linuxserver/pages-security 2025-08-24 01:24:37 +02:00
Roxedus
8c11e0b38d Clone security.txt from lsio site 2025-08-23 23:45:30 +02:00
KodeStar
81b7d2d171 Add files via upload 2018-02-17 22:48:54 +00:00
Chris
020a7d2ceb change css a bit 2018-02-15 10:03:23 +00:00
Chris
81a8da393f change body text colour 2018-02-15 09:45:18 +00:00
KodeStar
1d34fff9d0 changes to web 2018-02-11 23:41:45 +00:00
KodeStar
ed36ee7f10 shorter links 2018-02-11 23:11:05 +00:00
KodeStar
f3465efb4c updates 2018-02-11 22:23:31 +00:00
KodeStar
78cc234aae v2 2018-02-11 22:10:50 +00:00
KodeStar
994333f408 changes 2018-02-11 22:09:18 +00:00
KodeStar
bc9cb96d9d updates 2018-02-11 22:03:57 +00:00
KodeStar
515bc2a2a1 Add title and description 2018-02-11 19:27:48 +00:00
KodeStar
a5969ae538 remove js for now 2018-02-11 19:25:02 +00:00
KodeStar
e3ef335a07 fix relative links 2018-02-11 19:23:20 +00:00
KodeStar
45f69c26df Create CNAME 2018-02-11 19:04:20 +00:00
KodeStar
cbe0826600 fix relative links 2018-02-11 18:57:22 +00:00
KodeStar
49e5afd143 update relative links 2018-02-11 18:33:57 +00:00
KodeStar
1c26a6c51b update 2018-02-11 18:12:11 +00:00
KodeStar
c107f5f7b7 added logo 2018-02-11 17:48:14 +00:00
KodeStar
e3158267e8 Add gh-pages branch for site 2018-02-11 17:00:44 +00:00
14434 changed files with 13237 additions and 1489844 deletions

View File

@@ -1,69 +0,0 @@
APP_NAME=Heimdall
APP_ENV=local
APP_KEY=
APP_DEBUG=false
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=daily
LOG_STACK=single
DB_CONNECTION=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_CONNECTION=log
CACHE_STORE=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
AUTH_ROLES_ENABLE=false
AUTH_ROLES_HEADER="remote-groups"
AUTH_ROLES_HTTP_HEADER="HTTP_REMOTE_GROUPS"
AUTH_ROLES_ADMIN="admin"
AUTH_ROLES_DELIMITER=","
ALLOW_INTERNAL_REQUESTS=false

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
}
}

49
.gitattributes vendored
View File

@@ -1,49 +0,0 @@
# Configuration file for Git attributes
# Core Settings {{{
# .gitattributes
.gitattributes !filter !diff
# Line Endings
* text=auto
# Set binary to none-text files
*.png -text
# }}}
# GitHub Linguist {{{
# Exclude files/folder from being detected by the GitHub linguist
# statistic.
node_modules/* linguist-vendored
public/* linguist-generated=true
vendor/* linguist-vendored
# Remove Vue as it's currently not used in the project.
resources/assets/js/components/ExampleComponent.vue linguist-vendored
# System Wide
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
# Include user generated files that's removed bu the setting above.
resources/assets/js/app.js linguist-vendored=false
resources/assets/sass/_app.scss linguist-vendored=false
resources/assets/sass/_rune.scss linguist-vendored=false
resources/assets/sass/_variables.scss linguist-vendored=false
# }}}
# Archive Exlude {{{
# Exclude files/folders from being exported when creating an archive.
.gitattributes export-ignore
.gitignore export-ignore
.travis.yml export-ignore
CHANGELOG.md export-ignore
# }}}

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: '8.3'
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@v4
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@v4
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 --config-set ignore_warnings_on_exit 1
- name: Upload artifacts
uses: actions/upload-artifact@master
if: failure()
with:
name: Logs
path: ./storage/logs

32
.gitignore vendored
View File

@@ -1,31 +1 @@
/app/SupportedApps
/node_modules
/public/hot
/public/storage
/storage/*.key
/storage/debugbar
/.idea
/.vagrant
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
### macOS ###
*.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
storage/app/public/avatars/*
.env
.phpunit.result.cache
node_modules/

File diff suppressed because it is too large Load Diff

15
.vscode/launch.json vendored
View File

@@ -1,15 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/html": "${workspaceFolder}"
}
}
]
}

16
.vscode/tasks.json vendored
View File

@@ -1,16 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Start Docker Compose",
"type": "shell",
"command": "docker-compose up --build",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
}
]
}

5
.well-known/security.txt Normal file
View File

@@ -0,0 +1,5 @@
Contact: mailto:security@linuxserver.io
Expires: 2026-01-25T23:00:00.000Z
Preferred-Languages: en
Canonical: https://linuxserver.io/.well-known/security.txt
Policy: https://github.com/linuxserver/.github/blob/main/SECURITY.md

View File

@@ -1,74 +0,0 @@
# Release Notes
## v1.4.0 (2018-02-18)
### Added
- Tag(folder) support
- Image preview for uploading icons
- A load of supported apps, full list of apps https://github.com/linuxserver/Heimdall/projects/1
### Changed
- Edited vendor/laravelcollective/html/src/FormBuilder.php to allow relative links #3369de9
- Changed links to use relative links for reverse proxy support
- Links open in new tab
### Fixed
- adds all the fixes in the 1.3.x point releases and on master
## v1.3.0 (2018-02-09)
### Added
- guzzlehttp/guzzle as a dependency
- Variable polling, so interval is increased when an app is idle and decreased when it's active
- Turkish language translation
- Added Sabnzbd enhanced application
### Changed
- Updated composer dependencies
- Added live stats to Nzbget supported application
- Changed Pihole to an enhanced application
- Changed NZBGet to an enhanced application
### Fixed
- Fixed autocomplete being hard to see
- Fixed checkboxes not working on edge
## v1.2.0 (2018-02-07)
### Added
- Translation support
- Initial "Supported" application support
- Finnish translation
- Swedish translation
- German translation
- French translation
- Spanish translation
- Duplicati supported application
- Emby supported application
- Nzbget supported application
- Pfsense supported application
- Pihole supported application
- Plex supported application
- Portainer supported application
- Unifi supported application
### Changed
- button layout and behaviour
### Fixed
- Bottom of button too short in some browsers
- Icon not loading back in when required fields not filled in
## v1.1.0 (2018-02-05)
### Added
- Ability to change background
- Settings section
- Update procedure
- Google/DuckDuckGo/Bing search from homepage
- Added edit button to tile page
### Changed
- Icon used to put tiles into config mode

1
CNAME Normal file
View File

@@ -0,0 +1 @@
heimdall.site

21
LICENSE
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2018 Chris Hunt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1 +0,0 @@
theme: jekyll-theme-tactile

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -1,200 +0,0 @@
<?php
namespace App;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Database\Eloquent\Builder;
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
{
/**
* @var bool
*/
public $incrementing = false;
/**
* @var string
*/
protected $primaryKey = 'appid';
/**
* @return mixed
*/
public function icon()
{
if (! file_exists(storage_path('app/public/'.$this->icon))) {
$img_src = app_path('SupportedApps/'.$this->name.'/'.str_replace('icons/', '', $this->icon));
$img_dest = storage_path('app/public/'.$this->icon);
//die("i: ".$img_src);
@copy($img_src, $img_dest);
}
return $this->icon;
}
public function iconView(): string
{
return asset('storage/'.$this->icon);
}
public function defaultColour(): string
{
// check if light or dark
if ($this->tile_background == 'light') {
return '#fafbfc';
}
return '#161b1f';
}
public function class(): string
{
$name = $this->name;
$name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
return \App\SupportedApps::class.'\\'.$name.'\\'.$name;
}
/**
* @param $name
*/
public static function classFromName($name): string
{
$name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
$class = \App\SupportedApps::class.'\\'.$name.'\\'.$name;
return $class;
}
public static function apps(): Collection
{
$json = json_decode(file_get_contents(storage_path('app/supportedapps.json'))) ?? [];
$apps = collect($json->apps);
return $apps->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE);
}
public static function autocomplete(): array
{
$apps = self::apps();
$list = [];
foreach ($apps as $app) {
$list[] = (object) [
'label' => $app->name,
'value' => $app->appid,
];
}
return $list;
}
/**
* @param $appid
* @return mixed|null
* @throws GuzzleException
*/
public static function getApp($appid)
{
Log::debug("Get app triggered for: $appid");
$localapp = self::where('appid', $appid)->first();
$app = self::single($appid);
$application = ($localapp) ? $localapp : new self;
// Files missing? || app not in db || old sha version
if (! file_exists(app_path('SupportedApps/'.className($app->name))) ||
! $localapp ||
$localapp->sha !== $app->sha
) {
$gotFiles = SupportedApps::getFiles($app);
if ($gotFiles) {
$app = SupportedApps::saveApp($app, $application);
}
}
return $app;
}
/**
* @param $appid
* @return mixed|null
*/
public static function single($appid)
{
$apps = self::apps();
$app = $apps->where('appid', $appid)->first();
if ($app === null) {
// 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::class.'\\'.$classname.'\\'.$classname;
return $app;
}
public static function applist(): array
{
$list = [];
$list['null'] = 'None';
$apps = self::apps();
foreach ($apps as $app) {
$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;
}
}

View File

@@ -1,102 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Application;
use App\SupportedApps;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class RegisterApp extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'register:app {folder} {--remove}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Add a local app to the registry';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle(): void
{
$folder = $this->argument('folder');
if ($folder == 'all') {
$apps = scandir(app_path('SupportedApps'));
foreach ($apps as $folder) {
if ($folder == '.' || $folder == '..') {
continue;
}
$this->addApp($folder);
}
} else {
$this->addApp($folder, $this->option('remove'));
}
}
/**
* @param $folder
*/
public function addApp($folder, bool $remove = false): void
{
$json = app_path('SupportedApps/'.$folder.'/app.json');
if (!file_exists($json)) {
$this->error('Could not find ' . $json);
return;
}
$app = json_decode(file_get_contents($json));
if (!isset($app->appid)) {
$this->error('No App ID for - ' . $folder);
return;
}
$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;
}
// 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
*/
private function saveIcon($appFolder, $icon): void
{
$iconPath = app_path('SupportedApps/' . $appFolder . '/' . $icon);
$contents = file_get_contents($iconPath);
Storage::disk('public')->put('icons/'.$icon, $contents);
}
}

View File

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

View File

@@ -1,13 +0,0 @@
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Form extends Facade
{
protected static function getFacadeAccessor()
{
return 'custom-form';
}
}

View File

@@ -1,154 +0,0 @@
<?php
use Illuminate\Support\Str;
use enshrined\svgSanitize\Sanitizer;
/**
* @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;
$labels = ['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++) ;
if ($labels[$x] == 'TB') {
return round($bytes, 3) . $beforeunit . $labels[$x] . $afterunit;
} elseif ($labels[$x] == 'GB') {
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 parse_size($size) {
$unit = strtolower(substr($size, -1));
$bytes = (int)$size;
switch($unit) {
case 'g': $bytes *= 1024 * 1024 * 1024; break;
case 'm': $bytes *= 1024 * 1024; break;
case 'k': $bytes *= 1024; break;
}
return $bytes;
}
/**
* @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);
}
if (!function_exists('str_is')) {
/**
* Determine if a given string matches a given pattern.
*
* @param string|array $pattern
* @param string $value
* @return bool
*
* @deprecated Str::is() should be used directly instead. Will be removed in Laravel 6.0.
*/
function str_is($pattern, string $value): bool
{
return Str::is($pattern, $value);
}
}
/**
* @param $hex
* @return float|int
*/
function get_brightness($hex)
{
// returns brightness value from 0 to 255
// strip off any leading #
// $hex = str_replace('#', '', $hex);
$hex = preg_replace("/[^0-9A-Fa-f]/", '', $hex);
if (strlen($hex) == 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
$c_r = hexdec(substr($hex, 0, 2));
$c_g = hexdec(substr($hex, 2, 2));
$c_b = hexdec(substr($hex, 4, 2));
return (($c_r * 299) + ($c_g * 587) + ($c_b * 114)) / 1000;
}
/**
* @param $hex
* @return string
*/
function title_color($hex): string
{
if (get_brightness($hex) > 130) {
return ' black';
} else {
return ' white';
}
}
/**
* @return string
*/
function getLinkTargetAttribute(): string
{
$target = \App\Setting::fetch('window_target');
if ($target === 'current') {
return '';
} else {
return ' target="' . $target . '"';
}
}
/**
* @param $name
* @return array|string|string[]|null
*/
function className($name)
{
return preg_replace('/[^\p{L}\p{N}]/u', '', $name);
}
/**
* @param string $file
* @param string $extension
* @return bool
*/
function isImage(string $file, string $extension): bool
{
$allowedExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp', 'ico'];
if (!in_array($extension, $allowedExtensions)) {
return false;
}
$tempFileName = @tempnam("/tmp", "image-check-");
$handle = fopen($tempFileName, "w");
fwrite($handle, $file);
fclose($handle);
if ($extension === 'svg') {
$sanitizer = new Sanitizer();
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($tempFileName));
file_put_contents($tempFileName, $sanitizedSvg);
return 'image/svg+xml' === mime_content_type($tempFileName);
}
$size = @getimagesize($tempFileName);
return is_array($size) && str_starts_with($size['mime'], 'image');
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}

View File

@@ -1,148 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
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\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpFoundation\Response;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected string $redirectTo = '/';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
Session::put('backUrl', URL::previous());
$this->middleware('guest')->except(['logout','autologin']);
}
public function username(): string
{
return 'username';
}
/**
* Handle a login request to the application.
*
*
* @throws ValidationException
*/
public function login(Request $request): Response
{
$current_user = User::currentUser();
$request->merge(['username' => $current_user->username, 'remember' => true]);
//die(print_r($request->all()));
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
public function index()
{
}
public function setUser(User $user): RedirectResponse
{
Auth::logout();
session(['current_user' => $user]);
return redirect()->route('dash');
}
/**
* @param $uuid
*/
public function autologin($uuid): RedirectResponse
{
Auth::logout();
$user = User::where('autologin', $uuid)->first();
if (!$user) {
return redirect()->route('dash');
}
Auth::login($user, true);
session(['current_user' => $user]);
return redirect()->route('dash');
}
/**
* Show the application's login form.
*
* @return Application|Factory|View
*/
public function showLoginForm(): \Illuminate\View\View
{
return view('auth.login');
}
/**
* @param $user
*/
protected function authenticated(Request $request, $user): RedirectResponse
{
return back();
}
/**
* @return mixed|string
*/
public function redirectTo()
{
return Session::get('url.intended') ? Session::get('url.intended') : $this->redirectTo;
}
}

View File

@@ -1,65 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected string $redirectTo = '/';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*/
protected function validator(array $data): \Illuminate\Contracts\Validation\Validator
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*/
protected function create(array $data): User
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}

View File

@@ -1,39 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected string $redirectTo = '/';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
protected $user;
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = $this->user();
//print_r($this->user);
return $next($request);
});
}
public function user()
{
return User::currentUser();
}
}

View File

@@ -1,51 +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
{
private static function getUsers(): int
{
return User::count();
}
private static function getItems(): int
{
return Item::select('id')
->where('deleted_at', null)
->where('type', '0')
->count();
}
/**
* Handle the incoming 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

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

View File

@@ -1,28 +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.
*/
public function __invoke(Request $request): View
{
return view('items.import');
}
}

View File

@@ -1,616 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Application;
use App\Item;
use App\Jobs\ProcessApps;
use App\User;
use GuzzleHttp\Client;
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\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Validation\ValidationException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Illuminate\Http\Response;
use enshrined\svgSanitize\Sanitizer;
class ItemController extends Controller
{
public function __construct()
{
parent::__construct();
$this->middleware('allowed');
}
/**
* Display a listing of the resource on the dashboard.
*/
public function dash(Request $request): View
{
$treat_tags_as = \App\Setting::fetch('treat_tags_as');
$data["treat_tags_as"] = $treat_tags_as;
if (config('app.auth_roles_enable')) {
$roles = explode(config('app.auth_roles_delimiter'), $request->header(config('app.auth_roles_header')));
if ($treat_tags_as == 'categories') {
$data['categories'] = Item::whereHas('children')->with('children', function ($query) {
$query->pinned()->orderBy('order', 'asc');
})->pinned()->orderBy('order', 'asc')->get();
} elseif ($treat_tags_as == 'tags') {
$data['apps'] = Item::with('parents')->where('type', 0)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::where('type', 0)->orderBy('order', 'asc')->get();
$data['taglist'] = Item::where('id', 0)->orWhere(function ($query) {
$query->where('type', 1)->pinned();
})->orderBy('order', 'asc')->get();
} else {
$data['apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->whereIn('role', $roles)->orWhere('type', 1)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere('type', 1)->orderBy('order', 'asc')->get();
}
} else {
if ($treat_tags_as == 'categories') {
$data['categories'] = Item::whereHas('children')->with('children', function ($query) {
$query->pinned()->orderBy('order', 'asc');
})->pinned()->orderBy('order', 'asc')->get();
} elseif ($treat_tags_as == 'tags') {
$data['apps'] = Item::with('parents')->where('type', 0)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::where('type', 0)->orderBy('order', 'asc')->get();
$data['taglist'] = Item::where('id', 0)->orWhere(function ($query) {
$query->where('type', 1)->pinned();
})->orderBy('order', 'asc')->get();
} else {
$data['apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere('type', 1)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere(function ($query) {
$query->where('type', 1)->whereNot('id', 0);
})->orderBy('order', 'asc')->get();
}
}
//$data['all_apps'] = Item::doesntHave('parents')->get();
// die(print_r($data));
return view('welcome', $data);
}
/**
* Set order on the dashboard.
*
* @return void
*/
public function setOrder(Request $request)
{
$order = array_filter($request->input('order'));
foreach ($order as $o => $id) {
$item = Item::find($id);
$item->order = $o;
$item->save();
}
}
/**
* Pin item on the dashboard.
*
* @param $id
*/
public function pin($id): RedirectResponse
{
$item = Item::findOrFail($id);
$item->pinned = true;
$item->save();
$route = route('dash', []);
return redirect($route);
}
/**
* Unpin item on the dashboard.
*
* @param $id
*/
public function unpin($id): RedirectResponse
{
$item = Item::findOrFail($id);
$item->pinned = false;
$item->save();
$route = route('dash', []);
return redirect($route);
}
/**
* Unpin item on the dashboard.
*
* @return RedirectResponse|View
*/
public function pinToggle($id, $ajax = false, $tag = false)
{
$item = Item::findOrFail($id);
$new = !(((bool)$item->pinned === true));
$item->pinned = $new;
$item->save();
if ($ajax) {
$item = Item::whereId($tag)->first();
$data['apps'] = new Collection;
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;
return view('sortable', $data);
} else {
$route = route('dash', []);
return redirect($route);
}
}
/**
* Display a listing of the resource.
*/
public function index(Request $request): View
{
$trash = (bool)$request->input('trash');
$data['apps'] = Item::ofType('item')->orderBy('title', 'asc')->get();
$data['trash'] = Item::ofType('item')->onlyTrashed()->get();
if ($trash) {
return view('items.trash', $data);
} else {
return view('items.list', $data);
}
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
//
$data['item'] = new \App\Item();
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
$data['tags']->prepend(__('app.dashboard'), 0);
$data['current_tags'] = '0';
return view('items.create', $data);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
// Get the item
$item = Item::find($id);
if ($item->appid === null && $item->class !== null) { // old apps won't have an app id so set it
$app = Application::where('class', $item->class)->first();
if ($app) {
$item->appid = $app->appid;
}
}
$data['item'] = $item;
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
$data['tags']->prepend(__('app.dashboard'), 0);
$data['current_tags'] = $data['item']->tags();
//$data['current_tags'] = $data['item']->parent;
//die(print_r($data['current_tags']));
// show the edit form and pass the nerd
return view('items.edit', $data);
}
/**
* @param null $id
* @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')) {
$image = $request->file('file');
$extension = $image->getClientOriginalExtension();
if ($extension === 'svg') {
$sanitizer = new Sanitizer();
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($image->getRealPath()));
// Verify that the sanitization removed malicious content
if (strpos($sanitizedSvg, '<script>') !== false) {
throw ValidationException::withMessages(['file' => 'SVG contains malicious content and cannot be uploaded.']);
}
// Save the sanitized SVG back to the file
file_put_contents($image->getRealPath(), $sanitizedSvg);
}
$path = $image->store('icons', 'public');
$request->merge([
'icon' => $path,
]);
} elseif (strpos($request->input('icon'), 'http') === 0) {
$options = [
"ssl" => [
"verify_peer" => false,
"verify_peer_name" => false,
],
];
// Proxy management
$httpsProxy = getenv('HTTPS_PROXY');
$httpsProxyLower = getenv('https_proxy');
if ($httpsProxy !== false || $httpsProxyLower !== false) {
$options['proxy']['http'] = $httpsProxy ?: $httpsProxyLower;
}
$file = $request->input('icon');
$path_parts = pathinfo($file);
if (!array_key_exists('extension', $path_parts)) {
throw ValidationException::withMessages(['file' => 'Icon URL must have a valid file extension.']);
}
$extension = $path_parts['extension'];
$contents = file_get_contents($request->input('icon'), false, stream_context_create($options));
if ($extension === 'svg') {
$sanitizer = new Sanitizer();
$contents = $sanitizer->sanitize($contents);
// Verify that the sanitization removed malicious content
if (strpos($contents, '<script>') !== false) {
throw ValidationException::withMessages(['file' => 'SVG contains malicious content and cannot be uploaded.']);
}
}
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);
if ($storedConfigObject && property_exists($storedConfigObject, 'password')) {
$configObject->password = $storedConfigObject->password;
} else {
$configObject->password = null;
}
$config = json_encode($configObject);
}
$current_user = User::currentUser();
$request->merge([
'description' => $config,
'user_id' => $current_user->getId(),
]);
if ($request->input('appid') === 'null' || $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.
*/
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.
*/
public function show(int $id): void
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, int $id): RedirectResponse
{
self::storelogic($request, $id);
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.item_updated'));
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Request $request, int $id): RedirectResponse
{
//
$force = (bool)$request->input('force');
if ($force) {
Item::withTrashed()
->where('id', $id)
->forceDelete();
} else {
Item::find($id)->delete();
}
$route = route('items.index', []);
return redirect($route)
->with('success', __('app.alert.success.item_deleted'));
}
/**
* Restore the specified resource from soft deletion.
*/
public function restore(int $id): RedirectResponse
{
//
Item::withTrashed()
->where('id', $id)
->restore();
$route = route('items.index', []);
return redirect($route)
->with('success', __('app.alert.success.item_restored'));
}
/**
* Return details for supported apps
*
* @throws GuzzleException
*/
public function appload(Request $request): ?string
{
$output = [];
$appid = $request->input('app');
$itemId = $request->input('item_id');
if ($appid === 'null') {
return null;
}
$app = Application::single($appid);
if (!$app) {
return response()->json(['error' => 'Application not found.'], 404);
}
$output = (array)$app;
$appdetails = Application::getApp($appid);
if (!$appdetails) {
return response()->json(['error' => 'Application details not found.'], 404);
}
if ((bool)$app->enhanced === true) {
$item = $itemId ? Item::find($itemId) : Item::where('appid', $appid)->first();
if ($item) {
$output['custom'] = className($appdetails->name) . '.config';
$output['appvalue'] = $item->description;
} else {
// Ensure the app is installed if not found
$output['custom'] = className($appdetails->name) . '.config';
$output['appvalue'] = null;
}
}
$output['colour'] = ($app->tile_background == 'light') ? '#fafbfc' : '#161b1f';
if (strpos($app->icon, '://') !== false) {
$output['iconview'] = $app->icon;
} elseif (strpos($app->icon, 'icons/') !== false) {
$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 void
*/
public function testConfig(Request $request)
{
$data = $request->input('data');
//$url = $data[array_search('url', array_column($data, 'name'))]['value'];
$single = Application::single($data['type']);
$app = $single->class;
// 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->config = (object)$data;
$app_details->test();
}
/**
* @param $url
* @param array|bool $overridevars
* @throws GuzzleException
*/
public function execute($url, array $attrs = [], $overridevars = false): ?ResponseInterface
{
// Default Guzzle client configuration
$clientOptions = [
'http_errors' => false,
'timeout' => 15,
'connect_timeout' => 15,
'verify' => false, // In production, set this to `true` and manage certs.
];
// If the user provided overrides, use them.
if ($overridevars !== false) {
$clientOptions = $overridevars;
}
// Resolve the hostname to an IP address
$host = parse_url($url, PHP_URL_HOST);
$ip = gethostbyname($host);
// Check if the IP is private or reserved
$allowInternalIps = env('ALLOW_INTERNAL_REQUESTS', false);
if (!$allowInternalIps && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
Log::warning('Blocked access to private or reserved IPs.', ['ip' => $ip, 'host' => $host]);
abort(Response::HTTP_FORBIDDEN, 'Access to private or reserved IPs is not allowed.');
}
// Force Guzzle to use the resolved IP address
$clientOptions['curl'][CURLOPT_RESOLVE] = ["{$host}:80:{$ip}", "{$host}:443:{$ip}"];
$client = new Client($clientOptions);
$method = 'GET';
try {
return $client->request($method, $url, $attrs);
} catch (ConnectException $e) {
Log::warning('SSRF Attempt Blocked: Connection to a private IP was prevented.', [
'url' => $url,
'error' => $e->getMessage()
]);
return null;
} catch (ServerException $e) {
Log::debug($e->getMessage());
} catch (\Exception $e) {
Log::error('General error: ' . $e->getMessage());
}
return null;
}
/**
* @param $url
* @throws GuzzleException
*/
public function websitelookup($url): StreamInterface
{
$decodedUrl = base64_decode($url);
// Validate the URL format.
if (filter_var($decodedUrl, FILTER_VALIDATE_URL) === false) {
abort(Response::HTTP_BAD_REQUEST, 'Invalid URL format provided.');
}
$response = $this->execute($decodedUrl);
// If execute() returns null, it means the connection failed.
// This can happen for many reasons, including our SSRF protection kicking in.
if ($response === null) {
abort(Response::HTTP_FORBIDDEN, 'Access to the requested resource is not allowed or the resource is unavailable.');
}
return $response->getBody();
}
/**
* @param $id
* @return void
*/
public function getStats($id)
{
$item = Item::find($id);
$config = $item->getconfig();
if (isset($item->class)) {
$application = new $item->class;
$application->config = $config;
echo $application->livestats();
}
}
/**
* @return \Illuminate\Contracts\Foundation\Application|RedirectResponse|Redirector
*/
public function checkAppList(): RedirectResponse
{
ProcessApps::dispatch();
$route = route('items.index');
return redirect($route)
->with('success', __('app.alert.success.updating'));
}
}

View File

@@ -1,93 +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.
*/
public function index(): Collection
{
$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.
*/
public function store(Request $request): object
{
$item = ItemController::storelogic($request);
if ($item) {
return (object) ['status' => 'OK'];
}
return (object) ['status' => 'FAILED'];
}
/**
* Display the specified resource.
*/
public function show(Item $item): Response
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Item $item): Response
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Item $item): Response
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Item $item): Response
{
//
}
}

View File

@@ -1,139 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Search;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Http;
class SearchController extends Controller
{
/**
* @return Application|RedirectResponse|Redirector|mixed|void
*/
public function index(Request $request)
{
$requestprovider = $request->input('provider');
$query = $request->input('q');
// Sanitize the query to prevent XSS
$query = htmlspecialchars($query, ENT_QUOTES, 'UTF-8');
$provider = Search::providerDetails($requestprovider);
if (!$provider || !isset($provider->type)) {
abort(404, 'Invalid provider');
}
// If the query is empty, redirect to the provider's base URL
if (!$query || trim($query) === '') {
return redirect($provider->url);
}
if ($provider->type == 'standard') {
return redirect($provider->url.'?'.$provider->query.'='.urlencode($query));
} elseif ($provider->type == 'external') {
$class = new $provider->class;
return $class->getResults($query, $provider);
}
abort(404, 'Provider type not supported');
}
/**
* Get autocomplete suggestions for a search query
*
* @return JsonResponse
*/
public function autocomplete(Request $request)
{
$requestprovider = $request->input('provider');
$query = $request->input('q');
if (!$query || trim($query) === '') {
return response()->json([]);
}
$provider = Search::providerDetails($requestprovider);
if (!$provider || !isset($provider->autocomplete)) {
return response()->json([]);
}
// Replace {query} placeholder with actual query
$autocompleteUrl = str_replace('{query}', urlencode($query), $provider->autocomplete);
try {
$response = Http::timeout(5)->get($autocompleteUrl);
if ($response->successful()) {
$data = $response->body();
// Parse the response based on provider
$suggestions = $this->parseAutocompleteResponse($data, $provider->id);
return response()->json($suggestions);
}
} catch (\Exception $e) {
// Return empty array on error
return response()->json([]);
}
return response()->json([]);
}
/**
* Parse autocomplete response based on provider format
*
* @param string $data
* @param string $providerId
* @return array
*/
private function parseAutocompleteResponse($data, $providerId)
{
$suggestions = [];
switch ($providerId) {
case 'google':
// Google returns XML format
if (strpos($data, '<?xml') === 0) {
$xml = simplexml_load_string($data);
if ($xml && isset($xml->CompleteSuggestion)) {
foreach ($xml->CompleteSuggestion as $suggestion) {
if (isset($suggestion->suggestion['data'])) {
$suggestions[] = (string) $suggestion->suggestion['data'];
}
}
}
}
break;
case 'bing':
case 'ddg':
// Bing and DuckDuckGo return JSON array format
$json = json_decode($data, true);
if (is_array($json) && isset($json[1]) && is_array($json[1])) {
$suggestions = $json[1];
}
break;
default:
// Try to parse as JSON array
$json = json_decode($data, true);
if (is_array($json)) {
if (isset($json[1]) && is_array($json[1])) {
$suggestions = $json[1];
} else {
$suggestions = $json;
}
}
break;
}
return $suggestions;
}
}

View File

@@ -1,142 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Setting;
use App\SettingGroup;
use Exception;
use enshrined\svgSanitize\Sanitizer;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class SettingsController extends Controller
{
public function __construct()
{
parent::__construct();
$this->middleware('allowed');
}
public function index(): View
{
$settings = SettingGroup::with([
'settings',
])->orderBy('order', 'ASC')->get();
return view('settings.list')->with([
'groups' => $settings,
]);
}
/**
*
* @return RedirectResponse|View
*/
public function edit(int $id)
{
$setting = Setting::find($id);
//die("s: ".$setting->label);
if ((bool) $setting->system === true) {
return abort(404);
}
if (! is_null($setting)) {
return view('settings.edit')->with([
'setting' => $setting,
'value' => $setting->value,
]);
} else {
$route = route('settings.list', []);
return redirect($route)
->with([
'errors' => collect([__('app.alert.error.not_exist')]),
]);
}
}
public function update(Request $request, int $id): RedirectResponse
{
$setting = Setting::find($id);
$user = $this->user();
$route = route('settings.index', []);
try {
if (is_null($setting)) {
throw new Exception('not_exists');
}
if ($setting->type === 'image') {
$validatedData = $request->validate([
'value' => 'image',
]);
if (!$request->hasFile('value')) {
throw new \Exception('file_too_big');
}
$image = $request->file('value');
$extension = $image->getClientOriginalExtension();
if ($extension === 'svg') {
$sanitizer = new Sanitizer();
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($image->getRealPath()));
// Verify that the sanitization removed malicious content
if (strpos($sanitizedSvg, '<script>') !== false) {
throw new \Exception('SVG contains malicious content and cannot be uploaded.');
}
// Save the sanitized SVG back to the file
file_put_contents($image->getRealPath(), $sanitizedSvg);
}
$path = $image->store('backgrounds', 'public');
if ($path === null) {
throw new \Exception('file_not_stored');
}
$setting_value = $path;
} else {
$data = Setting::getInput($request);
$setting_value = $data->value;
}
$user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => $setting_value]);
return redirect($route)
->with([
'success' => __('app.alert.success.setting_updated'),
]);
} catch (Exception $e) {
return redirect($route)
->with([
'errors' => collect([__('app.alert.error.' . $e->getMessage())]),
]);
}
}
public function clear(int $id): RedirectResponse
{
$user = $this->user();
$setting = Setting::find($id);
if ((bool) $setting->system !== true) {
$user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => '']);
}
$route = route('settings.index', []);
return redirect($route)
->with([
'success' => __('app.alert.success.setting_updated'),
]);
}
public function search(Request $request)
{
}
}

View File

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

View File

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

View File

@@ -1,57 +0,0 @@
<?php
namespace App\Http\Middleware;
use Symfony\Component\HttpFoundation\Response;
use App\User;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Session;
class CheckAllowed
{
/**
* Handle an incoming request.
*
* @throws AuthenticationException
*/
public function handle(Request $request, Closure $next): Response
{
$route = Route::currentRouteName();
$current_user = User::currentUser();
// Non admin users can't access users management
if (str_is('users*', $route)) {
if ($current_user->getId() !== 1) {
return redirect()->route('dash');
}
}
// Public access to frontpage
if ($route === 'dash' || $route === 'tags.show') {
if ((bool)$current_user->public_front === true) {
return $next($request);
}
}
// Continue with passwordless user
if (empty($current_user->password)) {
return $next($request);
}
// Check if user is logged in as $current_user
if (Auth::check()) {
$loggedin_user = Auth::user();
if ($loggedin_user->id === $current_user->getId()) {
return $next($request);
}
}
// Redirect to login
Auth::authenticate();
return redirect()->route('user.select');
}
}

View File

@@ -1,23 +0,0 @@
<?php
namespace App\Http\Middleware;
use Symfony\Component\HttpFoundation\Response;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next, string $guard = null): Response
{
if (Auth::guard($guard)->check()) {
return redirect()->intended();
}
return $next($request);
}
}

View File

@@ -1,23 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array
*/
protected $proxies = ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8', '127.0.0.1'];
/**
* The current proxy header mappings.
*
* @var array
*/
protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
}

View File

@@ -1,397 +0,0 @@
<?php
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\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Casts\Attribute;
use stdClass;
use Symfony\Component\ClassLoader\ClassMapGenerator;
// @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 string|null $role
* @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 whereRole($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
{
use SoftDeletes;
use HasFactory;
protected static function boot(): void
{
parent::boot();
static::addGlobalScope('user_id', function (Builder $builder) {
$current_user = User::currentUser();
if ($current_user) {
$builder->where('user_id', $current_user->getId())->orWhere('user_id', 0);
} else {
$builder->where('user_id', 0);
}
});
}
protected $fillable = [
'title',
'url',
'colour',
'icon',
'appdescription',
'description',
'pinned',
'order',
'type',
'class',
'user_id',
'tag_id',
'appid',
'role',
];
/**
* Scope a query to only include pinned items.
*/
public function scopePinned(Builder $query): Builder
{
return $query->where('pinned', 1);
}
public static function checkConfig($config)
{
// die(print_r($config));
if (empty($config)) {
$config = null;
} else {
$config = json_encode($config);
}
return $config;
}
public function tags()
{
$id = $this->id;
$tags = ItemTag::select('tag_id')->where('item_id', $id)->pluck('tag_id')->toArray();
$tagdetails = self::select('id', 'title', 'url', 'pinned')->whereIn('id', $tags)->get();
return $tagdetails;
}
protected function title(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => ($value === 'app.dashboard' ? __('app.dashboard') : $value),
);
}
protected function tagUrl(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => ($attributes['id'] === 0 ? '0-dash' : $attributes['url']),
);
}
public function getTagClass(): string
{
$tags = $this->tags();
$slugs = [];
foreach ($tags as $tag) {
if ($tag->id === 0) {
$tag->url = '0-dash';
}
if ($tag->url) {
$slugs[] = 'tag-'.$tag->url;
}
}
return implode(' ', $slugs);
}
public function getTagList(): string
{
$tags = $this->tags();
$titles = [];
// print_r($tags);
foreach ($tags as $tag) {
if ($tag->title) {
$titles[] = $tag->title;
}
}
return implode(', ', $titles);
}
public function parents(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_tag', 'item_id', 'tag_id');
}
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()
{
if ((int) $this->type === 1) {
return url('tag/'.$this->url);
} else {
return $this->url;
}
}
public function getDroppableAttribute(): string
{
if ((int) $this->type === 1) {
return ' droppable';
} else {
return '';
}
}
public function getLinkTargetAttribute(): string
{
$target = Setting::fetch('window_target');
if ((int) $this->type === 1 || $target === 'current') {
return '';
} else {
return ' target="'.$target.'"';
}
}
public function getLinkIconAttribute(): string
{
if ((int) $this->type === 1) {
return 'fa-tag';
} else {
return 'fa-arrow-alt-to-right';
}
}
public function getLinkTypeAttribute(): string
{
if ((int) $this->type === 1) {
return 'tags';
} else {
return 'items';
}
}
/**
* @param $class
* @return false|mixed|string
*/
public static function nameFromClass($class)
{
$explode = explode('\\', $class);
$name = end($explode);
return $name;
}
/**
* @param $query
* @param $type
* @return mixed
*/
public function scopeOfType($query, $type)
{
switch ($type) {
case 'item':
$typeid = 0;
break;
case 'tag':
$typeid = 1;
break;
}
return $query->where('type', $typeid);
}
public function enhanced(): bool
{
/*if(isset($this->class) && !empty($this->class)) {
$app = new $this->class;
} else {
return false;
}
return (bool)($app instanceof \App\EnhancedApps);*/
return $this->description !== null;
}
/**
* @param $class
*/
public static function isEnhanced($class): bool
{
if (!class_exists($class, false) || $class === null || $class === 'null') {
return false;
}
$app = new $class;
return (bool) ($app instanceof EnhancedApps);
}
/**
* @param $class
* @return false|mixed
*/
public static function isSearchProvider($class)
{
if (!class_exists($class, false) || $class === null || $class === 'null') {
return false;
}
$app = new $class;
return ((bool) ($app instanceof SearchInterface)) ? $app : false;
}
public function enabled(): bool
{
if ($this->enhanced()) {
$config = $this->getconfig();
if ($config) {
return (bool) $config->enabled;
}
}
return false;
}
/**
* @return mixed|stdClass
*/
public function getconfig()
{
// $explode = explode('\\', $this->class);
if (! isset($this->description) || empty($this->description)) {
$config = new stdClass;
// $config->name = end($explode);
$config->enabled = false;
$config->override_url = null;
$config->apikey = null;
return $config;
}
$config = json_decode($this->description);
// $config->name = end($explode);
$config->url = $this->url;
if (isset($config->override_url) && ! empty($config->override_url)) {
$config->url = $config->override_url;
} else {
$config->override_url = null;
}
return $config;
}
/**
* @param $class
*/
public static function applicationDetails($class): ?Application
{
if (! empty($class)) {
$name = self::nameFromClass($class);
$application = Application::where('name', $name)->first();
if ($application) {
return $application;
}
}
return null;
}
/**
* @param $class
*/
public static function getApplicationDescription($class): string
{
$details = self::applicationDetails($class);
if ($details !== null) {
return $details->description.' - '.$details->license;
}
return '';
}
/**
* Get the user that owns the item.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View File

@@ -1,27 +0,0 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
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
{
use HasFactory;
}

View File

@@ -1,60 +0,0 @@
<?php
namespace App\Jobs;
use App\Application;
use App\Item;
use App\SupportedApps;
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\Log;
use Illuminate\Support\Facades\Storage;
class ProcessApps implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @throws GuzzleException
*/
public function handle(): void
{
Log::debug('Process Apps dispatched');
$localapps = Application::whereNull('class')->get();
$json = SupportedApps::getList()->getBody();
Storage::disk('local')->put('supportedapps.json', $json);
foreach ($localapps as $app) {
$app->class = $app->class();
$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,56 +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.
*
* @throws GuzzleException
*/
public function handle(): void
{
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();
}
public function failed($exception): void
{
Cache::lock('updateApps')->forceRelease();
}
}

View File

@@ -1,198 +0,0 @@
<?php
namespace App\Providers;
use App\Application;
use App\Jobs\ProcessApps;
use App\Jobs\UpdateApps;
use App\Setting;
use App\User;
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use App\Services\CustomFormBuilder;
use Spatie\Html\Html;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
if (! class_exists('ZipArchive')) {
die('You are missing php-zip');
}
$this->createEnvFile();
$this->setupDatabase();
if (! is_file(public_path('storage/.gitignore'))) {
Artisan::call('storage:link');
\Session::put('current_user', null);
}
$applications = Application::all();
if ($applications->count() <= 0) {
ProcessApps::dispatch();
}
$lang = Setting::fetch('language');
\App::setLocale($lang);
// User specific settings need to go here as session isn't available at this point in the app
view()->composer('*', function ($view) {
if (isset($_SERVER['HTTP_AUTHORIZATION']) && ! empty($_SERVER['HTTP_AUTHORIZATION'])) {
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
}
if (! \Auth::check()) {
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])
&& ! empty($_SERVER['PHP_AUTH_USER']) && ! empty($_SERVER['PHP_AUTH_PW'])) {
$credentials = ['username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']];
if (\Auth::attempt($credentials, true)) {
// Authentication passed...
$user = \Auth::user();
//\Session::put('current_user', $user);
session(['current_user' => $user]);
}
} elseif (isset($_SERVER['REMOTE_USER']) && ! empty($_SERVER['REMOTE_USER'])) {
$user = User::where('username', $_SERVER['REMOTE_USER'])->first();
if ($user) {
\Auth::login($user, true);
session(['current_user' => $user]);
}
}
}
$alt_bg = '';
$trianglify = 'false';
$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.')"';
}
$allusers = User::all();
$current_user = User::currentUser();
$view->with('alt_bg', $alt_bg);
$view->with('trianglify', $trianglify);
$view->with('trianglify_seed', $trianglify_seed);
$view->with('allusers', $allusers);
$view->with('current_user', $current_user);
if (config('app.auth_roles_enable')) {
$view->with('enable_auth_admin_controls', in_array(config('app.auth_roles_admin'), explode(config('app.auth_roles_delimiter'), $_SERVER[config('app.auth_roles_http_header')])));
} else {
$view->with('enable_auth_admin_controls', true);
}
});
$this->app['view']->addNamespace('SupportedApps', app_path('SupportedApps'));
if (env('FORCE_HTTPS') === true) {
\URL::forceScheme('https');
}
if (env('APP_URL') != 'http://localhost') {
\URL::forceRootUrl(env('APP_URL'));
}
}
/**
* Generate app key if missing and .env exists
*/
public function genKey(): void
{
if (is_file(base_path('.env'))) {
if (empty(env('APP_KEY'))) {
Artisan::call('key:generate', ['--force' => true, '--no-interaction' => true]);
}
}
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->isLocal()) {
$this->app->register(IdeHelperServiceProvider::class);
}
$this->app->singleton('custom-form', function ($app) {
return new CustomFormBuilder($app->make(Html::class));
});
$this->app->singleton('settings', function () {
return new Setting();
});
}
/**
* Check if database needs an update or do first time database setup
*
* @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'));
Log::debug('SQLite Database Path: ' . $db_file);
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();
}
}
public function createEnvFile(): void
{
if (!is_file(base_path('.env'))) {
copy(base_path('.env.example'), base_path('.env'));
}
$this->genKey();
}
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;
}
private function updateApps(): void
{
// 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

@@ -1,62 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* This namespace is applied to your controller routes.
*
* In addition, it is set as the URL generator's root namespace.
*
* REMOVED WITH LARAVEL 8 UPGRADE
*/
/**
* Define your route model bindings, pattern filters, etc.
*/
public function boot(): void
{
//
parent::boot();
}
/**
* Define the routes for the application.
*/
public function map(): void
{
$this->mapApiRoutes();
$this->mapWebRoutes();
//
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*/
protected function mapWebRoutes(): void
{
Route::middleware('web')
->group(base_path('routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*/
protected function mapApiRoutes(): void
{
Route::prefix('api')
->middleware('api')
->group(base_path('routes/api.php'));
}
}

View File

@@ -1,149 +0,0 @@
<?php
namespace App;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Request as Input;
use Yaml;
abstract class Search
{
/**
* List of all search providers
*
* @return Collection
*/
public static function providers(): Collection
{
$providers = self::standardProviders();
$providers = $providers + self::appProviders();
return collect($providers);
}
/**
* Gets details for a single provider
*
* @return false|object
*/
public static function providerDetails($provider)
{
$providers = self::providers();
if (! isset($providers[$provider])) {
return false;
}
return (object) $providers[$provider] ?? false;
}
/**
* Array of the standard providers
*
* @return array
*/
public static function standardProviders(): array
{
// $providers = json_decode(file_get_contents(storage_path('app/searchproviders.json')));
// print_r($providers);
$providers = Yaml::parseFile(storage_path('app/searchproviders.yaml'));
$all = [];
foreach ($providers as $key => $provider) {
$all[$key] = $provider;
$all[$key]['type'] = 'standard';
}
return $all;
}
/**
* Loops through users apps to see if app is a search provider, might be worth
* looking into caching this at some point
*
* @return array
*/
public static function appProviders(): array
{
$providers = [];
$userapps = Item::all();
foreach ($userapps as $app) {
if (empty($app->class)) {
continue;
}
if (($provider = Item::isSearchProvider($app->class)) !== false) {
$name = Item::nameFromClass($app->class);
$providers[$app->id] = [
'id' => $app->id,
'type' => $provider->type,
'class' => $app->class,
'url' => $app->url,
'name' => $app->title,
'colour' => $app->colour,
'icon' => $app->icon,
'description' => $app->description,
];
}
}
return $providers;
}
/**
* Outputs the search form
*
* @return string
*/
public static function form(): string
{
$output = '';
$homepage_search = Setting::fetch('homepage_search');
$search_provider = Setting::where('key', '=', 'search_provider')->first();
$user_search_provider = Setting::fetch('search_provider');
//die(print_r($search_provider));
//die(var_dump($user_search_provider));
// return early if search isn't applicable
if ((bool) $homepage_search !== true) {
return $output;
}
$user_search_provider = Input::get('p') ?? $user_search_provider ?? 'none';
if ((bool) $search_provider) {
if ((bool) $user_search_provider) {
$name = 'app.options.'.$user_search_provider;
$provider = self::providerDetails($user_search_provider);
$providers = self::providers();
$providerCount = count($providers);
// If there's only one provider, use its key instead of the user's setting
if ($providerCount === 1) {
$user_search_provider = $providers->keys()->first();
}
$output .= '<div class="searchform">';
$output .= '<form action="'.url('search').'"'.getLinkTargetAttribute().' method="get">';
$output .= '<div id="search-container" class="input-container">';
// Only show dropdown if there's more than one provider
if ($providerCount > 1) {
$output .= '<select name="provider">';
foreach ($providers as $key => $searchprovider) {
$selected = ((string) $key === (string) $user_search_provider) ? ' selected="selected"' : '';
$output .= '<option value="'.$key.'"'.$selected.'>'.$searchprovider['name'].'</option>';
}
$output .= '</select>';
} else {
// Hidden input for single provider
$output .= '<input type="hidden" name="provider" value="'.$user_search_provider.'" />';
}
$output .= '<input type="text" name="q" value="'.e(Input::get('q') ?? '').'" class="homesearch" autofocus placeholder="'.__('app.settings.search').'..." />';
$output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>';
$output .= '</div>';
$output .= '</form>';
$output .= '</div>';
}
}
return $output;
}
}

View File

@@ -1,8 +0,0 @@
<?php
namespace App;
interface SearchInterface
{
public function getResults($query, $providerdetails);
}

View File

@@ -1,67 +0,0 @@
<?php
namespace App\Services;
use Spatie\Html\Html;
use Illuminate\Support\HtmlString;
class CustomFormBuilder
{
protected Html $html;
public function __construct(Html $html)
{
$this->html = $html;
}
public function text($name, $value = null, $options = [])
{
return new HtmlString(
$this->html->input('text', $name, $value)->attributes($options)
);
}
public function password($name, $options = [])
{
return new HtmlString(
$this->html->input('password', $name)->attributes($options)
);
}
public function hidden($name, $value = null, $options = [])
{
return new HtmlString(
$this->html->input('hidden', $name, $value)->attributes($options)
);
}
public function checkbox($name, $value = null, $checked = false, $options = [])
{
return new HtmlString(
$this->html->checkbox($name, $value, $checked)->attributes($options)
);
}
public function select($name, $list = [], $selected = null, $options = [])
{
return new HtmlString(
$this->html->select($name)->options($list, $selected)->attributes($options)
);
}
public function textarea($name, $value = null, $options = [])
{
return new HtmlString(
$this->html->textarea($name, $value)->attributes($options)
);
}
public function input($type, $name, $value = null, $options = [])
{
return new HtmlString(
$this->html->input($type, $name, $value)->attributes($options)
);
}
// Add other methods as needed
}

View File

@@ -1,302 +0,0 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Builder;
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 enshrined\svgSanitize\Sanitizer;
/**
* 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
{
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'settings';
protected $fillable = [
'id', 'group_id', 'key', 'type', 'options', 'label', 'value', 'order', 'system',
];
/**
* Tell the Model this Table doesn't support timestamps.
*
* @var bool
*/
public $timestamps = false;
/**
* Cache storage for Settings.
*
* @var array
*/
protected static $cache = [];
public static function getInput(Request $request): object
{
$image = $request->file('value');
if ($image && $image->getClientOriginalExtension() === 'svg') {
$sanitizer = new Sanitizer();
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($image->getRealPath()));
// Verify that the sanitization removed malicious content
if (strpos($sanitizedSvg, '<script>') !== false) {
throw new \Exception('SVG contains malicious content and cannot be uploaded.');
}
// Save the sanitized SVG back to the file
file_put_contents($image->getRealPath(), $sanitizedSvg);
}
return (object) [
'value' => $request->input('value'),
'image' => $image,
];
}
public function getListValueAttribute()
{
if ((bool) $this->system === true) {
$value = self::_fetch($this->key);
} else {
$value = self::fetch($this->key);
}
$this->value = $value;
switch ($this->type) {
case 'image':
if (! empty($this->value)) {
$value = '<a href="'.asset('storage/'.$this->value).'" title="'.
__('app.settings.view').
'" target="_blank">'.
__('app.settings.view').
'</a>';
} else {
$value = __('app.options.none');
}
break;
case 'boolean':
if ((bool) $this->value === true) {
$value = __('app.options.yes');
} else {
$value = __('app.options.no');
}
break;
case 'select':
if (! empty($this->value) && $this->value !== 'none') {
$options = (array) json_decode($this->options);
if ($this->key === 'search_provider') {
$options = Search::providers()->pluck('name', 'id')->toArray();
}
$value = (array_key_exists($this->value, $options))
? __($options[$this->value])
: __('app.options.none');
} else {
$value = __('app.options.none');
}
break;
default:
$value = __($this->value);
break;
}
return $value;
}
public function getEditValueAttribute()
{
if ((bool) $this->system === true) {
$value = self::_fetch($this->key);
} else {
$value = self::fetch($this->key);
}
$this->value = $value;
switch ($this->type) {
case 'image':
$value = '';
if (isset($this->value) && !empty($this->value)) {
$value .= '<a class="setting-view-image" href="' .
asset('storage/' . $this->value) .
'" title="' .
__('app.settings.view') .
'" target="_blank"><img src="' .
asset('storage/' .
$this->value) .
'" /></a>';
}
$value .= '<input type="file" name="value" class="form-control" />';
if (isset($this->value) && !empty($this->value)) {
$value .= '<a class="settinglink" href="' .
route('settings.clear', $this->id) .
'" title="' .
__('app.settings.remove') .
'">' .
__('app.settings.reset') .
'</a>';
}
break;
case 'boolean':
$checked = false;
if (isset($this->value) && (bool)$this->value === true) {
$checked = true;
}
$set_checked = ($checked) ? ' checked="checked"' : '';
$value = '
<input type="hidden" name="value" value="0" />
<label class="switch">
<input type="checkbox" name="value" value="1"' . $set_checked . ' />
<span class="slider round"></span>
</label>';
break;
case 'select':
$options = json_decode($this->options);
if ($this->key === 'search_provider') {
$options = Search::providers()->pluck('name', 'id');
}
$value = '<select name="value" class="form-control">';
foreach ($options as $key => $opt) {
$value .= '<option value="' . $key . '" ' . (($this->value == $key) ? 'selected' : '') . '>' . __($opt) . '</option>';
}
$value .= '</select>';
break;
case 'textarea':
$value = '<textarea name="value" class="form-control" cols="44" rows="15">' . htmlspecialchars($this->value, ENT_QUOTES, 'UTF-8') . '</textarea>';
break;
default:
$value = '<input type="text" name="value" class="form-control" value="' . htmlspecialchars($this->value, ENT_QUOTES, 'UTF-8') . '" />';
break;
}
return $value;
}
public function group(): BelongsTo
{
return $this->belongsTo(\App\SettingGroup::class, 'group_id');
}
/**
*
* @return mixed
*/
public static function fetch(string $key)
{
$user = self::user();
return self::_fetch($key, $user);
}
// @codingStandardsIgnoreStart
/**
*
* @return mixed
*/
public static function _fetch(string $key, $user = null)
{
// @codingStandardsIgnoreEnd
//$cachekey = ($user === null) ? $key : $key.'-'.$user->id;
//if (Setting::cached($cachekey)) {
// return Setting::$cache[$cachekey];
//} else {
$find = self::where('key', '=', $key)->first();
if (! is_null($find)) {
if ((bool) $find->system === true) { // if system variable use global value
$value = $find->value;
} else { // not system variable so use user specific value
// check if user specified value has been set
//print_r($user);
$usersetting = $user->settings()->where('id', $find->id)->first();
//print_r($user->settings);
//die(var_dump($usersetting));
//->pivot->value;
//echo "user: ".$user->id." --- ".$usersettings;
if (isset($usersetting) && ! empty($usersetting)) {
$value = $usersetting->pivot->uservalue;
} else { // if not get default from base setting
//$user->settings()->save($find, ['value' => $find->value]);
//$has_setting = $user->settings()->where('id', $find->id)->exists();
//if($has_setting) {
// $user->settings()->updateExistingPivot($find->id, ['uservalue' => (string)$find->value]);
//} else {
// $user->settings()->save($find, ['uservalue' => (string)$find->value]);
//}
$value = $find->value;
}
}
//Setting::add($cachekey, $value);
return $value;
} else {
return false;
}
//}
}
/**
* @param $value
*/
public static function add(string $key, $value)
{
self::$cache[$key] = $value;
}
public static function cached(string $key): bool
{
return array_key_exists($key, self::$cache);
}
/**
* The users that belong to the setting.
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(\App\User::class)->using(\App\SettingUser::class)->withPivot('uservalue');
}
/**
* @return \Illuminate\Contracts\Foundation\Application|SessionManager|Store|mixed
*/
public static function user()
{
return User::currentUser();
}
}

View File

@@ -1,44 +0,0 @@
<?php
namespace App;
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
{
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'setting_groups';
/**
* Tell the Model this Table doesn't support timestamps.
*
* @var bool
*/
public $timestamps = false;
public function settings(): HasMany
{
return $this->hasMany(\App\Setting::class, 'group_id');
}
}

View File

@@ -1,24 +0,0 @@
<?php
namespace App;
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
{
//
}

View File

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

View File

@@ -1,110 +0,0 @@
<?php
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;
// @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
{
use Notifiable;
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'username', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* Get the items for the user.
*/
public function items(): HasMany
{
return $this->hasMany(Item::class);
}
/**
* The settings that belong to the user.
*/
public function settings(): BelongsToMany
{
return $this->belongsToMany(Setting::class)->withPivot('uservalue');
}
public static function currentUser()
{
$current_user = session('current_user');
if ($current_user) { // if logged in, set this user
return $current_user;
} else { // not logged in, get first user
$user = self::where('public_front', true)->first();
if (! $user) {
$user = self::first();
}
session(['current_user' => $user]);
return $user;
}
}
}

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

15
artisan
View File

@@ -1,15 +0,0 @@
#!/usr/bin/env php
<?php
use Symfony\Component\Console\Input\ArgvInput;
define('LARAVEL_START', microtime(true));
// Register the Composer autoloader...
require __DIR__.'/vendor/autoload.php';
// Bootstrap Laravel and handle the command...
$status = (require_once __DIR__.'/bootstrap/app.php')
->handleCommand(new ArgvInput);
exit($status);

View File

@@ -1,43 +0,0 @@
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withProviders([
\Spatie\Html\HtmlServiceProvider::class,
])
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
channels: __DIR__.'/../routes/channels.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->redirectGuestsTo(fn () => route('login'));
$middleware->validateCsrfTokens(except: [
//
'order',
'appload',
'test_config',
//'get_stats'
]);
$middleware->append(\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class);
$middleware->throttleApi('60,1');
$middleware->replace(\Illuminate\Http\Middleware\TrustProxies::class, \App\Http\Middleware\TrustProxies::class);
$middleware->alias([
'allowed' => \App\Http\Middleware\CheckAllowed::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();

View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -1,7 +0,0 @@
<?php
return [
App\Providers\AppServiceProvider::class,
App\Providers\FormMacroServiceProvider::class,
App\Providers\RouteServiceProvider::class,
];

View File

@@ -1,91 +0,0 @@
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": [
"framework",
"laravel"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.2",
"ext-intl": "*",
"ext-json": "*",
"enshrined/svg-sanitize": "^0.21.0",
"graham-campbell/github": "^12.5",
"guzzlehttp/guzzle": "^7.8",
"laravel/framework": "^11.45",
"laravel/tinker": "^2.9",
"laravel/ui": "^4.4",
"league/flysystem-aws-s3-v3": "^3.0",
"nunomaduro/collision": "^8.0",
"spatie/laravel-html": "^3.11",
"spatie/laravel-ignition": "^2.4",
"symfony/yaml": "^7.0"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^3.0",
"filp/whoops": "^2.8",
"mockery/mockery": "^1.6",
"phpunit/phpunit": "^10.5",
"squizlabs/php_codesniffer": "3.*",
"symfony/thanks": "^1.2",
"fakerphp/faker": "^1.23"
},
"autoload": {
"classmap": [
"database/seeders",
"database/factories"
],
"files": [
"app/Helper.php"
],
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-ide-helper"
]
}
},
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"@php artisan ide-helper:generate",
"@php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"kylekatarnls/update-helper": true,
"symfony/thanks": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}

10341
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

View File

@@ -1,32 +0,0 @@
<?php
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Facade;
return [
'version' => '2.7.7',
'appsource' => env('APP_SOURCE', 'https://appslist.heimdall.site/'),
'allow_internal_requests' => env('ALLOW_INTERNAL_REQUESTS', false),
'aliases' => Facade::defaultAliases()->merge([
'EnhancedApps' => App\EnhancedApps::class,
'Form' => App\Facades\Form::class,
'Redis' => Illuminate\Support\Facades\Redis::class,
'SupportedApps' => App\SupportedApps::class,
'Yaml' => Symfony\Component\Yaml\Yaml::class,
])->toArray(),
'auth_roles_enable' => (bool) env('AUTH_ROLES_ENABLE', false),
'auth_roles_header' => env('AUTH_ROLES_HEADER', 'remote-groups'),
'auth_roles_http_header' => env('AUTH_ROLES_HTTP_HEADER', 'HTTP_REMOTE_GROUPS'),
'auth_roles_admin' => env('AUTH_ROLES_ADMIN', 'admin'),
'auth_roles_delimiter' => env('AUTH_ROLES_DELIMITER', ','),
];

View File

@@ -1,19 +0,0 @@
<?php
return [
'guards' => [
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class, // Update this to the correct namespace
],
],
];

View File

@@ -1,22 +0,0 @@
<?php
return [
'default' => env('DB_CONNECTION', 'sqlite'), // Make sure the default connection is set
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => database_path(env('DB_DATABASE', 'app.sqlite')), // Make sure to use the correct path
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), // Enable foreign key constraints
],
],
'migrations' => [
'table' => 'migrations',
'update_date_on_publish' => false, // disable to preserve original behavior for existing applications
],
];

View File

@@ -1,15 +0,0 @@
<?php
return [
'cloud' => env('FILESYSTEM_CLOUD', 's3'),
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
],
];

View File

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

View File

@@ -1,22 +0,0 @@
<?php
return [
'mailers' => [
'mailgun' => [
'transport' => 'mailgun',
// 'client' => [
// 'timeout' => 5,
// ],
],
],
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
],
];

View File

@@ -1,12 +0,0 @@
<?php
return [
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
'scheme' => 'https',
],
];

View File

@@ -3635,6 +3635,7 @@ body {
font-family: 'Raleway', sans-serif;
font-weight: 400;
line-height: 1.5;
color: #4a5c67;
}
.section1 {
@@ -3683,7 +3684,7 @@ a {
}
h1 {
font-size: 28px;
font-size: 35px;
text-transform: uppercase;
}
@@ -3692,6 +3693,12 @@ h1 span {
opacity: 0.4;
}
h2 {
font-size: 35px;
text-transform: uppercase;
margin: 0;
}
.section5 {
background: #c7cfd4;
padding: 80px 40px 1px;

1
database/.gitignore vendored
View File

@@ -1 +0,0 @@
*.sqlite

View File

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

View File

@@ -1,18 +0,0 @@
<?php
namespace Database\Factories;
use App\Item;
use App\ItemTag;
use Illuminate\Database\Eloquent\Factories\Factory;
class ItemTagFactory extends Factory
{
/**
* Define the model's default state.
*/
public function definition(): array
{
return [];
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace Database\Factories;
use Illuminate\Support\Facades\Hash;
use App\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
protected static ?string $password;
public function definition(): array
{
return [
'username' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'password' => static::$password ??= Hash::make('password'),
'public_front' => 1,
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): Factory
{
return $this->state(function (array $attributes) {
return [
'email_verified_at' => null,
];
});
}
}

View File

@@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('items', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('colour')->nullable();
$table->string('icon')->nullable();
$table->string('url');
$table->text('description')->nullable();
$table->boolean('pinned')->default(false);
$table->integer('order')->default(0);
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('items');
}
};

View File

@@ -1,34 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('settings', function (Blueprint $table) {
$table->increments('id');
$table->integer('group_id')->default(0);
$table->string('key');
$table->string('type')->default('text');
$table->text('options')->nullable();
$table->string('label');
$table->string('value')->nullable();
$table->string('order')->default(0);
$table->boolean('system')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('settings');
}
};

View File

@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('setting_groups', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->integer('order')->default(0);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('setting_groups');
}
};

View File

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

View File

@@ -1,31 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('item_tag', function (Blueprint $table) {
$table->integer('item_id')->unsigned()->index();
$table->foreign('item_id')->references('id')->on('items')->onDelete('cascade');
$table->integer('tag_id')->unsigned()->index();
$table->foreign('tag_id')->references('id')->on('items')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('item_tag');
}
};

View File

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

View File

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

View File

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

View File

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

View File

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

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