feat: enterprise-grade Docker setup for Dokploy deployment
- Add multi-stage Dockerfile (node-builder, composer-builder, production) - Add Nginx config with WebSocket proxy at /_ws path - Add PHP-FPM pool config, php.ini with OPcache tuning - Add Supervisord managing 7 processes: php-fpm, nginx, horizon, scheduler, reverb, pulse-check, pulse-work - Add entrypoint.sh with auto-migration, config caching, storage setup - Add .dockerignore and .env.production.example - Install laravel/horizon for production queue management and dashboard - Install laravel/pulse for production monitoring with Reverb integration - Configure TrustProxies middleware for HTTPS behind Traefik - Add horizon:snapshot to scheduler - Add VITE_REVERB_PATH for WebSocket path routing through Nginx
This commit is contained in:
24
.dockerignore
Normal file
24
.dockerignore
Normal file
@@ -0,0 +1,24 @@
|
||||
.git
|
||||
.idea
|
||||
.vscode
|
||||
.agents
|
||||
.ai
|
||||
.claude
|
||||
.gemini
|
||||
.junie
|
||||
node_modules
|
||||
vendor
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
.env
|
||||
.env.*
|
||||
*.md
|
||||
!README.md
|
||||
storage/logs/*
|
||||
storage/framework/cache/*
|
||||
storage/framework/sessions/*
|
||||
storage/framework/views/*
|
||||
tests/
|
||||
phpunit.xml
|
||||
phpunit.result.cache
|
||||
docker-compose.yml
|
||||
@@ -83,6 +83,7 @@ VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
|
||||
VITE_REVERB_HOST="${REVERB_HOST}"
|
||||
VITE_REVERB_PORT="${REVERB_PORT}"
|
||||
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
|
||||
VITE_REVERB_PATH=""
|
||||
|
||||
ACTIVITY_LOGGER_ENABLED=true
|
||||
ACTIVITY_LOGGER_TABLE_NAME=activity_log
|
||||
|
||||
64
.env.production.example
Normal file
64
.env.production.example
Normal file
@@ -0,0 +1,64 @@
|
||||
APP_NAME=iMail
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://your-domain.com
|
||||
|
||||
LOG_CHANNEL=stderr
|
||||
LOG_LEVEL=info
|
||||
|
||||
# Database (MariaDB via Dokploy/External)
|
||||
DB_CONNECTION=mariadb
|
||||
DB_HOST=mariadb-host
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=imail
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
# MongoDB (via Dokploy/External)
|
||||
MONGODB_URI=mongodb://mongodb-host:27017
|
||||
|
||||
# Redis (via Dokploy/External)
|
||||
REDIS_CLIENT=phpredis # Required for Laravel Pulse ingest
|
||||
REDIS_HOST=redis-host
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
CACHE_STORE=redis
|
||||
QUEUE_CONNECTION=redis
|
||||
SESSION_DRIVER=redis
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=reverb
|
||||
|
||||
FILESYSTEM_DISK=s3
|
||||
|
||||
# S3 Compatible Storage (RustFS via Dokploy/External)
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=imail
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=true
|
||||
AWS_ENDPOINT=http://rustfs-host:9000
|
||||
|
||||
# Reverb Configuration
|
||||
REVERB_APP_ID=
|
||||
REVERB_APP_KEY=
|
||||
REVERB_APP_SECRET=
|
||||
REVERB_HOST="your-domain.com"
|
||||
REVERB_PORT=443
|
||||
REVERB_SCHEME=https
|
||||
|
||||
# Pulse Configuration
|
||||
PULSE_INGEST_DRIVER=redis
|
||||
PULSE_REDIS_CONNECTION=default
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
|
||||
VITE_REVERB_HOST="${REVERB_HOST}"
|
||||
VITE_REVERB_PORT="${REVERB_PORT}"
|
||||
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
|
||||
VITE_REVERB_PATH="/_ws"
|
||||
@@ -46,8 +46,10 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
||||
- filament/filament (FILAMENT) - v4
|
||||
- laravel/fortify (FORTIFY) - v1
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/horizon (HORIZON) - v5
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- laravel/pulse (PULSE) - v1
|
||||
- laravel/reverb (REVERB) - v1
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
- livewire/livewire (LIVEWIRE) - v3
|
||||
|
||||
@@ -46,8 +46,10 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
||||
- filament/filament (FILAMENT) - v4
|
||||
- laravel/fortify (FORTIFY) - v1
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/horizon (HORIZON) - v5
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- laravel/pulse (PULSE) - v1
|
||||
- laravel/reverb (REVERB) - v1
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
- livewire/livewire (LIVEWIRE) - v3
|
||||
|
||||
76
Dockerfile
Normal file
76
Dockerfile
Normal file
@@ -0,0 +1,76 @@
|
||||
# 1. Node Builder Stage
|
||||
FROM node:22-alpine AS node-builder
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY ./ ./
|
||||
RUN npm run build
|
||||
|
||||
# 2. Composer Builder Stage
|
||||
FROM php:8.4-cli-alpine AS composer-builder
|
||||
RUN apk add --no-cache unzip
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||
WORKDIR /app
|
||||
COPY composer.json composer.lock ./
|
||||
# Note: ignoring platform requirements since mongo/redis aren't natively in this alpine CLI image
|
||||
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist --ignore-platform-reqs
|
||||
COPY ./ ./
|
||||
RUN composer dump-autoload --optimize --no-dev
|
||||
|
||||
# 3. Production Stage
|
||||
FROM php:8.4-fpm-alpine
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk add --no-cache \
|
||||
nginx \
|
||||
supervisor \
|
||||
libpng-dev \
|
||||
libjpeg-turbo-dev \
|
||||
freetype-dev \
|
||||
libzip-dev \
|
||||
icu-dev \
|
||||
git \
|
||||
$PHPIZE_DEPS \
|
||||
linux-headers \
|
||||
openssl-dev
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||
&& docker-php-ext-install -j$(nproc) \
|
||||
pdo_mysql \
|
||||
gd \
|
||||
zip \
|
||||
intl \
|
||||
bcmath \
|
||||
pcntl \
|
||||
opcache \
|
||||
sockets
|
||||
|
||||
# Install PECL extensions (MongoDB and Redis)
|
||||
RUN pecl install mongodb redis \
|
||||
&& docker-php-ext-enable mongodb redis \
|
||||
&& rm -rf /tmp/pear
|
||||
|
||||
# Copy source code and vendor dependencies
|
||||
COPY . .
|
||||
COPY --from=composer-builder /app/vendor ./vendor
|
||||
COPY --from=node-builder /app/public/build ./public/build
|
||||
|
||||
# Copy configuration files
|
||||
COPY docker/nginx.conf /etc/nginx/http.d/default.conf
|
||||
COPY docker/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf
|
||||
COPY docker/php.ini /usr/local/etc/php/conf.d/production.ini
|
||||
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
|
||||
# Make entrypoint executable
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||
|
||||
# Cleanup
|
||||
RUN apk del $PHPIZE_DEPS \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
@@ -46,8 +46,10 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
||||
- filament/filament (FILAMENT) - v4
|
||||
- laravel/fortify (FORTIFY) - v1
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/horizon (HORIZON) - v5
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- laravel/pulse (PULSE) - v1
|
||||
- laravel/reverb (REVERB) - v1
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
- livewire/livewire (LIVEWIRE) - v3
|
||||
|
||||
36
app/Providers/HorizonServiceProvider.php
Normal file
36
app/Providers/HorizonServiceProvider.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Laravel\Horizon\Horizon;
|
||||
use Laravel\Horizon\HorizonApplicationServiceProvider;
|
||||
|
||||
class HorizonServiceProvider extends HorizonApplicationServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
// Horizon::routeSmsNotificationsTo('15556667777');
|
||||
// Horizon::routeMailNotificationsTo('example@example.com');
|
||||
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Horizon gate.
|
||||
*
|
||||
* This gate determines who can access Horizon in non-local environments.
|
||||
*/
|
||||
protected function gate(): void
|
||||
{
|
||||
Gate::define('viewHorizon', function ($user = null) {
|
||||
return in_array(optional($user)->email, [
|
||||
//
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
$middleware->trustProxies(at: '*');
|
||||
$middleware->alias([
|
||||
'verify.webhook.secret' => VerifyWebhookSecret::class,
|
||||
]);
|
||||
|
||||
@@ -5,4 +5,5 @@ return [
|
||||
App\Providers\DynamicMailConfigServiceProvider::class,
|
||||
App\Providers\Filament\DashPanelProvider::class,
|
||||
App\Providers\FortifyServiceProvider::class,
|
||||
App\Providers\HorizonServiceProvider::class,
|
||||
];
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
"jacobtims/filament-logger": "^1.0",
|
||||
"laravel/fortify": "^1.30",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/horizon": "^5.45",
|
||||
"laravel/pulse": "^1.6",
|
||||
"laravel/reverb": "^1.8",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"livewire/flux": "^2.1.1",
|
||||
|
||||
284
composer.lock
generated
284
composer.lock
generated
@@ -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": "ac4723b249fb557c0154b1b59679a168",
|
||||
"content-hash": "cd1592da57958973057c2f7e7a9cfb1d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@@ -1245,6 +1245,61 @@
|
||||
],
|
||||
"time": "2024-02-05T11:56:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/sql-formatter",
|
||||
"version": "1.5.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/sql-formatter.git",
|
||||
"reference": "9563949f5cd3bd12a17d12fb980528bc141c5806"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/9563949f5cd3bd12a17d12fb980528bc141c5806",
|
||||
"reference": "9563949f5cd3bd12a17d12fb980528bc141c5806",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^14",
|
||||
"ergebnis/phpunit-slow-test-detector": "^2.20",
|
||||
"phpstan/phpstan": "^2.1.31",
|
||||
"phpunit/phpunit": "^10.5.58"
|
||||
},
|
||||
"bin": [
|
||||
"bin/sql-formatter"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\SqlFormatter\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jeremy Dorn",
|
||||
"email": "jeremy@jeremydorn.com",
|
||||
"homepage": "https://jeremydorn.com/"
|
||||
}
|
||||
],
|
||||
"description": "a PHP SQL highlighting library",
|
||||
"homepage": "https://github.com/doctrine/sql-formatter/",
|
||||
"keywords": [
|
||||
"highlight",
|
||||
"sql"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/sql-formatter/issues",
|
||||
"source": "https://github.com/doctrine/sql-formatter/tree/1.5.4"
|
||||
},
|
||||
"time": "2026-02-08T16:21:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dragonmantank/cron-expression",
|
||||
"version": "v3.6.0",
|
||||
@@ -3072,6 +3127,86 @@
|
||||
},
|
||||
"time": "2025-11-25T14:46:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/horizon",
|
||||
"version": "v5.45.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/horizon.git",
|
||||
"reference": "637e065ae0a704288595b896ad1c7c3c9741869b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/horizon/zipball/637e065ae0a704288595b896ad1c7c3c9741869b",
|
||||
"reference": "637e065ae0a704288595b896ad1c7c3c9741869b",
|
||||
"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.1"
|
||||
},
|
||||
"time": "2026-03-06T15:31:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
"version": "v1.27.1",
|
||||
@@ -3198,6 +3333,94 @@
|
||||
},
|
||||
"time": "2026-02-06T12:17:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pulse",
|
||||
"version": "v1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/pulse.git",
|
||||
"reference": "7cde76c1abe23492edeee7dadec01906cf70427d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/pulse/zipball/7cde76c1abe23492edeee7dadec01906cf70427d",
|
||||
"reference": "7cde76c1abe23492edeee7dadec01906cf70427d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/sql-formatter": "^1.4.1",
|
||||
"guzzlehttp/promises": "^1.0|^2.0",
|
||||
"illuminate/auth": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/cache": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/config": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/console": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/contracts": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/database": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/events": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/http": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/queue": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/redis": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/routing": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/support": "^10.48.4|^11.0.8|^12.0",
|
||||
"illuminate/view": "^10.48.4|^11.0.8|^12.0",
|
||||
"laravel/sentinel": "^1.0",
|
||||
"livewire/livewire": "^3.6.4|^4.0",
|
||||
"nesbot/carbon": "^2.67|^3.0",
|
||||
"php": "^8.1",
|
||||
"symfony/console": "^6.0|^7.0"
|
||||
},
|
||||
"conflict": {
|
||||
"nunomaduro/collision": "<7.7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^7.7",
|
||||
"mockery/mockery": "^1.0",
|
||||
"orchestra/testbench": "^8.36|^9.15|^10.8",
|
||||
"pestphp/pest": "^2.0|^3.0|^4.0",
|
||||
"pestphp/pest-plugin-laravel": "^2.2|^3.0|^4.0",
|
||||
"phpstan/phpstan": "^1.12.21",
|
||||
"predis/predis": "^1.0|^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"Pulse": "Laravel\\Pulse\\Facades\\Pulse"
|
||||
},
|
||||
"providers": [
|
||||
"Laravel\\Pulse\\PulseServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Pulse\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.",
|
||||
"homepage": "https://github.com/laravel/pulse",
|
||||
"keywords": [
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/pulse/issues",
|
||||
"source": "https://github.com/laravel/pulse"
|
||||
},
|
||||
"time": "2026-02-12T18:51:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/reverb",
|
||||
"version": "v1.8.0",
|
||||
@@ -3277,6 +3500,65 @@
|
||||
},
|
||||
"time": "2026-02-21T14:37:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sentinel",
|
||||
"version": "v1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/sentinel.git",
|
||||
"reference": "7a98db53e0d9d6f61387f3141c07477f97425603"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/sentinel/zipball/7a98db53e0d9d6f61387f3141c07477f97425603",
|
||||
"reference": "7a98db53e0d9d6f61387f3141c07477f97425603",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/container": "^8.37|^9.0|^10.0|^11.0|^12.0|^13.0",
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.27",
|
||||
"orchestra/testbench": "^6.47.1|^7.56|^8.37|^9.16|^10.9|^11.0",
|
||||
"phpstan/phpstan": "^2.1.33"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Sentinel\\SentinelServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Sentinel\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
},
|
||||
{
|
||||
"name": "Mior Muhammad Zaki",
|
||||
"email": "mior@laravel.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/laravel/sentinel/tree/v1.0.1"
|
||||
},
|
||||
"time": "2026-02-12T13:32:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v2.0.10",
|
||||
|
||||
254
config/horizon.php
Normal file
254
config/horizon.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This name appears in notifications and in the Horizon UI. Unique names
|
||||
| can be useful while running multiple instances of Horizon within an
|
||||
| application, allowing you to identify the Horizon you're viewing.
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => 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'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 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',
|
||||
],
|
||||
];
|
||||
244
config/pulse.php
Normal file
244
config/pulse.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
use Laravel\Pulse\Http\Middleware\Authorize;
|
||||
use Laravel\Pulse\Pulse;
|
||||
use Laravel\Pulse\Recorders;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the subdomain which the Pulse dashboard will be accessible from.
|
||||
| When set to null, the dashboard will reside under the same domain as
|
||||
| the application. Remember to configure your DNS entries correctly.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('PULSE_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the path which the Pulse dashboard will be accessible from. Feel
|
||||
| free to change this path to anything you'd like. Note that this won't
|
||||
| affect the path of the internal API that is never exposed to users.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('PULSE_PATH', 'pulse'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Master Switch
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration option may be used to completely disable all Pulse
|
||||
| data recorders regardless of their individual configurations. This
|
||||
| provides a single option to quickly disable all Pulse recording.
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('PULSE_ENABLED', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Storage Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration option determines which storage driver will be used
|
||||
| while storing entries from Pulse's recorders. In addition, you also
|
||||
| may provide any options to configure the selected storage driver.
|
||||
|
|
||||
*/
|
||||
|
||||
'storage' => [
|
||||
'driver' => env('PULSE_STORAGE_DRIVER', 'database'),
|
||||
|
||||
'trim' => [
|
||||
'keep' => env('PULSE_STORAGE_KEEP', '7 days'),
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'connection' => env('PULSE_DB_CONNECTION'),
|
||||
'chunk' => 1000,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Ingest Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration options determines the ingest driver that will be used
|
||||
| to capture entries from Pulse's recorders. Ingest drivers are great to
|
||||
| free up your request workers quickly by offloading the data storage.
|
||||
|
|
||||
*/
|
||||
|
||||
'ingest' => [
|
||||
'driver' => env('PULSE_INGEST_DRIVER', 'storage'),
|
||||
|
||||
'buffer' => env('PULSE_INGEST_BUFFER', 5_000),
|
||||
|
||||
'trim' => [
|
||||
'lottery' => [1, 1_000],
|
||||
'keep' => env('PULSE_INGEST_KEEP', '7 days'),
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'connection' => env('PULSE_REDIS_CONNECTION'),
|
||||
'chunk' => 1000,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Cache Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration option determines the cache driver that will be used
|
||||
| for various tasks, including caching dashboard results, establishing
|
||||
| locks for events that should only occur on one server and signals.
|
||||
|
|
||||
*/
|
||||
|
||||
'cache' => env('PULSE_CACHE_DRIVER'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Route Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These middleware will be assigned to every Pulse route, giving you the
|
||||
| chance to add your own middleware to this list or change any of the
|
||||
| existing middleware. Of course, reasonable defaults are provided.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => [
|
||||
'web',
|
||||
Authorize::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Recorders
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following array lists the "recorders" that will be registered with
|
||||
| Pulse, along with their configuration. Recorders gather application
|
||||
| event data from requests and tasks to pass to your ingest driver.
|
||||
|
|
||||
*/
|
||||
|
||||
'recorders' => [
|
||||
Laravel\Reverb\Pulse\Recorders\ReverbConnections::class => [
|
||||
'sample_rate' => 1,
|
||||
],
|
||||
|
||||
Laravel\Reverb\Pulse\Recorders\ReverbMessages::class => [
|
||||
'sample_rate' => 1,
|
||||
],
|
||||
|
||||
Recorders\CacheInteractions::class => [
|
||||
'enabled' => env('PULSE_CACHE_INTERACTIONS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_CACHE_INTERACTIONS_SAMPLE_RATE', 1),
|
||||
'ignore' => [
|
||||
...Pulse::defaultVendorCacheKeys(),
|
||||
],
|
||||
'groups' => [
|
||||
'/^job-exceptions:.*/' => 'job-exceptions:*',
|
||||
// '/:\d+/' => ':*',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\Exceptions::class => [
|
||||
'enabled' => env('PULSE_EXCEPTIONS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_EXCEPTIONS_SAMPLE_RATE', 1),
|
||||
'location' => env('PULSE_EXCEPTIONS_LOCATION', true),
|
||||
'ignore' => [
|
||||
// '/^Package\\\\Exceptions\\\\/',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\Queues::class => [
|
||||
'enabled' => env('PULSE_QUEUES_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_QUEUES_SAMPLE_RATE', 1),
|
||||
'ignore' => [
|
||||
// '/^Package\\\\Jobs\\\\/',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\Servers::class => [
|
||||
'server_name' => env('PULSE_SERVER_NAME', gethostname()),
|
||||
'directories' => explode(':', env('PULSE_SERVER_DIRECTORIES', '/')),
|
||||
],
|
||||
|
||||
Recorders\SlowJobs::class => [
|
||||
'enabled' => env('PULSE_SLOW_JOBS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_SLOW_JOBS_SAMPLE_RATE', 1),
|
||||
'threshold' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
|
||||
'ignore' => [
|
||||
// '/^Package\\\\Jobs\\\\/',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\SlowOutgoingRequests::class => [
|
||||
'enabled' => env('PULSE_SLOW_OUTGOING_REQUESTS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_SLOW_OUTGOING_REQUESTS_SAMPLE_RATE', 1),
|
||||
'threshold' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
|
||||
'ignore' => [
|
||||
// '#^http://127\.0\.0\.1:13714#', // Inertia SSR...
|
||||
],
|
||||
'groups' => [
|
||||
// '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*',
|
||||
// '#^https?://([^/]*).*$#' => '\1',
|
||||
// '#/\d+#' => '/*',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\SlowQueries::class => [
|
||||
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_SLOW_QUERIES_SAMPLE_RATE', 1),
|
||||
'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
|
||||
'location' => env('PULSE_SLOW_QUERIES_LOCATION', true),
|
||||
'max_query_length' => env('PULSE_SLOW_QUERIES_MAX_QUERY_LENGTH'),
|
||||
'ignore' => [
|
||||
'/(["`])pulse_[\w]+?\1/', // Pulse tables...
|
||||
'/(["`])telescope_[\w]+?\1/', // Telescope tables...
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\SlowRequests::class => [
|
||||
'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_SLOW_REQUESTS_SAMPLE_RATE', 1),
|
||||
'threshold' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
|
||||
'ignore' => [
|
||||
'#^/'.env('PULSE_PATH', 'pulse').'$#', // Pulse dashboard...
|
||||
'#^/telescope#', // Telescope dashboard...
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\UserJobs::class => [
|
||||
'enabled' => env('PULSE_USER_JOBS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_USER_JOBS_SAMPLE_RATE', 1),
|
||||
'ignore' => [
|
||||
// '/^Package\\\\Jobs\\\\/',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\UserRequests::class => [
|
||||
'enabled' => env('PULSE_USER_REQUESTS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_USER_REQUESTS_SAMPLE_RATE', 1),
|
||||
'ignore' => [
|
||||
'#^/'.env('PULSE_PATH', 'pulse').'$#', // Pulse dashboard...
|
||||
'#^/telescope#', // Telescope dashboard...
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Laravel\Pulse\Support\PulseMigration;
|
||||
|
||||
return new class extends PulseMigration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (! $this->shouldRun()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::create('pulse_values', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('timestamp');
|
||||
$table->string('type');
|
||||
$table->mediumText('key');
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
};
|
||||
$table->mediumText('value');
|
||||
|
||||
$table->index('timestamp'); // For trimming...
|
||||
$table->index('type'); // For fast lookups and purging...
|
||||
$table->unique(['type', 'key_hash']); // For data integrity and upserts...
|
||||
});
|
||||
|
||||
Schema::create('pulse_entries', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('timestamp');
|
||||
$table->string('type');
|
||||
$table->mediumText('key');
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
};
|
||||
$table->bigInteger('value')->nullable();
|
||||
|
||||
$table->index('timestamp'); // For trimming...
|
||||
$table->index('type'); // For purging...
|
||||
$table->index('key_hash'); // For mapping...
|
||||
$table->index(['timestamp', 'type', 'key_hash', 'value']); // For aggregate queries...
|
||||
});
|
||||
|
||||
Schema::create('pulse_aggregates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('bucket');
|
||||
$table->unsignedMediumInteger('period');
|
||||
$table->string('type');
|
||||
$table->mediumText('key');
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
};
|
||||
$table->string('aggregate');
|
||||
$table->decimal('value', 20, 2);
|
||||
$table->unsignedInteger('count')->nullable();
|
||||
|
||||
$table->unique(['bucket', 'period', 'type', 'aggregate', 'key_hash']); // Force "on duplicate update"...
|
||||
$table->index(['period', 'bucket']); // For trimming...
|
||||
$table->index('type'); // For purging...
|
||||
$table->index(['period', 'type', 'aggregate', 'bucket']); // For aggregate queries...
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('pulse_values');
|
||||
Schema::dropIfExists('pulse_entries');
|
||||
Schema::dropIfExists('pulse_aggregates');
|
||||
}
|
||||
};
|
||||
34
docker/entrypoint.sh
Normal file
34
docker/entrypoint.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "Starting iMail container initialization..."
|
||||
|
||||
# Ensure storage directories exist
|
||||
mkdir -p /var/www/html/storage/framework/cache/data
|
||||
mkdir -p /var/www/html/storage/framework/sessions
|
||||
mkdir -p /var/www/html/storage/framework/views
|
||||
mkdir -p /var/www/html/storage/logs
|
||||
mkdir -p /var/www/html/storage/app/public
|
||||
mkdir -p /var/www/html/bootstrap/cache
|
||||
|
||||
# Fix permissions
|
||||
chown -R www-data:www-data /var/www/html/storage
|
||||
chown -R www-data:www-data /var/www/html/bootstrap/cache
|
||||
|
||||
# Cache configuration, routes, views, events
|
||||
echo "Caching configuration and routes..."
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
php artisan event:cache
|
||||
|
||||
# Create storage symlink
|
||||
php artisan storage:link
|
||||
|
||||
# Run migrations automatically
|
||||
echo "Running migrations..."
|
||||
php artisan migrate --force
|
||||
|
||||
echo "Initialization complete. Starting Supervisord..."
|
||||
# Execute supervisord in the foreground
|
||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||
71
docker/nginx.conf
Normal file
71
docker/nginx.conf
Normal file
@@ -0,0 +1,71 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /var/www/html/public;
|
||||
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
|
||||
index index.php;
|
||||
|
||||
charset utf-8;
|
||||
|
||||
client_max_body_size 64M;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
||||
|
||||
# Laravel routes
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
# Pulse Dashboard
|
||||
location = /pulse {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
# Horizon Dashboard
|
||||
location = /horizon {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
# Reverb WebSocket Proxy
|
||||
# The trailing slash on proxy_pass strips the /_ws prefix:
|
||||
# /_ws/app/{key} → /app/{key}
|
||||
location /_ws/ {
|
||||
proxy_pass http://127.0.0.1:8080/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Scheme $scheme;
|
||||
proxy_set_header SERVER_PORT $server_port;
|
||||
proxy_set_header REMOTE_ADDR $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_read_timeout 60m;
|
||||
proxy_connect_timeout 60m;
|
||||
}
|
||||
|
||||
# Pass PHP scripts to FastCGI server
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
fastcgi_hide_header X-Powered-By;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, no-transform";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location ~ /\.(?!well-known).* {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
21
docker/php-fpm.conf
Normal file
21
docker/php-fpm.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
[global]
|
||||
error_log = /dev/stderr
|
||||
|
||||
[www]
|
||||
user = www-data
|
||||
group = www-data
|
||||
|
||||
listen = 127.0.0.1:9000
|
||||
|
||||
pm = dynamic
|
||||
pm.max_children = 50
|
||||
pm.start_servers = 5
|
||||
pm.min_spare_servers = 5
|
||||
pm.max_spare_servers = 35
|
||||
pm.max_requests = 500
|
||||
|
||||
clear_env = no
|
||||
catch_workers_output = yes
|
||||
decorate_workers_output = no
|
||||
|
||||
access.log = /dev/null
|
||||
21
docker/php.ini
Normal file
21
docker/php.ini
Normal file
@@ -0,0 +1,21 @@
|
||||
[PHP]
|
||||
expose_php = Off
|
||||
display_errors = Off
|
||||
display_startup_errors = Off
|
||||
log_errors = On
|
||||
error_log = /dev/stderr
|
||||
memory_limit = 256M
|
||||
upload_max_filesize = 64M
|
||||
post_max_size = 64M
|
||||
max_execution_time = 60
|
||||
max_input_time = 60
|
||||
variables_order = "EGPCS"
|
||||
|
||||
[opcache]
|
||||
opcache.enable=1
|
||||
opcache.memory_consumption=256
|
||||
opcache.interned_strings_buffer=16
|
||||
opcache.max_accelerated_files=20000
|
||||
opcache.validate_timestamps=0
|
||||
opcache.save_comments=1
|
||||
opcache.fast_shutdown=1
|
||||
83
docker/supervisord.conf
Normal file
83
docker/supervisord.conf
Normal file
@@ -0,0 +1,83 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
logfile=/dev/null
|
||||
logfile_maxbytes=0
|
||||
|
||||
[program:php-fpm]
|
||||
command=php-fpm --nodaemonize --fpm-config /usr/local/etc/php-fpm.d/www.conf
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=5
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:nginx]
|
||||
command=nginx -g "daemon off;"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=10
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:horizon]
|
||||
command=php /var/www/html/artisan horizon
|
||||
user=www-data
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=15
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
stopwaitsecs=3600
|
||||
|
||||
[program:scheduler]
|
||||
command=/bin/sh -c "while sleep 60; do php /var/www/html/artisan schedule:run; done"
|
||||
user=www-data
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=20
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:reverb]
|
||||
command=php /var/www/html/artisan reverb:start --host=0.0.0.0 --port=8080
|
||||
user=www-data
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=25
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:pulse-check]
|
||||
command=php /var/www/html/artisan pulse:check
|
||||
user=www-data
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=30
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
stopwaitsecs=3600
|
||||
|
||||
[program:pulse-work]
|
||||
command=php /var/www/html/artisan pulse:work
|
||||
user=www-data
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=35
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
stopwaitsecs=3600
|
||||
BIN
install.log
Normal file
BIN
install.log
Normal file
Binary file not shown.
@@ -10,6 +10,7 @@ if (document.querySelector('[data-requires-reverb]')) {
|
||||
wsHost: import.meta.env.VITE_REVERB_HOST,
|
||||
wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
|
||||
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
|
||||
wsPath: import.meta.env.VITE_REVERB_PATH ?? '',
|
||||
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
|
||||
enabledTransports: ['ws', 'wss'],
|
||||
});
|
||||
|
||||
19
resources/views/vendor/pulse/dashboard.blade.php
vendored
Normal file
19
resources/views/vendor/pulse/dashboard.blade.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<x-pulse>
|
||||
<livewire:pulse.servers cols="full" />
|
||||
|
||||
<livewire:pulse.usage cols="4" rows="2" />
|
||||
|
||||
<livewire:pulse.queues cols="4" />
|
||||
|
||||
<livewire:pulse.cache cols="4" />
|
||||
|
||||
<livewire:pulse.slow-queries cols="8" />
|
||||
|
||||
<livewire:pulse.exceptions cols="6" />
|
||||
|
||||
<livewire:pulse.slow-requests cols="6" />
|
||||
|
||||
<livewire:pulse.slow-jobs cols="6" />
|
||||
|
||||
<livewire:pulse.slow-outgoing-requests cols="6" />
|
||||
</x-pulse>
|
||||
@@ -9,3 +9,4 @@ Artisan::command('inspire', function (): void {
|
||||
})->purpose('Display an inspiring quote');
|
||||
|
||||
Schedule::command('mailboxes:cleanup')->everyMinute();
|
||||
Schedule::command('horizon:snapshot')->everyFiveMinutes();
|
||||
|
||||
Reference in New Issue
Block a user