Added laravel/passport, assign ftn addresses to nodes

This commit is contained in:
Deon George 2021-06-20 23:03:20 +10:00
parent 84df9ce811
commit 7cab4e288b
24 changed files with 1163 additions and 176 deletions

View File

@ -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');

View File

@ -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
*/

View File

@ -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' => [

52
app/Models/Address.php Normal file
View File

@ -0,0 +1,52 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Http\Controllers\DomainController;
class Address extends Model
{
/* RELATIONS */
public function system()
{
return $this->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 '?';
}
}
}

View File

@ -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 */
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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();
}
/**

View File

@ -0,0 +1,33 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use App\Http\Controllers\DomainController;
class TwoByteInteger implements Rule
{
/**
* Determine if the validation rule passes.
* This will check that a number used for zone, net, host is between 1 and DomainController::NUMBER_MAX.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
return (is_numeric($value) && ($value > 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);
}
}

View File

@ -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"
},

View File

@ -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']);

View File

@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAddresses extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('addresses', function (Blueprint $table) {
$table->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');
}
}

View File

@ -0,0 +1,50 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class UniqueRelations extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('node_setup', function (Blueprint $table) {
$table->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']);
});
}
}

View File

@ -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
}

View File

@ -7,7 +7,7 @@
<div class="row">
<div class="col-12">
<h2>About FTN Domains</h2>
<p>In FTN network addresses, a domain is the 5th dimension and used when a system supports 5D addressing, ie: zone:hub/host.point@<strong class="highlight">domain</strong>.</p>
<p>In FTN network addresses, a domain is the 5th dimension and used when a system supports 5D addressing, ie: zone:net/node.point@<strong class="highlight">domain</strong>.</p>
<p>Domains are used with zones to uniquely identify a FTN network.</p>
<p><small>Some legacy Fidonet software is not 5D aware and may behave unexpectedly when a domain is used</small></p>
</div>

View File

@ -6,7 +6,7 @@
@section('content')
<h1>{{ $o->name }} <small class="push-right">Last Update: {{ $o->updated_at }}</small></h1>
<div class="accordion accordion-flush" id="accordion_homepage">
<div class="accordion" id="accordion_homepage">
<!-- About -->
<div class="accordion-item">
<h3 class="accordion-header" id="about" data-bs-toggle="collapse" data-bs-target="#collapse_about" aria-expanded="true" aria-controls="collapse_about">About</h3>
@ -19,8 +19,8 @@
</div>
<!-- Echomail -->
<div class="accordion-item">
<h3 class="accordion-header" id="echoareas" data-bs-toggle="collapse" data-bs-target="#collapse_echoareas" aria-expanded="true" aria-controls="collapse_echoareas">Echo Areas</h3>
<div class="accordion-item open">
<h3 class="accordion-header" id="echoareas" data-bs-toggle="collapse" data-bs-target="#collapse_echoareas" aria-expanded="false" aria-controls="collapse_echoareas">Echo Areas</h3>
<div id="collapse_echoareas" class="accordion-collapse collapse" aria-labelledby="echoareas" data-bs-parent="#accordion_homepage">
<div class="accordion-body">
@ -34,7 +34,7 @@
<!-- File areas -->
<div class="accordion-item">
<h3 class="accordion-header" id="fileareas" data-bs-toggle="collapse" data-bs-target="#collapse_fileareas" aria-expanded="true" aria-controls="collapse_fileareas">File Areas</h3>
<h3 class="accordion-header" id="fileareas" data-bs-toggle="collapse" data-bs-target="#collapse_fileareas" aria-expanded="false" aria-controls="collapse_fileareas">File Areas</h3>
<div id="collapse_fileareas" class="accordion-collapse collapse" aria-labelledby="fileareas" data-bs-parent="#accordion_homepage">
<div class="accordion-body">
@ -48,7 +48,7 @@
<!-- Systems -->
<div class="accordion-item">
<h3 class="accordion-header" id="systems" data-bs-toggle="collapse" data-bs-target="#collapse_systems" aria-expanded="true" aria-controls="collapse_systems">Systems</h3>
<h3 class="accordion-header" id="systems" data-bs-toggle="collapse" data-bs-target="#collapse_systems" aria-expanded="false" aria-controls="collapse_systems">Systems</h3>
<div id="collapse_systems" class="accordion-collapse collapse" aria-labelledby="systems" data-bs-parent="#accordion_homepage">
<div class="accordion-body">
@ -59,8 +59,9 @@
<th>System</th>
<th>Sysop</th>
<th>Location</th>
<th>Role</th>
<th>Address</th>
<th>Last Connect</th>
<th>Last Seen</th>
</tr>
</thead>
@ -71,11 +72,22 @@
<td>{{ $oz->system->name }}</td>
<td>{{ $oz->system->sysop }}</td>
<td>{{ $oz->system->location }}</td>
<td>{{ $oz->zone_id }}:1/0<span>@</span>{{ $oz->domain->name }}</td>
<td>Zone</td>
<td>{{ $oz->zone_id }}:0/0.0<span>@</span>{{ $oz->domain->name }}</td>
<td>-</td>
</tr>
<!-- Other Nodes -->
@foreach ($oz->addresses()->orderBy('region_id')->orderBy('host_id','desc')->orderBy('node_id')->orderBy('point_id')->with(['system','zone.domain'])->get() as $ao)
<tr>
<td>{{ $ao->system->name }}</td>
<td>{{ $ao->system->sysop }}</td>
<td>{{ $ao->system->location }}</td>
<td>{{ $ao->role }}</td>
<td>{{ $ao->ftn }}</td>
<td>-</td>
</tr>
@endforeach
@endforeach
</tbody>
</table>
@ -85,7 +97,7 @@
<!-- Sign up -->
<div class="accordion-item">
<h3 class="accordion-header" id="signup" data-bs-toggle="collapse" data-bs-target="#collapse_signup" aria-expanded="true" aria-controls="collapse_signup">Join Network</h3>
<h3 class="accordion-header" id="signup" data-bs-toggle="collapse" data-bs-target="#collapse_signup" aria-expanded="false" aria-controls="collapse_signup">Join Network</h3>
<div id="collapse_signup" class="accordion-collapse collapse" aria-labelledby="signup" data-bs-parent="#accordion_homepage">
<div class="accordion-body">

View File

@ -5,6 +5,9 @@
<meta name="description" content="{{ $decription ?? '' }}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">

View File

@ -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')

View File

@ -6,7 +6,6 @@
<dd><a href="{{ url('ftn/domain') }}">Domains</a></dd>
<dd><a href="{{ url('ftn/system') }}">Systems</a></dd>
<dd><a href="{{ url('ftn/zone') }}" >Zones</a></dd>
<dd><a href="{{ url('ftn/node') }}" >Nodes</a></dd>
</dl>
@can('admin')

View File

@ -13,7 +13,7 @@
@endif
</ul>
<ul class="push-right">
<ul class="float-end">
@auth
<li><a href="{{ url('settings') }}"><span>{{ Auth::user()->name }}</span></a></li>
<li><a href="{{ url('logout') }}"><span>Logout</span></a></li>

View File

@ -5,127 +5,635 @@
@endsection
@section('content')
<form class="row g-0 needs-validation" method="post" novalidate>
@csrf
<div class="accordion accordion-flush" id="accordion_homepage">
<div class="row">
<div class="col-12">
<div class="greyframe titledbox shadow0xb0">
<h2 class="cap">@if($o->exists) Update @else Add @endif System</h2>
@if ($o->exists)
<!-- System -->
<div class="accordion-item">
<h3 class="accordion-header" id="system" data-bs-toggle="collapse" data-bs-target="#collapse_system" aria-expanded="true" aria-controls="collapse_system">System</h3>
<div class="row">
<div class="col-4">
<label for="name" class="form-label">Name</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-tag-fill"></i></span>
<input type="text" class="form-control @error('name') is-invalid @enderror" id="name" placeholder="Name" name="name" value="{{ old('name',$o->name) }}" required @cannot('admin',$o)disabled @endcannot autofocus>
<span class="invalid-feedback" role="alert">
@error('name')
{{ $message }}
@else
A name is required.
@enderror
</span>
</div>
</div>
<div id="collapse_system" class="accordion-collapse collapse {{ ! session()->has('add_address') ? 'show' : '' }}" aria-labelledby="system" data-bs-parent="#accordion_homepage">
<div class="accordion-body">
@endif
<form class="row g-0 needs-validation" method="post" novalidate>
@csrf
<div class="col-4">
<label for="active" class="form-label">Active</label>
<div class="input-group">
<div class="btn-group" role="group">
<input type="radio" class="btn-check" name="active" id="active_yes" value="1" required @cannot('admin',$o)disabled @endcannot @if(old('active',$o->active))checked @endif>
<label class="btn btn-outline-success" for="active_yes">Yes</label>
<div class="row">
<div class="col-12">
<div class="greyframe titledbox shadow0xb0">
<h2 class="cap">@if($o->exists) Update @else Add @endif System</h2>
<input type="radio" class="btn-check btn-danger" name="active" id="active_no" value="0" required @cannot('admin',$o)disabled @endcannot @if(! old('active',$o->active))checked @endif>
<label class="btn btn-outline-danger" for="active_no">No</label>
<div class="row">
<div class="col-4">
<label for="name" class="form-label">Name</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-tag-fill"></i></span>
<input type="text" class="form-control @error('name') is-invalid @enderror" id="name" placeholder="Name" name="name" value="{{ old('name',$o->name) }}" required @cannot('admin',$o)disabled @endcannot autofocus>
<span class="invalid-feedback" role="alert">
@error('name')
{{ $message }}
@else
A name is required.
@enderror
</span>
</div>
</div>
<div class="col-4">
<label for="active" class="form-label">Active</label>
<div class="input-group">
<div class="btn-group" role="group">
<input type="radio" class="btn-check" name="active" id="active_yes" value="1" required @cannot('admin',$o)disabled @endcannot @if(old('active',$o->active))checked @endif>
<label class="btn btn-outline-success" for="active_yes">Yes</label>
<input type="radio" class="btn-check btn-danger" name="active" id="active_no" value="0" required @cannot('admin',$o)disabled @endcannot @if(! old('active',$o->active))checked @endif>
<label class="btn btn-outline-danger" for="active_no">No</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-4">
<label for="sysop" class="form-label">Sysop</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-globe"></i></span>
<input type="text" class="form-control @error('sysop') is-invalid @enderror" id="sysop" placeholder="Sysop" name="sysop" value="{{ old('sysop',$o->sysop) }}" required @cannot('admin',$o)disabled @endcannot autocomplete="name">
<span class="invalid-feedback" role="alert">
@error('sysop')
{{ $message }}
@else
A Sysop's name is required.
@enderror
</span>
</div>
</div>
<div class="col-8">
<label for="location" class="form-label">Location</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-globe"></i></span>
<input type="text" class="form-control @error('location') is-invalid @enderror" id="location" placeholder="Location" name="location" value="{{ old('location',$o->location) }}" required @cannot('admin',$o)disabled @endcannot>
<span class="invalid-feedback" role="alert">
@error('location')
{{ $message }}
@else
System location is required.
@enderror
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-4">
<label for="method" class="form-label">Connection Method</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-wifi"></i></span>
<select class="form-select @error('method') is-invalid @enderror" id="method" name="method" @cannot('admin',$o)disabled @endcannot>
<option></option>
<option value="23" @if(old('method',$o->method) == 23)selected @endif)}}>Telnet</option>
<option value="22" @if(old('method',$o->method) == 22)selected @endif)}}>SSH</option>
<option value="519" @if(old('method',$o->method) == 519)selected @endif)}}>Rlogin</option>
</select>
</div>
</div>
<div class="col-8">
<label for="address" class="form-label">Address</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-globe"></i></span>
<input type="text" class="w-75 form-control @error('address') is-invalid @enderror" id="address" placeholder="FQDN" name="address" value="{{ old('address',$o->address) }}" @cannot('admin',$o)disabled @endcannot>
<input type="text" class="form-control @error('port') is-invalid @enderror" id="port" placeholder="Port" name="port" value="{{ old('port',$o->port) }}" @cannot('admin',$o)disabled @endcannot>
<span class="invalid-feedback" role="alert">
@error('address')
{{ $message }}
@enderror
@error('port')
{{ $message }}
@enderror
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<label for="notes" class="form-label">Notes</label>
<textarea class="form-control" rows=3 name="notes" placeholder="Notes..." @cannot('admin',$o)disabled @endcannot>{{ old('notes',$o->notes) }}</textarea>
</div>
</div>
<div class="row">
<div class="col-12">
<a href="{{ url('ftn/system') }}" class="btn btn-danger">Cancel</a>
@can('admin',$o)
<button type="submit" name="submit" class="btn btn-success mr-0 float-end">@if ($o->exists)Save @else Add @endif</button>
@endcan
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-4">
<label for="sysop" class="form-label">Sysop</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-globe"></i></span>
<input type="text" class="form-control @error('sysop') is-invalid @enderror" id="sysop" placeholder="Sysop" name="sysop" value="{{ old('sysop',$o->sysop) }}" required @cannot('admin',$o)disabled @endcannot autocomplete="name">
<span class="invalid-feedback" role="alert">
@error('sysop')
{{ $message }}
@else
A Sysop's name is required.
@enderror
</span>
</div>
</div>
<div class="col-8">
<label for="location" class="form-label">Location</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-globe"></i></span>
<input type="text" class="form-control @error('location') is-invalid @enderror" id="location" placeholder="Location" name="location" value="{{ old('location',$o->location) }}" required @cannot('admin',$o)disabled @endcannot>
<span class="invalid-feedback" role="alert">
@error('location')
{{ $message }}
@else
System location is required.
@enderror
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-4">
<label for="method" class="form-label">Connection Method</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-wifi"></i></span>
<select class="form-select @error('method') is-invalid @enderror" id="method" name="method" @cannot('admin',$o)disabled @endcannot>
<option></option>
<option value="23" @if(old('method',$o->method) == 23)selected @endif)}}>Telnet</option>
<option value="22" @if(old('method',$o->method) == 22)selected @endif)}}>SSH</option>
<option value="519" @if(old('method',$o->method) == 519)selected @endif)}}>Rlogin</option>
</select>
</div>
</div>
<div class="col-8">
<label for="address" class="form-label">Address</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-globe"></i></span>
<input type="text" class="form-control @error('address') is-invalid @enderror" id="address" placeholder="FQDN" name="address" value="{{ old('address',$o->address) }}" @cannot('admin',$o)disabled @endcannot>
<span class="m-0 p-0 input-group-text" style="background-color: #ffffff;">
<input type="text" class="form-control @error('port') is-invalid @enderror" id="port" placeholder="port" name="port" value="{{ old('port',$o->port) }}" @cannot('admin',$o)disabled @endcannot>
</span>
<span class="invalid-feedback" role="alert">
@error('address')
{{ $message }}
@enderror
@error('port')
{{ $message }}
@enderror
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<label for="notes" class="form-label">Notes</label>
<textarea class="form-control" rows=3 name="notes" placeholder="Notes..." @cannot('admin',$o)disabled @endcannot>{{ old('notes',$o->notes) }}</textarea>
</div>
</div>
<div class="row">
<div class="col-12">
<a href="{{ url('ftn/system') }}" class="btn btn-danger">Cancel</a>
@can('admin',$o)
<button type="submit" name="submit" class="btn btn-success mr-0 float-end">@if ($o->exists)Save @else Add @endif</button>
@endcan
</div>
</form>
@if ($o->exists)
</div>
</div>
</div>
</div>
</form>
@endif
@if($o->exists)
<!-- Authentication -->
<div class="accordion-item">
<h3 class="accordion-header" id="authentication" data-bs-toggle="collapse" data-bs-target="#collapse_auth" aria-expanded="false" aria-controls="collapse_auth">System Authentication</h3>
<div id="collapse_auth" class="accordion-collapse collapse" aria-labelledby="authentication" data-bs-parent="#accordion_homepage">
<div class="accordion-body">
TBA
</div>
</div>
</div>
<!-- Addresses -->
<div class="accordion-item">
<h3 class="accordion-header" id="addresses" data-bs-toggle="collapse" data-bs-target="#collapse_addresses" aria-expanded="false" aria-controls="collapse_addresses">System AKAs</h3>
<div id="collapse_addresses" class="accordion-collapse collapse {{ session()->has('add_address') ? 'show' : '' }}" aria-labelledby="addresses" data-bs-parent="#accordion_homepage">
<div class="accordion-body">
<p>FidoNet addresses are constructed in the format <strong class="highlight">zone</strong>:<strong class="highlight">net</strong>/<strong class="highlight">node</strong>.<strong class="highlight">point</strong><span>@</span><strong class="highlight">domain</strong>.</p>
<div class="accordion accordion-flush" id="accordion_ftnaddress">
<h4 class="accordion-header" id="ftnaddress" data-bs-toggle="collapse" data-bs-target="#collapse_ftnaddresses" aria-expanded="false" aria-controls="collapse_addresses">Fidonet Addressing</h4>
<div id="collapse_ftnaddresses" class="accordion-collapse collapse" aria-labelledby="ftnaddress" data-bs-parent="#accordion_ftnaddress">
<div class="accordion-body">
<p>FidoNet system are also assigned some roles, and in some cases, those roles have a paritcular address format:</p>
<table class="table monotable">
<thead>
<tr>
<th>Role</th>
<th>Address Format</th>
</tr>
</thead>
<tbody>
<tr>
<td>Zone <small>(optional)</small></td>
<td>
<strong class="highlight">ZONE</strong>:0/0.0, where the net, node and point values are zero. Zones normally have 1 or more Regions and/or Hosts.<br><br>
<small>(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.)</small>
</td>
</tr>
<tr>
<td>Region <small>(optional)</small></td>
<td>
zone:<strong class="highlight">REGION</strong>/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.<br><br>
<small>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.</small>
</td>
</tr>
<tr>
<td>Host <small>(mandatory)</small></td>
<td>
zone:<strong class="highlight">HOST</strong>/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.<br><br>
<small>The host number must be unique within a zone, which implies that it cannot be the same as a region, if regions are used.</small>
</td>
</tr>
<tr>
<td>Hub <small>(optional)</small></td>
<td>
zone:net/<strong class="highlight">NODE</strong>.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.<br><br>
<small>The node is unique within the net and point is zero.</small>
</td>
</tr>
<tr>
<td>Node <small>(required)</small></td>
<td>
zone:net/<strong class="highlight">NODE</strong>.0, where the zone/net indicates which zone/net the node is in.<br><br>
<small>The node is unique within the net and point is zero.</small>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
@if($o->addresses->count())
<p>This system has the following addresses assigned to it:</p>
<table class="table monotable">
<thead>
<tr>
<th>Address</th>
<th>Active</th>
<th>Role</th>
</tr>
</thead>
<tbody>
@foreach ($o->addresses->sortBy(['region_id','host_id']) as $oo)
<tr>
<td>{{ $oo->ftn }}</td>
<td>{{ $oo->active ? 'YES' : 'NO' }}</td>
<td>{{ $oo->role }}</td>
</tr>
@endforeach
</tbody>
</table>
@endif
@can('admin',$o)
<form class="row g-0 needs-validation" method="post" action="{{ url('ftn/system/addaddress',$o->id) }}" novalidate>
<input type="hidden" id="action" name="action" value="">
@csrf
<div class="row pt-0">
<div class="col-12">
<div class="greyframe titledbox shadow0xb0">
<h2 class="cap">Assign New address</h2>
<div class="row">
<!-- Select Zone -->
<div class="col-3">
<label for="zone_id" class="form-label">Zone</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<select class="form-select @error('zone_id') is-invalid @enderror" id="zone_id" name="zone_id" required>
<option></option>
@foreach(\App\Models\Zone::active()->with(['domain'])->get() as $zo)
<option value="{{ $zo->id }}">{{ $zo->zone_id }} <small>({{ $zo->domain->name }})</small></option>
@endforeach
</select>
<span class="invalid-feedback" role="alert">
@error('zone_id')
{{ $message }}
@else
Please select the Zone for the node's address.
@enderror
</span>
</div>
</div>
<!-- Select Region -->
<div class="col-3 d-none" id="region-select">
<label for="region_id" class="form-label">Region</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-geo"></i></span>
<select class="form-select @error('region_id') is-invalid @enderror" id="region_id" name="region_id" required>
</select>
<span class="invalid-feedback" role="alert">
@error('region_id')
{{ $message }}
@else
Please make a choice.
@enderror
</span>
</div>
</div>
<!-- Select Host -->
<div class="col-3 d-none" id="host-select">
<label for="host_id" class="form-label">Host</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-diagram-3-fill"></i></span>
<select class="form-select @error('host_id') is-invalid @enderror" id="host_id" name="host_id">
</select>
<span class="invalid-feedback" role="alert">
@error('host_id')
{{ $message }}
@enderror
</span>
</div>
</div>
<!-- Select Hub -->
<div class="col-3 d-none" id="hub-select">
<label for="hub_id" class="form-label">Hub</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-diagram-2-fill"></i></span>
<select class="form-select @error('hub_id') is-invalid @enderror" id="hub_id" name="hub_id">
</select>
<span class="invalid-feedback" role="alert">
@error('hub_id')
{{ $message }}
@enderror
</span>
</div>
</div>
</div>
<div class="row">
<!-- Node/Point address -->
<div class="col-3 d-none" id="node-address">
<label for="node_id" class="form-label">Node/Point Address</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<input type="text" style="width: 35%;" class="form-control @error('node_id') is-invalid @enderror" id="node_id" placeholder="Node" name="node_id" value="{{ old('node_id',$o->node_id) }}" @cannot('admin',$o)disabled @endcannot>
<span class="input-group-text ml-1 mr-1 p-0">.</span>
<input type="text" class="form-control @error('point_id') is-invalid @enderror" id="point_id" placeholder="0" name="point_id" value="{{ old('point_id',$o->point_id) ?: 0 }}" @cannot('admin',$o)disabled @endcannot style="padding-left: 0;">
<span class="invalid-feedback" role="alert">
@error('node_id')
{{ $message }}
@enderror
@error('point_id')
{{ $message }}
@enderror
</span>
</div>
</div>
<!-- Region Address -->
<div class="pl-0 col-3 d-none" id="region-address" >
<label for="region" class="form-label">Region Address</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<input type="text" style="width: 35%;" class="form-control @error('region_id_new') is-invalid @enderror" id="region_id_new" placeholder="Region #" name="region_id_new" value="{{ old('region_id_new') }}" @cannot('admin',$o)disabled @endcannot>
<span class="input-group-text">/0.0</span>
<span class="invalid-feedback" role="alert">
@error('region_id_new')
{{ $message }}
@else
The region number is required.
@enderror
</span>
</div>
</div>
<!-- Host Address -->
<div class="pl-0 col-3 d-none" id="host-address">
<label for="host" class="form-label">Host Address</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<input type="text" class="form-control @error('host_id_new') is-invalid @enderror" id="host_id_new" placeholder="Host #" name="host_id_new" value="{{ old('host_id_new') }}" @cannot('admin',$o)disabled @endcannot>
<span class="input-group-text ml-1 mr-1 p-0">/</span>
<input type="text" class="form-control @error('node_id_new') is-invalid @enderror" id="node_id_new" placeholder="Node #" name="node_id_new" value="{{ old('node_id_new') }}" @cannot('admin',$o)disabled @endcannot>
<span class="input-group-text">.0</span>
<span class="invalid-feedback" role="alert">
@error('host_id_new')
{{ $message }}
@else
The host address is required.
@enderror
@error('node_id_new')
{{ $message }}
@else
The node address is required.
@enderror
</span>
</div>
</div>
<!-- Hub Checkbox -->
<div class="col-2 d-none" id="hub-checkbox">
<label for="hub" class="form-label">Hub</label>
<div class="input-group">
<div class="btn-group" role="group">
<input type="radio" class="btn-check" name="hub" id="hub_yes" value="1" required @cannot('admin',$o)disabled @endcannot @if(old('hub',$o->hub))checked @endif>
<label class="btn btn-outline-success" for="hub_yes">Yes</label>
<input type="radio" class="btn-check btn-danger" name="hub" id="hub_no" value="0" required @cannot('admin',$o)disabled @endcannot @if(! old('hub',$o->hub))checked @endif>
<label class="btn btn-outline-danger" for="hub_no">No</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-2">
<a href="{{ url('ftn/system') }}" class="btn btn-danger">Cancel</a>
</div>
<span class="col-6 mt-auto mx-auto text-center align-bottom">
@if($errors->count())
<span class="pl-5 btn btn-sm btn-danger" role="alert">
There were errors with the submission.
@dump($errors)
</span>
@endif
</span>
@can('admin',$o)
<div class="col-2">
<button type="submit" name="submit" class="btn btn-success mr-0 float-end">Add</button>
</div>
@endcan
</div>
</div>
</div>
</div>
</div>
</form>
@else
This system does not currently belong to any Fido networks. You'll need to ask an admin to assign addresses.
@endcan
</div>
</div>
</div>
@endif
</div>
@endsection
@section('page-scripts')
<script type="text/javascript">
$(document).ready(function() {
/*
// Disable enter for form submission.
$('input').on('keydown', function(event) {
var x = event.which;
if (x === 13) {
event.preventDefault();
}
});
*/
$('#zone_id').on('change',function() {
$(this).parent().find('i').addClass('spinner-grow spinner-grow-sm');
$('#region_id').prop('disabled',true);
$('#region_id').children().remove();
if (! $('#region-address').hasClass('d-none'))
$('#region-address').addClass('d-none');
if (! $('#host-address').hasClass('d-none'))
$('#host-address').addClass('d-none');
if (! $('#host-select').hasClass('d-none'))
$('#host-select').addClass('d-none');
if (! $('#hub-select').hasClass('d-none'))
$('#hub-select').addClass('d-none')
if (! $('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').addClass('d-none');
if (! $('#node-address').hasClass('d-none'))
$('#node-address').addClass('d-none');
$.get('{{ url('api/regions') }}'+'/'+this.value,function(data) {
$('#region_id').append('<option value=""></option>');
$('#region_id').append('<option value="no">No Region</option>');
$('#region_id').append('<option value="new">New Region</option>');
data.forEach(function(item) {
$('#region_id').append('<option value="'+item.id+'">'+item.id+':'+item.value+'</option>');
});
$('#region_id').prop('disabled',false);
$('#region_id').show();
});
$('#region-select').removeClass('d-none');
$(this).parent().find('i').removeClass('spinner-grow spinner-grow-sm');
});
$('#region_id').on('change',function() {
switch(this.value) {
case 'new':
if (! $('#host-select').hasClass('d-none'))
$('#host-select').addClass('d-none');
if (! $('#host-address').hasClass('d-none'))
$('#host-address').addClass('d-none');
if ($('#region-address').hasClass('d-none'))
$('#region-address').removeClass('d-none');
if (! $('#hub-select').hasClass('d-none'))
$('#hub-select').addClass('d-none')
if (! $('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').addClass('d-none');
if (! $('#node-address').hasClass('d-none'))
$('#node-address').addClass('d-none');
$('#action').val('region');
break;
case 'no':
default:
// Find Hosts
if ($('#host-select').hasClass('d-none'))
$('#host-select').removeClass('d-none');
if (! $('#region-address').hasClass('d-none'))
$('#region-address').addClass('d-none');
if (! $('#hub-select').hasClass('d-none'))
$('#hub-select').addClass('d-none')
if (! $('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').addClass('d-none');
if (! $('#host-address').hasClass('d-none'))
$('#host-address').addClass('d-none');
if (! $('#node-address').hasClass('d-none'))
$('#node-address').addClass('d-none');
$(this).parent().find('i').addClass('spinner-grow spinner-grow-sm');
$('#host_id').prop('disabled',true);
$('#host_id').children().remove();
var that = this;
$.get('{{ url('api/hosts') }}'+'/'+$('#zone_id').val()+'/'+((this.value === 'no') ? 0 : this.value),function(data) {
$('#host_id').append('<option value=""></option>');
if (that.value !== 'no')
$('#host_id').append('<option value="no">No Host</option>');
$('#host_id').append('<option value="new">New Host</option>');
data.forEach(function(item) {
$('#host_id').append('<option value="'+item.id+'">'+item.id+':'+item.value+'</option>');
});
$('#host_id').prop('disabled',false);
$('#host_id').show();
});
$('#host-select').removeClass('d-none');
$(this).parent().find('i').removeClass('spinner-grow spinner-grow-sm');
}
});
$('#host_id').on('change',function() {
switch(this.value) {
case 'new':
if ($('#host-address').hasClass('d-none'))
$('#host-address').removeClass('d-none');
if (! $('#hub-select').hasClass('d-none'))
$('#hub-select').addClass('d-none')
if (! $('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').addClass('d-none');
if (! $('#node-address').hasClass('d-none'))
$('#node-address').addClass('d-none');
$('#action').val('host');
break;
case 'no':
default:
if (! $('#host-address').hasClass('d-none'))
$('#host-address').addClass('d-none');
if ($('#hub-select').hasClass('d-none'))
$('#hub-select').removeClass('d-none');
if ($('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').removeClass('d-none');
if ($('#node-address').hasClass('d-none'))
$('#node-address').removeClass('d-none');
$(this).parent().find('i').addClass('spinner-grow spinner-grow-sm');
$('#hub_id').prop('disabled',true);
$('#hub_id').children().remove();
$.get('{{ url('api/hubs') }}'+'/'+$('#zone_id').val()+'/'+((this.value === 'no') ? 0 : this.value),function(data) {
$('#hub_id').append('<option value="no">No Hub</option>');
data.forEach(function(item) {
$('#hub_id').append('<option value="'+item.id+'">'+item.id+':'+item.value+'</option>');
});
$('#hub_id').prop('disabled',false);
$('#hub_id').show();
});
$('#hub-select').removeClass('d-none');
$(this).parent().find('i').removeClass('spinner-grow spinner-grow-sm');
$('#action').val('node');
}
});
$('#hub_id').on('change',function() {
switch(this.value) {
case 'no':
if ($('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').removeClass('d-none');
break;
default:
if (! $('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').addClass('d-none');
}
});
$('#point_id').on('change',function() {
switch(this.value) {
case '0':
if ($('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').removeClass('d-none');
break;
default:
if (! $('#hub-checkbox').hasClass('d-none'))
$('#hub-checkbox').addClass('d-none');
}
});
});
</script>
@append

View File

@ -7,7 +7,7 @@
<div class="row">
<div class="col-12">
<h2>About FTN Zones</h2>
<p>In FTN network addresses, a zone is the 3rd dimension and used when a system supports 3D (or better) addressing, ie: <strong class="highlight">zone</strong>:hub/host.point@domain.</p>
<p>In FTN network addresses, a zone is the 3rd dimension and used when a system supports 3D (or better) addressing, ie: <strong class="highlight">zone</strong>:net/node.point@domain.</p>
<p>Zones are used with domains to uniquely identify a FTN network. Within an FTN network there can be multiple zones with the same domain.</p>
<p>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.</p>
</div>

View File

@ -1,6 +1,7 @@
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\{DomainController};
/*
|--------------------------------------------------------------------------
@ -13,6 +14,8 @@ use Illuminate\Http\Request;
|
*/
#Route::middleware('auth:api')->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']);
});

View File

@ -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'])