mirror of
https://github.com/linuxserver/Heimdall.git
synced 2026-02-23 21:20:32 +09:00
Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7861ae1512 | ||
|
|
66dfe95c9f | ||
|
|
130661bd34 | ||
|
|
900fc83e79 | ||
|
|
4f30332854 | ||
|
|
852c231724 | ||
|
|
045bdf0deb | ||
|
|
755c3e59e1 | ||
|
|
32bf1d034f | ||
|
|
6d12c547e7 | ||
|
|
ae4ce92dab | ||
|
|
966279b252 | ||
|
|
54cf2b88ca | ||
|
|
31f1ba8192 | ||
|
|
eadd9d1dd8 | ||
|
|
c9ea2cdeb3 | ||
|
|
517f51ba90 | ||
|
|
825f67a4a4 | ||
|
|
05a552ffcf | ||
|
|
ad4584e548 | ||
|
|
7d93099f2c | ||
|
|
31ca05f74f | ||
|
|
cd95fc3b92 | ||
|
|
63e777b338 | ||
|
|
fd926e983d | ||
|
|
dce37c1412 | ||
|
|
6b9f61b0e6 | ||
|
|
d1a96dd752 | ||
|
|
1ccc0da2a7 | ||
|
|
41aa255b88 | ||
|
|
a8e4ab448b | ||
|
|
31db31d0f7 | ||
|
|
e42f78bd49 | ||
|
|
08b8ab6d4f | ||
|
|
57e0a335b7 | ||
|
|
0d90170385 | ||
|
|
abe08a7b04 | ||
|
|
6075dcca2d | ||
|
|
4fa41d8114 | ||
|
|
1e6b1f6de5 | ||
|
|
53fd6242db | ||
|
|
69bc8cb34e | ||
|
|
0388ee939a | ||
|
|
2df58472a1 | ||
|
|
abbc78ee8d | ||
|
|
d1801d1088 | ||
|
|
22f66d35e5 | ||
|
|
f197aeb013 | ||
|
|
8fb6438254 | ||
|
|
d972cbcd0a | ||
|
|
638d2fe4a4 | ||
|
|
e251f4f1bc | ||
|
|
ecc54b6fbe | ||
|
|
f0ce9d633a | ||
|
|
968a5823a1 | ||
|
|
53f28b5056 | ||
|
|
7bb0a7ceb4 | ||
|
|
f5ddd93141 | ||
|
|
8554861d0a | ||
|
|
9091d1d707 | ||
|
|
42d29f0fdb | ||
|
|
474059eee8 | ||
|
|
1fb6f75555 | ||
|
|
48e16ebfc9 | ||
|
|
7153a41e03 | ||
|
|
b9e75b9284 | ||
|
|
b3cdc5779c | ||
|
|
c1c3888673 | ||
|
|
11453e1018 | ||
|
|
749442ec6e | ||
|
|
5950ca9076 | ||
|
|
55d3766b39 | ||
|
|
c71479f266 | ||
|
|
2e5d7453cf | ||
|
|
3cbbf807ab | ||
|
|
65a6ad53bf | ||
|
|
dff3f90d00 | ||
|
|
041ec4236b | ||
|
|
808c41acd6 | ||
|
|
c08b0bfe39 | ||
|
|
f7de56b7a7 | ||
|
|
921631bdcd | ||
|
|
d146fed320 | ||
|
|
c56ffe1d1e | ||
|
|
e9d561466b | ||
|
|
b90b38eae9 | ||
|
|
be59ac794e | ||
|
|
f820c0b4f6 | ||
|
|
2b43760dc8 | ||
|
|
8f360f97ef | ||
|
|
014f054862 | ||
|
|
5ccb87cd7f | ||
|
|
8ba8f0c867 | ||
|
|
c2a3368c7b | ||
|
|
395c775d4e | ||
|
|
1063d2cd09 | ||
|
|
18609fb3c2 | ||
|
|
b880333856 | ||
|
|
026bcf9e43 | ||
|
|
837f5c49fa | ||
|
|
a4973869d4 | ||
|
|
07ea22dd10 | ||
|
|
6188b1d669 | ||
|
|
184e19abbc | ||
|
|
f405cf2ddf |
23
.env.example
23
.env.example
@@ -4,7 +4,15 @@ 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
|
||||
@@ -16,11 +24,14 @@ DB_DATABASE=app.sqlite
|
||||
#DB_USERNAME=<user>
|
||||
#DB_PASSWORD=<password>
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
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
|
||||
@@ -48,3 +59,11 @@ 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
|
||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
extensions: mbstring, dom, fileinfo, mysql, libxml, xml, xmlwriter, dom, tokenizer, filter, json, phar, pcre, openssl, pdo, intl, curl
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: vendor
|
||||
key: composer-${{ hashFiles('composer.lock') }}
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
php artisan key:generate
|
||||
|
||||
- name: Cache yarn dependencies
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: yarn-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
3201
.phpstorm.meta.php
3201
.phpstorm.meta.php
File diff suppressed because it is too large
Load Diff
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"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
Normal file
16
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Start Docker Compose",
|
||||
"type": "shell",
|
||||
"command": "docker-compose up --build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
14
SECURITY.md
14
SECURITY.md
@@ -1,14 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.3.x | :white_check_mark: |
|
||||
| < 2.3 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
You can report any vulnerabilities on our discord server by DM-ing a team member, or asking a team member to DM you.
|
||||
|
||||
https://discord.com/invite/YWrKVTn
|
||||
28476
_ide_helper.php
28476
_ide_helper.php
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
// $schedule->command('inspire')
|
||||
// ->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*/
|
||||
protected function commands(): void
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* The list of the inputs that are never flashed to the session on validation exceptions.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
13
app/Facades/Form.php
Normal file
13
app/Facades/Form.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class Form extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return 'custom-form';
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
/**
|
||||
* @param $bytes
|
||||
@@ -26,6 +27,17 @@ function format_bytes($bytes, bool $is_drive_size = true, string $beforeunit = '
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -117,7 +129,7 @@ function className($name)
|
||||
*/
|
||||
function isImage(string $file, string $extension): bool
|
||||
{
|
||||
$allowedExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp'];
|
||||
$allowedExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp', 'ico'];
|
||||
|
||||
if (!in_array($extension, $allowedExtensions)) {
|
||||
return false;
|
||||
@@ -129,7 +141,11 @@ function isImage(string $file, string $extension): bool
|
||||
fwrite($handle, $file);
|
||||
fclose($handle);
|
||||
|
||||
if ($extension == 'svg') {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ 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
|
||||
{
|
||||
@@ -33,34 +35,55 @@ class ItemController extends Controller
|
||||
/**
|
||||
* Display a listing of the resource on the dashboard.
|
||||
*/
|
||||
public function dash(): View
|
||||
public function dash(Request $request): View
|
||||
{
|
||||
$treat_tags_as = \App\Setting::fetch('treat_tags_as');
|
||||
|
||||
$data["treat_tags_as"] = $treat_tags_as;
|
||||
|
||||
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();
|
||||
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['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::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();
|
||||
@@ -171,6 +194,7 @@ class ItemController extends Controller
|
||||
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';
|
||||
@@ -185,7 +209,7 @@ class ItemController extends Controller
|
||||
{
|
||||
// Get the item
|
||||
$item = Item::find($id);
|
||||
if ($item->appid === null && $item->class !== null) { // old apps wont have an app id so set it
|
||||
if ($item->appid === null && $item->class !== null) { // old apps won't have an app id so set it
|
||||
$app = Application::where('class', $item->class)->first();
|
||||
if ($app) {
|
||||
$item->appid = $app->appid;
|
||||
@@ -215,7 +239,23 @@ class ItemController extends Controller
|
||||
]);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('icons', 'public');
|
||||
$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,
|
||||
]);
|
||||
@@ -227,12 +267,32 @@ class ItemController extends Controller
|
||||
],
|
||||
];
|
||||
|
||||
// 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.']);
|
||||
}
|
||||
@@ -259,7 +319,11 @@ class ItemController extends Controller
|
||||
$storedConfigObject = json_decode($storedItem->getAttribute('description'));
|
||||
|
||||
$configObject = json_decode($config);
|
||||
$configObject->password = $storedConfigObject->password;
|
||||
if ($storedConfigObject && property_exists($storedConfigObject, 'password')) {
|
||||
$configObject->password = $storedConfigObject->password;
|
||||
} else {
|
||||
$configObject->password = null;
|
||||
}
|
||||
|
||||
$config = json_encode($configObject);
|
||||
}
|
||||
@@ -270,7 +334,7 @@ class ItemController extends Controller
|
||||
'user_id' => $current_user->getId(),
|
||||
]);
|
||||
|
||||
if ($request->input('appid') === 'null') {
|
||||
if ($request->input('appid') === 'null' || $request->input('appid') === null) {
|
||||
$request->merge([
|
||||
'class' => null,
|
||||
]);
|
||||
@@ -370,23 +434,37 @@ class ItemController extends Controller
|
||||
{
|
||||
$output = [];
|
||||
$appid = $request->input('app');
|
||||
$itemId = $request->input('item_id');
|
||||
|
||||
if ($appid === 'null') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$output['config'] = null;
|
||||
$output['custom'] = 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) {
|
||||
// if(!isset($app->config)) { // class based config
|
||||
$output['custom'] = className($appdetails->name) . '.config';
|
||||
// }
|
||||
$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';
|
||||
@@ -394,14 +472,12 @@ class ItemController extends Controller
|
||||
if (strpos($app->icon, '://') !== false) {
|
||||
$output['iconview'] = $app->icon;
|
||||
} elseif (strpos($app->icon, 'icons/') !== false) {
|
||||
// Private apps have the icon locally
|
||||
$output['iconview'] = URL::to('/') . '/storage/' . $app->icon;
|
||||
$output['icon'] = str_replace('icons/', '', $output['icon']);
|
||||
} else {
|
||||
$output['iconview'] = config('app.appsource') . 'icons/' . $app->icon;
|
||||
}
|
||||
|
||||
|
||||
return json_encode($output);
|
||||
}
|
||||
|
||||
@@ -439,25 +515,48 @@ class ItemController extends Controller
|
||||
*/
|
||||
public function execute($url, array $attrs = [], $overridevars = false): ?ResponseInterface
|
||||
{
|
||||
$vars = ($overridevars !== false) ?
|
||||
$overridevars : [
|
||||
'http_errors' => false,
|
||||
'timeout' => 15,
|
||||
'connect_timeout' => 15,
|
||||
'verify' => false,
|
||||
];
|
||||
// Default Guzzle client configuration
|
||||
$clientOptions = [
|
||||
'http_errors' => false,
|
||||
'timeout' => 15,
|
||||
'connect_timeout' => 15,
|
||||
'verify' => false, // In production, set this to `true` and manage certs.
|
||||
];
|
||||
|
||||
$client = new Client($vars);
|
||||
// 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::error('Connection refused');
|
||||
Log::debug($e->getMessage());
|
||||
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;
|
||||
@@ -469,10 +568,22 @@ class ItemController extends Controller
|
||||
*/
|
||||
public function websitelookup($url): StreamInterface
|
||||
{
|
||||
$url = base64_decode($url);
|
||||
$data = $this->execute($url);
|
||||
$decodedUrl = base64_decode($url);
|
||||
|
||||
return $data->getBody();
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,9 +4,11 @@ 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
|
||||
{
|
||||
@@ -18,16 +20,120 @@ class SearchController extends Controller
|
||||
$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;
|
||||
//print_r($provider);
|
||||
return $class->getResults($query, $provider);
|
||||
}
|
||||
|
||||
//print_r($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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -44,6 +45,7 @@ class SettingsController extends Controller
|
||||
if (! is_null($setting)) {
|
||||
return view('settings.edit')->with([
|
||||
'setting' => $setting,
|
||||
'value' => $setting->value,
|
||||
]);
|
||||
} else {
|
||||
$route = route('settings.list', []);
|
||||
@@ -68,16 +70,30 @@ class SettingsController extends Controller
|
||||
|
||||
if ($setting->type === 'image') {
|
||||
$validatedData = $request->validate([
|
||||
'value' => 'image'
|
||||
'value' => 'image',
|
||||
]);
|
||||
|
||||
if (!$request->hasFile('value')) {
|
||||
throw new \Exception(
|
||||
'file_too_big'
|
||||
);
|
||||
throw new \Exception('file_too_big');
|
||||
}
|
||||
|
||||
$path = $request->file('value')->store('backgrounds', 'public');
|
||||
$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');
|
||||
@@ -99,7 +115,7 @@ class SettingsController extends Controller
|
||||
} catch (Exception $e) {
|
||||
return redirect($route)
|
||||
->with([
|
||||
'errors' => collect([__('app.alert.error.'.$e->getMessage())]),
|
||||
'errors' => collect([__('app.alert.error.' . $e->getMessage())]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,14 +88,21 @@ class TagController extends Controller
|
||||
*
|
||||
* @param $slug
|
||||
*/
|
||||
public function show($slug): View
|
||||
public function show($slug, Request $request): View
|
||||
{
|
||||
$item = Item::whereUrl($slug)->first();
|
||||
//print_r($item);
|
||||
$data['apps'] = $item->children()->pinned()->orderBy('order', 'asc')->get();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
\Illuminate\Routing\Middleware\ThrottleRequests::class.':60,1',
|
||||
'bindings',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's middleware aliases.
|
||||
*
|
||||
* Aliases may be used to conveniently assign middleware to routes and groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewareAliases = [
|
||||
'allowed' => \App\Http\Middleware\CheckAllowed::class,
|
||||
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
];
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
|
||||
|
||||
class ValidateSignature extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the query string parameters that should be ignored.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
// 'fbclid',
|
||||
// 'utm_campaign',
|
||||
// 'utm_content',
|
||||
// 'utm_medium',
|
||||
// 'utm_source',
|
||||
// 'utm_term',
|
||||
];
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
'order',
|
||||
'appload',
|
||||
'test_config',
|
||||
//'get_stats'
|
||||
];
|
||||
}
|
||||
@@ -33,6 +33,7 @@ use Symfony\Component\ClassLoader\ClassMapGenerator;
|
||||
* @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
|
||||
@@ -51,6 +52,7 @@ use Symfony\Component\ClassLoader\ClassMapGenerator;
|
||||
* @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)
|
||||
@@ -105,6 +107,7 @@ class Item extends Model
|
||||
'user_id',
|
||||
'tag_id',
|
||||
'appid',
|
||||
'role',
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -10,10 +10,13 @@ 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
|
||||
{
|
||||
@@ -88,6 +91,11 @@ class AppServiceProvider extends ServiceProvider
|
||||
$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'));
|
||||
@@ -122,6 +130,10 @@ class AppServiceProvider extends ServiceProvider
|
||||
$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();
|
||||
});
|
||||
@@ -139,6 +151,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
'App\Model' => 'App\Policies\ModelPolicy',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class BroadcastServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Broadcast::routes();
|
||||
|
||||
require base_path('routes/channels.php');
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event listener mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [
|
||||
'App\Events\Event' => [
|
||||
'App\Listeners\EventListener',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if events and listeners should be automatically discovered.
|
||||
*/
|
||||
public function shouldDiscoverEvents(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
namespace App;
|
||||
|
||||
use Cache;
|
||||
use Form;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Request as Input;
|
||||
use Yaml;
|
||||
|
||||
abstract class Search
|
||||
@@ -106,31 +105,38 @@ abstract class Search
|
||||
if ((bool) $homepage_search !== true) {
|
||||
return $output;
|
||||
}
|
||||
$user_search_provider = $user_search_provider ?? 'none';
|
||||
$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">';
|
||||
$output .= '<select name="provider">';
|
||||
foreach (self::providers() as $key => $searchprovider) {
|
||||
$selected = ((string) $key === (string) $user_search_provider) ? ' selected="selected"' : '';
|
||||
$output .= '<option value="'.$key.'"'.$selected.'>'.$searchprovider['name'].'</option>';
|
||||
|
||||
// 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 .= '</select>';
|
||||
$output .= Form::text(
|
||||
'q',
|
||||
null,
|
||||
[
|
||||
'class' => 'homesearch',
|
||||
'autofocus' => 'autofocus',
|
||||
'placeholder' => __('app.settings.search').'...'
|
||||
]
|
||||
);
|
||||
|
||||
$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>';
|
||||
|
||||
67
app/Services/CustomFormBuilder.php
Normal file
67
app/Services/CustomFormBuilder.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?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
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App;
|
||||
|
||||
use Form;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@@ -10,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Session\SessionManager;
|
||||
use Illuminate\Session\Store;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
/**
|
||||
* App\Setting
|
||||
@@ -72,9 +71,23 @@ class Setting extends Model
|
||||
|
||||
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' => $request->file('value'),
|
||||
'image' => $image,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -137,63 +150,64 @@ class Setting extends Model
|
||||
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).
|
||||
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 .= Form::file('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').
|
||||
$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) {
|
||||
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.' />
|
||||
<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) {
|
||||
$options->$key = __($opt);
|
||||
$value .= '<option value="' . $key . '" ' . (($this->value == $key) ? 'selected' : '') . '>' . __($opt) . '</option>';
|
||||
}
|
||||
$value = Form::select('value', $options, null, ['class' => 'form-control']);
|
||||
$value .= '</select>';
|
||||
break;
|
||||
case 'textarea':
|
||||
$value = Form::textarea('value', null, ['class' => 'form-control', 'cols' => '44', 'rows' => '15']);
|
||||
$value = '<textarea name="value" class="form-control" cols="44" rows="15">' . htmlspecialchars($this->value, ENT_QUOTES, 'UTF-8') . '</textarea>';
|
||||
break;
|
||||
default:
|
||||
$value = Form::text('value', null, ['class' => 'form-control']);
|
||||
$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');
|
||||
|
||||
50
artisan
50
artisan
@@ -1,53 +1,15 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run The Artisan Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When we run the console application, the current CLI command will be
|
||||
| executed in this console and the response sent back to a terminal
|
||||
| or another output device for the developers. Here goes nothing!
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||
|
||||
$status = $kernel->handle(
|
||||
$input = new Symfony\Component\Console\Input\ArgvInput,
|
||||
new Symfony\Component\Console\Output\ConsoleOutput
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Shutdown The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once Artisan has finished running, we will fire off the shutdown events
|
||||
| so that any final work may be done by the application before we shut
|
||||
| down the process. This is the last thing to happen to the request.
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel->terminate($input, $status);
|
||||
// Bootstrap Laravel and handle the command...
|
||||
$status = (require_once __DIR__.'/bootstrap/app.php')
|
||||
->handleCommand(new ArgvInput);
|
||||
|
||||
exit($status);
|
||||
|
||||
@@ -1,55 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Create The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The first thing we will do is create a new Laravel application instance
|
||||
| which serves as the "glue" for all the components of Laravel, and is
|
||||
| the IoC container for the system binding all of the various parts.
|
||||
|
|
||||
*/
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
$app = new Illuminate\Foundation\Application(
|
||||
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
|
||||
);
|
||||
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'));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bind Important Interfaces
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, we need to bind some important interfaces into the container so
|
||||
| we will be able to resolve them when needed. The kernels serve the
|
||||
| incoming requests to this application from both the web and CLI.
|
||||
|
|
||||
*/
|
||||
$middleware->validateCsrfTokens(except: [
|
||||
//
|
||||
'order',
|
||||
'appload',
|
||||
'test_config',
|
||||
//'get_stats'
|
||||
]);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Http\Kernel::class,
|
||||
App\Http\Kernel::class
|
||||
);
|
||||
$middleware->append(\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Console\Kernel::class,
|
||||
App\Console\Kernel::class
|
||||
);
|
||||
$middleware->throttleApi('60,1');
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||
App\Exceptions\Handler::class
|
||||
);
|
||||
$middleware->replace(\Illuminate\Http\Middleware\TrustProxies::class, \App\Http\Middleware\TrustProxies::class);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Return The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This script returns the application instance. The instance is given to
|
||||
| the calling script so we can separate the building of the instances
|
||||
| from the actual running of the application and sending responses.
|
||||
|
|
||||
*/
|
||||
|
||||
return $app;
|
||||
$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();
|
||||
|
||||
7
bootstrap/providers.php
Normal file
7
bootstrap/providers.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\FormMacroServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
];
|
||||
@@ -8,28 +8,29 @@
|
||||
"license": "MIT",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"graham-campbell/github": "^12.0",
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"laravel/framework": "^10.44",
|
||||
"laravel/tinker": "^2.8",
|
||||
"laravel/ui": "^4.2",
|
||||
"laravelcollective/html": "^6.4",
|
||||
"nunomaduro/collision": "^6.3",
|
||||
"symfony/yaml": "^6.2",
|
||||
"ext-json": "*",
|
||||
"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",
|
||||
"spatie/laravel-ignition": "^2.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": "^2.13",
|
||||
"barryvdh/laravel-ide-helper": "^3.0",
|
||||
"filp/whoops": "^2.8",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"phpunit/phpunit": "^9.5.10",
|
||||
"mockery/mockery": "^1.6",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"squizlabs/php_codesniffer": "3.*",
|
||||
"symfony/thanks": "^1.2",
|
||||
"fakerphp/faker": "^1.9.1"
|
||||
"fakerphp/faker": "^1.23"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
|
||||
4145
composer.lock
generated
4145
composer.lock
generated
File diff suppressed because it is too large
Load Diff
0
config/.gitkeep
Normal file
0
config/.gitkeep
Normal file
189
config/app.php
189
config/app.php
@@ -5,191 +5,28 @@ use Illuminate\Support\Facades\Facade;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application. This value is used when the
|
||||
| framework needs to place the application's name in a notification or
|
||||
| any other location as required by the application or its packages.
|
||||
|
|
||||
*/
|
||||
'version' => '2.7.7',
|
||||
|
||||
'name' => env('APP_NAME', 'Heimdall'),
|
||||
'version' => '2.6.1',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services your application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| your application so that it is used when running Artisan tasks.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
'asset_url' => env('ASSET_URL', null),
|
||||
'appsource' => env('APP_SOURCE', 'https://appslist.heimdall.site/'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. We have gone
|
||||
| ahead and set this to a sensible default for you out of the box.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by the translation service provider. You are free to set this value
|
||||
| to any of the locales which will be supported by the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => 'en',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Fallback Locale
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The fallback locale determines the locale to use when the current one
|
||||
| is not available. You may change the value to correspond to any of
|
||||
| the language folders that are provided through your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'fallback_locale' => 'en',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Faker Locale
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This locale will be used by the Faker PHP library when generating fake
|
||||
| data for your database seeds. For example, this will be used to get
|
||||
| localized telephone numbers, street address information and more.
|
||||
|
|
||||
*/
|
||||
|
||||
'faker_locale' => 'en_US',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is used by the Illuminate encrypter service and should be set
|
||||
| to a random, 32 character string, otherwise these encrypted strings
|
||||
| will not be safe. Please do this before deploying an application!
|
||||
|
|
||||
*/
|
||||
|
||||
'key' => env('APP_KEY', 'base64:I206O8ibx+GQyRE7BeOxDobn04Mfmyyc5Ptzns/C0mY='),
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options determine the driver used to determine and
|
||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||
| allow maintenance mode to be controlled across multiple machines.
|
||||
|
|
||||
| Supported drivers: "file", "cache"
|
||||
|
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => 'file',
|
||||
// 'store' => 'redis',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Autoloaded Service Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The service providers listed here will be automatically loaded on the
|
||||
| request to your application. Feel free to add your own services to
|
||||
| this array to grant expanded functionality to your applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => ServiceProvider::defaultProviders()->merge([
|
||||
/*
|
||||
* Package Service Providers...
|
||||
*/
|
||||
Collective\Html\HtmlServiceProvider::class,
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
// App\Providers\BroadcastServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
])->toArray(),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Class Aliases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This array of class aliases will be registered when this application
|
||||
| is started. However, feel free to register as many as you wish as
|
||||
| the aliases are "lazy" loaded so they don't hinder performance.
|
||||
|
|
||||
*/
|
||||
'allow_internal_requests' => env('ALLOW_INTERNAL_REQUESTS', false),
|
||||
|
||||
'aliases' => Facade::defaultAliases()->merge([
|
||||
'EnhancedApps' => App\EnhancedApps::class,
|
||||
'Form' => Collective\Html\FormFacade::class,
|
||||
'Html' => Collective\Html\HtmlFacade::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', ','),
|
||||
|
||||
];
|
||||
|
||||
104
config/auth.php
104
config/auth.php
@@ -2,120 +2,18 @@
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default authentication "guard" and password
|
||||
| reset options for your application. You may change these defaults
|
||||
| as required, but they're a perfect start for most applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => 'web',
|
||||
'passwords' => 'users',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, you may define every authentication guard for your application.
|
||||
| Of course, a great default configuration has been defined for you
|
||||
| here which uses session storage and the Eloquent user provider.
|
||||
|
|
||||
| All authentication drivers have a user provider. This defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| mechanisms used by this application to persist your user's data.
|
||||
|
|
||||
| Supported: "session"
|
||||
|
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'driver' => 'token',
|
||||
'provider' => 'users',
|
||||
'hash' => false,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All authentication drivers have a user provider. This defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| mechanisms used by this application to persist your user's data.
|
||||
|
|
||||
| If you have multiple user tables or models you may configure multiple
|
||||
| sources which represent each model / table. These sources may then
|
||||
| be assigned to any extra authentication guards you have defined.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => App\User::class,
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resetting Passwords
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may specify multiple password reset configurations if you have more
|
||||
| than one user table or model in the application and you want to have
|
||||
| separate password reset settings based on the specific user types.
|
||||
|
|
||||
| The expiry time is the number of minutes that each reset token will be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
| The throttle setting is the number of seconds a user must wait before
|
||||
| generating more password reset tokens. This prevents the user from
|
||||
| quickly generating a very large amount of password reset tokens.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'table' => 'password_reset_tokens',
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
'model' => App\User::class, // Update this to the correct namespace
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Confirmation Timeout
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the amount of seconds before a password confirmation
|
||||
| times out and the user is prompted to re-enter their password via the
|
||||
| confirmation screen. By default, the timeout lasts for three hours.
|
||||
|
|
||||
*/
|
||||
|
||||
'password_timeout' => 10800,
|
||||
|
||||
];
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Broadcaster
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default broadcaster that will be used by the
|
||||
| framework when an event needs to be broadcast. You may set this to
|
||||
| any of the connections defined in the "connections" array below.
|
||||
|
|
||||
| Supported: "pusher", "ably", "redis", "log", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('BROADCAST_DRIVER', 'null'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Broadcast Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the broadcast connections that will be used
|
||||
| to broadcast events to other systems or over websockets. Samples of
|
||||
| each available type of connection are provided inside this array.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'pusher' => [
|
||||
'driver' => 'pusher',
|
||||
'key' => env('PUSHER_APP_KEY'),
|
||||
'secret' => env('PUSHER_APP_SECRET'),
|
||||
'app_id' => env('PUSHER_APP_ID'),
|
||||
'options' => [
|
||||
'cluster' => env('PUSHER_APP_CLUSTER'),
|
||||
'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
|
||||
'port' => env('PUSHER_PORT', 443),
|
||||
'scheme' => env('PUSHER_SCHEME', 'https'),
|
||||
'encrypted' => true,
|
||||
'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
|
||||
],
|
||||
'client_options' => [
|
||||
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
|
||||
],
|
||||
],
|
||||
|
||||
'ably' => [
|
||||
'driver' => 'ably',
|
||||
'key' => env('ABLY_KEY'),
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'driver' => 'log',
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'null',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
111
config/cache.php
111
config/cache.php
@@ -1,111 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache connection that gets used while
|
||||
| using this caching library. This connection is used when another is
|
||||
| not explicitly specified when executing a given caching function.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_DRIVER', 'file'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
| Supported drivers: "apc", "array", "database", "file",
|
||||
| "memcached", "redis", "dynamodb", "octane", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'apc' => [
|
||||
'driver' => 'apc',
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
'serialize' => false,
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'table' => 'cache',
|
||||
'connection' => null,
|
||||
'lock_connection' => null,
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache/data'),
|
||||
'lock_path' => storage_path('framework/cache/data'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||
'sasl' => [
|
||||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
'port' => env('MEMCACHED_PORT', 11211),
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
'lock_connection' => 'default',
|
||||
],
|
||||
|
||||
'dynamodb' => [
|
||||
'driver' => 'dynamodb',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||
],
|
||||
|
||||
'octane' => [
|
||||
'driver' => 'octane',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the APC, database, memcached, Redis, or DynamoDB cache
|
||||
| stores there might be other applications using the same cache. For
|
||||
| that reason, you may prefix every cache key to avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
|
||||
|
||||
];
|
||||
@@ -1,151 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for all database work. Of course
|
||||
| you may use many connections at once using the Database library.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'mysql'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here are each of the database connections setup for your application.
|
||||
| Of course, examples of configuring each database platform that is
|
||||
| supported by Laravel is shown below to make development simple.
|
||||
|
|
||||
|
|
||||
| All database work in Laravel is done through the PHP PDO facilities
|
||||
| so make sure you have the driver for your particular database of
|
||||
| choice installed on your machine before you begin development.
|
||||
|
|
||||
*/
|
||||
'default' => env('DB_CONNECTION', 'sqlite'), // Make sure the default connection is set
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'url' => env('DATABASE_URL'),
|
||||
'database' => database_path(env('DB_DATABASE', 'database.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),
|
||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), // Enable foreign key constraints
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DATABASE_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DATABASE_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'search_path' => 'public',
|
||||
'sslmode' => 'prefer',
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
'url' => env('DATABASE_URL'),
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '1433'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
|
||||
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run in the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => 'migrations',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer body of commands than a typical key-value system
|
||||
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
],
|
||||
|
||||
'migrations' => [
|
||||
'table' => 'migrations',
|
||||
'update_date_on_publish' => false, // disable to preserve original behavior for existing applications
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -2,77 +2,14 @@
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default filesystem disk that should be used
|
||||
| by the framework. The "local" disk, as well as a variety of cloud
|
||||
| based disks are available to your application. Just store away!
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('FILESYSTEM_DISK', 'local'),
|
||||
|
||||
'cloud' => env('FILESYSTEM_CLOUD', 's3'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filesystem Disks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure as many filesystem "disks" as you wish, and you
|
||||
| may even configure multiple disks of the same driver. Defaults have
|
||||
| been set up for each driver as an example of the required values.
|
||||
|
|
||||
| Supported Drivers: "local", "ftp", "sftp", "s3"
|
||||
|
|
||||
*/
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app'),
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => env('APP_URL').'/storage',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION'),
|
||||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Symbolic Links
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the symbolic links that will be created when the
|
||||
| `storage:link` Artisan command is executed. The array keys should be
|
||||
| the locations of the links and the values should be their targets.
|
||||
|
|
||||
*/
|
||||
|
||||
'links' => [
|
||||
public_path('storage') => storage_path('app/public'),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Hash Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default hash driver that will be used to hash
|
||||
| passwords for your application. By default, the bcrypt algorithm is
|
||||
| used; however, you remain free to modify this option if you wish.
|
||||
|
|
||||
| Supported: "bcrypt", "argon", "argon2id"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => 'bcrypt',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bcrypt Options
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the configuration options that should be used when
|
||||
| passwords are hashed using the Bcrypt algorithm. This will allow you
|
||||
| to control the amount of time it takes to hash the given password.
|
||||
|
|
||||
*/
|
||||
|
||||
'bcrypt' => [
|
||||
'rounds' => env('BCRYPT_ROUNDS', 12),
|
||||
'verify' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Argon Options
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the configuration options that should be used when
|
||||
| passwords are hashed using the Argon algorithm. These will allow you
|
||||
| to control the amount of time it takes to hash the given password.
|
||||
|
|
||||
*/
|
||||
|
||||
'argon' => [
|
||||
'memory' => 65536,
|
||||
'threads' => 1,
|
||||
'time' => 4,
|
||||
'verify' => true,
|
||||
],
|
||||
|
||||
];
|
||||
112
config/mail.php
112
config/mail.php
@@ -2,127 +2,15 @@
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Mailer
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default mailer that is used to send any email
|
||||
| messages sent by your application. Alternative mailers may be setup
|
||||
| and used as needed; however, this mailer will be used by default.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('MAIL_MAILER', 'smtp'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mailer Configurations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure all of the mailers used by your application plus
|
||||
| their respective settings. Several examples have been configured for
|
||||
| you and you are free to add your own as your application requires.
|
||||
|
|
||||
| Laravel supports a variety of mail "transport" drivers to be used while
|
||||
| sending an e-mail. You will specify which one you are using for your
|
||||
| mailers below. You are free to add additional mailers as required.
|
||||
|
|
||||
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
|
||||
| "postmark", "log", "array", "failover", "roundrobin"
|
||||
|
|
||||
*/
|
||||
|
||||
'mailers' => [
|
||||
'smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'url' => env('MAIL_URL'),
|
||||
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
|
||||
'port' => env('MAIL_PORT', 587),
|
||||
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'timeout' => null,
|
||||
'local_domain' => env('MAIL_EHLO_DOMAIN'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'transport' => 'ses',
|
||||
],
|
||||
|
||||
'postmark' => [
|
||||
'transport' => 'postmark',
|
||||
// 'message_stream_id' => null,
|
||||
// 'client' => [
|
||||
// 'timeout' => 5,
|
||||
// ],
|
||||
],
|
||||
|
||||
'mailgun' => [
|
||||
'transport' => 'mailgun',
|
||||
// 'client' => [
|
||||
// 'timeout' => 5,
|
||||
// ],
|
||||
],
|
||||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'transport' => 'log',
|
||||
'channel' => env('MAIL_LOG_CHANNEL'),
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'transport' => 'array',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'transport' => 'failover',
|
||||
'mailers' => [
|
||||
'smtp',
|
||||
'log',
|
||||
],
|
||||
],
|
||||
|
||||
'roundrobin' => [
|
||||
'transport' => 'roundrobin',
|
||||
'mailers' => [
|
||||
'ses',
|
||||
'postmark',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global "From" Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may wish for all e-mails sent by your application to be sent from
|
||||
| the same address. Here, you may specify a name and address that is
|
||||
| used globally for all e-mails that are sent by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Markdown Mail Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you are using Markdown based email rendering, you may configure your
|
||||
| theme and component paths here, allowing you to customize the design
|
||||
| of the emails. Or, you may simply stick with the Laravel defaults!
|
||||
|
|
||||
*/
|
||||
|
||||
'markdown' => [
|
||||
'theme' => 'default',
|
||||
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Queue Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Laravel's queue API supports an assortment of back-ends via a single
|
||||
| API, giving you convenient access to each back-end using the same
|
||||
| syntax for every one. Here you may define a default connection.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'sync'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the connection information for each server that
|
||||
| is used by your application. A default configuration has been added
|
||||
| for each back-end shipped with Laravel. You are free to add more.
|
||||
|
|
||||
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sync' => [
|
||||
'driver' => 'sync',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'table' => 'jobs',
|
||||
'queue' => 'default',
|
||||
'retry_after' => 90,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'beanstalkd' => [
|
||||
'driver' => 'beanstalkd',
|
||||
'host' => 'localhost',
|
||||
'queue' => 'default',
|
||||
'retry_after' => 90,
|
||||
'block_for' => 0,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'sqs' => [
|
||||
'driver' => 'sqs',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
|
||||
'queue' => env('SQS_QUEUE', 'default'),
|
||||
'suffix' => env('SQS_SUFFIX'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
'queue' => env('REDIS_QUEUE', 'default'),
|
||||
'retry_after' => 90,
|
||||
'block_for' => null,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Failed Queue Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options configure the behavior of failed queue job logging so you
|
||||
| can control which database and table are used to store the jobs that
|
||||
| have failed. You may change them to any database / table you wish.
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => [
|
||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
||||
'database' => env('DB_CONNECTION', 'mysql'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -2,18 +2,6 @@
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Third Party Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is for storing the credentials for third party services such
|
||||
| as Mailgun, Postmark, AWS and more. This file provides the de facto
|
||||
| location for this type of information, allowing packages to have
|
||||
| a conventional file to locate the various service credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'mailgun' => [
|
||||
'domain' => env('MAILGUN_DOMAIN'),
|
||||
'secret' => env('MAILGUN_SECRET'),
|
||||
@@ -21,14 +9,4 @@ return [
|
||||
'scheme' => 'https',
|
||||
],
|
||||
|
||||
'postmark' => [
|
||||
'token' => env('POSTMARK_TOKEN'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Session Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default session "driver" that will be used on
|
||||
| requests. By default, we will use the lightweight native driver but
|
||||
| you may specify any of the other wonderful drivers provided here.
|
||||
|
|
||||
| Supported: "file", "cookie", "database", "apc",
|
||||
| "memcached", "redis", "dynamodb", "array"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'file'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Lifetime
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the number of minutes that you wish the session
|
||||
| to be allowed to remain idle before it expires. If you want them
|
||||
| to immediately expire on the browser closing, set that option.
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => env('SESSION_LIFETIME', 120),
|
||||
|
||||
'expire_on_close' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to easily specify that all of your session data
|
||||
| should be encrypted before it is stored. All encryption will be run
|
||||
| automatically by Laravel and you can use the Session like normal.
|
||||
|
|
||||
*/
|
||||
|
||||
'encrypt' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session File Location
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the native session driver, we need a location where session
|
||||
| files may be stored. A default has been set for you but a different
|
||||
| location may be specified. This is only needed for file sessions.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => storage_path('framework/sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" or "redis" session drivers, you may specify a
|
||||
| connection that should be used to manage these sessions. This should
|
||||
| correspond to a connection in your database configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'connection' => env('SESSION_CONNECTION'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" session driver, you may specify the table we
|
||||
| should use to manage the sessions. Of course, a sensible default is
|
||||
| provided for you; however, you are free to change this as needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => 'sessions',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While using one of the framework's cache driven session backends you may
|
||||
| list a cache store that should be used for these sessions. This value
|
||||
| must match with one of the application's configured cache "stores".
|
||||
|
|
||||
| Affects: "apc", "dynamodb", "memcached", "redis"
|
||||
|
|
||||
*/
|
||||
|
||||
'store' => env('SESSION_STORE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Sweeping Lottery
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some session drivers must manually sweep their storage location to get
|
||||
| rid of old sessions from storage. Here are the chances that it will
|
||||
| happen on a given request. By default, the odds are 2 out of 100.
|
||||
|
|
||||
*/
|
||||
|
||||
'lottery' => [2, 100],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the name of the cookie used to identify a session
|
||||
| instance by ID. The name specified here will get used every time a
|
||||
| new session cookie is created by the framework for every driver.
|
||||
|
|
||||
*/
|
||||
|
||||
'cookie' => env(
|
||||
'SESSION_COOKIE',
|
||||
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
|
||||
),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The session cookie path determines the path for which the cookie will
|
||||
| be regarded as available. Typically, this will be the root path of
|
||||
| your application but you are free to change this when necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => '/',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the domain of the cookie used to identify a session
|
||||
| in your application. This will determine which domains the cookie is
|
||||
| available to in your application. A sensible default has been set.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('SESSION_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTPS Only Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you when it can't be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
'secure' => env('SESSION_SECURE_COOKIE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTP Access Only
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will prevent JavaScript from accessing the
|
||||
| value of the cookie and the cookie will only be accessible through
|
||||
| the HTTP protocol. You are free to modify this option if needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'http_only' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Same-Site Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines how your cookies behave when cross-site requests
|
||||
| take place, and can be used to mitigate CSRF attacks. By default, we
|
||||
| will set this value to "lax" since this is a secure default value.
|
||||
|
|
||||
| Supported: "lax", "strict", "none", null
|
||||
|
|
||||
*/
|
||||
|
||||
'same_site' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Partitioned Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will tie the cookie to the top-level site for
|
||||
| a cross-site context. Partitioned cookies are accepted by the browser
|
||||
| when flagged "secure" and the Same-Site attribute is set to "none".
|
||||
|
|
||||
*/
|
||||
|
||||
'partitioned' => false,
|
||||
|
||||
];
|
||||
0
database/.gitignore
vendored
Normal file → Executable file
0
database/.gitignore
vendored
Normal file → Executable file
0
database/factories/ItemFactory.php
Normal file → Executable file
0
database/factories/ItemFactory.php
Normal file → Executable file
0
database/factories/ItemTagFactory.php
Normal file → Executable file
0
database/factories/ItemTagFactory.php
Normal file → Executable file
0
database/factories/UserFactory.php
Normal file → Executable file
0
database/factories/UserFactory.php
Normal file → Executable file
0
database/migrations/2018_01_27_155922_create_items_table.php
Normal file → Executable file
0
database/migrations/2018_01_27_155922_create_items_table.php
Normal file → Executable file
0
database/migrations/2018_02_04_185524_create_settings_table.php
Normal file → Executable file
0
database/migrations/2018_02_04_185524_create_settings_table.php
Normal file → Executable file
0
database/migrations/2018_02_04_185802_create_setting_groups_table.php
Normal file → Executable file
0
database/migrations/2018_02_04_185802_create_setting_groups_table.php
Normal file → Executable file
0
database/migrations/2018_02_16_175830_add_columns_to_items_for_groups.php
Normal file → Executable file
0
database/migrations/2018_02_16_175830_add_columns_to_items_for_groups.php
Normal file → Executable file
0
database/migrations/2018_02_16_193703_item_tag.php
Normal file → Executable file
0
database/migrations/2018_02_16_193703_item_tag.php
Normal file → Executable file
0
database/migrations/2018_10_12_122907_create_users_table.php
Normal file → Executable file
0
database/migrations/2018_10_12_122907_create_users_table.php
Normal file → Executable file
0
database/migrations/2018_10_12_123036_create_password_resets_table.php
Normal file → Executable file
0
database/migrations/2018_10_12_123036_create_password_resets_table.php
Normal file → Executable file
0
database/migrations/2018_10_12_131222_add_user_id_to_items_table.php
Normal file → Executable file
0
database/migrations/2018_10_12_131222_add_user_id_to_items_table.php
Normal file → Executable file
0
database/migrations/2018_10_12_140451_create_setting_user_pivot_table.php
Normal file → Executable file
0
database/migrations/2018_10_12_140451_create_setting_user_pivot_table.php
Normal file → Executable file
0
database/migrations/2018_10_18_110905_create_applications_table.php
Normal file → Executable file
0
database/migrations/2018_10_18_110905_create_applications_table.php
Normal file → Executable file
0
database/migrations/2018_10_23_132008_add_class_to_items_table.php
Normal file → Executable file
0
database/migrations/2018_10_23_132008_add_class_to_items_table.php
Normal file → Executable file
0
database/migrations/2018_10_31_191604_create_jobs_table.php
Normal file → Executable file
0
database/migrations/2018_10_31_191604_create_jobs_table.php
Normal file → Executable file
0
database/migrations/2018_11_06_112434_create_failed_jobs_table.php
Normal file → Executable file
0
database/migrations/2018_11_06_112434_create_failed_jobs_table.php
Normal file → Executable file
0
database/migrations/2022_03_15_140911_add_appid_to_items.php
Normal file → Executable file
0
database/migrations/2022_03_15_140911_add_appid_to_items.php
Normal file → Executable file
0
database/migrations/2022_03_16_093044_add_class_to_application.php
Normal file → Executable file
0
database/migrations/2022_03_16_093044_add_class_to_application.php
Normal file → Executable file
0
database/migrations/2022_03_16_181343_add_app_description_to_items.php
Normal file → Executable file
0
database/migrations/2022_03_16_181343_add_app_description_to_items.php
Normal file → Executable file
32
database/migrations/2023_01_27_121000_add_role_to_item.php
Executable file
32
database/migrations/2023_01_27_121000_add_role_to_item.php
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('items', function (Blueprint $table) {
|
||||
$table->text('role')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('items', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
0
database/migrations/2024_02_16_000000_rename_password_resets_table.php
Normal file → Executable file
0
database/migrations/2024_02_16_000000_rename_password_resets_table.php
Normal file → Executable file
35
database/migrations/2025_07_17_134226_create_cache_table.php
Normal file
35
database/migrations/2025_07_17_134226_create_cache_table.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
0
database/seeders/DatabaseSeeder.php
Normal file → Executable file
0
database/seeders/DatabaseSeeder.php
Normal file → Executable file
1
database/seeders/SettingsSeeder.php
Normal file → Executable file
1
database/seeders/SettingsSeeder.php
Normal file → Executable file
@@ -349,6 +349,5 @@ class SettingsSeeder extends Seeder
|
||||
$setting->label = 'app.settings.treat_tags_as';
|
||||
$setting->save();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
1
database/seeders/UsersSeeder.php
Normal file → Executable file
1
database/seeders/UsersSeeder.php
Normal file → Executable file
@@ -18,6 +18,7 @@ class UsersSeeder extends Seeder
|
||||
$user->username = 'admin';
|
||||
$user->email = 'admin@test.com';
|
||||
$user->password = null;
|
||||
$user->public_front = 0; // Default value for public_front
|
||||
$user->save();
|
||||
|
||||
$user_id = $user->id;
|
||||
|
||||
26
docker/docker-compose.yml
Normal file
26
docker/docker-compose.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
version: "3"
|
||||
services:
|
||||
nginx:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: nginx/Dockerfile
|
||||
ports:
|
||||
- "8080:80"
|
||||
networks:
|
||||
- internal
|
||||
volumes:
|
||||
- ../:/var/www/html
|
||||
php:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: php/Dockerfile
|
||||
networks:
|
||||
- internal
|
||||
environment:
|
||||
XDEBUG_MODE: debug
|
||||
XDEBUG_CONFIG: client_host=host.docker.internal client_port=9003
|
||||
volumes:
|
||||
- ../:/var/www/html
|
||||
networks:
|
||||
internal:
|
||||
driver: bridge
|
||||
2
docker/nginx/Dockerfile
Normal file
2
docker/nginx/Dockerfile
Normal file
@@ -0,0 +1,2 @@
|
||||
FROM nginx:alpine
|
||||
COPY ./default.conf /etc/nginx/conf.d
|
||||
19
docker/nginx/default.conf
Normal file
19
docker/nginx/default.conf
Normal file
@@ -0,0 +1,19 @@
|
||||
server {
|
||||
listen 0.0.0.0:80;
|
||||
|
||||
root /var/www/html;
|
||||
|
||||
location / {
|
||||
index index.php index.html;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
|
||||
}
|
||||
|
||||
error_log /var/log/nginx/error.log;
|
||||
access_log /var/log/nginx/access.log;
|
||||
}
|
||||
4
docker/php/Dockerfile
Normal file
4
docker/php/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM php:8.4-fpm
|
||||
|
||||
RUN pecl install xdebug \
|
||||
&& docker-php-ext-enable xdebug
|
||||
@@ -20,7 +20,7 @@ return array (
|
||||
'settings.language' => 'Sprache',
|
||||
'settings.reset' => 'Zurücksetzen auf Standard',
|
||||
'settings.remove' => 'Entfernen',
|
||||
'settings.search' => 'suche',
|
||||
'settings.search' => 'Suche',
|
||||
'settings.no_items' => 'Keine Elemente gefunden',
|
||||
'settings.label' => 'Bezeichnung',
|
||||
'settings.value' => 'Wert',
|
||||
@@ -33,7 +33,7 @@ return array (
|
||||
'options.ddg' => 'DuckDuckGo',
|
||||
'options.bing' => 'Bing',
|
||||
'options.qwant' => 'Qwant',
|
||||
'options.startpage' => 'StartSeite',
|
||||
'options.startpage' => 'Startseite',
|
||||
'options.yes' => 'Ja',
|
||||
'options.no' => 'Nein',
|
||||
'options.nzbhydra' => 'NZBHydra',
|
||||
@@ -46,7 +46,7 @@ return array (
|
||||
'dash.pin_item' => 'Element auf dem Dashboard anheften',
|
||||
'dash.no_apps' => 'Derzeit gibt es keine angeheftete Anwendungen. :link1 oder :link2',
|
||||
'dash.link1' => 'Anwendung neu hinzufügen',
|
||||
'dash.link2' => 'anheften',
|
||||
'dash.link2' => 'Anheften',
|
||||
'dash.pinned_items' => 'Angeheftete Elemente',
|
||||
'apps.app_list' => 'Anwendungsliste',
|
||||
'apps.view_trash' => 'Ansicht Papierkorb',
|
||||
@@ -66,7 +66,7 @@ return array (
|
||||
'apps.add_tag' => 'Tag hinzufügen',
|
||||
'apps.tag_name' => 'Tag Name',
|
||||
'apps.tags' => 'Tags',
|
||||
'apps.override' => 'Fals anders zur Haupt-URL',
|
||||
'apps.override' => 'Falls anders zur Haupt-URL',
|
||||
'apps.preview' => 'Vorschau',
|
||||
'apps.apptype' => 'Anwendungstyp',
|
||||
'apps.website' => 'Webseite',
|
||||
@@ -81,7 +81,7 @@ return array (
|
||||
'user.avatar' => 'Avatar',
|
||||
'user.email' => 'Email',
|
||||
'user.password_confirm' => 'Passwort bestätigen',
|
||||
'user.secure_front' => 'Öffentlichen Zugang erlauben - Tritt nur bei gesetztem Passwort in kraft.',
|
||||
'user.secure_front' => 'Öffentlichen Zugang erlauben - Tritt nur bei gesetztem Passwort in Kraft.',
|
||||
'user.autologin' => 'Anmelden von spezieller URL erlauben. Jeder mit diesem Link kann sich anmelden.',
|
||||
'url' => 'URL',
|
||||
'title' => 'Titel',
|
||||
@@ -105,4 +105,7 @@ return array (
|
||||
'alert.success.user_restored' => 'Nutzer erfolgreich wiederhergestellt',
|
||||
'dashboard.reorder' => 'Elemente neu anordnen und anheften',
|
||||
'dashboard.settings' => 'Einstellungen',
|
||||
'role' => 'Authentifizierungsrolle',
|
||||
'unauthorized_for_form' => 'Sie haben keinen Zugriff auf diese Seite.',
|
||||
'disabled_feature' => 'Diese Funktion ist deaktiviert.',
|
||||
);
|
||||
@@ -114,4 +114,7 @@ return array (
|
||||
'alert.success.user_restored' => 'User restored successfully',
|
||||
'dashboard.reorder' => 'Reorder and pin items',
|
||||
'dashboard.settings' => 'Settings',
|
||||
'role' => 'Authentication role',
|
||||
'unauthorized_for_form' => 'You are not authorized to view this form.',
|
||||
'disabled_feature' => 'This feature is disabled.',
|
||||
);
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
|
||||
return array (
|
||||
'failed' => 'These credentials do not match our records.',
|
||||
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||
);
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
|
||||
return array (
|
||||
'previous' => '« Previous',
|
||||
'next' => 'Next »',
|
||||
);
|
||||
@@ -4,6 +4,6 @@ return array (
|
||||
'password' => 'Passwords must be at least six characters and match the confirmation.',
|
||||
'reset' => 'Your password has been reset!',
|
||||
'sent' => 'We have e-mailed your password reset link!',
|
||||
'token' => 'This password reset token is invalid.',
|
||||
|
||||
'user' => 'We can\'t find a user with that e-mail address.',
|
||||
);
|
||||
@@ -1,191 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Validation Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines contain the default error messages used by
|
||||
| the validator class. Some of these rules have multiple versions such
|
||||
| as the size rules. Feel free to tweak each of these messages here.
|
||||
|
|
||||
*/
|
||||
|
||||
'accepted' => 'The :attribute field must be accepted.',
|
||||
'accepted_if' => 'The :attribute field must be accepted when :other is :value.',
|
||||
'active_url' => 'The :attribute field must be a valid URL.',
|
||||
'after' => 'The :attribute field must be a date after :date.',
|
||||
'after_or_equal' => 'The :attribute field must be a date after or equal to :date.',
|
||||
'alpha' => 'The :attribute field must only contain letters.',
|
||||
'alpha_dash' => 'The :attribute field must only contain letters, numbers, dashes, and underscores.',
|
||||
'alpha_num' => 'The :attribute field must only contain letters and numbers.',
|
||||
'array' => 'The :attribute field must be an array.',
|
||||
'ascii' => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.',
|
||||
'before' => 'The :attribute field must be a date before :date.',
|
||||
'before_or_equal' => 'The :attribute field must be a date before or equal to :date.',
|
||||
'between' => [
|
||||
'array' => 'The :attribute field must have between :min and :max items.',
|
||||
'file' => 'The :attribute field must be between :min and :max kilobytes.',
|
||||
'numeric' => 'The :attribute field must be between :min and :max.',
|
||||
'string' => 'The :attribute field must be between :min and :max characters.',
|
||||
],
|
||||
'boolean' => 'The :attribute field must be true or false.',
|
||||
'can' => 'The :attribute field contains an unauthorized value.',
|
||||
'confirmed' => 'The :attribute field confirmation does not match.',
|
||||
'current_password' => 'The password is incorrect.',
|
||||
'date' => 'The :attribute field must be a valid date.',
|
||||
'date_equals' => 'The :attribute field must be a date equal to :date.',
|
||||
'date_format' => 'The :attribute field must match the format :format.',
|
||||
'decimal' => 'The :attribute field must have :decimal decimal places.',
|
||||
'declined' => 'The :attribute field must be declined.',
|
||||
'declined_if' => 'The :attribute field must be declined when :other is :value.',
|
||||
'different' => 'The :attribute field and :other must be different.',
|
||||
'digits' => 'The :attribute field must be :digits digits.',
|
||||
'digits_between' => 'The :attribute field must be between :min and :max digits.',
|
||||
'dimensions' => 'The :attribute field has invalid image dimensions.',
|
||||
'distinct' => 'The :attribute field has a duplicate value.',
|
||||
'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.',
|
||||
'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.',
|
||||
'email' => 'The :attribute field must be a valid email address.',
|
||||
'ends_with' => 'The :attribute field must end with one of the following: :values.',
|
||||
'enum' => 'The selected :attribute is invalid.',
|
||||
'exists' => 'The selected :attribute is invalid.',
|
||||
'extensions' => 'The :attribute field must have one of the following extensions: :values.',
|
||||
'file' => 'The :attribute field must be a file.',
|
||||
'filled' => 'The :attribute field must have a value.',
|
||||
'gt' => [
|
||||
'array' => 'The :attribute field must have more than :value items.',
|
||||
'file' => 'The :attribute field must be greater than :value kilobytes.',
|
||||
'numeric' => 'The :attribute field must be greater than :value.',
|
||||
'string' => 'The :attribute field must be greater than :value characters.',
|
||||
],
|
||||
'gte' => [
|
||||
'array' => 'The :attribute field must have :value items or more.',
|
||||
'file' => 'The :attribute field must be greater than or equal to :value kilobytes.',
|
||||
'numeric' => 'The :attribute field must be greater than or equal to :value.',
|
||||
'string' => 'The :attribute field must be greater than or equal to :value characters.',
|
||||
],
|
||||
'hex_color' => 'The :attribute field must be a valid hexadecimal color.',
|
||||
'image' => 'The :attribute field must be an image.',
|
||||
'in' => 'The selected :attribute is invalid.',
|
||||
'in_array' => 'The :attribute field must exist in :other.',
|
||||
'integer' => 'The :attribute field must be an integer.',
|
||||
'ip' => 'The :attribute field must be a valid IP address.',
|
||||
'ipv4' => 'The :attribute field must be a valid IPv4 address.',
|
||||
'ipv6' => 'The :attribute field must be a valid IPv6 address.',
|
||||
'json' => 'The :attribute field must be a valid JSON string.',
|
||||
'lowercase' => 'The :attribute field must be lowercase.',
|
||||
'lt' => [
|
||||
'array' => 'The :attribute field must have less than :value items.',
|
||||
'file' => 'The :attribute field must be less than :value kilobytes.',
|
||||
'numeric' => 'The :attribute field must be less than :value.',
|
||||
'string' => 'The :attribute field must be less than :value characters.',
|
||||
],
|
||||
'lte' => [
|
||||
'array' => 'The :attribute field must not have more than :value items.',
|
||||
'file' => 'The :attribute field must be less than or equal to :value kilobytes.',
|
||||
'numeric' => 'The :attribute field must be less than or equal to :value.',
|
||||
'string' => 'The :attribute field must be less than or equal to :value characters.',
|
||||
],
|
||||
'mac_address' => 'The :attribute field must be a valid MAC address.',
|
||||
'max' => [
|
||||
'array' => 'The :attribute field must not have more than :max items.',
|
||||
'file' => 'The :attribute field must not be greater than :max kilobytes.',
|
||||
'numeric' => 'The :attribute field must not be greater than :max.',
|
||||
'string' => 'The :attribute field must not be greater than :max characters.',
|
||||
],
|
||||
'max_digits' => 'The :attribute field must not have more than :max digits.',
|
||||
'mimes' => 'The :attribute field must be a file of type: :values.',
|
||||
'mimetypes' => 'The :attribute field must be a file of type: :values.',
|
||||
'min' => [
|
||||
'array' => 'The :attribute field must have at least :min items.',
|
||||
'file' => 'The :attribute field must be at least :min kilobytes.',
|
||||
'numeric' => 'The :attribute field must be at least :min.',
|
||||
'string' => 'The :attribute field must be at least :min characters.',
|
||||
],
|
||||
'min_digits' => 'The :attribute field must have at least :min digits.',
|
||||
'missing' => 'The :attribute field must be missing.',
|
||||
'missing_if' => 'The :attribute field must be missing when :other is :value.',
|
||||
'missing_unless' => 'The :attribute field must be missing unless :other is :value.',
|
||||
'missing_with' => 'The :attribute field must be missing when :values is present.',
|
||||
'missing_with_all' => 'The :attribute field must be missing when :values are present.',
|
||||
'multiple_of' => 'The :attribute field must be a multiple of :value.',
|
||||
'not_in' => 'The selected :attribute is invalid.',
|
||||
'not_regex' => 'The :attribute field format is invalid.',
|
||||
'numeric' => 'The :attribute field must be a number.',
|
||||
'password' => [
|
||||
'letters' => 'The :attribute field must contain at least one letter.',
|
||||
'mixed' => 'The :attribute field must contain at least one uppercase and one lowercase letter.',
|
||||
'numbers' => 'The :attribute field must contain at least one number.',
|
||||
'symbols' => 'The :attribute field must contain at least one symbol.',
|
||||
'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
|
||||
],
|
||||
'present' => 'The :attribute field must be present.',
|
||||
'present_if' => 'The :attribute field must be present when :other is :value.',
|
||||
'present_unless' => 'The :attribute field must be present unless :other is :value.',
|
||||
'present_with' => 'The :attribute field must be present when :values is present.',
|
||||
'present_with_all' => 'The :attribute field must be present when :values are present.',
|
||||
'prohibited' => 'The :attribute field is prohibited.',
|
||||
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
|
||||
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
|
||||
'prohibits' => 'The :attribute field prohibits :other from being present.',
|
||||
'regex' => 'The :attribute field format is invalid.',
|
||||
'required' => 'The :attribute field is required.',
|
||||
'required_array_keys' => 'The :attribute field must contain entries for: :values.',
|
||||
'required_if' => 'The :attribute field is required when :other is :value.',
|
||||
'required_if_accepted' => 'The :attribute field is required when :other is accepted.',
|
||||
'required_unless' => 'The :attribute field is required unless :other is in :values.',
|
||||
'required_with' => 'The :attribute field is required when :values is present.',
|
||||
'required_with_all' => 'The :attribute field is required when :values are present.',
|
||||
'required_without' => 'The :attribute field is required when :values is not present.',
|
||||
'required_without_all' => 'The :attribute field is required when none of :values are present.',
|
||||
'same' => 'The :attribute field must match :other.',
|
||||
'size' => [
|
||||
'array' => 'The :attribute field must contain :size items.',
|
||||
'file' => 'The :attribute field must be :size kilobytes.',
|
||||
'numeric' => 'The :attribute field must be :size.',
|
||||
'string' => 'The :attribute field must be :size characters.',
|
||||
],
|
||||
'starts_with' => 'The :attribute field must start with one of the following: :values.',
|
||||
'string' => 'The :attribute field must be a string.',
|
||||
'timezone' => 'The :attribute field must be a valid timezone.',
|
||||
'unique' => 'The :attribute has already been taken.',
|
||||
'uploaded' => 'The :attribute failed to upload.',
|
||||
'uppercase' => 'The :attribute field must be uppercase.',
|
||||
'url' => 'The :attribute field must be a valid URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Validation Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify custom validation messages for attributes using the
|
||||
| convention "attribute.rule" to name the lines. This makes it quick to
|
||||
| specify a specific custom language line for a given attribute rule.
|
||||
|
|
||||
*/
|
||||
|
||||
'custom' => [
|
||||
'attribute-name' => [
|
||||
'rule-name' => 'custom-message',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Validation Attributes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used to swap our attribute placeholder
|
||||
| with something more reader friendly such as "E-Mail Address" instead
|
||||
| of "email". This simply helps us make our message more expressive.
|
||||
|
|
||||
*/
|
||||
|
||||
'attributes' => [],
|
||||
|
||||
];
|
||||
@@ -4,23 +4,23 @@ return array (
|
||||
'settings.system' => 'Sistema',
|
||||
'settings.appearance' => 'Apariencia',
|
||||
'settings.miscellaneous' => 'Miscelánea',
|
||||
'settings.advanced' => 'Avanzada',
|
||||
'settings.advanced' => 'Avanzado',
|
||||
'settings.support' => 'Soporte',
|
||||
'settings.donate' => 'Donar',
|
||||
'settings.version' => 'Versión',
|
||||
'settings.background_image' => 'Imagen de Fondo',
|
||||
'settings.trianglify' => 'Trianglify',
|
||||
'settings.trianglify_seed' => 'Trianglify Random Seed',
|
||||
'settings.window_target' => 'Vínculo abre en',
|
||||
'settings.window_target.current' => 'Abrir en ésta pestaña',
|
||||
'settings.trianglify' => 'Patrón de triángulos',
|
||||
'settings.trianglify_seed' => 'Semilla Aleatoria del Patrón de Triángulos',
|
||||
'settings.window_target' => 'Abrir enlace en',
|
||||
'settings.window_target.current' => 'Abrir en esta pestaña',
|
||||
'settings.window_target.one' => 'Abrir en la misma pestaña',
|
||||
'settings.window_target.new' => 'Abrir en una nueva pestaña',
|
||||
'settings.homepage_search' => 'Página de Inicio de Búsqueda',
|
||||
'settings.homepage_search' => 'Búsqueda en Página de Inicio',
|
||||
'settings.search_provider' => 'Proveedor de búsqueda',
|
||||
'settings.language' => 'Idioma',
|
||||
'settings.reset' => 'Restablecer a predeterminado',
|
||||
'settings.remove' => 'Quitar',
|
||||
'settings.search' => 'búsqueda',
|
||||
'settings.search' => 'buscar',
|
||||
'settings.no_items' => 'No se encontraron elementos',
|
||||
'settings.label' => 'Etiqueta',
|
||||
'settings.value' => 'Valor',
|
||||
@@ -28,6 +28,10 @@ return array (
|
||||
'settings.view' => 'Ver',
|
||||
'settings.custom_css' => 'CSS Personalizado',
|
||||
'settings.custom_js' => 'JavaScript Personalizado',
|
||||
'settings.treat_tags_as' => 'Tratar etiquetas como:',
|
||||
'settings.folders' => 'Carpetas',
|
||||
'settings.tags' => 'Etiquetas',
|
||||
'settings.categories' => 'Categorías',
|
||||
'options.none' => '- no establecido -',
|
||||
'options.google' => 'Google',
|
||||
'options.ddg' => 'DuckDuckGo',
|
||||
@@ -42,38 +46,39 @@ return array (
|
||||
'buttons.cancel' => 'Cancelar',
|
||||
'buttons.add' => 'Añadir',
|
||||
'buttons.upload' => 'Cargar un archivo',
|
||||
'buttons.downloadapps' => 'Actualizar lista de Applicaciones',
|
||||
'dash.pin_item' => 'Pin elemento al tablero',
|
||||
'buttons.downloadapps' => 'Actualizar Lista de Aplicaciones',
|
||||
'dash.pin_item' => 'Anclar elemento al tablero',
|
||||
'dash.no_apps' => 'Actualmente no hay aplicaciones ancladas, :link1 o :link2',
|
||||
'dash.link1' => 'Agregue una aplicación aquí',
|
||||
'dash.link2' => 'Pin de un elemento en el tablero',
|
||||
'dash.link2' => 'Anclar un elemento en el tablero',
|
||||
'dash.pinned_items' => 'Elementos Anclados',
|
||||
'apps.app_list' => 'Lista de aplicaciones',
|
||||
'apps.app_list' => 'Listado de aplicaciones',
|
||||
'apps.view_trash' => 'Vista de la papelera de reciclaje',
|
||||
'apps.add_application' => 'Agregar aplicación',
|
||||
'apps.application_name' => 'Nombre de la aplicación',
|
||||
'apps.colour' => 'Color',
|
||||
'apps.icon' => 'Icono',
|
||||
'apps.pinned' => 'Fijado',
|
||||
'apps.pinned' => 'Anclado',
|
||||
'apps.title' => 'Título',
|
||||
'apps.hex' => 'Código de color hexadecimal',
|
||||
'apps.username' => 'Nombre de usuario',
|
||||
'apps.password' => 'Contraseña',
|
||||
'apps.config' => 'Config',
|
||||
'apps.config' => 'Configuración',
|
||||
'apps.apikey' => 'Clave de API',
|
||||
'apps.enable' => 'Habilitar',
|
||||
'apps.tag_list' => 'Lista de Etiquetas',
|
||||
'apps.tag_list' => 'Listado de etiquetas',
|
||||
'apps.add_tag' => 'Añadir Etiqueta',
|
||||
'apps.tag_name' => 'Nombre de Etiqueta',
|
||||
'apps.tags' => 'Etiquetas',
|
||||
'apps.override' => 'Si es differente de la URL principal',
|
||||
'apps.override' => 'Si es diferente de la URL principal',
|
||||
'apps.preview' => 'Previsualizar',
|
||||
'apps.apptype' => 'Tipo de Aplicación',
|
||||
'apps.website' => 'Sitio Web',
|
||||
'apps.description' => 'Descripción',
|
||||
'apps.only_admin_account' => '¡Solo si tienes cuenta de administrador!',
|
||||
'apps.autologin_url' => 'URL de auto inicio de sessión',
|
||||
'apps.autologin_url' => 'URL de inicio de sesión automático',
|
||||
'apps.show_deleted' => 'Mostrando Aplicaciones Eliminadas',
|
||||
'apps.import' => 'Importar',
|
||||
'dashboard' => 'Tablero Inicial',
|
||||
'user.user_list' => 'Usuarios',
|
||||
'user.add_user' => 'Añadir Usuario',
|
||||
@@ -82,27 +87,31 @@ return array (
|
||||
'user.email' => 'Correo Electrónico',
|
||||
'user.password_confirm' => 'Confirmar Contraseña',
|
||||
'user.secure_front' => 'Permitir acceso público a la interfaz - Forzado solo si una contraseña está definida.',
|
||||
'user.autologin' => 'Permitir inicio de sesión desde una URL específica. Cualquiera con el vínculo puede iniciar sesión.',
|
||||
'user.autologin' => 'Permitir inicio de sesión desde una URL específica. Cualquiera con el enlace puede iniciar sesión.',
|
||||
'url' => 'Url',
|
||||
'title' => 'Título',
|
||||
'delete' => 'Borrar',
|
||||
'optional' => 'Opcional',
|
||||
'restore' => 'Restaurar',
|
||||
'export' => 'Exportar',
|
||||
'import' => 'Importar',
|
||||
'alert.success.item_created' => 'Elemento creado con éxito',
|
||||
'alert.success.item_updated' => 'Artículo actualizado con éxito',
|
||||
'alert.success.item_updated' => 'Elemento actualizado con éxito',
|
||||
'alert.success.item_deleted' => 'Elemento eliminado correctamente',
|
||||
'alert.success.item_restored' => 'Elemento restaurado con éxito',
|
||||
'alert.success.updating' => 'Actualizando lista de aplicaciones',
|
||||
'alert.success.tag_created' => 'Etiqueta creada exitósamente',
|
||||
'alert.success.tag_updated' => 'Etiqueta actualizada exitósamente',
|
||||
'alert.success.tag_deleted' => 'Etiqueta eliminada exitósamente',
|
||||
'alert.success.tag_restored' => 'Etiqueta restaurada exitósamente',
|
||||
'alert.success.setting_updated' => 'Ha editado con éxito esta configuración',
|
||||
'alert.success.tag_updated' => 'Etiqueta actualizada exitosamente',
|
||||
'alert.success.tag_deleted' => 'Etiqueta eliminada exitosamente',
|
||||
'alert.success.tag_restored' => 'Etiqueta restaurada exitosamente',
|
||||
'alert.success.setting_updated' => 'Configuración editada con éxito',
|
||||
'alert.error.not_exist' => 'Esta configuración no existe.',
|
||||
'alert.success.user_created' => 'Usuario creado exitósamente',
|
||||
'alert.success.user_updated' => 'Usuario actualizado exitósamente',
|
||||
'alert.success.user_deleted' => 'Usuario eliminado exitósamente',
|
||||
'alert.success.user_restored' => 'Usuario restaurado exitósamente',
|
||||
'alert.error.file_too_big' => 'El fichero es demasiado grande.',
|
||||
'alert.error.file_not_stored' => 'El fichero no se ha podido almacenar.',
|
||||
'alert.success.user_created' => 'Usuario creado exitosamente',
|
||||
'alert.success.user_updated' => 'Usuario actualizado exitosamente',
|
||||
'alert.success.user_deleted' => 'Usuario eliminado exitosamente',
|
||||
'alert.success.user_restored' => 'Usuario restaurado exitosamente',
|
||||
'dashboard.reorder' => 'Reordenar y anclar elementos',
|
||||
'dashboard.settings' => 'Ajustes',
|
||||
);
|
||||
@@ -93,7 +93,7 @@ return [
|
||||
'timezone' => ':attribute は有効なゾーンでなければなりません。',
|
||||
'unique' => ':attribute は既に取得されています。',
|
||||
'uploaded' => ':attribute のアップロードに失敗しました。',
|
||||
'url' => ':attribute 形式が無効です。'
|
||||
'url' => ':attribute 形式が無効です。',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
117
lang/rs/app.php
Normal file
117
lang/rs/app.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
return array (
|
||||
'settings.system' => 'Sistem',
|
||||
'settings.appearance' => 'Izgled',
|
||||
'settings.miscellaneous' => 'Razno',
|
||||
'settings.advanced' => 'Napredno',
|
||||
'settings.support' => 'Podrška',
|
||||
'settings.donate' => 'Doniraj',
|
||||
'settings.version' => 'Verzija',
|
||||
'settings.background_image' => 'Slika u pozadini',
|
||||
'settings.trianglify' => 'Trianglify pozadine',
|
||||
'settings.trianglify_seed' => 'Trianglify Random Seed',
|
||||
'settings.window_target' => 'Linkovi se otvaraju',
|
||||
'settings.window_target.current' => 'U ovom tab-u',
|
||||
'settings.window_target.one' => 'U istom tab-u',
|
||||
'settings.window_target.new' => 'U novom tab-u',
|
||||
'settings.homepage_search' => 'Pretraga na početnoj strani',
|
||||
'settings.search_provider' => 'Podrazumevani pretraživač',
|
||||
'settings.language' => 'Jezik',
|
||||
'settings.reset' => 'Vraćanje na podrazumevane vrednosti',
|
||||
'settings.remove' => 'Ukloni',
|
||||
'settings.search' => 'pretraga',
|
||||
'settings.no_items' => 'Nije pronađen ni jedan rezultat',
|
||||
'settings.label' => 'Labela',
|
||||
'settings.value' => 'Vrednost',
|
||||
'settings.edit' => 'Izmeni',
|
||||
'settings.view' => 'Pregled',
|
||||
'settings.custom_css' => 'Ručni CSS',
|
||||
'settings.custom_js' => 'Ručni JavaScript',
|
||||
'settings.treat_tags_as' => 'Tretiraj Oznake Kao:',
|
||||
'settings.folders' => 'Fascikle',
|
||||
'settings.tags' => 'Oznake',
|
||||
'settings.categories' => 'Kategorije',
|
||||
'options.none' => '- nepodešeno -',
|
||||
'options.google' => 'Google',
|
||||
'options.ddg' => 'DuckDuckGo',
|
||||
'options.bing' => 'Bing',
|
||||
'options.qwant' => 'Qwant',
|
||||
'options.startpage' => 'StartPage',
|
||||
'options.yes' => 'Da',
|
||||
'options.no' => 'Ne',
|
||||
'options.nzbhydra' => 'NZBHydra',
|
||||
'options.jackett' => 'Jackett',
|
||||
'buttons.save' => 'Sačuvaj',
|
||||
'buttons.cancel' => 'Otkaži',
|
||||
'buttons.add' => 'Dodaj',
|
||||
'buttons.upload' => 'Učitaj ikonu',
|
||||
'buttons.downloadapps' => 'Ažuriraj Listu Aplikacija',
|
||||
'dash.pin_item' => 'Zakači na Dashboard',
|
||||
'dash.no_apps' => 'Trenutno nema zakačenih aplikacija, :link1 or :link2',
|
||||
'dash.link1' => 'Dodaj aplikaciju ovde',
|
||||
'dash.link2' => 'Zakači stavku na Dashboard',
|
||||
'dash.pinned_items' => 'Zakačene Stavke',
|
||||
'apps.app_list' => 'Lista aplikacija',
|
||||
'apps.view_trash' => 'Pogledaj smeće',
|
||||
'apps.add_application' => 'Dodaj aplikaciju',
|
||||
'apps.application_name' => 'Naziv aplikacije',
|
||||
'apps.colour' => 'Boja',
|
||||
'apps.icon' => 'Ikona',
|
||||
'apps.pinned' => 'Zakačeno',
|
||||
'apps.title' => 'Naziv',
|
||||
'apps.hex' => 'Hexadecimalna boja',
|
||||
'apps.username' => 'Korisnik',
|
||||
'apps.password' => 'Šifra',
|
||||
'apps.config' => 'Konfiguracija',
|
||||
'apps.apikey' => 'API Ključ',
|
||||
'apps.enable' => 'Omogući',
|
||||
'apps.tag_list' => 'Lista oznaka',
|
||||
'apps.add_tag' => 'Dodaj oznaku',
|
||||
'apps.tag_name' => 'Naziv oznake',
|
||||
'apps.tags' => 'Oznake',
|
||||
'apps.override' => 'Ukoliko je različito od glavnom url-a',
|
||||
'apps.preview' => 'Prikaz',
|
||||
'apps.apptype' => 'Tip Aplikacije',
|
||||
'apps.website' => 'Web sajt',
|
||||
'apps.description' => 'Opis',
|
||||
'apps.only_admin_account' => 'Samo ako imate administratorski nalog!',
|
||||
'apps.autologin_url' => 'Url za automatsko logovanje',
|
||||
'apps.show_deleted' => 'Prikazivanje obrisanih aplikacija',
|
||||
'app.import' => 'Uvoz',
|
||||
'dashboard' => 'Početni Dashboard',
|
||||
'user.user_list' => 'Korisnici',
|
||||
'user.add_user' => 'Dodaj korisnika',
|
||||
'user.username' => 'Korisnik',
|
||||
'user.avatar' => 'Slika',
|
||||
'user.email' => 'E-mail',
|
||||
'user.password_confirm' => 'Potvrdi šifu',
|
||||
'user.secure_front' => 'Dozvoli javni pristup početnoj stranici - Forsira se samo ako je podešena šifra.',
|
||||
'user.autologin' => 'Dozvoli logovanje sa specifičnog URL-a. Bilo ko sa linkom se može ulogovati.',
|
||||
'url' => 'URL',
|
||||
'title' => 'Naziv',
|
||||
'delete' => 'Obriši',
|
||||
'optional' => 'Opcionalno',
|
||||
'restore' => 'Povrati',
|
||||
'export' => 'Izvezi',
|
||||
'import' => 'Uvezi',
|
||||
'alert.success.item_created' => 'Stavka uspešno kreirana',
|
||||
'alert.success.item_updated' => 'Stavka uspešno ažurirana',
|
||||
'alert.success.item_deleted' => 'Stavka uspešno obrisana',
|
||||
'alert.success.item_restored' => 'Stavka uspešno povraćena',
|
||||
'alert.success.updating' => 'Ažuriranje liste aplikacija',
|
||||
'alert.success.tag_created' => 'Oznaka uspešno kreirana',
|
||||
'alert.success.tag_updated' => 'Oznaka uspešno ažurirana',
|
||||
'alert.success.tag_deleted' => 'Oznaka uspešno obrisana',
|
||||
'alert.success.tag_restored' => 'Oznaka uspešno povraćena',
|
||||
'alert.success.setting_updated' => 'Uspešno ste izmenili ovo podešavanje',
|
||||
'alert.error.not_exist' => 'Ovo podešavanje ne postoji.',
|
||||
'alert.error.file_too_big' => 'Prevelik fajl.',
|
||||
'alert.error.file_not_stored' => 'Nije moguće učitati fajl.',
|
||||
'alert.success.user_created' => 'Korisnik uspešno kreiran',
|
||||
'alert.success.user_updated' => 'Korisnik uspešnop ažuriran',
|
||||
'alert.success.user_deleted' => 'Korisnik uspešno obrisan',
|
||||
'alert.success.user_restored' => 'Korisnik uspešno povraćen',
|
||||
'dashboard.reorder' => 'Preuredi i zakači stavke',
|
||||
'dashboard.settings' => 'Podešavanja',
|
||||
);
|
||||
6813
package-lock.json
generated
6813
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -2,12 +2,12 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"development": "mix",
|
||||
"watch": "mix watch",
|
||||
"watch-poll": "mix watch -- --watch-options-poll=1000",
|
||||
"hot": "mix watch --hot",
|
||||
"development": "npx mix",
|
||||
"watch": "npx mix watch",
|
||||
"watch-poll": "npx mix watch -- --watch-options-poll=1000",
|
||||
"hot": "npx mix watch --hot",
|
||||
"prod": "npm run production",
|
||||
"production": "mix --production",
|
||||
"production": "npx mix --production",
|
||||
"lint": "eslint 'resources/assets/js/*'"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -21,7 +21,9 @@
|
||||
"laravel-mix": "^6.0.49",
|
||||
"prettier": "^2.8.1",
|
||||
"sass": "^1.56.1",
|
||||
"sass-loader": "13.*"
|
||||
"sass-loader": "13.*",
|
||||
"webpack": "^5.100.1",
|
||||
"webpack-cli": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"select2": "^4.0.13",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<!-- <env name="DB_CONNECTION" value="sqlite"/> -->
|
||||
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
|
||||
2185
public/css/app.css
vendored
2185
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
61
public/index.php
generated
61
public/index.php
generated
@@ -1,60 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel - A PHP Framework For Web Artisans
|
||||
*
|
||||
* @package Laravel
|
||||
* @author Taylor Otwell <taylor@laravel.com>
|
||||
*/
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader for
|
||||
| our application. We just need to utilize it! We'll simply require it
|
||||
| into the script here so that we don't have to worry about manual
|
||||
| loading any of our classes later on. It feels great to relax.
|
||||
|
|
||||
*/
|
||||
// Determine if the application is in maintenance mode...
|
||||
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||
require $maintenance;
|
||||
}
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Turn On The Lights
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| We need to illuminate PHP development, so let us turn on the lights.
|
||||
| This bootstraps the framework and gets it ready for use, then it
|
||||
| will load up this application so that we can run it and send
|
||||
| the responses back to the browser and delight our users.
|
||||
|
|
||||
*/
|
||||
|
||||
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once we have the application, we can handle the incoming request
|
||||
| through the kernel, and send the associated response back to
|
||||
| the client's browser allowing them to enjoy the creative
|
||||
| and wonderful application we have prepared for them.
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
|
||||
|
||||
$response = $kernel->handle(
|
||||
$request = Illuminate\Http\Request::capture()
|
||||
);
|
||||
|
||||
$response->send();
|
||||
|
||||
$kernel->terminate($request, $response);
|
||||
// Bootstrap Laravel and handle the request...
|
||||
(require_once __DIR__.'/../bootstrap/app.php')
|
||||
->handleRequest(Request::capture());
|
||||
|
||||
4637
public/js/app.js
vendored
4637
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
4
public/mix-manifest.json
generated
4
public/mix-manifest.json
generated
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"/css/app.css": "/css/app.css?id=9ac09de3efefe57251cba47c5747c556",
|
||||
"/js/app.js": "/js/app.js?id=a3df64b8054d7a3f742957b38d687835"
|
||||
"/css/app.css": "/css/app.css?id=271cb5f5a1f91d0a6dfbc65e374ffc14",
|
||||
"/js/app.js": "/js/app.js?id=2ebeb753597d1cbbf88d8bc652e4af5b"
|
||||
}
|
||||
|
||||
31
readme.md
31
readme.md
@@ -25,7 +25,7 @@ Why not use it as your browser start page? It even has the ability to include a
|
||||
If you want to see a quick video of Heimdall in use, go to https://youtu.be/GXnnMAxPzMc
|
||||
|
||||
## Supported applications
|
||||
You can use the app to link to any site or application, but Foundation apps will auto fill in the icon for the app and supply a default color for the tile. In addition, Enhanced apps allow you provide details to an apps API, allowing you to view live stats directly on the dashboad. For example, the NZBGet and Sabnzbd Enhanced apps will display the queue size, and download speed while something is downloading.
|
||||
You can use the app to link to any site or application, but Foundation apps will auto fill in the icon for the app and supply a default color for the tile. In addition, Enhanced apps allow you provide details to an apps API, allowing you to view live stats directly on the dashboard. For example, the NZBGet and Sabnzbd Enhanced apps will display the queue size, and download speed while something is downloading.
|
||||
|
||||
Supported applications are recognized by the title of the application as entered in the title field when adding an application. For example, to add a link to pfSense, begin by typing "p" in the title field and then select "pfSense" from the list of supported applications.
|
||||
|
||||
@@ -183,6 +183,35 @@ openssl.cafile = /config/heimdall.pem
|
||||
|
||||
Restart the container and the Enhanced apps should now be able to access your local HTTP websites. This configuration will survive updating or recreating the Heimdall container.
|
||||
|
||||
## Allow Internal IP Requests
|
||||
|
||||
By default, Heimdall blocks requests to private or reserved IP addresses to mitigate potential security risks such as Server-Side Request Forgery (SSRF). However, you can enable access to internal IPs by setting the `ALLOW_INTERNAL_REQUESTS` environment variable in your `.env` file.
|
||||
|
||||
### Steps to Enable Internal IP Requests
|
||||
1. Open your `.env` file located in the root directory of your Heimdall installation.
|
||||
2. Add the following line:
|
||||
```env
|
||||
ALLOW_INTERNAL_REQUESTS=true
|
||||
```
|
||||
Setting this to `true` allows Heimdall to make requests to internal IP addresses (e.g., `192.168.x.x`, `10.x.x.x`, `127.0.0.1`).
|
||||
|
||||
3. Save the file and clear the Laravel configuration cache:
|
||||
```bash
|
||||
php artisan config:clear
|
||||
```
|
||||
|
||||
4. Restart your web server or development server:
|
||||
```bash
|
||||
php artisan serve
|
||||
```
|
||||
|
||||
### Default Behavior
|
||||
If the `ALLOW_INTERNAL_REQUESTS` variable is not set or is set to `false`, Heimdall will block requests to private or reserved IP addresses and return a `403 Forbidden` error.
|
||||
|
||||
### Important Notes
|
||||
- Enabling internal IP requests may expose your application to SSRF risks if your Heimdall instance is accessible from the internet. Ensure your instance is properly secured and not publicly accessible.
|
||||
- Use this feature only if you trust the internal network and understand the security implications.
|
||||
|
||||
## Running offline
|
||||
The apps list is hosted on github, you have a couple of options if you want to run without a connection to the outside world:
|
||||
1) Clone the repository and host it yourself, look at the .github actions file to see how to generate the apps list.
|
||||
|
||||
@@ -108,11 +108,90 @@ $.when($.ready).then(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// Autocomplete functionality
|
||||
let autocompleteTimeout = null;
|
||||
let currentAutocompleteRequest = null;
|
||||
|
||||
function hideAutocomplete() {
|
||||
$("#search-autocomplete").remove();
|
||||
}
|
||||
|
||||
function showAutocomplete(suggestions, inputElement) {
|
||||
hideAutocomplete();
|
||||
|
||||
if (!suggestions || suggestions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $input = $(inputElement);
|
||||
const position = $input.position();
|
||||
const width = $input.outerWidth();
|
||||
|
||||
const $autocomplete = $('<div id="search-autocomplete"></div>');
|
||||
|
||||
suggestions.forEach((suggestion) => {
|
||||
const $item = $('<div class="autocomplete-item"></div>')
|
||||
.text(suggestion)
|
||||
.on("click", () => {
|
||||
$input.val(suggestion);
|
||||
hideAutocomplete();
|
||||
$input.closest("form").submit();
|
||||
});
|
||||
$autocomplete.append($item);
|
||||
});
|
||||
|
||||
$autocomplete.css({
|
||||
position: "absolute",
|
||||
top: `${position.top + $input.outerHeight()}px`,
|
||||
left: `${position.left}px`,
|
||||
width: `${width}px`,
|
||||
});
|
||||
|
||||
$input.closest("#search-container").append($autocomplete);
|
||||
}
|
||||
|
||||
function fetchAutocomplete(query, provider) {
|
||||
// Cancel previous request if any
|
||||
if (currentAutocompleteRequest) {
|
||||
currentAutocompleteRequest.abort();
|
||||
}
|
||||
|
||||
if (!query || query.trim().length < 2) {
|
||||
hideAutocomplete();
|
||||
return;
|
||||
}
|
||||
|
||||
currentAutocompleteRequest = $.ajax({
|
||||
url: `${base}search/autocomplete`,
|
||||
method: "GET",
|
||||
data: {
|
||||
q: query,
|
||||
provider,
|
||||
},
|
||||
success(data) {
|
||||
const inputElement = $("#search-container input[name=q]")[0];
|
||||
showAutocomplete(data, inputElement);
|
||||
},
|
||||
error() {
|
||||
hideAutocomplete();
|
||||
},
|
||||
complete() {
|
||||
currentAutocompleteRequest = null;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
$("#search-container")
|
||||
.on("input", "input[name=q]", function () {
|
||||
const search = this.value;
|
||||
const items = $("#sortable").children(".item-container");
|
||||
if ($("#search-container select[name=provider]").val() === "tiles") {
|
||||
const items = $("#sortable").find(".item-container");
|
||||
// Get provider from either select or hidden input
|
||||
const provider =
|
||||
$("#search-container select[name=provider]").val() ||
|
||||
$("#search-container input[name=provider]").val();
|
||||
|
||||
if (provider === "tiles") {
|
||||
hideAutocomplete();
|
||||
if (search.length > 0) {
|
||||
items.hide();
|
||||
items
|
||||
@@ -126,10 +205,16 @@ $.when($.ready).then(() => {
|
||||
}
|
||||
} else {
|
||||
items.show();
|
||||
|
||||
// Debounce autocomplete requests
|
||||
clearTimeout(autocompleteTimeout);
|
||||
autocompleteTimeout = setTimeout(() => {
|
||||
fetchAutocomplete(search, provider);
|
||||
}, 300);
|
||||
}
|
||||
})
|
||||
.on("change", "select[name=provider]", function () {
|
||||
const items = $("#sortable").children(".item-container");
|
||||
const items = $("#sortable").find(".item-container");
|
||||
if ($(this).val() === "tiles") {
|
||||
$("#search-container button").hide();
|
||||
const search = $("#search-container input[name=q]").val();
|
||||
@@ -147,9 +232,26 @@ $.when($.ready).then(() => {
|
||||
} else {
|
||||
$("#search-container button").show();
|
||||
items.show();
|
||||
hideAutocomplete();
|
||||
}
|
||||
});
|
||||
|
||||
// Hide autocomplete when clicking outside
|
||||
$(document).on("click", (e) => {
|
||||
if (!$(e.target).closest("#search-container").length) {
|
||||
hideAutocomplete();
|
||||
}
|
||||
});
|
||||
|
||||
// Hide autocomplete on Escape key
|
||||
$(document).on("keydown", (e) => {
|
||||
if (e.key === "Escape") {
|
||||
hideAutocomplete();
|
||||
}
|
||||
});
|
||||
|
||||
$("#search-container select[name=provider]").trigger("change");
|
||||
|
||||
$("#app")
|
||||
.on("click", "#config-button", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -141,6 +141,7 @@ body {
|
||||
top: -58px;
|
||||
transition: all .35s ease-in-out;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
ul {
|
||||
display: inline-block;
|
||||
list-style: none;
|
||||
@@ -237,6 +238,7 @@ body {
|
||||
|
||||
.userlist {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.user {
|
||||
@@ -786,12 +788,20 @@ div.create {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
gap: 5px;
|
||||
}
|
||||
.icon-name {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
word-break: break-word;
|
||||
text-align: center;
|
||||
}
|
||||
.selecticon {
|
||||
max-width: 120px;
|
||||
height: auto;
|
||||
@@ -916,6 +926,12 @@ div.create {
|
||||
max-width: 620px;
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
|
||||
// Reduce width when there's no select dropdown (only has hidden input)
|
||||
&:has(input[name="provider"][type="hidden"]) {
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -923,7 +939,6 @@ div.create {
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 0px 5px 0 rgba(0,0,0,0.4);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
@@ -935,6 +950,11 @@ div.create {
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
// When there's no select dropdown, round the input's left corners
|
||||
input[name="q"]:first-child {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
@@ -955,7 +975,42 @@ div.create {
|
||||
background: #f5f5f5;
|
||||
border: none;
|
||||
border-right: 1px solid #ddd;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
// When select exists, remove input's left border radius
|
||||
select ~ input[name="q"] {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#search-autocomplete {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
border-top: none;
|
||||
border-radius: 0 0 5px 5px;
|
||||
box-shadow: 0px 4px 8px 0 rgba(0,0,0,0.2);
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
|
||||
.autocomplete-item {
|
||||
padding: 12px 15px;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-autocomplete {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
|
||||
@section('content')
|
||||
@if(!$app['config']->get('app.auth_roles_enable', false))
|
||||
<?php
|
||||
$user = \App\User::currentUser();
|
||||
?>
|
||||
@@ -21,5 +23,14 @@ $user = \App\User::currentUser();
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@else
|
||||
<section class="module-container">
|
||||
<header>
|
||||
<div class="section-title">
|
||||
{{ __('app.disabled_feature') }}
|
||||
</div>
|
||||
</header>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
@section('content')
|
||||
|
||||
{!! Form::open(array('route' => 'items.store', 'id' => 'itemform', 'files' => true, 'method'=>'POST')) !!}
|
||||
{{ html()->form('POST', route('items.store'))->id('itemform')->acceptsFiles()->open() }}
|
||||
@include('items.form')
|
||||
{!! Form::close() !!}
|
||||
{{ html()->form()->close() }}
|
||||
|
||||
@endsection
|
||||
@section('scripts')
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
@section('content')
|
||||
|
||||
{!! Form::model($item, ['data-item-id' =>$item->id, 'method' => 'PATCH', 'id' => 'itemform', 'files' => true, 'route' => ['items.update', $item->id]]) !!}
|
||||
{{ html()->modelForm($item, 'PATCH', route('items.update', $item->id))->data('item-id', $item->id)->id('itemform')->acceptsFiles()->open() }}
|
||||
@include('items.form')
|
||||
{!! Form::close() !!}
|
||||
{{ html()->closeModelForm() }}
|
||||
|
||||
@endsection
|
||||
@section('scripts')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="toggleinput">
|
||||
<label class="name">{{ __('app.apps.enable') }}</label>
|
||||
{!! Form::hidden('config[enabled]', '0') !!}
|
||||
{{ html()->hidden('config[enabled]', '0') }}
|
||||
<label class="switch">
|
||||
<?php
|
||||
$checked = false;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<section class="module-container">
|
||||
<section class="module-container">
|
||||
@if($enable_auth_admin_controls)
|
||||
<header>
|
||||
{{ html()->hidden('app_id', $item->id) }}
|
||||
<div class="section-title">{{ __('app.apps.preview') }}</div>
|
||||
<div class="module-actions">
|
||||
<div class="toggleinput">
|
||||
<label class="name">{{ __('app.apps.pinned') }}</label>
|
||||
{!! Form::hidden('pinned', '0') !!}
|
||||
{{ html()->hidden('pinned', '0') }}
|
||||
<label class="switch">
|
||||
<?php
|
||||
$checked = true;
|
||||
@@ -29,7 +31,7 @@
|
||||
<div><button id="optdetails-button" class="dark">{{ __('app.apps.apptype') }}</button></div>
|
||||
<div class="optvalue">
|
||||
<div class="input">
|
||||
{!! Form::select('appid', App\Application::applist(), null, array('class' => 'form-control config-item', 'id' => 'apptype', 'data-config' => 'type')) !!}
|
||||
{{ html()->select('appid', App\Application::applist())->class('form-control config-item')->id('apptype')->data('config', 'type') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,7 +39,7 @@
|
||||
<div><button class="dark">{{ __('app.apps.website') }}</button></div>
|
||||
<div class="optvalue">
|
||||
<div class="input">
|
||||
{!! Form::text('website', $item->url ?? null, array('placeholder' => __('app.apps.website'), 'id' => 'website', 'class' => 'form-control')) !!}
|
||||
{{ html()->text('website', $item->url ?? null)->placeholder(__('app.apps.website'))->id('website')->class('form-control') }}
|
||||
<small class="help">Don't forget http(s)://</small>
|
||||
</div>
|
||||
<div><button class="btn">Go</button></div>
|
||||
@@ -57,25 +59,32 @@
|
||||
{!! csrf_field() !!}
|
||||
<div class="input">
|
||||
<label>{{ __('app.apps.application_name') }} *</label>
|
||||
{!! Form::text('title', null, array('placeholder' => __('app.apps.title'), 'id' => 'appname', 'class' => 'form-control', 'required')) !!}
|
||||
{{ html()->text('title')->placeholder(__('app.apps.title'))->id('appname')->class('form-control')->required() }}
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<label>{{ __('app.apps.colour') }}</label>
|
||||
{!! Form::text('colour', $item->colour ?? '#161b1f', array('placeholder' => __('app.apps.hex'), 'id' => 'appcolour', 'class' => 'form-control color-picker set-bg-elem')) !!}
|
||||
{{ html()->text('colour', $item->colour ?? '#161b1f')->placeholder(__('app.apps.hex'))->id('appcolour')->class('form-control color-picker set-bg-elem') }}
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<label>{{ strtoupper(__('app.url')) }} *</label>
|
||||
{!! Form::text('url', $item->url ?? null, array('placeholder' => __('app.url'), 'id' => 'appurl', 'class' => 'form-control', 'required')) !!}
|
||||
{{ html()->text('url', $item->url ?? null)->placeholder(__('app.url'))->id('appurl')->class('form-control')->required() }}
|
||||
<small class="help">Don't forget http(s)://</small>
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<label>{{ __('app.apps.tags') }} ({{ __('app.optional') }})</label>
|
||||
{!! Form::select('tags[]', $tags, $current_tags, ['class' => 'tags', 'multiple']) !!}
|
||||
{{ html()->multiselect('tags[]', $tags, $current_tags)->class('tags') }}
|
||||
</div>
|
||||
|
||||
@if($app['config']->get('app.auth_roles_enable', false))
|
||||
<div class="input">
|
||||
<label>{{ __('app.role') }}</label>
|
||||
{{ html()->text('role', $item->role ?? null)->placeholder(__('app.role'))->id('role')->class('form-control') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="input">
|
||||
<div class="icon-container">
|
||||
<div id="appimage">
|
||||
@@ -85,7 +94,7 @@
|
||||
else $icon = old('icon');
|
||||
?>
|
||||
<img src="{{ asset('storage/'.$icon) }}" />
|
||||
{!! Form::hidden('icon', $icon, ['class' => 'form-control']) !!}
|
||||
{{ html()->hidden('icon', $icon)->class('form-control') }}
|
||||
@else
|
||||
<img src="{{ asset('/img/heimdall-icon-small.png') }}" />
|
||||
@endif
|
||||
@@ -112,7 +121,7 @@
|
||||
@if(isset($item) && $item->enhanced())
|
||||
|
||||
<div id="sapconfig" style="display: block;">
|
||||
@if(isset($item))
|
||||
@if(isset($item) && $item->class)
|
||||
@include('SupportedApps::'.App\Item::nameFromClass($item->class).'.config')
|
||||
@endif
|
||||
</div>
|
||||
@@ -137,6 +146,13 @@
|
||||
<a href="{{ route('items.index', []) }}" class="button"><i class="fa fa-ban"></i><span>{{ __('app.buttons.cancel') }}</span></a>
|
||||
</div>
|
||||
</footer>
|
||||
@else
|
||||
<header>
|
||||
<div class="section-title">
|
||||
{{ __('app.unauthorized_for_form') }}
|
||||
</div>
|
||||
</header>
|
||||
@endif
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
@section('content')
|
||||
|
||||
<section class="module-container">
|
||||
@if($enable_auth_admin_controls)
|
||||
<header>
|
||||
<div class="section-title">{{ __('app.import') }}</div>
|
||||
<div class="module-actions">
|
||||
@@ -31,6 +32,13 @@
|
||||
<a href="{{ route('settings.index', []) }}" class="button"><i class="fa fa-ban"></i><span>{{ __('app.buttons.cancel') }}</span></a>
|
||||
</div>
|
||||
</footer>
|
||||
@else
|
||||
<header>
|
||||
<div class="section-title">
|
||||
{{ __('app.unauthorized_for_form') }}
|
||||
</div>
|
||||
</header>
|
||||
@endif
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user