diff --git a/app/Http/Controllers/DomainController.php b/app/Http/Controllers/DomainController.php index 20378ab..7dd3ed6 100644 --- a/app/Http/Controllers/DomainController.php +++ b/app/Http/Controllers/DomainController.php @@ -2,12 +2,21 @@ namespace App\Http\Controllers; +use Illuminate\Support\Collection; use Illuminate\Http\Request; -use App\Models\Domain; +use App\Models\{Address,Domain,Zone}; class DomainController extends Controller { + public const NODE_ZC = 1<<1; // Zone + public const NODE_RC = 1<<2; // Region + public const NODE_NC = 1<<3; // Host + public const NODE_HC = 1<<4; // Hub + + // http://ftsc.org/docs/frl-1002.001 + public const NUMBER_MAX = 0x7fff; + public function __construct() { $this->middleware('auth'); @@ -40,6 +49,69 @@ class DomainController extends Controller ->with('o',$o); } + /** + * Get all the hosts for a zone of a particular region (or not) + * + * @param Zone $o + * @param int $region + * @return Collection + */ + public function api_hosts(Zone $o,int $region): Collection + { + $oo = Address::where('role',self::NODE_NC) + ->where('zone_id',$o->id) + ->when($region,function($query,$region) { return $query->where('region_id',$region)->where('node_id','<>',0); }) + ->when((! $region),function($query) use ($region) { return $query->whereNull('region_id'); }) + ->where('point_id',0) + ->with(['system']) + ->get(); + + return $oo->map(function($item) { + return ['id'=>$item->host_id,'value'=>sprintf('%s %s',$item->ftn,$item->system->name)]; + }); + } + + /** + * Find all the hubs for a host + * + * @param Zone $o + * @param int $host + * @return Collection + */ + public function api_hubs(Zone $o,int $host): Collection + { + $oo = Address::where('role',self::NODE_HC) + ->where('zone_id',$o->id) + ->when($host,function($query,$host) { return $query->where('host_id',$host)->where('node_id','<>',0); }) + ->with(['system']) + ->get(); + + return $oo->map(function($item) { + return ['id'=>$item->host_id,'value'=>sprintf('%s %s',$item->ftn,$item->system->name)]; + }); + } + + /** + * Get all the regions for a zone + * + * @param Zone $o + * @return Collection + */ + public function api_regions(Zone $o): Collection + { + $oo = Address::where('role',self::NODE_RC) + ->where('zone_id',$o->id) + ->where('node_id',0) + ->where('point_id',0) + ->orderBy('region_id') + ->with(['system']) + ->get(); + + return $oo->map(function($item) { + return ['id'=>$item->region_id,'value'=>sprintf('%s %s',$item->ftn,$item->system->location)]; + }); + } + public function home() { return view('domain.home'); diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php index 0c5cbbd..d3b2131 100644 --- a/app/Http/Controllers/SystemController.php +++ b/app/Http/Controllers/SystemController.php @@ -4,7 +4,8 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; -use App\Models\System; +use App\Models\{Address,System}; +use App\Rules\TwoByteInteger; class SystemController extends Controller { @@ -13,6 +14,171 @@ class SystemController extends Controller $this->middleware('auth'); } + /** + * Add an address to a system + * + * @param Request $request + * @param System $o + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function add_address(Request $request,System $o) + { + $this->authorize('admin',$o); + session()->flash('add_address',TRUE); + + $request->validate([ + 'action' => 'required|in:region,host,node', + 'zone_id' => 'required|exists:zones,id', + ]); + + switch ($request->post('action')) { + case 'region': + $request->validate([ + 'region_id_new' => [ + 'required', + new TwoByteInteger, + function ($attribute,$value,$fail) { + // Check that the region doesnt already exist + $o = Address::where(function($query) use ($value) { + return $query->where('region_id',$value) + ->whereNULL('host_id') + ->where('node_id',0) + ->where('point_id',0) + ->where('role',DomainController::NODE_RC); + }) + // Check that a host doesnt already exist + ->orWhere(function($query) use ($value) { + return $query->where('host_id',$value) + ->where('point_id',0) + ->where('role',DomainController::NODE_NC); + }); + + if ($o->count()) { + $fail('Region or host already exists'); + } + }, + ], + ]); + + $oo = new Address; + $oo->zone_id = $request->post('zone_id'); + $oo->region_id = $request->post('region_id_new'); + $oo->node_id = 0; + $oo->point_id = 0; + $oo->role = DomainController::NODE_RC; + $oo->active = TRUE; + + $o->addresses()->save($oo); + break; + + case 'host': + $request->validate([ + 'region_id' => ['nullable',new TwoByteInteger], + 'host_id_new' => [ + 'required', + new TwoByteInteger, + function ($attribute,$value,$fail) { + // Check that the region doesnt already exist + $o = Address::where(function($query) use ($value) { + return $query->where('region_id',$value) + ->whereNULL('host_id') + ->where('node_id',0) + ->where('point_id',0) + ->where('role',DomainController::NODE_RC); + }) + // Check that a host doesnt already exist + ->orWhere(function($query) use ($value) { + return $query->where('host_id',$value) + ->where('point_id',0) + ->where('role',DomainController::NODE_NC); + }); + + if ($o->count()) { + $fail('Region or host already exists'); + } + }, + ], + 'node_id_new' => [ + 'required', + new TwoByteInteger, + function ($attribute,$value,$fail) use ($request) { + // Check that the region doesnt already exist + $o = Address::where(function($query) use ($request,$value) { + return $query + ->where('host_id',$request->post('host_id_new')) + ->where('node_id',$value) + ->where('point_id',0) + ->where('role',DomainController::NODE_RC); + }); + + if ($o->count()) { + $fail('Host already exists'); + } + }, + ] + ]); + + $oo = new Address; + $oo->zone_id = $request->post('zone_id'); + $oo->region_id = ($x=$request->post('region_id')) == 'no' ? NULL : $x; + $oo->host_id = $request->post('host_id_new'); + $oo->node_id = $request->post('node_id_new'); + $oo->point_id = 0; + $oo->role = DomainController::NODE_NC; + $oo->active = TRUE; + + $o->addresses()->save($oo); + break; + + case 'node': + $request->validate([ + 'region_id' => ['nullable',new TwoByteInteger], + 'host_id' => ['nullable',new TwoByteInteger], + 'node_id' => [ + 'required', + new TwoByteInteger, + function ($attribute,$value,$fail) use ($request) { + // Check that the region doesnt already exist + $o = Address::where(function($query) use ($request,$value) { + return $query + ->where('host_id',$request->post('host_id_new')) + ->where('node_id',$value) + ->where('point_id',0) + ->where('role',DomainController::NODE_RC); + }); + + if ($o->count()) { + $fail('Host already exists'); + } + }, + ], + 'point_id' => ['required',function($attribute,$value,$fail) { + if (! is_numeric($value) || $value > DomainController::NUMBER_MAX) + $fail(sprintf('Point numbers must be between 0 and %d',DomainController::NUMBER_MAX)); + }], + 'hub' => 'required|boolean', + ]); + + $oo = new Address; + $oo->zone_id = $request->post('zone_id'); + $oo->region_id = ($x=$request->post('region_id')) == 'no' ? NULL : $x; + $oo->host_id = $request->post('host_id'); + $oo->node_id = $request->post('node_id'); + $oo->point_id = $request->post('point_id'); + $oo->role = (! $oo->point_id) && $request->post('hub') ? DomainController::NODE_HC : NULL; + $oo->active = TRUE; + + $o->addresses()->save($oo); + break; + + default: + return redirect()->back()->withErrors(['action'=>'Unknown action: '.$request->post('action')]); + } + + return redirect()->to(sprintf('ftn/system/addedit/%d',$o->id)); + } + /** * Add or edit a node */ diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 037f28a..1b44804 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -35,6 +35,7 @@ class Kernel extends HttpKernel \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, ], 'api' => [ diff --git a/app/Models/Address.php b/app/Models/Address.php new file mode 100644 index 0000000..7aec4ad --- /dev/null +++ b/app/Models/Address.php @@ -0,0 +1,52 @@ +belongsTo(System::class); + } + + public function zone() + { + return $this->belongsTo(Zone::class); + } + + /* ATTRIBUTES */ + + /** + * Render the node name in full 5D + * + * @return string + */ + public function getFTNAttribute() + { + return sprintf('%d:%d/%d.%d@%s',$this->zone->zone_id,$this->host_id ?: $this->region_id,$this->node_id,$this->point_id,$this->zone->domain->name); + } + + public function getRoleAttribute($value) + { + switch ($value) { + case DomainController::NODE_ZC; + return 'Zone'; + case DomainController::NODE_RC; + return 'Region'; + case DomainController::NODE_NC; + return 'Host'; + case DomainController::NODE_HC; + return 'Hub'; + case NULL: + return 'Node'; + default: + return '?'; + } + } +} diff --git a/app/Models/System.php b/app/Models/System.php index a41faa1..7a3f492 100644 --- a/app/Models/System.php +++ b/app/Models/System.php @@ -15,5 +15,14 @@ class System extends Model /* RELATIONS */ + public function addresses() + { + return $this->hasMany(Address::class) + ->orderBy('region_id') + ->orderBy('host_id') + ->orderBy('node_id') + ->orderBy('point_id'); + } + /* CASTS */ } \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php index 00cfcd5..b88d6c8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,40 +6,68 @@ use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Passport\HasApiTokens; +/** + * Class User + * + * User Roles: + * + Site Admin + * + ZC - For Domain/Zone + * + RC - For sub portion of a Domain/Zone (aka Region) + * + Host Admin - For sub portion of a Region + * + Hub Admin - For a sub portion of a Hosts system + * + Sysop - Individual system + * + Guest + * + * @package App\Models + */ class User extends Authenticatable implements MustVerifyEmail { - use HasFactory, Notifiable; + use HasFactory,Notifiable,HasApiTokens; - /** - * The attributes that are mass assignable. - * - * @var array - */ - protected $fillable = [ - 'name', - 'email', - 'password', - ]; + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'email', + 'password', + ]; - /** - * The attributes that should be hidden for arrays. - * - * @var array - */ - protected $hidden = [ - 'password', - 'remember_token', - ]; + /** + * The attributes that should be hidden for arrays. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; - /** - * The attributes that should be cast to native types. - * - * @var array - */ - protected $casts = [ - 'email_verified_at' => 'datetime', - ]; + /** + * The attributes that should be cast to native types. + * + * @var array + */ + protected $casts = [ + 'email_verified_at' => 'datetime', + ]; - protected $dates = ['last_on']; + protected $dates = ['last_on']; + + /* GENERAL METHODS */ + + /** + * See if the user is already a member of the chosen network + * + * @param Domain $o + * @return bool + */ + public function isMember(Domain $o): bool + { + return FALSE; + } } diff --git a/app/Models/Zone.php b/app/Models/Zone.php index c7bc931..c9a23c0 100644 --- a/app/Models/Zone.php +++ b/app/Models/Zone.php @@ -12,6 +12,11 @@ class Zone extends Model /* RELATIONS */ + public function addresses() + { + return $this->hasMany(Address::class); + } + public function domain() { return $this->belongsTo(Domain::class); diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 4cda910..417fc39 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,6 +4,7 @@ namespace App\Providers; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\ServiceProvider; +use Laravel\Passport\Passport; class AppServiceProvider extends ServiceProvider { @@ -14,7 +15,7 @@ class AppServiceProvider extends ServiceProvider */ public function register() { - // + Passport::ignoreMigrations(); } /** diff --git a/app/Rules/TwoByteInteger.php b/app/Rules/TwoByteInteger.php new file mode 100644 index 0000000..edcc93f --- /dev/null +++ b/app/Rules/TwoByteInteger.php @@ -0,0 +1,33 @@ + 0) && ($value < DomainController::NUMBER_MAX)) || ($value === 'no'); + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return sprintf('The number must be between 1 and %d.',DomainController::NUMBER_MAX); + } +} diff --git a/composer.json b/composer.json index 8fe8349..b3ce3be 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "fideloper/proxy": "^4.4", "fruitcake/laravel-cors": "^2.0", "laravel/framework": "^8.0", + "laravel/passport": "^10.1", "laravel/ui": "^3.2", "repat/laravel-job-models": "^0.5.1" }, diff --git a/database/migrations/2019_04_16_105254_create_nodes.php b/database/migrations/2019_04_16_105254_create_nodes.php index 97055eb..dff0c8b 100644 --- a/database/migrations/2019_04_16_105254_create_nodes.php +++ b/database/migrations/2019_04_16_105254_create_nodes.php @@ -74,7 +74,6 @@ class CreateNodes extends Migration $table->integer('software_id')->nullable(); $table->foreign('software_id')->references('id')->on('software'); - // $table->unique(['zone_id','host_id','id']); // $table->index(['zone_id','host_id']); // $table->index(['zone_id','id']); diff --git a/database/migrations/2021_06_19_045817_create_addresses.php b/database/migrations/2021_06_19_045817_create_addresses.php new file mode 100644 index 0000000..5f7bf7f --- /dev/null +++ b/database/migrations/2021_06_19_045817_create_addresses.php @@ -0,0 +1,49 @@ +id(); + $table->timestamps(); + $table->boolean('active'); + + $table->integer('zone_id'); + $table->foreign('zone_id')->references('id')->on('zones'); + + $table->integer('region_id')->nullable(); + $table->integer('host_id')->nullable(); + $table->integer('node_id'); + $table->integer('point_id'); + $table->integer('status')->nullable(); // @note Used to record Down/Private/Pending, etc + + $table->integer('role')->nullable(); + + $table->integer('system_id'); + $table->foreign('system_id')->references('id')->on('systems'); + + $table->unique(['zone_id','region_id','host_id','node_id']); + $table->unique(['zone_id','host_id','node_id','point_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('addresses'); + } +} diff --git a/database/migrations/2021_06_20_124638_unique_relations.php b/database/migrations/2021_06_20_124638_unique_relations.php new file mode 100644 index 0000000..2fc1565 --- /dev/null +++ b/database/migrations/2021_06_20_124638_unique_relations.php @@ -0,0 +1,50 @@ +unique(['node_id','setup_id']); + }); + Schema::table('domain_user', function (Blueprint $table) { + $table->unique(['domain_id','user_id']); + }); + Schema::table('node_system', function (Blueprint $table) { + $table->unique(['node_id','system_id']); + }); + Schema::table('system_user', function (Blueprint $table) { + $table->unique(['system_id','user_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('node_setup', function (Blueprint $table) { + $table->dropUnique(['node_id','setup_id']); + }); + Schema::table('domain_user', function (Blueprint $table) { + $table->dropUnique(['domain_id','user_id']); + }); + Schema::table('node_system', function (Blueprint $table) { + $table->dropUnique(['node_id','system_id']); + }); + Schema::table('system_user', function (Blueprint $table) { + $table->dropUnique(['system_id','user_id']); + }); + } +} diff --git a/public/oldschool/css/main.css b/public/oldschool/css/main.css index d84709e..6ad8ed6 100644 --- a/public/oldschool/css/main.css +++ b/public/oldschool/css/main.css @@ -590,19 +590,6 @@ tbody { border-bottom:1px solid #666 } -.push-right { - float:right; -} -.text-center { - text-align:center; -} -.text-left { - text-align:left; -} -.text-right { - text-align:right; -} - .titledbox { margin:1.5em auto 2.5em auto } diff --git a/resources/views/domain/home.blade.php b/resources/views/domain/home.blade.php index bbb0062..e0292c8 100644 --- a/resources/views/domain/home.blade.php +++ b/resources/views/domain/home.blade.php @@ -7,7 +7,7 @@

About FTN Domains

-

In FTN network addresses, a domain is the 5th dimension and used when a system supports 5D addressing, ie: zone:hub/host.point@domain.

+

In FTN network addresses, a domain is the 5th dimension and used when a system supports 5D addressing, ie: zone:net/node.point@domain.

Domains are used with zones to uniquely identify a FTN network.

Some legacy Fidonet software is not 5D aware and may behave unexpectedly when a domain is used

diff --git a/resources/views/domain/view.blade.php b/resources/views/domain/view.blade.php index 22e0414..9592b4b 100644 --- a/resources/views/domain/view.blade.php +++ b/resources/views/domain/view.blade.php @@ -6,7 +6,7 @@ @section('content')

{{ $o->name }} Last Update: {{ $o->updated_at }}

-
+

About

@@ -19,8 +19,8 @@
-
-

Echo Areas

+
+
@@ -34,7 +34,7 @@
-

File Areas

+
@@ -48,7 +48,7 @@
-

Systems

+
@@ -59,8 +59,9 @@ System Sysop Location + Role Address - Last Connect + Last Seen @@ -71,11 +72,22 @@ {{ $oz->system->name }} {{ $oz->system->sysop }} {{ $oz->system->location }} - {{ $oz->zone_id }}:1/0@{{ $oz->domain->name }} + Zone + {{ $oz->zone_id }}:0/0.0@{{ $oz->domain->name }} - + @foreach ($oz->addresses()->orderBy('region_id')->orderBy('host_id','desc')->orderBy('node_id')->orderBy('point_id')->with(['system','zone.domain'])->get() as $ao) + + {{ $ao->system->name }} + {{ $ao->system->sysop }} + {{ $ao->system->location }} + {{ $ao->role }} + {{ $ao->ftn }} + - + + @endforeach @endforeach @@ -85,7 +97,7 @@
-

Join Network

+
diff --git a/resources/views/layouts/partials/htmlheader.blade.php b/resources/views/layouts/partials/htmlheader.blade.php index 44f165e..061fffa 100644 --- a/resources/views/layouts/partials/htmlheader.blade.php +++ b/resources/views/layouts/partials/htmlheader.blade.php @@ -5,6 +5,9 @@ + + + diff --git a/resources/views/layouts/partials/scripts.blade.php b/resources/views/layouts/partials/scripts.blade.php index b9f3099..5d8f2ad 100644 --- a/resources/views/layouts/partials/scripts.blade.php +++ b/resources/views/layouts/partials/scripts.blade.php @@ -10,6 +10,13 @@ (function () { 'use strict' + // Our CSRF token to each interaction + $.ajaxSetup({ + headers: { + 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') + } + }); + // Fetch all the forms we want to apply custom Bootstrap validation styles to var forms = document.querySelectorAll('.needs-validation') diff --git a/resources/views/layouts/partials/sidebar.blade.php b/resources/views/layouts/partials/sidebar.blade.php index 1abe345..8b7b6ba 100644 --- a/resources/views/layouts/partials/sidebar.blade.php +++ b/resources/views/layouts/partials/sidebar.blade.php @@ -6,7 +6,6 @@
Domains
Systems
Zones
-
Nodes
@can('admin') diff --git a/resources/views/layouts/partials/topmenu.blade.php b/resources/views/layouts/partials/topmenu.blade.php index 15d26ca..19e7a68 100644 --- a/resources/views/layouts/partials/topmenu.blade.php +++ b/resources/views/layouts/partials/topmenu.blade.php @@ -13,7 +13,7 @@ @endif -
    +
      @auth
    • {{ Auth::user()->name }}
    • Logout
    • diff --git a/resources/views/system/addedit.blade.php b/resources/views/system/addedit.blade.php index 4eec86a..3a98a9e 100644 --- a/resources/views/system/addedit.blade.php +++ b/resources/views/system/addedit.blade.php @@ -5,127 +5,635 @@ @endsection @section('content') -
      - @csrf +
      -
      -
      -
      -

      @if($o->exists) Update @else Add @endif System

      + @if ($o->exists) + +
      +

      System

      -
      -
      - -
      - - - - @error('name') - {{ $message }} - @else - A name is required. - @enderror - -
      -
      +
      +
      + @endif + + @csrf -
      - -
      -
      - active))checked @endif> - +
      +
      +
      +

      @if($o->exists) Update @else Add @endif System

      - active))checked @endif> - +
      +
      + +
      + + + + @error('name') + {{ $message }} + @else + A name is required. + @enderror + +
      +
      + +
      + +
      +
      + active))checked @endif> + + + active))checked @endif> + +
      +
      +
      +
      + +
      +
      + +
      + + + + @error('sysop') + {{ $message }} + @else + A Sysop's name is required. + @enderror + +
      +
      + +
      + +
      + + + + @error('location') + {{ $message }} + @else + System location is required. + @enderror + +
      +
      +
      + +
      +
      + +
      + + +
      +
      + +
      + +
      + + + + + @error('address') + {{ $message }} + @enderror + @error('port') + {{ $message }} + @enderror + +
      +
      +
      + +
      +
      + + +
      +
      + +
      +
      + Cancel + @can('admin',$o) + + @endcan +
      +
      +
      -
      -
      - -
      -
      - -
      - - - - @error('sysop') - {{ $message }} - @else - A Sysop's name is required. - @enderror - -
      -
      - -
      - -
      - - - - @error('location') - {{ $message }} - @else - System location is required. - @enderror - -
      -
      -
      - -
      -
      - -
      - - -
      -
      - -
      - -
      - - - - - - - @error('address') - {{ $message }} - @enderror - @error('port') - {{ $message }} - @enderror - -
      -
      -
      - -
      -
      - - -
      -
      - -
      -
      - Cancel - @can('admin',$o) - - @endcan -
      + + @if ($o->exists)
      -
      - + @endif + + @if($o->exists) + +
      + + +
      +
      + TBA +
      +
      +
      + + +
      + + +
      +
      +

      FidoNet addresses are constructed in the format zone:net/node.point@domain.

      + +
      + + +
      +
      +

      FidoNet system are also assigned some roles, and in some cases, those roles have a paritcular address format:

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      RoleAddress Format
      Zone (optional) + ZONE:0/0.0, where the net, node and point values are zero. Zones normally have 1 or more Regions and/or Hosts.

      + (Systems that do not configure other systems with a zone, assume that that other system is in the same zone as the system being configured.) +
      Region (optional) + zone:REGION/0.0, where the zone indicates which zone the region is in. The node and point values are zero. Regions normally have 1 or more Hosts.

      + Fidonet software normally doesnt configure the region address per-se. It is used by the mailer to receive packets destined to it by routing, in transition to the final destination. The region number must be unique with a zone. +
      Host (mandatory) + zone:HOST/0.0, where the zone indicates which zone the host is in. The node and point values are zero. The Host system is normally configured with an additional address, where the NET address is the same and the NODE number that is greater than zero. Hosts may may zero or more Hubs and 1 or more Nodes.

      + The host number must be unique within a zone, which implies that it cannot be the same as a region, if regions are used. +
      Hub (optional) + zone:net/NODE.0, where the zone/net indicates which zone/net the hub is in. The system(s) in the nodelist below a Hub are fed from that hub from a routing perspective.

      + The node is unique within the net and point is zero. +
      Node (required) + zone:net/NODE.0, where the zone/net indicates which zone/net the node is in.

      + The node is unique within the net and point is zero. +
      +
      +
      +
      + + @if($o->addresses->count()) +

      This system has the following addresses assigned to it:

      + + + + + + + + + + + + @foreach ($o->addresses->sortBy(['region_id','host_id']) as $oo) + + + + + + @endforeach + +
      AddressActiveRole
      {{ $oo->ftn }}{{ $oo->active ? 'YES' : 'NO' }}{{ $oo->role }}
      + @endif + + @can('admin',$o) +
      + + @csrf + +
      +
      +
      +

      Assign New address

      + +
      + +
      + +
      + + + + @error('zone_id') + {{ $message }} + @else + Please select the Zone for the node's address. + @enderror + +
      +
      + + +
      + +
      + + + + @error('region_id') + {{ $message }} + @else + Please make a choice. + @enderror + +
      +
      + + +
      + +
      + + + + @error('host_id') + {{ $message }} + @enderror + +
      +
      + + +
      + +
      + + + + @error('hub_id') + {{ $message }} + @enderror + +
      +
      +
      + +
      + +
      + +
      + + + . + + + @error('node_id') + {{ $message }} + @enderror + @error('point_id') + {{ $message }} + @enderror + +
      +
      + + +
      + +
      + + + /0.0 + + @error('region_id_new') + {{ $message }} + @else + The region number is required. + @enderror + +
      +
      + + +
      + +
      + + + / + + .0 + + @error('host_id_new') + {{ $message }} + @else + The host address is required. + @enderror + @error('node_id_new') + {{ $message }} + @else + The node address is required. + @enderror + +
      +
      + + +
      + +
      +
      + hub))checked @endif> + + + hub))checked @endif> + +
      +
      +
      +
      + +
      +
      + Cancel +
      + + + @if($errors->count()) + + There were errors with the submission. + @dump($errors) + + @endif + + + @can('admin',$o) +
      + +
      + @endcan +
      +
      +
      +
      +
      + + @else + This system does not currently belong to any Fido networks. You'll need to ask an admin to assign addresses. + @endcan +
      +
      +
      + @endif +
      @endsection + +@section('page-scripts') + +@append \ No newline at end of file diff --git a/resources/views/zone/home.blade.php b/resources/views/zone/home.blade.php index 89f8731..8c8989e 100644 --- a/resources/views/zone/home.blade.php +++ b/resources/views/zone/home.blade.php @@ -7,7 +7,7 @@

      About FTN Zones

      -

      In FTN network addresses, a zone is the 3rd dimension and used when a system supports 3D (or better) addressing, ie: zone:hub/host.point@domain.

      +

      In FTN network addresses, a zone is the 3rd dimension and used when a system supports 3D (or better) addressing, ie: zone:net/node.point@domain.

      Zones are used with domains to uniquely identify a FTN network. Within an FTN network there can be multiple zones with the same domain.

      It is rare that a domain has multiple zones - unless it grows quite large. Zones can also be used to group systems into a common boundary.

      diff --git a/routes/api.php b/routes/api.php index aa56f7e..1403e24 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,7 @@ get('/user', function (Request $request) { -# return $request->user(); -#}); +Route::middleware(['auth:api'])->group(function () { + Route::get('regions/{o}',[DomainController::class,'api_regions']); + Route::get('hosts/{o}/{region}',[DomainController::class,'api_hosts']); + Route::get('hubs/{o}/{host}',[DomainController::class,'api_hubs']); +}); diff --git a/routes/web.php b/routes/web.php index 11ee269..4e4e9e5 100644 --- a/routes/web.php +++ b/routes/web.php @@ -42,6 +42,8 @@ Route::middleware(['verified','activeuser'])->group(function () { Route::get('ftn/system',[SystemController::class,'home']); Route::match(['get','post'],'ftn/system/addedit/{o?}',[SystemController::class,'add_edit']) ->where('o','[0-9]+'); + Route::post('ftn/system/addaddress/{o?}',[SystemController::class,'add_address']) + ->where('o','[0-9]+'); Route::get('ftn/zone',[ZoneController::class,'home']); Route::match(['get','post'],'ftn/zone/addedit/{o?}',[ZoneController::class,'add_edit'])