Update README demo links and bump version
This commit is contained in:
13
.dockerignore
Normal file
13
.dockerignore
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
node_modules
|
||||||
|
storage/logs
|
||||||
|
storage/framework/cache
|
||||||
|
storage/framework/sessions
|
||||||
|
storage/framework/views
|
||||||
|
storage/app/private
|
||||||
|
storage/app/private/*
|
||||||
|
storage/app/private/livewire-tmp
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
12
AGENT.md
12
AGENT.md
@@ -326,6 +326,18 @@ dns.get_ds_records - Get DS records for registrar
|
|||||||
| Admin | `https://jabali.lan/jabali-admin` | `admin@jabali.lan` | `q1w2E#R$` |
|
| Admin | `https://jabali.lan/jabali-admin` | `admin@jabali.lan` | `q1w2E#R$` |
|
||||||
| User | `https://jabali.lan/jabali-panel` | `user@jabali.lan` | `wjqr9t6Z#%r&@C$4` |
|
| User | `https://jabali.lan/jabali-panel` | `user@jabali.lan` | `wjqr9t6Z#%r&@C$4` |
|
||||||
|
|
||||||
|
### Demo Credentials
|
||||||
|
| Panel | URL | Email | Password |
|
||||||
|
|-------|-----|-------|----------|
|
||||||
|
| Admin | `https://demo.jabali-panel.com/jabali-admin` | `admin@jabali-panel.com` | `demo1234` |
|
||||||
|
| User | `https://demo.jabali-panel.com/jabali-panel` | `demo@jabali-panel.com` | `demo1234` |
|
||||||
|
|
||||||
|
**Demo mode behavior**
|
||||||
|
- `JABALI_DEMO=1` enables read-only mode via `App\Http\Middleware\DemoReadOnly`.
|
||||||
|
- Livewire `authenticate` calls are allowed for unauthenticated users.
|
||||||
|
- Some pages use static demo data to avoid agent socket calls.
|
||||||
|
- Reverse proxy must set `X-Forwarded-Proto` and the app must trust proxies for HTTPS Livewire updates.
|
||||||
|
|
||||||
## Models
|
## Models
|
||||||
|
|
||||||
| Model | Table | Description |
|
| Model | Table | Description |
|
||||||
|
|||||||
12
CONTEXT.md
12
CONTEXT.md
@@ -1,6 +1,6 @@
|
|||||||
# CONTEXT.md
|
# CONTEXT.md
|
||||||
|
|
||||||
Last updated: 2026-02-01
|
Last updated: 2026-02-03
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
- Laravel 12, Filament v5, Livewire v4
|
- Laravel 12, Filament v5, Livewire v4
|
||||||
@@ -11,6 +11,16 @@ Last updated: 2026-02-01
|
|||||||
- Admin panel: `/jabali-admin`
|
- Admin panel: `/jabali-admin`
|
||||||
- User panel: `/jabali-panel`
|
- User panel: `/jabali-panel`
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
- Public demo: `https://demo.jabali-panel.com`
|
||||||
|
- Demo container port: `5555` behind Nginx TLS proxy
|
||||||
|
- Demo DB: `database/database-demo.sqlite`
|
||||||
|
- Demo mode: `JABALI_DEMO=1` (read-only)
|
||||||
|
- Demo credentials:
|
||||||
|
- Admin: `admin@jabali-panel.com` / `demo1234`
|
||||||
|
- User: `demo@jabali-panel.com` / `demo1234`
|
||||||
|
- Livewire HTTPS requires proxy trust (`trustProxies(at: '*')`) and `X-Forwarded-Proto`.
|
||||||
|
|
||||||
## Data
|
## Data
|
||||||
- Panel config DB: SQLite at `database/database.sqlite`
|
- Panel config DB: SQLite at `database/database.sqlite`
|
||||||
- Hosting services use MariaDB/Postfix/Dovecot/etc. as configured by the agent
|
- Hosting services use MariaDB/Postfix/Dovecot/etc. as configured by the agent
|
||||||
|
|||||||
@@ -6,3 +6,8 @@
|
|||||||
- Asset builds must be writable for `public/build` and `node_modules`; upgrade checks both.
|
- Asset builds must be writable for `public/build` and `node_modules`; upgrade checks both.
|
||||||
- Installer builds assets as `www-data` to avoid permission issues.
|
- Installer builds assets as `www-data` to avoid permission issues.
|
||||||
- Default panel database is SQLite (`database/database.sqlite`).
|
- Default panel database is SQLite (`database/database.sqlite`).
|
||||||
|
|
||||||
|
## 2026-02-03
|
||||||
|
- Demo mode is enforced by `App\Http\Middleware\DemoReadOnly` (read-only, but allow Livewire `authenticate`).
|
||||||
|
- Demo container runs without agent sockets; select pages use static demo data to avoid 500s.
|
||||||
|
- Reverse proxies must be trusted for HTTPS Livewire updates in demo (`trustProxies(at: '*')`).
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -39,6 +39,26 @@ After install:
|
|||||||
- User panel: `https://your-host/jabali-panel`
|
- User panel: `https://your-host/jabali-panel`
|
||||||
- Webmail: `https://your-host/webmail`
|
- Webmail: `https://your-host/webmail`
|
||||||
|
|
||||||
|
Website: `https://jabali-panel.com/`
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
Public demo:
|
||||||
|
|
||||||
|
- User panel: `https://demo.jabali-panel.com/jabali-panel/`
|
||||||
|
- Admin panel: `https://demo.jabali-panel.com/jabali-admin/`
|
||||||
|
|
||||||
|
Credentials:
|
||||||
|
|
||||||
|
- Admin: `admin@jabali-panel.com` / `demo1234`
|
||||||
|
- User: `demo@jabali-panel.com` / `demo1234`
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Demo is read-only; actions that change data are blocked.
|
||||||
|
- Some pages use static demo data where the privileged agent is unavailable
|
||||||
|
(for example: PHP Manager, PHP Settings, Protected Directories).
|
||||||
|
|
||||||
## Feature Map
|
## Feature Map
|
||||||
|
|
||||||
### Admin Panel
|
### Admin Panel
|
||||||
@@ -79,27 +99,6 @@ After install:
|
|||||||
- Redis ACL isolation for WordPress caching
|
- Redis ACL isolation for WordPress caching
|
||||||
- Multi-language UI
|
- Multi-language UI
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
Admin panel:
|
|
||||||
|
|
||||||
- Dashboard: 
|
|
||||||
- Server Status: 
|
|
||||||
- Server Settings: 
|
|
||||||
- Security Center: 
|
|
||||||
- Users: 
|
|
||||||
- SSL Manager: 
|
|
||||||
- DNS Zones: 
|
|
||||||
- Backups: 
|
|
||||||
- Services: 
|
|
||||||
|
|
||||||
User panel:
|
|
||||||
|
|
||||||
- Dashboard: 
|
|
||||||
- Domain Management: 
|
|
||||||
- Backups: 
|
|
||||||
- cPanel Migration: 
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- Control plane: Laravel app with Filament panels
|
- Control plane: Laravel app with Filament panels
|
||||||
|
|||||||
2
TODO.md
2
TODO.md
@@ -6,3 +6,5 @@ Keep this list current as work progresses.
|
|||||||
- [ ] Confirm WAF whitelist + blocked requests tables refresh correctly after changes.
|
- [ ] Confirm WAF whitelist + blocked requests tables refresh correctly after changes.
|
||||||
- [ ] Validate sysstat collection interval (10s) and chart intervals align.
|
- [ ] Validate sysstat collection interval (10s) and chart intervals align.
|
||||||
- [ ] Audit installer/uninstaller parity for newly added packages.
|
- [ ] Audit installer/uninstaller parity for newly added packages.
|
||||||
|
- [ ] Demo mode: stub remaining agent-dependent pages to avoid 500s.
|
||||||
|
- [ ] Demo mode: reduce repeated "changes are disabled" notifications for blocked actions.
|
||||||
|
|||||||
@@ -8,10 +8,23 @@ use App\Models\User;
|
|||||||
use Filament\Auth\Http\Responses\Contracts\LoginResponse;
|
use Filament\Auth\Http\Responses\Contracts\LoginResponse;
|
||||||
use Filament\Auth\Pages\Login as BaseLogin;
|
use Filament\Auth\Pages\Login as BaseLogin;
|
||||||
use Filament\Facades\Filament;
|
use Filament\Facades\Filament;
|
||||||
|
use Illuminate\Support\HtmlString;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
class Login extends BaseLogin
|
class Login extends BaseLogin
|
||||||
{
|
{
|
||||||
|
public function getSubheading(): string | HtmlString | null
|
||||||
|
{
|
||||||
|
if (env('JABALI_DEMO', false)) {
|
||||||
|
return new HtmlString(
|
||||||
|
__('Demo credentials') .
|
||||||
|
': <code>admin@jabali-panel.com</code> / <code>demo1234</code>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getSubheading();
|
||||||
|
}
|
||||||
|
|
||||||
public function authenticate(): ?LoginResponse
|
public function authenticate(): ?LoginResponse
|
||||||
{
|
{
|
||||||
$data = $this->form->getState();
|
$data = $this->form->getState();
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ class Backups extends Page implements HasActions, HasForms, HasTable
|
|||||||
->color('gray'),
|
->color('gray'),
|
||||||
TextColumn::make('duration')
|
TextColumn::make('duration')
|
||||||
->label(__('Duration'))
|
->label(__('Duration'))
|
||||||
->placeholder('-')
|
->placeholder(__('-'))
|
||||||
->color('gray'),
|
->color('gray'),
|
||||||
])
|
])
|
||||||
->recordActions([
|
->recordActions([
|
||||||
|
|||||||
@@ -574,7 +574,7 @@ class CpanelMigration extends Page implements HasActions, HasForms, HasInfolists
|
|||||||
Grid::make(['default' => 1, 'sm' => 2])->schema([
|
Grid::make(['default' => 1, 'sm' => 2])->schema([
|
||||||
TextInput::make('hostname')
|
TextInput::make('hostname')
|
||||||
->label(__('cPanel Hostname'))
|
->label(__('cPanel Hostname'))
|
||||||
->placeholder('cpanel.example.com')
|
->placeholder(__('cpanel.example.com'))
|
||||||
->required(fn () => $this->sourceType === 'remote')
|
->required(fn () => $this->sourceType === 'remote')
|
||||||
->helperText(__('Your cPanel server hostname or IP address')),
|
->helperText(__('Your cPanel server hostname or IP address')),
|
||||||
TextInput::make('port')
|
TextInput::make('port')
|
||||||
@@ -610,7 +610,7 @@ class CpanelMigration extends Page implements HasActions, HasForms, HasInfolists
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('localBackupPath')
|
TextInput::make('localBackupPath')
|
||||||
->label(__('Backup File Path'))
|
->label(__('Backup File Path'))
|
||||||
->placeholder('/home/user/backups/backup-date_username.tar.gz')
|
->placeholder(__('/home/user/backups/backup-date_username.tar.gz'))
|
||||||
->required(fn () => $this->sourceType === 'local')
|
->required(fn () => $this->sourceType === 'local')
|
||||||
->helperText(__('Full path to the cPanel backup file (e.g., /var/backups/backup.tar.gz)')),
|
->helperText(__('Full path to the cPanel backup file (e.g., /var/backups/backup.tar.gz)')),
|
||||||
Text::make(__('Supported formats: .tar.gz, .tgz'))->color('gray'),
|
Text::make(__('Supported formats: .tar.gz, .tgz'))->color('gray'),
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class Dashboard extends Page implements HasActions, HasForms
|
|||||||
->label(__('Your Email Address'))
|
->label(__('Your Email Address'))
|
||||||
->helperText(__('Enter your email to receive important server notifications.'))
|
->helperText(__('Enter your email to receive important server notifications.'))
|
||||||
->email()
|
->email()
|
||||||
->placeholder('admin@example.com'),
|
->placeholder(__('admin@example.com')),
|
||||||
])
|
])
|
||||||
->modalSubmitActionLabel(__('Get Started'))
|
->modalSubmitActionLabel(__('Get Started'))
|
||||||
->action(function (array $data): void {
|
->action(function (array $data): void {
|
||||||
|
|||||||
@@ -254,12 +254,12 @@ class DnsZones extends Page implements HasActions, HasForms, HasTable
|
|||||||
->sortable(),
|
->sortable(),
|
||||||
TextColumn::make('priority')
|
TextColumn::make('priority')
|
||||||
->label(__('Priority'))
|
->label(__('Priority'))
|
||||||
->placeholder('-')
|
->placeholder(__('-'))
|
||||||
->color(fn (DnsRecord $record) => $this->isRecordPendingDelete($record->id) ? 'danger' : null)
|
->color(fn (DnsRecord $record) => $this->isRecordPendingDelete($record->id) ? 'danger' : null)
|
||||||
->sortable(),
|
->sortable(),
|
||||||
TextColumn::make('domain.user.username')
|
TextColumn::make('domain.user.username')
|
||||||
->label(__('Owner'))
|
->label(__('Owner'))
|
||||||
->placeholder('N/A')
|
->placeholder(__('N/A'))
|
||||||
->sortable(),
|
->sortable(),
|
||||||
])
|
])
|
||||||
->filters([])
|
->filters([])
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class IpAddresses extends Page implements HasActions, HasTable
|
|||||||
->form([
|
->form([
|
||||||
TextInput::make('ip')
|
TextInput::make('ip')
|
||||||
->label(__('IP Address'))
|
->label(__('IP Address'))
|
||||||
->placeholder('203.0.113.10')
|
->placeholder(__('203.0.113.10'))
|
||||||
->live()
|
->live()
|
||||||
->afterStateUpdated(function (?string $state, callable $set): void {
|
->afterStateUpdated(function (?string $state, callable $set): void {
|
||||||
if (! $state) {
|
if (! $state) {
|
||||||
@@ -198,7 +198,7 @@ class IpAddresses extends Page implements HasActions, HasTable
|
|||||||
->getStateUsing(fn (array $record): ?string => $this->getDefaultLabel($record))
|
->getStateUsing(fn (array $record): ?string => $this->getDefaultLabel($record))
|
||||||
->badge()
|
->badge()
|
||||||
->color('success')
|
->color('success')
|
||||||
->placeholder('-'),
|
->placeholder(__('-')),
|
||||||
])
|
])
|
||||||
->recordActions([
|
->recordActions([
|
||||||
Action::make('setDefault')
|
Action::make('setDefault')
|
||||||
|
|||||||
@@ -65,7 +65,30 @@ class PhpManager extends Page implements HasActions, HasForms, HasTable
|
|||||||
|
|
||||||
public function loadPhpVersions(): void
|
public function loadPhpVersions(): void
|
||||||
{
|
{
|
||||||
$result = $this->getAgent()->send('php.list_versions', []);
|
if ((bool) env('JABALI_DEMO', false)) {
|
||||||
|
$this->installedVersions = [
|
||||||
|
['version' => '8.4', 'fpm_status' => 'active'],
|
||||||
|
['version' => '8.3', 'fpm_status' => 'active'],
|
||||||
|
['version' => '8.2', 'fpm_status' => 'inactive'],
|
||||||
|
['version' => '8.1', 'fpm_status' => 'inactive'],
|
||||||
|
];
|
||||||
|
$this->defaultVersion = '8.4';
|
||||||
|
$allVersions = ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'];
|
||||||
|
$installed = array_column($this->installedVersions, 'version');
|
||||||
|
$this->availableVersions = array_diff($allVersions, $installed);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $this->getAgent()->send('php.list_versions', []);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->installedVersions = [];
|
||||||
|
$this->defaultVersion = null;
|
||||||
|
$this->availableVersions = [];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($result['success'] ?? false) {
|
if ($result['success'] ?? false) {
|
||||||
$this->installedVersions = $result['versions'] ?? [];
|
$this->installedVersions = $result['versions'] ?? [];
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
Grid::make(['default' => 1, 'md' => 2])->schema([
|
Grid::make(['default' => 1, 'md' => 2])->schema([
|
||||||
TextInput::make('brandingData.panel_name')
|
TextInput::make('brandingData.panel_name')
|
||||||
->label(__('Control Panel Name'))
|
->label(__('Control Panel Name'))
|
||||||
->placeholder('Jabali')
|
->placeholder(__('Jabali'))
|
||||||
->helperText(__('Appears in browser title and navigation'))
|
->helperText(__('Appears in browser title and navigation'))
|
||||||
->required(),
|
->required(),
|
||||||
]),
|
]),
|
||||||
@@ -320,7 +320,7 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('hostnameData.hostname')
|
TextInput::make('hostnameData.hostname')
|
||||||
->label(__('Hostname'))
|
->label(__('Hostname'))
|
||||||
->placeholder('server.example.com')
|
->placeholder(__('server.example.com'))
|
||||||
->required(),
|
->required(),
|
||||||
Actions::make([
|
Actions::make([
|
||||||
FormAction::make('saveHostname')
|
FormAction::make('saveHostname')
|
||||||
@@ -338,10 +338,10 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
->icon('heroicon-o-server-stack')
|
->icon('heroicon-o-server-stack')
|
||||||
->schema([
|
->schema([
|
||||||
Grid::make(['default' => 1, 'md' => 2, 'lg' => 4])->schema([
|
Grid::make(['default' => 1, 'md' => 2, 'lg' => 4])->schema([
|
||||||
TextInput::make('dnsData.ns1')->label(__('NS1 Hostname'))->placeholder('ns1.example.com'),
|
TextInput::make('dnsData.ns1')->label(__('NS1 Hostname'))->placeholder(__('ns1.example.com')),
|
||||||
TextInput::make('dnsData.ns1_ip')->label(__('NS1 IP Address'))->placeholder('192.168.1.1'),
|
TextInput::make('dnsData.ns1_ip')->label(__('NS1 IP Address'))->placeholder(__('192.168.1.1')),
|
||||||
TextInput::make('dnsData.ns2')->label(__('NS2 Hostname'))->placeholder('ns2.example.com'),
|
TextInput::make('dnsData.ns2')->label(__('NS2 Hostname'))->placeholder(__('ns2.example.com')),
|
||||||
TextInput::make('dnsData.ns2_ip')->label(__('NS2 IP Address'))->placeholder('192.168.1.2'),
|
TextInput::make('dnsData.ns2_ip')->label(__('NS2 IP Address'))->placeholder(__('192.168.1.2')),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
Section::make(__('Zone Defaults'))
|
Section::make(__('Zone Defaults'))
|
||||||
@@ -349,20 +349,20 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
Grid::make(['default' => 1, 'md' => 3])->schema([
|
Grid::make(['default' => 1, 'md' => 3])->schema([
|
||||||
TextInput::make('dnsData.default_ip')
|
TextInput::make('dnsData.default_ip')
|
||||||
->label(__('Default Server IP'))
|
->label(__('Default Server IP'))
|
||||||
->placeholder('192.168.1.1')
|
->placeholder(__('192.168.1.1'))
|
||||||
->helperText(__('Default A record IP for new zones')),
|
->helperText(__('Default A record IP for new zones')),
|
||||||
TextInput::make('dnsData.default_ipv6')
|
TextInput::make('dnsData.default_ipv6')
|
||||||
->label(__('Default IPv6'))
|
->label(__('Default IPv6'))
|
||||||
->placeholder('2001:db8::1')
|
->placeholder(__('2001:db8::1'))
|
||||||
->helperText(__('Default AAAA record IP for new zones'))
|
->helperText(__('Default AAAA record IP for new zones'))
|
||||||
->rule('nullable|ipv6'),
|
->rule('nullable|ipv6'),
|
||||||
TextInput::make('dnsData.default_ttl')
|
TextInput::make('dnsData.default_ttl')
|
||||||
->label(__('Default TTL'))
|
->label(__('Default TTL'))
|
||||||
->placeholder('3600'),
|
->placeholder(__('3600')),
|
||||||
]),
|
]),
|
||||||
TextInput::make('dnsData.admin_email')
|
TextInput::make('dnsData.admin_email')
|
||||||
->label(__('Admin Email (SOA)'))
|
->label(__('Admin Email (SOA)'))
|
||||||
->placeholder('admin.example.com')
|
->placeholder(__('admin.example.com'))
|
||||||
->helperText(__('Use dots instead of @ (e.g., admin.example.com)')),
|
->helperText(__('Use dots instead of @ (e.g., admin.example.com)')),
|
||||||
Actions::make([
|
Actions::make([
|
||||||
FormAction::make('saveDns')
|
FormAction::make('saveDns')
|
||||||
@@ -386,10 +386,10 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
->action('applyQuad9Resolvers'),
|
->action('applyQuad9Resolvers'),
|
||||||
])->alignment('left'),
|
])->alignment('left'),
|
||||||
Grid::make(['default' => 1, 'md' => 2, 'lg' => 4])->schema([
|
Grid::make(['default' => 1, 'md' => 2, 'lg' => 4])->schema([
|
||||||
TextInput::make('resolversData.resolver1')->label(__('Resolver 1'))->placeholder('8.8.8.8'),
|
TextInput::make('resolversData.resolver1')->label(__('Resolver 1'))->placeholder(__('8.8.8.8')),
|
||||||
TextInput::make('resolversData.resolver2')->label(__('Resolver 2'))->placeholder('8.8.4.4'),
|
TextInput::make('resolversData.resolver2')->label(__('Resolver 2'))->placeholder(__('8.8.4.4')),
|
||||||
TextInput::make('resolversData.resolver3')->label(__('Resolver 3'))->placeholder('1.1.1.1'),
|
TextInput::make('resolversData.resolver3')->label(__('Resolver 3'))->placeholder(__('1.1.1.1')),
|
||||||
TextInput::make('resolversData.search_domain')->label(__('Search Domain'))->placeholder('example.com'),
|
TextInput::make('resolversData.search_domain')->label(__('Search Domain'))->placeholder(__('example.com')),
|
||||||
]),
|
]),
|
||||||
Actions::make([
|
Actions::make([
|
||||||
FormAction::make('saveResolvers')
|
FormAction::make('saveResolvers')
|
||||||
@@ -470,7 +470,7 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
TextInput::make('quotaData.default_quota_mb')
|
TextInput::make('quotaData.default_quota_mb')
|
||||||
->label(__('Default Quota (MB)'))
|
->label(__('Default Quota (MB)'))
|
||||||
->numeric()
|
->numeric()
|
||||||
->placeholder('5120')
|
->placeholder(__('5120'))
|
||||||
->helperText(__('Default disk quota for new users (5120 MB = 5 GB)')),
|
->helperText(__('Default disk quota for new users (5120 MB = 5 GB)')),
|
||||||
]),
|
]),
|
||||||
Actions::make([
|
Actions::make([
|
||||||
@@ -487,7 +487,7 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
->numeric()
|
->numeric()
|
||||||
->minValue(1)
|
->minValue(1)
|
||||||
->maxValue(500)
|
->maxValue(500)
|
||||||
->placeholder('100')
|
->placeholder(__('100'))
|
||||||
->helperText(__('Maximum file size users can upload (1-500 MB)')),
|
->helperText(__('Maximum file size users can upload (1-500 MB)')),
|
||||||
Actions::make([
|
Actions::make([
|
||||||
FormAction::make('saveFileManagerSettings')
|
FormAction::make('saveFileManagerSettings')
|
||||||
@@ -507,7 +507,7 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
Grid::make(['default' => 1, 'md' => 2])->schema([
|
Grid::make(['default' => 1, 'md' => 2])->schema([
|
||||||
TextInput::make('emailData.mail_hostname')
|
TextInput::make('emailData.mail_hostname')
|
||||||
->label(__('Mail Server Hostname'))
|
->label(__('Mail Server Hostname'))
|
||||||
->placeholder('mail.example.com')
|
->placeholder(__('mail.example.com'))
|
||||||
->helperText(__('The hostname used for mail server identification')),
|
->helperText(__('The hostname used for mail server identification')),
|
||||||
TextInput::make('emailData.mail_default_quota_mb')
|
TextInput::make('emailData.mail_default_quota_mb')
|
||||||
->label(__('Default Mailbox Quota (MB)'))
|
->label(__('Default Mailbox Quota (MB)'))
|
||||||
@@ -527,11 +527,11 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
Grid::make(['default' => 1, 'md' => 2])->schema([
|
Grid::make(['default' => 1, 'md' => 2])->schema([
|
||||||
TextInput::make('emailData.webmail_url')
|
TextInput::make('emailData.webmail_url')
|
||||||
->label(__('Webmail URL'))
|
->label(__('Webmail URL'))
|
||||||
->placeholder('/webmail')
|
->placeholder(__('/webmail'))
|
||||||
->helperText(__('URL path for Roundcube webmail')),
|
->helperText(__('URL path for Roundcube webmail')),
|
||||||
TextInput::make('emailData.webmail_product_name')
|
TextInput::make('emailData.webmail_product_name')
|
||||||
->label(__('Webmail Product Name'))
|
->label(__('Webmail Product Name'))
|
||||||
->placeholder('Jabali Webmail')
|
->placeholder(__('Jabali Webmail'))
|
||||||
->helperText(__('Name displayed on the webmail login page')),
|
->helperText(__('Name displayed on the webmail login page')),
|
||||||
]),
|
]),
|
||||||
Actions::make([
|
Actions::make([
|
||||||
@@ -556,7 +556,7 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('notificationsData.admin_email_recipients')
|
TextInput::make('notificationsData.admin_email_recipients')
|
||||||
->label(__('Email Addresses'))
|
->label(__('Email Addresses'))
|
||||||
->placeholder('admin@example.com, alerts@example.com')
|
->placeholder(__('admin@example.com, alerts@example.com'))
|
||||||
->helperText(__('Comma-separated list of email addresses to receive notifications')),
|
->helperText(__('Comma-separated list of email addresses to receive notifications')),
|
||||||
]),
|
]),
|
||||||
Section::make(__('Notification Types & High Load Alerts'))
|
Section::make(__('Notification Types & High Load Alerts'))
|
||||||
@@ -598,14 +598,14 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
->minValue(1)
|
->minValue(1)
|
||||||
->maxValue(100)
|
->maxValue(100)
|
||||||
->step(0.5)
|
->step(0.5)
|
||||||
->placeholder('5')
|
->placeholder(__('5'))
|
||||||
->helperText(__('Alert when load exceeds this value')),
|
->helperText(__('Alert when load exceeds this value')),
|
||||||
TextInput::make('notificationsData.load_alert_minutes')
|
TextInput::make('notificationsData.load_alert_minutes')
|
||||||
->label(__('Alert After (minutes)'))
|
->label(__('Alert After (minutes)'))
|
||||||
->numeric()
|
->numeric()
|
||||||
->minValue(1)
|
->minValue(1)
|
||||||
->maxValue(60)
|
->maxValue(60)
|
||||||
->placeholder('5')
|
->placeholder(__('5'))
|
||||||
->helperText(__('Minutes of high load before alerting')),
|
->helperText(__('Minutes of high load before alerting')),
|
||||||
]),
|
]),
|
||||||
Actions::make([
|
Actions::make([
|
||||||
@@ -649,7 +649,7 @@ class ServerSettings extends Page implements HasActions, HasForms
|
|||||||
->helperText(__('Requests before worker recycle')),
|
->helperText(__('Requests before worker recycle')),
|
||||||
TextInput::make('phpFpmData.memory_limit')
|
TextInput::make('phpFpmData.memory_limit')
|
||||||
->label(__('Memory Limit'))
|
->label(__('Memory Limit'))
|
||||||
->placeholder('512M')
|
->placeholder(__('512M'))
|
||||||
->helperText(__('PHP memory_limit (e.g., 512M, 1G)')),
|
->helperText(__('PHP memory_limit (e.g., 512M, 1G)')),
|
||||||
]),
|
]),
|
||||||
Grid::make(['default' => 1, 'md' => 2, 'lg' => 3])->schema([
|
Grid::make(['default' => 1, 'md' => 2, 'lg' => 3])->schema([
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ class SslManager extends Page implements HasTable
|
|||||||
->limit(30)
|
->limit(30)
|
||||||
->tooltip(fn ($state) => $state)
|
->tooltip(fn ($state) => $state)
|
||||||
->color('danger')
|
->color('danger')
|
||||||
->placeholder('-'),
|
->placeholder(__('-')),
|
||||||
])
|
])
|
||||||
->filters([
|
->filters([
|
||||||
SelectFilter::make('ssl_status')
|
SelectFilter::make('ssl_status')
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ class WhmMigration extends Page implements HasActions, HasForms, HasInfolists, H
|
|||||||
Grid::make(['default' => 1, 'sm' => 2])->schema([
|
Grid::make(['default' => 1, 'sm' => 2])->schema([
|
||||||
TextInput::make('hostname')
|
TextInput::make('hostname')
|
||||||
->label(__('WHM Hostname'))
|
->label(__('WHM Hostname'))
|
||||||
->placeholder('whm.example.com')
|
->placeholder(__('whm.example.com'))
|
||||||
->required()
|
->required()
|
||||||
->helperText(__('Your WHM server hostname or IP address')),
|
->helperText(__('Your WHM server hostname or IP address')),
|
||||||
TextInput::make('port')
|
TextInput::make('port')
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class DnsPendingAddsTable extends Component implements HasActions, HasSchemas, H
|
|||||||
->label(__('TTL')),
|
->label(__('TTL')),
|
||||||
TextColumn::make('priority')
|
TextColumn::make('priority')
|
||||||
->label(__('Priority'))
|
->label(__('Priority'))
|
||||||
->placeholder('-'),
|
->placeholder(__('-')),
|
||||||
])
|
])
|
||||||
->actions([
|
->actions([
|
||||||
Action::make('removePending')
|
Action::make('removePending')
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class Fail2banLogsTable extends Component implements HasActions, HasSchemas, Has
|
|||||||
->label(__('IP'))
|
->label(__('IP'))
|
||||||
->fontFamily('mono')
|
->fontFamily('mono')
|
||||||
->copyable()
|
->copyable()
|
||||||
->placeholder('-'),
|
->placeholder(__('-')),
|
||||||
TextColumn::make('message')
|
TextColumn::make('message')
|
||||||
->label(__('Message'))
|
->label(__('Message'))
|
||||||
->wrap(),
|
->wrap(),
|
||||||
|
|||||||
@@ -12,9 +12,22 @@ use Filament\Facades\Filament;
|
|||||||
use Filament\Models\Contracts\FilamentUser;
|
use Filament\Models\Contracts\FilamentUser;
|
||||||
use Illuminate\Contracts\Auth\Guard;
|
use Illuminate\Contracts\Auth\Guard;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\HtmlString;
|
||||||
|
|
||||||
class Login extends BaseLogin
|
class Login extends BaseLogin
|
||||||
{
|
{
|
||||||
|
public function getSubheading(): string | HtmlString | null
|
||||||
|
{
|
||||||
|
if (env('JABALI_DEMO', false)) {
|
||||||
|
return new HtmlString(
|
||||||
|
__('Demo credentials') .
|
||||||
|
': <code>demo@jabali-panel.com</code> / <code>demo1234</code>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getSubheading();
|
||||||
|
}
|
||||||
|
|
||||||
public function authenticate(): ?LoginResponse
|
public function authenticate(): ?LoginResponse
|
||||||
{
|
{
|
||||||
$panel = Filament::getPanel('jabali');
|
$panel = Filament::getPanel('jabali');
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ class Backups extends Page implements HasActions, HasForms, HasTable
|
|||||||
->sortable(),
|
->sortable(),
|
||||||
TextColumn::make('duration')
|
TextColumn::make('duration')
|
||||||
->label(__('Duration'))
|
->label(__('Duration'))
|
||||||
->placeholder('-'),
|
->placeholder(__('-')),
|
||||||
])
|
])
|
||||||
->defaultSort('created_at', 'desc')
|
->defaultSort('created_at', 'desc')
|
||||||
->emptyStateHeading(__('No restore history'))
|
->emptyStateHeading(__('No restore history'))
|
||||||
@@ -1092,7 +1092,7 @@ class Backups extends Page implements HasActions, HasForms, HasTable
|
|||||||
Grid::make(2)->schema([
|
Grid::make(2)->schema([
|
||||||
TextInput::make('host')
|
TextInput::make('host')
|
||||||
->label(__('Host'))
|
->label(__('Host'))
|
||||||
->placeholder('backup.example.com')
|
->placeholder(__('backup.example.com'))
|
||||||
->required(),
|
->required(),
|
||||||
TextInput::make('port')
|
TextInput::make('port')
|
||||||
->label(__('Port'))
|
->label(__('Port'))
|
||||||
|
|||||||
@@ -571,7 +571,7 @@ class CpanelMigration extends Page implements HasActions, HasForms
|
|||||||
Grid::make(['default' => 1, 'sm' => 2])->schema([
|
Grid::make(['default' => 1, 'sm' => 2])->schema([
|
||||||
TextInput::make('hostname')
|
TextInput::make('hostname')
|
||||||
->label(__('cPanel Hostname'))
|
->label(__('cPanel Hostname'))
|
||||||
->placeholder('cpanel.example.com')
|
->placeholder(__('cpanel.example.com'))
|
||||||
->required()
|
->required()
|
||||||
->helperText(__('Your cPanel server hostname or IP address')),
|
->helperText(__('Your cPanel server hostname or IP address')),
|
||||||
TextInput::make('port')
|
TextInput::make('port')
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ class DnsRecords extends Page implements HasActions, HasForms, HasTable
|
|||||||
->sortable(),
|
->sortable(),
|
||||||
TextColumn::make('priority')
|
TextColumn::make('priority')
|
||||||
->label(__('Priority'))
|
->label(__('Priority'))
|
||||||
->placeholder('-')
|
->placeholder(__('-'))
|
||||||
->color(fn (DnsRecord $record) => $this->isRecordPendingDelete($record->id) ? 'danger' : null)
|
->color(fn (DnsRecord $record) => $this->isRecordPendingDelete($record->id) ? 'danger' : null)
|
||||||
->sortable(),
|
->sortable(),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ class Domains extends Page implements HasActions, HasForms, HasTable
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('domain_redirect_url')
|
TextInput::make('domain_redirect_url')
|
||||||
->label(__('Redirect To'))
|
->label(__('Redirect To'))
|
||||||
->placeholder('https://newdomain.com')
|
->placeholder(__('https://newdomain.com'))
|
||||||
->helperText(__('All requests to this domain will be redirected to this URL'))
|
->helperText(__('All requests to this domain will be redirected to this URL'))
|
||||||
->url()
|
->url()
|
||||||
->required(fn ($get) => $get('domain_redirect_enabled'))
|
->required(fn ($get) => $get('domain_redirect_enabled'))
|
||||||
@@ -275,13 +275,13 @@ class Domains extends Page implements HasActions, HasForms, HasTable
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('source_path')
|
TextInput::make('source_path')
|
||||||
->label(__('Source Path'))
|
->label(__('Source Path'))
|
||||||
->placeholder('/old-page')
|
->placeholder(__('/old-page'))
|
||||||
->helperText(__('Path to redirect from (e.g., /old-page)'))
|
->helperText(__('Path to redirect from (e.g., /old-page)'))
|
||||||
->required()
|
->required()
|
||||||
->columnSpan(['default' => 2, 'md' => 1]),
|
->columnSpan(['default' => 2, 'md' => 1]),
|
||||||
TextInput::make('destination_url')
|
TextInput::make('destination_url')
|
||||||
->label(__('Destination URL'))
|
->label(__('Destination URL'))
|
||||||
->placeholder('https://example.com/new-page')
|
->placeholder(__('https://example.com/new-page'))
|
||||||
->helperText(__('Full URL to redirect to'))
|
->helperText(__('Full URL to redirect to'))
|
||||||
->required()
|
->required()
|
||||||
->url()
|
->url()
|
||||||
@@ -359,13 +359,13 @@ class Domains extends Page implements HasActions, HasForms, HasTable
|
|||||||
Textarea::make('allowed_domains')
|
Textarea::make('allowed_domains')
|
||||||
->label(__('Allowed Domains'))
|
->label(__('Allowed Domains'))
|
||||||
->helperText(__('One domain per line that can link to your files (your own domain is always allowed)'))
|
->helperText(__('One domain per line that can link to your files (your own domain is always allowed)'))
|
||||||
->placeholder("example.com\ntrusted-site.com")
|
->placeholder(__("example.com\ntrusted-site.com"))
|
||||||
->rows(4)
|
->rows(4)
|
||||||
->columnSpan(['default' => 2, 'md' => 1]),
|
->columnSpan(['default' => 2, 'md' => 1]),
|
||||||
TextInput::make('protected_extensions')
|
TextInput::make('protected_extensions')
|
||||||
->label(__('Protected File Extensions'))
|
->label(__('Protected File Extensions'))
|
||||||
->helperText(__('Comma-separated list of file extensions to protect'))
|
->helperText(__('Comma-separated list of file extensions to protect'))
|
||||||
->placeholder('jpg,jpeg,png,gif,webp,svg,mp4,mp3,pdf')
|
->placeholder(__('jpg,jpeg,png,gif,webp,svg,mp4,mp3,pdf'))
|
||||||
->default(DomainHotlinkSetting::getDefaultExtensions())
|
->default(DomainHotlinkSetting::getDefaultExtensions())
|
||||||
->columnSpan(['default' => 2, 'md' => 1]),
|
->columnSpan(['default' => 2, 'md' => 1]),
|
||||||
])
|
])
|
||||||
@@ -381,7 +381,7 @@ class Domains extends Page implements HasActions, HasForms, HasTable
|
|||||||
TextInput::make('redirect_url')
|
TextInput::make('redirect_url')
|
||||||
->label(__('Redirect URL (Optional)'))
|
->label(__('Redirect URL (Optional)'))
|
||||||
->helperText(__('Redirect blocked requests to this URL instead of showing an error'))
|
->helperText(__('Redirect blocked requests to this URL instead of showing an error'))
|
||||||
->placeholder('https://example.com/hotlink-blocked.png')
|
->placeholder(__('https://example.com/hotlink-blocked.png'))
|
||||||
->url()
|
->url()
|
||||||
->columnSpan(['default' => 2, 'md' => 1]),
|
->columnSpan(['default' => 2, 'md' => 1]),
|
||||||
])
|
])
|
||||||
@@ -842,7 +842,7 @@ class Domains extends Page implements HasActions, HasForms, HasTable
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('alias')
|
TextInput::make('alias')
|
||||||
->label(__('Alias Domain'))
|
->label(__('Alias Domain'))
|
||||||
->placeholder('alias-example.com')
|
->placeholder(__('alias-example.com'))
|
||||||
->required()
|
->required()
|
||||||
->rule('regex:/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*\\.[a-z]{2,}$/')
|
->rule('regex:/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*\\.[a-z]{2,}$/')
|
||||||
->helperText(__('Enter a full domain name.')),
|
->helperText(__('Enter a full domain name.')),
|
||||||
|
|||||||
@@ -185,11 +185,11 @@ class Email extends Page implements HasActions, HasForms, HasTable
|
|||||||
Textarea::make('whitelist')
|
Textarea::make('whitelist')
|
||||||
->label(__('Whitelist (one per line)'))
|
->label(__('Whitelist (one per line)'))
|
||||||
->rows(6)
|
->rows(6)
|
||||||
->placeholder("friend@example.com\ntrusted.com"),
|
->placeholder(__("friend@example.com\ntrusted.com")),
|
||||||
Textarea::make('blacklist')
|
Textarea::make('blacklist')
|
||||||
->label(__('Blacklist (one per line)'))
|
->label(__('Blacklist (one per line)'))
|
||||||
->rows(6)
|
->rows(6)
|
||||||
->placeholder("spam@example.com\nbad-domain.com"),
|
->placeholder(__("spam@example.com\nbad-domain.com")),
|
||||||
TextInput::make('score')
|
TextInput::make('score')
|
||||||
->label(__('Spam Score Threshold'))
|
->label(__('Spam Score Threshold'))
|
||||||
->numeric()
|
->numeric()
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
// Invalid path from URL - reset to home directory
|
// Invalid path from URL - reset to home directory
|
||||||
$this->currentPath = '';
|
$this->currentPath = '';
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Invalid path')
|
->title(__('Invalid path'))
|
||||||
->body('The requested path is not allowed.')
|
->body('The requested path is not allowed.')
|
||||||
->danger()
|
->danger()
|
||||||
->send();
|
->send();
|
||||||
@@ -222,7 +222,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->items = [];
|
$this->items = [];
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Error loading directory')
|
->title(__('Error loading directory'))
|
||||||
->body($e->getMessage())
|
->body($e->getMessage())
|
||||||
->danger()
|
->danger()
|
||||||
->send();
|
->send();
|
||||||
@@ -237,7 +237,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
$this->resetTable();
|
$this->resetTable();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Invalid path')
|
->title(__('Invalid path'))
|
||||||
->body($e->getMessage())
|
->body($e->getMessage())
|
||||||
->danger()
|
->danger()
|
||||||
->send();
|
->send();
|
||||||
@@ -425,7 +425,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
->form([
|
->form([
|
||||||
TextInput::make('mode')
|
TextInput::make('mode')
|
||||||
->label(__('Numeric Mode'))
|
->label(__('Numeric Mode'))
|
||||||
->placeholder('755')
|
->placeholder(__('755'))
|
||||||
->maxLength(4)
|
->maxLength(4)
|
||||||
->helperText(__('Enter octal mode (e.g., 755, 644)')),
|
->helperText(__('Enter octal mode (e.g., 755, 644)')),
|
||||||
Grid::make(3)
|
Grid::make(3)
|
||||||
@@ -750,7 +750,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
$this->getAgent()->fileMove($this->getUsername(), $sourcePath, $destPath);
|
$this->getAgent()->fileMove($this->getUsername(), $sourcePath, $destPath);
|
||||||
|
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Item moved successfully')
|
->title(__('Item moved successfully'))
|
||||||
->success()
|
->success()
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
@@ -758,7 +758,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
$this->resetTable();
|
$this->resetTable();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Error moving item')
|
->title(__('Error moving item'))
|
||||||
->body($e->getMessage())
|
->body($e->getMessage())
|
||||||
->danger()
|
->danger()
|
||||||
->send();
|
->send();
|
||||||
@@ -788,7 +788,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
);
|
);
|
||||||
|
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title("Uploaded: $filename")
|
->title(__('Uploaded: :filename', ['filename' => $filename]))
|
||||||
->success()
|
->success()
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
@@ -796,7 +796,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
$this->resetTable();
|
$this->resetTable();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title("Upload failed: $filename")
|
->title(__('Upload failed: :filename', ['filename' => $filename]))
|
||||||
->body($e->getMessage())
|
->body($e->getMessage())
|
||||||
->danger()
|
->danger()
|
||||||
->send();
|
->send();
|
||||||
@@ -939,7 +939,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
$uploaded++;
|
$uploaded++;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title(__('Upload failed: ').$file->getClientOriginalName())
|
->title(__('Upload failed: :filename', ['filename' => $file->getClientOriginalName()]))
|
||||||
->body($e->getMessage())
|
->body($e->getMessage())
|
||||||
->danger()
|
->danger()
|
||||||
->send();
|
->send();
|
||||||
@@ -1007,7 +1007,7 @@ class Files extends Page implements HasActions, HasForms, HasTable
|
|||||||
filename: basename($path)
|
filename: basename($path)
|
||||||
);
|
);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Notification::make()->title('Error downloading')->body($e->getMessage())->danger()->send();
|
Notification::make()->title(__('Error downloading'))->body($e->getMessage())->danger()->send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ class GitDeployment extends Page implements HasActions, HasForms, HasTable
|
|||||||
}),
|
}),
|
||||||
TextInput::make('repo_url')
|
TextInput::make('repo_url')
|
||||||
->label(__('Repository URL'))
|
->label(__('Repository URL'))
|
||||||
->placeholder('git@github.com:org/repo.git')
|
->placeholder(__('git@github.com:org/repo.git'))
|
||||||
->required(),
|
->required(),
|
||||||
TextInput::make('branch')
|
TextInput::make('branch')
|
||||||
->label(__('Branch'))
|
->label(__('Branch'))
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class MailingLists extends Page implements HasActions, HasForms
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('listmonk_url')
|
TextInput::make('listmonk_url')
|
||||||
->label(__('Listmonk URL'))
|
->label(__('Listmonk URL'))
|
||||||
->placeholder('https://lists.example.com')
|
->placeholder(__('https://lists.example.com'))
|
||||||
->url()
|
->url()
|
||||||
->visible(fn ($get) => $get('provider') === 'listmonk'),
|
->visible(fn ($get) => $get('provider') === 'listmonk'),
|
||||||
TextInput::make('listmonk_token')
|
TextInput::make('listmonk_token')
|
||||||
@@ -99,7 +99,7 @@ class MailingLists extends Page implements HasActions, HasForms
|
|||||||
->schema([
|
->schema([
|
||||||
TextInput::make('mailman_url')
|
TextInput::make('mailman_url')
|
||||||
->label(__('Mailman URL'))
|
->label(__('Mailman URL'))
|
||||||
->placeholder('https://lists.example.com/mailman')
|
->placeholder(__('https://lists.example.com/mailman'))
|
||||||
->url()
|
->url()
|
||||||
->visible(fn ($get) => $get('provider') === 'mailman'),
|
->visible(fn ($get) => $get('provider') === 'mailman'),
|
||||||
TextInput::make('mailman_admin')
|
TextInput::make('mailman_admin')
|
||||||
|
|||||||
@@ -83,6 +83,16 @@ class PhpSettings extends Page implements HasActions, HasForms
|
|||||||
|
|
||||||
protected function loadDomains(): void
|
protected function loadDomains(): void
|
||||||
{
|
{
|
||||||
|
if ((bool) env('JABALI_DEMO', false)) {
|
||||||
|
$this->domains = [
|
||||||
|
['domain' => 'jabali-panel.com'],
|
||||||
|
['domain' => 'demo-site.com'],
|
||||||
|
['domain' => 'store.demo'],
|
||||||
|
];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->getAgent()->send('domain.list', [
|
$result = $this->getAgent()->send('domain.list', [
|
||||||
'username' => $this->getUsername(),
|
'username' => $this->getUsername(),
|
||||||
]);
|
]);
|
||||||
@@ -92,6 +102,17 @@ class PhpSettings extends Page implements HasActions, HasForms
|
|||||||
|
|
||||||
protected function loadPhpVersions(): void
|
protected function loadPhpVersions(): void
|
||||||
{
|
{
|
||||||
|
if ((bool) env('JABALI_DEMO', false)) {
|
||||||
|
$this->phpVersions = [
|
||||||
|
'8.4' => 'PHP 8.4',
|
||||||
|
'8.3' => 'PHP 8.3',
|
||||||
|
'8.2' => 'PHP 8.2',
|
||||||
|
'8.1' => 'PHP 8.1',
|
||||||
|
];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->getAgent()->send('php.list_versions', []);
|
$result = $this->getAgent()->send('php.list_versions', []);
|
||||||
|
|
||||||
$this->phpVersions = [];
|
$this->phpVersions = [];
|
||||||
@@ -120,6 +141,20 @@ class PhpSettings extends Page implements HasActions, HasForms
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((bool) env('JABALI_DEMO', false)) {
|
||||||
|
$this->data = [
|
||||||
|
'php_version' => array_key_first($this->phpVersions),
|
||||||
|
'memory_limit' => '256M',
|
||||||
|
'upload_max_filesize' => '64M',
|
||||||
|
'post_max_size' => '64M',
|
||||||
|
'max_input_vars' => '3000',
|
||||||
|
'max_execution_time' => '300',
|
||||||
|
'max_input_time' => '300',
|
||||||
|
];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->getAgent()->send('php.getSettings', [
|
$result = $this->getAgent()->send('php.getSettings', [
|
||||||
'domain' => $this->selectedDomain,
|
'domain' => $this->selectedDomain,
|
||||||
'username' => $this->getUsername(),
|
'username' => $this->getUsername(),
|
||||||
|
|||||||
@@ -80,6 +80,16 @@ class ProtectedDirectories extends Page implements HasActions, HasForms, HasTabl
|
|||||||
|
|
||||||
protected function loadDomains(): void
|
protected function loadDomains(): void
|
||||||
{
|
{
|
||||||
|
if ((bool) env('JABALI_DEMO', false)) {
|
||||||
|
$this->domains = [
|
||||||
|
['domain' => 'jabali-panel.com'],
|
||||||
|
['domain' => 'demo-site.com'],
|
||||||
|
['domain' => 'store.demo'],
|
||||||
|
];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->getAgent()->send('domain.list', [
|
$result = $this->getAgent()->send('domain.list', [
|
||||||
'username' => $this->getUsername(),
|
'username' => $this->getUsername(),
|
||||||
]);
|
]);
|
||||||
@@ -102,6 +112,30 @@ class ProtectedDirectories extends Page implements HasActions, HasForms, HasTabl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((bool) env('JABALI_DEMO', false)) {
|
||||||
|
$this->protectedDirs = [
|
||||||
|
[
|
||||||
|
'path' => '/admin',
|
||||||
|
'name' => 'Restricted Area',
|
||||||
|
'users_count' => 2,
|
||||||
|
'users' => [
|
||||||
|
['username' => 'demo', 'created_at' => now()->subDays(10)->toDateTimeString()],
|
||||||
|
['username' => 'editor', 'created_at' => now()->subDays(3)->toDateTimeString()],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'path' => '/private',
|
||||||
|
'name' => 'Private Files',
|
||||||
|
'users_count' => 1,
|
||||||
|
'users' => [
|
||||||
|
['username' => 'staff', 'created_at' => now()->subDays(1)->toDateTimeString()],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->getAgent()->send('domain.list_protected_dirs', [
|
$result = $this->getAgent()->send('domain.list_protected_dirs', [
|
||||||
'domain' => $this->selectedDomain,
|
'domain' => $this->selectedDomain,
|
||||||
'username' => $this->getUsername(),
|
'username' => $this->getUsername(),
|
||||||
@@ -239,7 +273,7 @@ class ProtectedDirectories extends Page implements HasActions, HasForms, HasTabl
|
|||||||
->form([
|
->form([
|
||||||
TextInput::make('path')
|
TextInput::make('path')
|
||||||
->label(__('Directory Path'))
|
->label(__('Directory Path'))
|
||||||
->placeholder('/admin')
|
->placeholder(__('/admin'))
|
||||||
->required()
|
->required()
|
||||||
->helperText(__('Path relative to your document root (e.g., /admin, /private, /members)')),
|
->helperText(__('Path relative to your document root (e.g., /admin, /private, /members)')),
|
||||||
TextInput::make('name')
|
TextInput::make('name')
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class Ssl extends Page implements HasActions, HasForms, HasTable
|
|||||||
? __('Expired :days days ago', ['days' => abs($record->sslCertificate->days_until_expiry)])
|
? __('Expired :days days ago', ['days' => abs($record->sslCertificate->days_until_expiry)])
|
||||||
: __(':days days left', ['days' => $record->sslCertificate->days_until_expiry]))
|
: __(':days days left', ['days' => $record->sslCertificate->days_until_expiry]))
|
||||||
: null)
|
: null)
|
||||||
->placeholder('-')
|
->placeholder(__('-'))
|
||||||
->sortable(),
|
->sortable(),
|
||||||
IconColumn::make('sslCertificate.auto_renew')
|
IconColumn::make('sslCertificate.auto_renew')
|
||||||
->label(__('Auto-Renew'))
|
->label(__('Auto-Renew'))
|
||||||
@@ -421,19 +421,19 @@ class Ssl extends Page implements HasActions, HasForms, HasTable
|
|||||||
->helperText(__('Select the domain to install the certificate on')),
|
->helperText(__('Select the domain to install the certificate on')),
|
||||||
Textarea::make('certificate')
|
Textarea::make('certificate')
|
||||||
->label(__('Certificate (PEM format)'))
|
->label(__('Certificate (PEM format)'))
|
||||||
->placeholder("-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----")
|
->placeholder(__("-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"))
|
||||||
->rows(8)
|
->rows(8)
|
||||||
->required()
|
->required()
|
||||||
->helperText(__('Paste your SSL certificate in PEM format')),
|
->helperText(__('Paste your SSL certificate in PEM format')),
|
||||||
Textarea::make('private_key')
|
Textarea::make('private_key')
|
||||||
->label(__('Private Key (PEM format)'))
|
->label(__('Private Key (PEM format)'))
|
||||||
->placeholder("-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----")
|
->placeholder(__("-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"))
|
||||||
->rows(8)
|
->rows(8)
|
||||||
->required()
|
->required()
|
||||||
->helperText(__('Paste your private key in PEM format. Keep this secure!')),
|
->helperText(__('Paste your private key in PEM format. Keep this secure!')),
|
||||||
Textarea::make('ca_bundle')
|
Textarea::make('ca_bundle')
|
||||||
->label(__('CA Bundle (optional)'))
|
->label(__('CA Bundle (optional)'))
|
||||||
->placeholder("-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----")
|
->placeholder(__("-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"))
|
||||||
->rows(6)
|
->rows(6)
|
||||||
->helperText(__('Paste the certificate authority chain if required by your certificate provider')),
|
->helperText(__('Paste the certificate authority chain if required by your certificate provider')),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class DnsPendingAddsTable extends Component implements HasActions, HasSchemas, H
|
|||||||
->label(__('TTL')),
|
->label(__('TTL')),
|
||||||
TextColumn::make('priority')
|
TextColumn::make('priority')
|
||||||
->label(__('Priority'))
|
->label(__('Priority'))
|
||||||
->placeholder('-'),
|
->placeholder(__('-')),
|
||||||
])
|
])
|
||||||
->actions([
|
->actions([
|
||||||
Action::make('removePending')
|
Action::make('removePending')
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
->withMiddleware(function (Middleware $middleware): void {
|
->withMiddleware(function (Middleware $middleware): void {
|
||||||
|
$middleware->trustProxies(at: '*');
|
||||||
$middleware->append(\App\Http\Middleware\SecurityHeaders::class);
|
$middleware->append(\App\Http\Middleware\SecurityHeaders::class);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions): void {
|
->withExceptions(function (Exceptions $exceptions): void {
|
||||||
|
|||||||
49
docker/Dockerfile.demo
Normal file
49
docker/Dockerfile.demo
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
FROM php:8.4-cli
|
||||||
|
|
||||||
|
WORKDIR /var/www/jabali
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
git \
|
||||||
|
unzip \
|
||||||
|
sqlite3 \
|
||||||
|
libsqlite3-dev \
|
||||||
|
libzip-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libjpeg62-turbo-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libicu-dev \
|
||||||
|
libonig-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||||
|
&& docker-php-ext-install -j$(nproc) \
|
||||||
|
bcmath \
|
||||||
|
exif \
|
||||||
|
gd \
|
||||||
|
intl \
|
||||||
|
mbstring \
|
||||||
|
pdo \
|
||||||
|
pdo_sqlite \
|
||||||
|
zip \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY . /var/www/jabali
|
||||||
|
|
||||||
|
COPY docker/entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
ENV APP_ENV=demo \
|
||||||
|
APP_DEBUG=false \
|
||||||
|
APP_URL=http://localhost:5555 \
|
||||||
|
DB_CONNECTION=sqlite \
|
||||||
|
DB_DATABASE=/var/www/jabali/database/demo.sqlite \
|
||||||
|
JABALI_DEMO=1 \
|
||||||
|
JABALI_DEMO_EMAIL=demo-admin@jabali-panel.com \
|
||||||
|
JABALI_DEMO_PASSWORD=demo12345 \
|
||||||
|
SESSION_DRIVER=file \
|
||||||
|
CACHE_STORE=file \
|
||||||
|
QUEUE_CONNECTION=sync
|
||||||
|
|
||||||
|
EXPOSE 5555
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
27
docker/entrypoint.sh
Normal file
27
docker/entrypoint.sh
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cd /var/www/jabali
|
||||||
|
|
||||||
|
mkdir -p storage/framework/cache storage/framework/sessions storage/framework/views storage/logs bootstrap/cache
|
||||||
|
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
cp .env.example .env
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${APP_URL:-}" ]; then
|
||||||
|
sed -i "s|^APP_URL=.*|APP_URL=${APP_URL}|" .env
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${ASSET_URL:-}" ]; then
|
||||||
|
sed -i "s|^ASSET_URL=.*|ASSET_URL=${ASSET_URL}|" .env
|
||||||
|
fi
|
||||||
|
|
||||||
|
php -r "if (trim(getenv('APP_KEY') ?: '') === '') { echo 'Generating APP_KEY...\n'; }"
|
||||||
|
php artisan key:generate --force >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
php artisan storage:link >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
chmod -R ug+rw storage bootstrap/cache
|
||||||
|
|
||||||
|
exec php -S 0.0.0.0:5555 -t public
|
||||||
@@ -120,3 +120,9 @@ This blueprint describes a modern web hosting control panel (cPanel/DirectAdmin-
|
|||||||
- Local + S3 target
|
- Local + S3 target
|
||||||
- Schedule + retention
|
- Schedule + retention
|
||||||
- Restore job with step logs
|
- Restore job with step logs
|
||||||
|
|
||||||
|
## 9) Demo mode considerations
|
||||||
|
|
||||||
|
- Read-only middleware should block data mutations but allow authentication.
|
||||||
|
- Demo deployments may run without privileged agent sockets; provide static demo data for agent-dependent pages.
|
||||||
|
- Reverse proxy must be trusted to ensure Livewire update URLs are HTTPS.
|
||||||
|
|||||||
@@ -10,3 +10,11 @@ Archived to `/opt/jabali-archive/cleanup-20260124/repo-root`:
|
|||||||
- `jabali_logo.svg` (duplicate of `public/images/jabali_logo.svg`)
|
- `jabali_logo.svg` (duplicate of `public/images/jabali_logo.svg`)
|
||||||
|
|
||||||
Restore by moving the files back to the repo root if needed.
|
Restore by moving the files back to the repo root if needed.
|
||||||
|
|
||||||
|
## Demo housekeeping (2026-02-03)
|
||||||
|
|
||||||
|
Disk cleanup steps used on demo hosts:
|
||||||
|
|
||||||
|
- Vacuum systemd journals (`journalctl --vacuum-time=7d`)
|
||||||
|
- Clean apt cache (`apt-get clean` + remove `/var/lib/apt/lists/*`)
|
||||||
|
- Prune Docker build cache (`docker builder prune -af`)
|
||||||
|
|||||||
@@ -37,6 +37,63 @@ After install, systemd services are enabled and started:
|
|||||||
- `jabali-queue`
|
- `jabali-queue`
|
||||||
- `jabali-health-monitor`
|
- `jabali-health-monitor`
|
||||||
|
|
||||||
|
## Demo Docker (single container)
|
||||||
|
|
||||||
|
Demo images run the panel in read-only mode with demo data preloaded.
|
||||||
|
|
||||||
|
Key requirements:
|
||||||
|
|
||||||
|
- `JABALI_DEMO=1`
|
||||||
|
- `DB_DATABASE` must point to the demo SQLite file: `database/database-demo.sqlite`
|
||||||
|
- Trust reverse proxy so Livewire update URLs are HTTPS
|
||||||
|
|
||||||
|
Example container run (port 5555):
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -d --name jabali-panel-demo \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 5555:5555 \
|
||||||
|
-e APP_URL=https://demo.jabali-panel.com \
|
||||||
|
-e ASSET_URL=https://demo.jabali-panel.com \
|
||||||
|
-e DB_DATABASE=/var/www/jabali/database/database-demo.sqlite \
|
||||||
|
-e JABALI_DEMO=1 \
|
||||||
|
-e JABALI_DEMO_ADMIN_EMAIL=admin@jabali-panel.com \
|
||||||
|
-e JABALI_DEMO_ADMIN_PASSWORD=demo1234 \
|
||||||
|
-e JABALI_DEMO_USER_EMAIL=demo@jabali-panel.com \
|
||||||
|
-e JABALI_DEMO_USER_PASSWORD=demo1234 \
|
||||||
|
jabali-panel-demo-current
|
||||||
|
```
|
||||||
|
|
||||||
|
Nginx reverse proxy (HTTPS):
|
||||||
|
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name demo.jabali-panel.com;
|
||||||
|
ssl_certificate /etc/letsencrypt/live/demo.jabali-panel.com/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/demo.jabali-panel.com/privkey.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5555;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reverse proxy trust (required for Livewire HTTPS):
|
||||||
|
|
||||||
|
- Ensure `bootstrap/app.php` includes `trustProxies(at: '*')`.
|
||||||
|
|
||||||
|
Demo limitations:
|
||||||
|
|
||||||
|
- No agent socket in the container: pages that rely on the agent should use
|
||||||
|
static demo data to avoid 500s.
|
||||||
|
|
||||||
## Panel notifications (admin + user)
|
## Panel notifications (admin + user)
|
||||||
|
|
||||||
Jabali ships with a hardened Filament notifications setup that prevents Livewire
|
Jabali ships with a hardened Filament notifications setup that prevents Livewire
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
if [[ -f "$SCRIPT_DIR/VERSION" ]]; then
|
if [[ -f "$SCRIPT_DIR/VERSION" ]]; then
|
||||||
JABALI_VERSION="$(sed -n 's/^VERSION=//p' "$SCRIPT_DIR/VERSION")"
|
JABALI_VERSION="$(sed -n 's/^VERSION=//p' "$SCRIPT_DIR/VERSION")"
|
||||||
fi
|
fi
|
||||||
JABALI_VERSION="${JABALI_VERSION:-0.9-rc51}"
|
JABALI_VERSION="${JABALI_VERSION:-0.9-rc52}"
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
|
|||||||
3536
lang/en.json
3536
lang/en.json
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,11 @@
|
|||||||
<x-application-logo class="block h-12 w-auto" />
|
<x-application-logo class="block h-12 w-auto" />
|
||||||
|
|
||||||
<h1 class="mt-8 text-2xl font-medium text-gray-900">
|
<h1 class="mt-8 text-2xl font-medium text-gray-900">
|
||||||
Welcome to your Jetstream application!
|
{{ __('Welcome to your Jetstream application!') }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="mt-6 text-gray-500 leading-relaxed">
|
<p class="mt-6 text-gray-500 leading-relaxed">
|
||||||
Laravel Jetstream provides a beautiful, robust starting point for your next Laravel application. Laravel is designed
|
{{ __('Laravel Jetstream provides a beautiful, robust starting point for your next Laravel application. Laravel is designed to help you build your application using a development environment that is simple, powerful, and enjoyable. We believe you should love expressing your creativity through programming, so we have spent time carefully crafting the Laravel ecosystem to be a breath of fresh air. We hope you love it.') }}
|
||||||
to help you build your application using a development environment that is simple, powerful, and enjoyable. We believe
|
|
||||||
you should love expressing your creativity through programming, so we have spent time carefully crafting the Laravel
|
|
||||||
ecosystem to be a breath of fresh air. We hope you love it.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -20,17 +17,17 @@
|
|||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
|
||||||
</svg>
|
</svg>
|
||||||
<h2 class="ms-3 text-xl font-semibold text-gray-900">
|
<h2 class="ms-3 text-xl font-semibold text-gray-900">
|
||||||
<a href="https://laravel.com/docs">Documentation</a>
|
<a href="https://laravel.com/docs">{{ __('Documentation') }}</a>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-4 text-gray-500 text-sm leading-relaxed">
|
<p class="mt-4 text-gray-500 text-sm leading-relaxed">
|
||||||
Laravel has wonderful documentation covering every aspect of the framework. Whether you're new to the framework or have previous experience, we recommend reading all of the documentation from beginning to end.
|
{{ __('Laravel has wonderful documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience, we recommend reading all of the documentation from beginning to end.') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="mt-4 text-sm">
|
<p class="mt-4 text-sm">
|
||||||
<a href="https://laravel.com/docs" class="inline-flex items-center font-semibold text-indigo-700">
|
<a href="https://laravel.com/docs" class="inline-flex items-center font-semibold text-indigo-700">
|
||||||
Explore the documentation
|
{{ __('Explore the documentation') }}
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="ms-1 size-5 fill-indigo-500">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="ms-1 size-5 fill-indigo-500">
|
||||||
<path fill-rule="evenodd" d="M5 10a.75.75 0 01.75-.75h6.638L10.23 7.29a.75.75 0 111.04-1.08l3.5 3.25a.75.75 0 010 1.08l-3.5 3.25a.75.75 0 11-1.04-1.08l2.158-1.96H5.75A.75.75 0 015 10z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M5 10a.75.75 0 01.75-.75h6.638L10.23 7.29a.75.75 0 111.04-1.08l3.5 3.25a.75.75 0 010 1.08l-3.5 3.25a.75.75 0 11-1.04-1.08l2.158-1.96H5.75A.75.75 0 015 10z" clip-rule="evenodd" />
|
||||||
@@ -45,17 +42,17 @@
|
|||||||
<path stroke-linecap="round" d="M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z" />
|
<path stroke-linecap="round" d="M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z" />
|
||||||
</svg>
|
</svg>
|
||||||
<h2 class="ms-3 text-xl font-semibold text-gray-900">
|
<h2 class="ms-3 text-xl font-semibold text-gray-900">
|
||||||
<a href="https://laracasts.com">Laracasts</a>
|
<a href="https://laracasts.com">{{ __('Laracasts') }}</a>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-4 text-gray-500 text-sm leading-relaxed">
|
<p class="mt-4 text-gray-500 text-sm leading-relaxed">
|
||||||
Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process.
|
{{ __('Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process.') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="mt-4 text-sm">
|
<p class="mt-4 text-sm">
|
||||||
<a href="https://laracasts.com" class="inline-flex items-center font-semibold text-indigo-700">
|
<a href="https://laracasts.com" class="inline-flex items-center font-semibold text-indigo-700">
|
||||||
Start watching Laracasts
|
{{ __('Start watching Laracasts') }}
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="ms-1 size-5 fill-indigo-500">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="ms-1 size-5 fill-indigo-500">
|
||||||
<path fill-rule="evenodd" d="M5 10a.75.75 0 01.75-.75h6.638L10.23 7.29a.75.75 0 111.04-1.08l3.5 3.25a.75.75 0 010 1.08l-3.5 3.25a.75.75 0 11-1.04-1.08l2.158-1.96H5.75A.75.75 0 015 10z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M5 10a.75.75 0 01.75-.75h6.638L10.23 7.29a.75.75 0 111.04-1.08l3.5 3.25a.75.75 0 010 1.08l-3.5 3.25a.75.75 0 11-1.04-1.08l2.158-1.96H5.75A.75.75 0 015 10z" clip-rule="evenodd" />
|
||||||
@@ -70,12 +67,12 @@
|
|||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
<h2 class="ms-3 text-xl font-semibold text-gray-900">
|
<h2 class="ms-3 text-xl font-semibold text-gray-900">
|
||||||
<a href="https://tailwindcss.com/">Tailwind</a>
|
<a href="https://tailwindcss.com/">{{ __('Tailwind') }}</a>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-4 text-gray-500 text-sm leading-relaxed">
|
<p class="mt-4 text-gray-500 text-sm leading-relaxed">
|
||||||
Laravel Jetstream is built with Tailwind, an amazing utility first CSS framework that doesn't get in your way. You'll be amazed how easily you can build and maintain fresh, modern designs with this wonderful framework at your fingertips.
|
{{ __('Laravel Jetstream is built with Tailwind, an amazing utility first CSS framework that does not get in your way. You will be amazed how easily you can build and maintain fresh, modern designs with this wonderful framework at your fingertips.') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -85,12 +82,12 @@
|
|||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" />
|
||||||
</svg>
|
</svg>
|
||||||
<h2 class="ms-3 text-xl font-semibold text-gray-900">
|
<h2 class="ms-3 text-xl font-semibold text-gray-900">
|
||||||
Authentication
|
{{ __('Authentication') }}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-4 text-gray-500 text-sm leading-relaxed">
|
<p class="mt-4 text-gray-500 text-sm leading-relaxed">
|
||||||
Authentication and registration views are included with Laravel Jetstream, as well as support for user email verification and resetting forgotten passwords. So, you're free to get started with what matters most: building your application.
|
{{ __('Authentication and registration views are included with Laravel Jetstream, as well as support for user email verification and resetting forgotten passwords. So, you are free to get started with what matters most: building your application.') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
<x-slot name="description">{{ __('Use these endpoints with a token that has the automation ability.') }}</x-slot>
|
<x-slot name="description">{{ __('Use these endpoints with a token that has the automation ability.') }}</x-slot>
|
||||||
|
|
||||||
<div class="space-y-2 fi-section-header-description">
|
<div class="space-y-2 fi-section-header-description">
|
||||||
<div><span class="font-mono">GET</span> /api/automation/users</div>
|
<div><span class="font-mono">{{ __('GET') }}</span> {{ __('/api/automation/users') }}</div>
|
||||||
<div><span class="font-mono">POST</span> /api/automation/users</div>
|
<div><span class="font-mono">{{ __('POST') }}</span> {{ __('/api/automation/users') }}</div>
|
||||||
<div><span class="font-mono">POST</span> /api/automation/domains</div>
|
<div><span class="font-mono">{{ __('POST') }}</span> {{ __('/api/automation/domains') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</x-filament::section>
|
</x-filament::section>
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<x-filament::badge color="success">{{ $add['type'] }}</x-filament::badge>
|
<x-filament::badge color="success">{{ $add['type'] }}</x-filament::badge>
|
||||||
<span class="font-mono fi-section-header-heading">{{ $add['name'] }}</span>
|
<span class="font-mono fi-section-header-heading">{{ $add['name'] }}</span>
|
||||||
<span class="fi-section-header-description">→</span>
|
<span class="fi-section-header-description">{{ __('→') }}</span>
|
||||||
<span class="font-mono fi-section-header-description truncate max-w-xs" title="{{ $add['content'] }}">{{ Str::limit($add['content'], 40) }}</span>
|
<span class="font-mono fi-section-header-description truncate max-w-xs" title="{{ $add['content'] }}">{{ Str::limit($add['content'], 40) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<x-filament::icon-button
|
<x-filament::icon-button
|
||||||
|
|||||||
@@ -37,13 +37,13 @@
|
|||||||
<div class="flex w-full items-center gap-3 sm:w-auto sm:justify-end">
|
<div class="flex w-full items-center gap-3 sm:w-auto sm:justify-end">
|
||||||
{{-- CPU --}}
|
{{-- CPU --}}
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="fi-section-header-description">CPU</div>
|
<div class="fi-section-header-description">{{ __('CPU') }}</div>
|
||||||
<x-filament::badge :color="$cpuColor">{{ $proc['cpu'] }}%</x-filament::badge>
|
<x-filament::badge :color="$cpuColor">{{ $proc['cpu'] }}%</x-filament::badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Memory --}}
|
{{-- Memory --}}
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="fi-section-header-description">MEM</div>
|
<div class="fi-section-header-description">{{ __('MEM') }}</div>
|
||||||
<x-filament::badge :color="$memColor">{{ $proc['memory'] }}%</x-filament::badge>
|
<x-filament::badge :color="$memColor">{{ $proc['memory'] }}%</x-filament::badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
<x-slot name="description">
|
<x-slot name="description">
|
||||||
{{ __('Deleting or modifying system files can break your website. Avoid editing files in the') }}
|
{{ __('Deleting or modifying system files can break your website. Avoid editing files in the') }}
|
||||||
<code class="px-1.5 py-0.5 rounded bg-warning-100 dark:bg-warning-900/50 fi-color-warning fi-text-color-700 dark:fi-text-color-300 font-mono fi-section-header-description">conf</code>
|
<code class="px-1.5 py-0.5 rounded bg-warning-100 dark:bg-warning-900/50 fi-color-warning fi-text-color-700 dark:fi-text-color-300 font-mono fi-section-header-description">{{ __('conf') }}</code>
|
||||||
{{ __('and') }}
|
{{ __('and') }}
|
||||||
<code class="px-1.5 py-0.5 rounded bg-warning-100 dark:bg-warning-900/50 fi-color-warning fi-text-color-700 dark:fi-text-color-300 font-mono fi-section-header-description">logs</code>
|
<code class="px-1.5 py-0.5 rounded bg-warning-100 dark:bg-warning-900/50 fi-color-warning fi-text-color-700 dark:fi-text-color-300 font-mono fi-section-header-description">{{ __('logs') }}</code>
|
||||||
{{ __('folders unless you know what you are doing.') }}
|
{{ __('folders unless you know what you are doing.') }}
|
||||||
</x-slot>
|
</x-slot>
|
||||||
</x-filament::section>
|
</x-filament::section>
|
||||||
|
|||||||
@@ -28,21 +28,21 @@
|
|||||||
href="{{ url('/dashboard') }}"
|
href="{{ url('/dashboard') }}"
|
||||||
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] border-[#19140035] hover:border-[#1915014a] border text-[#1b1b18] dark:border-[#3E3E3A] dark:hover:border-[#62605b] rounded-sm text-sm leading-normal"
|
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] border-[#19140035] hover:border-[#1915014a] border text-[#1b1b18] dark:border-[#3E3E3A] dark:hover:border-[#62605b] rounded-sm text-sm leading-normal"
|
||||||
>
|
>
|
||||||
Dashboard
|
{{ __('Dashboard') }}
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
<a
|
<a
|
||||||
href="{{ route('login') }}"
|
href="{{ route('login') }}"
|
||||||
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] text-[#1b1b18] border border-transparent hover:border-[#19140035] dark:hover:border-[#3E3E3A] rounded-sm text-sm leading-normal"
|
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] text-[#1b1b18] border border-transparent hover:border-[#19140035] dark:hover:border-[#3E3E3A] rounded-sm text-sm leading-normal"
|
||||||
>
|
>
|
||||||
Log in
|
{{ __('Log in') }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@if (Route::has('register'))
|
@if (Route::has('register'))
|
||||||
<a
|
<a
|
||||||
href="{{ route('register') }}"
|
href="{{ route('register') }}"
|
||||||
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] border-[#19140035] hover:border-[#1915014a] border text-[#1b1b18] dark:border-[#3E3E3A] dark:hover:border-[#62605b] rounded-sm text-sm leading-normal">
|
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] border-[#19140035] hover:border-[#1915014a] border text-[#1b1b18] dark:border-[#3E3E3A] dark:hover:border-[#62605b] rounded-sm text-sm leading-normal">
|
||||||
Register
|
{{ __('Register') }}
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
@endauth
|
@endauth
|
||||||
@@ -52,8 +52,8 @@
|
|||||||
<div class="flex items-center justify-center w-full transition-opacity opacity-100 duration-750 lg:grow starting:opacity-0">
|
<div class="flex items-center justify-center w-full transition-opacity opacity-100 duration-750 lg:grow starting:opacity-0">
|
||||||
<main class="flex max-w-[335px] w-full flex-col-reverse lg:max-w-4xl lg:flex-row">
|
<main class="flex max-w-[335px] w-full flex-col-reverse lg:max-w-4xl lg:flex-row">
|
||||||
<div class="text-[13px] leading-[20px] flex-1 p-6 pb-12 lg:p-20 bg-white dark:bg-[#161615] dark:text-[#EDEDEC] shadow-[inset_0px_0px_0px_1px_rgba(26,26,0,0.16)] dark:shadow-[inset_0px_0px_0px_1px_#fffaed2d] rounded-bl-lg rounded-br-lg lg:rounded-tl-lg lg:rounded-br-none">
|
<div class="text-[13px] leading-[20px] flex-1 p-6 pb-12 lg:p-20 bg-white dark:bg-[#161615] dark:text-[#EDEDEC] shadow-[inset_0px_0px_0px_1px_rgba(26,26,0,0.16)] dark:shadow-[inset_0px_0px_0px_1px_#fffaed2d] rounded-bl-lg rounded-br-lg lg:rounded-tl-lg lg:rounded-br-none">
|
||||||
<h1 class="mb-1 font-medium">Let's get started</h1>
|
<h1 class="mb-1 font-medium">{{ __('Let's get started') }}</h1>
|
||||||
<p class="mb-2 text-[#706f6c] dark:text-[#A1A09A]">Laravel has an incredibly rich ecosystem. <br>We suggest starting with the following.</p>
|
<p class="mb-2 text-[#706f6c] dark:text-[#A1A09A]">{{ __('Laravel has an incredibly rich ecosystem.') }} <br>{{ __('We suggest starting with the following.') }}</p>
|
||||||
<ul class="flex flex-col mb-4 lg:mb-6">
|
<ul class="flex flex-col mb-4 lg:mb-6">
|
||||||
<li class="flex items-center gap-4 py-2 relative before:border-l before:border-[#e3e3e0] dark:before:border-[#3E3E3A] before:top-1/2 before:bottom-0 before:left-[0.4rem] before:absolute">
|
<li class="flex items-center gap-4 py-2 relative before:border-l before:border-[#e3e3e0] dark:before:border-[#3E3E3A] before:top-1/2 before:bottom-0 before:left-[0.4rem] before:absolute">
|
||||||
<span class="relative py-1 bg-white dark:bg-[#161615]">
|
<span class="relative py-1 bg-white dark:bg-[#161615]">
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
Read the
|
{{ __('Read the') }}
|
||||||
<a href="https://laravel.com/docs" target="_blank" class="inline-flex items-center space-x-1 font-medium underline underline-offset-4 text-[#f53003] dark:text-[#FF4433] ml-1">
|
<a href="https://laravel.com/docs" target="_blank" class="inline-flex items-center space-x-1 font-medium underline underline-offset-4 text-[#f53003] dark:text-[#FF4433] ml-1">
|
||||||
<span>Documentation</span>
|
<span>{{ __('Documentation') }}</span>
|
||||||
<svg
|
<svg
|
||||||
width="10"
|
width="10"
|
||||||
height="11"
|
height="11"
|
||||||
@@ -89,9 +89,9 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
Watch video tutorials at
|
{{ __('Watch video tutorials at') }}
|
||||||
<a href="https://laracasts.com" target="_blank" class="inline-flex items-center space-x-1 font-medium underline underline-offset-4 text-[#f53003] dark:text-[#FF4433] ml-1">
|
<a href="https://laracasts.com" target="_blank" class="inline-flex items-center space-x-1 font-medium underline underline-offset-4 text-[#f53003] dark:text-[#FF4433] ml-1">
|
||||||
<span>Laracasts</span>
|
<span>{{ __('Laracasts') }}</span>
|
||||||
<svg
|
<svg
|
||||||
width="10"
|
width="10"
|
||||||
height="11"
|
height="11"
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
<ul class="flex gap-3 text-sm leading-normal">
|
<ul class="flex gap-3 text-sm leading-normal">
|
||||||
<li>
|
<li>
|
||||||
<a href="https://cloud.laravel.com" target="_blank" class="inline-block dark:bg-[#eeeeec] dark:border-[#eeeeec] dark:text-[#1C1C1A] dark:hover:bg-white dark:hover:border-white hover:bg-black hover:border-black px-5 py-1.5 bg-[#1b1b18] rounded-sm border border-black text-white text-sm leading-normal">
|
<a href="https://cloud.laravel.com" target="_blank" class="inline-block dark:bg-[#eeeeec] dark:border-[#eeeeec] dark:text-[#1C1C1A] dark:hover:bg-white dark:hover:border-white hover:bg-black hover:border-black px-5 py-1.5 bg-[#1b1b18] rounded-sm border border-black text-white text-sm leading-normal">
|
||||||
Deploy now
|
{{ __('Deploy now') }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
Reference in New Issue
Block a user