diff --git a/.env.example b/.env.example
index 10b0cc1bd..d9351be02 100644
--- a/.env.example
+++ b/.env.example
@@ -99,6 +99,9 @@ REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_CLIENT=phpredis
+# Horizon (Redis queue dashboard and workers)
+HORIZON_PREFIX=horizon:
+
SESSION_DRIVER=redis
SESSION_DOMAIN=null
SESSION_SECURE_COOKIE=false
diff --git a/app/Providers/HorizonServiceProvider.php b/app/Providers/HorizonServiceProvider.php
new file mode 100644
index 000000000..5e77b055c
--- /dev/null
+++ b/app/Providers/HorizonServiceProvider.php
@@ -0,0 +1,34 @@
+hasRole('Admin');
+ });
+ }
+}
diff --git a/bootstrap/providers.php b/bootstrap/providers.php
index a877118bc..41f7a9d9c 100644
--- a/bootstrap/providers.php
+++ b/bootstrap/providers.php
@@ -5,10 +5,11 @@ return [
App\Providers\AppServiceProvider::class,
App\Providers\CategorizationServiceProvider::class,
App\Providers\ForumServiceProvider::class,
+ App\Providers\HorizonServiceProvider::class,
App\Providers\ProcessingServiceProvider::class,
App\Providers\RouteServiceProvider::class,
+ App\Providers\SearchServiceProvider::class,
App\Providers\TvProcessingServiceProvider::class,
App\Providers\UserServiceProvider::class,
App\Providers\VoltServiceProvider::class,
- App\Providers\SearchServiceProvider::class,
];
diff --git a/composer.json b/composer.json
index 4956db2fa..770dc09bc 100644
--- a/composer.json
+++ b/composer.json
@@ -59,6 +59,7 @@
"intervention/image": "^3.11",
"jrean/laravel-user-verification": "^13.0",
"laravel/framework": "^12.28",
+ "laravel/horizon": "^5.23",
"laravel/pulse": "^1.4",
"laravel/sanctum": "^4.0",
"laravel/scout": "^10.13",
diff --git a/composer.lock b/composer.lock
index 9ce16ad8a..b08b821b7 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "8de687f72473f4ec9902d492ff625378",
+ "content-hash": "ca95edd36c83ffac21d43b5a1330100a",
"packages": [
{
"name": "aharen/omdbapi",
@@ -2839,6 +2839,86 @@
},
"time": "2026-02-24T14:35:15+00:00"
},
+ {
+ "name": "laravel/horizon",
+ "version": "v5.45.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/horizon.git",
+ "reference": "7126ddf27fe9750c43ab0b567085dee3917d0510"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/horizon/zipball/7126ddf27fe9750c43ab0b567085dee3917d0510",
+ "reference": "7126ddf27fe9750c43ab0b567085dee3917d0510",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pcntl": "*",
+ "ext-posix": "*",
+ "illuminate/contracts": "^9.21|^10.0|^11.0|^12.0|^13.0",
+ "illuminate/queue": "^9.21|^10.0|^11.0|^12.0|^13.0",
+ "illuminate/support": "^9.21|^10.0|^11.0|^12.0|^13.0",
+ "laravel/sentinel": "^1.0",
+ "nesbot/carbon": "^2.17|^3.0",
+ "php": "^8.0",
+ "ramsey/uuid": "^4.0",
+ "symfony/console": "^6.0|^7.0|^8.0",
+ "symfony/error-handler": "^6.0|^7.0|^8.0",
+ "symfony/polyfill-php83": "^1.28",
+ "symfony/process": "^6.0|^7.0|^8.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.0",
+ "orchestra/testbench": "^7.56|^8.37|^9.16|^10.9|^11.0",
+ "phpstan/phpstan": "^1.10|^2.0",
+ "predis/predis": "^1.1|^2.0|^3.0"
+ },
+ "suggest": {
+ "ext-redis": "Required to use the Redis PHP driver.",
+ "predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0|^3.0)."
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "aliases": {
+ "Horizon": "Laravel\\Horizon\\Horizon"
+ },
+ "providers": [
+ "Laravel\\Horizon\\HorizonServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-master": "6.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Horizon\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Dashboard and code-driven configuration for Laravel queues.",
+ "keywords": [
+ "laravel",
+ "queue"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/horizon/issues",
+ "source": "https://github.com/laravel/horizon/tree/v5.45.0"
+ },
+ "time": "2026-02-21T14:20:09+00:00"
+ },
{
"name": "laravel/prompts",
"version": "v0.3.13",
diff --git a/config/horizon.php b/config/horizon.php
new file mode 100644
index 000000000..777026bb3
--- /dev/null
+++ b/config/horizon.php
@@ -0,0 +1,254 @@
+ env('HORIZON_NAME'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Domain
+ |--------------------------------------------------------------------------
+ |
+ | This is the subdomain where Horizon will be accessible from. If this
+ | setting is null, Horizon will reside under the same domain as the
+ | application. Otherwise, this value will serve as the subdomain.
+ |
+ */
+
+ 'domain' => env('HORIZON_DOMAIN'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Path
+ |--------------------------------------------------------------------------
+ |
+ | This is the URI path where Horizon will be accessible from. Feel free
+ | to change this path to anything you like. Note that the URI will not
+ | affect the paths of its internal API that aren't exposed to users.
+ |
+ */
+
+ 'path' => env('HORIZON_PATH', 'horizon'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Redis Connection
+ |--------------------------------------------------------------------------
+ |
+ | This is the name of the Redis connection where Horizon will store the
+ | meta information required for it to function. It includes the list
+ | of supervisors, failed jobs, job metrics, and other information.
+ |
+ */
+
+ 'use' => 'default',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Redis Prefix
+ |--------------------------------------------------------------------------
+ |
+ | This prefix will be used when storing all Horizon data in Redis. You
+ | may modify the prefix when you are running multiple installations
+ | of Horizon on the same server so that they don't have problems.
+ |
+ */
+
+ 'prefix' => env(
+ 'HORIZON_PREFIX',
+ Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
+ ),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Horizon Route Middleware
+ |--------------------------------------------------------------------------
+ |
+ | These middleware will get attached onto each Horizon route, giving you
+ | the chance to add your own middleware to this list or change any of
+ | the existing middleware. Or, you can simply stick with this list.
+ |
+ */
+
+ 'middleware' => ['web', 'auth', 'role:Admin', '2fa'],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Queue Wait Time Thresholds
+ |--------------------------------------------------------------------------
+ |
+ | This option allows you to configure when the LongWaitDetected event
+ | will be fired. Every connection / queue combination may have its
+ | own, unique threshold (in seconds) before this event is fired.
+ |
+ */
+
+ 'waits' => [
+ 'redis:default' => 60,
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Job Trimming Times
+ |--------------------------------------------------------------------------
+ |
+ | Here you can configure for how long (in minutes) you desire Horizon to
+ | persist the recent and failed jobs. Typically, recent jobs are kept
+ | for one hour while all failed jobs are stored for an entire week.
+ |
+ */
+
+ 'trim' => [
+ 'recent' => 60,
+ 'pending' => 60,
+ 'completed' => 60,
+ 'recent_failed' => 10080,
+ 'failed' => 10080,
+ 'monitored' => 10080,
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Silenced Jobs
+ |--------------------------------------------------------------------------
+ |
+ | Silencing a job will instruct Horizon to not place the job in the list
+ | of completed jobs within the Horizon dashboard. This setting may be
+ | used to fully remove any noisy jobs from the completed jobs list.
+ |
+ */
+
+ 'silenced' => [
+ // App\Jobs\ExampleJob::class,
+ ],
+
+ 'silenced_tags' => [
+ // 'notifications',
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Metrics
+ |--------------------------------------------------------------------------
+ |
+ | Here you can configure how many snapshots should be kept to display in
+ | the metrics graph. This will get used in combination with Horizon's
+ | `horizon:snapshot` schedule to define how long to retain metrics.
+ |
+ */
+
+ 'metrics' => [
+ 'trim_snapshots' => [
+ 'job' => 24,
+ 'queue' => 24,
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Fast Termination
+ |--------------------------------------------------------------------------
+ |
+ | When this option is enabled, Horizon's "terminate" command will not
+ | wait on all of the workers to terminate unless the --wait option
+ | is provided. Fast termination can shorten deployment delay by
+ | allowing a new instance of Horizon to start while the last
+ | instance will continue to terminate each of its workers.
+ |
+ */
+
+ 'fast_termination' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Memory Limit (MB)
+ |--------------------------------------------------------------------------
+ |
+ | This value describes the maximum amount of memory the Horizon master
+ | supervisor may consume before it is terminated and restarted. For
+ | configuring these limits on your workers, see the next section.
+ |
+ */
+
+ 'memory_limit' => 64,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Queue Worker Configuration
+ |--------------------------------------------------------------------------
+ |
+ | Here you may define the queue worker settings used by your application
+ | in all environments. These supervisors and settings handle all your
+ | queued jobs and will be provisioned by Horizon during deployment.
+ |
+ */
+
+ 'defaults' => [
+ 'supervisor-1' => [
+ 'connection' => 'redis',
+ 'queue' => ['default'],
+ 'balance' => 'auto',
+ 'autoScalingStrategy' => 'time',
+ 'maxProcesses' => 1,
+ 'maxTime' => 0,
+ 'maxJobs' => 0,
+ 'memory' => 128,
+ 'tries' => 1,
+ 'timeout' => 60,
+ 'nice' => 0,
+ ],
+ ],
+
+ 'environments' => [
+ 'production' => [
+ 'supervisor-1' => [
+ 'maxProcesses' => 10,
+ 'balanceMaxShift' => 1,
+ 'balanceCooldown' => 3,
+ ],
+ ],
+
+ 'local' => [
+ 'supervisor-1' => [
+ 'maxProcesses' => 3,
+ ],
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | File Watcher Configuration
+ |--------------------------------------------------------------------------
+ |
+ | The following list of directories and files will be watched when using
+ | the `horizon:listen` command. Whenever any directories or files are
+ | changed, Horizon will automatically restart to apply all changes.
+ |
+ */
+
+ 'watch' => [
+ 'app',
+ 'bootstrap',
+ 'config/**/*.php',
+ 'database/**/*.php',
+ 'public/**/*.php',
+ 'resources/**/*.php',
+ 'routes',
+ 'composer.lock',
+ 'composer.json',
+ '.env',
+ ],
+];
diff --git a/resources/views/partials/admin-menu.blade.php b/resources/views/partials/admin-menu.blade.php
index e31143fc5..b27ad964f 100644
--- a/resources/views/partials/admin-menu.blade.php
+++ b/resources/views/partials/admin-menu.blade.php
@@ -300,6 +300,9 @@
Statistics
+
+ Horizon
+