feat: initialize Infra Manager with React, Tailwind CSS, and Inertia.js
- Added main application entry point (app.jsx) and bootstrap file for React setup. - Created Wiki index page (Index.jsx) for documentation and file management. - Implemented utility functions for formatting and status handling in utils.js. - Set up Blade template (app.blade.php) for Inertia.js integration. - Defined API routes for metrics and terminal access in api.php. - Established console commands for scheduled tasks in console.php. - Created web routes for various application features including authentication, hypervisors, containers, cloud accounts, security, backups, and wiki management in web.php. - Developed setup scripts for Windows (setup.ps1) and Linux (setup.sh) environments. - Configured Tailwind CSS for styling with custom themes and animations in tailwind.config.js. - Set up Vite for asset bundling and development server in vite.config.js.
This commit is contained in:
22
app/Models/Alert.php
Normal file
22
app/Models/Alert.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Alert extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'rule_id', 'target_type', 'target_id', 'target_name',
|
||||
'title', 'message', 'severity', 'status',
|
||||
'acknowledged_by', 'acknowledged_at', 'resolved_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'acknowledged_at' => 'datetime',
|
||||
'resolved_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function rule() { return $this->belongsTo(AlertRule::class); }
|
||||
public function acknowledgedBy() { return $this->belongsTo(User::class, 'acknowledged_by'); }
|
||||
}
|
||||
14
app/Models/AlertRule.php
Normal file
14
app/Models/AlertRule.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AlertRule extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name', 'target_type', 'metric_name', 'condition', 'threshold',
|
||||
'duration_seconds', 'severity', 'notification_channels', 'is_active', 'created_by',
|
||||
];
|
||||
protected $casts = ['notification_channels' => 'array', 'is_active' => 'boolean'];
|
||||
}
|
||||
16
app/Models/Backup.php
Normal file
16
app/Models/Backup.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Backup extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'policy_id', 'target_type', 'target_id', 'target_name',
|
||||
'status', 'storage_path', 'size_bytes', 'checksum', 'metadata', 'notes',
|
||||
'expires_at', 'created_by', 'started_at', 'completed_at',
|
||||
];
|
||||
protected $casts = ['metadata' => 'array', 'expires_at' => 'datetime', 'started_at' => 'datetime', 'completed_at' => 'datetime'];
|
||||
public function policy() { return $this->belongsTo(BackupPolicy::class); }
|
||||
}
|
||||
15
app/Models/BackupPolicy.php
Normal file
15
app/Models/BackupPolicy.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class BackupPolicy extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name', 'target_type', 'target_ids', 'schedule', 'retention_type', 'retention_value',
|
||||
'storage_type', 'storage_config', 'is_active', 'compression', 'encryption', 'created_by',
|
||||
];
|
||||
protected $casts = ['target_ids' => 'array', 'storage_config' => 'array', 'compression' => 'array', 'encryption' => 'array', 'is_active' => 'boolean'];
|
||||
public function backups() { return $this->hasMany(Backup::class, 'policy_id'); }
|
||||
}
|
||||
34
app/Models/Certificate.php
Normal file
34
app/Models/Certificate.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class Certificate extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'domain', 'san', 'certificate', 'private_key', 'chain',
|
||||
'issuer', 'type', 'auto_renew', 'issued_at', 'expires_at', 'tags', 'status', 'created_by',
|
||||
];
|
||||
|
||||
protected $hidden = ['certificate', 'private_key', 'chain'];
|
||||
|
||||
protected $casts = [
|
||||
'san' => 'array',
|
||||
'tags' => 'array',
|
||||
'auto_renew' => 'boolean',
|
||||
'issued_at' => 'datetime',
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function setPrivateKeyAttribute(string $value): void
|
||||
{
|
||||
$this->attributes['private_key'] = Crypt::encryptString($value);
|
||||
}
|
||||
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
}
|
||||
40
app/Models/CloudAccount.php
Normal file
40
app/Models/CloudAccount.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class CloudAccount extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'provider', 'account_id', 'credentials', 'region',
|
||||
'regions', 'is_active', 'status', 'tags', 'last_sync_at', 'created_by',
|
||||
];
|
||||
|
||||
protected $hidden = ['credentials'];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'regions' => 'array',
|
||||
'tags' => 'array',
|
||||
'last_sync_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function setCredentialsAttribute(array $value): void
|
||||
{
|
||||
$this->attributes['credentials'] = Crypt::encryptString(json_encode($value));
|
||||
}
|
||||
|
||||
public function getCredentialsAttribute(string $value): array
|
||||
{
|
||||
return json_decode(Crypt::decryptString($value), true) ?? [];
|
||||
}
|
||||
|
||||
public function resources() { return $this->hasMany(CloudResource::class, 'account_id'); }
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
}
|
||||
15
app/Models/CloudResource.php
Normal file
15
app/Models/CloudResource.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CloudResource extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'account_id', 'resource_id', 'resource_type', 'name',
|
||||
'status', 'region', 'properties', 'tags', 'monthly_cost',
|
||||
];
|
||||
protected $casts = ['properties' => 'array', 'tags' => 'array'];
|
||||
public function account() { return $this->belongsTo(CloudAccount::class); }
|
||||
}
|
||||
23
app/Models/Container.php
Normal file
23
app/Models/Container.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Container extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'endpoint_id', 'container_id', 'name', 'image', 'image_id',
|
||||
'status', 'state', 'ports', 'volumes', 'env', 'labels', 'networks', 'created_at_unix',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'ports' => 'array',
|
||||
'volumes' => 'array',
|
||||
'env' => 'array',
|
||||
'labels' => 'array',
|
||||
'networks' => 'array',
|
||||
];
|
||||
|
||||
public function endpoint() { return $this->belongsTo(DockerEndpoint::class); }
|
||||
}
|
||||
29
app/Models/DockerEndpoint.php
Normal file
29
app/Models/DockerEndpoint.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class DockerEndpoint extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'type', 'host', 'tls_enabled', 'tls_cert', 'tls_key', 'tls_ca',
|
||||
'status', 'info', 'last_sync_at', 'created_by',
|
||||
];
|
||||
|
||||
protected $hidden = ['tls_cert', 'tls_key', 'tls_ca'];
|
||||
|
||||
protected $casts = [
|
||||
'tls_enabled' => 'boolean',
|
||||
'info' => 'array',
|
||||
'last_sync_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function containers() { return $this->hasMany(Container::class, 'endpoint_id'); }
|
||||
public function stacks() { return $this->hasMany(Stack::class, 'endpoint_id'); }
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
}
|
||||
70
app/Models/Hypervisor.php
Normal file
70
app/Models/Hypervisor.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class Hypervisor extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'type', 'version', 'host', 'port',
|
||||
'username', 'password', 'ssl_verify', 'datacenter',
|
||||
'status', 'metadata', 'last_sync_at', 'last_error', 'created_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'ssl_verify' => 'boolean',
|
||||
'metadata' => 'array',
|
||||
'last_sync_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = ['password'];
|
||||
|
||||
public function setPasswordAttribute(string $value): void
|
||||
{
|
||||
$this->attributes['password'] = Crypt::encryptString($value);
|
||||
}
|
||||
|
||||
public function getPasswordAttribute(string $value): string
|
||||
{
|
||||
return Crypt::decryptString($value);
|
||||
}
|
||||
|
||||
public function virtualMachines()
|
||||
{
|
||||
return $this->hasMany(VirtualMachine::class);
|
||||
}
|
||||
|
||||
public function templates()
|
||||
{
|
||||
return $this->hasMany(VmTemplate::class);
|
||||
}
|
||||
|
||||
public function createdBy()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function isOnline(): bool
|
||||
{
|
||||
return $this->status === 'online';
|
||||
}
|
||||
|
||||
public function getTypeLabel(): string
|
||||
{
|
||||
return match($this->type) {
|
||||
'vcenter' => 'VMware vCenter',
|
||||
'esxi' => 'VMware ESXi',
|
||||
'proxmox' => 'Proxmox VE',
|
||||
'hyper-v' => 'Microsoft Hyper-V',
|
||||
'xen' => 'Citrix XenServer',
|
||||
'kvm' => 'KVM/QEMU',
|
||||
default => ucfirst($this->type),
|
||||
};
|
||||
}
|
||||
}
|
||||
35
app/Models/LdapConfig.php
Normal file
35
app/Models/LdapConfig.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class LdapConfig extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name', 'host', 'port', 'use_ssl', 'use_tls', 'base_dn',
|
||||
'bind_dn', 'bind_password', 'user_search_base', 'user_filter',
|
||||
'group_search_base', 'group_filter', 'admin_group_dn',
|
||||
'attribute_map', 'is_active', 'status',
|
||||
];
|
||||
|
||||
protected $hidden = ['bind_password'];
|
||||
|
||||
protected $casts = [
|
||||
'use_ssl' => 'boolean',
|
||||
'use_tls' => 'boolean',
|
||||
'is_active' => 'boolean',
|
||||
'attribute_map' => 'array',
|
||||
];
|
||||
|
||||
public function setBindPasswordAttribute(?string $value): void
|
||||
{
|
||||
$this->attributes['bind_password'] = $value ? Crypt::encryptString($value) : null;
|
||||
}
|
||||
|
||||
public function getBindPasswordAttribute(?string $value): ?string
|
||||
{
|
||||
return $value ? Crypt::decryptString($value) : null;
|
||||
}
|
||||
}
|
||||
40
app/Models/PasswordEntry.php
Normal file
40
app/Models/PasswordEntry.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class PasswordEntry extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id', 'title', 'category', 'url', 'username',
|
||||
'password', 'notes', 'custom_fields', 'tags',
|
||||
'is_shared', 'shared_with', 'expires_at', 'last_used_at',
|
||||
];
|
||||
|
||||
protected $hidden = ['password', 'notes', 'custom_fields'];
|
||||
|
||||
protected $casts = [
|
||||
'tags' => 'array',
|
||||
'shared_with' => 'array',
|
||||
'is_shared' => 'boolean',
|
||||
'expires_at' => 'datetime',
|
||||
'last_used_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function setPasswordAttribute(string $value): void
|
||||
{
|
||||
$this->attributes['password'] = Crypt::encryptString($value);
|
||||
}
|
||||
|
||||
public function getDecryptedPassword(): string
|
||||
{
|
||||
return Crypt::decryptString($this->attributes['password']);
|
||||
}
|
||||
|
||||
public function user() { return $this->belongsTo(User::class); }
|
||||
}
|
||||
19
app/Models/SecurityScan.php
Normal file
19
app/Models/SecurityScan.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SecurityScan extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'target_type', 'target_id', 'target_name', 'scan_type', 'status',
|
||||
'score', 'grade', 'critical_count', 'high_count', 'medium_count', 'low_count', 'info_count',
|
||||
'findings', 'raw_output', 'initiated_by', 'started_at', 'completed_at',
|
||||
];
|
||||
protected $casts = [
|
||||
'findings' => 'array',
|
||||
'started_at' => 'datetime',
|
||||
'completed_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
11
app/Models/SecurityScore.php
Normal file
11
app/Models/SecurityScore.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SecurityScore extends Model
|
||||
{
|
||||
protected $fillable = ['target_type', 'target_id', 'score', 'grade', 'breakdown', 'calculated_at'];
|
||||
protected $casts = ['breakdown' => 'array', 'calculated_at' => 'datetime'];
|
||||
}
|
||||
57
app/Models/Setting.php
Normal file
57
app/Models/Setting.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class Setting extends Model
|
||||
{
|
||||
protected $fillable = ['group', 'key', 'value', 'type', 'is_encrypted'];
|
||||
|
||||
protected $casts = [
|
||||
'is_encrypted' => 'boolean',
|
||||
];
|
||||
|
||||
public function getValueAttribute($value)
|
||||
{
|
||||
if ($this->is_encrypted && $value) {
|
||||
$value = Crypt::decryptString($value);
|
||||
}
|
||||
|
||||
return match ($this->type) {
|
||||
'boolean' => (bool) $value,
|
||||
'integer' => (int) $value,
|
||||
'json' => json_decode($value, true),
|
||||
default => $value,
|
||||
};
|
||||
}
|
||||
|
||||
public function setValueAttribute($value): void
|
||||
{
|
||||
if ($this->type === 'json') {
|
||||
$value = json_encode($value);
|
||||
}
|
||||
|
||||
if ($this->is_encrypted) {
|
||||
$value = Crypt::encryptString((string) $value);
|
||||
}
|
||||
|
||||
$this->attributes['value'] = $value;
|
||||
}
|
||||
|
||||
public static function get(string $key, $default = null)
|
||||
{
|
||||
$setting = static::where('key', $key)->first();
|
||||
return $setting ? $setting->value : $default;
|
||||
}
|
||||
|
||||
public static function set(string $key, $value, string $group = 'general', string $type = 'string'): void
|
||||
{
|
||||
static::updateOrCreate(['key' => $key], [
|
||||
'group' => $group,
|
||||
'value' => $value,
|
||||
'type' => $type,
|
||||
]);
|
||||
}
|
||||
}
|
||||
15
app/Models/Stack.php
Normal file
15
app/Models/Stack.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Stack extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
protected $fillable = ['endpoint_id', 'name', 'type', 'compose_content', 'env_vars', 'status', 'remote_id', 'created_by'];
|
||||
protected $casts = ['env_vars' => 'array'];
|
||||
public function endpoint() { return $this->belongsTo(DockerEndpoint::class); }
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
}
|
||||
58
app/Models/User.php
Normal file
58
app/Models/User.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Spatie\Activitylog\Traits\CausesActivity;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasApiTokens, HasFactory, HasRoles, Notifiable, SoftDeletes, CausesActivity;
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'email', 'password', 'avatar', 'auth_source',
|
||||
'ldap_guid', 'ldap_domain', 'is_active', 'mfa_enabled',
|
||||
'mfa_secret', 'mfa_recovery_codes', 'last_login_at',
|
||||
'last_login_ip', 'preferences',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password', 'remember_token', 'mfa_secret', 'mfa_recovery_codes',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'last_login_at' => 'datetime',
|
||||
'is_active' => 'boolean',
|
||||
'mfa_enabled' => 'boolean',
|
||||
'mfa_recovery_codes' => 'encrypted:array',
|
||||
'mfa_secret' => 'encrypted',
|
||||
'preferences' => 'array',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
|
||||
public function getAvatarUrlAttribute(): string
|
||||
{
|
||||
if ($this->avatar) {
|
||||
return asset('storage/' . $this->avatar);
|
||||
}
|
||||
$hash = md5(strtolower(trim($this->email)));
|
||||
return "https://www.gravatar.com/avatar/{$hash}?d=identicon&s=128";
|
||||
}
|
||||
|
||||
public function isLdapUser(): bool
|
||||
{
|
||||
return $this->auth_source === 'ldap';
|
||||
}
|
||||
|
||||
public function passwordEntries()
|
||||
{
|
||||
return $this->hasMany(PasswordEntry::class);
|
||||
}
|
||||
}
|
||||
23
app/Models/VirtualMachine.php
Normal file
23
app/Models/VirtualMachine.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class VirtualMachine extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'hypervisor_id', 'remote_id', 'name', 'status', 'os_type', 'os_version',
|
||||
'cpu_count', 'memory_mb', 'disk_gb', 'ip_address', 'mac_address',
|
||||
'host_node', 'cluster', 'datastore', 'tags', 'metadata',
|
||||
'cpu_usage', 'memory_usage', 'created_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'tags' => 'array',
|
||||
'metadata' => 'array',
|
||||
];
|
||||
|
||||
public function hypervisor() { return $this->belongsTo(Hypervisor::class); }
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
}
|
||||
19
app/Models/VmTemplate.php
Normal file
19
app/Models/VmTemplate.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class VmTemplate extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
protected $fillable = [
|
||||
'hypervisor_id', 'name', 'description', 'os_type', 'os_version',
|
||||
'cpu_count', 'memory_mb', 'disk_gb', 'network_config', 'customization',
|
||||
'variables', 'provision_script', 'status', 'remote_id', 'tags', 'created_by',
|
||||
];
|
||||
protected $casts = ['network_config' => 'array', 'customization' => 'array', 'variables' => 'array', 'tags' => 'array'];
|
||||
public function hypervisor() { return $this->belongsTo(Hypervisor::class); }
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
}
|
||||
25
app/Models/WikiPage.php
Normal file
25
app/Models/WikiPage.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class WikiPage extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'space_id', 'parent_id', 'title', 'slug', 'content', 'content_html',
|
||||
'tags', 'is_published', 'order', 'created_by', 'updated_by',
|
||||
];
|
||||
|
||||
protected $casts = ['tags' => 'array', 'is_published' => 'boolean'];
|
||||
|
||||
public function space() { return $this->belongsTo(WikiSpace::class); }
|
||||
public function parent() { return $this->belongsTo(WikiPage::class, 'parent_id'); }
|
||||
public function children() { return $this->hasMany(WikiPage::class, 'parent_id'); }
|
||||
public function revisions() { return $this->hasMany(WikiPageRevision::class, 'page_id'); }
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
public function updatedBy() { return $this->belongsTo(User::class, 'updated_by'); }
|
||||
}
|
||||
12
app/Models/WikiPageRevision.php
Normal file
12
app/Models/WikiPageRevision.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class WikiPageRevision extends Model
|
||||
{
|
||||
protected $fillable = ['page_id', 'content', 'change_summary', 'created_by'];
|
||||
public function page() { return $this->belongsTo(WikiPage::class); }
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
}
|
||||
18
app/Models/WikiSpace.php
Normal file
18
app/Models/WikiSpace.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class WikiSpace extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = ['name', 'slug', 'description', 'icon', 'color', 'is_public', 'created_by'];
|
||||
protected $casts = ['is_public' => 'boolean'];
|
||||
|
||||
public function pages() { return $this->hasMany(WikiPage::class, 'space_id'); }
|
||||
public function createdBy() { return $this->belongsTo(User::class, 'created_by'); }
|
||||
}
|
||||
Reference in New Issue
Block a user