clrghouz/app/Models/System.php
Deon George e963675fd3
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 26s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 1m31s
Create Docker Image / Final Docker Image Manifest (push) Successful in 8s
Continue to show all common addresses in Items Waiting tab, Add Address Clear Queue job to delete anything in the queue for an address
2024-11-04 15:59:00 +11:00

351 lines
8.1 KiB
PHP

<?php
namespace App\Models;
use Carbon\Carbon;
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(),
fn($query)=>
$query
->whereIn('id',$uo->systems->pluck('id'))
->orWhere($this->getTable().'.active',TRUE),
fn($query)=>$query->where($this->getTable().'.active',TRUE))
->orderBy('name');
}
/* RELATIONS */
/**
* All addresses assigned to a system, including addresses pending deletion
* @return mixed
*/
public function addresses()
{
return $this->hasMany(Address::class)
->FTN()
->withTrashed();
}
/**
* System addresses that are active
*
* @return mixed
*/
public function akas()
{
return $this->hasMany(Address::class)
->FTN()
->ActiveFTN();
}
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)
->select(['id','zones.zone_id','domain_id','active'])
->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)
->select(['zones.id','zone_id','domain_id','system_id','zones.active'])
->join('domains',['domains.id'=>'zones.domain_id'])
->orderBy('domains.name')
->orderBy('zone_id')
->with([
'domain:id,name,active',
]);
}
/**
* 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 getIsOwnedAttribute(): bool
{
return $this->users->count();
}
public function getPktMsgsAttribute(?int $val): int
{
return $val ?: Setup::findOrFail(config('app.id'))->msgs_pkt;
}
public function getLastSeenAttribute(): ?Carbon
{
return $this->logs_recent->first()?->created_at;
}
/* METHODS */
public function addresses_common(): Collection
{
$our = our_address()->pluck('zone.domain_id')->unique();
// Return our akas, filter with our_addresses()
return $this->addresses->filter(fn($item)=>$our->contains($item->zone->domain_id));
}
/**
* Return the ACTIVE addresses that are common with our addresses
*
* @return Collection
* @throws \Exception
*/
public function aka_common(): Collection
{
$our = our_address()->pluck('zone.domain_id')->unique();
// Return our akas, filter with our_addresses()
return $this->akas->filter(fn($item)=>$our->contains($item->zone->domain_id));
}
/**
* Return the ACTIVE addresses that we auth with
*
* @return Collection
* @throws \Exception
*/
public function aka_known(): Collection
{
return $this->aka_common()
->filter(fn($item)=>$this->sessions->contains($item->zone_id));
}
/**
* Return the ACTIVE addresses in the same networks as us, but dont auth here
*
* @return Collection
* @throws \Exception
*/
public function aka_unknown(): Collection
{
return $this->aka_common()
->filter(fn($item)=>! $this->sessions->contains($item->zone_id));
}
/**
* Return the AKAs that are in networks not common with us
*
* @return Collection
* @throws \Exception
*/
public function aka_uncommon(): Collection
{
$our = our_address()->pluck('zone.domain_id')->unique();
// Return our akas, filter with our_addresses()
return $this->akas->filter(fn($item)=>! $our->contains($item->zone->domain_id));
}
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;
}
/**
* Return the packet that this system uses
*
* @param Address $ao
* @param string|null $password
* @return Packet
* @throws \Exception
*/
public function packet(Address $ao,string $password=NULL): Packet
{
if ($ao->system_id !== $this->id)
throw new \Exception('Packet for [%s] is not for system [%d]',$ao->ftn,$this->id);
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(fn($item)=>$this->akas->pluck('id')->contains($item->command->address->id))
->last();
}
}