Merge pull request #81 from cp6/Development

Development 2.1.9
This commit is contained in:
corbpie 2022-12-02 14:35:14 +11:00 committed by GitHub
commit 159d17e77b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 584 additions and 33 deletions

3
.gitattributes vendored
View File

@ -3,3 +3,6 @@
*.scss linguist-vendored
*.js linguist-vendored
CHANGELOG.md export-ignore
/public/css/* -diff
/public/js/* -diff

8
.gitignore vendored
View File

@ -57,7 +57,7 @@ fabric.properties
# .idea/misc.xml
# *.ipr
storage/clockwork/
public/css/
public/js/
public/fonts/
public/webfonts/
#public/css/
#public/js/
#public/fonts/
#public/webfonts/

View File

@ -18,20 +18,9 @@ GeekBench 5 scores to do easier comparing and sorting.
## Project sponsor
[Cloud Five Limited](https://cloud-v.net/) for providing the hosting for demo installation.
Currently seeking a project sponsor
## 2.1.8 changes (20th October 2022):
#### You can no longer use the form to submit YABS results
yabs.sh now has JSON formatted response and can POST the output directly from calling the script.
With My idlers you can use your API key and the server id to directly POST the benchmark result
`http://domain.com/api/yabs/tnSJLyhz/USERAPIKEYISHERE`
Example yabs.sh call to POST the result
`curl -sL yabs.sh | bash -s -- -s "http://domain.com/api/yabs/tnSJLyhz/USERAPIKEYISHERE"`
## 2.1.9 changes (2nd December 2022):
### NPM / Laravel mix now in use
@ -43,18 +32,15 @@ npm run prod
#### Please run the following if updating from existing install:
```shell
php artisan migrate
php artisan route:cache
php artisan cache:clear
```
* Added & implemented details footer blade component
* Added new index layout
* Updated domains, misc, reseller, seedboxes and shared use new index layout
* Updated validation for store and update
* Updated `updatePricing()` to not need `$as_usd` parameter
* Updated labels assigned insert
* Updated order/sort by methods for pricing related columns
* Removed add YABS button on servers index page
* Added & implemented NPM webpack
* Added compiled assets
* Added notes (Servers, shared, reseller, domains, DNS and IPs)
* Fixed create views default provider is no longer the former sponsor
## Requires
@ -78,6 +64,7 @@ php artisan cache:clear
* Assign labels
* Assign server type (KVM, OVZ, LXC & dedi)
* Easy to edit values
* Assign notes
## Install
@ -120,6 +107,18 @@ Run with a single click on [PikaPods.com](https://www.pikapods.com/)
[![PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=my-idlers)
## Adding a YABS benchmark
yabs.sh now has JSON formatted response and can POST the output directly from calling the script.
With My idlers you can use your API key and the server id to directly POST the benchmark result
`https://yourdomain.com/api/yabs/SERVERID/USERAPIKEYISHERE`
Example yabs.sh call to POST the result:
`curl -sL yabs.sh | bash -s -- -s "https://yourdomain.com/api/yabs/SERVERID/USERAPIKEYISHERE"`
## API endpoints
For GET requests the header must have `Accept: application/json` and your API token (found at `/account`)

View File

@ -0,0 +1,124 @@
<?php
namespace App\Http\Controllers;
use App\Models\DNS;
use App\Models\Domains;
use App\Models\IPs;
use App\Models\Note;
use App\Models\Reseller;
use App\Models\SeedBoxes;
use App\Models\Server;
use App\Models\Shared;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class NoteController extends Controller
{
public function index()
{
$notes = Note::allNotes();
return view('notes.index', compact('notes'));
}
public function create()
{
$servers = Server::all();
$shareds = Shared::all();
$resellers = Reseller::all();
$domains = Domains::all();
$dns = DNS::all();
$ips = IPs::all();
return view('notes.create', compact(['servers', 'shareds', 'resellers', 'domains', 'dns', 'ips']));
}
public function store(Request $request)
{
$request->validate([
'service_id' => 'required|string|size:8',
'note' => 'required|string',
]);
try {
$note_id = Str::random(8);
$a = Note::create([
'id' => $note_id,
'service_id' => $request->service_id,
'note' => $request->note
]);
} catch (\Exception $e) {
if ($e->getCode() === "23000") {
$message = "A note already exists for this service";
} else {
$message = "Error inserting note";
}
return redirect()->route('notes.create')
->withInput($request->input())->with('error', $message);
}
Cache::forget('all_notes');
return redirect()->route('notes.index')
->with('success', 'Note created successfully.');
}
public function edit(Note $note)
{
$note = Note::note($note->service_id);
$servers = Server::all();
$shareds = Shared::all();
$resellers = Reseller::all();
$domains = Domains::all();
$dns = DNS::all();
$ips = IPs::all();
return view('notes.edit', compact(['note', 'servers', 'shareds', 'resellers', 'domains', 'dns', 'ips']));
}
public function update(Request $request, Note $note)
{
$request->validate([
'service_id' => 'required|string|size:8',
'note' => 'required|string'
]);
$note->update([
'service_id' => $request->service_id,
'note' => $request->note
]);
Cache::forget('all_notes');
Cache::forget("note.$note->service_id");
return redirect()->route('notes.index')
->with('success', 'Note was updated successfully.');
}
public function show(Note $note)
{
$note = Note::note($note->service_id);
return view('notes.show', compact(['note']));
}
public function destroy(Note $note)
{
if ($note->delete()) {
Cache::forget("all_notes");
Cache::forget("note.$note->service_id");
return redirect()->route('notes.index')
->with('success', 'Note was deleted successfully.');
}
return redirect()->route('notes.index')
->with('error', 'Note was not deleted.');
}
}

View File

@ -25,4 +25,10 @@ class DNS extends Model
return DB::table('d_n_s')->count();
});
}
public function note(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(Note::class, 'service_id', 'id');
}
}

View File

@ -56,4 +56,9 @@ class Domains extends Model
return $this->hasMany(LabelsAssigned::class, 'service_id', 'id');
}
public function note(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(Note::class, 'service_id', 'id');
}
}

View File

@ -47,4 +47,9 @@ class IPs extends Model
});
}
public function note(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(Note::class, 'service_id', 'id');
}
}

66
app/Models/Note.php Normal file
View File

@ -0,0 +1,66 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Session;
class Note extends Model
{
use HasFactory;
public $incrementing = false;
protected $table = 'notes';
protected $keyType = 'string';
protected $fillable = ['id', 'service_id', 'note'];
public static function note(string $service_id): Note
{
return Cache::remember("note.$service_id", now()->addMonth(1), function () use ($service_id) {
return self::where('service_id', $service_id)->with(['server', 'shared', 'reseller', 'domain', 'dns', 'ip'])->first();
});
}
public static function allNotes()
{
return Cache::remember("all_notes", now()->addMonth(1), function () {
return self::with(['server', 'shared', 'reseller', 'domain', 'dns', 'ip'])->orderBy('created_at', 'desc')->get();
});
}
public function server(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Server::class, 'service_id', 'id');
}
public function shared(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Shared::class, 'service_id', 'id');
}
public function reseller(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Reseller::class, 'service_id', 'id');
}
public function domain(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Domains::class, 'service_id', 'id');
}
public function dns(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(DNS::class, 'service_id', 'id');
}
public function ip(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(IPs::class, 'service_id', 'id');
}
}

View File

@ -78,4 +78,9 @@ class Reseller extends Model
return $this->hasMany(LabelsAssigned::class, 'service_id', 'id');
}
public function note(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(Note::class, 'service_id', 'id');
}
}

View File

@ -234,4 +234,9 @@ class Server extends Model
return $this->hasMany(LabelsAssigned::class, 'service_id', 'id');
}
public function note(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(Note::class, 'service_id', 'id');
}
}

View File

@ -78,4 +78,9 @@ class Shared extends Model
return $this->hasMany(LabelsAssigned::class, 'service_id', 'id');
}
public function note(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(Note::class, 'service_id', 'id');
}
}

View File

@ -0,0 +1,23 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('notes', function (Blueprint $table) {
$table->char('id', 8)->primary();
$table->char('service_id', 8)->unique();
$table->text('note');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('notes');
}
};

BIN
public/css/app.css vendored

Binary file not shown.

BIN
public/css/dark.css vendored Normal file

Binary file not shown.

BIN
public/css/light.css vendored Normal file

Binary file not shown.

BIN
public/js/app.js vendored

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,6 @@
{
"/js/app.js": "/js/app.js",
"/css/app.css": "/css/app.css"
"/css/app.css": "/css/app.css",
"/css/dark.css": "/css/dark.css",
"/css/light.css": "/css/light.css"
}

View File

@ -88,6 +88,12 @@
</table>
</div>
</div>
<div class="col-12 col-lg-6">
@if(isset($dns->note))
<p class="font-bold text-muted mt-3 mb-1 pb-0">Note:</p>
<p class="pt-0">{{$dns->note->note}}</p>
@endif
</div>
</div>
<a href="{{ route('dns.index') }}"
class="btn btn-success btn-sm mx-2">

View File

@ -52,7 +52,7 @@
<div class="row">
<div class="col-md-3 mb-3">
<x-providers-select>
<x-slot name="current">10</x-slot>
<x-slot name="current">{{random_int(1,98)}}</x-slot>
</x-providers-select>
</div>
<div class="col-12 col-md-3 mb-3">

View File

@ -85,6 +85,12 @@
</table>
</div>
</div>
<div class="col-12 col-lg-6">
@if(isset($domain_info->note))
<p class="font-bold text-muted mt-3 mb-1 pb-0">Note:</p>
<p class="pt-0">{{$domain_info->note->note}}</p>
@endif
</div>
</div>
<x-back-btn>
<x-slot name="route">{{ route('domains.index') }}</x-slot>

View File

@ -38,6 +38,7 @@
<li><a class="dropdown-item" href="{{route('providers.index')}}">Providers</a></li>
<li><a class="dropdown-item" href="{{route('seedboxes.index')}}">Seedboxes</a></li>
<li><a class="dropdown-item" href="{{route('yabs.index')}}">YABS</a></li>
<li><a class="dropdown-item" href="{{route('notes.index')}}">Notes</a></li>
<li><a class="dropdown-item" href="{{route('settings.index')}}">Settings</a></li>
<li><a class="dropdown-item" href="{{route('account.index')}}">Account</a></li>
</ul>

View File

@ -0,0 +1,67 @@
@section("title", "Add a note")
<x-app-layout>
<x-slot name="header">
{{ __('Create a note') }}
</x-slot>
<div class="container">
<x-card class="shadow mt-3">
<x-back-button>
<x-slot name="href">{{ route('notes.index') }}</x-slot>
Go back
</x-back-button>
<x-response-alerts></x-response-alerts>
<form action="{{ route('notes.store') }}" method="POST">
@csrf
<div class="row">
<div class="col-12 mb-4">
<textarea class="form-control" id="note" name="note" rows="6">{{ old('note') }}</textarea>
</div>
</div>
<div class="row">
<p>This note is for:</p>
<div class="col-12 col-md-6 mb-3">
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text">Service</span></div>
<select class="form-control" name="service_id">
@foreach ($servers as $server)
<option value="{{ $server['id'] }}">
{{ $server['hostname'] }} (Server)
</option>
@endforeach
@foreach ($shareds as $shared)
<option value="{{ $shared['id'] }}">
{{ $shared['main_domain'] }} (Shared)
</option>
@endforeach
@foreach ($resellers as $reseller)
<option value="{{ $reseller['id'] }}">
{{ $reseller['main_domain'] }} (Reseller)
</option>
@endforeach
@foreach ($domains as $seed_box)
<option value="{{ $seed_box['id'] }}">
{{ $seed_box['domain'] }}.{{ $seed_box['extension'] }} (Domain)
</option>
@endforeach
@foreach ($dns as $dn)
<option value="{{ $dn['id'] }}">
{{ $dn['dns_type'] }} {{ $dn['hostname'] }} {{ $dn['address'] }} (DNS)
</option>
@endforeach
@foreach ($ips as $ip)
<option value="{{ $ip['id'] }}">
{{ $ip['address'] }} (IP)
</option>
@endforeach
</select></div>
</div>
</div>
<div class="row">
<div class="col-12 col-lg-4">
<x-submit-button>Create note</x-submit-button>
</div>
</div>
</form>
</x-card>
</div>
</x-app-layout>

View File

@ -0,0 +1,74 @@
@section("title", "Edit note")
<x-app-layout>
<x-slot name="header">
{{ __('Edit note') }}
</x-slot>
<div class="container">
<x-card class="shadow mt-3">
<x-back-button>
<x-slot name="href">{{ route('notes.index') }}</x-slot>
Go back
</x-back-button>
<x-response-alerts></x-response-alerts>
<form action="{{ route('notes.update', $note->id) }}" method="POST">
@csrf
@method('PUT')
<div class="row">
<div class="col-12 mb-4">
<textarea class="form-control" id="note" name="note" rows="6">{{ $note->note }}</textarea>
</div>
</div>
<div class="row">
<p>This note is for:</p>
<div class="col-12 col-md-6 mb-3">
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text">Service</span></div>
<select class="form-control" name="service_id">
@foreach ($servers as $server)
<option value="{{ $server['id'] }}"
@if ($server['id'] === $note->service_id) selected @endif>
{{ $server['hostname'] }} (Server)
</option>
@endforeach
@foreach ($shareds as $shared)
<option value="{{ $shared['id'] }}"
@if ($shared['id'] === $note->service_id) selected @endif>
{{ $shared['main_domain'] }} (Shared)
</option>
@endforeach
@foreach ($resellers as $reseller)
<option value="{{ $reseller['id'] }}"
@if ($reseller['id'] === $note->service_id) selected @endif>
{{ $reseller['main_domain'] }} (Reseller)
</option>
@endforeach
@foreach ($domains as $seed_box)
<option value="{{ $seed_box['id'] }}"
@if ($seed_box['id'] === $note->service_id) selected @endif>
{{ $seed_box['domain'] }}.{{ $seed_box['extension'] }} (Domain)
</option>
@endforeach
@foreach ($dns as $dn)
<option value="{{ $dn['id'] }}"
@if ($dn['id'] === $note->service_id) selected @endif>
{{ $dn['dns_type'] }}.{{ $dn['hostname'] }} (DNS)
</option>
@endforeach
@foreach ($ips as $ip)
<option value="{{ $ip['id'] }}"
@if ($ip['id'] === $note->service_id) selected @endif>
{{ $ip['address'] }} (IP)
</option>
@endforeach
</select></div>
</div>
</div>
<div class="row">
<div class="col-12 col-lg-4">
<x-submit-button>Update note</x-submit-button>
</div>
</div>
</form>
</x-card>
</div>
</x-app-layout>

View File

@ -0,0 +1,90 @@
@section('title', 'Notes')
<x-app-layout>
<x-slot name="header">
{{ __('Notes') }}
</x-slot>
<div class="container" id="app">
<x-delete-confirm-modal></x-delete-confirm-modal>
<x-card class="shadow mt-3">
<a href="{{ route('notes.create') }}" class="btn btn-primary mb-3">Add a note</a>
<x-response-alerts></x-response-alerts>
<div class="table-responsive">
<table class="table table-bordered">
<thead class="table-light">
<tr>
<th class="text-nowrap">Service</th>
<th class="text-nowrap">Type</th>
<th class="text-nowrap">Note Preview</th>
<th class="text-nowrap">Actions</th>
</tr>
</thead>
<tbody>
@if(!empty($notes[0]))
@foreach($notes as $n)
<tr>
<td class="text-nowrap">
@if(!is_null($n->server))
{{$n->server->hostname}}
@elseif(!is_null($n->shared))
{{$n->shared->main_domain}}
@elseif(!is_null($n->reseller))
{{$n->reseller->main_domain}}
@elseif(!is_null($n->domain))
{{$n->domain->domain}}.{{$n->domain->extension}}
@elseif(!is_null($n->dns))
{{$n->dns->dns_type}} {{$n->dns->hostname}}
@elseif(!is_null($n->ip))
{{$n->ip->address}}
@endif
</td>
<td class="text-nowrap">
@if(!is_null($n->server))
SERVER
@elseif(!is_null($n->shared))
SHARED
@elseif(!is_null($n->reseller))
RESELLER
@elseif(!is_null($n->domain))
DOMAIN
@elseif(!is_null($n->dns))
DNS
@elseif(!is_null($n->ip))
IP
@endif
</td>
<td class="text-nowrap">{{strlen($n->note) > 80 ? substr($n->note,0,80)."" : $n->note}}</td>
<td class="text-nowrap">
<form action="{{ route('notes.destroy', $n->id) }}" method="POST">
<a href="{{ route('notes.edit', $n->id) }}"
class="text-body mx-1">
<i class="fas fa-pen" title="edit"></i></a>
<a href="{{ route('notes.show', $n->id) }}"
class="text-body mx-1">
<i class="fas fa-eye" title="view"></i></a>
@csrf
@method('DELETE')
<i class="fas fa-trash text-danger ms-3" @click="confirmDeleteModal"
id="{{$n->id}}"
title="{{strlen($n->note) > 24 ? substr($n->note,0,24)."" : $n->note}}"></i>
</form>
</td>
</tr>
@endforeach
@else
<tr>
<td class="border text-red-500">No notes found.</td>
<td></td>
<td></td>
<td></td>
</tr>
@endif
</tbody>
</table>
</div>
</x-card>
<x-details-footer></x-details-footer>
</div>
<x-modal-delete-script>
<x-slot name="uri">notes</x-slot>
</x-modal-delete-script>
</x-app-layout>

View File

@ -0,0 +1,40 @@
@section("title", "Note $note->id")
<x-app-layout>
<x-slot name="header">
{{ __('Note') }}
</x-slot>
<div class="container">
<x-card class="shadow mt-3">
<div class="row">
<div class="col-12 mb-2">
<h2>
@if(!is_null($note->server))
{{$note->server->hostname}} (server)
@elseif(!is_null($note->shared))
{{$note->shared->main_domain}} (shared)
@elseif(!is_null($note->reseller))
{{$note->reseller->main_domain}} (reseller)
@elseif(!is_null($note->domain))
{{$note->domain->domain}}.{{$note->domain->extension}} (domain)
@elseif(!is_null($note->dns))
{{$note->dns->dns_type}} {{$note->dns->hostname}} (DNS)
@elseif(!is_null($note->ip))
{{$note->ip->address}} (IP)
@endif
</h2>
</div>
</div>
<div class="row mb-4">
<div class="col-12 px-lg-4">
<code>{{$note->note}}</code>
</div>
</div>
<x-back-btn>
<x-slot name="route">{{ route('notes.index') }}</x-slot>
</x-back-btn>
<x-edit-btn>
<x-slot name="route">{{ route('notes.edit', $note->id) }}</x-slot>
</x-edit-btn>
</x-card>
</div>
</x-app-layout>

View File

@ -47,7 +47,7 @@
<div class="row">
<div class="col-md-3 mb-3">
<x-providers-select>
<x-slot name="current">10</x-slot>
<x-slot name="current">{{random_int(1,98)}}</x-slot>
</x-providers-select>
</div>
<div class="col-md-3 mb-3">

View File

@ -133,6 +133,12 @@
<td class="px-2 py-2 font-bold text-muted">FTP Limit</td>
<td>{{$reseller->ftp_limit}}</td>
</tr>
@if(isset($reseller->note))
<tr>
<td class="px-2 py-2 font-bold text-muted">Note:</td>
<td>{{$reseller->note->note}}</td>
</tr>
@endif
</tbody>
</table>

View File

@ -45,7 +45,7 @@
<div class="row">
<div class="col-md-3 mb-3">
<x-providers-select>
<x-slot name="current">10</x-slot>
<x-slot name="current">{{random_int(1,98)}}</x-slot>
</x-providers-select>
</div>
<div class="col-md-3 mb-3">

View File

@ -110,7 +110,7 @@
<div class="row">
<div class="col-md-3 mb-3">
<x-providers-select>
<x-slot name="current">10</x-slot>
<x-slot name="current">{{random_int(1,98)}}</x-slot>
</x-providers-select>
</div>
<div class="col-md-3 mb-3">

View File

@ -210,6 +210,10 @@
@endif
</div>
<p id="yabs_code" class="d-none pt-3"><code>curl -sL yabs.sh | bash -s -- -s "{{route('api.store-yabs', [$server_data->id, \Illuminate\Support\Facades\Auth::user()->api_token])}}"</code></p>
@if(isset($server_data->note))
<p class="font-bold text-muted mt-3 mb-1 pb-0">Note:</p>
<p class="pt-0">{{$server_data->note->note}}</p>
@endif
</div>
</x-card>
<x-details-footer></x-details-footer>

View File

@ -47,7 +47,7 @@
<div class="row">
<div class="col-md-3 mb-3">
<x-providers-select>
<x-slot name="current">10</x-slot>
<x-slot name="current">{{random_int(1,98)}}</x-slot>
</x-providers-select>
</div>
<div class="col-md-3 mb-3">

View File

@ -20,7 +20,7 @@
</div>
</div>
<div class="row">
<div class="'col-12 col-lg-6">
<div class="col-12 col-lg-6">
<div class="table-responsive">
<table class="table table-borderless text-nowrap">
<tbody>
@ -97,7 +97,7 @@
</table>
</div>
</div>
<div class="'col-12 col-lg-6">
<div class="col-12 col-lg-6">
<table class="table table-borderless">
<tbody>
<tr>
@ -128,6 +128,12 @@
<td class="px-2 py-2 font-bold text-muted">FTP Limit</td>
<td>{{$shared->ftp_limit}}</td>
</tr>
@if(isset($shared->note))
<tr>
<td class="px-2 py-2 font-bold text-muted">Note:</td>
<td>{{$shared->note->note}}</td>
</tr>
@endif
</tbody>
</table>

View File

@ -7,6 +7,7 @@ use App\Http\Controllers\IPsController;
use App\Http\Controllers\LabelsController;
use App\Http\Controllers\LocationsController;
use App\Http\Controllers\MiscController;
use App\Http\Controllers\NoteController;
use App\Http\Controllers\OsController;
use App\Http\Controllers\ProvidersController;
use App\Http\Controllers\ResellerController;
@ -64,6 +65,8 @@ Route::resource('shared', SharedController::class)->middleware(['auth']);
Route::resource('yabs', YabsController::class)->middleware(['auth']);
Route::resource('notes', NoteController::class)->middleware(['auth']);
Route::get('yabs/{yab}/json', 'App\Http\Controllers\YabsController@yabsToJson')->middleware(['auth'])->name('yabs.json');
Route::get('yabs-compare-choose', 'App\Http\Controllers\YabsController@chooseYabsCompare')->middleware(['auth'])->name('yabs.compare-choose');