Compare commits

...

4 Commits

Author SHA1 Message Date
e963675fd3 Continue to show all common addresses in Items Waiting tab, Add Address Clear Queue job to delete anything in the queue for an address
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
2024-11-04 15:59:00 +11:00
7aecbe2f6e Fix for 3b7ce4b, change where setup is defined for our protocols
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 28s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 1m30s
Create Docker Image / Final Docker Image Manifest (push) Successful in 8s
2024-11-04 13:06:04 +11:00
db6c569345 Added New Nodes Report
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 29s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 1m32s
Create Docker Image / Final Docker Image Manifest (push) Successful in 8s
2024-11-04 12:44:46 +11:00
231977932c Consistency and SQL Query optimisations - focused around the UI 2024-11-04 09:07:39 +11:00
31 changed files with 729 additions and 373 deletions

View File

@ -137,7 +137,7 @@ abstract class Protocol
public function __construct() public function __construct()
{ {
$this->setup = Config::get('setup'); $this->setup = Config::get('setup',Setup::findOrFail(config('app.id')));
} }
/** /**

View File

@ -1290,7 +1290,7 @@ final class Binkp extends BaseProtocol
Log::info(sprintf('%s:- SECURE',self::LOGKEY)); Log::info(sprintf('%s:- SECURE',self::LOGKEY));
// @todo Since we have connected, if the node was marked down/hold reset that // @todo Since we have connected, if the node was marked down/hold reset that
// Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations())); // Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
} }
return $this->binkp_hsdone(); return $this->binkp_hsdone();
@ -1389,7 +1389,7 @@ final class Binkp extends BaseProtocol
$this->msgs(self::BPM_OK,sprintf('%ssecure',$have_pwd ? '' : 'non-')); $this->msgs(self::BPM_OK,sprintf('%ssecure',$have_pwd ? '' : 'non-'));
// @todo Since we have connected, if the node was marked down/hold reset that // @todo Since we have connected, if the node was marked down/hold reset that
// Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations())); // Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
} else { } else {
$this->msgs(self::OK,'non-secure'); $this->msgs(self::OK,'non-secure');

View File

@ -1062,7 +1062,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
} }
// @todo Since we have connected, if the node was marked down/hold reset that // @todo Since we have connected, if the node was marked down/hold reset that
// Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations())); // Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
// @todo Lock Node AKAs // @todo Lock Node AKAs

View File

@ -0,0 +1,42 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Jobs\AddressClearQueue as Job;
use App\Models\Address;
class AddressClearQueue extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'address:clear:queue'
.' {ftn : FTN}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clear up anything queued for an FTN';
/**
* Execute the console command.
*/
public function handle(): int
{
$ao = Address::findFTN($this->argument('ftn'),TRUE,TRUE);
if (! $ao) {
$this->error('FTN not found: '.$this->argument('ftn'));
return self::FAILURE;
}
return Job::dispatchSync($ao);
}
}

View File

@ -38,7 +38,7 @@ class CommBinkpReceive extends Command
$o = Setup::findOrFail(config('app.id')); $o = Setup::findOrFail(config('app.id'));
$server = new SocketServer($o->binkp_port,$o->binkp_bind); $server = new SocketServer($o->binkp_port,$o->binkp_bind);
$server->handler = [new Binkp($o),'onConnect']; $server->handler = [new Binkp,'onConnect'];
try { try {
$server->listen(); $server->listen();

View File

@ -38,7 +38,7 @@ class CommEMSIReceive extends Command
$o = Setup::findOrFail(config('app.id')); $o = Setup::findOrFail(config('app.id'));
$server = new SocketServer($o->emsi_port,$o->emsi_bind); $server = new SocketServer($o->emsi_port,$o->emsi_bind);
$server->handler = [new EMSI($o),'onConnect']; $server->handler = [new EMSI,'onConnect'];
try { try {
$server->listen(); $server->listen();

View File

@ -0,0 +1,50 @@
<?php
namespace App\Console\Commands;
use App\Models\Address;
use Carbon\Carbon;
use Illuminate\Console\Command;
use App\Jobs\NodesNew as Job;
use App\Models\Domain;
class NodesNew extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nodes:new'
.' {domain : Domain}'
.' {--date= : From a specific date (default 1 since last Saturday)}'
.' {--netmail= : Send a Netmail to FTN}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'List new nodes since last Saturday (or a specific date)';
/**
* Execute the console command.
*/
public function handle(): int
{
$do = Domain::where('name',$this->argument('domain'))->singleOrFail();
$ao = NULL;
if ($this->option('netmail')) {
$ao = Address::findFTN($this->option('netmail'));
if (! $ao) {
$this->error('Address not found: '.$this->option('netmail'));
return self::FAILURE;
}
}
return Job::dispatchSync($do,$this->option('date') ? Carbon::parse($this->option('date')) : Carbon::parse('last saturday'),$ao);
}
}

View File

@ -50,7 +50,7 @@ class ServerStart extends Command
'address'=>$o->binkp_bind, 'address'=>$o->binkp_bind,
'port'=>$o->binkp_port, 'port'=>$o->binkp_port,
'proto'=>SOCK_STREAM, 'proto'=>SOCK_STREAM,
'class'=>new Binkp($o), 'class'=>new Binkp,
]); ]);
if ($o->emsi_active) if ($o->emsi_active)
@ -58,7 +58,7 @@ class ServerStart extends Command
'address'=>$o->emsi_bind, 'address'=>$o->emsi_bind,
'port'=>$o->emsi_port, 'port'=>$o->emsi_port,
'proto'=>SOCK_STREAM, 'proto'=>SOCK_STREAM,
'class'=>new EMSI($o), 'class'=>new EMSI,
]); ]);
if ($o->dns_active) if ($o->dns_active)
@ -66,7 +66,7 @@ class ServerStart extends Command
'address'=>$o->dns_bind, 'address'=>$o->dns_bind,
'port'=>$o->dns_port, 'port'=>$o->dns_port,
'proto'=>SOCK_DGRAM, 'proto'=>SOCK_DGRAM,
'class'=>new DNS(), 'class'=>new DNS,
]); ]);
$children = collect(); $children = collect();

View File

@ -115,7 +115,7 @@ class DomainController extends Controller
if (! $o->public && ! Gate::check('admin',$o)) if (! $o->public && ! Gate::check('admin',$o))
abort(404); abort(404);
$o->load(['zones.system','zones.domain','zones.addresses.nodes_hub','zones.addresses']); $o->load(['zones.system','zones.domain','zones.addresses.nodes_hub','zones.addresses.echomail_from']);
return view('domain.view') return view('domain.view')
->with('o',$o); ->with('o',$o);

View File

@ -66,7 +66,15 @@ class SystemController extends Controller
->with('saved',TRUE); ->with('saved',TRUE);
} }
$o->load(['addresses.zone.domain','addresses.system','sessions.domain','sessions.systems']); $o->loadMissing([
'zcs',
// For 'ftn' to work
'addresses.zone:id,zone_id,domain_id,active',
'addresses.zone.domain:id,name,active',
// For 'role'
'addresses.system:id,address',
]);
//$o->load(['sessions.domain','sessions.systems']);
return view('system.addedit') return view('system.addedit')
->with('action',$o->exists ? 'update_nn' : 'create') ->with('action',$o->exists ? 'update_nn' : 'create')
@ -611,31 +619,12 @@ class SystemController extends Controller
*/ */
public function register(SystemRegisterRequest $request) public function register(SystemRegisterRequest $request)
{ {
// Step 1, show the user a form to select an existing defined system
if ($request->isMethod('GET'))
return view('user.system.register');
if ($request->action === 'register' && $request->name && is_numeric($request->name)) if ($request->action === 'register' && $request->name && is_numeric($request->name))
return view('user.system.widget.register_confirm') return view('user.system.widget.register_confirm')
->with('o',System::findOrFail($request->name)); ->with('o',System::findOrFail($request->name));
$o = System::findOrNew(is_numeric($request->system_id) ? $request->system_id : NULL); $o = System::findOrNew(is_numeric($request->system_id) ? $request->system_id : NULL);
// If the system exists, and we are 'register', we'll start the address claim process
if ($o->exists && $request->action === 'Link') {
$validate = Setup::findOrFail(config('app.id'))->system->inMyZones($o->addresses);
// If we have addresses, we'll trigger the routed netmail
if ($validate->count()) {
Notification::route('netmail',$x=$validate->first())->notify(new AddressLink(Auth::user()));
AddressPoll::dispatch($x)->delay(15);
}
return view('user.system.widget.register_send')
->with('validate',$validate)
->with('o',$o);
}
// If the system doesnt exist, we'll create it // If the system doesnt exist, we'll create it
if (! $o->exist) { if (! $o->exist) {
$o->sysop = Auth::user()->name; $o->sysop = Auth::user()->name;

View File

@ -0,0 +1,67 @@
<?php
namespace App\Jobs;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use App\Classes\FTN\Message;
use App\Models\{Address,Domain,System};
use App\Notifications\Echomails\AbsentNodes;
use App\Notifications\Emails\NodeMarkedDown as NodeMarkedDownEmail;
use App\Notifications\Netmails\NodeMarkedDown as NodeMarkedDownNetmail;
use App\Notifications\Emails\NodeMarkedHold as NodeMarkedHoldEmail;
use App\Notifications\Netmails\NodeMarkedHold as NodeMarkedHoldNetmail;
use App\Notifications\Emails\NodeDelisted as NodeDelistedEmail;
use App\Notifications\Netmails\NodeDelisted as NodeDelistedNetmail;
class AddressClearQueue implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private const LOGKEY = 'JAC';
private Address $ao; // System address
/**
* Create a new job instance.
*/
public function __construct(Address $ao)
{
$this->ao = $ao->withoutRelations();
}
/**
* Execute the job.
*/
public function handle(): void
{
// Remove echomail not collected from echomail_seenby
DB::table('echomail_seenby')
->where('address_id',$this->ao->id)
->whereNotNull('export_at')
->whereNull('sent_at')
->delete();
// Remove FLAG_INTRANSIT from netmail that hasnt been delivered
DB::table('netmails')
->where('tftn_id',$this->ao->id)
->whereRaw(sprintf('(flags & %d) > 0',Message::FLAG_INTRANSIT))
->update(['flags'=>DB::raw(sprintf('(flags & ~%d)',Message::FLAG_INTRANSIT))]);
// Remove files not collected
DB::table('file_seenby')
->where('address_id',$this->ao->id)
->whereNotNull('export_at')
->whereNull('sent_at')
->delete();
}
}

View File

@ -67,26 +67,6 @@ class AddressIdle implements ShouldQueue
Log::info(sprintf('%s:- Delisting [%s], not seen for at least [%d] days',self::LOGKEY,$ao->ftn,$ao->system->last_seen?->diffInDays())); Log::info(sprintf('%s:- Delisting [%s], not seen for at least [%d] days',self::LOGKEY,$ao->ftn,$ao->system->last_seen?->diffInDays()));
$contact = FALSE; $contact = FALSE;
// Remove echomail not collected from echomail_seenby
DB::table('echomail_seenby')
->where('address_id',$ao->id)
->whereNotNull('export_at')
->whereNull('sent_at')
->delete();
// Remove FLAG_INTRANSIT from netmail that hasnt been delivered
DB::table('netmails')
->where('tftn_id',$ao->id)
->whereRaw(sprintf('(flags & %d) > 0',Message::FLAG_INTRANSIT))
->update(['flags'=>DB::raw(sprintf('(flags & ~%d)',Message::FLAG_INTRANSIT))]);
// Remove files not collected
DB::table('file_seenby')
->where('address_id',$ao->id)
->whereNotNull('export_at')
->whereNull('sent_at')
->delete();
// Remove subscribed echoareas // Remove subscribed echoareas
$ao->echoareas()->detach(); $ao->echoareas()->detach();
@ -97,6 +77,9 @@ class AddressIdle implements ShouldQueue
$ao->validated = FALSE; $ao->validated = FALSE;
$ao->save(); $ao->save();
// Clear the queue
AddressClearQueue::dispatchSync($ao);
// Email Alert // Email Alert
if ($ao->system->users->count()) { if ($ao->system->users->count()) {
Notification::send($ao->system->users,new NodeDelistedEmail($ao->withoutRelations())); Notification::send($ao->system->users,new NodeDelistedEmail($ao->withoutRelations()));
@ -104,8 +87,8 @@ class AddressIdle implements ShouldQueue
} }
// Netmail Alert (to othernet network address) // Netmail Alert (to othernet network address)
if ($ao->system->uncommon()->count()) { if ($ao->system->aka_unknown()->count()) {
Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeDelistedNetmail($ao->withoutRelations())); Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeDelistedNetmail($ao->withoutRelations()));
$contact = TRUE; $contact = TRUE;
} }
@ -140,8 +123,8 @@ class AddressIdle implements ShouldQueue
} }
// Netmail Alert (to othernet network address) // Netmail Alert (to othernet network address)
if ($ao->system->uncommon()->count()) { if ($ao->system->aka_unknown()->count()) {
Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations())); Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
$contact = TRUE; $contact = TRUE;
} }
@ -188,8 +171,8 @@ class AddressIdle implements ShouldQueue
} }
// Netmail Alert (to othernet network address) // Netmail Alert (to othernet network address)
if ($ao->system->uncommon()->count()) { if ($ao->system->aka_unknown()->count()) {
Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeMarkedHoldNetmail($ao->withoutRelations())); Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedHoldNetmail($ao->withoutRelations()));
$contact = TRUE; $contact = TRUE;
} }
@ -214,21 +197,9 @@ class AddressIdle implements ShouldQueue
$age = Carbon::now()->subDays($days)->endOfDay(); $age = Carbon::now()->subDays($days)->endOfDay();
return Address::select([ return Address::FTN()
'addresses.id', ->ActiveFTN()
'system_id', ->addSelect(['addresses.updated_at'])
'zone_id',
'region_id',
'host_id',
'node_id',
'point_id',
'addresses.active',
'hub_id',
'role',
'addresses.updated_at',
])
->activeFTN()
->where('systems.active',TRUE)
->where(fn($query)=>$query->where('point_id',0)->orWhereNull('point_id')) ->where(fn($query)=>$query->where('point_id',0)->orWhereNull('point_id'))
->whereIn('addresses.id',our_nodes($do)->pluck('id')) ->whereIn('addresses.id',our_nodes($do)->pluck('id'))
->when($ao,fn($query)=>$query->where('addresses.id',$ao->id)) ->when($ao,fn($query)=>$query->where('addresses.id',$ao->id))
@ -236,9 +207,7 @@ class AddressIdle implements ShouldQueue
->whereRaw(sprintf('((role IS NULL) OR (role=0) OR ((role & %d) > 0))',$flags)) ->whereRaw(sprintf('((role IS NULL) OR (role=0) OR ((role & %d) > 0))',$flags))
->whereRaw(sprintf('((role IS NULL) OR ((role & %d) = 0))',Address::NODE_KEEP)) ->whereRaw(sprintf('((role IS NULL) OR ((role & %d) = 0))',Address::NODE_KEEP))
->join('systems',['systems.id'=>'addresses.system_id']) ->join('systems',['systems.id'=>'addresses.system_id'])
->ftnOrder() //->with(['system','zone.domain'])
->with(['system','zone.domain'])
->dontCache()
->get(); ->get();
} }
} }

View File

@ -93,13 +93,13 @@ class AddressPoll implements ShouldQueue, ShouldBeUnique
switch ($o->name) { switch ($o->name) {
case 'BINKP': case 'BINKP':
$s = new Binkp(Setup::findOrFail(config('app.id'))); $s = new Binkp;
$mo = Mailer::where('name','BINKP')->singleOrFail(); $mo = Mailer::where('name','BINKP')->singleOrFail();
break; break;
case 'EMSI': case 'EMSI':
$s = new EMSI(Setup::findOrFail(config('app.id'))); $s = new EMSI;
$mo = Mailer::where('name','EMSI')->singleOrFail(); $mo = Mailer::where('name','EMSI')->singleOrFail();
break; break;

65
app/Jobs/NodesNew.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace App\Jobs;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use App\Models\{Address, Domain, System};
use App\Notifications\Netmails\NodesNew as NotificationNodesNew;
class NodesNew implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private const LOGKEY = 'JNN';
private ?Carbon $since; // New nodes since this date
private Address $ao; // Domain we are processing
private Domain $do; // Domain we are processing
/**
* Create a new job instance.
*/
public function __construct(Domain $do,Carbon $since=NULL,Address $ao=NULL)
{
$this->do = $do->withoutRelations();
$this->ao = $ao?->withoutRelations();
$this->since = $since;
}
/**
* Execute the job.
*/
public function handle(): void
{
$since = ($this->since ?: Carbon::parse('last saturday'))->startOfDay();
$result = Address::FTN()
->ActiveFTN()
->join('systems',['systems.id'=>'addresses.system_id'])
->join('system_zone',['system_zone.system_id'=>'systems.id','system_zone.zone_id'=>'zones.id'])
->whereIn('zones.id',$this->do->zones->pluck('id'))
->where('systems.active',TRUE)
->where('systems.created_at','>=',$since)
->get();
if ($result->count()) {
Log::notice(sprintf('%s:- Sending new nodes since [%s] (%d)',self::LOGKEY,$since,$result->count()));
Notification::route('netmail',$this->ao->withoutRelations())
->notify(new NotificationNodesNew(
$since,
$result,
));
} else
Log::notice(sprintf('%s:- No nodes since [%s]',self::LOGKEY,$since));
}
}

View File

@ -24,19 +24,28 @@ use App\Traits\{QueryCacheableConfig,ScopeActive};
* *
* If an address is validated, we know that the system is using the address (we've confirmed that during a session). * If an address is validated, we know that the system is using the address (we've confirmed that during a session).
* validated is update/removed is during a mailer session, confirming if the node has the address * validated is update/removed is during a mailer session, confirming if the node has the address
* @todo Remove validated, if that system hasnt used the address for a defined period (eg: 30)
* *
* We'll only trigger a poll to a system that we have mail for if active && validated, unless "forced". * We'll only trigger a poll to a system that we have mail for if active && validated, unless "forced".
* *
* Any mail for that address will be delivered, if active && validated. * Any mail for that address will be delivered, if active && validated.
* *
* Address status:
* + Active (active=true/validated=true) - mail can flow and in one of our networks (we have session details)
* + Pending (active=true/validated=false) - remote hasnt configured address during a session and in one of our networks
* + Known (active=true/validated=true) - the node presented this address, but we didnt auth it, and its a network we are not in
* + Unconfirm (active=true/validated=true) - the node presented this address, but we dont manage the address (it uses a different hub)
* + Nodelist (active=true/validated=true) - the node presented this address, but we dont manage the address and its in a recent nodelist
* + Delisted (active=false/validated=true) - this node shouldnt use this address any more
* + Freed (active=false/validated=false) - this node shouldnt is known to not be using this address any more
*
* Other Status
* + citizen - The address belongs to one of our jurisdiction (hub_id = our address, NC,RC,ZC)
* + foreign - The address doesnt belong to our jurisdiction
*
* @see \App\Http\Requests\AddressAdd::class for rules about AKA and role * @see \App\Http\Requests\AddressAdd::class for rules about AKA and role
*/ */
// Need to add
// Calculated Region for an FTN, ie: find the parent and use their region id
// - if creating an RC, then the region is part of the creation, ZC region is 0
// Then use this in createFTN
class Address extends Model class Address extends Model
{ {
use QueryCacheableConfig,ScopeActive,SoftDeletes; use QueryCacheableConfig,ScopeActive,SoftDeletes;
@ -246,7 +255,7 @@ class Address extends Model
$o = self::findZone($do,$ftn['n'],$ftn['f'],$ftn['p'],$trashed); $o = self::findZone($do,$ftn['n'],$ftn['f'],$ftn['p'],$trashed);
} }
return ($o && $o->system->active) ? $o : NULL; return ($o && ($trashed || $o->system->active)) ? $o : NULL;
} }
public static function newFTN(string $address): self public static function newFTN(string $address): self
@ -395,16 +404,14 @@ class Address extends Model
* *
* @param $query * @param $query
* @return mixed * @return mixed
* @note zones and domains needs to be joined in the base call, or use FTN()
*/ */
public function scopeActiveFTN($query) public function scopeActiveFTN($query)
{ {
return $query->select($this->getTable().'.*') return $query
->join('zones',['zones.id'=>'addresses.zone_id'])
->join('domains',['domains.id'=>'zones.domain_id'])
->where('zones.active',TRUE) ->where('zones.active',TRUE)
->where('domains.active',TRUE) ->where('domains.active',TRUE)
->active() ->active();
->FTNorder();
} }
/** /**
@ -416,13 +423,21 @@ class Address extends Model
public function scopeFTN($query) public function scopeFTN($query)
{ {
return $query return $query
->select(['id','addresses.zone_id','host_id','node_id','point_id','system_id']) ->select(['addresses.id','region_id','host_id','node_id','point_id','addresses.zone_id','addresses.active','role','security','addresses.system_id','addresses.active','validated','deleted_at'])
->join('zones',['zones.id'=>'addresses.zone_id'])
->join('domains',['domains.id'=>'zones.domain_id'])
->orderBy('domains.name')
->orderBy('region_id')
->orderBy('host_id')
->orderBy('node_id')
->orderBy('point_id')
->with([ ->with([
'zone:zones.id,domain_id,zone_id', 'zone:zones.id,domain_id,zone_id,active',
'zone.domain:domains.id,name', 'zone.domain:domains.id,name,active,public',
]); ]);
} }
/** @deprecated use FTN() */
public function scopeFTNOrder($query) public function scopeFTNOrder($query)
{ {
return $query return $query

View File

@ -48,35 +48,39 @@ class System extends Model
$uo = Auth::user(); $uo = Auth::user();
return $query return $query
->when($uo && ! $uo->isAdmin(),function($query) use ($uo) { ->when(
return $query->whereIn('id',$uo->systems->pluck('id')) $uo && ! $uo->isAdmin(),
->orWhere($this->getTable().'.active',TRUE); fn($query)=>
},function($query) { $query->where($this->getTable().'.active',TRUE); }) $query
->whereIn('id',$uo->systems->pluck('id'))
->orWhere($this->getTable().'.active',TRUE),
fn($query)=>$query->where($this->getTable().'.active',TRUE))
->orderBy('name'); ->orderBy('name');
} }
/* RELATIONS */ /* RELATIONS */
/**
* All addresses assigned to a system, including addresses pending deletion
* @return mixed
*/
public function addresses() public function addresses()
{ {
return $this->hasMany(Address::class) return $this->hasMany(Address::class)
->withTrashed() ->FTN()
->FTNorder(); ->withTrashed();
} }
/**
* System addresses that are active
*
* @return mixed
*/
public function akas() public function akas()
{ {
return $this->hasMany(Address::class) return $this->hasMany(Address::class)
->select('addresses.*') ->FTN()
->join('zones',['zones.id'=>'addresses.zone_id']) ->ActiveFTN();
->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() public function mailers()
@ -136,7 +140,14 @@ class System extends Model
*/ */
public function zcs() public function zcs()
{ {
return $this->hasMany(Zone::class); 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',
]);
} }
/** /**
@ -189,6 +200,66 @@ class System extends Model
/* METHODS */ /* 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() public function echoareas()
{ {
return Echoarea::select('echoareas.*') return Echoarea::select('echoareas.*')
@ -250,28 +321,13 @@ class System extends Model
: $akas; : $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 * Return the packet that this system uses
* *
* @param Address $ao * @param Address $ao
* @param string|null $password * @param string|null $password
* @return Packet * @return Packet
* @throws \Exception
*/ */
public function packet(Address $ao,string $password=NULL): Packet public function packet(Address $ao,string $password=NULL): Packet
{ {
@ -289,22 +345,7 @@ class System extends Model
{ {
return Job::where('queue',AddressPoll::QUEUE) return Job::where('queue',AddressPoll::QUEUE)
->get() ->get()
->where(function($item) { ->where(fn($item)=>$this->akas->pluck('id')->contains($item->command->address->id))
return $this->akas->pluck('id')->search($item->command->address->id) !== FALSE; })
->last(); ->last();
} }
/**
* Return other addresses that are not 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)=>(($x=$item->parent()) && (! $our->contains($x)) && ($our->pluck('zone.domain_id')->contains($item->zone->domain_id))));
}
} }

View File

@ -75,19 +75,14 @@ class User extends Authenticatable implements MustVerifyEmail
/* GENERAL METHODS */ /* GENERAL METHODS */
public function addresses(Domain $o=NULL): Collection public function addresses(): Collection
{ {
return Address::select('addresses.*') return Address::select('addresses.*')
->join('systems',['systems.id'=>'addresses.system_id']) ->join('systems',['systems.id'=>'addresses.system_id'])
->join('system_user',['system_user.system_id'=>'systems.id']) ->join('system_user',['system_user.system_id'=>'systems.id'])
->when(! is_null($o),function($query) use ($o) {
return $query
->join('zones',['zones.id'=>'addresses.zone_id'])
->where('zones.domain_id',$o->id);
})
->where('system_user.user_id',$this->id) ->where('system_user.user_id',$this->id)
->activeFTN() ->ActiveFTN()
->with(['zone.domain']) ->FTN()
->get(); ->get();
} }

View File

@ -46,9 +46,9 @@ class NodeMarkedDown extends Netmails //implements ShouldQueue
->addText(sprintf("Your system has been marked **DOWN**, because it hasnt polled **%s** with address %s since **%s** (%d days).\r",$this->ao->zone->domain->name,$this->ao->ftn4d,$this->ao->system->last_seen?->format('Y-m-d') ?: 'Not seen',$this->ao->system->last_seen?->diffInDays($now))) ->addText(sprintf("Your system has been marked **DOWN**, because it hasnt polled **%s** with address %s since **%s** (%d days).\r",$this->ao->zone->domain->name,$this->ao->ftn4d,$this->ao->system->last_seen?->format('Y-m-d') ?: 'Not seen',$this->ao->system->last_seen?->diffInDays($now)))
->addText("\r") ->addText("\r")
->addText("You have (waiting for collection):\r") ->addText("You have (waiting for collection):\r")
->addText(sprintf('* %s Netmails',number_format($this->ao->netmailWaiting()->count()))) ->addText(sprintf("* %s Netmails\r",number_format($this->ao->netmailWaiting()->count())))
->addText(sprintf('* %s Echomails',number_format($this->ao->echomailWaiting()->count()))) ->addText(sprintf("* %s Echomails\r",number_format($this->ao->echomailWaiting()->count())))
->addText(sprintf('* %s Files',number_format($this->ao->filesWaiting()->count()))) ->addText(sprintf("* %s Files\r",number_format($this->ao->filesWaiting()->count())))
->addText("\r") ->addText("\r")
->addText(sprintf("Your system will automatically be **DE-LISTED** if your system hasnt polled to collected your mail/file(s) by **%s**\r\r",$now->addDays(7)->format('Y-m-d'))) ->addText(sprintf("Your system will automatically be **DE-LISTED** if your system hasnt polled to collected your mail/file(s) by **%s**\r\r",$now->addDays(7)->format('Y-m-d')))
->addText("If you think you've received this netmail by mistake or need help, please let me know.\r"); ->addText("If you think you've received this netmail by mistake or need help, please let me know.\r");

View File

@ -47,9 +47,9 @@ class NodeMarkedHold extends Netmails //implements ShouldQueue
->addText(sprintf("Your system has been marked **HOLD**, because it hasnt polled **%s** with address %s since **%s** (%d days).\r",$this->ao->zone->domain->name,$this->ao->ftn4d,$this->ao->system->last_seen?->format('Y-m-d') ?: 'Not seen',$this->ao->system->last_seen?->diffInDays($now))) ->addText(sprintf("Your system has been marked **HOLD**, because it hasnt polled **%s** with address %s since **%s** (%d days).\r",$this->ao->zone->domain->name,$this->ao->ftn4d,$this->ao->system->last_seen?->format('Y-m-d') ?: 'Not seen',$this->ao->system->last_seen?->diffInDays($now)))
->addText("\r") ->addText("\r")
->addText("You have (waiting for collection):\r") ->addText("You have (waiting for collection):\r")
->addText(sprintf('* %s Netmails',number_format($this->ao->netmailWaiting()->count()))) ->addText(sprintf("* %s Netmails\r",number_format($this->ao->netmailWaiting()->count())))
->addText(sprintf('* %s Echomails',number_format($this->ao->echomailWaiting()->count()))) ->addText(sprintf("* %s Echomails\r",number_format($this->ao->echomailWaiting()->count())))
->addText(sprintf('* %s Files',number_format($this->ao->filesWaiting()->count()))) ->addText(sprintf("* %s Files\r",number_format($this->ao->filesWaiting()->count())))
->addText("\r") ->addText("\r")
->addText(sprintf("To clear this status, all you need to do make sure your system polls and collects mail by **%s**\r\r",($this->ao->system->last_seen ?: Carbon::now())->addDays(config('fido.idle.down'))->format('Y-m-d'))) ->addText(sprintf("To clear this status, all you need to do make sure your system polls and collects mail by **%s**\r\r",($this->ao->system->last_seen ?: Carbon::now())->addDays(config('fido.idle.down'))->format('Y-m-d')))
->addText("If you think you've received this netmail by mistake or need help, please let me know.\r"); ->addText("If you think you've received this netmail by mistake or need help, please let me know.\r");

View File

@ -0,0 +1,104 @@
<?php
namespace App\Notifications\Netmails;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Notifications\Netmails;
use App\Models\{Address,Netmail};
use App\Traits\PageTemplate;
class NodesNew extends Netmails //implements ShouldQueue
{
use Queueable,PageTemplate;
private const LOGKEY = 'NNN';
private Carbon $since;
private Collection $list;
/**
* Create a new notification instance.
*/
public function __construct(Carbon $since,Collection $list)
{
parent::__construct();
$this->list = $list;
$this->since = $since;
}
/**
* Get the mail representation of the notification.
*/
public function toNetmail(object $notifiable): Netmail
{
$o = $this->setupNetmail($notifiable);
$ao = $notifiable->routeNotificationFor(static::via);
Log::info(sprintf('%s:+ Sending a NEW NODE LIST to [%s] at address [%s]',self::LOGKEY,$ao->system->sysop,$ao->ftn));
$o->subject = sprintf('Here is a list of new nodes on clrghouz since %s',$x=$this->since->format('Y-m-d'));
$o->flags = (Message::FLAG_LOCAL|Message::FLAG_PRIVATE|Message::FLAG_CRASH);
// Message
$msg = $this->page(FALSE,'new nodes');
$msg->addText(sprintf("Hi %s,\r\r",$ao->system->sysop))
->addText(sprintf("The following new system have been defined on clrghouz since %s.\r\r",$x));
$this->list->loadMissing(['system']);
$space = str_repeat(' ',$this->list->pluck('ftn4d')->max(fn($item)=>strlen($item))+2);
$c = 0;
foreach ($this->list as $oo) {
if ($c++)
$msg->addText("\r");
$msg->addText(sprintf("* %s - %s from %s.\r",$oo->ftn4D,$oo->system->sysop,$oo->system->location));
if ($oo->system->method) {
switch ($oo->system->method) {
case 23:
$msg->addText(sprintf("%s - BBS is available TELNET [%s:%d]\r",$space,$oo->system->address,$oo->system->port));
break;
case 22:
$msg->addText(sprintf("%s - BBS is available SSH [%s:%d]\r",$space,$oo->system->address,$oo->system->port));
break;
case 519:
$msg->addText(sprintf("%s - BBS is available RLOGIN [%s:%d]\r",$space,$oo->system->address,$oo->system->port));
break;
default:
$msg->addText(sprintf("%s - No Details available for connecting to BBS\r",$space));
}
}
if ($oo->system->mailers->count()) {
$msg->addText("\r");
$msg->addText(sprintf("%s - Mail can be sent using:\r",$space));
foreach ($oo->system->mailers as $mo) {
$msg->addText(sprintf("%s * %s [%s:%d]\r",$space,$mo->name,$oo->system->address,$mo->pivot->port));
}
} else {
$msg->addText(sprintf("%s - No mailer information provided, so will be PRIVATE\r",$space));
}
}
$o->msg = $msg->render();
$o->set_tagline = 'All aboard!';
$o->save();
return $o;
}
}

View File

@ -114,6 +114,7 @@ function our_address(Domain|Address $o=NULL): Collection|Address|NULL
// We are requesting a list of addresses for a Domain, or a specific Address, and we have more than 1 // We are requesting a list of addresses for a Domain, or a specific Address, and we have more than 1
switch (get_class($o)) { switch (get_class($o)) {
// Looking for addresses in the same domain, and if fido.strict, addresses that have a higher role (ie: uplink)
case Address::class: case Address::class:
$filter = $so->system->akas $filter = $so->system->akas
->filter(fn($item)=>$item->zone->domain_id === $o->zone->domain_id) ->filter(fn($item)=>$item->zone->domain_id === $o->zone->domain_id)
@ -125,6 +126,7 @@ function our_address(Domain|Address $o=NULL): Collection|Address|NULL
return $filter->count() ? $filter->last()->unsetRelation('nodes_hub') : NULL; return $filter->count() ? $filter->last()->unsetRelation('nodes_hub') : NULL;
// Addresses in this domain
case Domain::class: case Domain::class:
return $so->system->akas return $so->system->akas
->filter(fn($item)=>$item->zone->domain_id === $o->id) ->filter(fn($item)=>$item->zone->domain_id === $o->id)

View File

@ -28,9 +28,7 @@ $user->load(['systems.akas.zone.domain.echoareas','systems.akas.echoareas']);
@foreach($user @foreach($user
->addresses() ->addresses()
->diff(our_address()) ->diff(our_address())
->filter(fn($item)=>($item->point_id === 0)) ->filter(fn($item)=>($item->point_id === 0) && ($item->zone->domain->isManaged()))
->filter(function($item) { return $item->zone->domain->active && $item->zone->domain->public && $item->zone->domain->isManaged(); })
->sortBy('zone.domain.name')
->groupBy('zone.domain.name') as $list) ->groupBy('zone.domain.name') as $list)
<!-- {{ $x=$list->first()->domain->name }} --> <!-- {{ $x=$list->first()->domain->name }} -->
<div class="accordion-item"> <div class="accordion-item">

View File

@ -1,5 +1,5 @@
<!-- $o=Domain::class -->
@use(Illuminate\Mail\Markdown) @use(Illuminate\Mail\Markdown)
<!-- $o=Domain::class -->
@extends('layouts.app') @extends('layouts.app')
@section('htmlheader_title') @section('htmlheader_title')
@ -137,12 +137,13 @@
<div id="collapse_systems" class="accordion-collapse collapse" aria-labelledby="systems" data-bs-parent="#accordion_homepage"> <div id="collapse_systems" class="accordion-collapse collapse" aria-labelledby="systems" data-bs-parent="#accordion_homepage">
<div class="accordion-body"> <div class="accordion-body">
<p>The following systems are members of this network.</p> <p>The following systems are members of this network.</p>
<table class="table monotable" id="system"> <table class="table monotable w-100" id="system">
<thead> <thead>
<tr> <tr>
<th>System</th> <th>System</th>
<th>Sysop</th> <th>Sysop</th>
<th>Location</th> <th>Location</th>
<th>Region</th>
<th>Address</th> <th>Address</th>
<th>Last Echomail</th> <th>Last Echomail</th>
</tr> </tr>
@ -155,6 +156,7 @@
<td><a href="{{ url('system/addedit',[$ao->system_id]) }}">{{ $ao->system->full_name($ao) }}</a> @auth<span class="float-end"><small>@if($ao->is_hosted)<sup>{{ $ao->is_default ? '**' : '*' }}</sup>@elseif($ao->system->setup)<sup class="success">+</sup>@endif[{{ $ao->system_id }}]</small></span>@endauth</td> <td><a href="{{ url('system/addedit',[$ao->system_id]) }}">{{ $ao->system->full_name($ao) }}</a> @auth<span class="float-end"><small>@if($ao->is_hosted)<sup>{{ $ao->is_default ? '**' : '*' }}</sup>@elseif($ao->system->setup)<sup class="success">+</sup>@endif[{{ $ao->system_id }}]</small></span>@endauth</td>
<td>{{ $ao->system->sysop }}</td> <td>{{ $ao->system->sysop }}</td>
<td>{{ $ao->system->location }}</td> <td>{{ $ao->system->location }}</td>
<td>{{ $ao->region_id }}</td>
<td>{{ $ao->ftn4d }}</td> <td>{{ $ao->ftn4d }}</td>
<td>{{ $ao->echomail_from->count() ? $ao->echomail_from->first()->datetime->format('Y-m-d H:i') : '-' }}</td> <td>{{ $ao->echomail_from->count() ? $ao->echomail_from->first()->datetime->format('Y-m-d H:i') : '-' }}</td>
</tr> </tr>
@ -279,6 +281,18 @@
searching: true, searching: true,
ordering: true, ordering: true,
order: [], order: [],
rowGroup: {
dataSrc: [3],
startRender: function(rows,group) {
return 'Region '+group;
}
},
columnDefs: [
{
targets: [3],
visible: false,
},
],
conditionalPaging: { conditionalPaging: {
style: 'fade', style: 'fade',
speed: 500 // optional speed: 500 // optional

View File

@ -20,7 +20,7 @@
@endif @endif
@if($o->zcs->count()) @if($o->zcs->count())
<p>This system is the ZC for the following zones: <strong class="highlight">{!! $o->zcs->sortBy('zone_id')->map(function($item) { return sprintf('%d@%s',$item->zone_id,$item->domain->name); })->join('</strong>, <strong class="highlight">') !!} </strong></p> <p>This system is the ZC for the following zones: <strong class="highlight">{!! $o->zcs->map(fn($item)=>sprintf('%d@%s',$item->zone_id,$item->domain->name))->join('</strong>, <strong class="highlight">') !!} </strong></p>
@endif @endif
@include('widgets.error') @include('widgets.error')
@ -123,9 +123,9 @@
</thead> </thead>
<tbody> <tbody>
@foreach ($o->addresses->sortBy(function($item) { return sprintf('%04x%04x%04x%04x%04x',$item->zone->zone_id,$item->region_id,$item->host_id,$item->node_id,$item->point_id); }) as $oo) @foreach ($o->addresses as $oo)
<tr> <tr>
<td @if($oo->trashed()) class="trashed" @elseif (! $oo->active) class="inactive" @endif>{{ $oo->ftn }}<span class="float-end"><data value="{{ $oo->id }}:{{ $oo->validated ? 1 : 0 }}" class="validated"><i title="@if($oo->validated)Mail flowing @else Mail held @endif" @class(['bi','bi-activity'=>$oo->validated,'bi-radioactive'=>(! $oo->validated)])></i></data></span></td> <td @class(['trashed'=>$oo->trashed(),'inactive'=>(! $oo->trashed()) && (! $oo->active)])>{{ $oo->ftn }}<span class="float-end"><data value="{{ $oo->id }}:{{ $oo->validated ? 1 : 0 }}" class="validated"><i title="@if($oo->validated)Mail flowing @else Mail held @endif" @class(['bi','bi-activity'=>$oo->validated,'bi-radioactive'=>(! $oo->validated)])></i></data></span></td>
<td>{{ $oo->active ? 'YES' : 'NO' }}</td> <td>{{ $oo->active ? 'YES' : 'NO' }}</td>
<td class="text-end">{{ $oo->security }}</td> <td class="text-end">{{ $oo->security }}</td>
<td>{{ $oo->role_name }}</td> <td>{{ $oo->role_name }}</td>
@ -156,7 +156,6 @@
</tbody> </tbody>
</table> </table>
@endif @endif
</div> </div>
</div> </div>
@ -321,10 +320,10 @@
</thead> </thead>
<tbody> <tbody>
@foreach ($o->addresses->sortBy('zone.zone_id') as $ao) @foreach ($o->addresses_common() as $ao)
<tr> <tr>
<td>{{ $ao->ftn }}</td> <td @class(['trashed'=>$ao->trashed(),'inactive'=>(! $ao->trashed()) && (! $ao->active)])>{{ $ao->ftn }}</td>
<td>{{ $ao->netmailWaiting()->count() }}</td> <td class="text-end">{{ $ao->netmailWaiting()->count() }}</td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>
@ -343,10 +342,10 @@
</thead> </thead>
<tbody> <tbody>
@foreach ($o->addresses->sortBy('zone.zone_id') as $ao) @foreach ($o->addresses_common() as $ao)
<tr> <tr>
<td>{{ $ao->ftn }}</td> <td @class(['trashed'=>$ao->trashed(),'inactive'=>(! $ao->trashed()) && (! $ao->active)])>{{ $ao->ftn }}</td>
<td>{{ $ao->echomailWaiting()->count() }}</td> <td class="text-end">{{ $ao->echomailWaiting()->count() }}</td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>
@ -365,10 +364,10 @@
</thead> </thead>
<tbody> <tbody>
@foreach ($o->addresses->sortBy('zone.zone_id') as $ao) @foreach ($o->addresses_common() as $ao)
<tr> <tr>
<td>{{ $ao->ftn }}</td> <td @class(['trashed'=>$ao->trashed(),'inactive'=>(! $ao->trashed()) && (! $ao->active)])>{{ $ao->ftn }}</td>
<td>{{ $ao->filesWaiting()->count() }}</td> <td class="text-end">{{ $ao->filesWaiting()->count() }}</td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>

View File

@ -1,91 +1,84 @@
@if(($x=\App\Models\Zone::active() <form class="needs-validation" method="post" action="{{ url('system/echoarea',$o->id) }}" novalidate>
->whereIn('id',$o->sessions->pluck('pivot.zone_id')) @csrf
->orderBy('zone_id')
->with(['domain'])
->get())->count())
<form class="needs-validation" method="post" action="{{ url('system/echoarea',$o->id) }}" novalidate> <div class="row pt-0">
@csrf <div class="col-12">
<div class="greyframe titledbox shadow0xb0">
<div class="row pt-0"> <div class="row">
<div class="col-12"> <!-- Select Domain -->
<div class="greyframe titledbox shadow0xb0"> <div class="col-3">
<div class="row"> <label for="echoarea_domain_id" class="form-label">Network</label>
<!-- Select Domain --> <div class="input-group has-validation">
<div class="col-3"> <span class="input-group-text"><i class="bi bi-hash"></i></span>
<label for="echoarea_domain_id" class="form-label">Network</label> <select class="form-select @error('domain_id') is-invalid @enderror" id="echoarea_domain_id" name="domain_id" required>
<div class="input-group has-validation"> <option></option>
<span class="input-group-text"><i class="bi bi-hash"></i></span> @foreach($o->sessions as $zo)
<select class="form-select @error('domain_id') is-invalid @enderror" id="echoarea_domain_id" name="domain_id" required> <option value="{{ $zo->domain_id }}" @if(old('domain_id') == $zo->domain_id)selected @endif>{{ $zo->zone_id }} <small>({{ $zo->domain->name }})</small></option>
<option></option>
@foreach($x as $zo)
<option value="{{ $zo->domain_id }}" @if(old('domain_id') == $zo->domain_id)selected @endif>{{ $zo->zone_id }} <small>({{ $zo->domain->name }})</small></option>
@endforeach
</select>
<span class="invalid-feedback" role="alert">
@error('domain_id')
{{ $message }}
@enderror
</span>
</div>
</div>
<!-- Select Address -->
<div class="col-3">
<div class="d-none" id="echoarea_address-select">
<label for="echoarea_address_id" class="form-label">Address</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<select class="form-select" id="echoarea_address_id" name="address_id" required>
<option></option>
</select>
</div>
</div>
</div>
<!-- Summary of Addresses -->
<div class="offset-3 col-3" id="echoarea-summary">
<table class="table monotable">
<thead>
<tr>
<th>Network</th>
<th class="text-end">Areas</th>
</tr>
</thead>
<tbody>
@foreach ($o->echoareas()->with(['domain'])->get()->groupBy('domain_id') as $oo)
<tr>
<td>{{ $oo->first()->domain->name }}</td>
<td class="text-end">{{ $oo->count() }}</td>
</tr>
@endforeach @endforeach
</tbody> </select>
</table> <span class="invalid-feedback" role="alert">
</div> @error('domain_id')
</div> {{ $message }}
@enderror
@if($errors->count())
<div class="row">
<span class="btn btn-sm btn-danger" role="alert" style="text-align: left;">
There were errors with the submission.
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</span> </span>
</div> </div>
@endif
<div class="row">
<div class="col-12 d-none" id="echoarea-select"></div>
</div> </div>
<!-- Select Address -->
<div class="col-3">
<div class="d-none" id="echoarea_address-select">
<label for="echoarea_address_id" class="form-label">Address</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<select class="form-select" id="echoarea_address_id" name="address_id" required>
<option></option>
</select>
</div>
</div>
</div>
<!-- Summary of Addresses -->
<div class="offset-3 col-3" id="echoarea-summary">
<table class="table monotable">
<thead>
<tr>
<th>Network</th>
<th class="text-end">Areas</th>
</tr>
</thead>
<tbody>
@foreach ($o->echoareas()->with(['domain'])->get()->groupBy('domain_id') as $oo)
<tr>
<td>{{ $oo->first()->domain->name }}</td>
<td class="text-end">{{ $oo->count() }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@if($errors->count())
<div class="row">
<span class="btn btn-sm btn-danger" role="alert" style="text-align: left;">
There were errors with the submission.
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</span>
</div>
@endif
<div class="row">
<div class="col-12 d-none" id="echoarea-select"></div>
</div> </div>
</div> </div>
</div> </div>
</form> </div>
@endif </form>
@section('page-scripts') @section('page-scripts')
<script type="text/javascript" src="{{ asset('plugin/checkboxes/jquery.checkboxes-1.2.2.min.js') }}"></script> <script type="text/javascript" src="{{ asset('plugin/checkboxes/jquery.checkboxes-1.2.2.min.js') }}"></script>

View File

@ -1,78 +1,71 @@
@if(($x=\App\Models\Zone::active() <form class="needs-validation" method="post" action="{{ url('system/filearea',$o->id) }}" novalidate>
->whereIn('id',$o->sessions->pluck('pivot.zone_id')) @csrf
->orderBy('zone_id')
->with(['domain'])
->get())->count())
<form class="needs-validation" method="post" action="{{ url('system/filearea',$o->id) }}" novalidate> <div class="row pt-0">
@csrf <div class="col-12">
<div class="greyframe titledbox shadow0xb0">
<div class="row pt-0"> <div class="row">
<div class="col-12"> <!-- Select Domain -->
<div class="greyframe titledbox shadow0xb0"> <div class="col-3">
<div class="row"> <label for="domain_id" class="form-label">Network</label>
<!-- Select Domain --> <div class="input-group has-validation">
<div class="col-3"> <span class="input-group-text"><i class="bi bi-hash"></i></span>
<label for="domain_id" class="form-label">Network</label> <select class="form-select @error('domain_id') is-invalid @enderror" id="filearea_domain_id" name="domain_id" required>
<div class="input-group has-validation"> <option></option>
<span class="input-group-text"><i class="bi bi-hash"></i></span> @foreach($o->sessions as $zo)
<select class="form-select @error('domain_id') is-invalid @enderror" id="filearea_domain_id" name="domain_id" required> <option value="{{ $zo->domain_id }}" @if(old('domain_id') == $zo->domain_id)selected @endif>{{ $zo->zone_id }} <small>({{ $zo->domain->name }})</small></option>
<option></option>
@foreach($x as $zo)
<option value="{{ $zo->domain_id }}" @if(old('domain_id') == $zo->domain_id)selected @endif>{{ $zo->zone_id }} <small>({{ $zo->domain->name }})</small></option>
@endforeach
</select>
<span class="invalid-feedback" role="alert">
@error('domain_id')
{{ $message }}
@enderror
</span>
</div>
</div>
<!-- Select Address -->
<div class="col-3">
<div class="d-none" id="filearea_address-select">
<label for="filearea_address_id" class="form-label">Address</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<select class="form-select" id="filearea_address_id" name="address_id" required>
<option></option>
</select>
</div>
</div>
</div>
<!-- Summary of Addresses -->
<div class="offset-3 col-3" id="filearea-summary">
<table class="table monotable">
<thead>
<tr>
<th>Network</th>
<th class="text-end">Areas</th>
</tr>
</thead>
<tbody>
@foreach ($o->fileareas()->with(['domain'])->get()->groupBy('domain_id') as $oo)
<tr>
<td>{{ $oo->first()->domain->name }}</td>
<td class="text-end">{{ $oo->count() }}</td>
</tr>
@endforeach @endforeach
</tbody> </select>
</table> <span class="invalid-feedback" role="alert">
@error('domain_id')
{{ $message }}
@enderror
</span>
</div> </div>
</div> </div>
<div class="row"> <!-- Select Address -->
<div class="col-12 d-none" id="filearea-select"></div> <div class="col-3">
<div class="d-none" id="filearea_address-select">
<label for="filearea_address_id" class="form-label">Address</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<select class="form-select" id="filearea_address_id" name="address_id" required>
<option></option>
</select>
</div>
</div>
</div> </div>
<!-- Summary of Addresses -->
<div class="offset-3 col-3" id="filearea-summary">
<table class="table monotable">
<thead>
<tr>
<th>Network</th>
<th class="text-end">Areas</th>
</tr>
</thead>
<tbody>
@foreach ($o->fileareas()->with(['domain'])->get()->groupBy('domain_id') as $oo)
<tr>
<td>{{ $oo->first()->domain->name }}</td>
<td class="text-end">{{ $oo->count() }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-12 d-none" id="filearea-select"></div>
</div> </div>
</div> </div>
</div> </div>
</form> </div>
@endif </form>
@section('page-scripts') @section('page-scripts')
<script type="text/javascript" src="{{ asset('plugin/checkboxes/jquery.checkboxes-1.2.2.min.js') }}"></script> <script type="text/javascript" src="{{ asset('plugin/checkboxes/jquery.checkboxes-1.2.2.min.js') }}"></script>

View File

@ -6,7 +6,7 @@
<div class="row pt-0"> <div class="row pt-0">
<div class="col-12"> <div class="col-12">
<div class="greyframe titledbox shadow0xb0"> <div class="greyframe titledbox shadow0xb0">
<h2 class="cap">@can($action,$o) @if($o->exists) Update @else Add @endif @endif System</h2> <h2 class="cap">@can($action,$o) @if($o->exists) Update @else Add @endif @endcan System</h2>
@include('system.widget.system') @include('system.widget.system')
</div> </div>

View File

@ -1,15 +1,9 @@
@php @use(App\Models\Address)
use App\Models\{Address,Zone}; <!-- $o=System::class -->
@endphp
<div class="row"> <div class="row">
<!-- Zone mail sent to --> <!-- Zone mail sent to, because we dont have session details for these addresses -->
<div class="col-6"> <div class="col-6">
@if(! $o->setup && ($x=Zone::active() @if($o->aka_unknown()->count())
->whereIn('id',$o->zones->pluck('id'))
->whereNotIn('id',$o->sessions->pluck('id'))
->get())->count())
<h4>This host's mail is sent to:</h4> <h4>This host's mail is sent to:</h4>
<table class="table monotable"> <table class="table monotable">
<thead> <thead>
@ -20,19 +14,17 @@
</thead> </thead>
<tbody> <tbody>
@foreach($x as $zo) @foreach($o->aka_unknown() as $ao)
@foreach ($o->match($zo,Address::NODE_ALL) as $oo) <tr>
<tr> <td>{{ $ao->ftn }}</td>
<td>{{ $oo->ftn }}</td> <td>
<td> @if ($xx=$ao->uplink())
@if ($x=$oo->uplink()) {{ $xx->ftn4d }}
{{ $x->ftn4d }} @else
@else None
None @endif
@endif </td>
</td> </tr>
</tr>
@endforeach
@endforeach @endforeach
</tbody> </tbody>
</table> </table>
@ -41,28 +33,62 @@
<!-- Systems this host collects for --> <!-- Systems this host collects for -->
<div class="col-6"> <div class="col-6">
@if($o->sessions->count()) <div class="row">
<h4>This host collects mail for the following systems:</h4> <div class="col-12">
@if($o->sessions->count())
<h4>This host collects mail for the following systems:</h4>
<table class="table monotable"> <table class="table monotable">
<thead> <thead>
<tr>
<th>AKA</th>
<th>For Systems(s)</th>
</tr>
</thead>
<tbody>
@foreach ($o->sessions->sortBy('zone_id') as $zo)
@foreach ($o->match($zo,Address::NODE_ALL) as $oo)
<tr> <tr>
<td>{{ $oo->ftn }}</td> <th>AKA</th>
<td>{!! (($x=$oo->downlinks()) && $x->count()) ? $x->pluck('ftn')->join('<br>') : 'None' !!}</td> <th>Incl Systems(s)</th>
</tr> </tr>
@endforeach </thead>
@endforeach
</tbody> <tbody>
</table> @foreach ($o->sessions->sortBy('zone_id') as $zo)
@if($zo->pivot->default)
<tr>
<td>{!! $o->akas->filter(fn($item)=>$item->zone->domain_id === $zo->domain_id)->pluck('ftn')->join('<br>') !!}</td>
<td>All <small>(not otherwise routed)</small></td>
</tr>
@else
@foreach ($o->match($zo,Address::NODE_ALL) as $oo)
<tr>
<td>{{ $oo->ftn }}</td>
<td>{!! (($x=$oo->downlinks()) && $x->count()) ? $x->pluck('ftn')->join('<br>') : 'None' !!}</td>
</tr>
@endforeach
@endif
@endforeach
</tbody>
</table>
@endif
</div>
</div>
@if($o->aka_uncommon()->count())
<div class="row">
<div class="rol-12">
<h4>This host's mail not known here:</h4>
<table class="table monotable">
<thead>
<tr>
<th>AKA</th>
</tr>
</thead>
<tbody>
@foreach($o->aka_uncommon() as $ao)
<tr>
<td>{{ $ao->ftn }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif @endif
</div> </div>
</div> </div>

View File

@ -1,8 +1,6 @@
@php @use(App\Classes\FTN\Packet)
use App\Classes\FTN\Packet; @use(App\Models\Mailer)
use App\Models\{Mailer,User}; @use(App\Models\User)
@endphp
<!-- $o=System::class --> <!-- $o=System::class -->
<div class="row"> <div class="row">
<div class="col-xl-9 col-12"> <div class="col-xl-9 col-12">
@ -20,7 +18,7 @@ use App\Models\{Mailer,User};
<select style="width: 80%;" class="form-select @error('users') is-invalid @enderror" id="users" name="users[]"> <select style="width: 80%;" class="form-select @error('users') is-invalid @enderror" id="users" name="users[]">
<option value="">&nbsp;</option> <option value="">&nbsp;</option>
@foreach (User::orderBy('name')->active()->get() as $uo) @foreach (User::orderBy('name')->active()->get() as $uo)
<option value="{{ $uo->id }}" @if(in_array($uo->id,old('users',$o->users->pluck('id')->toArray())))selected @endif>{{ $uo->name }} <small>({{ $uo->email }})</small></option> <option value="{{ $uo->id }}" @selected(in_array($uo->id,old('users',$o->users->pluck('id')->toArray())))>{{ $uo->name }} <small>({{ $uo->email }})</small></option>
@endforeach @endforeach
</select> </select>
@ -325,8 +323,6 @@ use App\Models\{Mailer,User};
</div> </div>
</div> </div>
@endcan @endcan
</div> </div>
@if(! is_null($o->pollmode)) @if(! is_null($o->pollmode))
@ -438,9 +434,6 @@ use App\Models\{Mailer,User};
@else @else
<input type="hidden" name="system_id" value="{{ $o->id }}"> <input type="hidden" name="system_id" value="{{ $o->id }}">
<span><small><strong>NOTE:</strong> You'll be able to update these details after registration is completed.</small></span> <span><small><strong>NOTE:</strong> You'll be able to update these details after registration is completed.</small></span>
{{--
<button type="submit" class="btn btn-success float-end" name="submit" value="register">Register</button>
--}}
@endcan @endcan
@else @else
<button type="submit" class="btn btn-success float-end" name="submit" value="create">Register</button> <button type="submit" class="btn btn-success float-end" name="submit" value="create">Register</button>

View File

@ -29,7 +29,7 @@ use App\Models\System;
@foreach (System::select(['systems.id','systems.name']) @foreach (System::select(['systems.id','systems.name'])
->active() ->active()
->where('id','<>',$setup->system_id) ->where('id','<>',$setup->system_id)
->whereRaw('id NOT IN (SELECT system_id FROM system_user)') ->whereRaw('id NOT IN (SELECT system_id FROM "system_user")')
->cursor() as $oo) ->cursor() as $oo)
<option value="{{ $oo->id }}" @if(old('id')===$oo->id)selected @endif>{{ $oo->name }}</option> <option value="{{ $oo->id }}" @if(old('id')===$oo->id)selected @endif>{{ $oo->name }}</option>
@endforeach @endforeach

View File

@ -133,7 +133,8 @@ Route::middleware(['auth','verified','activeuser'])->group(function () {
->where('o','[0-9]+') ->where('o','[0-9]+')
->where('packet','[0-9a-zA-Z]+'); ->where('packet','[0-9a-zA-Z]+');
Route::match(['get','post'],'user/system/register',[SystemController::class,'register']); Route::view('user/system/register','user.system.register');
Route::post('user/system/register',[SystemController::class,'register']);
Route::match(['post'],'user/system/link',[SystemController::class,'system_link']); Route::match(['post'],'user/system/link',[SystemController::class,'system_link']);
/* ZONE PATHS */ /* ZONE PATHS */