Initial commit: CABM-IT (ItGlue-style MSP documentation platform)
Laravel 13 + Livewire/Volt app for managing client organizations, documents, passwords, flexible assets, virtual machines, contacts, domains, todos, shortcuts, and role-based user administration.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
#[Fillable([
|
||||
'organization_id', 'location_id', 'first_name', 'last_name',
|
||||
'title', 'email', 'phone', 'mobile', 'is_important', 'notes',
|
||||
])]
|
||||
class Contact extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_important' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
protected function fullName(): Attribute
|
||||
{
|
||||
return Attribute::get(fn () => trim("{$this->first_name} {$this->last_name}"));
|
||||
}
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function location()
|
||||
{
|
||||
return $this->belongsTo(Location::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\Linkable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
|
||||
#[Fillable(['organization_id', 'folder_id', 'name', 'type', 'is_folder', 'content', 'file_path', 'office_extension', 'office_key', 'current_version', 'created_by'])]
|
||||
class Document extends Model implements Linkable
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_folder' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function isOffice(): bool
|
||||
{
|
||||
return $this->type === 'office';
|
||||
}
|
||||
|
||||
public function isMarkdown(): bool
|
||||
{
|
||||
return $this->type === 'markdown';
|
||||
}
|
||||
|
||||
public function isUpload(): bool
|
||||
{
|
||||
return $this->type === 'upload';
|
||||
}
|
||||
|
||||
public function renderedMarkdown(): string
|
||||
{
|
||||
return (new CommonMarkConverter())->convert($this->content ?? '')->getContent();
|
||||
}
|
||||
|
||||
public function isFolder(): bool
|
||||
{
|
||||
return $this->is_folder || $this->children()->exists();
|
||||
}
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function folder()
|
||||
{
|
||||
return $this->belongsTo(Document::class, 'folder_id');
|
||||
}
|
||||
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany(Document::class, 'folder_id');
|
||||
}
|
||||
|
||||
public function versions()
|
||||
{
|
||||
return $this->hasMany(DocumentVersion::class)->orderByDesc('version');
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function relatedItems()
|
||||
{
|
||||
return RelatedItem::forItem($this);
|
||||
}
|
||||
|
||||
public function linkableKind(): string
|
||||
{
|
||||
return 'Document';
|
||||
}
|
||||
|
||||
public function linkableLabel(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function linkableUrl(): string
|
||||
{
|
||||
return route('organizations.documents.edit', [$this->organization_id, $this]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DocumentVersion extends Model
|
||||
{
|
||||
const UPDATED_AT = null;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function document()
|
||||
{
|
||||
return $this->belongsTo(Document::class);
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
#[Fillable(['organization_id', 'name', 'registrar', 'nameservers', 'expires_at', 'auto_renew', 'last_checked_at', 'notes'])]
|
||||
class Domain extends Model
|
||||
{
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'expires_at' => 'date',
|
||||
'auto_renew' => 'boolean',
|
||||
'last_checked_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function sslCertificates()
|
||||
{
|
||||
return $this->hasMany(SslCertificate::class);
|
||||
}
|
||||
|
||||
public function tld(): ?string
|
||||
{
|
||||
$parts = explode('.', $this->name);
|
||||
|
||||
return count($parts) > 1 ? '.'.end($parts) : null;
|
||||
}
|
||||
|
||||
public function expiresInDays(): ?int
|
||||
{
|
||||
return $this->expires_at ? now()->startOfDay()->diffInDays($this->expires_at, false) : null;
|
||||
}
|
||||
|
||||
public function expiryStatus(): string
|
||||
{
|
||||
$days = $this->expiresInDays();
|
||||
|
||||
return match (true) {
|
||||
$days === null => 'unknown',
|
||||
$days < 0 => 'expired',
|
||||
$days <= 30 => 'soon',
|
||||
default => 'ok',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\Linkable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
#[Fillable(['flexible_asset_type_id', 'organization_id', 'name', 'data', 'is_archived', 'created_by'])]
|
||||
class FlexibleAsset extends Model implements Linkable
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'data' => 'array',
|
||||
'is_archived' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function type()
|
||||
{
|
||||
return $this->belongsTo(FlexibleAssetType::class, 'flexible_asset_type_id');
|
||||
}
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function relatedItems()
|
||||
{
|
||||
return RelatedItem::forItem($this);
|
||||
}
|
||||
|
||||
public function logs()
|
||||
{
|
||||
return $this->hasMany(FlexibleAssetLog::class)->orderByDesc('created_at');
|
||||
}
|
||||
|
||||
public function linkableKind(): string
|
||||
{
|
||||
return $this->type?->name ?? 'Flexible Asset';
|
||||
}
|
||||
|
||||
public function linkableLabel(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function linkableUrl(): string
|
||||
{
|
||||
return route('organizations.assets.edit', [$this->organization_id, $this->flexible_asset_type_id, $this]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
#[Fillable([
|
||||
'flexible_asset_type_id', 'name', 'label', 'field_type',
|
||||
'options', 'is_required', 'show_in_list', 'sort_order',
|
||||
])]
|
||||
class FlexibleAssetField extends Model
|
||||
{
|
||||
const FIELD_TYPES = ['text', 'textarea', 'password', 'checkbox', 'date', 'tag', 'select', 'related_asset'];
|
||||
|
||||
/**
|
||||
* Built-in record lists a "related_asset" field can point to.
|
||||
* Keys are stored in options.target; values map to an Organization relation
|
||||
* name and the attribute used as the display label.
|
||||
*/
|
||||
const RELATED_TARGETS = [
|
||||
'contact' => ['relation' => 'contacts', 'label' => 'full_name'],
|
||||
'location' => ['relation' => 'locations', 'label' => 'name'],
|
||||
'domain' => ['relation' => 'domains', 'label' => 'name'],
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'options' => 'array',
|
||||
'is_required' => 'boolean',
|
||||
'show_in_list' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function type()
|
||||
{
|
||||
return $this->belongsTo(FlexibleAssetType::class, 'flexible_asset_type_id');
|
||||
}
|
||||
|
||||
public function relatedTarget(): ?string
|
||||
{
|
||||
return $this->options['target'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records available for this field's related_asset target, scoped to the organization.
|
||||
* @return Collection<int, array{id: int, label: string}>
|
||||
*/
|
||||
public function relatedOptions(Organization $organization): Collection
|
||||
{
|
||||
$target = $this->relatedTarget();
|
||||
|
||||
if (! $target) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
if (str_starts_with($target, 'flexible_asset:')) {
|
||||
$typeId = (int) substr($target, strlen('flexible_asset:'));
|
||||
|
||||
return $organization->flexibleAssets()
|
||||
->where('flexible_asset_type_id', $typeId)
|
||||
->orderBy('name')
|
||||
->get()
|
||||
->map(fn (FlexibleAsset $asset) => ['id' => $asset->id, 'label' => $asset->name]);
|
||||
}
|
||||
|
||||
$config = self::RELATED_TARGETS[$target] ?? null;
|
||||
|
||||
if (! $config) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
return $organization->{$config['relation']}()
|
||||
->orderBy($config['label'] === 'full_name' ? 'last_name' : $config['label'])
|
||||
->get()
|
||||
->map(fn (Model $record) => ['id' => $record->id, 'label' => (string) $record->{$config['label']}]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class FlexibleAssetLog extends Model
|
||||
{
|
||||
const UPDATED_AT = null;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function flexibleAsset()
|
||||
{
|
||||
return $this->belongsTo(FlexibleAsset::class);
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
#[Fillable(['name', 'icon', 'description', 'enabled_for_all_organizations'])]
|
||||
class FlexibleAssetType extends Model
|
||||
{
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'enabled_for_all_organizations' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function fields()
|
||||
{
|
||||
return $this->hasMany(FlexibleAssetField::class)->orderBy('sort_order');
|
||||
}
|
||||
|
||||
public function assets()
|
||||
{
|
||||
return $this->hasMany(FlexibleAsset::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
#[Fillable([
|
||||
'organization_id', 'name', 'address_line_1', 'address_line_2',
|
||||
'city', 'region', 'postal_code', 'country', 'phone', 'is_primary',
|
||||
])]
|
||||
class Location extends Model
|
||||
{
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_primary' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function contacts()
|
||||
{
|
||||
return $this->hasMany(Contact::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
#[Fillable(['name', 'slug', 'org_type', 'status', 'description', 'allowed_ip_ranges', 'logo_path'])]
|
||||
class Organization extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'allowed_ip_ranges' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
protected function logoUrl(): Attribute
|
||||
{
|
||||
return Attribute::get(
|
||||
fn () => $this->logo_path ? Storage::disk('public')->url($this->logo_path) : asset('images/default-organization-logo.svg')
|
||||
);
|
||||
}
|
||||
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany(User::class);
|
||||
}
|
||||
|
||||
public function locations()
|
||||
{
|
||||
return $this->hasMany(Location::class);
|
||||
}
|
||||
|
||||
public function contacts()
|
||||
{
|
||||
return $this->hasMany(Contact::class);
|
||||
}
|
||||
|
||||
public function passwords()
|
||||
{
|
||||
return $this->hasMany(Password::class);
|
||||
}
|
||||
|
||||
public function flexibleAssets()
|
||||
{
|
||||
return $this->hasMany(FlexibleAsset::class);
|
||||
}
|
||||
|
||||
public function documents()
|
||||
{
|
||||
return $this->hasMany(Document::class);
|
||||
}
|
||||
|
||||
public function domains()
|
||||
{
|
||||
return $this->hasMany(Domain::class);
|
||||
}
|
||||
|
||||
public function sslCertificates()
|
||||
{
|
||||
return $this->hasMany(SslCertificate::class);
|
||||
}
|
||||
|
||||
public function virtualMachines()
|
||||
{
|
||||
return $this->hasMany(VirtualMachine::class);
|
||||
}
|
||||
|
||||
public function logEntries()
|
||||
{
|
||||
return $this->hasMany(OrganizationLogEntry::class)->orderByDesc('created_at');
|
||||
}
|
||||
|
||||
public function todos()
|
||||
{
|
||||
return $this->hasMany(Todo::class);
|
||||
}
|
||||
|
||||
public function shortcuts()
|
||||
{
|
||||
return $this->hasMany(Shortcut::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class OrganizationLogEntry extends Model
|
||||
{
|
||||
const UPDATED_AT = null;
|
||||
|
||||
const CATEGORIES = [
|
||||
'project' => 'Projet en cours',
|
||||
'attention' => "Point d'attention",
|
||||
'intervention' => 'Intervention prévue',
|
||||
'info' => 'Information générale',
|
||||
];
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function categoryLabel(): string
|
||||
{
|
||||
return self::CATEGORIES[$this->category] ?? $this->category;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\EncryptedField;
|
||||
use App\Contracts\Linkable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
#[Fillable([
|
||||
'organization_id', 'resource_type', 'resource_id', 'name', 'username',
|
||||
'password_ciphertext', 'url', 'notes', 'otp_secret', 'is_favorite',
|
||||
'password_updated_at', 'rotation_due_at', 'created_by',
|
||||
])]
|
||||
class Password extends Model implements Linkable
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'password_ciphertext' => EncryptedField::class,
|
||||
'otp_secret' => EncryptedField::class,
|
||||
'is_favorite' => 'boolean',
|
||||
'password_updated_at' => 'datetime',
|
||||
'rotation_due_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function resource()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function auditLogs()
|
||||
{
|
||||
return $this->hasMany(PasswordAuditLog::class);
|
||||
}
|
||||
|
||||
public function logAction(string $action): void
|
||||
{
|
||||
$this->auditLogs()->create([
|
||||
'user_id' => auth()->id(),
|
||||
'action' => $action,
|
||||
'ip_address' => request()->ip(),
|
||||
'user_agent' => (string) request()->userAgent(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function relatedItems()
|
||||
{
|
||||
return RelatedItem::forItem($this);
|
||||
}
|
||||
|
||||
public function linkableKind(): string
|
||||
{
|
||||
return 'Password';
|
||||
}
|
||||
|
||||
public function linkableLabel(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function linkableUrl(): string
|
||||
{
|
||||
return route('organizations.passwords.edit', [$this->organization_id, $this]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PasswordAuditLog extends Model
|
||||
{
|
||||
const UPDATED_AT = null;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function password()
|
||||
{
|
||||
return $this->belongsTo(Password::class);
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
class RelatedItem extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
public function item(): MorphTo
|
||||
{
|
||||
return $this->morphTo(__FUNCTION__, 'item_type', 'item_id');
|
||||
}
|
||||
|
||||
public function relatedItem(): MorphTo
|
||||
{
|
||||
return $this->morphTo(__FUNCTION__, 'related_item_type', 'related_item_id');
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* All edges touching a given model, either as owner or as target (symmetric view).
|
||||
*/
|
||||
public static function forItem(Model $model)
|
||||
{
|
||||
return static::where(function ($query) use ($model) {
|
||||
$query->where('item_type', $model->getMorphClass())->where('item_id', $model->getKey());
|
||||
})->orWhere(function ($query) use ($model) {
|
||||
$query->where('related_item_type', $model->getMorphClass())->where('related_item_id', $model->getKey());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The model on the "other side" of this edge, relative to the given model.
|
||||
*/
|
||||
public function otherItem(Model $relativeTo): ?Model
|
||||
{
|
||||
$isOwner = $this->item_type === $relativeTo->getMorphClass() && (string) $this->item_id === (string) $relativeTo->getKey();
|
||||
|
||||
return $isOwner ? $this->relatedItem : $this->item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
#[Fillable(['organization_id', 'label', 'url', 'icon', 'created_by'])]
|
||||
class Shortcut extends Model
|
||||
{
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function isGlobal(): bool
|
||||
{
|
||||
return $this->organization_id === null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
#[Fillable(['organization_id', 'domain_id', 'name', 'issuer', 'issued_at', 'expires_at', 'last_checked_at'])]
|
||||
class SslCertificate extends Model
|
||||
{
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'issued_at' => 'date',
|
||||
'expires_at' => 'date',
|
||||
'last_checked_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function domain()
|
||||
{
|
||||
return $this->belongsTo(Domain::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\Linkable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
#[Fillable([
|
||||
'organization_id', 'title', 'description', 'category', 'criticality',
|
||||
'due_date', 'assigned_to', 'is_done', 'completed_at', 'created_by',
|
||||
])]
|
||||
class Todo extends Model implements Linkable
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
const CATEGORIES = [
|
||||
'infra' => 'Infra',
|
||||
'support' => 'Support',
|
||||
'metier' => 'Métier',
|
||||
'autre' => 'Autre',
|
||||
];
|
||||
|
||||
const CRITICALITIES = [
|
||||
'low' => 'Basse',
|
||||
'medium' => 'Moyenne',
|
||||
'high' => 'Haute',
|
||||
'urgent' => 'Urgente',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'due_date' => 'date',
|
||||
'is_done' => 'boolean',
|
||||
'completed_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function assignee()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'assigned_to');
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function categoryLabel(): string
|
||||
{
|
||||
return self::CATEGORIES[$this->category] ?? $this->category;
|
||||
}
|
||||
|
||||
public function criticalityLabel(): string
|
||||
{
|
||||
return self::CRITICALITIES[$this->criticality] ?? $this->criticality;
|
||||
}
|
||||
|
||||
public function isOverdue(): bool
|
||||
{
|
||||
return ! $this->is_done && $this->due_date && $this->due_date->isPast();
|
||||
}
|
||||
|
||||
public function linkableKind(): string
|
||||
{
|
||||
return 'Todo';
|
||||
}
|
||||
|
||||
public function linkableLabel(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function linkableUrl(): string
|
||||
{
|
||||
return route('organizations.todos.edit', [$this->organization_id, $this]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
#[Fillable(['name', 'email', 'password', 'is_msp_staff'])]
|
||||
#[Hidden(['password', 'remember_token', 'two_factor_secret', 'two_factor_recovery_codes'])]
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory, Notifiable, HasApiTokens, HasRoles, TwoFactorAuthenticatable;
|
||||
|
||||
public function organizations()
|
||||
{
|
||||
return $this->belongsToMany(Organization::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'is_msp_staff' => 'boolean',
|
||||
'last_login_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\Linkable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
#[Fillable([
|
||||
'organization_id', 'name', 'role', 'memory_gb', 'cpu_count',
|
||||
'operating_system', 'notes', 'created_by',
|
||||
])]
|
||||
class VirtualMachine extends Model implements Linkable
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
public function organization()
|
||||
{
|
||||
return $this->belongsTo(Organization::class);
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function logs()
|
||||
{
|
||||
return $this->hasMany(VirtualMachineLog::class)->orderByDesc('created_at');
|
||||
}
|
||||
|
||||
public function linkableKind(): string
|
||||
{
|
||||
return 'VirtualMachine';
|
||||
}
|
||||
|
||||
public function linkableLabel(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function linkableUrl(): string
|
||||
{
|
||||
return route('organizations.virtual-machines.edit', [$this->organization_id, $this]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class VirtualMachineLog extends Model
|
||||
{
|
||||
const UPDATED_AT = null;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function virtualMachine()
|
||||
{
|
||||
return $this->belongsTo(VirtualMachine::class);
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user