clrghouz/app/Models/System.php

296 lines
7.1 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use App\Classes\FTN\Packet;
use App\Jobs\AddressPoll;
class System extends Model
{
use HasFactory;
public const default = 'Discovered System';
protected $casts = [
'last_session' => 'datetime:Y-m-d H:i:s'
];
/* STATIC */
public static function createUnknownSystem(): self
{
self::unguard();
$so = self::firstOrCreate([
'name' => self::default,
'sysop' => 'Unknown',
'location' => 'Unknown',
'active' => TRUE,
]);
self::reguard();
return $so;
}
/* SCOPES */
/**
* Only query active records
* @todo test false action
*/
public function scopeActive($query)
{
$uo = Auth::user();
return $query
->when($uo && ! $uo->isAdmin(),function($query) use ($uo) {
return $query->whereIn('id',$uo->systems->pluck('id'))
->orWhere($this->getTable().'.active',TRUE);
},function($query) { $query->where($this->getTable().'.active',TRUE); })
->orderBy('name');
}
/* RELATIONS */
public function addresses()
{
return $this->hasMany(Address::class)
->withTrashed()
->FTNorder();
}
public function akas()
{
return $this->hasMany(Address::class)
->select('addresses.*')
->join('zones',['zones.id'=>'addresses.zone_id'])
->join('domains',['domains.id'=>'zones.domain_id'])
->where('addresses.active',TRUE)
->where('zones.active',TRUE)
->where('domains.active',TRUE)
->orderBy('domains.name')
->with(['zone.domain'])
->FTNorder()
->orderBy('role','ASC');
}
public function mailers()
{
return $this->belongsToMany(Mailer::class)
->withPivot(['last_poll','port','attempts','active']);
}
public function mailer_preferred()
{
return $this->mailers()
->preferred();
}
public function logs()
{
return $this->hasMany(SystemLog::class);
}
public function logs_recent()
{
return $this->hasMany(SystemLog::class)
->orderby('created_at','DESC')
->limit(10);
}
/**
* Session Passwords for system
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function sessions()
{
return $this->belongsToMany(Zone::class)
->withPivot(['sespass','pktpass','ticpass','fixpass','zt_ipv4','zt_ipv6','default'])
->dontCache();
}
/**
* If this system is configured as this host
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function setup()
{
return $this->hasOne(Setup::class);
}
public function users()
{
return $this->belongsToMany(User::class);
}
/**
* This system is the ZC for the following zones
*/
public function zcs()
{
return $this->hasMany(Zone::class);
}
/**
* Zones a system has addresses for
*
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function zones()
{
return $this->hasManyThrough(Zone::class,Address::class,'system_id','id','id','zone_id');
}
/* ATTRIBUTES */
public function getAccessMethodAttribute(): string
{
switch ($this->method) {
case 23: return sprintf('telnet://%s:%s',$this->address,$this->port);
case 22: return sprintf('ssh://%s:%s',$this->address,$this->port);
case 513: return sprintf('rlogin://%s:%s',$this->address,$this->port);
default:
return $this->method ? sprintf('%s:%s',$this->address,$this->port) : 'No access method available.';
}
}
public function getAccessMailerAttribute(): string
{
switch (($x=$this->mailer_preferred()->first())?->name) {
case 'BINKP': return sprintf('binkp://%s:%s',$this->address,$x->pivot->port);
case 'EMSI': return sprintf('emsi://%s:%s',$this->address,$x->pivot->port);
default:
return $x?->name ? sprintf('%s:%s',$this->address,$x->pivot->port) : 'No mailer available.';
}
}
public function getPktMsgsAttribute(?int $val): int
{
return $val ?: Setup::findOrFail(config('app.id'))->msgs_pkt;
}
/* METHODS */
public function echoareas()
{
return Echoarea::select('echoareas.*')
->join('address_echoarea',['address_echoarea.echoarea_id'=>'echoareas.id'])
->join('addresses',['addresses.id'=>'address_echoarea.address_id'])
->where('addresses.system_id',$this->id);
}
public function fileareas()
{
return Filearea::select('fileareas.*')
->join('address_filearea',['address_filearea.filearea_id'=>'fileareas.id'])
->join('addresses',['addresses.id'=>'address_filearea.address_id'])
->where('addresses.system_id',$this->id);
}
/**
* Return the system name, or role name for the zone
*
* @param Address $o
* @return string
*/
public function full_name(Address $o): string
{
switch ($o->role_id) {
case Address::NODE_ZC;
return sprintf('ZC-%s-%05d',$o->zone->domain->name,$o->zone->zone_id);
case Address::NODE_RC;
return sprintf('RC-%s-%05d',$o->zone->domain->name,$o->region_id);
case Address::NODE_NC;
return sprintf('NC-%s-%05d',$o->zone->domain->name,$o->host_id);
case Address::NODE_HC;
case Address::NODE_NN;
default:
return $this->name;
}
}
/**
* Return the system's address in the same zone
* This function can filter based on the address type needed.
*
* @param Zone $o
* @param int $type
* @return Collection
*/
public function match(Zone $o,int $type=(Address::NODE_NC|Address::NODE_HC|Address::NODE_NN|Address::NODE_POINT)): Collection
{
$akas = $this->akas
->where(function($item) use($o) {
return ($item->zone_id === $o->id) || ($item->zone->domain_id === $o->domain_id);
});
return ($akas->count() > 1)
? $akas->filter(function($item) use ($type) { return $item->role_id & $type;})
: $akas;
}
/**
* Parse the addresses and return which ones are in my zones
*
* @param \Illuminate\Database\Eloquent\Collection $addresses
* @param int $type
* @return Collection
*/
public function inMyZones(Collection $addresses,int $type=(Address::NODE_HC|Address::NODE_NN|Address::NODE_POINT)): Collection
{
$myzones = $this->addresses->pluck('zone_id')->unique();
return $addresses->filter(function($item) use ($myzones,$type) {
return ($item->role & $type) && ($myzones->search($item->zone_id) !== FALSE);
});
}
/**
* Return the packet that this system uses
*
* @param Address $ao
* @param string|null $password
* @return Packet
*/
public function packet(Address $ao,string $password=NULL): Packet
{
// @todo Check that the address is one of the system's addresses
return
(new (collect(Packet::PACKET_TYPES)
->get($this->pkt_type ?: config('fido.packet_default'))))
->for($ao)
->password($password);
}
public function poll(): ?Job
{
return Job::where('queue',AddressPoll::QUEUE)
->get()
->where(function($item) {
return $this->akas->pluck('id')->search($item->command->address->id) !== FALSE; })
->last();
}
/**
* Return other addresses that are no collected here, but are on the same network as us.
*
* @return \Illuminate\Database\Eloquent\Collection
* @throws \Exception
*/
public function uncommon(): Collection
{
$our = our_address();
return $this->akas->filter(fn($item)=>($item->parent() && (! $our->contains($item->parent()))));
}
}