Compare commits

...

1033 Commits

Author SHA1 Message Date
KodeStar 238452c104 Merge pull request #1583 from linuxserver/fix/app-icon-consistent-size
Fix inconsistent SVG tile icon sizes
2026-08-03 15:52:03 +01:00
KodeStar 91f7a2ec8d Fix inconsistent SVG tile icon sizes
SVG icons with explicit width/height attributes rendered tiny because
max-width/max-height only caps intrinsic size and never scales up, while
dimensionless SVGs defaulted large and were capped to 60px. Pin .app-icon
to a fixed 60x60 box with object-fit: contain so every icon renders at a
consistent size while preserving aspect ratio.

Fixes #1582
2026-08-03 15:47:36 +01:00
KodeStar 62823aef63 Merge pull request #1580 from tuxinaut/chore/de-language-update
🇩🇪 Update German translation
2026-07-10 17:10:02 +01:00
Schäfer, Denny 2ceec61e17 Update German translation 2026-07-10 12:30:14 +02:00
KodeStar 8fc5f3ccbf Merge pull request #1579 from linuxserver/release/v2.8.1
Tag and release / Tag and publish release (push) Has been cancelled
Bump version to 2.8.1
2026-07-09 22:58:46 +01:00
github-actions[bot] 6a45d44f69 Bump version to 2.8.1 2026-07-09 21:35:30 +00:00
KodeStar 327af60d1c Update CI 2026-07-09 22:33:11 +01:00
KodeStar a38ecd4864 Automate bumping version 2026-07-09 22:21:37 +01:00
KodeStar 1f7432fa26 Merge pull request #1578 from linuxserver/feat/truenas-websocket-client
Add WebSocket support for TrueNAS JSON-RPC 2.0 API
2026-07-09 22:06:47 +01:00
KodeStar 55bda9daf0 Merge pull request #1577 from linuxserver/fix/checkbox-test-config
Fix checkbox config values always returning "1" in Test config
2026-07-09 22:06:42 +01:00
KodeStar 667dd4c129 Merge pull request #1576 from linuxserver/feat/global-tls-skip
Add global TLS verification skip setting
2026-07-09 22:06:38 +01:00
KodeStar d16b7f60f3 Vendor phrity/websocket dependency and fix v3 exception namespace
The WebSocket support added for TrueNAS JSON-RPC 2.0 requires the
phrity/websocket library to actually be available at runtime. This repo
commits the vendor/ tree (CI does not run composer install), so the
dependency and composer.lock must be committed for class_exists() checks
in the TrueNAS app to succeed.

- composer require phrity/websocket:^3.6 (resolves to 3.7.3) with lock
  and vendor/ committed
- Fix TrueNASWebSocketClient to catch WebSocket\Exception\Exception
  (phrity/websocket v3 namespace) instead of the non-existent
  WebSocket\ConnectionException from the old textalk/websocket v1/v2 API,
  so connection/call failures are logged and wrapped as intended
2026-07-09 22:01:18 +01:00
Jay Collett 57fa9f26fb Fix checkbox config values always returning "1"
Checkbox-type config options (e.g. ignore_tls) always tested as "1"
regardless of the checkbox state. The Test-button config gatherer used
$(this).val() for every .config-item, and jQuery .val() on a checkbox
returns its value attribute ("1") regardless of checked state. App
config blades pair a hidden input (0) with a checkbox (1) sharing the
same data-config, and the checkbox is last in DOM order, so it always
overwrote the value with "1". Normal form saves were unaffected.

Use the :checked state for checkboxes so unchecking now tests as "0".

Also rebuilds the committed compiled bundle (public/js/app.js) so the
fix takes effect at runtime; the edit is byte-identical to the Laravel
Mix development build output for this block.

Fixes linuxserver/Heimdall-Apps#782
2026-07-09 21:59:46 +01:00
Jay Collett bdf9160fdc Add global TLS verification skip setting
Adds a new boolean setting in the Advanced settings group that allows
users to globally skip TLS certificate verification for all enhanced
apps. This is useful for users who have self-signed certificates on
their services.

When enabled, the Guzzle HTTP client will set 'verify' => false for
all API requests made by enhanced apps.

Resolves linuxserver/Heimdall-Apps#687
2026-07-09 21:57:45 +01:00
Jay Collett 93a321ff5b Add WebSocket support for TrueNAS JSON-RPC 2.0 API
TrueNAS is deprecating the REST API (api/v2.0/) in version 26.04,
requiring migration to JSON-RPC 2.0 over WebSocket.

This commit adds:
- phrity/websocket dependency for WebSocket communication
- TrueNASWebSocketClient helper class that handles:
  - Connection to ws(s)://host/api/current
  - Authentication via auth.login_with_api_key
  - JSON-RPC 2.0 request/response formatting
  - TLS verification toggle
  - Proper connection cleanup

Refs: #1530
2026-07-09 21:56:41 +01:00
KodeStar 3f483c9ca7 Merge pull request #1575 from linuxserver/chore/laravel-13-upgrade
Fix to try and mitigate any disappearing tiles
2026-07-09 11:30:47 +01:00
KodeStar 1c7ae3e335 Fix silent tile disappearance and ownership-reassignment bugs
Tiles could vanish without ever being deleted:

- Editing an item merged the editor's user_id into every save, so
  updating a visible item (e.g. a shared user_id=0 tile) silently
  reassigned ownership and hid it from everyone else. user_id is now
  set on create only, and excluded from update input in both Item and
  Tag controllers since it is mass-assignable.
- Deleting a user left their items orphaned with a dangling user_id,
  invisible to all users forever. The user's items are now hard-deleted
  with the account, and a data migration reassigns already-orphaned
  items to the admin user so previously "lost" tiles reappear.
- The Item global scope's ownership filter had an ungrouped orWhere,
  breaking operator precedence in any query that adds further clauses.

Includes regression coverage for ownership on create/update, user
deletion cleanup, and the orphan-recovery migration.
2026-07-09 10:52:29 +01:00
KodeStar 2555ab1b3c Run tests against in-memory sqlite and refuse real databases
The :memory: overrides in phpunit.xml had been commented out since 2024,
so RefreshDatabase ran migrate:fresh against the real .env database and
wiped it on every local test run. Enable the overrides, let the special
:memory: identifier bypass database_path() resolution and the boot-time
touch(), and add a TestCase guard that aborts the suite unless it is
pointed at in-memory sqlite.

ItemExportTest only ever passed by reading the populated dev database;
seed the root dashboard item it depends on so it passes on a fresh DB.
2026-07-09 10:52:17 +01:00
KodeStar 49c29c8681 Merge pull request #1574 from linuxserver/chore/laravel-13-upgrade
Upgrade to Laravel 13, bump to v2.8.0, and remediate vulnerabilities
2026-07-09 09:31:38 +01:00
KodeStar 6f932918aa Fix import status regression from appload 404 + tighten upgrade tests
- itemImport: check response.ok in fetchAppDetails so a genuine 404
  (from the appload return-type fix) is reported as 'Failed to find app
  id' instead of being parsed as a successful import; applied to the
  source and the committed compiled bundle.
- phpunit.xml: point the schema URL at 12.5 to match the installed
  PHPUnit 12.5.x.
- ColorHelpersTest: exercise the get_brightness() non-hex stripping the
  test name promised (interior separators), which the prior assertion
  never covered.
2026-07-09 09:28:53 +01:00
KodeStar 13642d59d9 Fix committed PHPUnit vendor path casing for case-sensitive CI
PHPUnit 12 renamed PHPT -> Phpt (src/Framework/Exception/PhptAssertionFailedError.php
and the src/Runner/Phpt/ directory). Because this repo commits vendor/ and the
macOS dev filesystem is case-insensitive, git kept the old-case paths in the index
while Composer wrote the new case to disk, so the change went undetected locally.
On CI's case-sensitive Linux filesystem the checked-out old-case files don't satisfy
PHPUnit's require of the new-case names and 'php artisan test' fatals before running.
Re-track these 8 files under their correct case.
2026-07-08 20:40:49 +01:00
KodeStar 25d1dc3c6a Bump version to 2.8.0 2026-07-08 20:33:27 +01:00
KodeStar f1eec81591 Fix appload() returning a coerced HTTP 200 instead of a 404
ItemController::appload() was declared ': ?string', so its two error
branches that 'return response()->json([...], 404)' had the JsonResponse
coerced through Response::__toString() into a raw HTTP message served as
an HTTP 200 body. Widen the return type to
'\Illuminate\Http\JsonResponse|string|null' so those branches emit
real 404 JSON responses. The method body is unchanged, so the happy path
still returns the same JSON string and the frontend contract is preserved.

Flip the endpoint characterization test to assert the corrected 404.
2026-07-08 20:33:26 +01:00
KodeStar 46e09d172a Add upgrade-regression test coverage
Add tests guarding the surfaces most likely to break on a future
Laravel/PHP upgrade:
- Helper globals: format_bytes, parse_size, className, get_brightness,
  title_color (tests/Unit/helpers)
- CSRF exception config actually reaches the framework
  (PreventRequestForgery neverVerify list) and the excepted routes resolve
- Core GET routes boot and render on the current framework
- Filesystem disks resolve and the local disk root stays pinned to
  storage_path('app') (guards the Laravel 12 default-root change)
2026-07-08 20:33:25 +01:00
KodeStar 9cfa2548fa Remediate npm build-toolchain vulnerabilities via overrides
Add a package.json "overrides" block forcing patched versions of
vulnerable transitive build dependencies (shell-quote, ws, node-forge,
serialize-javascript, lodash, minimatch, path-to-regexp, svgo, postcss,
qs, uuid and others). This takes `npm audit` from 41 vulnerabilities
(1 critical, 13 high, 18 moderate, 9 low) down to 9 (0 critical, 0 high,
3 moderate, 6 low).

The 9 residuals are all dev/build-time-only advisories in the EOL
laravel-mix@6 toolchain (the elliptic crypto-polyfill chain with no
upstream patch, ajv 6.x under babel-loader, webpack-dev-server /
laravel-mix, and webpack) that cannot be cleared without replacing
laravel-mix; none ship in Heimdall's production runtime. Both
'npm run production' and 'npm run dev' still compile successfully.
2026-07-08 20:33:25 +01:00
KodeStar 8a622545ff Upgrade to Laravel 13
- laravel/framework ^12.0 -> ^13.0 (installed 13.19.0)
- laravel/tinker ^2.9 -> ^3.0 (installed 3.0.2, pulls psysh >=0.12.19)
- phpunit/phpunit ^11.0 -> ^12.0 (installed 12.5.31)
- Symfony components move to 8.x (supported by L13); symfony/yaml stays on
  patched 7.4.14 via its direct ^7.0 constraint
- league/commonmark auto-bumped to 2.8.2 (patched)
- No application/config code changes required; the fluent
  validateCsrfTokens(except: ...) config still resolves under L13

composer audit: No security vulnerability advisories found.
Full suite green: 59 tests, 128 assertions (1 skipped).
2026-07-08 19:57:08 +01:00
KodeStar 39191885e5 Upgrade to Laravel 12
- laravel/framework ^11.45 -> ^12.0 (installed 12.63.0)
- phpunit/phpunit ^10.5 -> ^11.0; migrate phpunit.xml to the 11.5 schema
- graham-campbell/github ^12.5 -> ^13.0 (v12 caps illuminate/support at ^11)
- Carbon 3 pulled in by L12; no application code changes required
- gitignore /.phpunit.cache

Full suite green: 59 tests, 128 assertions (1 skipped).
2026-07-08 19:49:05 +01:00
KodeStar cb59689ead Merge pull request #1569 from linuxserver/chore/security-dependency-updates
Update security-flagged dependencies and require PHP 8.4 (#1564)
2026-07-08 18:30:36 +01:00
KodeStar 6d0242dead Merge pull request #1572 from linuxserver/feature/default-tag-group
Add a configurable default tag for the dashboard
2026-07-08 18:29:43 +01:00
KodeStar caa4f39edd Merge pull request #1567 from linuxserver/fix/export-import-tags
Include tags in item export and restore them on import
2026-07-08 18:29:18 +01:00
KodeStar a1f0d8f75d Merge pull request #1568 from linuxserver/fix/host-header-injection
Harden against host header injection and open redirect (CVE-2025-50578)
2026-07-08 18:29:04 +01:00
KodeStar 076348478e Merge pull request #1570 from linuxserver/fix/getstats-graceful-failure
Return graceful output from get_stats instead of a 500
2026-07-08 18:28:55 +01:00
KodeStar ad9baffa62 Only offer pinned tags as the default tag
The default_tag dropdown was populated from every tag (type=1), but the
dashboard taglist only renders pinned tags. Selecting an unpinned tag as the
default therefore triggered a click on a taglist entry that does not exist,
silently doing nothing. Filter the option queries in both Setting accessors to
pinned tags so only selectable tags are offered, and assert an unpinned tag is
excluded.

Also drop the unused $data['default_tag'] assignment in ItemController: the
taglist partial reads the setting directly via Setting::fetch(), so the view
variable was never consumed.
2026-07-08 18:26:19 +01:00
KodeStar 9a9877a0dc Enforce TRUSTED_HOSTS allow-list regardless of APP_ENV
The custom TrustHosts middleware only overrode hosts(), so it inherited the
parent's shouldSpecifyTrustedHosts() gate, which skips enforcement whenever the
app runs in the local environment or under the test runner. Heimdall ships
APP_ENV=local by default (.env.example, copied to .env on install), so the
TRUSTED_HOSTS allow-list a user configures per the .env.example guidance was
never actually applied.

Override shouldSpecifyTrustedHosts() to tie enforcement to configuration
instead of environment: apply the allow-list whenever TRUSTED_HOSTS is set, in
any environment; when it is unset hosts() is empty and enforcement stays off,
preserving the historic no-restriction behaviour. Add handle()-driven tests
covering both the configured and unconfigured cases.
2026-07-08 18:22:49 +01:00
KodeStar c0c202c5ff Add a configurable default tag for the dashboard
In tags mode the dashboard always opened showing every link. This adds a
"Default tag" setting (Advanced) that pre-selects one tag group on load, so
the dashboard opens filtered to it - the built-in equivalent of the custom
JavaScript workaround people have been sharing.

The setting is a select populated from the user's own tags, following the
same dynamic-option pattern already used for the search provider. When a tag
is chosen its slug is exposed on the tag list and the matching tab is
activated on load; when the setting is empty, behaviour is unchanged and all
links are shown.

Resolves #1556
2026-07-08 17:10:19 +01:00
KodeStar f69cbba6cd Update dependencies flagged by security advisories and require PHP 8.4
Bumps the packages reported by CVE/GHSA scans in #1564 to their patched
releases (with transitive dependencies):

- symfony/http-foundation 7.3.1 -> 7.4.14 (CVE-2025-64500 / GHSA-3rg7-wf37-54rm)
- phpunit/phpunit 10.5.47 -> 10.5.64 (CVE-2026-24765 / GHSA-vvj3-c3rp-c85p)
- aws/aws-sdk-php 3.349.3 -> 3.388.0 (GHSA-27qh-8cxx-2cr5)
- enshrined/svg-sanitize 0.21.0 -> 0.22.0 (GHSA-22wq-q86m-83fh)

Some transitive dependencies now require PHP 8.4, which matches the runtime
shipped in the official LinuxServer image, so the composer requirement is
raised to ^8.4, CI is pinned to PHP 8.4, and the readme is updated to match.

The remaining advisories in the report (php84, curl, libpq, git, sqlite,
busybox, coreutils) come from the LinuxServer base image, not this
repository, and are addressed by rebuilding the image on an updated base.

Refs #1564
2026-07-08 16:30:22 +01:00
KodeStar ec229ea0af Merge branch '2.x' into fix/export-import-tags 2026-07-08 16:09:46 +01:00
KodeStar 5dcf462542 Merge branch '2.x' into fix/host-header-injection 2026-07-08 16:09:44 +01:00
KodeStar d1c52b886b Merge branch '2.x' into fix/getstats-graceful-failure 2026-07-08 16:09:42 +01:00
KodeStar e2215fe42b Merge pull request #1571 from linuxserver/fix/ci-node-select2
Fix CI: pin Node 24 and install frontend deps with npm ci
2026-07-08 16:08:59 +01:00
KodeStar 5247349d33 Fix CI: pin Node 24 and install frontend deps with npm ci
CI ran `yarn && yarn dev` with no committed yarn.lock, so every run resolved
the latest matching versions and dependency drift broke the pipeline in two
independent ways:

- select2 4.1.0 added engines.node ">=24" but the runner used node 22, so
  `yarn install` failed outright on every pull request.
- webpack 5.108 removed lib/SizeFormatHelpers, which laravel-mix 6 still
  requires, so `yarn dev` would have failed the build regardless of node.

Switch the workflow to `npm ci`, which installs the exact, known-good
versions already pinned in package-lock.json (select2 4.0.13, webpack
5.100.1) and is verified to build and lint cleanly. Pin the runner to node
24 via actions/setup-node so the toolchain is explicit rather than tracking
the runner default. Also pin select2 to ~4.0.13 in package.json so a future
`npm install` cannot pull the incompatible 4.1.0 back in.
2026-07-08 16:06:35 +01:00
KodeStar f547ae42bb Return graceful output from get_stats instead of a 500
get_stats/{id} fataled when the item id was missing and 500'd whenever an
enhanced app's livestats() threw - a broken or updated remote app definition
(e.g. Komga) took the whole request down, and the frontend then stopped
refreshing that tile entirely.

getStats now returns valid JSON (200) with an inactive/empty payload when the
item is missing, has no class, references a stale class, or throws, logging
the failure for diagnosis. The successful path is unchanged and returns the
livestats output verbatim.

Resolves #1558
2026-07-08 15:21:39 +01:00
KodeStar 881533baa5 Harden against host header injection and open redirect
Heimdall trusted the incoming X-Forwarded-Host header for URL generation, so
a spoofed value poisoned the page base href, asset() URLs and redirect
targets - loading assets from and redirecting to an attacker-controlled host
(CVE-2025-50578).

- TrustProxies no longer trusts X-Forwarded-Host; a forged value can no longer
  influence getHost(), url(), asset() or redirects. X-Forwarded-For/Port/Proto
  handling is unchanged.
- Trusted proxies are now configurable via the TRUSTED_PROXIES env var
  (comma-separated CIDRs/IPs, "*" to trust all), defaulting to the previous
  private ranges.
- Added an opt-in TRUSTED_HOSTS allow-list: when set, only the listed hosts are
  served and any other Host header is rejected. Unset keeps the historic
  behaviour of serving arbitrary hosts, so existing installs are unaffected.

Resolves #1451
2026-07-08 14:42:12 +01:00
KodeStar fb9af1b216 Include tags in item export and restore them on import
The export endpoint (api/item) now emits each item's assigned tag titles,
excluding the root/default dashboard tag. On import, those titles are
resolved back to local tags - reusing an existing tag or creating a missing
one - instead of dropping every imported item onto the default dashboard.

Tags round-trip by title so a config can be moved between instances without
having to reassign each item to its section by hand.

Resolves #1555
2026-07-08 14:18:45 +01:00
KodeStar 5907a1f231 Merge pull request #1559 from JoshSalway/queue-safety-2026-04-22
[2.x] Bound retry and unique-lock lifetimes on UpdateApps and ProcessApps
2026-07-08 09:35:47 +01:00
KodeStar df0eba046b Merge pull request #1563 from Nyuwb/patch-1
fix: proxy options in ItemController
2026-07-08 09:34:39 +01:00
Fabien Ehrlich 6a776e30f8 fix: proxy options in ItemController
The correct context is http->proxy :

https://www.php.net/manual/en/context.http.php
2026-05-13 11:35:13 +02:00
Josh Salway cbf099be2c Remove QueueFailedHandlerTest from the shipped suite
Reproduction-style behavior test, not a regression test, so it does
not belong in the main suite. Full test remains in this branch's
commit history at 56c53ab9 for reviewers:

    git checkout 56c53ab9 -- tests/Feature/QueueFailedHandlerTest.php
    vendor/bin/phpunit --filter QueueFailedHandlerTest

Keeps the PR aligned with Heimdall's existing test conventions
(HTTP-feature tests, no facade-mocking unit tests).
2026-04-22 20:16:11 +10:00
Josh Salway 56c53ab9c5 Prove failed() log shape with behavior test
Tests that UpdateApps::failed() and ProcessApps::failed():
 - call Log::error with the 'permanently failed' message
 - include exception_class, exception_message, and file context keys
 - for UpdateApps, still call Cache::lock('updateApps')->forceRelease()

Uses Log::spy() and Mockery to capture facade calls. 2 tests, 4
assertions, green on PHP 8.4.20. See follow-up commit for why this
lands in history but not in the shipped suite.
2026-04-22 20:15:54 +10:00
Josh Salway 98b6d96cd1 Enrich failed() log context with exception class and file:line
Previously logged only the exception message. Adds:
 - exception_class: distinguishes ClientException vs ConnectException
   vs other Guzzle/PHP failure types at a glance
 - file: file path and line where the exception was raised, useful
   for distinguishing 'failed inside Guzzle' from 'failed inside our
   code path'

The exception message itself often contains the GitHub API URL,
which encodes the app identifier. Capturing the specific appid at
the moment of failure would require touching handle() to track the
current iteration; left as a follow-up.
2026-04-22 17:01:18 +10:00
Josh Salway 5be7a65677 Tighten retry shape: $tries=1, $uniqueFor=600, drop $timeout and $backoff
Most failures for these jobs are GitHub API rate-limit responses;
retries inside the same window do not help, so one attempt is enough
and $backoff has nothing to pace.

$timeout would clip the intentionally throttled handle() loop
(sleep(1) per app) below realistic workloads. Heavy users with 60+
apps would lose updates mid-cycle. The original code left $timeout
unset, and `UpdateApps` is dispatched via `dispatchAfterResponse()`
which doesn't go through `queue:work` at all (the queue $timeout
is irrelevant in that path). Letting the operator's worker config
govern is more honest.

$uniqueFor reduced to 600 (10 min) since with $tries=1 +
worker-governed timeout there is no long retry chain to outlive.
Lock self-heals 10 minutes after a crashed worker.
2026-04-22 16:57:49 +10:00
Josh Salway 44be3cb319 Drop tests/Feature/QueueSafetyTest.php
The test only asserted property values and method_exists. The diff
itself shows the property values; the test added zero confidence
beyond reading the diff. Heimdall is an app, not a package; its
existing tests are HTTP feature tests against user behavior, not
class-property assertions. Removing this file keeps the PR aligned
with the existing test conventions.
2026-04-22 16:56:21 +10:00
Josh Salway 243ad00810 Bound retry and unique-lock lifetimes on UpdateApps and ProcessApps
Both jobs implement ShouldBeUnique without a $uniqueFor value, which on
Redis and database drivers produces a lock that never expires. If the
worker is killed mid-fire (OOM, SIGKILL, server crash), the lock
persists and blocks all future dispatches of UpdateApps or ProcessApps
until the cache entry is manually cleared.

Neither job sets $tries, $backoff, or $timeout, so they inherit the
worker command's defaults (1 for queue:work, 0 for vapor:work), which
varies by platform and is brittle.

This change adds:
 - $uniqueFor = 3600   lock expires after 1 hour
 - $tries = 3          hard cap across worker restarts
 - $backoff = [30, 60, 120]  pace retries to reduce GitHub API load
 - $timeout = 60       bound per-attempt wall-clock time
 - failed(Throwable)    log permanent failures (and preserve the
                        existing Cache::lock('updateApps')->forceRelease
                        on UpdateApps)

A new test (tests/Feature/QueueSafetyTest.php) asserts the retry
properties are present.

All existing tests still pass.
2026-04-22 14:55:01 +10:00
KodeStar 7861ae1512 Merge pull request #1524 from KodeStar/remove_dropdown_on_single_provider
Mark stale issues and pull requests / stale (push) Has been cancelled
Remove search provider dropdown when there's only a single provider
2025-11-11 12:05:43 +00:00
Chris Hunt 66dfe95c9f Fix lint issue 2025-11-11 12:02:53 +00:00
Chris Hunt 130661bd34 Remove search provider dropdown when there's only a single provider
Resolves #1509
2025-11-11 12:00:41 +00:00
KodeStar 900fc83e79 Merge pull request #1523 from KodeStar/add_autocomplete_suggestions
Add autocomplete suggestions support
2025-11-11 11:43:55 +00:00
Chris Hunt 4f30332854 Fix lint issues 2025-11-11 11:42:38 +00:00
Chris Hunt 852c231724 Add autocomplete suggestions support and added to bing, duckduckgo, and google 2025-11-11 11:39:06 +00:00
KodeStar 045bdf0deb Merge pull request #1507 from KodeStar/taglist
Mark stale issues and pull requests / stale (push) Has been cancelled
Fix tag list url when tags are treated as tags
2025-09-16 09:53:06 +01:00
Chris Hunt 755c3e59e1 Fix tag list url when tags are treated as tags 2025-09-16 09:50:03 +01:00
KodeStar 32bf1d034f Add password field to fix #1498 2025-09-15 16:49:42 +01:00
Chris Hunt 6d12c547e7 Add password field 2025-09-15 16:42:53 +01:00
KodeStar ae4ce92dab Merge pull request #1503 from KodeStar/background_max_file_size
Add current background maxsize #1501
2025-09-15 16:28:52 +01:00
KodeStar 966279b252 Merge pull request #1499 from webmogul1/patch-1
Update app.php
2025-09-15 16:28:28 +01:00
KodeStar 54cf2b88ca Update application version to 2.7.6 2025-09-15 16:28:00 +01:00
Chris Hunt 31f1ba8192 Add current background maxsize #1501 2025-09-15 16:26:10 +01:00
webmogul1 eadd9d1dd8 Update app.php
Change version to 2.7.5
2025-09-11 14:46:50 -04:00
KodeStar c9ea2cdeb3 Merge pull request #1496 from KodeStar/bugfix/update_proxy_and_items_with_no_password
Update items with no password
2025-09-10 16:15:23 +01:00
Chris Hunt 517f51ba90 Update items with no password 2025-09-10 16:14:14 +01:00
KodeStar 825f67a4a4 Merge pull request #1480 from Nyuwb/patch-1
feat(icon-upload): proxy management
2025-09-10 15:15:48 +01:00
KodeStar 05a552ffcf Merge pull request #1475 from micvog/fix-german-translation
Fixed multiple typos (German translation)
2025-09-10 15:14:55 +01:00
Adam ad4584e548 Merge pull request #1488 from linuxserver/inherit-security-md 2025-08-23 15:22:58 +01:00
Adam 7d93099f2c Delete SECURITY.md
Inherit LSIO standard security.md from https://github.com/linuxserver/.github/blob/main/SECURITY.md
2025-08-23 15:20:18 +01:00
KodeStar 31ca05f74f Merge pull request #1483 from KodeStar/2.x
Mark stale issues and pull requests / stale (push) Has been cancelled
Fix for some enhanced apps not working
2025-08-02 17:50:17 +01:00
Chris Hunt cd95fc3b92 Update search test 2025-08-02 17:43:49 +01:00
Chris Hunt 63e777b338 Redirect to search provider without error fixes #1482 2025-08-02 17:40:37 +01:00
Chris Hunt fd926e983d Fix for some enhanced apps not working 2025-08-02 17:17:40 +01:00
Fabien Ehrlich dce37c1412 feat(icon-upload): proxy management 2025-07-31 16:54:44 +02:00
KodeStar 6b9f61b0e6 Merge pull request #1477 from KodeStar/2.x
Mark stale issues and pull requests / stale (push) Has been cancelled
Escape search queries and add setting value on edit
2025-07-24 19:06:50 +01:00
Chris Hunt d1a96dd752 Escape search queries and add setting value on edit 2025-07-24 19:05:16 +01:00
KodeStar 1ccc0da2a7 Merge pull request #1476 from KodeStar/2.x
Mark stale issues and pull requests / stale (push) Has been cancelled
Load in configs values if class has been lost
2025-07-22 15:59:00 +01:00
Chris Hunt 41aa255b88 Add missing variable 2025-07-22 15:57:29 +01:00
Chris Hunt a8e4ab448b Load in configs values if class has been lost 2025-07-22 15:50:51 +01:00
micvog 31db31d0f7 Fixed multiple typos (German translation) 2025-07-21 17:37:39 +02:00
KodeStar e42f78bd49 Merge pull request #1473 from KodeStar/2.x
Add misisng input type in the form builder
2025-07-21 10:12:27 +01:00
Chris Hunt 08b8ab6d4f Add misisng input type in the form builder 2025-07-21 10:10:13 +01:00
KodeStar 57e0a335b7 Merge pull request #1471 from KodeStar/2.x
Mark stale issues and pull requests / stale (push) Has been cancelled
Add cache table
2025-07-17 14:46:16 +01:00
Chris Hunt 0d90170385 Add cache table 2025-07-17 14:43:09 +01:00
KodeStar abe08a7b04 Merge pull request #1470 from KodeStar/2.x
Fix importing apps and logging in
2025-07-17 14:23:09 +01:00
Chris Hunt 6075dcca2d Fix importing apps and logging in 2025-07-17 14:19:02 +01:00
KodeStar 4fa41d8114 Merge pull request #1469 from KodeStar/2.x
Add tests and fix user edit form
2025-07-15 17:10:49 +01:00
Chris Hunt 1e6b1f6de5 Add tests and fix user edit form 2025-07-15 17:04:47 +01:00
KodeStar 53fd6242db Merge pull request #1467 from KodeStar/2.x
Fixes to reduce the SSRF attack vector plus display image names
2025-07-14 11:15:13 +01:00
Chris Hunt 69bc8cb34e Fixes to reduce the SSRF attack vector. 2025-07-13 19:06:33 +01:00
KodeStar 0388ee939a Merge pull request #1466 from KodeStar/2.x
Fix uploads and displaying of malicious SVG files
2025-07-13 17:02:27 +01:00
Chris Hunt 2df58472a1 Fix uploads and displaying of malicious SVG files 2025-07-13 17:00:23 +01:00
KodeStar abbc78ee8d Merge pull request #1464 from KodeStar/2.x
Update framework to laravel 11 and fix images with no extension causing error 500
2025-07-11 16:36:40 +01:00
Chris Hunt d1801d1088 Update cache version 2025-07-11 16:33:05 +01:00
Chris Hunt 22f66d35e5 Throw error if image doesn't have an extension #1446 2025-07-11 16:19:34 +01:00
Chris Hunt f197aeb013 Fix forms on enhanced apps 2025-07-11 16:18:13 +01:00
Chris Hunt 8fb6438254 Updates to vendors etc 2025-07-11 15:57:48 +01:00
KodeStar d972cbcd0a Merge pull request #14 from KodeStar/shift-154026
Migrate to `spatie/laravel-html`
2025-07-10 20:20:26 +01:00
Shift 638d2fe4a4 Convert Form facade 2025-07-10 19:17:24 +00:00
Shift e251f4f1bc Swap dependency 2025-07-10 19:17:23 +00:00
KodeStar ecc54b6fbe Merge pull request #13 from KodeStar/shift-154023
Laravel 11.x Shift
2025-07-10 20:15:54 +01:00
Shift f0ce9d633a Remove createApplication method 2025-07-10 18:54:07 +00:00
Shift 968a5823a1 Adopt Laravel type hints 2025-07-10 18:54:07 +00:00
Shift 53f28b5056 Adopt anonymous migrations 2025-07-10 18:54:05 +00:00
Shift 7bb0a7ceb4 Bump Composer dependencies 2025-07-10 18:54:04 +00:00
Shift f5ddd93141 Re-register routes 2025-07-10 18:54:03 +00:00
Shift 8554861d0a Re-register service providers 2025-07-10 18:54:02 +00:00
Shift 9091d1d707 Consolidate service providers 2025-07-10 18:54:02 +00:00
Shift 42d29f0fdb Re-register HTTP middleware 2025-07-10 18:54:02 +00:00
Shift 474059eee8 Default new bootstrap/app.php 2025-07-10 18:54:01 +00:00
Shift 1fb6f75555 Set new ENV variables 2025-07-10 18:54:01 +00:00
Shift 48e16ebfc9 Streamline config files 2025-07-10 18:54:00 +00:00
Shift 7153a41e03 Shift core files 2025-07-10 18:53:55 +00:00
Shift b9e75b9284 Remove default app files 2025-07-10 18:53:54 +00:00
Shift b3cdc5779c Slim lang files 2025-07-10 18:53:48 +00:00
Shift c1c3888673 Apply code style 2025-07-10 18:53:44 +00:00
KodeStar 11453e1018 Merge pull request #1453 from riv-gh/bugfix/userlist-wrap
fixing page "/userselect" if many users on this
2025-07-10 19:25:34 +01:00
KodeStar 749442ec6e Merge pull request #1454 from knom/2.x
Enable/Disable Items with authentification headers
2025-07-10 19:24:57 +01:00
knom 5950ca9076 renamed "controles" to "controls" 2025-05-05 11:49:42 +02:00
Tobias Kolzer 55d3766b39 added AUTH_ROLES_DELIMITER to example env file 2025-05-05 11:49:41 +02:00
Tobias Kolzer c71479f266 fixed some typos 2025-05-05 11:49:41 +02:00
Tobias Kolzer 2e5d7453cf added ENV variable for the auth roles delimiter 2025-05-05 11:49:41 +02:00
Tobias Kolzer 3cbbf807ab corrected some spelling mistakes 2025-05-05 11:49:41 +02:00
Tobias Kolzer 65a6ad53bf small 2025-05-05 11:49:41 +02:00
Tobias Kolzer dff3f90d00 Update readme.md 2025-05-05 11:49:41 +02:00
Tobias Kolzer 041ec4236b updated supportedapps 2025-05-05 11:49:41 +02:00
Tobias Kolzer 808c41acd6 disabled users, items, tags for non admin users and user and login forms for all users 2025-05-05 11:49:31 +02:00
Tobias Kolzer c08b0bfe39 changed naming of property 2025-05-05 11:49:31 +02:00
Tobias Kolzer f7de56b7a7 added missing variable to example env 2025-05-05 11:49:18 +02:00
Tobias Kolzer 921631bdcd fixed some rebase conflicts 2025-05-05 11:49:18 +02:00
Tobias Kolzer d146fed320 hide switchuser when auth_role is used 2025-05-05 11:49:18 +02:00
Tobias Kolzer c56ffe1d1e fixed some rebase conflicts 2025-05-05 11:49:18 +02:00
daenur e9d561466b Add warp on userlist
fixing page "/userselect" if many users on this
2025-04-25 10:57:56 +03:00
KodeStar b90b38eae9 Merge pull request #1392 from linuxserver/bugfix/allow_ico_images
Allow ico images
2024-11-05 11:23:43 +00:00
Chris Hunt be59ac794e Allow ico images fixes #1357 2024-11-05 11:11:34 +00:00
KodeStar f820c0b4f6 Merge pull request #1388 from dfernandezdaw/2.x
Update Spanish translations
2024-11-05 10:56:55 +00:00
KodeStar 2b43760dc8 Merge pull request #1391 from linuxserver/bugfix/two_dots
Fix 2 dots that are visible on light backgrounds
2024-11-05 10:50:21 +00:00
KodeStar 8f360f97ef Merge branch '2.x' into bugfix/two_dots 2024-11-05 10:44:15 +00:00
KodeStar 014f054862 Merge pull request #1390 from linuxserver/feature/search_from_url
Search from URL fixed #1369
2024-11-05 10:42:59 +00:00
Chris Hunt 5ccb87cd7f Fix 2 dots that are visible on light backgrounds #1383 2024-11-05 10:42:05 +00:00
Chris Hunt 8ba8f0c867 Prettier fix 2024-11-05 10:33:12 +00:00
Chris Hunt c2a3368c7b Search from URL fixed #1369 2024-11-05 10:29:34 +00:00
KodeStar 395c775d4e Merge pull request #1387 from linuxserver/bugfix/search_category_tiles
Fix searching of tiles in category mode
2024-11-05 10:14:02 +00:00
KodeStar 1063d2cd09 Update app.php 2024-11-05 09:42:10 +00:00
David 18609fb3c2 Update Spanish translations 2024-11-04 21:18:04 +01:00
Chris Hunt b880333856 Fixes #1386 2024-11-04 10:30:24 +00:00
KodeStar 026bcf9e43 Merge pull request #1384 from linuxserver/feature/throttle_websitelookup
Throttle websitelookup's to 10 per minute to limit port scan effectiveness
2024-11-04 10:05:52 +00:00
Chris Hunt 837f5c49fa Throttle websitelookup's to 10 per minute to limit port scan effictiveness 2024-11-04 09:54:10 +00:00
KodeStar a4973869d4 Merge pull request #1372 from tremor021/2.x
Added Serbian translation
2024-11-04 09:38:23 +00:00
KodeStar 07ea22dd10 Merge pull request #1328 from goodactive/2.x
chore: fix some typos in comments
2024-11-04 09:25:06 +00:00
tremor021 6188b1d669 Added Serbian translation 2024-09-27 22:04:30 +02:00
goodactive 184e19abbc chore: fix some typos in comments
Signed-off-by: goodactive <goodactive@qq.com>
2024-04-19 18:00:20 +08:00
KodeStar f405cf2ddf Update validation.php
Fix missing comma in Japanese translation
2024-03-31 21:40:31 +01:00
KodeStar c16a4847f7 Update version number 2024-02-19 11:08:45 +00:00
KodeStar 9d0209c9df Merge pull request #1293 from KodeStar/2.x
Fixes to tags and show tags on application list
2024-02-19 11:08:18 +00:00
Chris Hunt 599035b3f8 Fix empty title when creating 2024-02-19 11:06:42 +00:00
Chris Hunt f3bc6ab618 Add tags to application list, and fix home dashboard tag 2024-02-19 10:58:19 +00:00
Chris Hunt 09e4bb8cad Fix required fields and default items and tags to pinned 2024-02-19 10:03:03 +00:00
Chris Hunt 4b8bf5156d Fix prettier complaints 2024-02-19 09:56:03 +00:00
KodeStar 809a997631 Merge pull request #1291 from KodeStar/2.x
Fix upload paths
2024-02-18 20:27:26 +00:00
Chris Hunt fb7f9de127 Fix upload paths 2024-02-18 20:26:20 +00:00
KodeStar 002bae3fb3 Merge pull request #1290 from KodeStar/feature/add_columns
Add alternate tag types
2024-02-18 18:35:43 +00:00
Chris Hunt b7c0fd29c6 Fix extra prettier complaints 2024-02-18 18:33:47 +00:00
Chris Hunt 70252b7a43 Fix prettier complaints 2024-02-18 18:26:34 +00:00
Chris Hunt d276bf8b1d Add alternate tag types 2024-02-18 18:16:46 +00:00
Chris Hunt a0726a43ad Updates 2024-02-18 09:06:37 +00:00
Chris Hunt 8fa870e8eb Initial commit 2024-02-18 09:06:03 +00:00
KodeStar d184427016 Merge pull request #1289 from linuxserver/revert-1288-2.x
Revert "Replace Mix with Vite"
2024-02-17 21:03:02 +00:00
KodeStar 36d1d55934 Revert "Replace Mix with Vite" 2024-02-17 21:02:45 +00:00
KodeStar 3a509fca47 Merge pull request #1288 from KodeStar/2.x
Replace Mix with Vite
2024-02-17 20:00:19 +00:00
KodeStar afc07ab9bc Update ci.yml 2024-02-17 19:59:33 +00:00
KodeStar 0de1e5bd25 Merge pull request #9 from KodeStar/shift-110402
Migrate from Laravel Mix to Vite
2024-02-17 19:57:42 +00:00
Chris Hunt 029def9d90 Update vite 2024-02-17 19:56:47 +00:00
Shift c6c9ec1d89 Ignore build assets 2024-02-17 19:03:26 +00:00
Shift 0f19e3c5de Add .vue extension to Vue imports 2024-02-17 19:03:25 +00:00
Shift f3a4ac3619 Rename ENV variables 2024-02-17 19:03:25 +00:00
Shift baf7131f20 Use ES modules 2024-02-17 19:03:24 +00:00
Shift 4430b21740 Remove webpack.mix.js 2024-02-17 19:03:24 +00:00
Shift 3a3cea437b Configure Vite 2024-02-17 19:03:23 +00:00
Shift aa5a6fce4c Update build scripts 2024-02-17 19:03:23 +00:00
Shift 8781dbc9f5 Add npm dependencies 2024-02-17 19:03:23 +00:00
KodeStar 23f3a9c343 Merge pull request #1283 from KodeStar/bugfix/unable_to_load_apps
Add missing classname
2024-02-17 17:06:44 +00:00
Chris Hunt 2899b7db61 Add missing 2024-02-17 17:05:06 +00:00
KodeStar e9bfd9a878 Merge pull request #1280 from KodeStar/bugfix/unable_to_load_apps
Unable to load apps after update to Laravel 10
2024-02-17 16:44:33 +00:00
Chris Hunt 5825858abc Unable to load apps after update to laravel 10 2024-02-17 16:42:26 +00:00
KodeStar 379ba34fe1 Merge pull request #1278 from KodeStar/bugfix/fix_language_dropdown
Fix languages dropdown
2024-02-17 15:27:55 +00:00
Chris Hunt a0e5111771 Fix languages 2024-02-17 15:13:43 +00:00
KodeStar c69961b200 Merge pull request #1277 from KodeStar/2.x
Update php version in CI tests
2024-02-17 10:21:13 +00:00
KodeStar 39f23103e4 Update ci.yml 2024-02-17 10:19:12 +00:00
KodeStar e7d581244c Update ci.yml 2024-02-17 10:14:55 +00:00
KodeStar 2fea82c21e Change to php 8.3 2024-02-17 10:08:50 +00:00
Chris Hunt 274894562c Update php version in CI tests 2024-02-17 10:04:01 +00:00
KodeStar 9fbb3457d1 Add Laravel 10 dependencies 2024-02-17 08:12:27 +00:00
KodeStar 70f212c502 Update readme.md 2024-02-16 21:44:27 +00:00
KodeStar 5e2c3a5f66 Merge pull request #1276 from KodeStar/2.x
Update Laravel from 8 to 10
2024-02-16 21:41:05 +00:00
Chris Hunt ffb5b03704 Update version 2024-02-16 21:38:25 +00:00
Chris Hunt d52ae0d3c3 Update dependencies 2024-02-16 21:36:54 +00:00
KodeStar 22d7a59e59 Merge pull request #8 from KodeStar/shift-110368
Laravel 10.x Shift
2024-02-16 21:30:12 +00:00
Shift f3a5be79dc Remove redundant typing from DocBlocks 2024-02-16 21:13:14 +00:00
Shift 3487f52a6b Add type hints from DocBlocks 2024-02-16 21:13:14 +00:00
Shift 2cb837e4b5 Add type hints for Laravel 10 2024-02-16 21:13:13 +00:00
Shift 8725493fcf Rename password_resets table 2024-02-16 21:13:11 +00:00
Shift e902b3a38a Bump Composer dependencies 2024-02-16 21:13:09 +00:00
Shift 854fb420c2 Default config files
In an effort to make upgrading the constantly changing config files
easier, Shift defaulted them and merged your true customizations -
where ENV variables may not be used.
2024-02-16 21:13:09 +00:00
Shift 5b4e14d886 Shift config files 2024-02-16 21:13:09 +00:00
Shift ef25ec4f69 Shift core files 2024-02-16 21:13:07 +00:00
Shift b943674881 Remove explicit call to register policies 2024-02-16 21:12:58 +00:00
Shift 3188a8f311 Convert string references to ::class
PHP 5.5.9 adds the new static `class` property which provides the fully qualified class name. This is preferred over using strings for class names since the `class` property references are checked by PHP.
2024-02-16 21:12:53 +00:00
KodeStar 095aff86c2 Merge pull request #7 from KodeStar/shift-110367
Laravel 9.x Shift
2024-02-16 21:11:51 +00:00
Shift 018996526c Shift cleanup 2024-02-16 20:33:47 +00:00
Shift 1c73cf08de Adopt anonymous migrations 2024-02-16 20:33:46 +00:00
Shift 4bfcd643e6 Use <env> tags for configuration
`<env>` tags have a lower precedence than system environment variables making it easier to overwrite PHPUnit configuration values in additional environments, such a CI.

Review this blog post for more details on configuration precedence when testing Laravel: https://jasonmccreary.me/articles/laravel-testing-configuration-precedence/
2024-02-16 20:33:44 +00:00
Shift 04b8ede8db Bump Composer dependencies 2024-02-16 20:33:43 +00:00
Shift a6ae60270f Default config files
In an effort to make upgrading the constantly changing config files
easier, Shift defaulted them and merged your true customizations -
where ENV variables may not be used.
2024-02-16 20:33:43 +00:00
Shift ee3361d9c9 Shift config files 2024-02-16 20:33:43 +00:00
Shift 42b5a294d0 Adopt class based routes 2024-02-16 20:33:38 +00:00
Shift 14b89ac44d Convert route options to fluent methods
Laravel 8 adopts the tuple syntax for controller actions. Since the old options array is incompatible with this syntax, Shift converted them to use modern, fluent methods.
2024-02-16 20:33:37 +00:00
Shift ad86e54f37 Remove unnecessary $model property 2024-02-16 20:33:37 +00:00
Shift 0f4b336d82 Remove web.config 2024-02-16 20:33:37 +00:00
Shift 6423ccd075 Shift core files 2024-02-16 20:33:36 +00:00
Shift 72cd16afa1 Upgrade to Flysystem 3.0 2024-02-16 20:33:33 +00:00
Shift d41c4c8d4c Replace deprecated HEADER_X_FORWARDED_ALL constant 2024-02-16 20:33:33 +00:00
Shift 2b602ce66d Streamline $commands property 2024-02-16 20:33:32 +00:00
Shift aa72ce0a3f Shift registered middleware 2024-02-16 20:33:32 +00:00
Shift 825ff6a6c8 Move resources/lang folder 2024-02-16 20:33:30 +00:00
Shift be93195188 Convert string references to ::class
PHP 5.5.9 adds the new static `class` property which provides the fully qualified class name. This is preferred over using strings for class names since the `class` property references are checked by PHP.
2024-02-16 20:33:27 +00:00
Shift 51b30e55cd Adopt short array syntax
Since PHP 5.4 the short array syntax `[]` may be used instead of `array()`.
2024-02-16 20:33:26 +00:00
KodeStar ecb668a8f3 Merge pull request #1155 from TheDen/add-search-application-item
add search via select2 for application dropdown
2024-02-12 19:44:23 +00:00
KodeStar 2e96b7bd09 Update app.php version 2023-11-11 15:04:44 +00:00
KodeStar 768e7a6576 Merge pull request #1222 from fyutins/2.x
Fix and add SVG support
2023-11-11 14:58:41 +00:00
Fyutins 431eafb2b6 Fix and add SVG support 2023-09-26 18:51:12 +02:00
Ian Jones d2184eef0a Update readme.md (#1186)
Spelling and Grammar, included a few apostrophes for correct plural form or possessive. 

Added Capitalization to proper nouns

Included a single colon for consistency to match the styling of  it's predecessor paragraph
2023-08-09 11:21:57 +02:00
Attila Kerekes fbd050d4e4 fix: validate icons to be images (#1173) 2023-06-06 12:08:47 +02:00
Attila Kerekes 5d67f570a9 fix: validate icons to be images (#1167) 2023-06-05 18:27:30 +02:00
Attila Kerekes 7d016cdaa6 fix: Remove mix from trianglify.js (#1166) 2023-05-26 13:36:07 +02:00
ullbergm 6e954a355d Add Trianglify (#1158) 2023-05-26 10:23:06 +02:00
KodeStar 60faccad27 Remove register route (#1163) 2023-05-20 21:56:34 +02:00
jatdung 1f5493ac98 fix sortable tooltip (#1130) 2023-05-16 16:42:17 +02:00
TheDen 427353bdfc add search via select2 for application dropdown 2023-04-29 18:25:02 +10:00
aptalca 9c117b7946 Merge pull request #1150 from linuxserver/2.x-project
handle issue-pr close and review submitted actions
2023-04-19 09:22:36 -04:00
aptalca c72fee9644 handle issue-pr close and review submitted actions 2023-04-14 15:31:46 -04:00
aptalca 0999bebcb4 Merge pull request #1147 from linuxserver/2.x-issues
add issue-pr workflows
2023-04-08 19:00:40 -04:00
aptalca 1c41e3d1ef add issue-pr workflows 2023-04-06 15:25:53 -04:00
Chris Hunt ed3cd79c92 Change minor version ready for next release 2023-03-09 16:17:32 +00:00
Chris Hunt 192002f898 Update minor version 2023-03-09 16:10:51 +00:00
jatdung 18f7306b2c Update Chinese language. (#1124) 2023-03-07 17:51:19 +01:00
jatdung e9754519be fix Request class error (#1126) 2023-03-07 12:32:58 +01:00
ShiftHackZ 1c276fbfc2 feat: Added Ukrainian translation (#1125) 2023-03-07 12:32:01 +01:00
jatdung cefe07d218 fix language setting only available in view (#1117) 2023-02-23 18:24:11 +01:00
jatdung cd4b522935 use sortablejs instead of jquery-ui (#1115) 2023-02-10 21:32:58 +01:00
angrystar170 1ad5603c3e Update Korean language. (#1116) 2023-02-10 21:30:38 +01:00
Attila Kerekes 302e04bb12 fix: Update jquery, jquery-ui (#1113) 2023-02-04 20:34:49 +01:00
Attila Kerekes 49dab6e72b fix: Add more error handling for app test (#1111) 2023-02-01 11:43:26 +01:00
Filipe F Moreira c5d0769afb Add BR translations (#1080) 2023-01-16 20:14:33 +01:00
KodeStar 4005894c16 Update app.php 2023-01-09 19:28:56 +00:00
Attila Kerekes a4022ce517 fix: Escape app title and tag title on list pages CVE-2022-47968 (#1088) 2023-01-05 20:31:15 +01:00
Attila Kerekes cd07d47445 fix: Add more verbose error when uploading background image (#1082) 2022-12-24 10:56:00 +01:00
Attila Kerekes df70dcc521 feat: Add tags as classes to items (#1079) 2022-12-19 14:40:38 +01:00
Attila Kerekes 9e6321e500 fix: Enable tag slug creation from CN characters (#1077) 2022-12-16 10:26:00 +01:00
KodeStar 0d9850c1c7 Merge pull request #1073 from linuxserver/fix/395
fix: Autologin forces logout
2022-12-15 23:38:36 +00:00
Attila Kerekes 4d3083886e fix: Autologin forces logout 2022-12-15 22:21:16 +01:00
KodeStar 52f59afe63 Merge pull request #1072 from linuxserver/fix/895
fix: Public access to front also applies to tags
2022-12-15 19:48:54 +00:00
Attila Kerekes aa886e4f77 fix: Public access to front also applies to tags 2022-12-15 20:17:08 +01:00
Attila Kerekes 3c9f361f5f chore: Add eslint github check (#1067) 2022-12-11 18:44:37 +01:00
Attila Kerekes 5eb1f55b82 chore: Add php code sniffer github check (#1066) 2022-12-11 11:58:58 +01:00
KodeStar d910f8e4f7 Merge pull request #1065 from linuxserver/feat/health
feat: Add health endpoint
2022-12-11 09:46:20 +00:00
Attila Kerekes 751b23a5e4 feat: Add health endpoint 2022-12-11 01:39:39 +01:00
Yusuke Arakawa a4fa490fb4 feat: Add Japanese language (#1064)
Co-authored-by: Yusuke Arakawa <nekolaboratory@users.noreply.github.com>
2022-12-09 23:43:23 +01:00
Yusuke Arakawa 662f6ac3eb chore: Sort the supported language list of readme.md (#1062) 2022-12-09 09:40:52 +01:00
Attila Kerekes 11257a272e feat: Introduce queue based live stat refresh (#1059) 2022-12-05 16:29:06 +01:00
Attila Kerekes 6b93f8ed5c test: Add item create test (#1058) 2022-12-05 16:28:18 +01:00
Attila Kerekes 8f9b2959b2 feat: Add basic import screen (#1056)
Co-authored-by: Chris Hunt <chris.hunt@kasmweb.com>
2022-12-04 22:12:54 +01:00
Attila Kerekes 45cc84c99c test: Add feature tests 2022-12-04 17:21:03 +00:00
Attila Kerekes 52620bc331 chore: Add laravel ide helper 2022-12-01 09:17:54 +00:00
Attila Kerekes bd76efc613 chore: Migrate route definition to Laravel 8 format 2022-11-29 21:34:11 +00:00
Attila Kerekes 6dead1c529 fix: Make supportedApps legacy interface compatible 2022-11-29 19:26:07 +00:00
Attila Kerekes bb5a078f35 feat: Add export import 2022-11-29 17:32:46 +00:00
Attila Kerekes 2ee5d07e48 fix: Make supportedapps compatible with legacy interface 2022-11-29 17:26:40 +00:00
Attila Kerekes d30edb6749 chore: Add eslint prettier phpcs config PSR2 2022-11-28 19:47:10 +00:00
Chris Hunt a53192beeb FIx: Blur onauxclick as well 2022-11-28 08:33:05 +00:00
Chris Hunt ef1ffe880d Update version to 2.5.4 2022-11-28 08:30:34 +00:00
KodeStar 877b31f947 Merge pull request #1041 from linuxserver/bug/links_opening_in_new_tab_keep_focus
Blur tiles after clicking on them
2022-11-26 17:02:45 +00:00
Chris Hunt 827088df76 Blur tiles after clicking on them 2022-11-26 14:48:54 +00:00
Attila Kerekes a12c1d559d fix: Use correct appTest argument type 2022-11-26 13:31:05 +00:00
KodeStar f0b60fc19a Merge pull request #1039 from linuxserver/fix/1038
fix: Enter should open search when not tile search is selected
2022-11-26 12:00:43 +00:00
Attila Kerekes 7c4619adb9 fix: Enter should open search when not tile search is selected 2022-11-26 12:22:12 +01:00
KodeStar aa49a5fb42 Fixes #371 Tagged items when pinning/unpinning (#1036) 2022-11-25 23:21:45 +00:00
Attila Jozsef Kerekes 7565bd4028 chore: Add php code sniffer and apply fixes 2022-11-25 23:05:58 +00:00
Chris Hunt 181b7564e8 Merge branch '2.x' of github.com:linuxserver/Heimdall into 2.x 2022-11-25 16:23:40 +00:00
Chris Hunt 0d3640b67a Fix asset links 2022-11-25 16:23:34 +00:00
Attila Kerekes 82b209d4b5 fix: Test enhanced app with apikey 2022-11-25 16:11:39 +00:00
Chris Hunt f576a82803 Fix: Change default logging to daily 2022-11-24 22:56:27 +00:00
Attila Kerekes bb07ba5964 feat: Add db connection example config 2022-11-24 14:28:16 +00:00
Attila Kerekes 9aaa900a3e fix: New package lock with node 18.12 2022-11-24 08:36:20 +00:00
KodeStar 07f07449b9 Update: Updated npm dependencies + using mix (#1026) 2022-11-23 19:39:00 +00:00
Attila Kerekes 2836dadf1a fix: Disable saerch submit when tile search is active 2022-11-23 18:12:21 +00:00
KodeStar 4a90fdf22d Merge pull request #1024 from KodeStar/2.x
Updated translations using lokalise.com
2022-11-23 15:11:54 +00:00
KodeStar 0de56e5876 Merge pull request #5 from KodeStar/lokalise-2022-11-23_16-08-41
Lokalise: Translations update
2022-11-23 15:10:37 +00:00
KodeStar d886b44c1e Updated translations using lokalise.com 2022-11-23 15:09:10 +00:00
Chris Hunt 898ee6915d Merge branch '2.x' of github.com:linuxserver/Heimdall into 2.x 2022-11-23 13:25:10 +00:00
Chris Hunt f6eae7f25f FIx: Use mix version rather than app version 2022-11-23 13:24:36 +00:00
KodeStar c1255280aa Merge pull request #1023 from linuxserver/feature/focus_and_sidebar_tooltips
Feature - focus and sidebar tooltips
2022-11-23 10:50:07 +00:00
Chris Hunt 5514d893f8 Fix: Add translations to sidebar tooltips 2022-11-23 10:19:27 +00:00
Chris Hunt c4d5ca43dd Feature: Add focus styling 2022-11-23 10:09:16 +00:00
Chris Hunt a90bcb1151 Fix: Move tooltips in from right 2022-11-23 09:42:33 +00:00
Chris Hunt ffc736ada6 Fix: Added nicer tooltips to sidebar 2022-11-23 09:29:52 +00:00
KodeStar 966027faf5 Merge pull request #1022 from keriati/feat/kbr 2022-11-23 08:41:21 +00:00
Attila Kerekes 1111bf72d6 feat: Add basic keyboard navigation 2022-11-22 22:53:07 +01:00
KodeStar cc6e47372e Merge pull request #1020 from keriati/feat/nopwsubmit
feat: Prevent password submission when password is not changed.
2022-11-22 17:06:16 +00:00
KodeStar 256160727c Merge pull request #1019 from keriati/feat/uniqueappupdate
feat: Make app update job unique
2022-11-22 17:05:28 +00:00
Attila Kerekes 0299ee6fa9 feat: Prevent password submission when password is not changed. 2022-11-22 17:52:10 +01:00
Attila Kerekes 8957a2f49f feat: Make app update job unique 2022-11-22 17:23:26 +01:00
KodeStar 756ab353f3 Merge pull request #1018 from keriati/feat/appupdatejob
feat: Move app update to background job
2022-11-22 09:12:16 +00:00
Attila Kerekes 76f0b84827 feat: Move app update to background job 2022-11-22 00:05:57 +01:00
KodeStar 68c8dbc33a Merge pull request #1017 from keriati/fix/appupdatereduce
fix: Throttle app updates when updating to new version.
2022-11-20 20:44:43 +00:00
Attila Kerekes b1e856ccfc fix: Throttle app updates when updating to new version. 2022-11-20 21:37:13 +01:00
KodeStar 44fb923b7f Merge pull request #1015 from keriati/feat/testpwinject
feat: Inject password from db during testing when editing enhanced apps
2022-11-19 19:53:32 +00:00
Attila Kerekes 8bad474808 feat: Inject password from db during testing when editing enhanced apps 2022-11-19 20:48:31 +01:00
KodeStar 9a80064a50 Merge pull request #1014 from KodeStar/2.x
Automated missing translation strings using lokalise.com
2022-11-19 13:22:28 +00:00
KodeStar e86c5e601d Merge pull request #4 from KodeStar/lokalise-2022-11-19_14-15-24
Lokalise: Translations update
2022-11-19 13:18:05 +00:00
KodeStar c4bf619be4 Updated translations using lokalise.com 2022-11-19 13:15:51 +00:00
KodeStar 4b29b476df Merge pull request #1012 from keriati/feat/appupdateon
feat: Trigger app update when new version is deployed
2022-11-19 12:41:09 +00:00
KodeStar 3a39809585 Merge pull request #1013 from keriati/fix/fri18n
fix: Add missing FR translations
2022-11-19 12:04:58 +00:00
Attila Kerekes 157b972ff5 feat: Trigger app update when new version is deployed 2022-11-18 17:35:55 +01:00
Attila Kerekes 71b15a5aed fix: Add missing FR translations 2022-11-18 17:12:50 +01:00
KodeStar b50bf716dc Merge pull request #1010 from keriati/feat/enhancedapppw
feat: Keep stored password when Enhanced App is updated
2022-11-17 21:20:16 +00:00
KodeStar 722d00df5f Merge pull request #1009 from keriati/feat/eslangupdate
feat: Add more ES translations. fixes #961
2022-11-17 21:18:49 +00:00
Attila Kerekes 54679ff9eb feat: Keep stored password when Enhanced App is updated 2022-11-17 21:13:10 +01:00
Attila Kerekes 4c7e56e6fe feat: Add more ES translations fixes #961 2022-11-17 21:05:50 +01:00
KodeStar 7b0ae66101 Merge pull request #1008 from keriati/fix/stoppollonerror
fix: Stop polling enhanced apps when api returns error
2022-11-17 17:30:26 +00:00
Attila Kerekes 48616e87bd fix: Stop polling enhanced apps when api returns error 2022-11-17 18:00:35 +01:00
Chris Hunt f9280cd4c0 Don't commit debugbar json files 2022-11-17 16:20:21 +00:00
KodeStar 3124a01011 Merge pull request #1007 from keriati/fix/userlang
fix: User language setting
2022-11-17 16:17:36 +00:00
Attila Kerekes ec27889086 fix: User language setting 2022-11-17 17:12:24 +01:00
KodeStar aba1f37b1c Merge pull request #1006 from smelikmartin/2.x
Added czech language resources
2022-11-17 09:51:01 +00:00
Martin Smelik 6565dfde07 Added czech language 2022-11-17 10:27:43 +01:00
KodeStar aa98c3505c Don't break if default search provider deleted from yaml
Fixes #904
2022-11-17 00:20:54 +00:00
KodeStar 15f5c3e5be Merge pull request #999 from keriati/fix/sessionexpired
fix: Expired session setCookie issue #379
2022-11-16 22:54:59 +00:00
Attila Kerekes fc2d153ded fix: Expired session setCookie issue #379 2022-11-16 23:53:34 +01:00
KodeStar 4a7690147e Update readme.md 2022-11-15 21:29:39 +00:00
KodeStar 7d32e6edb0 Merge pull request #975 from keriati/feat/langit
feat: add missing IT translations
2022-11-15 18:33:58 +00:00
Attila Kerekes 3d1fc80d64 feat: add missing IT translations 2022-11-15 14:07:12 +01:00
KodeStar 8b0700e9ef Merge pull request #974 from keriati/feat/langderu
Add missing DE and RU translations
2022-11-15 11:56:29 +00:00
Attila Kerekes cc241f1bd6 feat: Add DE and RU translations 2022-11-15 12:51:34 +01:00
Attila Kerekes 810ee03965 test: improve lang test debug output 2022-11-15 11:09:57 +01:00
KodeStar 7809abb376 Merge pull request #956 from keriati/chore/php7432
chore: set min php version 7.4.32
2022-11-14 23:49:15 +00:00
Attila Kerekes 2f3b7e52db chore: set min php version 7.4.32 2022-11-14 23:34:32 +01:00
KodeStar fdd5a78eb8 Merge pull request #955 from keriati/fix/php74dep
fix: install dependencies on php 7.4
2022-11-14 20:42:41 +00:00
Attila Jozsef Kerekes 98401f20a2 reinstall dependencies on php 7.4 2022-11-14 21:39:27 +01:00
Chris Hunt 30663e4d5e Change php version 2022-11-14 20:28:46 +00:00
Chris Hunt 8972a11c0a SImplify action 2022-11-14 19:43:43 +00:00
Chris Hunt bf349eec7a Merge branch '2.x' of github.com:linuxserver/Heimdall into 2.x 2022-11-14 19:42:43 +00:00
Chris Hunt cb314a4fd9 Change action to pull request 2022-11-14 19:42:37 +00:00
KodeStar 1a78c8077a Merge pull request #954 from keriati/feat/lang-support
feat: Dynamically add all languages to db
2022-11-14 19:38:26 +00:00
Attila Kerekes a3c75b2dd4 fix: Fall back to english only if intl is missing. 2022-11-14 20:35:25 +01:00
Chris Hunt ee77d09c58 Update dependencies 2022-11-14 19:35:16 +00:00
Chris Hunt 0966639699 Add actions 2022-11-14 19:22:46 +00:00
Attila Jozsef Kerekes 153bb0e91d chore: update readme.md with intl dependency 2022-11-14 20:02:51 +01:00
Attila Kerekes 0c7a60a5c7 fix: Add missing hungarian translations
Improve some existing translation keys
2022-11-14 19:56:10 +01:00
Attila Kerekes edb51e56f1 feat: Dynamically add all languages to db
Add unit test to find missing translations
2022-11-14 19:31:07 +01:00
KodeStar 99193a578e Merge pull request #818 from everaldofilho/language-pt-br
Change: added language pt in configuration, translated validation for pt-br
2022-11-14 15:07:45 +00:00
KodeStar 49b5d57e14 Delete SettingsSeeder.php 2022-11-14 15:07:22 +00:00
KodeStar 716d5a2f69 Merge pull request #880 from Yoshi315161/patch-1
Update German Translation
2022-11-14 15:05:07 +00:00
KodeStar 62340b07a9 Merge pull request #854 from bsciretti/2.x
Lombard translation
2022-11-14 15:04:36 +00:00
KodeStar 2312e8b648 Merge pull request #953 from keriati/refactor/ideafixes
refactor: apply auto fixes from idea
2022-11-14 14:53:14 +00:00
Attila Kerekes b390a719e9 refactor: apply auto fixes from idea 2022-11-14 14:48:38 +01:00
KodeStar d4f7ad842c Merge pull request #951 from keriati/fix/950-env-db
fix: change how database type is checked #950
2022-11-14 09:57:49 +00:00
Attila Kerekes 5c2501c1a7 fix: change how database type is checked #950 2022-11-14 10:24:58 +01:00
KodeStar 198ffd7665 Merge pull request #949 from keriati/fix/554-private-app-icon
fix: Don't write existing icons to disk
2022-11-13 23:22:54 +00:00
Attila Kerekes 7cf9d46eb3 fix: Don't write existing icons to disk 2022-11-14 00:08:09 +01:00
KodeStar 66dbbc835e Update app.php 2022-11-13 22:08:41 +00:00
KodeStar 178e46ca05 Merge pull request #947 from keriati/chore/laravel8
upgrade to laravel 8.0
2022-11-13 21:54:08 +00:00
Attila Kerekes 27f58c0866 upgrade to laravel 8.0 2022-11-13 21:18:38 +01:00
KodeStar 43f894b58d Merge pull request #946 from keriati/chore/db-setup
Remove sqlite db file creation when mysql or pgsql is used
2022-11-13 15:12:18 +00:00
Attila Kerekes b83f24c6f3 remove sqlite db file creation when mysql or pgsql is used 2022-11-13 11:34:58 +01:00
KodeStar b22fbdbad1 Merge pull request #944 from keriati/fix/429
fix: adjust home tag index for mysql/mariadb in database seeder
2022-11-11 20:30:54 +00:00
Attila Kerekes 6552a8c061 fix: adjust user id for postgresql in database seeder 2022-11-11 20:28:57 +00:00
Attila Kerekes bcd6f72b72 fix: adjust home tag index for mysql/mariadb in database seeder 2022-11-11 19:22:50 +00:00
KodeStar d4fe699029 Merge pull request #941 from keriati/fix/886
fix: disable tooltips while reordering items
2022-11-07 16:35:40 +00:00
Attila Kerekes db8fa38665 fix: disable tooltips while reordering items 2022-11-07 16:32:19 +00:00
KodeStar df41de3a9a Update app.php 2022-11-07 15:43:08 +00:00
KodeStar b3070bc8f9 Merge pull request #940 from keriati/fix/appmodelcheck
fix: Add check to appModel so websites can be added again
2022-11-05 08:18:54 +00:00
Attila Kerekes 487cdc483c fix: Add check to appModel so websites can be added again 2022-11-05 07:02:20 +00:00
KodeStar dcc0f90b23 Merge pull request #912 from eekdood/tag-hyperlink-fix
fix links in tag list
2022-10-31 17:46:03 +00:00
KodeStar 75014c8269 Merge pull request #937 from keriati/privateAppSupport
Improve Private App support
2022-10-31 10:32:55 +00:00
Attila Kerekes 386e4c788b feat: add remove option to registerapp 2022-10-28 14:48:04 +00:00
Attila Kerekes fe0109494e feat: support the listing of private apps 2022-10-28 13:21:25 +00:00
Attila Kerekes 6907695c5d feat: support the listing of private apps 2022-10-28 09:55:16 +00:00
Attila Kerekes 533af2dc49 feat: support the listing of private apps 2022-10-27 21:57:23 +00:00
Eric Fawcett 1ac5eb6d23 fix links 2022-09-20 17:47:38 +00:00
Yoshi315161 ca557fcaf1 Update app.php
Add missing translations
2022-07-21 12:34:22 +02:00
Kode e2ba89c80e Update dependencies 2022-06-29 13:20:08 +01:00
Kode bbae811cd8 Update app.php 2022-06-29 13:19:02 +01:00
Kode 589aa73a15 Update app.php 2022-06-29 13:17:08 +01:00
Kode fc023401f5 remove heimdall specific xsrf token as clearly not working 2022-06-29 13:13:00 +01:00
Brian Sciretti 1a4b6da3ea Update app.php 2022-05-28 02:08:43 +02:00
Brian Sciretti 02b82dc740 Update app.php 2022-05-28 02:06:25 +02:00
Brian Sciretti 4e5129ab8f Update app.php 2022-05-28 01:58:34 +02:00
Brian Sciretti 62d9b0cc76 Create app.php 2022-05-28 01:56:47 +02:00
KodeStar 02dfed0bc7 Merge pull request #823 from tomyvi/patch-2
Disable SSL checks on API calls
2022-04-12 11:56:20 +01:00
KodeStar 71d356935f Merge pull request #822 from tomyvi/patch-1
Disable SSL checks on API calls
2022-04-12 11:56:04 +01:00
KodeStar e53bae2c69 Update app.php 2022-04-12 11:51:47 +01:00
KodeStar 3117b9c483 Don't try to autoload file when checking if a class exists 2022-04-12 11:48:03 +01:00
tomyvi c5817c9b2e Disable SSL checks on API calls 2022-04-07 10:34:07 +02:00
tomyvi c403e8b2df Disable SSL checks on API calls 2022-04-07 10:33:31 +02:00
Everaldo da costa filho 1ca403cc6e change: added language pt in configuration, translated validation for pt-br 2022-04-02 21:20:17 -03:00
Kode 758c174141 Only getApp if app found 2022-04-02 15:19:55 +01:00
KodeStar 9bc9c48e06 Localhost doesn't like the certificate so ignore the warning 2022-03-30 13:14:35 +01:00
KodeStar 9a6dd623db add tag_id to fillable to try and fix #810 2022-03-30 13:14:05 +01:00
Kode d1c6001fae update app version 2022-03-26 19:01:11 +00:00
Kode 74196a2bfa Check if class exists before checking if it's a search provider 2022-03-26 18:59:49 +00:00
Kode 724110dad2 Run ProcessApps when database version is bumped 2022-03-25 15:35:05 +00:00
KodeStar 3a043e0165 Update app.php 2022-03-25 15:02:35 +00:00
KodeStar ac37c60e1f Merge pull request #800 from xiazeyu/baidu
added Baidu and sort order by alphabet
2022-03-25 15:01:22 +00:00
S4kura0ne 937e576520 move tiles to the first 2022-03-25 01:00:07 +08:00
KodeStar 9086e9bd75 Merge pull request #794 from xiazeyu/2.x
Latest Chinese translation combined #789 and #617
2022-03-24 13:21:07 +00:00
s4kura0ne 4c90d8c87e added Baidu and sort order by alphabet 2022-03-24 09:25:30 +08:00
s4kura0ne a4cceb3ae0 remove Baidu in searchprovides.yaml 2022-03-24 09:20:46 +08:00
s4kura0ne 90c21d700d merged old 'zh' translation, improved translation 2022-03-24 00:33:36 +08:00
s4kura0ne 15b1f3234b added translation for baidu 2022-03-24 00:23:08 +08:00
s4kura0ne a116345d60 remove unused translation 2022-03-24 00:21:47 +08:00
s4kura0ne 2b4e6d372c remove changes in outdated SettingsSeeder.php 2022-03-24 00:19:43 +08:00
s4kura0ne d370f2c3ee finished chinese translation 2022-03-23 10:09:18 +08:00
S4kura0ne ade09f3b1c Merge pull request #3 from xiazeyu/master
https://github.com/xiazeyu/Heimdall/pull/2
2022-03-23 09:31:58 +08:00
s4kura0ne 80aa120086 solved Russian readme conflict 2022-03-23 09:28:39 +08:00
S4kura0ne a5cbcaca03 Merge pull request #1 from anerg2046/2.x
Merge Chinese translate from anerg2046
2022-03-23 09:24:33 +08:00
Kode f2bfc26c5e Update app.php 2022-03-21 21:37:44 +00:00
Kode 9f0abb6f4d Strip out invalid characters from tile background 2022-03-21 21:37:14 +00:00
KodeStar 9ca2078e7d Add app version to asset url to bust cache 2022-03-21 06:42:59 +00:00
罗翀 9e4a06999b Chinese translate
add baidu search
2022-03-21 00:53:28 +08:00
KodeStar 8b72ff0a0d Merge pull request #787 from KodeStar/2.x
Laravel shift and update defaults
2022-03-19 15:07:11 +00:00
Kode 51ddaccc16 Update defaults 2022-03-19 15:04:26 +00:00
KodeStar 4ff1a4aec4 Merge pull request #1 from KodeStar/shift-58933
Upgrade Checker
2022-03-19 14:27:44 +00:00
KodeStar d5bd614cc6 Update Application.php 2022-03-19 14:05:01 +00:00
Shift 0f498eca75 Remove explicit deleted_at date cast 2022-03-19 13:54:34 +00:00
Shift 3dac9828c8 Convert to new helper methods
Laravel 5 added several new helper functions, including:

- `view()`
- `response()`
- `redirect()`
- `config()`
- `abort()`

Review the [helpers][1] documentation for more details.

[1]: https://laravel.com/docs/5.0/helpers
2022-03-19 13:54:34 +00:00
Shift 297c2bb30f Shift bindings
PHP 5.5.9+ adds the new static `class` property which provides the fully qualified class name. This is preferred over using strings for class names since the `class` property references are checked by PHP.
2022-03-19 13:54:33 +00:00
Shift b1dc4d4a41 Apply Laravel coding style
Shift automatically applies the Laravel coding style - which uses the PSR-2 coding style as a base with some minor additions.

You may customize the adopted coding style by adding a [PHP CS Fixer][1] or [PHP CodeSniffer][2] config to your project root. Feel free to use [Shift's Laravel ruleset][3] to help you get started.

For more information on customizing the code style applied by Shift, [watch this short video][4].

[1]: https://github.com/FriendsOfPHP/PHP-CS-Fixer
[2]: https://github.com/squizlabs/PHP_CodeSniffer
[3]: https://gist.github.com/laravel-shift/cab527923ed2a109dda047b97d53c200
[4]: https://laravelshift.com/videos/shift-code-style
2022-03-19 13:54:32 +00:00
KodeStar 0fe6565346 Update app.php 2022-03-19 07:06:02 +00:00
KodeStar 85c68c1f46 Merge pull request #783 from xavier-GitHub76/2.x
Update app.php (French translations Fix) + version app (2.4.4)
2022-03-18 16:40:10 +00:00
Kode fc2191b8db Fix preview image 2022-03-18 16:36:05 +00:00
xavier-GitHub76 ee36a0cfae Update app.php
Update version app to 2.4.4
2022-03-18 14:32:15 +01:00
Kode a868a6cac1 Fix for creating tags not setting home dashboard as default 2022-03-18 10:10:00 +00:00
xavier-GitHub76 574756b236 Update app.php
French translate fix
2022-03-17 22:43:05 +01:00
Kode f9599079e5 Update app.php 2022-03-17 19:04:39 +00:00
Kode 10afffb71d Fix test button 2022-03-17 19:03:06 +00:00
KodeStar 79d53af339 Update app.php 2022-03-17 13:42:07 +00:00
KodeStar 8cc6a9cc63 Pull missing apps on update apps list 2022-03-17 13:41:50 +00:00
KodeStar 18001fbdd0 Get app regardless of if it's enhanced (might be a search provider) 2022-03-17 12:56:51 +00:00
KodeStar 398df75865 Merge branch '2.x' 2022-03-17 11:01:43 +00:00
KodeStar 41c2983312 Add warning about http being needed in url 2022-03-17 10:55:52 +00:00
KodeStar bd3b882b3a Squashed commit of the following:
commit e52bc9c6b2
Author: KodeStar <kodestar@gmail.com>
Date:   Thu Mar 17 10:23:15 2022 +0000

    Update supportedapps.json

commit a16233ee10
Author: KodeStar <kodestar@gmail.com>
Date:   Thu Mar 17 10:21:24 2022 +0000

    Fix for displaying svg icons correctly
2022-03-17 10:24:31 +00:00
KodeStar e52bc9c6b2 Update supportedapps.json 2022-03-17 10:23:15 +00:00
KodeStar a16233ee10 Fix for displaying svg icons correctly 2022-03-17 10:21:24 +00:00
Kode 5b177f1127 Merge branch '2.3' into 2.x 2022-03-16 20:16:04 +00:00
Kode f7633b6a5f Update mix-manifest.json 2022-03-16 20:15:22 +00:00
Kode d58e6cb560 Fix for #774 2022-03-16 20:13:16 +00:00
Kode 87fe9c1771 Move setLocale out of vie composer so it's available everywhere
Fixes #776
2022-03-16 20:07:43 +00:00
Kode 95d9e9edb9 Update app.php 2022-03-16 19:43:03 +00:00
Kode f7b2c51f9d Fix for old apps and icon selector 2022-03-16 19:41:01 +00:00
Kode 72c70e27f2 Remove refresh button for now as update code isn't ready yet 2022-03-16 18:39:47 +00:00
Kode becbe5ab96 More work on apps 2022-03-16 18:35:40 +00:00
Kode f5bce95808 Add missing lang string, fixed #773 2022-03-16 18:34:48 +00:00
KodeStar 1e2b807e05 Lots of work on apps 2022-03-16 15:49:44 +00:00
Kode ed3dbf2f14 More work on enhanced apps 2022-03-15 20:09:22 +00:00
Kode 1e35f83fed Changes to the tooltips 2022-03-15 19:08:10 +00:00
KodeStar 8499d100ff More work on apps 2022-03-15 18:19:01 +00:00
KodeStar bdeae6a722 Add missing files 2022-03-15 11:30:21 +00:00
Kode 4c63b66dbf Initial start of replacing apps list to use github generated list 2022-03-14 15:56:36 +00:00
Kode 371f52f92e Fixes #469 2022-03-14 12:25:20 +00:00
KodeStar 6daaf94974 Add details for search providers 2022-03-14 10:34:37 +00:00
Kode b8e5a22648 Update app.php 2022-03-14 07:56:41 +00:00
Kode e821edf01a Fix tags url
Fixes #763
2022-03-14 07:56:02 +00:00
Kode 188316df47 Fix search using incorrect value
fixes #764
2022-03-14 07:55:26 +00:00
Kode dd14ce8738 Update app.php 2022-03-13 19:31:30 +00:00
Kode d8e4053ce4 Merge branch 'master' of https://github.com/linuxserver/Heimdall 2022-03-13 19:30:27 +00:00
Kode f73b78f292 Add search providers as an editable yaml file + allow searching tiles 2022-03-13 19:30:24 +00:00
Kode 96ec1e0b08 Fixes #435 2022-03-13 16:13:36 +00:00
KodeStar 1904d9d910 Merge pull request #731 from linuxserver/dependabot/npm_and_yarn/lodash-4.17.21
Bump lodash from 4.17.11 to 4.17.21
2022-03-13 12:21:14 +00:00
KodeStar beb2851f4a Add noopener noreferrer to link 2022-03-13 09:20:58 +00:00
dependabot[bot] 645a0bc7fe Bump lodash from 4.17.11 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.21)

---
updated-dependencies:
- dependency-name: lodash
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-13 08:53:35 +00:00
Kode ef73918098 Update jquery 2022-03-12 23:27:11 +00:00
Kode 06a23c70af Should fix #379 2022-03-12 13:09:50 +00:00
Kode 323a6e17c7 Update database.php 2022-03-12 13:09:03 +00:00
Kode 0ca07c9698 Update composer.json 2022-03-12 12:55:42 +00:00
Kode fbb21d2885 update redis config 2022-03-12 12:54:15 +00:00
Kode b6741c8ff0 Update readme.md 2022-03-12 11:55:06 +00:00
Kode 7fdca02af7 Allow numbers in apps 2022-03-12 10:45:37 +00:00
Kode 271100c25b Update app.php 2022-03-12 09:38:19 +00:00
KodeStar cfecce6100 Create SECURITY.md 2022-03-12 09:37:36 +00:00
Kode d71341df7a Update app.blade.php 2022-03-11 23:20:18 +00:00
Kode ac851f5a02 Update manifest.json 2022-03-11 23:15:55 +00:00
Kode d2a8b7af48 Update app.blade.php 2022-03-11 23:10:47 +00:00
Kode 4d7a86fbf7 Update .env.example 2022-03-11 22:53:25 +00:00
KodeStar 87a3af3dbf Merge pull request #654 from NoahNxT/master
Translation correction NL
2022-03-11 20:30:16 +00:00
KodeStar 79c76b9f50 Merge pull request #648 from angrystar170/master
Add korean language files
2022-03-11 20:29:52 +00:00
KodeStar d7742df802 Merge pull request #644 from OllieJC/custom
Add custom CSS and JS settings
2022-03-11 20:28:28 +00:00
KodeStar 736222e173 Merge pull request #614 from risiko79/fix/#507
FIX: #507 Tag URLs do not respect APP_URL
2022-03-11 20:23:34 +00:00
KodeStar 047ffb0b55 Merge pull request #581 from rtzra/feature/RussianLanguage
added Russian translation
2022-03-11 20:22:32 +00:00
KodeStar 505d17cf57 Merge pull request #561 from mattycourtney/master
Updated link to letsencrypt image to point to SWAG
2022-03-11 20:22:10 +00:00
KodeStar 71c2da160d Merge pull request #522 from bence192/patch-3
Create pagination.php
2022-03-11 20:17:58 +00:00
KodeStar 3487b9e479 Merge pull request #521 from bence192/patch-4
Create passwords.php
2022-03-11 20:17:39 +00:00
KodeStar 7cdf315d78 Merge pull request #512 from Babasile/master
Lang fr update
2022-03-11 20:17:18 +00:00
KodeStar a1812a962c Merge pull request #506 from Forceu/master
Added info about updating
2022-03-11 20:16:49 +00:00
KodeStar b97cab34b0 Merge pull request #492 from crazychatting/lang_de_update
Lang de update
2022-03-11 20:16:24 +00:00
KodeStar 3426450b3c Merge pull request #475 from e7jonas/patch-1
Fixed some spelling errors.
2022-03-11 20:15:53 +00:00
KodeStar 75804f4624 Merge pull request #457 from skay-zhang/patch-1
Creat new language
2022-03-11 20:15:21 +00:00
KodeStar 23f757e06f Merge pull request #454 from shaohao/Fix-password-issue
Update FormBuilder.php
2022-03-11 20:13:51 +00:00
KodeStar fabd704bab Merge pull request #520 from bence192/patch-1
Hungarian lang added
2022-03-11 20:13:01 +00:00
KodeStar c16fbae963 Merge pull request #523 from bence192/patch-2
Create auth.php
2022-03-11 20:12:45 +00:00
Kode 348759b9ad update logging 2022-03-11 19:38:08 +00:00
Kode d961aeb19b more urls with double slashes 2022-03-11 19:06:29 +00:00
Kode 987a9c03b0 fix for double slash on views 2022-03-11 19:03:03 +00:00
Kode d17fb04983 fix fa in pseudo classes 2022-03-11 10:40:47 +00:00
Kode b07918d751 changes to lv7 to maybe fix the symlink issues? 2022-03-11 10:07:42 +00:00
KodeStar f9a19fce91 Update to laravel 7 2022-03-10 11:54:29 +00:00
Noah Gillard 1b71e80d52 Translation correction NL 2022-02-08 22:58:07 +01:00
angrystar170 9fac11cb77 Update passwords.php 2022-01-23 23:30:53 +09:00
angrystar170 a94fb108fa Add korean language files 2022-01-23 23:24:54 +09:00
OllieJC f51893b2d6 Use pre for textarea settings 2022-01-22 13:30:50 +00:00
OllieJC a4b32dcafd Add textarea option 2022-01-18 22:59:40 +00:00
OllieJC 862f870303 Add settings.advanced/custom_css/custom_js 2022-01-18 22:42:53 +00:00
OllieJC 869cfd34ab Add custom CSS and JS options 2022-01-18 22:40:09 +00:00
OllieJC 9e67b7c72e Add custom_css and custom_js advance settings 2022-01-18 22:33:21 +00:00
risiko79 a5b7f10809 FIX: #507 Tag URLs do not respect APP_URL
see https://github.com/linuxserver/Heimdall/issues/507
2021-08-29 19:58:50 +02:00
Evgeny Cheremnykh 698665176c added Russian translation 2021-05-01 18:56:27 +03:00
Matty Courtney 1bd65222e4 Updated link to letsencrypt image to point to SWAG 2021-02-21 10:13:48 +08:00
KodeStar 61a5a1a8b0 Merge pull request #530 from jhaveDK/master
Danish translation
2020-12-07 12:39:14 +00:00
Jesper Have e51cdd7f7a Danish translation update 2020-12-06 18:58:05 +01:00
Jesper Have a626222ff3 Opdateret 2020-12-06 18:50:32 +01:00
Jesper Have af68a45a3d . 2020-12-06 18:40:37 +01:00
Jesper Have 7969df9344 Danish translation 2020-12-06 16:36:55 +01:00
bence192 67bc656325 Create passwords.php 2020-11-29 20:02:49 +01:00
bence192 b5baf0d606 Create pagination.php 2020-11-29 20:01:48 +01:00
bence192 32964e9045 Create auth.php 2020-11-29 20:00:29 +01:00
bence192 27a667675a Create app.php 2020-11-29 19:57:27 +01:00
Babasile 9a221f47bf Lang fr update 2020-10-24 11:19:57 +02:00
Marc Ole Bulling 8d2fbc5599 Added info about updating 2020-09-25 13:37:31 +02:00
Robin Loose 84ebe6587d added admin-account-needed in language 2020-08-03 00:45:36 +02:00
Robin Loose 89a71a18da updated and corrected language de 2020-08-01 02:12:01 +02:00
e7jonas 05dd7a090b Fixed some spelling errors. 2020-05-14 13:40:18 +01:00
KodeStar 3a9bdd2c43 Merge pull request #458 from AndyTempel/slovenian-translation
added Slovenian translation
2020-03-22 19:20:42 +00:00
AndrazKorenc 0292e9976e added Slovenian translation 2020-03-15 09:50:17 +01:00
sKai 365de9512f Creat new language
Creat simplified chinese , apply to china
2020-03-09 20:57:31 +08:00
Shao Hao e857023903 Update FormBuilder.php
Make she the value is passed to password input.
2020-03-03 14:11:51 +08:00
KodeStar c058e1a452 Merge pull request #446 from vinanrra/patch-1
Update app.php
2020-02-10 09:10:34 +00:00
vinanrra 7f42967b67 Update app.php
Added Jackett
2020-02-10 09:58:26 +01:00
KodeStar 994961de54 Merge pull request #438 from LeoShivas/patch-1
Make background image relative
2020-02-10 08:57:53 +00:00
KodeStar 049d20536c Merge pull request #397 from scottt732/search-app-fixes
Improved custom search handling
2020-02-10 08:56:29 +00:00
LeoShivas c9c8171a52 Make background image relative
I use Heimdall in a subfolder so that I access to it threw www.mydomain.com/heimdall/.
Custom background image doesn't load because Heimdall generate an absolute link (ie : www.mydomain.com/storage/backgrounds/mycustomimage.jpeg).
The link expected is www.mydomain.com/heimdall/storage/backgrounds/mycustomimage.jpeg.
To fix this, you have to make the path relative by replace in the code "/storage" by "storage" or "./storage".
2020-02-04 11:41:53 +01:00
KodeStar e7a6ac5a75 Merge pull request #412 from vincentbitter/remote_user-login
Fix auto-login using REMOTE_USER variable
2020-01-06 08:44:06 +00:00
KodeStar 3a372107ba Merge pull request #405 from vorpalhex/feature/#376/upload_icon_text
Update 'upload a file' to 'upload an icon'
2020-01-06 08:42:29 +00:00
KodeStar 493374de65 Merge pull request #404 from panigrc/feature-greek-translation
Greek translation
2020-01-06 08:40:49 +00:00
Vincent Bitter c43fc929f7 Do not try to login if user was not found 2019-11-15 21:30:10 +01:00
Vincent Bitter 895d5f2ebe Check if variables are empty since they always exist 2019-11-15 21:29:15 +01:00
VorpalHex cb4768e2cf Update 'upload a file' to 'upload an icon' 2019-10-12 18:11:23 -05:00
panigrc 6ca4bfb9ee Add greek language to the SettingsSeeder 2019-10-06 14:44:18 +02:00
panigrc 1f7de03d90 Add greek language files 2019-10-06 14:43:34 +02:00
Scott Holodak 861d287750 Improved custom search handling 2019-09-11 13:42:33 -04:00
KodeStar e2cc32fd0a Merge pull request #392 from auanasgheps/patch-1
Update Italian app.php
2019-08-29 14:58:19 +01:00
KodeStar 762df25c45 Merge pull request #393 from auanasgheps/patch-2
Create Italian auth.php
2019-08-29 14:56:39 +01:00
KodeStar 0b114e7dc1 Merge pull request #394 from auanasgheps/patch-3
Create pagination.php
2019-08-29 14:56:13 +01:00
Oliver Cervera 981876c43c Create pagination.php
File was missing.
Translated.
2019-08-28 18:56:46 +02:00
Oliver Cervera 7fe9c477e3 Create Italian auth.php
File was missing.
Translated.
2019-08-28 18:55:56 +02:00
Oliver Cervera 5b3a7f4e02 Update Italian app.php
- Added and translated missing strings
- Reworked existing strings
2019-08-28 18:53:15 +02:00
Kode 60e20c4023 fix route typo when restoring trash 2019-07-14 09:46:10 +01:00
Chris 9e1bb6c075 Update composer min php version 2019-07-11 15:01:07 +01:00
KodeStar 9c05d0d803 Update readme.md 2019-07-03 12:59:27 +01:00
KodeStar 3d1393c0b3 Merge pull request #370 from scodx/patch-1
some minor spanish translations fixes
2019-06-25 08:27:46 +01:00
KodeStar 9914fdb111 Merge pull request #373 from alxlaxv/patch-1
New translation for new SearchInterface
2019-06-25 08:27:07 +01:00
alxlaxv 9b24c9f1b5 New translation for new SearchInterface 2019-06-24 23:36:19 +02:00
Oscar Sánchez 81b8c646d2 some minor spanish translations fixes 2019-06-24 12:15:55 -05:00
Chris f734832e0f remove url() from around app link 2019-06-24 08:38:50 +01:00
KodeStar 1bde11b30b Update readme.md 2019-06-23 18:42:51 +01:00
Kode 9e80c3fe40 2.2.2 hasn't been released yet 2019-06-22 17:05:05 +01:00
Kode f272dd13ce Fix for tags not showing 2019-06-22 17:03:01 +01:00
Chris 9f26de89a4 add remember user and missing check icon 2019-06-19 12:11:55 +01:00
Chris e23964ebad set session and remember user when logged in via remote means 2019-06-19 12:01:17 +01:00
Chris d0584ac941 Update app version ready for next release 2019-06-19 11:49:17 +01:00
Chris 1410c41f48 Add REMOTE_USER auth 2019-06-19 11:32:41 +01:00
Chris 5f524563cf Merge branch 'master' of https://github.com/linuxserver/Heimdall 2019-06-18 13:41:06 +01:00
Chris 3d181623c3 fix small issue 2019-06-18 13:41:02 +01:00
KodeStar 1919dbfd96 Update readme.md 2019-06-18 13:22:14 +01:00
Chris 8e1615ac5f fix search tab behaviour and tag link in subfolder 2019-06-18 12:25:05 +01:00
Chris 7966f07fdb update scripts + add home dash as default tag 2019-06-18 11:51:07 +01:00
Chris ac8fe7012b remove false from routes 2019-06-18 10:51:51 +01:00
Chris 51d89dae82 Possible fix for folder based RP 2019-06-18 08:46:57 +01:00
KodeStar ef21ac0fe7 Merge pull request #359 from pahakalle/patch-1
Fix typo in Finnish translation
2019-06-13 19:10:04 +01:00
KodeStar 02697687cc Merge pull request #364 from linuxserver/hometags
Allow apps to be tagged to the homepage
2019-06-13 19:09:19 +01:00
Kode fd2f7f27a6 Make home tag translatable 2019-06-13 19:07:38 +01:00
Kode 21b1ef5e4b fix pin toggle and get homepage dash working 2019-06-13 18:59:01 +01:00
Chris e452d3b5f6 add seed 2019-06-13 15:57:54 +01:00
Chris 1419882455 initial 2019-06-13 15:40:26 +01:00
KodeStar d79202ed87 Update readme.md 2019-06-12 11:02:19 +01:00
KodeStar aa05c947a9 Remove url validation 2019-06-11 14:11:13 +01:00
Chris eb69ea05fa Update package-lock.json 2019-06-11 12:35:48 +01:00
Chris 244b0998f9 update bootstrap-sass 2019-06-11 12:34:28 +01:00
Chris 1f608b1c21 Update composer dependencies 2019-06-11 12:29:32 +01:00
Chris 7d6df3843b update node deps 2019-06-11 12:20:04 +01:00
Kalle Laine dd4b94ba71 Fix typo in Finnish translation 2019-06-11 14:18:08 +03:00
Chris edb9397a47 Add startpage back in 2019-06-11 12:02:18 +01:00
KodeStar 66815a8487 readd startpage 2019-06-11 11:45:13 +01:00
KodeStar 5ee19bceb6 Merge pull request #357 from Agurato/master
Add Qwant as search engine
2019-06-11 11:32:31 +01:00
KodeStar deba7d0279 Merge branch 'master' into master 2019-06-11 11:32:11 +01:00
KodeStar 38e93d33f1 remove startpage 2019-06-11 11:30:33 +01:00
KodeStar f182f305fc remove startpage 2019-06-11 11:30:17 +01:00
KodeStar 03c675ed57 remove startpage 2019-06-11 11:29:54 +01:00
KodeStar 3916b2d4dc remove startpage 2019-06-11 11:29:38 +01:00
KodeStar 698a7a9bbc remove startpage 2019-06-11 11:28:07 +01:00
KodeStar 9954d1b6bf Merge pull request #342 from dmbekker/master
Dutch Translation update
2019-06-11 11:04:52 +01:00
KodeStar a98b981efe Merge pull request #348 from Aerion/toggle-livestats-refresh-active-tab
turn off livestats update when the tab isn't active
2019-06-11 11:03:14 +01:00
KodeStar dd2ca62eaf Add private subnets to trusted proxies for reverse proxy use 2019-06-11 11:00:44 +01:00
Agurato dc2a42ad6d Add Qwant as search engine 2019-06-08 13:02:12 +02:00
Guillaume Taquet Gasperini b5b25458db turn off livestats update when the tab isn't active
Fixes #330: Turning off the updating when the tab isn't active

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

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

Minor language-issues fixed
- Changed 'Toepassing' to 'Applicatie' (Both Acronyms were used)
- Changed 'Reeks' to 'Tekenreeks'
2019-04-22 16:38:07 +02:00
Kode 4980bfab12 fix not protocol breaking adding for some reason 2019-03-30 23:10:00 +00:00
Chris 63e0d07d50 remove unused storeSearchProvider 2019-02-04 08:51:49 +00:00
Kode 2b76520e3a change version number and update lang value 2019-01-18 18:42:17 +00:00
Kode 6631b8a23b update dev npm deps 2019-01-18 18:37:51 +00:00
Kode 04d34017c1 Composer deps update 2019-01-18 18:33:28 +00:00
KodeStar 6dcbcb452e Merge pull request #316 from linuxserver/search-enhancements
Search enhancements
2019-01-18 18:27:19 +00:00
Kode 7c74b3bb13 update manifest 2019-01-18 18:26:07 +00:00
Kode b76a9ee118 Merge branch 'master' into search-enhancements 2019-01-18 18:25:00 +00:00
Kode 40da649b10 Changes to search 2019-01-18 18:21:44 +00:00
Chris cd64d762e7 additions to search 2019-01-18 15:21:50 +00:00
Chris b91c52820a replace fontawesome with js version 2019-01-18 14:07:17 +00:00
KodeStar 1204ffd306 Update app.php 2019-01-16 17:24:31 +00:00
Chris 978267dd14 Update Search.php 2019-01-16 15:00:42 +00:00
Chris f935fd94c6 initial work on seach changes 2019-01-16 14:47:32 +00:00
Chris fb2428e21b fixes #109 2019-01-16 14:20:33 +00:00
Chris e36a126c01 fixes #286 2019-01-16 12:02:38 +00:00
Chris b186978624 fixes #299 2019-01-16 11:39:39 +00:00
Chris 49773d7654 Fixes #302 2019-01-16 10:37:25 +00:00
Chris 53c03751a1 ignore supported apps folder 2019-01-16 10:35:55 +00:00
Chris 9d2908449a Merge branch 'master' of https://github.com/linuxserver/Heimdall 2019-01-15 15:27:29 +00:00
KodeStar caf92bcf6d check for gitignore in public storage and symlink if missing 2019-01-15 15:25:53 +00:00
KodeStar cbef469e02 Update AppServiceProvider.php 2019-01-15 15:23:10 +00:00
Chris a6bfc1d76c ignore .env 2019-01-15 14:27:19 +00:00
KodeStar 0446a74de9 Merge pull request #308 from openseabrus/master
Portuguese (PT-PT) translation.
2019-01-07 08:27:46 +00:00
KodeStar 1a14079b39 add APP_KEY 2019-01-04 14:00:15 +00:00
KodeStar d9c1919ddc Merge pull request #313 from linuxserver/Fix-default-appkey
Update .env.example
2019-01-04 11:52:07 +00:00
KodeStar f42ac0c5ba if .env is missing copy example and generate key 2019-01-04 10:38:07 +00:00
KodeStar c073929895 Delete .env 2019-01-04 10:36:04 +00:00
KodeStar 02df7844a4 Update .env.example 2019-01-04 10:34:12 +00:00
l1904l e43aba0111 Portuguese (PT-PT) translation. 2018-12-26 15:56:34 +00:00
Kode ac7446ffe6 update version 2018-11-14 18:14:23 +00:00
Kode e45d3ca6ec remove apps 2018-11-14 18:10:55 +00:00
Chris 603550a453 autofocus password 2018-11-13 12:38:19 +00:00
Kode 4463ef4a9a add right to livestats 2018-11-10 21:37:21 +00:00
Kode 49b8dc0079 Update version number 2018-11-10 18:05:19 +00:00
Kode ab83c3a551 Small changes
Copy icon if missing on new app add.
Change order of create symlink and load apps
2018-11-09 23:07:01 +00:00
Chris 98c6093674 Check url isn't missing when testing 2018-11-07 11:24:26 +00:00
Chris e1f51521bf Update version 2018-11-07 08:57:18 +00:00
Chris e85bc98dcc Fix ProcessJobs not working if folder is empty but table isn't 2018-11-07 08:54:00 +00:00
Chris 4ba52baa5c Remove old code from tags form 2018-11-06 15:44:09 +00:00
Chris a82077b4de Update version 2018-11-06 15:15:55 +00:00
Chris b8f04f3d11 Fix preview and config not retaining values 2018-11-06 14:52:45 +00:00
Chris e91f65f833 Ignore copy errors 2018-11-06 11:53:25 +00:00
Chris 575ab9be2d update version 2018-11-06 11:29:33 +00:00
Chris 64071bb60f Add migration and change path 2018-11-06 11:28:22 +00:00
Chris 44621a1a61 Change version + replace supported with icons 2018-11-06 09:48:03 +00:00
Chris c56043e1f9 Add default value to null rather than not set 2018-11-06 09:20:53 +00:00
Kode e8e4cbfd41 Change sort order for applist 2018-11-05 21:03:42 +00:00
Kode 4b2bbe0614 update version 2018-11-05 19:03:26 +00:00
Kode 9adfa14e62 fix light colour livestats 2018-11-05 19:02:19 +00:00
Kode 067f82b632 fix copy failing 2018-11-05 18:47:56 +00:00
Chris e24e7979be update version number 2018-11-05 15:28:17 +00:00
Chris c7c2b6e6f2 change location of icons so it's persistant 2018-11-05 15:27:29 +00:00
Chris 96798963d6 Update register app to allow all 2018-11-05 12:31:43 +00:00
Chris 75508a81ef Update version 2018-11-05 09:44:11 +00:00
Chris f7f4efadb7 Change html comment to php comment 2018-11-05 09:40:44 +00:00
Kode 4cc80b98db inconsequential update to try and fix broken build 2018-11-04 22:33:44 +00:00
Kode b07efaa7d0 Merge branch 'master' of https://github.com/linuxserver/ifai 2018-11-04 21:43:16 +00:00
Kode 7ba8ea6dd4 die if missing php-zip 2018-11-04 21:43:12 +00:00
KodeStar 479461821f Update readme to use shields for apps 2018-11-04 18:30:27 +00:00
Kode d25aea38fb update app version to correct number 2018-11-04 16:26:08 +00:00
Kode 0067502b37 Initial application process 2018-11-04 16:20:12 +00:00
Kode d321af6085 update app type via dropdown 2018-11-04 16:03:55 +00:00
Kode b30f1730e4 order applist in dropdown by name 2018-11-04 15:27:57 +00:00
KodeStar 7a02986982 Merge pull request #282 from linuxserver/2.1.0
2.1.0
2018-11-04 14:54:13 +00:00
Kode a4107cba25 Fix tags 2018-11-04 14:33:39 +00:00
KodeStar ba187aa345 Merge pull request #281 from BiohZn/master
Finnish and Swedish translation
2018-11-04 13:24:39 +00:00
Conny Sjöblom b400cef734 Update Finnish translation 2018-11-04 15:20:53 +02:00
Conny Sjöblom 03b72b8d2e Update Swedish translation 2018-11-04 15:19:48 +02:00
Conny Sjöblom 0c7e20dee0 Finnish translation 2018-11-04 15:06:38 +02:00
Conny Sjöblom aa42c71a06 Swedish translation 2018-11-04 14:53:20 +02:00
Kode 041c0b81a3 updates 2018-11-01 08:55:21 +00:00
Chris fe6776ee9d Some changes to SupportedApps.php 2018-10-31 15:42:34 +00:00
Chris 125b9f4160 Some fixes and changes to run css 2018-10-30 14:58:45 +00:00
Kode 488fee7b4b fixes 2018-10-29 19:43:08 +00:00
Kode 4fba596909 add enable button 2018-10-29 19:01:25 +00:00
Chris af04781830 remove class name from icon name 2018-10-29 15:24:47 +00:00
Chris 2507cda94c Normalise classname to remove non alpha chars 2018-10-29 15:12:47 +00:00
Kode 21c1401859 fixes 2018-10-28 20:41:46 +00:00
Kode 4351f55225 updates 2018-10-28 10:59:59 +00:00
Kode d1956c4e88 updates 2018-10-26 19:55:40 +01:00
Chris d76d056ed1 changes 2018-10-25 14:42:14 +01:00
Chris e081cc31a2 small update 2018-10-25 12:14:18 +01:00
Chris 88153b0e32 Redo app form page 2018-10-25 09:44:40 +01:00
Chris 2b2d51cb6f changes 2018-10-23 15:53:56 +01:00
Kode abd8c7227d Merge branch 'master' into 2.1.0 2018-10-21 14:09:39 +01:00
Kode 50d6dc7b71 change to find first user with public front 2018-10-21 13:47:03 +01:00
Kode b4a1ecc305 update EnhancedApps 2018-10-21 13:32:58 +01:00
Kode 540bead0db Fix test button 2018-10-21 13:23:23 +01:00
Kode 6e9f25d680 Fixes 2018-10-21 12:39:12 +01:00
Kode 268afe7006 Get working 2018-10-21 00:17:36 +01:00
Kode 7b9d3f0ec6 replace text input with password input and swap input for Form::password 2018-10-20 20:08:36 +01:00
Kode de116030bc change password field on nzbget blade 2018-10-20 13:55:56 +01:00
Chris 4fb59385b7 work on applications 2018-10-19 15:10:05 +01:00
Chris 40d6808067 Merge branch 'master' into 2.1.0 2018-10-19 08:47:58 +01:00
Chris 3572bd8068 update laravel requirements 2018-10-19 08:13:00 +01:00
Chris 380a0e8623 changes 2018-10-18 15:59:38 +01:00
Chris 4f6a0cb7c6 Move current apps out 2018-10-18 10:37:18 +01:00
Chris 56cce8233b Replace svgs 2018-10-18 09:53:13 +01:00
KodeStar af8afcf931 Update readme.md 2018-10-18 08:41:13 +01:00
Kode 7f997d60cd Update version 2018-10-17 22:17:27 +01:00
Kode d45f5a805e wrap asset() round root assets 2018-10-17 22:16:23 +01:00
Kode fc9c624509 update donate link 2018-10-17 19:53:27 +01:00
KodeStar 33372509eb Merge pull request #266 from linuxserver/Multi-user
v2.0.0
2018-10-17 18:41:51 +01:00
Kode bca0b02925 update version number to 2.0.0 2018-10-17 18:37:22 +01:00
Kode 24995135e6 update dependency 2018-10-17 18:35:09 +01:00
Chris c1e4103edd Add support for pinned tabs in safari (potentially) 2018-10-17 11:31:42 +01:00
Chris 6eda423156 Added mailcow, webmin and virtualmin 2018-10-17 10:27:05 +01:00
Chris 7dc72d3519 style switch user icon same as user list page 2018-10-17 09:59:39 +01:00
Chris 907c22179b rearrange order 2018-10-17 09:49:23 +01:00
Chris cdafbab7b1 possible fix for basic auth 2018-10-17 09:41:20 +01:00
Chris e0064504e7 Merge branch 'master' into Multi-user 2018-10-17 08:39:10 +01:00
KodeStar b22117dd01 Add missing override url 2018-10-17 08:36:06 +01:00
KodeStar c88efa8dbc Fix broken config 2018-10-17 08:33:42 +01:00
Kode 7d060ff803 Merge branch 'master' into Multi-user 2018-10-16 19:05:04 +01:00
KodeStar d5f8b6aae0 Merge pull request #257 from ABOTlegacy/Monica
Monica
2018-10-16 19:01:52 +01:00
KodeStar 2416993c5e Merge pull request #258 from ABOTlegacy/Bitwarden
Added Bitwarden to foundation apps.
2018-10-16 19:01:10 +01:00
KodeStar 494165a03a Merge pull request #259 from dgw/readme-polishing
readme: formatting, typo fixes, spit & polish
2018-10-16 19:00:20 +01:00
KodeStar 88e607533c Change to multiarch docker 2018-10-16 18:58:33 +01:00
KodeStar 035d2f9209 Merge pull request #265 from alxlaxv/master
Updating french translations
2018-10-16 18:53:05 +01:00
alxlaxv 53903daa87 Update app.php 2018-10-16 19:32:37 +02:00
alxlaxv 567994be1a Merge pull request #1 from alxlaxv/patch-1
Update app.php
2018-10-16 19:21:03 +02:00
alxlaxv 32fa27337a Update app.php
French translations improved
2018-10-16 17:27:53 +02:00
Chris e1bb7646ac small fixes 2018-10-16 15:44:23 +01:00
KodeStar 108b636def Merge pull request #260 from keranoz/patch-1
Update app.php
2018-10-16 15:20:03 +01:00
keranoz 4abb14bf54 Update app.php
Improved some translations that seemed machine translated and added missing entries.
2018-10-16 14:08:19 +00:00
Chris f8f96593c1 Update userseeder 2018-10-16 15:04:53 +01:00
Chris cb21b0f8f1 add basic auth support and put username in switch user section 2018-10-16 14:57:21 +01:00
Chris 4c8c5fa27f Replace name for username in list view 2018-10-16 14:28:12 +01:00
Chris 6093119dde Change name to username 2018-10-16 14:14:14 +01:00
Chris 15755a3fd1 move all assets locally 2018-10-16 12:58:56 +01:00
Chris 0213c81e0d muted the switch user button until it's hovered over 2018-10-16 11:24:21 +01:00
Chris c47f296f17 Change location of switch user 2018-10-16 10:59:35 +01:00
Chris 75133474f7 Added support and br translation 2018-10-16 09:33:24 +01:00
Kode ddbe171f3a minor change to user avatar size to cut down whitespace a bit 2018-10-15 20:43:27 +01:00
Kode 12e109f82c add unique validation for name and don't fail validation when already the owner of data 2018-10-15 20:34:21 +01:00
Kode e095589172 fix validation on email 2018-10-15 20:25:21 +01:00
Kode d0293c785b Get settings working and autologin 2018-10-15 19:56:45 +01:00
Chris aa351e31bf trying to get session settings to work 2018-10-15 16:00:20 +01:00
Chris aceed3d13b fix settings edit not working 2018-10-15 14:35:14 +01:00
Chris 10b70d4a09 changes 2018-10-15 13:02:16 +01:00
Chris cb9e014cf3 Autologin togles working 2018-10-15 09:52:36 +01:00
Chris 99017d834e Merge branch 'master' into Multi-user 2018-10-15 09:08:42 +01:00
Chris fb73f5ca24 Attempt to allow changing root URL 2018-10-15 09:03:54 +01:00
dgw 4369f1aeda readme: formatting, typo fixes, spit & polish
Includes making inter-sentence spaces consistent, `pre-formatting`
values where it seemed appropriate, correcting typos, and other
miscellaneous niceties to help the readme make a good first impression.
2018-10-14 17:38:05 -05:00
Kode 6501aacb1b update to laravel 5.7 and try getting autologin saved 2018-10-14 20:50:32 +01:00
Kode c3da17befc User specific items 2018-10-14 17:27:28 +01:00
Kode 46bb073001 Working on multi user 2018-10-14 16:17:55 +01:00
Kode e8b47776ce Merge branch 'master' into Multi-user 2018-10-13 15:52:42 +01:00
Kode 6941fd3e2d Fixes for folder installs 2018-10-13 15:32:15 +01:00
Andy Bottom f5937879df Added Bitwarden to foundation apps. 2018-10-12 14:59:19 -05:00
Andy Bottom 93877b7025 Fixed spaces vs tabs. 2018-10-12 14:35:59 -05:00
Andy Bottom 6612631cc3 Added Monica to foundation apps. 2018-10-12 14:32:34 -05:00
Chris 30c3079a90 added setting user many to many relationship 2018-10-12 15:10:40 +01:00
Chris e86e681c53 Initial commit of multi user 2018-10-12 14:57:46 +01:00
Chris 48f72652da Merge branch 'master' of https://github.com/linuxserver/Heimdall 2018-10-12 11:33:43 +01:00
Chris a3f7bafedb change css and js links to asset links 2018-10-12 11:33:39 +01:00
KodeStar d30d610042 Update readme.md 2018-10-12 11:13:51 +01:00
Kode 882e406266 remove var dump 2018-09-30 15:47:44 +01:00
Kode 45d421256c Update readme and remove addition to .env as not necessary for everyone 2018-09-30 11:52:54 +01:00
Kode a2f20fc18f allow forcing https for reverse proxy use 2018-09-30 11:45:08 +01:00
KodeStar 988364cb7c Merge pull request #246 from CHBMB/Bazarr
Bazarr
2018-09-13 13:40:12 +01:00
chbmb a3816ed8a1 Update readme.md 2018-09-13 08:44:05 +01:00
chbmb d48805ee2c Update Item.php 2018-09-13 08:43:21 +01:00
chbmb e820b81259 Add files via upload 2018-09-13 08:42:26 +01:00
chbmb 8b1046ce17 Create Bazarr.php 2018-09-13 08:41:57 +01:00
chbmb a138b65842 Merge pull request #1 from linuxserver/master
Rebase
2018-09-13 08:36:04 +01:00
KodeStar c344de3f04 Merge pull request #244 from CHBMB/patch-3
Better logo and colour scheme for Unraid
2018-09-03 12:55:25 +01:00
chbmb 427659a897 Add files via upload 2018-08-30 11:42:02 +01:00
chbmb a6543970e7 Update Unraid.php 2018-08-30 11:40:55 +01:00
KodeStar 399ea088dc Merge pull request #243 from CHBMB/patch-2
Update Unraid Icon & Colour Scheme
2018-08-29 17:37:18 +01:00
chbmb 3f87833e52 Add files via upload 2018-08-29 15:56:02 +01:00
chbmb 6dcb77023c Update Unraid colour in line with new logo 2018-08-29 15:54:19 +01:00
KodeStar 4d37135bdf Merge pull request #233 from CHBMB/freshrss
Freshrss
2018-07-19 21:49:13 +01:00
KodeStar d109047fa5 Merge pull request #232 from CHBMB/bookstack
BookStack
2018-07-19 21:48:51 +01:00
KodeStar b6112501e2 Merge pull request #231 from CHBMB/tvheadend
Add TVheadend to foundation apps
2018-07-19 21:47:16 +01:00
KodeStar 0663e236b4 Merge pull request #230 from CHBMB/patch-1
PfSense update
2018-07-19 21:46:30 +01:00
chbmb f25cea1749 Add FreshRSS logo 2018-07-19 19:57:26 +01:00
chbmb 03e16415aa Create FreshRSS.php 2018-07-19 19:56:39 +01:00
chbmb 9a55e05943 Add FreshRSS 2018-07-19 19:55:21 +01:00
chbmb c06fa4eab6 Update readme.md 2018-07-19 19:54:24 +01:00
chbmb 5575b082ea Update and rename Bookstack.php to BookStack.php 2018-07-18 21:18:19 +01:00
chbmb 1c6da858bc Update Item.php 2018-07-18 21:17:33 +01:00
chbmb f0a14641c1 Update readme.md 2018-07-18 21:17:03 +01:00
chbmb c6d07cd8a4 Update readme.md 2018-07-18 21:15:48 +01:00
chbmb df9f07faf4 Add files via upload 2018-07-18 21:15:14 +01:00
chbmb a3dcc278d7 Create Bookstack.php 2018-07-18 21:13:53 +01:00
chbmb 5de473fa44 Add bookstack 2018-07-18 21:00:36 +01:00
chbmb 763c1545a6 Update readme.md 2018-07-18 20:47:25 +01:00
chbmb ecde7a0f32 Update readme.md 2018-07-18 20:46:27 +01:00
chbmb 7fc5e0abb5 Add TVheadend 2018-07-18 20:45:40 +01:00
chbmb 06a655ac0c Create TVheadend.php 2018-07-18 20:43:36 +01:00
chbmb 875ddaa834 Add TVheadend logo 2018-07-18 20:30:30 +01:00
chbmb 030fccbb50 Change pfSense logo to 250 x 250 2018-07-18 16:06:47 +01:00
chbmb 908f70d90a Update pfsense logo to new version 2018-07-18 16:02:11 +01:00
chbmb 8724ced531 pfSense New Logo/Colour 2018-07-18 16:00:30 +01:00
KodeStar aa1a3a95ca Merge pull request #227 from MindTooth/update_package_json
Cleaned up package.json
2018-07-09 12:54:35 +01:00
Birger J. Nordølum 0767dc075e Cleaned up package.json
And updated some dependencies.
2018-07-09 13:08:02 +02:00
KodeStar c8a6c89036 Merge pull request #225 from linuxserver/revert-217-fix_reported_errors
Revert "Changed from variable assignment to comparison"
2018-06-22 11:37:34 +01:00
KodeStar cafe386cc4 Revert "Changed from variable assignment to comparison" 2018-06-22 11:36:58 +01:00
KodeStar 0184c9695b Merge pull request #218 from MindTooth/new_view
Cleaned of variable population in scripts.blade.php
2018-06-22 11:15:26 +01:00
KodeStar 19536edf28 Merge pull request #222 from MindTooth/add_info_to_files
Clarified _rune.scss file
2018-06-22 11:09:37 +01:00
KodeStar 045e4a20fa Merge pull request #213 from MindTooth/tweak_readme
Readme style tweaks
2018-06-22 11:09:13 +01:00
Birger J. Nordølum 6cb8487a52 Clarified _rune.scss file 2018-06-21 17:41:04 +02:00
Birger J. Nordølum c39e9aa13f Trim variable in script view for create/edit.blade
Removed the large logic to populate the varible in page specific view.
2018-06-20 23:42:31 +02:00
KodeStar 0203440b06 Merge pull request #217 from MindTooth/fix_reported_errors
Changed from variable assignment to comparison
2018-06-20 21:20:12 +01:00
Birger J. Nordølum 7f7bf60456 Changed from variable assignment to comparison
Issue reported by CodeFactor:
https://www.codefactor.io/repository/github/mindtooth/heimdall/file/master/database/seeds/SettingsSeeder.php
2018-06-18 00:34:05 +02:00
KodeStar e8673634bc Merge pull request #216 from MindTooth/fix_error
Fixes compile error with webpack.mix.js and app.js
2018-06-17 22:33:33 +01:00
KodeStar 53e52c93ee Merge pull request #214 from MindTooth/up_node_packages
Up packages.json to reflect upstream Laravel 5.5
2018-06-17 22:32:17 +01:00
Birger J. Nordølum c239c0ea5a Compiled new source for fix 2018-06-17 23:19:45 +02:00
Birger J. Nordølum 7142f755f5 Fixes error thrown by UglifyJS 2018-06-17 23:17:35 +02:00
Birger J. Nordølum 9fbc8dc1f9 Fix missing bracket in app.js 2018-06-17 23:17:35 +02:00
Birger J. Nordølum d3819a6a88 Last fix for Huebee issue.
After some major debugging once again, I've concluded that the previous
webpack.mix.js worked fine, but threw an error when minifying the app.js
file.  The culprit is because the current app.js contains some newer
ECMAScript code that needs to be translated.  However, with the current
inclusion of huebee.js into the samme command, it seems to mess things
up.

Just as a potensial fix for now is to just ignore the issue, and have a
stab at it at a later date.  This reverts the "fixes" I've made before.
2018-06-16 15:15:00 +02:00
Birger J. Nordølum 6e93ed8e5f Up packages.json to reflect upstream Laravel 5.5
This updates laravel-mix to 2.0.  Removed node-sass,
as it's already pulled by laravel-mix.
2018-06-16 14:56:45 +02:00
Birger J. Nordølum 98543d49a9 Readme style tweaks 2018-06-16 14:11:48 +02:00
KodeStar 9195eead27 Merge pull request #208 from MindTooth/fix_huebee
Fixes bug introduced in #203
2018-06-16 12:06:04 +01:00
KodeStar e5b384736d Merge pull request #209 from dansoaress/master
Brazilian Portuguese translation
2018-06-15 14:14:48 +01:00
Daniel Soares 4def720d1a spell checking 2018-06-14 14:06:59 -03:00
Daniel Soares 2a0404ea17 Brazilian Portuguese translation 2018-06-14 07:02:14 -03:00
Birger J. Nordølum 6d22c4f02e Fixes bug introduced in #203
Needs to import the library when using webpack.mix.js.
Will also be the case for future libraries.
2018-06-14 00:20:42 +02:00
KodeStar 24ac12da65 Merge pull request #207 from MindTooth/fix_app_js
Fix issue with using override_url and running a test
2018-06-13 22:39:44 +01:00
Birger J. Nordølum 3f19882df8 All the other tweaks 2018-06-13 22:35:51 +02:00
Birger J. Nordølum dd54c16c1f Fixed test when using override_url 2018-06-13 22:35:51 +02:00
Kode 223e9289dc include arrow in text color fix 2018-06-13 20:11:28 +01:00
Kode 2e301bdd51 This closes #184, closes #126 2018-06-13 20:03:26 +01:00
Kode e3ec7de23a Dependency updates and update version number 2018-06-13 19:35:28 +01:00
KodeStar 18ec208381 Merge pull request #205 from MindTooth/add_gitattribute
.gitattributes: More settings added
2018-06-13 15:22:04 +01:00
KodeStar 8666daa07d Merge pull request #197 from finish06/master
Add App - Flood
2018-06-13 15:20:24 +01:00
KodeStar 926a9bdb03 Merge pull request #190 from ullbergm/app-Mylar
Foundation App: Mylar
2018-06-13 15:18:41 +01:00
Birger J. Nordølum fc2d837a2c .gitattributes: More settings added 2018-06-13 16:15:38 +02:00
KodeStar 3f69ccab99 Merge pull request #192 from ullbergm/app-Headphones
Foundation App: Headphones
2018-06-13 15:09:58 +01:00
KodeStar b6ee82ee52 Merge pull request #201 from shuaiscott/master
Enhanced App: Deluge
2018-06-13 15:09:25 +01:00
KodeStar da1eb859a9 Merge pull request #202 from MindTooth/enhanced_traefik
Enhanced App: Traefik
2018-06-13 15:08:11 +01:00
KodeStar 2b5269b823 Merge pull request #203 from MindTooth/fix_webpack
Fixes typo in webpack.mix.js
2018-06-13 15:07:19 +01:00
ullbergm 6c8eeb0ced Merge branch 'master' into app-Mylar 2018-06-10 07:39:37 -04:00
Magnus Ullberg c5e0f3abc8 Revert "Merge branch 'master' into app-Mylar"
This reverts commit 586941ece7, reversing
changes made to 263a4578d4.
2018-06-10 07:34:48 -04:00
Birger J. Nordølum c6dbe22c57 Fixes typo in webpack.mix.js 2018-06-10 13:20:46 +02:00
KodeStar 51776e2aa3 Merge pull request #200 from ullbergm/Add-option-to-open-links-in-current-window-or-new-tab
WIP: Add option to open links in current window or new tab
2018-06-10 09:01:25 +01:00
KodeStar c9d24607f6 Merge pull request #199 from MindTooth/tweak_names
lang_en: Reworded some strings, and removed whitespace
2018-06-10 08:53:37 +01:00
KodeStar 46f7a3d4f3 Merge pull request #198 from MindTooth/transmission_url
Transmission: Added custom URL
2018-06-10 08:50:33 +01:00
KodeStar 56cf450c89 Merge pull request #195 from ullbergm/app-MusicBrainz
Foundation App: MusicBrainz
2018-06-10 08:36:33 +01:00
KodeStar 38c350b020 Merge pull request #194 from ullbergm/app-LibreNMS
Foundation App: LibreNMS
2018-06-10 08:30:09 +01:00
KodeStar 1f46040f1b Merge pull request #193 from ullbergm/app-LazyLibrarian
Foundation App: LazyLibrarian
2018-06-10 08:29:03 +01:00
KodeStar 63b95319cd Merge pull request #191 from ullbergm/app-Booksonic
Foundation App: Booksonic
2018-06-10 08:25:17 +01:00
Birger J. Nordølum a25d92be9e Traefik: enhanced conversion 2018-06-09 18:20:36 +02:00
Scott Carlson a161210a4a Moving Deluge to Enhanced List 2018-06-08 15:39:57 -05:00
shuaiscott 24469eb2fa Adding Enhanced Deluge 2018-06-08 13:58:17 -06:00
ullbergm a93fb49875 Update Item.php 2018-06-07 17:35:01 -04:00
ullbergm 08d7cdf95b Update SettingsSeeder.php 2018-06-07 12:34:35 -04:00
ullbergm a9334bc247 Update Item.php 2018-06-07 12:33:40 -04:00
ullbergm 2357d0c466 Update app.php 2018-06-07 12:33:14 -04:00
ullbergm b003d51276 Update SettingsSeeder.php 2018-06-07 12:32:45 -04:00
ullbergm 503cbf9830 Update SettingsSeeder.php 2018-06-07 12:32:08 -04:00
ullbergm 2466058c5a Update Item.php 2018-06-07 12:31:26 -04:00
Magnus Ullberg 6b1f422456 Initial stab at allowing you to select how links open. 2018-06-07 12:17:08 -04:00
Birger J. Nordølum 1d16d67733 lang_en: Reworded some strings, and removed whitespace 2018-06-07 10:49:35 +02:00
Birger J. Nordølum 6f9d15aed8 Transmission: Added custom URL 2018-06-07 10:42:02 +02:00
ullbergm 8725f974da Update readme.md 2018-06-06 19:09:42 -04:00
ullbergm 2551c949ae Update Item.php 2018-06-06 19:08:51 -04:00
ullbergm 30c6020ce7 Delete LazyLibrarian.php 2018-06-06 19:08:32 -04:00
ullbergm d00b1ce1a1 Delete lazylibrarian.png 2018-06-06 19:08:12 -04:00
Caleb Dunn 2c43d79585 Add Flood class 2018-06-05 11:18:49 -05:00
Caleb Dunn 3e4aacb2b0 Add Flood 2018-06-05 11:17:17 -05:00
KodeStar 67cd22371b Merge pull request #189 from ullbergm/app-Rancher
Foundation App: Rancher
2018-06-05 11:59:24 +01:00
ullbergm 586941ece7 Merge branch 'master' into app-Mylar 2018-06-04 21:41:52 -04:00
ullbergm 32a57cbfa6 Merge pull request #5 from ullbergm/app-Headphones
Added Headphones support.
2018-06-04 21:41:04 -04:00
ullbergm 1837a69b3e Merge pull request #4 from ullbergm/app-Booksonic
Added Booksonic support.
2018-06-04 21:40:38 -04:00
ullbergm aa8bfcfd92 Merge pull request #3 from ullbergm/app-MusicBrainz
Added MusicBrainz support.
2018-06-04 21:40:00 -04:00
ullbergm 9e0c658470 Merge pull request #2 from ullbergm/app-LibreNMS
Added LibreNMS support.
2018-06-04 19:20:01 -04:00
ullbergm 66aefff4f3 Merge branch 'master' into app-LibreNMS 2018-06-04 19:19:41 -04:00
ullbergm bd0fdeecee Merge pull request #1 from ullbergm/app-LazyLibrarian
Added LazyLibrarian support.
2018-06-04 19:18:25 -04:00
Magnus Ullberg 1055eeb01b Added Rancher support. 2018-06-04 19:13:30 -04:00
Magnus Ullberg 263a4578d4 Added Mylar support. 2018-06-04 19:13:08 -04:00
Magnus Ullberg c446b8a5af Added MusicBrainz support. 2018-06-04 19:12:24 -04:00
Magnus Ullberg 847b34cdcb Added LibreNMS support. 2018-06-04 19:12:02 -04:00
Magnus Ullberg 5a57f90b8f Added LazyLibrarian support. 2018-06-04 19:11:36 -04:00
Magnus Ullberg f8eb9f5bd0 Added Headphones support. 2018-06-04 19:11:11 -04:00
Magnus Ullberg c8c336b574 Added Booksonic support. 2018-06-04 19:10:22 -04:00
KodeStar 43f1410974 added fileinfo note to installing section 2018-05-25 09:12:32 +01:00
KodeStar 1df110b3fb update app version 2018-05-04 17:57:03 +01:00
KodeStar 9bedce0df5 Merge pull request #177 from CHBMB/patch-1
Add unRAID to readme
2018-05-03 23:19:24 +01:00
chbmb ae1d879e5a Add unRAID to readme 2018-05-03 21:56:24 +01:00
KodeStar caab2e0952 Merge pull request #176 from ninthwalker/master
Add Now Showing to Supported Apps
2018-05-01 21:55:16 +01:00
ninthwalker a29d6517d3 add NowShowing 2018-05-01 11:43:56 -07:00
ninthwalker dae2781818 Add files via upload 2018-05-01 11:34:56 -07:00
ninthwalker c1de609b1a Create NowShowing.php 2018-05-01 11:31:54 -07:00
KodeStar 4e84807950 Merge pull request #173 from CHBMB/patch-5
Add Unraid to supported apps
2018-04-30 18:45:00 +01:00
chbmb 41c9cb45d6 Add files via upload 2018-04-29 01:39:27 +01:00
chbmb 2d72772f86 Delete unraid.png 2018-04-29 01:39:11 +01:00
chbmb 1cf1f0e04d Update Item.php 2018-04-29 01:30:45 +01:00
chbmb 324b88ec2b Add files via upload 2018-04-29 01:29:08 +01:00
chbmb ec64c7ba0b Create Unraid.php 2018-04-29 01:27:48 +01:00
KodeStar 67e0f8570e fix autofocus 2018-04-25 23:03:48 +01:00
KodeStar 1745100705 fix @CHBMB 's fsckup 2018-04-25 22:58:14 +01:00
KodeStar 28501944c5 Merge pull request #172 from CHBMB/master
Add StartPage support
2018-04-25 22:46:50 +01:00
chbmb c5c1a68f5c Merge pull request #3 from CHBMB/patch-4
Update SettingsSeeder.php
2018-04-25 22:41:54 +01:00
chbmb e2ed7fbd28 Merge pull request #2 from CHBMB/patch-3
Update app.php
2018-04-25 22:41:36 +01:00
chbmb ab0675055c Merge pull request #1 from CHBMB/patch-2
Update Setting.php
2018-04-25 22:41:14 +01:00
chbmb 2b65559f36 Update SettingsSeeder.php 2018-04-25 22:40:15 +01:00
chbmb b8beb07df5 Update app.php 2018-04-25 22:39:29 +01:00
chbmb 01748fec49 Update Setting.php 2018-04-25 22:38:30 +01:00
KodeStar 42a50b1a02 Merge pull request #169 from ferrymanders/master
AllowOverride explained for apache .htaccess
2018-04-17 09:03:41 +01:00
Ferry Manders d81595f43f AllowOverride explained for apache .htaccess
added extra example so users have a reference
2018-04-12 10:36:45 +02:00
Ferry Manders 71fa2d867f AllowOverride explained for apache .htaccess
In some apache installations .htaccess is disallowed by default. explained a few fixes/work arounds.
2018-04-12 10:32:32 +02:00
KodeStar f55dbb0002 Merge pull request #165 from kimpenhaus/master
added autofocus to search input #149
2018-04-11 15:02:19 +01:00
KodeStar 3e4d623786 Update readme.md 2018-04-06 09:13:30 +01:00
KodeStar 1b109aac3a Merge pull request #166 from halorrr/patch-1
SupportedApp: Syncthing
2018-04-06 09:02:50 +01:00
KodeStar 8558975d55 Merge pull request #164 from albertsj1/fix_cp_icon
fix couchpotato icon capitalization
2018-04-04 09:45:20 +01:00
halorrr 0e895089c7 Create Syncthing.php 2018-04-04 01:31:43 -04:00
halorrr 5dd44f66c1 Update Item.php 2018-04-04 01:26:08 -04:00
halorrr a1cfea46c6 Add files via upload 2018-04-04 07:25:18 +02:00
halorrr 42c492c85a Update readme.md 2018-04-04 01:24:07 -04:00
Marcus Kimpenhaus 9e8794a39f added autofocus to search input #149 2018-04-02 11:13:49 +02:00
John Alberts 66c3604b2a fix couchpotato icon capitalization 2018-03-31 22:29:59 -07:00
KodeStar c0ee1ee27b Update app.php 2018-03-27 10:57:49 +01:00
KodeStar a3e669e433 Merge pull request #162 from halorrr/master
Bug Fix On Newly Added Apps
2018-03-27 10:52:29 +01:00
halorrr 8a83b4fff5 Capitalized first letter of filename to fix link 2018-03-26 09:29:26 -04:00
halorrr 69c48d3f5d Deleted extra space at beginning of file 2018-03-26 09:28:53 -04:00
halorrr 88504a335e Deleted extra space at beginning of file 2018-03-26 09:28:39 -04:00
halorrr a44a433807 Deleted extra space at beginning of file 2018-03-26 09:28:26 -04:00
halorrr 9c03a8ae28 Deleted extra space at beginning of file 2018-03-26 09:28:12 -04:00
halorrr 767a5f3a94 Deleted extra space at beginning of file 2018-03-26 09:27:57 -04:00
halorrr db1e138d36 Deleted extra space at beginning of file 2018-03-26 09:27:36 -04:00
halorrr 84aa05f9a9 Deleted extra space at beginning of file 2018-03-26 09:27:19 -04:00
KodeStar 1071b85472 Merge pull request #160 from LiamAkkerman/master
SupportedApp: Grafana
2018-03-26 08:34:56 +01:00
KodeStar 479412b190 Merge pull request #157 from halorrr/patch-2
SupportedApp: Cardigann
2018-03-26 08:34:26 +01:00
KodeStar ac4fc5b0ba Merge pull request #156 from halorrr/patch-1
SupportedApp: pyLoad
2018-03-26 08:33:28 +01:00
KodeStar 4a8770232d Merge pull request #158 from rigrassm/mobile-app-compatible
Add html needed to allow the site to load as a mobile app
2018-03-26 08:31:40 +01:00
Liam B. Akkerman 2ea983bdae rename grafana icon file 2018-03-24 21:40:52 -07:00
Liam B. Akkerman ffce8fa505 add grafana to readme 2018-03-24 21:37:07 -07:00
Liam B. Akkerman cb9e529eb7 add grafana to Item.php 2018-03-24 21:35:23 -07:00
Liam B. Akkerman 7f0ee208fa add grafana icon 2018-03-24 21:29:46 -07:00
Liam B. Akkerman 2d38652034 add grafana app php 2018-03-24 21:23:15 -07:00
Ricky Grassmuck 4a2076a550 One more indentation fix
This time I really did fix it I swear!
2018-03-23 18:25:36 -05:00
Ricky Grassmuck 35049b26fe Fix Indentation
Fixed indentation of newly added lines to match existing lines.
2018-03-23 18:24:40 -05:00
Ricky Grassmuck 39648ba372 Add html needed to allow the site to load as a mobile app 2018-03-23 18:11:04 -05:00
KodeStar 44ce0bbffd Merge pull request #155 from imcdona/master
Document adding a supported application
2018-03-22 15:01:44 +00:00
halorrr a634472873 Create Cardigann.php 2018-03-21 11:28:30 -04:00
halorrr a98c30f7fa Update Item.php 2018-03-21 11:25:31 -04:00
halorrr 43d4b80e11 Add files via upload 2018-03-21 16:24:47 +01:00
halorrr 09926fcc53 Update readme.md 2018-03-21 11:24:14 -04:00
halorrr 17eef7a4aa Update pyLoad.php 2018-03-21 11:17:12 -04:00
halorrr 4738d7c951 Create pyLoad.php 2018-03-21 11:07:16 -04:00
halorrr 4be0af5fa3 Update Item.php 2018-03-21 11:06:33 -04:00
halorrr 8aaa0900e5 Add files via upload 2018-03-21 16:05:38 +01:00
halorrr 49b5d9b886 Update readme.md 2018-03-21 11:03:40 -04:00
Isaac McDonald 7a3912767b Document adding a supported application
If you type the name of a supported application quickly enough in the title field, you'll never see the option to select that application from the list. Long term I think this needs to be addressed. In the short term, an explanation will suffice.
2018-03-21 07:52:47 -07:00
KodeStar d1e473aafe Merge pull request #151 from halorrr/patch-3
SupportedApp: Watcher3
2018-03-21 12:59:41 +00:00
KodeStar c91eb7ed47 Merge branch 'master' into patch-3 2018-03-21 12:59:21 +00:00
KodeStar 443f631bac Merge pull request #146 from halorrr/patch-1
SupportedApp: AirSonic
2018-03-21 12:57:53 +00:00
KodeStar 95be8d4698 Merge branch 'master' into patch-1 2018-03-21 12:57:39 +00:00
KodeStar f3926d020f Merge pull request #154 from imcdona/master
Changed pFsense to the proper pfSense. Changed Ttrss to tt-rss
2018-03-21 12:56:13 +00:00
KodeStar b48eb5fdd3 Merge pull request #153 from albertsj1/better_transmission_icon
New Transmission icon with transparent background
2018-03-21 12:55:41 +00:00
KodeStar b0fecdd017 Merge pull request #147 from halorrr/patch-2
SupportedApp: OpenVaultMedia
2018-03-21 12:55:08 +00:00
KodeStar 793483b6e9 Merge pull request #145 from halorrr/master
SupportedApp: WebTools
2018-03-21 12:54:21 +00:00
Isaac McDonald c99c7fa9af Changed pFsense to the proper pfSense. Changed Ttrss to tt-rss 2018-03-20 14:15:01 -07:00
John Alberts 59449a73e1 New Transmission icon with transparent background 2018-03-20 08:31:12 -07:00
halorrr dc86d636ac Create Watcher3.php 2018-03-18 09:20:39 -04:00
halorrr 27dd2dd5f3 Update Item.php 2018-03-18 09:18:27 -04:00
halorrr 494ae1a47f Add files via upload 2018-03-18 14:17:25 +01:00
halorrr 7d1e1e5ff1 Update readme.md 2018-03-18 09:09:10 -04:00
KodeStar bcd1567b7d Merge pull request #133 from albertsj1/couchpotato
Add Couchpotato as an enhanced app
2018-03-17 13:18:03 +00:00
KodeStar ae391b885b added missing dataonly attribute so it polls every 20 seconds 2018-03-17 13:17:45 +00:00
KodeStar 226bc84a4c removed duplicate semi-colon 2018-03-17 13:15:39 +00:00
halorrr e60a0c8f61 Logo for OpenMediaVault 2018-03-17 07:07:50 +01:00
halorrr 218c90a306 Delete openvaultmedia.png 2018-03-17 02:07:29 -04:00
halorrr 2a60c80194 Create OpenMediaVault.php 2018-03-17 02:06:26 -04:00
halorrr 35b1c55564 Update Item.php 2018-03-17 02:05:32 -04:00
halorrr 20476387ff Add files via upload 2018-03-17 07:04:23 +01:00
halorrr a787748a00 Update readme.md 2018-03-17 02:03:43 -04:00
halorrr 34ee540c30 Changed default colour for AirSonic 2018-03-17 02:02:35 -04:00
halorrr 975a5ffc82 Create AirSonic.php 2018-03-17 01:52:17 -04:00
halorrr dee0870bf6 Airsonic added 2018-03-17 01:48:40 -04:00
halorrr 96ec3bd44e Airsonic logo added 2018-03-17 06:47:20 +01:00
halorrr e4cf4096a6 Adding AirSonic to readme 2018-03-17 01:46:12 -04:00
halorrr 3d79694c0a Create WebTools.php 2018-03-17 01:24:14 -04:00
halorrr eab3e4e6f7 Update Item.php 2018-03-17 01:19:30 -04:00
halorrr 462152bab2 Update readme.md 2018-03-17 01:17:33 -04:00
halorrr 766a455db0 Add files via upload 2018-03-17 06:16:06 +01:00
John Alberts 30200ac219 Get rid off aggressive polling option and always use inactive status 2018-03-16 15:08:04 -07:00
KodeStar 90a9113971 Merge pull request #140 from halorrr/master
FoundationApp: Krusader closes #144
2018-03-16 22:00:17 +00:00
KodeStar ad1834568f Merge pull request #141 from halorrr/patch-1
Adding Tautulli to Enhanced Apps
2018-03-16 21:58:19 +00:00
KodeStar 6231500b4a Merge pull request #142 from grOGH/master
Add a readme section about using self-signed certificates with enhanced apps
2018-03-15 13:04:38 +00:00
grOGH b2dbc08ea0 Add a readme section about using self-signed certificates with enhanced
apps
2018-03-15 08:48:56 +01:00
KodeStar 4700f68f4d Update readme.md 2018-03-13 21:09:59 +00:00
halorrr fa73738309 Update readme.md 2018-03-13 16:01:44 -04:00
halorrr 25f92ec438 Create tautulli.blade.php 2018-03-13 16:01:14 -04:00
halorrr 6a59c1dfd6 Update Item.php 2018-03-13 15:58:37 -04:00
halorrr bad6e9d2fe Create Tautulli.php 2018-03-13 15:57:14 -04:00
halorrr e544972c1d Add files via upload 2018-03-13 20:56:12 +01:00
halorrr f1e5de0d58 Update readme.md 2018-03-13 15:52:44 -04:00
halorrr e434e1effb Update readme.md 2018-03-13 15:32:54 -04:00
halorrr ca04b210e6 Update Item.php 2018-03-13 12:38:29 -04:00
halorrr 51275af41d Create Krusader.php 2018-03-13 12:35:26 -04:00
halorrr c2417ac5b3 Add files via upload 2018-03-13 17:32:04 +01:00
John Alberts 72831d413b update couchpotato icon 2018-03-13 08:14:18 -07:00
John Alberts 5cb4bd5819 Icon name should be lower case. 2018-03-13 06:49:05 -07:00
KodeStar 2d9bbca9a9 Merge pull request #135 from albertsj1/fix_transmission_pic
Fix capitalization of tranmission icon name
2018-03-13 09:36:29 +00:00
KodeStar 7b54c4e969 Merge pull request #134 from albertsj1/update_readme
add some apps to the enhanced app section of the readme that were mis…
2018-03-13 09:35:54 +00:00
Chris b50f4ec5ce Merge branch 'master' of https://github.com/linuxserver/Heimdall 2018-03-12 09:59:43 +00:00
Chris 86cc7534c2 fix html error in link, but replaced target _blank to a named window to keep the experience 2018-03-12 09:59:37 +00:00
John Alberts 1ab2565244 Fix capitalization of tranmission icon name 2018-03-11 21:23:01 -07:00
John Alberts d2089a9344 add some apps to the enhanced app section of the readme that were missing. 2018-03-11 21:12:25 -07:00
John Alberts 39153c6936 Add Couchpotato as an enhanced app 2018-03-11 20:48:58 -07:00
KodeStar 1a8e2b92de closes #128 2018-03-11 17:47:05 +00:00
KodeStar cf63e751bf Merge pull request #127 from albertsj1/add_transmission
Add Enhanced App Transmission
2018-03-11 17:41:02 +00:00
KodeStar 0c51bc2771 fix multiple tags 2018-03-11 17:40:08 +00:00
John Alberts 5f278cce3e cleanup a couple symlinks I didn't intend to commit and some errant whitespace 2018-03-09 07:59:01 -08:00
KodeStar f8cf3ac832 Merge pull request #125 from frenos/foundation_glances
Foundation App for Glances
2018-03-09 08:05:07 +00:00
John Alberts 50bdd02a72 Add Enhanced App Transmission 2018-03-08 16:18:39 -08:00
frenos 424155e5cd Foundation App for Glances
Default color is the backgroundcolor of the official site.
2018-03-07 17:39:45 +01:00
Chris c4a4d25f7e change js version to break cache 2018-03-06 09:43:54 +00:00
KodeStar ca2e135cba closes #122, closes #82, 2018-03-05 19:38:23 +00:00
19502 changed files with 1439903 additions and 550818 deletions
-31
View File
@@ -1,31 +0,0 @@
APP_NAME=Heimdall
APP_ENV=local
APP_KEY=base64:I206O8ibx+GQyRE7BeOxDobn04Mfmyyc5Ptzns/C0mY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost
DB_CONNECTION=sqlite
DB_DATABASE=app.sqlite
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_LIFETIME=120
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
+56 -12
View File
@@ -1,35 +1,79 @@
APP_NAME=Laravel
APP_NAME=Heimdall
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_DEBUG=false
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
# Security: Host Header Injection / Open Redirect hardening (CVE-2025-50578).
# TRUSTED_PROXIES: comma-separated CIDRs/IPs of reverse proxies allowed to set
# X-Forwarded-* headers. Defaults to the private ranges below when unset. Use
# "*" to trust all proxies (only behind a trusted network boundary).
#TRUSTED_PROXIES=192.168.0.0/16,172.16.0.0/12,10.0.0.0/8,127.0.0.1
# TRUSTED_HOSTS: comma-separated hostnames Heimdall is allowed to serve. Unset
# means no restriction (default, backward compatible). Set this to your own
# domain to fully prevent host-header injection / open redirects.
#TRUSTED_HOSTS=heimdall.example.com
BROADCAST_DRIVER=log
CACHE_DRIVER=file
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=daily
LOG_STACK=single
DB_CONNECTION=sqlite
DB_DATABASE=app.sqlite
#DB_CONNECTION=<mysql | pgsql>
#DB_HOST=<hostname | ip>
#DB_PORT=<port number>
#DB_DATABASE=<database>
#DB_USERNAME=<user>
#DB_PASSWORD=<password>
BROADCAST_CONNECTION=log
CACHE_STORE=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
AUTH_ROLES_ENABLE=false
AUTH_ROLES_HEADER="remote-groups"
AUTH_ROLES_HTTP_HEADER="HTTP_REMOTE_GROUPS"
AUTH_ROLES_ADMIN="admin"
AUTH_ROLES_DELIMITER=","
ALLOW_INTERNAL_REQUESTS=false
+3
View File
@@ -0,0 +1,3 @@
huebee.js
jquery-ui.min.js
bootstrap.js
+13
View File
@@ -0,0 +1,13 @@
{
"extends": ["airbnb-base", "prettier"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": ["error"]
},
"env": {
"browser": true
},
"globals": {
"$": true
}
}
+44
View File
@@ -1,5 +1,49 @@
# Configuration file for Git attributes
# Core Settings {{{
# .gitattributes
.gitattributes !filter !diff
# Line Endings
* text=auto
# Set binary to none-text files
*.png -text
# }}}
# GitHub Linguist {{{
# Exclude files/folder from being detected by the GitHub linguist
# statistic.
node_modules/* linguist-vendored
public/* linguist-generated=true
vendor/* linguist-vendored
# Remove Vue as it's currently not used in the project.
resources/assets/js/components/ExampleComponent.vue linguist-vendored
# System Wide
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
# Include user generated files that's removed bu the setting above.
resources/assets/js/app.js linguist-vendored=false
resources/assets/sass/_app.scss linguist-vendored=false
resources/assets/sass/_rune.scss linguist-vendored=false
resources/assets/sass/_variables.scss linguist-vendored=false
# }}}
# Archive Exlude {{{
# Exclude files/folders from being exported when creating an archive.
.gitattributes export-ignore
.gitignore export-ignore
.travis.yml export-ignore
CHANGELOG.md export-ignore
# }}}
@@ -0,0 +1,16 @@
name: Issue & PR Tracker
on:
issues:
types: [opened,reopened,labeled,unlabeled,closed]
pull_request_target:
types: [opened,reopened,review_requested,review_request_removed,labeled,unlabeled,closed]
pull_request_review:
types: [submitted,edited,dismissed]
jobs:
manage-project:
permissions:
issues: write
uses: linuxserver/github-workflows/.github/workflows/issue-pr-tracker.yml@v1
secrets: inherit
+13
View File
@@ -0,0 +1,13 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: '35 15 * * *'
workflow_dispatch:
jobs:
stale:
permissions:
issues: write
pull-requests: write
uses: linuxserver/github-workflows/.github/workflows/issues-cron.yml@v1
secrets: inherit
+59
View File
@@ -0,0 +1,59 @@
name: Tests (PHP)
on: [pull_request]
jobs:
tests:
name: Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php
with:
php-version: '8.4'
extensions: mbstring, dom, fileinfo, mysql, libxml, xml, xmlwriter, dom, tokenizer, filter, json, phar, pcre, openssl, pdo, intl, curl
- name: Cache composer dependencies
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
#- name: Run composer install
# run: composer install -n --prefer-dist
# env:
# APP_ENV: testing
- name: Prepare Laravel Application
run: |
cp .env.example .env
php artisan key:generate
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'
- name: Install node modules and build assets
run: npm ci && npm run dev
- name: Run ESLint
run: npm run lint
- name: Run tests
run: php artisan test
env:
APP_ENV: testing
- name: Php code sniffer
run: ./vendor/bin/phpcs --config-set ignore_warnings_on_exit 1
- name: Upload artifacts
uses: actions/upload-artifact@master
if: failure()
with:
name: Logs
path: ./storage/logs
+97
View File
@@ -0,0 +1,97 @@
name: Release
# Stage 1 of the release flow: opens a version-bump PR against 2.x.
# When that PR is merged, tag-release.yml (stage 2) creates the tag and
# the GitHub release automatically.
on:
workflow_dispatch:
inputs:
bump:
description: 'Version bump type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
version:
description: 'Explicit version (e.g. 2.9.0) — overrides bump type'
required: false
type: string
permissions:
contents: write
pull-requests: write
jobs:
release-pr:
name: Open version bump PR
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: 2.x
fetch-depth: 0
# Optional: set a RELEASE_TOKEN repo secret (fine-grained PAT with
# contents + pull-requests write) so the bump PR triggers CI checks.
# PRs created with the default github.token do not trigger workflows.
token: ${{ secrets.RELEASE_TOKEN || github.token }}
- name: Determine new version
id: version
env:
EXPLICIT_VERSION: ${{ inputs.version }}
BUMP: ${{ inputs.bump }}
run: |
current=$(sed -nE "s/^[[:space:]]*'version' => '([0-9]+\.[0-9]+\.[0-9]+)',/\1/p" config/app.php)
if [ -z "$current" ]; then
echo "::error::Could not read current version from config/app.php"
exit 1
fi
if [ -n "$EXPLICIT_VERSION" ]; then
new="$EXPLICIT_VERSION"
if ! echo "$new" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::error::Invalid version '$new' — expected X.Y.Z"
exit 1
fi
else
IFS=. read -r major minor patch <<< "$current"
case "$BUMP" in
major) new="$((major + 1)).0.0" ;;
minor) new="$major.$((minor + 1)).0" ;;
patch) new="$major.$minor.$((patch + 1))" ;;
esac
fi
if git rev-parse -q --verify "refs/tags/v$new" > /dev/null; then
echo "::error::Tag v$new already exists"
exit 1
fi
echo "Bumping $current -> $new"
echo "new=$new" >> "$GITHUB_OUTPUT"
- name: Push bump commit to release branch
env:
NEW_VERSION: ${{ steps.version.outputs.new }}
run: |
sed -i -E "s/^([[:space:]]*'version' => ')[0-9.]+(',)/\1$NEW_VERSION\2/" config/app.php
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -am "Bump version to $NEW_VERSION"
git push --force origin "HEAD:refs/heads/release/v$NEW_VERSION"
- name: Open pull request
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN || github.token }}
NEW_VERSION: ${{ steps.version.outputs.new }}
run: |
existing=$(gh pr list --head "release/v$NEW_VERSION" --base 2.x --state open --json number -q '.[0].number')
if [ -n "$existing" ]; then
echo "PR #$existing is already open for release/v$NEW_VERSION"
else
gh pr create \
--base 2.x \
--head "release/v$NEW_VERSION" \
--title "Bump version to $NEW_VERSION" \
--body "Automated version bump. Merging this PR will tag v$NEW_VERSION and publish the GitHub release. Merge it last, once everything for the release is on 2.x."
fi
+41
View File
@@ -0,0 +1,41 @@
name: Tag and release
# Stage 2 of the release flow: whenever the version in config/app.php changes
# on 2.x (normally by merging the PR opened by release.yml, but a hand-made
# bump PR works too), create the matching tag and GitHub release.
on:
push:
branches:
- 2.x
paths:
- config/app.php
permissions:
contents: write
jobs:
tag-release:
name: Tag and publish release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Create release if version is untagged
env:
GH_TOKEN: ${{ github.token }}
run: |
version=$(sed -nE "s/^[[:space:]]*'version' => '([0-9]+\.[0-9]+\.[0-9]+)',/\1/p" config/app.php)
if [ -z "$version" ]; then
echo "::error::Could not read version from config/app.php"
exit 1
fi
if git rev-parse -q --verify "refs/tags/v$version" > /dev/null; then
echo "Tag v$version already exists — nothing to do"
exit 0
fi
gh release create "v$version" \
--target "$GITHUB_SHA" \
--title "v$version" \
--generate-notes
+26
View File
@@ -0,0 +1,26 @@
name: Tag version check
# Safety net for manually pushed tags: fails if the tag doesn't match the
# version in config/app.php. Tags created by tag-release.yml use GITHUB_TOKEN
# and therefore don't trigger this (they always match anyway).
on:
push:
tags:
- 'v*'
jobs:
check:
name: Tag matches config/app.php
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Compare tag with app version
run: |
tag="${GITHUB_REF_NAME#v}"
version=$(sed -nE "s/^[[:space:]]*'version' => '([0-9]+\.[0-9]+\.[0-9]+)',/\1/p" config/app.php)
if [ "$tag" != "$version" ]; then
echo "::error::Tag v$tag does not match config/app.php version $version — bump the version (or use the Release workflow, which does it for you)"
exit 1
fi
echo "Tag v$tag matches config/app.php"
+7 -1
View File
@@ -1,7 +1,9 @@
/app/SupportedApps
/node_modules
/public/hot
/public/storage
/storage/*.key
/storage/debugbar
/.idea
/.vagrant
Homestead.json
@@ -23,4 +25,8 @@ yarn-error.log
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.VolumeIcon.icns
storage/app/public/avatars/*
.env
.phpunit.result.cache
/.phpunit.cache
+2548
View File
File diff suppressed because it is too large Load Diff
+15
View 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
View 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": []
}
]
}
+28371
View File
File diff suppressed because it is too large Load Diff
+200
View File
@@ -0,0 +1,200 @@
<?php
namespace App;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* App\Application
*
* @property string $appid
* @property string $name
* @property string|null $sha
* @property string|null $icon
* @property string|null $website
* @property string|null $license
* @property string|null $description
* @property int $enhanced
* @property string $tile_background
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $class
* @method static Builder|Application newModelQuery()
* @method static Builder|Application newQuery()
* @method static Builder|Application query()
* @method static Builder|Application whereAppid($value)
* @method static Builder|Application whereClass($value)
* @method static Builder|Application whereCreatedAt($value)
* @method static Builder|Application whereDescription($value)
* @method static Builder|Application whereEnhanced($value)
* @method static Builder|Application whereIcon($value)
* @method static Builder|Application whereLicense($value)
* @method static Builder|Application whereName($value)
* @method static Builder|Application whereSha($value)
* @method static Builder|Application whereTileBackground($value)
* @method static Builder|Application whereUpdatedAt($value)
* @method static Builder|Application whereWebsite($value)
*/
class Application extends Model
{
/**
* @var bool
*/
public $incrementing = false;
/**
* @var string
*/
protected $primaryKey = 'appid';
/**
* @return mixed
*/
public function icon()
{
if (! file_exists(storage_path('app/public/'.$this->icon))) {
$img_src = app_path('SupportedApps/'.$this->name.'/'.str_replace('icons/', '', $this->icon));
$img_dest = storage_path('app/public/'.$this->icon);
//die("i: ".$img_src);
@copy($img_src, $img_dest);
}
return $this->icon;
}
public function iconView(): string
{
return asset('storage/'.$this->icon);
}
public function defaultColour(): string
{
// check if light or dark
if ($this->tile_background == 'light') {
return '#fafbfc';
}
return '#161b1f';
}
public function class(): string
{
$name = $this->name;
$name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
return \App\SupportedApps::class.'\\'.$name.'\\'.$name;
}
/**
* @param $name
*/
public static function classFromName($name): string
{
$name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
$class = \App\SupportedApps::class.'\\'.$name.'\\'.$name;
return $class;
}
public static function apps(): Collection
{
$json = json_decode(file_get_contents(storage_path('app/supportedapps.json'))) ?? [];
$apps = collect($json->apps);
return $apps->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE);
}
public static function autocomplete(): array
{
$apps = self::apps();
$list = [];
foreach ($apps as $app) {
$list[] = (object) [
'label' => $app->name,
'value' => $app->appid,
];
}
return $list;
}
/**
* @param $appid
* @return mixed|null
* @throws GuzzleException
*/
public static function getApp($appid)
{
Log::debug("Get app triggered for: $appid");
$localapp = self::where('appid', $appid)->first();
$app = self::single($appid);
$application = ($localapp) ? $localapp : new self;
// Files missing? || app not in db || old sha version
if (! file_exists(app_path('SupportedApps/'.className($app->name))) ||
! $localapp ||
$localapp->sha !== $app->sha
) {
$gotFiles = SupportedApps::getFiles($app);
if ($gotFiles) {
$app = SupportedApps::saveApp($app, $application);
}
}
return $app;
}
/**
* @param $appid
* @return mixed|null
*/
public static function single($appid)
{
$apps = self::apps();
$app = $apps->where('appid', $appid)->first();
if ($app === null) {
// Try in db for Private App
$appModel = self::where('appid', $appid)->first();
if ($appModel) {
$app = json_decode($appModel->toJson());
}
}
if ($app === null) {
return null;
}
$classname = preg_replace('/[^\p{L}\p{N}]/u', '', $app->name);
$app->class = \App\SupportedApps::class.'\\'.$classname.'\\'.$classname;
return $app;
}
public static function applist(): array
{
$list = [];
$list['null'] = 'None';
$apps = self::apps();
foreach ($apps as $app) {
$list[$app->appid] = $app->name;
}
// Check for private apps in the db
$appsListFromDB = self::all(['appid', 'name']);
foreach ($appsListFromDB as $app) {
// Already existing keys are overwritten,
// only private apps should be added at the end
$list[$app->appid] = $app->name;
}
return $list;
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
namespace App\Console\Commands;
use App\Application;
use App\SupportedApps;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class RegisterApp extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'register:app {folder} {--remove}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Add a local app to the registry';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle(): void
{
$folder = $this->argument('folder');
if ($folder == 'all') {
$apps = scandir(app_path('SupportedApps'));
foreach ($apps as $folder) {
if ($folder == '.' || $folder == '..') {
continue;
}
$this->addApp($folder);
}
} else {
$this->addApp($folder, $this->option('remove'));
}
}
/**
* @param $folder
*/
public function addApp($folder, bool $remove = false): void
{
$json = app_path('SupportedApps/'.$folder.'/app.json');
if (!file_exists($json)) {
$this->error('Could not find ' . $json);
return;
}
$app = json_decode(file_get_contents($json));
if (!isset($app->appid)) {
$this->error('No App ID for - ' . $folder);
return;
}
$exists = Application::find($app->appid);
if ($exists) {
if ($remove) {
$exists->delete();
$this->info('Application Removed - ' . $app->name . ' - ' . $app->appid);
return;
}
$this->error('Application already registered - ' . $exists->name . ' - ' . $exists->appid);
return;
}
// Doesn't exist so add it
SupportedApps::saveApp($app, new Application);
$this->saveIcon($folder, $app->icon);
$this->info('Application Added - ' . $app->name . ' - ' . $app->appid);
}
/**
* @param $appFolder
* @param $icon
*/
private function saveIcon($appFolder, $icon): void
{
$iconPath = app_path('SupportedApps/' . $appFolder . '/' . $icon);
$contents = file_get_contents($iconPath);
Storage::disk('public')->put('icons/'.$icon, $contents);
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')
// ->hourly();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App;
interface EnhancedApps
{
public function test();
public function livestats();
public function url($endpoint);
}
-53
View File
@@ -1,53 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
}
+13
View 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';
}
}
+150 -8
View File
@@ -1,12 +1,154 @@
<?php
function format_bytes($bytes, $is_drive_size = true, $beforeunit = '', $afterunit = '')
use Illuminate\Support\Str;
use enshrined\svgSanitize\Sanitizer;
/**
* @param $bytes
* @param bool $is_drive_size
* @param string $beforeunit
* @param string $afterunit
* @return string
*/
function format_bytes($bytes, bool $is_drive_size = true, string $beforeunit = '', string $afterunit = ''): string
{
$btype = ($is_drive_size === true) ? 1000 : 1024;
$labels = array('B','KB','MB','GB','TB');
for($x = 0; $bytes >= $btype && $x < (count($labels) - 1); $bytes /= $btype, $x++); // use 1000 rather than 1024 to simulate HD size not real size
if($labels[$x] == "TB") return(round($bytes, 3).$beforeunit.$labels[$x].$afterunit);
elseif($labels[$x] == "GB") return(round($bytes, 2).$beforeunit.$labels[$x].$afterunit);
elseif($labels[$x] == "MB") return(round($bytes, 2).$beforeunit.$labels[$x].$afterunit);
else return(round($bytes, 0).$beforeunit.$labels[$x].$afterunit);
$btype = ($is_drive_size === true) ? 1000 : 1024;
$labels = ['B', 'KB', 'MB', 'GB', 'TB'];
// use 1000 rather than 1024 to simulate HD size not real size
for ($x = 0; $bytes >= $btype && $x < (count($labels) - 1); $bytes /= $btype, $x++) ;
if ($labels[$x] == 'TB') {
return round($bytes, 3) . $beforeunit . $labels[$x] . $afterunit;
} elseif ($labels[$x] == 'GB') {
return round($bytes, 2) . $beforeunit . $labels[$x] . $afterunit;
} elseif ($labels[$x] == 'MB') {
return round($bytes, 2) . $beforeunit . $labels[$x] . $afterunit;
} else {
return round($bytes, 0) . $beforeunit . $labels[$x] . $afterunit;
}
}
function parse_size($size) {
$unit = strtolower(substr($size, -1));
$bytes = (int)$size;
switch($unit) {
case 'g': $bytes *= 1024 * 1024 * 1024; break;
case 'm': $bytes *= 1024 * 1024; break;
case 'k': $bytes *= 1024; break;
}
return $bytes;
}
/**
* @param $title
* @param string $separator
* @param string $language
* @return string
*/
function str_slug($title, string $separator = '-', string $language = 'en'): string
{
return Str::slug($title, $separator, $language);
}
if (!function_exists('str_is')) {
/**
* Determine if a given string matches a given pattern.
*
* @param string|array $pattern
* @param string $value
* @return bool
*
* @deprecated Str::is() should be used directly instead. Will be removed in Laravel 6.0.
*/
function str_is($pattern, string $value): bool
{
return Str::is($pattern, $value);
}
}
/**
* @param $hex
* @return float|int
*/
function get_brightness($hex)
{
// returns brightness value from 0 to 255
// strip off any leading #
// $hex = str_replace('#', '', $hex);
$hex = preg_replace("/[^0-9A-Fa-f]/", '', $hex);
if (strlen($hex) == 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
$c_r = hexdec(substr($hex, 0, 2));
$c_g = hexdec(substr($hex, 2, 2));
$c_b = hexdec(substr($hex, 4, 2));
return (($c_r * 299) + ($c_g * 587) + ($c_b * 114)) / 1000;
}
/**
* @param $hex
* @return string
*/
function title_color($hex): string
{
if (get_brightness($hex) > 130) {
return ' black';
} else {
return ' white';
}
}
/**
* @return string
*/
function getLinkTargetAttribute(): string
{
$target = \App\Setting::fetch('window_target');
if ($target === 'current') {
return '';
} else {
return ' target="' . $target . '"';
}
}
/**
* @param $name
* @return array|string|string[]|null
*/
function className($name)
{
return preg_replace('/[^\p{L}\p{N}]/u', '', $name);
}
/**
* @param string $file
* @param string $extension
* @return bool
*/
function isImage(string $file, string $extension): bool
{
$allowedExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp', 'ico'];
if (!in_array($extension, $allowedExtensions)) {
return false;
}
$tempFileName = @tempnam("/tmp", "image-check-");
$handle = fopen($tempFileName, "w");
fwrite($handle, $file);
fclose($handle);
if ($extension === 'svg') {
$sanitizer = new Sanitizer();
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($tempFileName));
file_put_contents($tempFileName, $sanitizedSvg);
return 'image/svg+xml' === mime_content_type($tempFileName);
}
$size = @getimagesize($tempFileName);
return is_array($size) && str_starts_with($size['mime'], 'image');
}
+187
View File
@@ -0,0 +1,187 @@
<?php
namespace App\Helpers;
use Illuminate\Support\Facades\Log;
use Phrity\Net\Context;
use WebSocket\Client;
use WebSocket\Exception\Exception as WebSocketException;
/**
* TrueNAS JSON-RPC 2.0 WebSocket Client
*
* Handles WebSocket communication with TrueNAS using the JSON-RPC 2.0 protocol.
* Required for TrueNAS 25.04+ as the REST API is deprecated.
*
* @see https://api.truenas.com/v25.10/jsonrpc.html
*/
class TrueNASWebSocketClient
{
private ?Client $client = null;
private string $url;
private string $apiKey;
private bool $ignoreTls;
private bool $authenticated = false;
private int $requestId = 1;
/**
* Create a new TrueNAS WebSocket client instance.
*
* @param string $baseUrl The base URL of the TrueNAS instance (e.g., https://truenas.local)
* @param string $apiKey The API key for authentication
* @param bool $ignoreTls Whether to skip TLS certificate verification
*/
public function __construct(string $baseUrl, string $apiKey, bool $ignoreTls = false)
{
$baseUrl = rtrim($baseUrl, '/');
$scheme = parse_url($baseUrl, PHP_URL_SCHEME);
$host = parse_url($baseUrl, PHP_URL_HOST);
$port = parse_url($baseUrl, PHP_URL_PORT);
$wsScheme = ($scheme === 'https') ? 'wss' : 'ws';
$portPart = $port ? ':' . $port : '';
$this->url = "{$wsScheme}://{$host}{$portPart}/api/current";
$this->apiKey = $apiKey;
$this->ignoreTls = $ignoreTls;
}
/**
* Connect to the TrueNAS WebSocket API and authenticate.
*
* @return bool True if connection and authentication succeeded
* @throws \Exception If connection or authentication fails
*/
public function connect(): bool
{
if ($this->client !== null && $this->authenticated) {
return true;
}
// Build SSL options - always force HTTP/1.1 via ALPN for WebSocket compatibility
// TrueNAS nginx defaults to HTTP/2 which doesn't support WebSocket upgrade
$sslOptions = [
'alpn_protocols' => 'http/1.1',
];
if ($this->ignoreTls) {
$sslOptions['verify_peer'] = false;
$sslOptions['verify_peer_name'] = false;
$sslOptions['allow_self_signed'] = true;
}
// Create context using phrity/net-stream Context class (required by phrity/websocket v3.x)
$streamContext = stream_context_create(['ssl' => $sslOptions]);
$context = new Context($streamContext);
try {
$this->client = new Client($this->url);
$this->client->setTimeout(15);
$this->client->setContext($context);
$authResult = $this->call('auth.login_with_api_key', [$this->apiKey]);
if ($authResult === true) {
$this->authenticated = true;
return true;
}
throw new \Exception('Authentication failed: Invalid API key');
} catch (WebSocketException $e) {
Log::error('TrueNAS WebSocket connection failed: ' . $e->getMessage());
$this->disconnect();
throw new \Exception('WebSocket connection failed: ' . $e->getMessage());
}
}
/**
* Make a JSON-RPC 2.0 call to the TrueNAS API.
*
* @param string $method The JSON-RPC method name (e.g., 'system.info')
* @param array $params Optional parameters for the method
* @return mixed The result from the API call
* @throws \Exception If the call fails or returns an error
*/
public function call(string $method, array $params = [])
{
if ($this->client === null) {
throw new \Exception('WebSocket client not connected');
}
$request = [
'jsonrpc' => '2.0',
'method' => $method,
'id' => $this->requestId++,
];
if (!empty($params)) {
$request['params'] = $params;
}
try {
$this->client->text(json_encode($request));
$response = $this->client->receive();
$decoded = json_decode($response->getContent(), true);
if (isset($decoded['error'])) {
$errorMsg = $decoded['error']['message'] ?? 'Unknown error';
$errorCode = $decoded['error']['code'] ?? 0;
throw new \Exception("API error ({$errorCode}): {$errorMsg}");
}
return $decoded['result'] ?? null;
} catch (WebSocketException $e) {
Log::error('TrueNAS WebSocket call failed: ' . $e->getMessage());
throw new \Exception('WebSocket call failed: ' . $e->getMessage());
}
}
/**
* Close the WebSocket connection.
*/
public function disconnect(): void
{
if ($this->client !== null) {
try {
$this->client->close();
} catch (\Exception $e) {
Log::debug('Error closing WebSocket: ' . $e->getMessage());
}
$this->client = null;
$this->authenticated = false;
}
}
/**
* Check if the client is connected and authenticated.
*
* @return bool
*/
public function isConnected(): bool
{
return $this->client !== null && $this->authenticated;
}
/**
* Test the connection by calling core.ping.
*
* @return bool True if the ping succeeds
*/
public function ping(): bool
{
try {
$result = $this->call('core.ping');
return $result === 'pong';
} catch (\Exception $e) {
return false;
}
}
/**
* Clean up on destruction.
*/
public function __destruct()
{
$this->disconnect();
}
}
+111 -2
View File
@@ -3,7 +3,18 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpFoundation\Response;
class LoginController extends Controller
{
@@ -25,7 +36,7 @@ class LoginController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected string $redirectTo = '/';
/**
* Create a new controller instance.
@@ -34,6 +45,104 @@ class LoginController extends Controller
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
Session::put('backUrl', URL::previous());
$this->middleware('guest')->except(['logout','autologin']);
}
public function username(): string
{
return 'username';
}
/**
* Handle a login request to the application.
*
*
* @throws ValidationException
*/
public function login(Request $request): Response
{
$current_user = User::currentUser();
$request->merge(['username' => $current_user->username, 'remember' => true]);
//die(print_r($request->all()));
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
public function index()
{
}
public function setUser(User $user): RedirectResponse
{
Auth::logout();
session(['current_user' => $user]);
return redirect()->route('dash');
}
/**
* @param $uuid
*/
public function autologin($uuid): RedirectResponse
{
Auth::logout();
$user = User::where('autologin', $uuid)->first();
if (!$user) {
return redirect()->route('dash');
}
Auth::login($user, true);
session(['current_user' => $user]);
return redirect()->route('dash');
}
/**
* Show the application's login form.
*
* @return Application|Factory|View
*/
public function showLoginForm(): \Illuminate\View\View
{
return view('auth.login');
}
/**
* @param $user
*/
protected function authenticated(Request $request, $user): RedirectResponse
{
return back();
}
/**
* @return mixed|string
*/
public function redirectTo()
{
return Session::get('url.intended') ? Session::get('url.intended') : $this->redirectTo;
}
}
@@ -2,10 +2,10 @@
namespace App\Http\Controllers\Auth;
use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
@@ -27,7 +27,7 @@ class RegisterController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected string $redirectTo = '/';
/**
* Create a new controller instance.
@@ -41,11 +41,8 @@ class RegisterController extends Controller
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
protected function validator(array $data): \Illuminate\Contracts\Validation\Validator
{
return Validator::make($data, [
'name' => 'required|string|max:255',
@@ -56,11 +53,8 @@ class RegisterController extends Controller
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
protected function create(array $data): User
{
return User::create([
'name' => $data['name'],
@@ -25,7 +25,7 @@ class ResetPasswordController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected string $redirectTo = '/';
/**
* Create a new controller instance.
+20 -4
View File
@@ -2,12 +2,28 @@
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use App\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
use AuthorizesRequests, ValidatesRequests;
protected $user;
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = $this->user();
//print_r($this->user);
return $next($request);
});
}
public function user()
{
return User::currentUser();
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers;
use App\Item;
use App\User;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\RateLimiter;
class HealthController extends Controller
{
private static function getUsers(): int
{
return User::count();
}
private static function getItems(): int
{
return Item::select('id')
->where('deleted_at', null)
->where('type', '0')
->count();
}
/**
* Handle the incoming request.
*
* @return JsonResponse|Response
* @throws BindingResolutionException
*/
public function __invoke(Request $request)
{
$REQUESTS_MAX_PER_MIN = 30;
$STATUS_TOO_MANY_REQUESTS = 429;
if (RateLimiter::remaining('health', $REQUESTS_MAX_PER_MIN) < 1) {
return response()->make('Too many attempts.', $STATUS_TOO_MANY_REQUESTS);
}
RateLimiter::hit('health');
return response()->json([
'status' => 'ok',
'items' => self::getItems(),
'users' => self::getUsers(),
]);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->middleware('auth');
}
/**
* Show the application dashboard.
*/
public function index(): RedirectResponse
{
return redirect()->route('dash');
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\View\View;
class ImportController extends Controller
{
/**
* Instantiate a new controller instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->middleware('allowed');
}
/**
* Handle the incoming request.
*/
public function __invoke(Request $request): View
{
return view('items.import');
}
}
+479 -161
View File
@@ -2,238 +2,407 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Application;
use App\Item;
use App\Setting;
use App\SupportedApps\Nzbget;
use App\Jobs\ProcessApps;
use App\User;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ServerException;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Validation\ValidationException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Illuminate\Http\Response;
use enshrined\svgSanitize\Sanitizer;
class ItemController extends Controller
{
/**
* Display a listing of the resource on the dashboard.
*
* @return \Illuminate\Http\Response
*/
public function dash()
public function __construct()
{
$data['apps'] = Item::doesntHave('parents')->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::doesntHave('parents')->get();
parent::__construct();
$this->middleware('allowed');
}
/**
* Display a listing of the resource on the dashboard.
*/
public function dash(Request $request): View
{
$treat_tags_as = \App\Setting::fetch('treat_tags_as');
$data["treat_tags_as"] = $treat_tags_as;
if (config('app.auth_roles_enable')) {
$roles = explode(config('app.auth_roles_delimiter'), $request->header(config('app.auth_roles_header')));
if ($treat_tags_as == 'categories') {
$data['categories'] = Item::whereHas('children')->with('children', function ($query) {
$query->pinned()->orderBy('order', 'asc');
})->pinned()->orderBy('order', 'asc')->get();
} elseif ($treat_tags_as == 'tags') {
$data['apps'] = Item::with('parents')->where('type', 0)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::where('type', 0)->orderBy('order', 'asc')->get();
$data['taglist'] = Item::where('id', 0)->orWhere(function ($query) {
$query->where('type', 1)->pinned();
})->orderBy('order', 'asc')->get();
} else {
$data['apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->whereIn('role', $roles)->orWhere('type', 1)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere('type', 1)->orderBy('order', 'asc')->get();
}
} else {
if ($treat_tags_as == 'categories') {
$data['categories'] = Item::whereHas('children')->with('children', function ($query) {
$query->pinned()->orderBy('order', 'asc');
})->pinned()->orderBy('order', 'asc')->get();
} elseif ($treat_tags_as == 'tags') {
$data['apps'] = Item::with('parents')->where('type', 0)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::where('type', 0)->orderBy('order', 'asc')->get();
$data['taglist'] = Item::where('id', 0)->orWhere(function ($query) {
$query->where('type', 1)->pinned();
})->orderBy('order', 'asc')->get();
} else {
$data['apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere('type', 1)->pinned()->orderBy('order', 'asc')->get();
$data['all_apps'] = Item::whereHas('parents', function ($query) {
$query->where('id', 0);
})->orWhere(function ($query) {
$query->where('type', 1)->whereNot('id', 0);
})->orderBy('order', 'asc')->get();
}
}
//$data['all_apps'] = Item::doesntHave('parents')->get();
// die(print_r($data));
return view('welcome', $data);
}
/**
/**
* Set order on the dashboard.
*
* @return \Illuminate\Http\Response
* @return void
*/
public function setOrder(Request $request)
{
$order = array_filter($request->input('order'));
foreach($order as $o => $id) {
foreach ($order as $o => $id) {
$item = Item::find($id);
$item->order = $o;
$item->save();
}
}
/**
/**
* Pin item on the dashboard.
*
* @return \Illuminate\Http\Response
* @param $id
*/
public function pin($id)
public function pin($id): RedirectResponse
{
$item = Item::findOrFail($id);
$item->pinned = true;
$item->save();
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route);
}
/**
/**
* Unpin item on the dashboard.
*
* @return \Illuminate\Http\Response
* @param $id
*/
public function unpin($id)
public function unpin($id): RedirectResponse
{
$item = Item::findOrFail($id);
$item->pinned = false;
$item->save();
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route);
}
/**
/**
* Unpin item on the dashboard.
*
* @return \Illuminate\Http\Response
* @return RedirectResponse|View
*/
public function pinToggle($id, $ajax=false)
public function pinToggle($id, $ajax = false, $tag = false)
{
$item = Item::findOrFail($id);
$new = ((bool)$item->pinned === true) ? false : true;
$new = !(((bool)$item->pinned === true));
$item->pinned = $new;
$item->save();
if($ajax) {
$data['apps'] = Item::pinned()->get();
if ($ajax) {
$item = Item::whereId($tag)->first();
$data['apps'] = new Collection;
if ((int)$tag === 0) {
$tags = Item::where('type', 1)->pinned()->orderBy('order', 'asc')->get();
$data['apps'] = $data['apps']->merge($tags);
}
$apps = $item->children()->pinned()->orderBy('order', 'asc')->get();
$data['apps'] = $data['apps']->merge($apps);
$data['ajax'] = true;
return view('sortable', $data);
} else {
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route);
}
}
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
public function index(Request $request): View
{
$trash = (bool)$request->input('trash');
$data['apps'] = Item::ofType('item')->orderBy('title', 'asc')->get();
$data['trash'] = Item::ofType('item')->onlyTrashed()->get();
if($trash) {
if ($trash) {
return view('items.trash', $data);
} else {
return view('items.list', $data);
}
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
public function create(): View
{
//
$data['item'] = new \App\Item();
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
$data['current_tags'] = [];
return view('items.create', $data);
$data['tags']->prepend(__('app.dashboard'), 0);
$data['current_tags'] = '0';
return view('items.create', $data);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* Show the form for editing the specified resource.
*/
public function store(Request $request)
public function edit(int $id): View
{
//
// Get the item
$item = Item::find($id);
if ($item->appid === null && $item->class !== null) { // old apps won't have an app id so set it
$app = Application::where('class', $item->class)->first();
if ($app) {
$item->appid = $app->appid;
}
}
$data['item'] = $item;
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
$data['tags']->prepend(__('app.dashboard'), 0);
$data['current_tags'] = $data['item']->tags();
//$data['current_tags'] = $data['item']->parent;
//die(print_r($data['current_tags']));
// show the edit form and pass the nerd
return view('items.edit', $data);
}
/**
* @param null $id
* @throws ValidationException
*/
public static function storelogic(Request $request, $id = null): Item
{
$application = Application::single($request->input('appid'));
$validatedData = $request->validate([
'title' => 'required|max:255',
'url' => 'required|url',
'url' => 'required',
'file' => 'image'
]);
if($request->hasFile('file')) {
$path = $request->file('file')->store('icons');
if ($request->hasFile('file')) {
$image = $request->file('file');
$extension = $image->getClientOriginalExtension();
if ($extension === 'svg') {
$sanitizer = new Sanitizer();
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($image->getRealPath()));
// Verify that the sanitization removed malicious content
if (strpos($sanitizedSvg, '<script>') !== false) {
throw ValidationException::withMessages(['file' => 'SVG contains malicious content and cannot be uploaded.']);
}
// Save the sanitized SVG back to the file
file_put_contents($image->getRealPath(), $sanitizedSvg);
}
$path = $image->store('icons', 'public');
$request->merge([
'icon' => $path
'icon' => $path,
]);
} elseif (strpos($request->input('icon'), 'http') === 0) {
$options = [
"ssl" => [
"verify_peer" => false,
"verify_peer_name" => false,
],
];
// Proxy management
$httpsProxy = getenv('HTTPS_PROXY');
$httpsProxyLower = getenv('https_proxy');
if ($httpsProxy !== false || $httpsProxyLower !== false) {
$options['http']['proxy'] = $httpsProxy ?: $httpsProxyLower;
}
$file = $request->input('icon');
$path_parts = pathinfo($file);
if (!array_key_exists('extension', $path_parts)) {
throw ValidationException::withMessages(['file' => 'Icon URL must have a valid file extension.']);
}
$extension = $path_parts['extension'];
$contents = file_get_contents($request->input('icon'), false, stream_context_create($options));
if ($extension === 'svg') {
$sanitizer = new Sanitizer();
$contents = $sanitizer->sanitize($contents);
// Verify that the sanitization removed malicious content
if (strpos($contents, '<script>') !== false) {
throw ValidationException::withMessages(['file' => 'SVG contains malicious content and cannot be uploaded.']);
}
}
if (!isImage($contents, $extension)) {
throw ValidationException::withMessages(['file' => 'Icon must be an image.']);
}
$path = 'icons/' . ($application ? $application->icon : md5($contents) . '.' . $extension);
// Private apps could have here duplicated icons folder
if (strpos($path, 'icons/icons/') !== false) {
$path = str_replace('icons/icons/', 'icons/', $path);
}
if (!Storage::disk('public')->exists($path)) {
Storage::disk('public')->put($path, $contents);
}
$request->merge([
'icon' => $path,
]);
}
$config = Item::checkConfig($request->input('config'));
// Don't overwrite the stored password if it wasn't submitted when updating the item
if ($id !== null && strpos($config, '"password":null') !== false) {
$storedItem = Item::find($id);
$storedConfigObject = json_decode($storedItem->getAttribute('description'));
$configObject = json_decode($config);
if ($storedConfigObject && property_exists($storedConfigObject, 'password')) {
$configObject->password = $storedConfigObject->password;
} else {
$configObject->password = null;
}
$config = json_encode($configObject);
}
$request->merge([
'description' => $config
'description' => $config,
]);
//die(print_r($request->input('config')));
$item = Item::create($request->all());
// Only assign ownership when creating; updates must keep the existing owner.
if ($id === null) {
$current_user = User::currentUser();
$request->merge([
'user_id' => $current_user->getId(),
]);
}
if ($request->input('appid') === 'null' || $request->input('appid') === null) {
$request->merge([
'class' => null,
]);
} else {
$request->merge([
'class' => Application::classFromName($application->name),
]);
}
if ($id === null) {
$item = Item::create($request->all());
} else {
$item = Item::find($id);
// Exclude user_id so an update can never reassign ownership
$item->update($request->except(['user_id']));
}
$item->parents()->sync($request->tags);
return $item;
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
self::storelogic($request);
$route = route('dash', []);
$route = route('dash', [], false);
return redirect($route)
->with('success', __('app.alert.success.item_created'));
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
public function show(int $id): void
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
// Get the item
$data['item'] = Item::find($id);
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
$data['current_tags'] = $data['item']->parents;
// show the edit form and pass the nerd
return view('items.edit', $data);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
public function update(Request $request, int $id): RedirectResponse
{
$validatedData = $request->validate([
'title' => 'required|max:255',
'url' => 'required|url',
]);
//die(print_r($request->all()));
if($request->hasFile('file')) {
$path = $request->file('file')->store('icons');
$request->merge([
'icon' => $path
]);
}
$config = Item::checkConfig($request->input('config'));
$request->merge([
'description' => $config
]);
self::storelogic($request, $id);
$route = route('dash', []);
$item = Item::find($id);
$item->update($request->all());
$item->parents()->sync($request->tags);
$route = route('dash', [], false);
return redirect($route)
->with('success',__('app.alert.success.item_updated'));
->with('success', __('app.alert.success.item_updated'));
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Request $request, $id)
public function destroy(Request $request, int $id): RedirectResponse
{
//
$force = (bool)$request->input('force');
if($force) {
if ($force) {
Item::withTrashed()
->where('id', $id)
->forceDelete();
@@ -241,93 +410,242 @@ class ItemController extends Controller
Item::find($id)->delete();
}
$route = route('items.index', [], false);
return redirect($route)
->with('success',__('app.alert.success.item_deleted'));
$route = route('items.index', []);
return redirect($route)
->with('success', __('app.alert.success.item_deleted'));
}
/**
* Restore the specified resource from soft deletion.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function restore($id)
public function restore(int $id): RedirectResponse
{
//
Item::withTrashed()
->where('id', $id)
->restore();
$route = route('items.inded', [], false);
return redirect($route)
->with('success',__('app.alert.success.item_restored'));
}
->where('id', $id)
->restore();
public function isSupportedAppByKey($app)
{
$output = false;
$all_supported = Item::supportedList();
if(array_key_exists($app, $all_supported)) {
$output = new $all_supported[$app];
}
return $output;
$route = route('items.index', []);
return redirect($route)
->with('success', __('app.alert.success.item_restored'));
}
/**
* Return details for supported apps
*
* @return Json
* @throws GuzzleException
*/
public function appload(Request $request)
public function appload(Request $request): \Illuminate\Http\JsonResponse|string|null
{
$output = [];
$app = $request->input('app');
$appid = $request->input('app');
$itemId = $request->input('item_id');
if(($app_details = $this->isSupportedAppByKey($app)) !== false) {
// basic details
$output['icon'] = $app_details->icon();
$output['colour'] = $app_details->defaultColour();
if ($appid === 'null') {
return null;
}
// live details
if($app_details instanceof \App\SupportedApps\Contracts\Livestats) {
$output['config'] = $app_details->configDetails();
$app = Application::single($appid);
if (!$app) {
return response()->json(['error' => 'Application not found.'], 404);
}
$output = (array)$app;
$appdetails = Application::getApp($appid);
if (!$appdetails) {
return response()->json(['error' => 'Application details not found.'], 404);
}
if ((bool)$app->enhanced === true) {
$item = $itemId ? Item::find($itemId) : Item::where('appid', $appid)->first();
if ($item) {
$output['custom'] = className($appdetails->name) . '.config';
$output['appvalue'] = $item->description;
} else {
$output['config'] = null;
// Ensure the app is installed if not found
$output['custom'] = className($appdetails->name) . '.config';
$output['appvalue'] = null;
}
}
$output['colour'] = ($app->tile_background == 'light') ? '#fafbfc' : '#161b1f';
if (strpos($app->icon, '://') !== false) {
$output['iconview'] = $app->icon;
} elseif (strpos($app->icon, 'icons/') !== false) {
$output['iconview'] = URL::to('/') . '/storage/' . $app->icon;
$output['icon'] = str_replace('icons/', '', $output['icon']);
} else {
$output['iconview'] = config('app.appsource') . 'icons/' . $app->icon;
}
return json_encode($output);
}
/**
* @return void
*/
public function testConfig(Request $request)
{
$data = $request->input('data');
//$url = $data[array_search('url', array_column($data, 'name'))]['value'];
$app = $data['type'];
$single = Application::single($data['type']);
$app = $single->class;
// If password is not resubmitted fill it from the database when in edit mode
if (array_key_exists('password', $data) &&
$data['password'] === null &&
array_key_exists('id', $data)
) {
$item = Item::find($data['id']);
if ($item) {
$itemConfig = $item->getConfig();
$data['password'] = $itemConfig->password;
}
}
$app_details = new $app();
$app_details->config = (object)$data;
$app_details->testConfig();
$app_details->test();
}
/**
* @param $url
* @param array|bool $overridevars
* @throws GuzzleException
*/
public function execute($url, array $attrs = [], $overridevars = false): ?ResponseInterface
{
// Default Guzzle client configuration
$clientOptions = [
'http_errors' => false,
'timeout' => 15,
'connect_timeout' => 15,
'verify' => false, // In production, set this to `true` and manage certs.
];
// If the user provided overrides, use them.
if ($overridevars !== false) {
$clientOptions = $overridevars;
}
// Resolve the hostname to an IP address
$host = parse_url($url, PHP_URL_HOST);
$ip = gethostbyname($host);
// Check if the IP is private or reserved
$allowInternalIps = env('ALLOW_INTERNAL_REQUESTS', false);
if (!$allowInternalIps && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
Log::warning('Blocked access to private or reserved IPs.', ['ip' => $ip, 'host' => $host]);
abort(Response::HTTP_FORBIDDEN, 'Access to private or reserved IPs is not allowed.');
}
// Force Guzzle to use the resolved IP address
$clientOptions['curl'][CURLOPT_RESOLVE] = ["{$host}:80:{$ip}", "{$host}:443:{$ip}"];
$client = new Client($clientOptions);
$method = 'GET';
try {
return $client->request($method, $url, $attrs);
} catch (ConnectException $e) {
Log::warning('SSRF Attempt Blocked: Connection to a private IP was prevented.', [
'url' => $url,
'error' => $e->getMessage()
]);
return null;
} catch (ServerException $e) {
Log::debug($e->getMessage());
} catch (\Exception $e) {
Log::error('General error: ' . $e->getMessage());
}
return null;
}
/**
* @param $url
* @throws GuzzleException
*/
public function websitelookup($url): StreamInterface
{
$decodedUrl = base64_decode($url);
// Validate the URL format.
if (filter_var($decodedUrl, FILTER_VALIDATE_URL) === false) {
abort(Response::HTTP_BAD_REQUEST, 'Invalid URL format provided.');
}
$response = $this->execute($decodedUrl);
// If execute() returns null, it means the connection failed.
// This can happen for many reasons, including our SSRF protection kicking in.
if ($response === null) {
abort(Response::HTTP_FORBIDDEN, 'Access to the requested resource is not allowed or the resource is unavailable.');
}
return $response->getBody();
}
/**
* Return live stats for an enhanced application tile.
*
* Always responds with HTTP 200 and valid JSON so the frontend refresh
* loop (liveStatRefresh.js) keeps re-queueing the tile. On any failure we
* degrade gracefully to an inactive, empty tile instead of a 500.
*
* @param int|string $id
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
*/
public function getStats($id)
{
$item = Item::find($id);
$graceful = response()->json(['status' => 'inactive', 'html' => '']);
$config = json_decode($item->description);
if(isset($config->type)) {
$config->url = $item->url;
if(isset($config->override_url) && !empty($config->override_url)) {
$config->url = $config->override_url;
}
$app_details = new $config->type;
$app_details->config = $config;
echo $app_details->executeConfig();
$item = Item::find($id);
if ($item === null) {
return $graceful;
}
// Non-enhanced items (or stale records) have no live-stats class.
if (empty($item->class)) {
return $graceful;
}
try {
$config = $item->getconfig();
// Guard against a stale/renamed class string from the remote apps repo.
if (! class_exists($item->class)) {
return $graceful;
}
$application = new $item->class;
$application->config = $config;
// livestats() returns a JSON string; return it verbatim (no re-encoding).
return response($application->livestats());
} catch (\Throwable $e) {
Log::error('getStats failed for item '.$id.' ('.$item->class.'): '.$e->getMessage());
return $graceful;
}
}
/**
* @return \Illuminate\Contracts\Foundation\Application|RedirectResponse|Redirector
*/
public function checkAppList(): RedirectResponse
{
ProcessApps::dispatch();
$route = route('items.index');
return redirect($route)
->with('success', __('app.alert.success.updating'));
}
}
+176
View File
@@ -0,0 +1,176 @@
<?php
namespace App\Http\Controllers;
use App\Item;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Collection;
class ItemRestController extends Controller
{
public function __construct()
{
parent::__construct();
$this->middleware('allowed');
}
/**
* Display a listing of the resource.
*/
public function index(): Collection
{
$columns = [
'id',
'title',
'colour',
'url',
'description',
'appid',
'appdescription',
];
return Item::with('parents')
->select($columns)
->where('deleted_at', null)
->where('type', '0')
->orderBy('order', 'asc')
->get()
->map(function (Item $item) {
return [
'title' => $item->title,
'colour' => $item->colour,
'url' => $item->url,
'description' => $item->description,
'appid' => $item->appid,
'appdescription' => $item->appdescription,
'tags' => $item->parents
->where('id', '!=', 0)
->pluck('title')
->values()
->all(),
];
});
}
/**
* Show the form for creating a new resource.
*
* @return void
*/
public function create()
{
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): object
{
// Imports pass tags as an array of tag titles so they can round-trip
// across instances. Resolve those titles into local tag ids (creating
// any that don't yet exist) before handing off to the shared store
// logic. When no tags are supplied we keep the previous behaviour.
if ($request->has('tags')) {
$request->merge([
'tags' => $this->resolveTags($request->input('tags')),
]);
}
$item = ItemController::storelogic($request);
if ($item) {
return (object) ['status' => 'OK'];
}
return (object) ['status' => 'FAILED'];
}
/**
* Resolve an incoming list of tags into tag ids.
*
* Numeric 0 (or "0") maps to the root/default dashboard. Every other entry
* is treated as a tag title: an existing tag with that title is reused, and
* a missing one is created. The lookup keeps the operation idempotent so
* importing many items that share a tag title only ever creates one tag.
*
* @param mixed $tags
* @return array<int, int>
*/
private function resolveTags($tags): array
{
if (! is_array($tags)) {
return [0];
}
$ids = [];
foreach ($tags as $tag) {
if ($tag === 0 || $tag === '0') {
$ids[] = 0;
continue;
}
$title = is_string($tag) ? trim($tag) : $tag;
if ($title === '' || $title === null) {
continue;
}
$existing = Item::where('type', '1')
->where('title', $title)
->first();
if ($existing) {
$ids[] = (int) $existing->id;
continue;
}
$created = Item::create([
'title' => $title,
'type' => '1',
'url' => str_slug($title, '-', 'en_US'),
'user_id' => User::currentUser()->getId(),
]);
$ids[] = (int) $created->id;
}
$ids = array_values(array_unique($ids));
return empty($ids) ? [0] : $ids;
}
/**
* Display the specified resource.
*/
public function show(Item $item): Response
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Item $item): Response
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Item $item): Response
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Item $item): Response
{
//
}
}
+139
View File
@@ -0,0 +1,139 @@
<?php
namespace App\Http\Controllers;
use App\Search;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Http;
class SearchController extends Controller
{
/**
* @return Application|RedirectResponse|Redirector|mixed|void
*/
public function index(Request $request)
{
$requestprovider = $request->input('provider');
$query = $request->input('q');
// Sanitize the query to prevent XSS
$query = htmlspecialchars($query, ENT_QUOTES, 'UTF-8');
$provider = Search::providerDetails($requestprovider);
if (!$provider || !isset($provider->type)) {
abort(404, 'Invalid provider');
}
// If the query is empty, redirect to the provider's base URL
if (!$query || trim($query) === '') {
return redirect($provider->url);
}
if ($provider->type == 'standard') {
return redirect($provider->url.'?'.$provider->query.'='.urlencode($query));
} elseif ($provider->type == 'external') {
$class = new $provider->class;
return $class->getResults($query, $provider);
}
abort(404, 'Provider type not supported');
}
/**
* Get autocomplete suggestions for a search query
*
* @return JsonResponse
*/
public function autocomplete(Request $request)
{
$requestprovider = $request->input('provider');
$query = $request->input('q');
if (!$query || trim($query) === '') {
return response()->json([]);
}
$provider = Search::providerDetails($requestprovider);
if (!$provider || !isset($provider->autocomplete)) {
return response()->json([]);
}
// Replace {query} placeholder with actual query
$autocompleteUrl = str_replace('{query}', urlencode($query), $provider->autocomplete);
try {
$response = Http::timeout(5)->get($autocompleteUrl);
if ($response->successful()) {
$data = $response->body();
// Parse the response based on provider
$suggestions = $this->parseAutocompleteResponse($data, $provider->id);
return response()->json($suggestions);
}
} catch (\Exception $e) {
// Return empty array on error
return response()->json([]);
}
return response()->json([]);
}
/**
* Parse autocomplete response based on provider format
*
* @param string $data
* @param string $providerId
* @return array
*/
private function parseAutocompleteResponse($data, $providerId)
{
$suggestions = [];
switch ($providerId) {
case 'google':
// Google returns XML format
if (strpos($data, '<?xml') === 0) {
$xml = simplexml_load_string($data);
if ($xml && isset($xml->CompleteSuggestion)) {
foreach ($xml->CompleteSuggestion as $suggestion) {
if (isset($suggestion->suggestion['data'])) {
$suggestions[] = (string) $suggestion->suggestion['data'];
}
}
}
}
break;
case 'bing':
case 'ddg':
// Bing and DuckDuckGo return JSON array format
$json = json_decode($data, true);
if (is_array($json) && isset($json[1]) && is_array($json[1])) {
$suggestions = $json[1];
}
break;
default:
// Try to parse as JSON array
$json = json_decode($data, true);
if (is_array($json)) {
if (isset($json[1]) && is_array($json[1])) {
$suggestions = $json[1];
} else {
$suggestions = $json;
}
}
break;
}
return $suggestions;
}
}
+91 -59
View File
@@ -2,17 +2,23 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Setting;
use App\SettingGroup;
use App\Http\Controllers\Controller;
use Exception;
use enshrined\svgSanitize\Sanitizer;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class SettingsController extends Controller
{
/**
* @return \Illuminate\View\View
*/
public function index()
public function __construct()
{
parent::__construct();
$this->middleware('allowed');
}
public function index(): View
{
$settings = SettingGroup::with([
'settings',
@@ -24,87 +30,113 @@ class SettingsController extends Controller
}
/**
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
* @return RedirectResponse|View
*/
public function edit($id)
public function edit(int $id)
{
$setting = Setting::find($id);
//die("s: ".$setting->label);
if((bool)$setting->system === true) return abort(404);
if ((bool) $setting->system === true) {
return abort(404);
}
if (!is_null($setting)) {
if (! is_null($setting)) {
return view('settings.edit')->with([
'setting' => $setting,
'value' => $setting->value,
]);
} else {
$route = route('settings.list', [], false);
return redirect($route)
$route = route('settings.list', []);
return redirect($route)
->with([
'error' => __('app.alert.error.not_exist'),
'errors' => collect([__('app.alert.error.not_exist')]),
]);
}
}
/**
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $id)
public function update(Request $request, int $id): RedirectResponse
{
$setting = Setting::find($id);
$user = $this->user();
$route = route('settings.index', []);
if (!is_null($setting)) {
$data = Setting::getInput();
if ($setting->type == 'image') {
if($request->hasFile('value')) {
$path = $request->file('value')->store('backgrounds');
$setting->value = $path;
}
} else {
$setting->value = $data->value;
try {
if (is_null($setting)) {
throw new Exception('not_exists');
}
$setting->save();
if ($setting->type === 'image') {
$validatedData = $request->validate([
'value' => 'image',
]);
$route = route('settings.index', [], false);
return redirect($route)
->with([
'success' => __('app.alert.success.setting_updated'),
]);
} else {
$route = route('settings.index', [], false);
return redirect($route)
->with([
'error' => __('app.alert.error.not_exist'),
]);
if (!$request->hasFile('value')) {
throw new \Exception('file_too_big');
}
$image = $request->file('value');
$extension = $image->getClientOriginalExtension();
if ($extension === 'svg') {
$sanitizer = new Sanitizer();
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($image->getRealPath()));
// Verify that the sanitization removed malicious content
if (strpos($sanitizedSvg, '<script>') !== false) {
throw new \Exception('SVG contains malicious content and cannot be uploaded.');
}
// Save the sanitized SVG back to the file
file_put_contents($image->getRealPath(), $sanitizedSvg);
}
$path = $image->store('backgrounds', 'public');
if ($path === null) {
throw new \Exception('file_not_stored');
}
$setting_value = $path;
} else {
$data = Setting::getInput($request);
$setting_value = $data->value;
}
$user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => $setting_value]);
return redirect($route)
->with([
'success' => __('app.alert.success.setting_updated'),
]);
} catch (Exception $e) {
return redirect($route)
->with([
'errors' => collect([__('app.alert.error.' . $e->getMessage())]),
]);
}
}
/**
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
*/
public function clear($id)
public function clear(int $id): RedirectResponse
{
$user = $this->user();
$setting = Setting::find($id);
if((bool)$setting->system !== true) {
$setting->value = '';
$setting->save();
if ((bool) $setting->system !== true) {
$user->settings()->detach($setting->id);
$user->settings()->save($setting, ['uservalue' => '']);
}
$route = route('settings.index', [], false);
return redirect($route)
$route = route('settings.index', []);
return redirect($route)
->with([
'success' => __('app.alert.success.setting_updated'),
]);
}
public function search(Request $request)
{
}
}
+83 -64
View File
@@ -2,24 +2,33 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Item;
use DB;
use App\User;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class TagController extends Controller
{
public function __construct()
{
$this->middleware('allowed');
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
* @return Application|Factory|View
*/
public function index(Request $request)
public function index(Request $request): \Illuminate\View\View
{
$trash = (bool)$request->input('trash');
$trash = (bool) $request->input('trash');
$data['apps'] = Item::ofType('tag')->orderBy('title', 'asc')->get();
$data['trash'] = Item::ofType('tag')->onlyTrashed()->get();
if($trash) {
$data['apps'] = Item::ofType('tag')->where('id', '>', 0)->orderBy('title', 'asc')->get();
$data['trash'] = Item::ofType('tag')->where('id', '>', 0)->onlyTrashed()->get();
if ($trash) {
return view('tags.trash', $data);
} else {
return view('tags.list', $data);
@@ -29,44 +38,47 @@ class TagController extends Controller
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
* @return Application|Factory|View
*/
public function create()
public function create(): \Illuminate\View\View
{
$data = [];
return view('tags.create', $data);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
public function store(Request $request): RedirectResponse
{
$validatedData = $request->validate([
'title' => 'required|max:255',
'file' => 'image'
]);
if($request->hasFile('file')) {
$path = $request->file('file')->store('icons');
if ($request->hasFile('file')) {
$path = $request->file('file')->store('icons', 'public');
$request->merge([
'icon' => $path
'icon' => $path,
]);
}
$slug = str_slug($request->title, '-');
$slug = str_slug($request->title, '-', 'en_US');
$current_user = User::currentUser();
// set item type to tag
$request->merge([
'type' => '1',
'url' => $slug
'url' => $slug,
'user_id' => $current_user->getId(),
]);
//die(print_r($request->all()));
Item::create($request->all());
$route = route('dash', [], false);
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.tag_created'));
}
@@ -74,120 +86,127 @@ class TagController extends Controller
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
* @param $slug
*/
public function show($slug)
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);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
public function edit(int $id): View
{
// Get the item
$item = Item::find($id);
// show the edit form and pass the nerd
return view('tags.edit')
->with('item', $item);
->with('item', $item);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
public function update(Request $request, int $id): RedirectResponse
{
$validatedData = $request->validate([
'title' => 'required|max:255',
'file' => 'image'
]);
if($request->hasFile('file')) {
$path = $request->file('file')->store('icons');
if ($request->hasFile('file')) {
$path = $request->file('file')->store('icons', 'public');
$request->merge([
'icon' => $path
'icon' => $path,
]);
}
$slug = str_slug($request->title, '-');
$slug = str_slug($request->title, '-', 'en_US');
// set item type to tag
$request->merge([
'url' => $slug
'url' => $slug,
]);
Item::find($id)->update($request->all());
// Exclude user_id so an update can never reassign ownership
Item::find($id)->update($request->except(['user_id']));
$route = route('dash', []);
$route = route('dash', [], false);
return redirect($route)
->with('success',__('app.alert.success.tag_updated'));
->with('success', __('app.alert.success.tag_updated'));
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Request $request, $id)
public function destroy(Request $request, int $id): RedirectResponse
{
//
$force = (bool)$request->input('force');
if($force) {
$force = (bool) $request->input('force');
if ($force) {
Item::withTrashed()
->where('id', $id)
->forceDelete();
} else {
Item::find($id)->delete();
}
$route = route('tags.index', [], false);
$route = route('tags.index', []);
return redirect($route)
->with('success',__('app.alert.success.item_deleted'));
->with('success', __('app.alert.success.item_deleted'));
}
/**
* Restore the specified resource from soft deletion.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function restore($id)
public function restore(int $id): RedirectResponse
{
//
Item::withTrashed()
->where('id', $id)
->restore();
$route = route('tags.index', [], false);
->restore();
$route = route('tags.index', []);
return redirect($route)
->with('success',__('app.alert.success.item_restored'));
->with('success', __('app.alert.success.item_restored'));
}
public function add($tag, $item)
/**
* Add item to tag
*
* @param $tag
* @param $item
* @return int 1|0
*/
public function add($tag, $item): int
{
$output = 0;
$tag = Item::find($tag);
$item = Item::find($item);
if($tag && $item) {
if ($tag && $item) {
// only add items, not cats
if((int)$item->type === 0) {
if ((int) $item->type === 0) {
$tag->children()->attach($item);
return 1;
}
}
return $output;
}
return 0;
}
}
+170
View File
@@ -0,0 +1,170 @@
<?php
namespace App\Http\Controllers;
use App\Item;
use App\User;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
class UserController extends Controller
{
public function __construct()
{
parent::__construct();
$this->middleware('allowed')->except(['selectUser']);
}
/**
* Display a listing of the resource.
*/
public function index(): View
{
$data['users'] = User::all();
return view('users.index', $data);
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
$data = [];
return view('users.create', $data);
}
public function selectUser(): \Illuminate\View\View
{
Auth::logout();
$data['users'] = User::all();
return view('userselect', $data);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
$validatedData = $request->validate([
'username' => 'required|max:255|unique:users',
'email' => 'required|email',
'password' => 'nullable|confirmed',
'password_confirmation' => 'nullable',
'file' => 'image'
]);
$user = new User;
$user->username = $request->input('username');
$user->email = $request->input('email');
$user->public_front = $request->input('public_front');
$password = $request->input('password');
if (! empty($password)) {
$user->password = bcrypt($password);
}
if ($request->hasFile('file')) {
$path = $request->file('file')->store('avatars', 'public');
$user->avatar = $path;
}
if ((bool) $request->input('autologin_allow') === true) {
$user->autologin = (string) Str::uuid();
}
$user->save();
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.user_updated'));
}
/**
* Display the specified resource.
*/
public function show(int $id): void
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(User $user): View
{
$data['user'] = $user;
return view('users.edit', $data);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, User $user): RedirectResponse
{
$validatedData = $request->validate([
'username' => 'required|max:255|unique:users,username,'.$user->id,
'email' => 'required|email',
'password' => 'nullable|confirmed',
'password_confirmation' => 'nullable',
'file' => 'image'
]);
//die(print_r($request->all()));
$user->username = $request->input('username');
$user->email = $request->input('email');
$user->public_front = $request->input('public_front');
$password = $request->input('password');
if (! empty($password)) {
$user->password = bcrypt($password);
} elseif ($password == 'null') {
$user->password = null;
}
if ($request->hasFile('file')) {
$path = $request->file('file')->store('avatars', 'public');
$user->avatar = $path;
}
if ((bool) $request->input('autologin_allow') === true) {
$user->autologin = (is_null($user->autologin)) ? (string) Str::uuid() : $user->autologin;
} else {
$user->autologin = null;
}
$user->save();
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.user_updated'));
}
/**
* Remove the specified resource from storage.
*
* @return RedirectResponse | void
*/
public function destroy(User $user): RedirectResponse
{
if ($user->id !== 1) {
// Hard-delete this user's items (tiles and tags) so they don't become
// orphaned; item_tag pivot rows cascade via the existing FK. Shared
// items (user_id = 0) are left untouched.
Item::withoutGlobalScopes()->where('user_id', $user->id)->forceDelete();
$user->delete();
$route = route('dash', []);
return redirect($route)
->with('success', __('app.alert.success.user_deleted'));
}
}
}
-61
View File
@@ -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\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'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,
];
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace App\Http\Middleware;
use Symfony\Component\HttpFoundation\Response;
use App\User;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Session;
class CheckAllowed
{
/**
* Handle an incoming request.
*
* @throws AuthenticationException
*/
public function handle(Request $request, Closure $next): Response
{
$route = Route::currentRouteName();
$current_user = User::currentUser();
// Non admin users can't access users management
if (str_is('users*', $route)) {
if ($current_user->getId() !== 1) {
return redirect()->route('dash');
}
}
// Public access to frontpage
if ($route === 'dash' || $route === 'tags.show') {
if ((bool)$current_user->public_front === true) {
return $next($request);
}
}
// Continue with passwordless user
if (empty($current_user->password)) {
return $next($request);
}
// Check if user is logged in as $current_user
if (Auth::check()) {
$loggedin_user = Auth::user();
if ($loggedin_user->id === $current_user->getId()) {
return $next($request);
}
}
// Redirect to login
Auth::authenticate();
return redirect()->route('user.select');
}
}
-17
View File
@@ -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 = [
//
];
}
@@ -2,23 +2,20 @@
namespace App\Http\Middleware;
use Symfony\Component\HttpFoundation\Response;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @return mixed
*/
public function handle($request, Closure $next, $guard = null)
public function handle(Request $request, Closure $next, string $guard = null): Response
{
if (Auth::guard($guard)->check()) {
return redirect('/home');
return redirect()->intended();
}
return $next($request);
-18
View File
@@ -1,18 +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
*/
protected $except = [
'password',
'password_confirmation',
];
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* The allow-list is read from the TRUSTED_HOSTS env var (comma-separated
* hostnames). When it is unset/empty an empty array is returned so that NO
* host restriction is applied, preserving Heimdall's historic behaviour of
* running on arbitrary hosts. When set, only the listed hosts (and their
* subdomains) are accepted; any other Host header is rejected by Symfony
* with a SuspiciousOperationException (HTTP 400).
*
* @return array
*/
public function hosts()
{
$trustedHosts = env('TRUSTED_HOSTS');
if ($trustedHosts === null || trim((string) $trustedHosts) === '') {
return [];
}
$hosts = [];
foreach (explode(',', (string) $trustedHosts) as $host) {
$host = trim($host);
if ($host !== '') {
$hosts[] = '^(.+\.)?'.preg_quote($host).'$';
}
}
return $hosts;
}
/**
* Determine if the application should specify trusted hosts.
*
* The parent implementation skips enforcement whenever the app runs in the
* "local" environment (Heimdall's shipped default, see .env.example) or
* under the test runner, which would leave the TRUSTED_HOSTS allow-list
* silently unenforced for almost every real deployment. Instead we tie
* enforcement directly to configuration: apply the allow-list whenever one
* has actually been provided, in any environment. When TRUSTED_HOSTS is
* unset hosts() is empty and this returns false, preserving the historic
* no-restriction behaviour.
*
* @return bool
*/
protected function shouldSpecifyTrustedHosts()
{
return ! empty($this->hosts());
}
}
+40 -10
View File
@@ -2,28 +2,58 @@
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
* The default trusted proxies used when the TRUSTED_PROXIES env var is unset.
*
* @var array
*/
protected $defaultProxies = ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8', '127.0.0.1'];
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The current proxy header mappings.
*
* @var array
* Note: Request::HEADER_X_FORWARDED_HOST is intentionally NOT trusted to
* prevent Host header injection / open redirects (CVE-2025-50578). A spoofed
* X-Forwarded-Host header must never influence getHost()/url()/asset().
*
* @var int
*/
protected $headers = [
Request::HEADER_FORWARDED => 'FORWARDED',
Request::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR',
Request::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST',
Request::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT',
Request::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO',
];
protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
/**
* Create a new middleware instance.
*
* The set of trusted proxies is read from the TRUSTED_PROXIES env var
* (comma-separated CIDRs/IPs). When unset it falls back to the historic
* default list. The special value "*" trusts all proxies.
*
* @return void
*/
public function __construct()
{
$trustedProxies = env('TRUSTED_PROXIES');
if ($trustedProxies === null || trim((string) $trustedProxies) === '') {
$this->proxies = $this->defaultProxies;
} elseif (trim((string) $trustedProxies) === '*') {
$this->proxies = '*';
} else {
$this->proxies = array_values(array_filter(
array_map('trim', explode(',', (string) $trustedProxies)),
fn ($proxy) => $proxy !== ''
));
}
}
}
-21
View File
@@ -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'
];
}
+316 -109
View File
@@ -2,181 +2,272 @@
namespace App;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Symfony\Component\ClassLoader\ClassMapGenerator;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Casts\Attribute;
use stdClass;
use Symfony\Component\ClassLoader\ClassMapGenerator;
// @codingStandardsIgnoreStart
/**
* App\Item
*
* @property int $id
* @property string $title
* @property string|null $colour
* @property string|null $icon
* @property string $url
* @property string|null $description
* @property int $pinned
* @property int $order
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property int $type
* @property int $user_id
* @property string|null $class
* @property string|null $appid
* @property string|null $appdescription
* @property string|null $role
* @property-read \Illuminate\Database\Eloquent\Collection|Item[] $children
* @property-read int|null $children_count
* @property-read string $droppable
* @property-read \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\UrlGenerator|mixed|string $link
* @property-read string $link_icon
* @property-read string $link_target
* @property-read string $link_type
* @property-read \Illuminate\Database\Eloquent\Collection|Item[] $parents
* @property-read int|null $parents_count
* @property-read \App\User|null $user
* @method static \Database\Factories\ItemFactory factory(...$parameters)
* @method static Builder|Item newModelQuery()
* @method static Builder|Item newQuery()
* @method static Builder|Item ofType($type)
* @method static \Illuminate\Database\Query\Builder|Item onlyTrashed()
* @method static Builder|Item pinned()
* @method static Builder|Item query()
* @method static Builder|Item whereAppdescription($value)
* @method static Builder|Item whereRole($value)
* @method static Builder|Item whereAppid($value)
* @method static Builder|Item whereClass($value)
* @method static Builder|Item whereColour($value)
* @method static Builder|Item whereCreatedAt($value)
* @method static Builder|Item whereDeletedAt($value)
* @method static Builder|Item whereDescription($value)
* @method static Builder|Item whereIcon($value)
* @method static Builder|Item whereId($value)
* @method static Builder|Item whereOrder($value)
* @method static Builder|Item wherePinned($value)
* @method static Builder|Item whereTitle($value)
* @method static Builder|Item whereType($value)
* @method static Builder|Item whereUpdatedAt($value)
* @method static Builder|Item whereUrl($value)
* @method static Builder|Item whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|Item withTrashed()
* @method static \Illuminate\Database\Query\Builder|Item withoutTrashed()
* @mixin \Eloquent
*/
// @codingStandardsIgnoreEnd
class Item extends Model
{
use SoftDeletes;
//
use HasFactory;
protected static function boot(): void
{
parent::boot();
static::addGlobalScope('user_id', function (Builder $builder) {
$current_user = User::currentUser();
if ($current_user) {
$builder->where(function ($query) use ($current_user) {
$query->where('user_id', $current_user->getId())->orWhere('user_id', 0);
});
} else {
$builder->where('user_id', 0);
}
});
}
protected $fillable = [
'title', 'url', 'colour', 'icon', 'description', 'pinned', 'order', 'type'
'title',
'url',
'colour',
'icon',
'appdescription',
'description',
'pinned',
'order',
'type',
'class',
'user_id',
'tag_id',
'appid',
'role',
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['deleted_at'];
public static function supportedList()
{
return [
'Deluge' => \App\SupportedApps\Deluge::class,
'Dokuwiki' => \App\SupportedApps\Dokuwiki::class,
'Duplicati' => \App\SupportedApps\Duplicati::class,
'Emby' => \App\SupportedApps\Emby::class,
'Gitea' => \App\SupportedApps\Gitea::class,
'Graylog' => \App\SupportedApps\Graylog::class,
'Home Assistant' => \App\SupportedApps\HomeAssistant::class,
'Jackett' => \App\SupportedApps\Jackett::class,
'Jdownloader' => \App\SupportedApps\Jdownloader::class,
'Lidarr' => \App\SupportedApps\Lidarr::class,
'Mcmyadmin' => \App\SupportedApps\Mcmyadmin::class,
'Medusa' => \App\SupportedApps\Medusa::class,
'NZBGet' => \App\SupportedApps\Nzbget::class,
'Netdata' => \App\SupportedApps\Netdata::class,
'Nextcloud' => \App\SupportedApps\Nextcloud::class,
'Nzbhydra' => \App\SupportedApps\Nzbhydra::class,
'OPNSense' => \App\SupportedApps\Opnsense::class,
'Ombi' => \App\SupportedApps\Ombi::class,
'Openhab' => \App\SupportedApps\Openhab::class,
'Pihole' => \App\SupportedApps\Pihole::class,
'Plex' => \App\SupportedApps\Plex::class,
'Plexpy' => \App\SupportedApps\Plexpy::class,
'Plexrequests' => \App\SupportedApps\Plexrequests::class,
'Portainer' => \App\SupportedApps\Portainer::class,
'Proxmox' => \App\SupportedApps\Proxmox::class,
'Radarr' => \App\SupportedApps\Radarr::class,
'Runeaudio' => \App\SupportedApps\Runeaudio::class,
'Sabnzbd' => \App\SupportedApps\Sabnzbd::class,
'Sickrage' => \App\SupportedApps\Sickrage::class,
'Sonarr' => \App\SupportedApps\Sonarr::class,
'Traefik' => \App\SupportedApps\Traefik::class,
'Ttrss' => \App\SupportedApps\Ttrss::class,
'UniFi' => \App\SupportedApps\Unifi::class,
'pFsense' => \App\SupportedApps\Pfsense::class,
'ruTorrent' => \App\SupportedApps\ruTorrent::class,
];
}
public static function supportedOptions()
{
return array_keys(self::supportedList());
}
/**
* Scope a query to only include pinned items.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePinned($query)
public function scopePinned(Builder $query): Builder
{
return $query->where('pinned', 1);
}
public function getConfigAttribute()
{
$output = null;
$view = null;
if(isset($this->description) && !empty($this->description)){
$output = json_decode($this->description);
$output = is_object($output) ? $output : new \stdClass();
if(isset($output->type) && !empty($output->type)) {
$class = $output->type;
$sap = new $class();
$view = $sap->configDetails();
$output->view = $view;
}
if(!isset($output->dataonly)) $output->dataonly = '0';
}
return (object)$output;
}
public static function checkConfig($config)
{
if(empty($config)) {
// die(print_r($config));
if (empty($config)) {
$config = null;
} else {
$store = false;
//die(var_dump($config));
foreach($config as $key => $check) {
if($key == 'type') continue;
if($key == 'dataonly') continue;
if(!empty($check) && $check != '0') {
$store = true;
break;
}
}
//die(var_dump($store))
$config['enabled'] = ($store) ? true : false;
$config = json_encode($config);
}
return $config;
}
public function parents()
public function tags()
{
return $this->belongsToMany('App\Item', 'item_tag', 'item_id', 'tag_id');
}
public function children()
{
return $this->belongsToMany('App\Item', 'item_tag', 'tag_id', 'item_id');
$id = $this->id;
$tags = ItemTag::select('tag_id')->where('item_id', $id)->pluck('tag_id')->toArray();
$tagdetails = self::select('id', 'title', 'url', 'pinned')->whereIn('id', $tags)->get();
return $tagdetails;
}
protected function title(): Attribute
{
return Attribute::make(
get: fn (mixed $value) => ($value === 'app.dashboard' ? __('app.dashboard') : $value),
);
}
protected function tagUrl(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => ($attributes['id'] === 0 ? '0-dash' : $attributes['url']),
);
}
public function getTagClass(): string
{
$tags = $this->tags();
$slugs = [];
foreach ($tags as $tag) {
if ($tag->id === 0) {
$tag->url = '0-dash';
}
if ($tag->url) {
$slugs[] = 'tag-'.$tag->url;
}
}
return implode(' ', $slugs);
}
public function getTagList(): string
{
$tags = $this->tags();
$titles = [];
// print_r($tags);
foreach ($tags as $tag) {
if ($tag->title) {
$titles[] = $tag->title;
}
}
return implode(', ', $titles);
}
public function parents(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_tag', 'item_id', 'tag_id');
}
public function children(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_tag', 'tag_id', 'item_id');
}
/**
* @return \Illuminate\Contracts\Foundation\Application|UrlGenerator|mixed|string
*/
public function getLinkAttribute()
{
if((int)$this->type === 1) {
return '/tag/'.$this->url;
if ((int) $this->type === 1) {
return url('tag/'.$this->url);
} else {
return $this->url;
}
}
public function getDroppableAttribute()
public function getDroppableAttribute(): string
{
if((int)$this->type === 1) {
if ((int) $this->type === 1) {
return ' droppable';
} else {
return '';
}
}
public function getLinkTargetAttribute()
public function getLinkTargetAttribute(): string
{
if((int)$this->type === 1) {
$target = Setting::fetch('window_target');
if ((int) $this->type === 1 || $target === 'current') {
return '';
} else {
return ' target="_blank"';
return ' target="'.$target.'"';
}
}
public function getLinkIconAttribute()
public function getLinkIconAttribute(): string
{
if((int)$this->type === 1) {
if ((int) $this->type === 1) {
return 'fa-tag';
} else {
return 'fa-arrow-alt-to-right';
}
}
public function getLinkTypeAttribute()
public function getLinkTypeAttribute(): string
{
if((int)$this->type === 1) {
if ((int) $this->type === 1) {
return 'tags';
} else {
return 'items';
}
}
/**
* @param $class
* @return false|mixed|string
*/
public static function nameFromClass($class)
{
$explode = explode('\\', $class);
$name = end($explode);
return $name;
}
/**
* @param $query
* @param $type
* @return mixed
*/
public function scopeOfType($query, $type)
{
switch($type) {
switch ($type) {
case 'item':
$typeid = 0;
break;
@@ -188,5 +279,121 @@ class Item extends Model
return $query->where('type', $typeid);
}
public function enhanced(): bool
{
/*if(isset($this->class) && !empty($this->class)) {
$app = new $this->class;
} else {
return false;
}
return (bool)($app instanceof \App\EnhancedApps);*/
return $this->description !== null;
}
/**
* @param $class
*/
public static function isEnhanced($class): bool
{
if (!class_exists($class, false) || $class === null || $class === 'null') {
return false;
}
$app = new $class;
return (bool) ($app instanceof EnhancedApps);
}
/**
* @param $class
* @return false|mixed
*/
public static function isSearchProvider($class)
{
if (!class_exists($class, false) || $class === null || $class === 'null') {
return false;
}
$app = new $class;
return ((bool) ($app instanceof SearchInterface)) ? $app : false;
}
public function enabled(): bool
{
if ($this->enhanced()) {
$config = $this->getconfig();
if ($config) {
return (bool) $config->enabled;
}
}
return false;
}
/**
* @return mixed|stdClass
*/
public function getconfig()
{
// $explode = explode('\\', $this->class);
if (! isset($this->description) || empty($this->description)) {
$config = new stdClass;
// $config->name = end($explode);
$config->enabled = false;
$config->override_url = null;
$config->apikey = null;
return $config;
}
$config = json_decode($this->description);
// $config->name = end($explode);
$config->url = $this->url;
if (isset($config->override_url) && ! empty($config->override_url)) {
$config->url = $config->override_url;
} else {
$config->override_url = null;
}
return $config;
}
/**
* @param $class
*/
public static function applicationDetails($class): ?Application
{
if (! empty($class)) {
$name = self::nameFromClass($class);
$application = Application::where('name', $name)->first();
if ($application) {
return $application;
}
}
return null;
}
/**
* @param $class
*/
public static function getApplicationDescription($class): string
{
$details = self::applicationDetails($class);
if ($details !== null) {
return $details->description.' - '.$details->license;
}
return '';
}
/**
* Get the user that owns the item.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\Pivot;
/**
* App\ItemTag
*
* @property int $item_id
* @property int $tag_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag query()
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereItemId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereTagId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereUpdatedAt($value)
* @mixin \Eloquent
*/
class ItemTag extends Pivot
{
use HasFactory;
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace App\Jobs;
use App\Application;
use App\Item;
use App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Throwable;
class ProcessApps implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Most failures here are GitHub rate-limit responses; retries inside the
* same window do not help, so a single attempt is enough.
*/
public int $tries = 1;
/**
* Expire the ShouldBeUnique lock after 10 minutes so a crashed worker
* does not permanently block future ProcessApps dispatches.
*/
public int $uniqueFor = 600;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @throws GuzzleException
*/
public function handle(): void
{
Log::debug('Process Apps dispatched');
$localapps = Application::whereNull('class')->get();
$json = SupportedApps::getList()->getBody();
Storage::disk('local')->put('supportedapps.json', $json);
foreach ($localapps as $app) {
$app->class = $app->class();
$app->save();
}
$items = Item::whereNotNull('class')->get();
foreach ($items as $item) {
if (! file_exists(app_path('SupportedApps/'.Item::nameFromClass($item->class)))) {
$app = Application::where('class', $item->class)->first();
if ($app) {
Application::getApp($app->appid);
}
}
}
}
public function failed(Throwable $exception): void
{
Log::error(static::class . ' permanently failed', [
'exception_class' => $exception::class,
'exception_message' => $exception->getMessage(),
'file' => $exception->getFile() . ':' . $exception->getLine(),
]);
}
}
+77
View File
@@ -0,0 +1,77 @@
<?php
namespace App\Jobs;
use App\Application;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Throwable;
class UpdateApps implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Most failures here are GitHub rate-limit responses; retries inside the
* same window do not help, so a single attempt is enough. The throttle
* loop in handle() means the job is intentionally long-running, so we
* leave $timeout unset and let the operator's worker config govern.
*/
public int $tries = 1;
/**
* Expire the ShouldBeUnique lock after 10 minutes so a crashed worker
* does not permanently block future UpdateApps dispatches.
*/
public int $uniqueFor = 600;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @throws GuzzleException
*/
public function handle(): void
{
Log::debug('Update of all apps triggered!');
$apps = Application::all('appid')->toArray();
// We onl update the apps that are actually in use by items
// 1 sec delay after each update to throttle the requests
foreach ($apps as $appKey => $app) {
Application::getApp($app['appid']);
sleep(1);
}
Log::debug('Update of all apps finished!');
Cache::lock('updateApps')->forceRelease();
}
public function failed(Throwable $exception): void
{
Cache::lock('updateApps')->forceRelease();
Log::error(static::class . ' permanently failed', [
'exception_class' => $exception::class,
'exception_message' => $exception->getMessage(),
'file' => $exception->getFile() . ':' . $exception->getLine(),
]);
}
}
+170 -41
View File
@@ -2,69 +2,198 @@
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Artisan;
use Schema;
use App\Application;
use App\Jobs\ProcessApps;
use App\Jobs\UpdateApps;
use App\Setting;
use App\User;
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use App\Services\CustomFormBuilder;
use Spatie\Html\Html;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
public function boot(): void
{
$alt_bg = '';
if(!is_file(base_path('.env'))) {
touch(base_path('.env'));
Artisan::call('key:generate');
if (! class_exists('ZipArchive')) {
die('You are missing php-zip');
}
if(!is_file(database_path('app.sqlite'))) {
// first time setup
touch(database_path('app.sqlite'));
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
//Cache
//Artisan::call('config:cache');
//Artisan::call('route:cache');
}
if(is_file(database_path('app.sqlite'))) {
if(Schema::hasTable('settings')) {
if($bg_image = Setting::fetch('background_image')) {
$alt_bg = ' style="background-image: url(/storage/'.$bg_image.')"';
}
// check version to see if an upgrade is needed
$db_version = Setting::fetch('version');
$app_version = config('app.version');
if(version_compare($app_version, $db_version) == 1) { // app is higher than db, so need to run migrations etc
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
}
} else {
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
}
$lang = Setting::fetch('language');
\App::setLocale($lang);
$this->createEnvFile();
}
if(!is_file(public_path('storage'))) {
$this->setupDatabase();
if (! is_file(public_path('storage/.gitignore'))) {
Artisan::call('storage:link');
\Session::put('current_user', null);
}
view()->share('alt_bg', $alt_bg);
$applications = Application::all();
if ($applications->count() <= 0) {
ProcessApps::dispatch();
}
$lang = Setting::fetch('language');
\App::setLocale($lang);
// User specific settings need to go here as session isn't available at this point in the app
view()->composer('*', function ($view) {
if (isset($_SERVER['HTTP_AUTHORIZATION']) && ! empty($_SERVER['HTTP_AUTHORIZATION'])) {
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
}
if (! \Auth::check()) {
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])
&& ! empty($_SERVER['PHP_AUTH_USER']) && ! empty($_SERVER['PHP_AUTH_PW'])) {
$credentials = ['username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']];
if (\Auth::attempt($credentials, true)) {
// Authentication passed...
$user = \Auth::user();
//\Session::put('current_user', $user);
session(['current_user' => $user]);
}
} elseif (isset($_SERVER['REMOTE_USER']) && ! empty($_SERVER['REMOTE_USER'])) {
$user = User::where('username', $_SERVER['REMOTE_USER'])->first();
if ($user) {
\Auth::login($user, true);
session(['current_user' => $user]);
}
}
}
$alt_bg = '';
$trianglify = 'false';
$trianglify_seed = null;
if (Setting::fetch('trianglify')) {
$trianglify = 'true';
$trianglify_seed = Setting::fetch('trianglify_seed');
} elseif ($bg_image = Setting::fetch('background_image')) {
$alt_bg = ' style="background-image: url(storage/'.$bg_image.')"';
}
$allusers = User::all();
$current_user = User::currentUser();
$view->with('alt_bg', $alt_bg);
$view->with('trianglify', $trianglify);
$view->with('trianglify_seed', $trianglify_seed);
$view->with('allusers', $allusers);
$view->with('current_user', $current_user);
if (config('app.auth_roles_enable')) {
$view->with('enable_auth_admin_controls', in_array(config('app.auth_roles_admin'), explode(config('app.auth_roles_delimiter'), $_SERVER[config('app.auth_roles_http_header')])));
} else {
$view->with('enable_auth_admin_controls', true);
}
});
$this->app['view']->addNamespace('SupportedApps', app_path('SupportedApps'));
if (env('FORCE_HTTPS') === true) {
\URL::forceScheme('https');
}
if (env('APP_URL') != 'http://localhost') {
\URL::forceRootUrl(env('APP_URL'));
}
}
/**
* Generate app key if missing and .env exists
*/
public function genKey(): void
{
if (is_file(base_path('.env'))) {
if (empty(env('APP_KEY'))) {
Artisan::call('key:generate', ['--force' => true, '--no-interaction' => true]);
}
}
}
/**
* Register any application services.
*
* @return void
*/
public function register()
public function register(): void
{
if ($this->app->isLocal()) {
$this->app->register(IdeHelperServiceProvider::class);
}
$this->app->singleton('custom-form', function ($app) {
return new CustomFormBuilder($app->make(Html::class));
});
$this->app->singleton('settings', function () {
return new Setting();
});
}
/**
* Check if database needs an update or do first time database setup
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function setupDatabase(): void
{
$db_type = config()->get('database.default');
if ($db_type == 'sqlite') {
$db_file = config()->get('database.connections.sqlite.database');
Log::debug('SQLite Database Path: ' . $db_file);
// Do not create a file for the in-memory database identifier.
if ($db_file !== ':memory:' && ! is_file($db_file)) {
touch($db_file);
}
}
if ($this->needsDBUpdate()) {
Artisan::call('migrate', ['--path' => 'database/migrations', '--force' => true, '--seed' => true]);
ProcessApps::dispatchSync();
$this->updateApps();
}
}
public function createEnvFile(): void
{
if (!is_file(base_path('.env'))) {
copy(base_path('.env.example'), base_path('.env'));
}
$this->genKey();
}
private function needsDBUpdate(): bool
{
if (!Schema::hasTable('settings')) {
return true;
}
$db_version = Setting::_fetch('version');
$app_version = config('app.version');
return version_compare($app_version, $db_version) === 1;
}
private function updateApps(): void
{
// This lock ensures that the job is not invoked multiple times.
// In 5 minutes all app updates should be finished.
$lock = Cache::lock('updateApps', 5*60);
if ($lock->get()) {
UpdateApps::dispatchAfterResponse();
}
}
}
-30
View File
@@ -1,30 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
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.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
@@ -1,21 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Broadcast;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}
-32
View File
@@ -1,32 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
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.
*
* @return void
*/
public function boot()
{
parent::boot();
//
}
}
+6 -17
View File
@@ -2,8 +2,8 @@
namespace App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
@@ -12,16 +12,13 @@ class RouteServiceProvider extends ServiceProvider
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
* REMOVED WITH LARAVEL 8 UPGRADE
*/
protected $namespace = 'App\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
public function boot(): void
{
//
@@ -30,10 +27,8 @@ class RouteServiceProvider extends ServiceProvider
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
public function map(): void
{
$this->mapApiRoutes();
@@ -46,13 +41,10 @@ class RouteServiceProvider extends ServiceProvider
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
protected function mapWebRoutes(): void
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
@@ -60,14 +52,11 @@ class RouteServiceProvider extends ServiceProvider
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
protected function mapApiRoutes(): void
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
}
+149
View File
@@ -0,0 +1,149 @@
<?php
namespace App;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Request as Input;
use Yaml;
abstract class Search
{
/**
* List of all search providers
*
* @return Collection
*/
public static function providers(): Collection
{
$providers = self::standardProviders();
$providers = $providers + self::appProviders();
return collect($providers);
}
/**
* Gets details for a single provider
*
* @return false|object
*/
public static function providerDetails($provider)
{
$providers = self::providers();
if (! isset($providers[$provider])) {
return false;
}
return (object) $providers[$provider] ?? false;
}
/**
* Array of the standard providers
*
* @return array
*/
public static function standardProviders(): array
{
// $providers = json_decode(file_get_contents(storage_path('app/searchproviders.json')));
// print_r($providers);
$providers = Yaml::parseFile(storage_path('app/searchproviders.yaml'));
$all = [];
foreach ($providers as $key => $provider) {
$all[$key] = $provider;
$all[$key]['type'] = 'standard';
}
return $all;
}
/**
* Loops through users apps to see if app is a search provider, might be worth
* looking into caching this at some point
*
* @return array
*/
public static function appProviders(): array
{
$providers = [];
$userapps = Item::all();
foreach ($userapps as $app) {
if (empty($app->class)) {
continue;
}
if (($provider = Item::isSearchProvider($app->class)) !== false) {
$name = Item::nameFromClass($app->class);
$providers[$app->id] = [
'id' => $app->id,
'type' => $provider->type,
'class' => $app->class,
'url' => $app->url,
'name' => $app->title,
'colour' => $app->colour,
'icon' => $app->icon,
'description' => $app->description,
];
}
}
return $providers;
}
/**
* Outputs the search form
*
* @return string
*/
public static function form(): string
{
$output = '';
$homepage_search = Setting::fetch('homepage_search');
$search_provider = Setting::where('key', '=', 'search_provider')->first();
$user_search_provider = Setting::fetch('search_provider');
//die(print_r($search_provider));
//die(var_dump($user_search_provider));
// return early if search isn't applicable
if ((bool) $homepage_search !== true) {
return $output;
}
$user_search_provider = Input::get('p') ?? $user_search_provider ?? 'none';
if ((bool) $search_provider) {
if ((bool) $user_search_provider) {
$name = 'app.options.'.$user_search_provider;
$provider = self::providerDetails($user_search_provider);
$providers = self::providers();
$providerCount = count($providers);
// If there's only one provider, use its key instead of the user's setting
if ($providerCount === 1) {
$user_search_provider = $providers->keys()->first();
}
$output .= '<div class="searchform">';
$output .= '<form action="'.url('search').'"'.getLinkTargetAttribute().' method="get">';
$output .= '<div id="search-container" class="input-container">';
// Only show dropdown if there's more than one provider
if ($providerCount > 1) {
$output .= '<select name="provider">';
foreach ($providers as $key => $searchprovider) {
$selected = ((string) $key === (string) $user_search_provider) ? ' selected="selected"' : '';
$output .= '<option value="'.$key.'"'.$selected.'>'.$searchprovider['name'].'</option>';
}
$output .= '</select>';
} else {
// Hidden input for single provider
$output .= '<input type="hidden" name="provider" value="'.$user_search_provider.'" />';
}
$output .= '<input type="text" name="q" value="'.e(Input::get('q') ?? '').'" class="homesearch" autofocus placeholder="'.__('app.settings.search').'..." />';
$output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>';
$output .= '</div>';
$output .= '</form>';
$output .= '</div>';
}
}
return $output;
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace App;
interface SearchInterface
{
public function getResults($query, $providerdetails);
}
+67
View 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
}
+199 -103
View File
@@ -2,10 +2,46 @@
namespace App;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Input;
use Form;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Http\Request;
use Illuminate\Session\SessionManager;
use Illuminate\Session\Store;
use enshrined\svgSanitize\Sanitizer;
/**
* App\Setting
*
* @mixin Builder
* @property int $id
* @property int $group_id
* @property string $key
* @property string $type
* @property string|null $options
* @property string $label
* @property string|null $value
* @property string $order
* @property int $system
* @property-read mixed $edit_value
* @property-read mixed $list_value
* @property-read \App\SettingGroup|null $group
* @property-read \Illuminate\Database\Eloquent\Collection|\App\User[] $users
* @property-read int|null $users_count
* @method static Builder|Setting newModelQuery()
* @method static Builder|Setting newQuery()
* @method static Builder|Setting query()
* @method static Builder|Setting whereGroupId($value)
* @method static Builder|Setting whereId($value)
* @method static Builder|Setting whereKey($value)
* @method static Builder|Setting whereLabel($value)
* @method static Builder|Setting whereOptions($value)
* @method static Builder|Setting whereOrder($value)
* @method static Builder|Setting whereSystem($value)
* @method static Builder|Setting whereType($value)
* @method static Builder|Setting whereValue($value)
*/
class Setting extends Model
{
/**
@@ -16,7 +52,7 @@ class Setting extends Model
protected $table = 'settings';
protected $fillable = [
'id', 'group_id', 'key', 'type', 'options', 'label', 'value', 'order', 'system'
'id', 'group_id', 'key', 'type', 'options', 'label', 'value', 'order', 'system',
];
/**
@@ -33,41 +69,73 @@ class Setting extends Model
*/
protected static $cache = [];
/**
* @return array
*/
public static function getInput()
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' => Input::get('value'),
'image' => Input::file('value'),
'value' => $request->input('value'),
'image' => $image,
];
}
public function getListValueAttribute()
{
switch($this->type) {
if ((bool) $this->system === true) {
$value = self::_fetch($this->key);
} else {
$value = self::fetch($this->key);
}
$this->value = $value;
switch ($this->type) {
case 'image':
if(!empty($this->value)) {
$value = '<a href="'.asset('storage/'.$this->value).'" title="'.__('app.settings.view').'" target="_blank">'.__('app.settings.view').'</a>';
if (! empty($this->value)) {
$value = '<a href="'.asset('storage/'.$this->value).'" title="'.
__('app.settings.view').
'" target="_blank">'.
__('app.settings.view').
'</a>';
} else {
$value = __('app.options.none');
}
}
break;
case 'boolean':
if((bool)$this->value === true) {
if ((bool) $this->value === true) {
$value = __('app.options.yes');
} else {
$value = __('app.options.no');
}
}
break;
case 'select':
if(!empty($this->value) && $this->value !== 'none') {
$options = (array)json_decode($this->options);
$value = __($options[$this->value]);
if (! empty($this->value) && $this->value !== 'none') {
$options = (array) json_decode($this->options);
if ($this->key === 'search_provider') {
$options = Search::providers()->pluck('name', 'id')->toArray();
} elseif ($this->key === 'default_tag') {
$options = [];
$tags = Item::where('type', 1)->where('id', '>', 0)->pinned()->orderBy('title', 'asc')->get();
foreach ($tags as $tag) {
$options[$tag->tag_url] = $tag->title;
}
}
$value = (array_key_exists($this->value, $options))
? __($options[$this->value])
: __('app.options.none');
} else {
$value = __('app.options.none');
}
}
break;
default:
$value = __($this->value);
@@ -75,144 +143,172 @@ class Setting extends Model
}
return $value;
}
public function getEditValueAttribute()
{
switch($this->type) {
if ((bool) $this->system === true) {
$value = self::_fetch($this->key);
} else {
$value = self::fetch($this->key);
}
$this->value = $value;
switch ($this->type) {
case 'image':
$value = '';
if(isset($this->value) && !empty($this->value)) {
$value .= '<a class="setting-view-image" href="'.asset('storage/'.$this->value).'" title="'.__('app.settings.view').'" target="_blank"><img src="'.asset('storage/'.$this->value).'" /></a>';
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').'</a>';
$value .= '<input type="file" name="value" class="form-control" />';
if (isset($this->value) && !empty($this->value)) {
$value .= '<a class="settinglink" href="' .
route('settings.clear', $this->id) .
'" title="' .
__('app.settings.remove') .
'">' .
__('app.settings.reset') .
'</a>';
}
break;
case 'boolean':
$checked = false;
if(isset($this->value) && (bool)$this->value === true) $checked = true;
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);
foreach($options as $key => $opt) {
$options->$key = __($opt);
if ($this->key === 'search_provider') {
$options = Search::providers()->pluck('name', 'id');
} elseif ($this->key === 'default_tag') {
$options = ['' => 'app.options.none'];
$tags = Item::where('type', 1)->where('id', '>', 0)->pinned()->orderBy('title', 'asc')->get();
foreach ($tags as $tag) {
$options[$tag->tag_url] = $tag->title;
}
}
$value = Form::select('value', $options, null, ['class' => 'form-control']);
$value = '<select name="value" class="form-control">';
foreach ($options as $key => $opt) {
$value .= '<option value="' . $key . '" ' . (($this->value == $key) ? 'selected' : '') . '>' . __($opt) . '</option>';
}
$value .= '</select>';
break;
case 'textarea':
$value = '<textarea name="value" class="form-control" cols="44" rows="15">' . htmlspecialchars($this->value, ENT_QUOTES, 'UTF-8') . '</textarea>';
break;
default:
$value = 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()
public function group(): BelongsTo
{
return $this->belongsTo('App\SettingGroup', 'group_id');
return $this->belongsTo(\App\SettingGroup::class, 'group_id');
}
/**
* @param string $key
*
* @return mixed
*/
public static function fetch($key)
public static function fetch(string $key)
{
if (Setting::cached($key)) {
return Setting::$cache[$key];
} else {
$find = self::where('key', '=', $key)->first();
$user = self::user();
if (!is_null($find)) {
return self::_fetch($key, $user);
}
// @codingStandardsIgnoreStart
/**
*
* @return mixed
*/
public static function _fetch(string $key, $user = null)
{
// @codingStandardsIgnoreEnd
//$cachekey = ($user === null) ? $key : $key.'-'.$user->id;
//if (Setting::cached($cachekey)) {
// return Setting::$cache[$cachekey];
//} else {
$find = self::where('key', '=', $key)->first();
if (! is_null($find)) {
if ((bool) $find->system === true) { // if system variable use global value
$value = $find->value;
Setting::add($key, $value);
return $value;
} else {
return false;
} else { // not system variable so use user specific value
// check if user specified value has been set
//print_r($user);
$usersetting = $user->settings()->where('id', $find->id)->first();
//print_r($user->settings);
//die(var_dump($usersetting));
//->pivot->value;
//echo "user: ".$user->id." --- ".$usersettings;
if (isset($usersetting) && ! empty($usersetting)) {
$value = $usersetting->pivot->uservalue;
} else { // if not get default from base setting
//$user->settings()->save($find, ['value' => $find->value]);
//$has_setting = $user->settings()->where('id', $find->id)->exists();
//if($has_setting) {
// $user->settings()->updateExistingPivot($find->id, ['uservalue' => (string)$find->value]);
//} else {
// $user->settings()->save($find, ['uservalue' => (string)$find->value]);
//}
$value = $find->value;
}
}
//Setting::add($cachekey, $value);
return $value;
} else {
return false;
}
//}
}
/**
* @param string $key
* @param $value
*/
public static function add($key, $value)
public static function add(string $key, $value)
{
Setting::$cache[$key] = $value;
self::$cache[$key] = $value;
}
public static function cached(string $key): bool
{
return array_key_exists($key, self::$cache);
}
/**
* @param string $key
*
* @return bool
* The users that belong to the setting.
*/
public static function cached($key)
public function users(): BelongsToMany
{
return array_key_exists($key, Setting::$cache);
return $this->belongsToMany(\App\User::class)->using(\App\SettingUser::class)->withPivot('uservalue');
}
/**
* @return html
* @return \Illuminate\Contracts\Foundation\Application|SessionManager|Store|mixed
*/
public static function search()
public static function user()
{
$output = '';
$homepage_search = self::fetch('homepage_search');
$search_provider = self::where('key', '=', 'search_provider')->first();
//die(var_dump($search_provider->value));
// return early if search isn't applicable
if((bool)$homepage_search !== true) return $output;
if($search_provider->value === 'none') return $output;
if(empty($search_provider->value)) return $output;
if(is_null($search_provider->value)) return $output;
if((bool)$homepage_search && (bool)$search_provider) {
$options = (array)json_decode($search_provider->options);
$name = $options[$search_provider->value];
if((bool)$search_provider->value) {
switch($search_provider->value) {
case 'google':
$url = 'https://www.google.com/search';
$var = 'q';
break;
case 'ddg':
$url = 'https://duckduckgo.com/';
$var = 'q';
break;
case 'bing':
$url = 'https://www.bing.com/search';
$var = 'q';
break;
}
$output .= '<div class="searchform">';
$output .= Form::open(['url' => $url, 'method' => 'get']);
$output .= '<div class="input-container">';
$output .= Form::text($var, null, ['class' => 'homesearch', 'placeholder' => __($name).' '.__('app.settings.search').'...']);
$output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>';
$output .= '</div>';
$output .= Form::close();
$output .= '</div>';
}
}
return $output;
return User::currentUser();
}
}
+19 -2
View File
@@ -3,7 +3,24 @@
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* App\SettingGroup
*
* @property int $id
* @property string $title
* @property int $order
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Setting[] $settings
* @property-read int|null $settings_count
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup query()
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereOrder($value)
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereTitle($value)
* @mixin \Eloquent
*/
class SettingGroup extends Model
{
/**
@@ -20,8 +37,8 @@ class SettingGroup extends Model
*/
public $timestamps = false;
public function settings()
public function settings(): HasMany
{
return $this->hasMany('App\Setting', 'group_id');
return $this->hasMany(\App\Setting::class, 'group_id');
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
/**
* App\SettingUser
*
* @property int $setting_id
* @property int $user_id
* @property string|null $uservalue
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser query()
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereSettingId($value)
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereUserId($value)
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereUservalue($value)
* @mixin \Eloquent
*/
class SettingUser extends Pivot
{
//
}
+237
View File
@@ -0,0 +1,237 @@
<?php
namespace App;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ServerException;
use Illuminate\Support\Facades\Log;
use Psr\Http\Message\ResponseInterface;
abstract class SupportedApps
{
protected $jar = false;
protected $method = 'GET';
protected $error;
/**
* @param $url
* @param array $attrs
* @return object
* @throws GuzzleException
*/
public function appTest($url, array $attrs = []): object
{
if (empty($this->config->url)) {
return (object) [
'code' => 404,
'status' => 'No URL has been specified',
'response' => 'No URL has been specified',
];
}
$res = $this->execute($url, $attrs);
if ($res == null) {
return (object) [
'code' => null,
'status' => $this->error,
'response' => 'Connection failed',
];
}
switch ($res->getStatusCode()) {
case 200:
$status = 'Successfully communicated with the API';
break;
case 401:
$status = 'Failed: Invalid credentials';
break;
case 404:
$status = 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
$status = 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
return (object) [
'code' => $res->getStatusCode(),
'status' => $status,
'response' => $res->getBody(),
];
}
/**
* @param $url
* @param array $attrs
* @param array|bool|null $overridevars
* @param string|bool|null $overridemethod
* @return ResponseInterface|null
* @throws GuzzleException
*/
public function execute(
$url,
array $attrs = [],
$overridevars = null,
$overridemethod = null
): ?ResponseInterface {
$res = null;
$vars = ($overridevars === null || $overridevars === false) ?
[
'http_errors' => false,
'timeout' => 15,
'connect_timeout' => 15,
] : $overridevars;
// Check global setting to skip TLS verification (useful for self-signed certificates)
if (Setting::fetch('skip_tls_verification')) {
$vars['verify'] = false;
}
$client = new Client($vars);
$method = ($overridemethod === null || $overridemethod === false) ? $this->method : $overridemethod;
try {
return $client->request($method, $url, $attrs);
} catch (ConnectException $e) {
Log::error('Connection refused');
Log::debug($e->getMessage());
$this->error = 'Connection refused - '.(string) $e->getMessage();
} catch (ServerException $e) {
Log::debug($e->getMessage());
$this->error = (string) $e->getResponse()->getBody();
}
$this->error = 'General error connecting with API';
return $res;
}
/**
* @return void
*/
public function login()
{
}
/**
* @param string $url
* @param bool $addslash
* @return string
*/
public function normaliseurl(string $url, bool $addslash = true): string
{
$url = rtrim($url, '/');
if ($addslash) {
$url .= '/';
}
return $url;
}
/**
* @param $status
* @param $data
* @return false|string
*/
public function getLiveStats($status, $data)
{
$className = $this::class;
$explode = explode('\\', $className);
$name = end($explode);
$html = view('SupportedApps::'.$name.'.livestats', $data)->with('data', $data)->render();
return json_encode(['status' => $status, 'html' => $html]);
//return
}
/**
* @return ResponseInterface
* @throws GuzzleException
*/
public static function getList(): ResponseInterface
{
// $list_url = 'https://apps.heimdall.site/list';
$list_url = config('app.appsource').'list.json';
$client = new Client(['http_errors' => false, 'verify' => false, 'timeout' => 15, 'connect_timeout' => 15]);
return $client->request('GET', $list_url);
}
public static function configValue($item = null, $key = null)
{
if (isset($item) && ! empty($item)) {
return $item->getconfig()->$key;
} else {
return null;
}
}
/**
* @param $app
* @return bool|false
* @throws GuzzleException
*/
public static function getFiles($app): bool
{
Log::debug("Download triggered for ".print_r($app, true));
$zipurl = config('app.appsource').'files/'.$app->sha.'.zip';
$client = new Client(['http_errors' => false, 'timeout' => 60, 'connect_timeout' => 15, 'verify' => false]);
$res = $client->request('GET', $zipurl);
// Something went wrong?
if ($res->getStatusCode() !== 200) {
return false;
}
if (! file_exists(app_path('SupportedApps'))) {
mkdir(app_path('SupportedApps'), 0777, true);
}
$src = app_path('SupportedApps/'.className($app->name).'.zip');
file_put_contents($src, $res->getBody());
$zip = new \ZipArchive();
$x = $zip->open($src); // open the zip file to extract
if ($x === true) {
$zip->extractTo(app_path('SupportedApps')); // place in the directory with same name
$zip->close();
unlink($src); //Deleting the Zipped file
} else {
var_dump($x);
return false;
}
return true;
}
/**
* @param $details
* @param $app
* @return mixed
*/
public static function saveApp($details, $app)
{
$app->appid = $details->appid;
$app->name = $details->name;
$app->sha = $details->sha ?? null;
$app->icon = 'icons/'.$details->icon;
$app->website = $details->website;
$app->license = $details->license;
$appclass = $app->class();
$application = new $appclass;
$enhanced = (bool) ($application instanceof \App\EnhancedApps);
$app->class = $appclass;
$app->enhanced = $enhanced;
$app->tile_background = $details->tile_background;
$app->save();
return $app;
}
}
@@ -1,9 +0,0 @@
<?php namespace App\SupportedApps\Contracts;
interface Applications {
public function defaultColour();
public function icon();
}
-11
View File
@@ -1,11 +0,0 @@
<?php namespace App\SupportedApps\Contracts;
interface Livestats {
public function configDetails();
public function testConfig();
public function executeConfig();
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Deluge implements Contracts\Applications {
public function defaultColour()
{
return '#357';
}
public function icon()
{
return 'supportedapps/deluge.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Dokuwiki implements Contracts\Applications {
public function defaultColour()
{
return '#9d7056';
}
public function icon()
{
return 'supportedapps/dokuwiki.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Duplicati implements Contracts\Applications {
public function defaultColour()
{
return '#2c3744';
}
public function icon()
{
return 'supportedapps/duplicati.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Emby implements Contracts\Applications {
public function defaultColour()
{
return '#222';
}
public function icon()
{
return 'supportedapps/emby.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Gitea implements Contracts\Applications {
public function defaultColour()
{
return '#585e52';
}
public function icon()
{
return 'supportedapps/gitea.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Graylog implements Contracts\Applications {
public function defaultColour()
{
return '#158';
}
public function icon()
{
return 'supportedapps/graylog.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class HomeAssistant implements Contracts\Applications {
public function defaultColour()
{
return '#073c52';
}
public function icon()
{
return 'supportedapps/homeassistant.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Jackett implements Contracts\Applications {
public function defaultColour()
{
return '#484814';
}
public function icon()
{
return 'supportedapps/jackett.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Jdownloader implements Contracts\Applications {
public function defaultColour()
{
return '#2b494f';
}
public function icon()
{
return 'supportedapps/jdownloader.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Lidarr implements Contracts\Applications {
public function defaultColour()
{
return '#183c18';
}
public function icon()
{
return 'supportedapps/lidarr.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Mcmyadmin implements Contracts\Applications {
public function defaultColour()
{
return '#30404b';
}
public function icon()
{
return 'supportedapps/mcmyadmin.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Medusa implements Contracts\Applications {
public function defaultColour()
{
return '#4b5e55';
}
public function icon()
{
return 'supportedapps/medusa.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Netdata implements Contracts\Applications {
public function defaultColour()
{
return '#543737';
}
public function icon()
{
return 'supportedapps/netdata.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Nextcloud implements Contracts\Applications {
public function defaultColour()
{
return '#0e2c3e';
}
public function icon()
{
return 'supportedapps/nextcloud.png';
}
}
-82
View File
@@ -1,82 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Nzbget implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#253827';
}
public function icon()
{
return 'supportedapps/nzbget.png';
}
public function configDetails()
{
return 'nzbget';
}
public function testConfig()
{
$res = $this->buildRequest('status');
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$output = '';
$res = $this->buildRequest('status');
$data = json_decode($res->getBody());
//$data->result->RemainingSizeMB = '10000000';
//$data->result->DownloadRate = '100000000';
$size = $data->result->RemainingSizeMB;
$rate = $data->result->DownloadRate;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate, false, ' <span>');
if($size > 0 || $rate > 0) {
$output = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
';
}
return $output;
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$username = $config->username;
$password = $config->password;
$rebuild_url = str_replace('http://', 'http://'.$username.':'.$password.'@', $url);
$rebuild_url = str_replace('https://', 'https://'.$username.':'.$password.'@', $rebuild_url);
$rebuild_url = rtrim($rebuild_url, '/');
$api_url = $rebuild_url.'/jsonrpc/'.$endpoint;
$client = new Client(['http_errors' => false]);
$res = $client->request('GET', $api_url);
return $res;
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Nzbhydra implements Contracts\Applications {
public function defaultColour()
{
return '#53644d';
}
public function icon()
{
return 'supportedapps/nzbhydra.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Ombi implements Contracts\Applications {
public function defaultColour()
{
return '#150f09';
}
public function icon()
{
return 'supportedapps/ombi.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Openhab implements Contracts\Applications {
public function defaultColour()
{
return '#2b2525';
}
public function icon()
{
return 'supportedapps/openhab.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Opnsense implements Contracts\Applications {
public function defaultColour()
{
return '#211914';
}
public function icon()
{
return 'supportedapps/opnsense.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Pfsense implements Contracts\Applications {
public function defaultColour()
{
return '#4e4742';
}
public function icon()
{
return 'supportedapps/pfsense.png';
}
}
-72
View File
@@ -1,72 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Pihole implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#352222';
}
public function icon()
{
return 'supportedapps/pihole.png';
}
public function configDetails()
{
return 'pihole';
}
public function testConfig()
{
$res = $this->buildRequest();
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$output = '';
$res = $this->buildRequest();
$data = json_decode($res->getBody());
$output = '
<ul class="livestats">
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
</ul>
';
return $output;
}
public function buildRequest()
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/api.php';
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false]);
$res = $client->request('GET', $api_url);
return $res;
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Plex implements Contracts\Applications {
public function defaultColour()
{
return '#222';
}
public function icon()
{
return 'supportedapps/plex.png';
}
}
-76
View File
@@ -1,76 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Plexpy implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#2d2208';
}
public function icon()
{
return 'supportedapps/plexpy.png';
}
public function configDetails()
{
return 'plexpy';
}
public function testConfig()
{
$res = $this->buildRequest('arnold');
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(isset($data->error) && !empty($data->error)) {
echo 'Failed: '.$data->error;
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$output = '';
$res = $this->buildRequest('get_activity');
$data = json_decode($res->getBody());
$stream_count = $data->response->data->stream_count;
$output = '
<ul class="livestats">
<li><span class="title">Stream Count</span><strong>'.$stream_count.'</strong></li>
</ul>
';
return $output;
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$apikey = $config->apikey;
$url = rtrim($url, '/');
$api_url = $url.'/api/v2?apikey='.$apikey.'&cmd='.$endpoint;
$client = new Client(['http_errors' => false]);
$res = $client->request('GET', $api_url);
return $res;
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Plexrequests implements Contracts\Applications {
public function defaultColour()
{
return '#3c2d1c';
}
public function icon()
{
return 'supportedapps/plexrequests.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Portainer implements Contracts\Applications {
public function defaultColour()
{
return '#283f44';
}
public function icon()
{
return 'supportedapps/portainer.png';
}
}
-80
View File
@@ -1,80 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Proxmox implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#542e0a';
}
public function icon()
{
return 'supportedapps/proxmox.png';
}
public function configDetails()
{
//return 'proxmox';
return null;
}
public function testConfig()
{
/*$res = $this->buildRequest();
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}*/
return null;
}
public function executeConfig()
{
/*
$output = '';
$res = $this->buildRequest();
$data = json_decode($res->getBody());
$output = '
<ul class="livestats">
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
</ul>
';
return $output;
*/
return null;
}
public function buildRequest($endpoint='')
{
$config = $this->config;
$username = $config->username;
$password = $config->password;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/api2/json/'.$endpoint.'?username='.$username.'&password='.$password;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false, 'verify' => false ]);
$res = $client->request('GET', $api_url);
return $res;
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Radarr implements Contracts\Applications {
public function defaultColour()
{
return '#3e3726';
}
public function icon()
{
return 'supportedapps/radarr.png';
}
}
-94
View File
@@ -1,94 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Runeaudio implements Contracts\Applications, Contracts\Livestats {
public function defaultColour()
{
return '#05A';
}
public function icon()
{
return 'supportedapps/runeaudio.png';
}
public function configDetails()
{
return 'runeaudio';
}
public function testConfig()
{
$res = $this->buildRequest('status');
switch($res->getStatusCode()) {
case 200:
echo 'Successfully connected to the API';
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$output = '';
$artist = '';
$song_title = '';
$res = $this->buildRequest('currentsong');
$array = explode("\n", $res->getBody());
foreach($array as $item) {
$item_array = explode(": ", $item);
if ($item_array[0] == 'Artist') {
$artist = $item_array[1];
} elseif ($item_array[0] == 'Title') {
$song_title = $item_array[1];
}
}
$output = '<ul class="livestats">';
if (strlen($artist) > 12) {
$output = $output.'<li><span class="title-marquee"><span>'.$artist.'</span></span></li>';
} else {
$output = $output.'<li><span class="title">'.$artist.'</span></li>';
}
$output = $output.'</ul><ul class="livestats">';
if (strlen($song_title) > 12) {
$output = $output.'<li><span class="title-marquee"><span>'.$song_title.'</span></span></li>';
} else {
$output = $output.'<li><span class="title">'.$song_title.'</span></li>';
}
$output = $output.'</ul>';
return $output;
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$url = rtrim($url, '/');
$api_url = $url.'/command/?cmd='.$endpoint;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false]);
$res = $client->request('GET', $api_url);
return $res;
}
}
-84
View File
@@ -1,84 +0,0 @@
<?php namespace App\SupportedApps;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class Sabnzbd implements Contracts\Applications, Contracts\Livestats {
public $config;
public function defaultColour()
{
return '#3e3924';
}
public function icon()
{
return 'supportedapps/sabnzbd.png';
}
public function configDetails()
{
return 'sabnzbd';
}
public function testConfig()
{
$res = $this->buildRequest('queue');
switch($res->getStatusCode()) {
case 200:
$data = json_decode($res->getBody());
if(isset($data->error) && !empty($data->error)) {
echo 'Failed: '.$data->error;
} else {
echo 'Successfully connected to the API';
}
break;
case 401:
echo 'Failed: Invalid credentials';
break;
case 404:
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
break;
default:
echo 'Something went wrong... Code: '.$res->getStatusCode();
break;
}
}
public function executeConfig()
{
$output = '';
$res = $this->buildRequest('queue');
$data = json_decode($res->getBody());
//$data->result->RemainingSizeMB = '10000000';
//$data->result->DownloadRate = '100000000';
$size = $data->queue->mbleft;
$rate = $data->queue->kbpersec;
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
$current_speed = format_bytes($rate*1000, false, ' <span>');
if($size > 0 || $rate > 0) {
$output = '
<ul class="livestats">
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
</ul>
';
}
return $output;
}
public function buildRequest($endpoint)
{
$config = $this->config;
$url = $config->url;
$apikey = $config->apikey;
$url = rtrim($url, '/');
$api_url = $url.'/api?output=json&apikey='.$apikey.'&mode='.$endpoint;
//die( $api_url.' --- ');
$client = new Client(['http_errors' => false]);
$res = $client->request('GET', $api_url);
return $res;
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Sickrage implements Contracts\Applications {
public function defaultColour()
{
return '#6185a6';
}
public function icon()
{
return 'supportedapps/sickrage.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Sonarr implements Contracts\Applications {
public function defaultColour()
{
return '#163740';
}
public function icon()
{
return 'supportedapps/sonarr.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Traefik implements Contracts\Applications {
public function defaultColour()
{
return '#28434a';
}
public function icon()
{
return 'supportedapps/traefik.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Ttrss implements Contracts\Applications {
public function defaultColour()
{
return '#9d704c';
}
public function icon()
{
return 'supportedapps/tt-rss.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class Unifi implements Contracts\Applications {
public function defaultColour()
{
return '#363840';
}
public function icon()
{
return 'supportedapps/unifi.png';
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php namespace App\SupportedApps;
class ruTorrent implements Contracts\Applications {
public function defaultColour()
{
return '#004';
}
public function icon()
{
return 'supportedapps/rutorrent.png';
}
}
+83 -2
View File
@@ -2,20 +2,61 @@
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
// @codingStandardsIgnoreStart
/**
* App\User
*
* @property int $id
* @property string $username
* @property string $email
* @property string|null $avatar
* @property string|null $password
* @property string|null $autologin
* @property int $public_front
* @property string|null $remember_token
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Item[] $items
* @property-read int|null $items_count
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications
* @property-read int|null $notifications_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Setting[] $settings
* @property-read int|null $settings_count
* @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User query()
* @method static \Illuminate\Database\Eloquent\Builder|User whereAutologin($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereAvatar($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|User wherePassword($value)
* @method static \Illuminate\Database\Eloquent\Builder|User wherePublicFront($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereUsername($value)
* @mixin \Eloquent
*/
// @codingStandardsIgnoreEnd
class User extends Authenticatable
{
use Notifiable;
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
'username', 'email', 'password',
];
/**
@@ -26,4 +67,44 @@ class User extends Authenticatable
protected $hidden = [
'password', 'remember_token',
];
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* Get the items for the user.
*/
public function items(): HasMany
{
return $this->hasMany(Item::class);
}
/**
* The settings that belong to the user.
*/
public function settings(): BelongsToMany
{
return $this->belongsToMany(Setting::class)->withPivot('uservalue');
}
public static function currentUser()
{
$current_user = session('current_user');
if ($current_user) { // if logged in, set this user
return $current_user;
} else { // not logged in, get first user
$user = self::where('public_front', true)->first();
if (! $user) {
$user = self::first();
}
session(['current_user' => $user]);
return $user;
}
}
}
+6 -44
View File
@@ -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);

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