From 1890b66dc78c89bca8da5afffe034fd278b40a07 Mon Sep 17 00:00:00 2001 From: Deon George Date: Sun, 3 Dec 2023 18:18:05 +1100 Subject: [PATCH] Implemented Dynamic Items for data to be sent to polled systems based on data in db, like stats/nodelists --- app/Classes/Dynamic.php | 12 ++ app/Classes/Dynamic/HubStats.php | 91 +++++++++++++ app/Classes/File/Send.php | 26 ++++ app/Classes/File/Send/Dynamic.php | 124 ++++++++++++++++++ app/Classes/Protocol/Binkp.php | 1 + app/Http/Controllers/HomeController.php | 1 + app/Models/Address.php | 18 +++ app/Models/Dynamic.php | 32 +++++ app/helpers.php | 25 +++- .../2023_12_01_205144_dynamic_items.php | 40 ++++++ 10 files changed, 366 insertions(+), 4 deletions(-) create mode 100644 app/Classes/Dynamic.php create mode 100644 app/Classes/Dynamic/HubStats.php create mode 100644 app/Classes/File/Send/Dynamic.php create mode 100644 app/Models/Dynamic.php create mode 100644 database/migrations/2023_12_01_205144_dynamic_items.php diff --git a/app/Classes/Dynamic.php b/app/Classes/Dynamic.php new file mode 100644 index 0000000..1a42c93 --- /dev/null +++ b/app/Classes/Dynamic.php @@ -0,0 +1,12 @@ +ftn),['args'=>$arg]); + $this->name = $arg->get('name'); + } + + public function __toString(): string + { + $date = Carbon::now()->yesterday()->endOfday(); + + $r = Address::select([ + 'a.id', + 'addresses.system_id', + 'addresses.zone_id', + 'addresses.region_id', + 'addresses.host_id', + 'addresses.node_id', + 'addresses.point_id', + 'addresses.hub_id', + 'addresses.role', + DB::raw('sum(a.uncollected_echomail) as uncollected_echomail'), + DB::raw('sum(a.uncollected_netmail) as uncollected_netmail'), + DB::raw('sum(a.uncollected_files) as uncollected_files') + ]) + ->from(Address::UncollectedEchomail()->union(Address::UncollectedNetmail())->union(Address::UncollectedFiles()),'a') + ->where('systems.active',true) + ->where('addresses.active',TRUE) + ->where('zones.active',TRUE) + ->where('domains.active',TRUE) + ->where('zones.id',$this->ao->zone_id) + ->join('addresses',['addresses.id'=>'a.id']) + ->join('systems',['systems.id'=>'addresses.system_id']) + ->join('zones',['zones.id'=>'addresses.zone_id']) + ->join('domains',['domains.id'=>'zones.domain_id']) + ->ftnOrder() + ->groupBy('addresses.system_id','a.id','addresses.zone_id','addresses.region_id','addresses.host_id','addresses.node_id','addresses.point_id','addresses.hub_id','addresses.role') + ->with(['system','zone.domain']); + + $header = "| %-12s | %4d | %3d | %3d | %16s | %5s | %5s |\r\n"; + + $output = sprintf("Hub Status for [%s] as at [%s]\r\n",our_address($this->ao->zone)->where('active',TRUE)->first()->ftn,$date); + $output .= "\r"; + $output .= "+--------------+------+-----+-----+------------------+-------+-------+\r\n"; + $output .= "| FTN | ECHO | NET |FILES| LAST SESSION | MODE |AUTOHLD|\r\n"; + $output .= "+--------------+------+-----+-----+------------------+-------+-------+\r\n"; + + foreach($r->get() as $o) { + $output .= sprintf($header, + $o->ftn4d, + $o->uncollected_echomail ?? 0, + $o->uncollected_netmail ?? 0, + $o->uncollected_files ?? 0, + $o->system->last_session?->format('Y-m-d H:i'), + is_null($o->system->pollmode) ? 'HOLD' : ($o->system->pollmode ? 'CRASH' : 'DAILY'), + $o->system->autohold ? 'YES' : 'NO'); + } + + $output .= "+--------------+------+-----+-----+------------------+-------+-------+\r\n"; + + return $output; + } + + public function getName(): string + { + return $this->name ?: 'hubstats.txt'; + } +} \ No newline at end of file diff --git a/app/Classes/File/Send.php b/app/Classes/File/Send.php index ad23179..4c18c74 100644 --- a/app/Classes/File/Send.php +++ b/app/Classes/File/Send.php @@ -6,6 +6,7 @@ use Exception; use Illuminate\Support\Facades\Log; use League\Flysystem\UnreadableFileEncountered; +use App\Classes\File\Send\Dynamic; use App\Classes\Node; use App\Models\Address; @@ -129,6 +130,31 @@ class Send extends Base $this->index = NULL; } + public function dynamic(Address $ao): bool + { + $file = FALSE; + + // If the node is marked as hold - dont send any files. + if ($ao->system->hold) { + Log::info(sprintf('%s: - System [%d] is marked as hold - not checking for files.',self::LOGKEY,$ao->system_id)); + + return FALSE; + } + + // Files + if (($x=$ao->dynamicWaiting())->count()) { + Log::debug(sprintf('%s:- [%d] Dynamic Files(s) added for sending to [%s]',self::LOGKEY,$x->count(),$ao->ftn)); + + // Add Files + foreach ($x as $do) + $this->list->push(new Dynamic($do,$ao,self::T_FILE)); + + $file = TRUE; + } + + return $file; + } + /* private function compress(string $comp_mode): void { diff --git a/app/Classes/File/Send/Dynamic.php b/app/Classes/File/Send/Dynamic.php new file mode 100644 index 0000000..7b6e3a0 --- /dev/null +++ b/app/Classes/File/Send/Dynamic.php @@ -0,0 +1,124 @@ +ftype = ((($type&0xff)<<8)|self::IS_FILE); + $this->item = new $this->do->model($ao,$this->do->arguments); + $this->sent = Carbon::now(); + } + + public function __get($key) { + switch ($key) { + case 'dbids': + return collect([$this->do->id]); + + case 'nameas': + return $this->item->getName(); + + case 'mtime': + return $this->sent->timestamp; + + case 'size': + return strlen($this->buffer); + + default: + return NULL; + } + } + + public function close(bool $successful,Node $node): void + { + if ($successful) { + $this->complete = TRUE; + + $next_at = $this->do->next_at + ->startOfDay() + ->addHours($this->do->start_time->hour) + ->addMinutes($this->do->start_time->minute); + + switch ($this->do->frequency) { + case 'ONCE': + $this->do->active = FALSE; + + break; + + case 'DAILY': + $this->do->next_at = $next_at + ->addDay(); + + break; + + case 'WEEKLY': + $this->do->next_at = $next_at + ->addWeek(); + + break; + + case 'MONTHLY': + $this->do->next_at = $next_at + ->addMonth(); + + break; + + default: + throw new \Exception(sprintf('%s:! Unknown frequency [%s] for [%d]',self::LOGKEY,$this->do->frequency,$this->do->id)); + } + + $this->do->save(); + } + } + + public function feof(): bool + { + return ($this->readpos === $this->size); + } + + public function open(string $compress=''): bool + { + $this->buffer = (string)$this->item; + + return TRUE; + } + + public function read(int $length): string + { + $result = substr($this->buffer,$this->readpos,$length); + $this->readpos += strlen($result); + + return $result; + } + + public function seek(int $pos): bool + { + $this->readpos = ($pos < $this->size) ? $pos : $this->size; + return TRUE; + } +} \ No newline at end of file diff --git a/app/Classes/Protocol/Binkp.php b/app/Classes/Protocol/Binkp.php index 09e7f8f..7c28501 100644 --- a/app/Classes/Protocol/Binkp.php +++ b/app/Classes/Protocol/Binkp.php @@ -1489,6 +1489,7 @@ final class Binkp extends BaseProtocol $this->send->mail($ao); $this->send->files($ao); + $this->send->dynamic($ao); /* * Add "dynamic files", eg: nodelist, nodelist segment, status reports. diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 61a77ec..8c430ca 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -272,6 +272,7 @@ class HomeController extends Controller ->join('systems',['systems.id'=>'addresses.system_id']) ->join('zones',['zones.id'=>'addresses.zone_id']) ->join('domains',['domains.id'=>'zones.domain_id']) + ->ftnOrder() ->groupBy('addresses.system_id','a.id','addresses.zone_id','addresses.region_id','addresses.host_id','addresses.node_id','addresses.point_id','addresses.hub_id','addresses.role') ->with(['system','zone.domain']); diff --git a/app/Models/Address.php b/app/Models/Address.php index e5adab8..2f05575 100644 --- a/app/Models/Address.php +++ b/app/Models/Address.php @@ -242,6 +242,11 @@ class Address extends Model ->with(['zone.domain']); } + public function dynamics() + { + return $this->hasMany(Dynamic::class); + } + /** * Echoareas this address is subscribed to * @@ -691,6 +696,19 @@ class Address extends Model } } + /** + * Files waiting to be sent to this system + * + * @return Collection + */ + public function dynamicWaiting(): Collection + { + return $this->dynamics() + ->where('next_at','<=',Carbon::now()) + ->where('active',TRUE) + ->get(); + } + /** * Echomail waiting to be sent to this system * diff --git a/app/Models/Dynamic.php b/app/Models/Dynamic.php new file mode 100644 index 0000000..fafaf64 --- /dev/null +++ b/app/Models/Dynamic.php @@ -0,0 +1,32 @@ + CollectionOrNull::class, + 'next_at' => 'datetime:Y-m-d H:i:s', + 'start_date' => 'datetime:Y-m-d', + 'start_time' => 'datetime:H:i:s', + ]; + + /* RELATIONS */ + + public function address() + { + return $this->belongsTo(Address::class); + } + + /* ATTRIBUTES */ + + /* METHODS */ +} \ No newline at end of file diff --git a/app/helpers.php b/app/helpers.php index d134499..00dc03a 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -1,12 +1,13 @@ system->addresses; + + return $zo + ? $our->filter(function($item) use ($zo) { return $item->zone_id === $zo->id; }) + : $our; +} + if (! function_exists('timew')) { /** * Convert a time into an 32 bit value. This is primarily used to create 8 character hex filenames that diff --git a/database/migrations/2023_12_01_205144_dynamic_items.php b/database/migrations/2023_12_01_205144_dynamic_items.php new file mode 100644 index 0000000..bc65a5f --- /dev/null +++ b/database/migrations/2023_12_01_205144_dynamic_items.php @@ -0,0 +1,40 @@ +id(); + $table->timestamps(); + $table->string('name')->unqiue(); + $table->enum('frequency',['ONCE','DAILY','WEEKLY','MONTHLY'])->unsigned(); + $table->date('start_date'); + $table->time('start_time'); + $table->datetime('next_at'); + $table->string('model'); + $table->json('arguments')->nullable(); + $table->boolean('active')->nullable(); + + $table->bigInteger('address_id'); + $table->foreign('address_id')->references('id')->on('addresses'); + + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('dynamics'); + } +};