mirror of https://github.com/MarceauKa/shaark.git
✨ Health Checks for Links
This commit is contained in:
parent
045ecaaf7d
commit
7d7210424f
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Link;
|
||||
use App\Services\Shaark\Shaark;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class LinkHealthChecks extends Command
|
||||
{
|
||||
protected $signature = 'shaark:link_health_check {--all}';
|
||||
protected $description = 'Run health checks on links';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$check_all_links = $this->option('all');
|
||||
|
||||
$links = Link::where('http_checked_at', '<', now()->subDays(app(Shaark::class)->getLinkHealthChecksAge()))
|
||||
->orWhereNull('http_checked_at')
|
||||
->orderBy('http_checked_at', 'ASC');
|
||||
|
||||
if (! $check_all_links) {
|
||||
$links->limit(20);
|
||||
}
|
||||
|
||||
$links->get()
|
||||
->each(function (Link $link) {
|
||||
try {
|
||||
$response = (new Client())->request('GET', $link->getUrlAttribute(), [
|
||||
'headers' => [
|
||||
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36',
|
||||
],
|
||||
'http_errors' => false,
|
||||
'timeout' => 5,
|
||||
]);
|
||||
|
||||
$link->http_status = $response->getStatusCode();
|
||||
} catch (RequestException $exception) {
|
||||
// Might happen when the domain as expired
|
||||
$link->http_status = 500;
|
||||
} finally {
|
||||
$link->http_checked_at = now();
|
||||
$link->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -30,6 +30,9 @@ class Kernel extends ConsoleKernel
|
|||
|
||||
// Make backup
|
||||
$this->scheduleBackup($schedule);
|
||||
|
||||
// Link health checks
|
||||
$this->scheduleLinkHealthChecks($schedule);
|
||||
}
|
||||
|
||||
protected function scheduleBackup(Schedule $schedule): self
|
||||
|
@ -54,4 +57,15 @@ class Kernel extends ConsoleKernel
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function scheduleLinkHealthChecks(Schedule $schedule): self
|
||||
{
|
||||
$shaark = app(Shaark::class);
|
||||
|
||||
if (false === $shaark->getLinkHealthChecksEnabled()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$schedule->command('shaark:link_health_check')->everyTenMinutes();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Manage;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Link;
|
||||
use App\Services\HealthCheckStats;
|
||||
use App\Services\Shaark\Shaark;
|
||||
|
||||
class LinksController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
|
||||
$this->middleware('demo')->except('view');
|
||||
}
|
||||
|
||||
public function view()
|
||||
{
|
||||
$stats = new HealthCheckStats();
|
||||
|
||||
return view('manage.links')->with([
|
||||
'page_title' => __('Links'),
|
||||
'num_total' => $stats->countTotal(),
|
||||
'health_checks_enabled' => app(Shaark::class)->getLinkHealthChecksEnabled(),
|
||||
'num_pending' => $stats->countPending(),
|
||||
'num_healthy' => $stats->countHealthy(),
|
||||
'num_other' => $stats->countOther(),
|
||||
'num_dead' => $stats->countDead(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function viewDead()
|
||||
{
|
||||
return view('manage.links_dead')->with([
|
||||
'page_title' => __('Dead Links'),
|
||||
'dead_links' => Link::whereBetween('http_status', [400, 499])->orderBy('http_checked_at', 'DESC')->paginate(10),
|
||||
]);
|
||||
}
|
||||
|
||||
public function viewOther()
|
||||
{
|
||||
return view('manage.links_other')->with([
|
||||
'page_title' => __('Other Status Links'),
|
||||
'other_links' => Link::whereBetween('http_status', [300, 399])
|
||||
->orWhereBetween('http_status', [500, 599])
|
||||
->orderBy('http_checked_at', 'DESC')->paginate(10),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Services\Shaark\Shaark;
|
||||
|
||||
class HealthCheckStats
|
||||
{
|
||||
/** @var \Illuminate\Support\Collection $stats */
|
||||
public $stats;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->stats = \DB::table('links')
|
||||
->select('http_status', \DB::raw('count(id) as num_count'))
|
||||
->groupBy('http_status')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function get()
|
||||
{
|
||||
return [
|
||||
'num_healthy' => $this->countHealthy(),
|
||||
'num_other' => $this->countOther(),
|
||||
'num_dead' => $this->countDead(),
|
||||
'num_pending' => $this->countPending(),
|
||||
];
|
||||
}
|
||||
|
||||
public function countTotal()
|
||||
{
|
||||
return $this->stats->sum('num_count');
|
||||
}
|
||||
|
||||
public function countHealthy()
|
||||
{
|
||||
return $this->stats->where('http_status', 200)->sum('num_count');
|
||||
}
|
||||
|
||||
public function countOther()
|
||||
{
|
||||
$redirects = $this->stats->whereBetween('http_status', [300, 399])
|
||||
->sum('num_count');
|
||||
|
||||
$server_errors = $this->stats->whereBetween('http_status', [500, 599])
|
||||
->sum('num_count');
|
||||
|
||||
return $redirects + $server_errors;
|
||||
}
|
||||
|
||||
public function countDead()
|
||||
{
|
||||
return $this->stats->whereBetween('http_status', [400, 499])
|
||||
->sum('num_count');
|
||||
}
|
||||
|
||||
public function countPending()
|
||||
{
|
||||
return \DB::table('links')
|
||||
->where('http_checked_at', '<', now()->subDays(app(Shaark::class)->getLinkHealthChecksAge()))
|
||||
->orWhereNull('http_checked_at')
|
||||
->count();
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
"php": "^7.2",
|
||||
"doctrine/dbal": "^2.9",
|
||||
"fideloper/proxy": "^4.0",
|
||||
"guzzlehttp/guzzle": "^6.5",
|
||||
"hashids/hashids": "^2.0.4|~3.0",
|
||||
"lab404/laravel-auth-checker": "^1.4",
|
||||
"laravel/framework": "^6.0",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "6a1c0803587a9053ccab074ee0d36977",
|
||||
"content-hash": "5f8b851e33ca8323793fce95286f8db4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "clue/socket-raw",
|
||||
|
@ -689,6 +689,124 @@
|
|||
],
|
||||
"time": "2019-12-12T13:22:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/aab4ebd862aa7d04f01a4b51849d657db56d882e",
|
||||
"reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.0",
|
||||
"guzzlehttp/psr7": "^1.6.1",
|
||||
"php": ">=5.5",
|
||||
"symfony/polyfill-intl-idn": "^1.11"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
|
||||
"psr/log": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "6.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle is a PHP HTTP client library",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": [
|
||||
"client",
|
||||
"curl",
|
||||
"framework",
|
||||
"http",
|
||||
"http client",
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"time": "2020-04-18T10:38:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "v1.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
|
||||
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle promises library",
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"time": "2016-12-20T10:07:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "1.6.1",
|
||||
|
@ -7594,5 +7712,6 @@
|
|||
"platform": {
|
||||
"php": "^7.2"
|
||||
},
|
||||
"platform-dev": []
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -142,5 +142,13 @@ return [
|
|||
'default' => 'whitelist',
|
||||
'rules' => ['nullable', 'in:disabled,whitelist,all']
|
||||
],
|
||||
'link_health_checks_enabled' => [
|
||||
'default' => false,
|
||||
'rules' => ['nullable', 'in:on,off']
|
||||
],
|
||||
'link_health_checks_age' => [
|
||||
'default' => '7',
|
||||
'rules' => ['required', 'numeric', 'min:7', 'max:365']
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
@ -5,9 +5,25 @@ use App\Link;
|
|||
use Faker\Generator as Faker;
|
||||
|
||||
$factory->define(Link::class, function (Faker $faker) {
|
||||
$http_status_codes = [
|
||||
null,
|
||||
200,
|
||||
302,
|
||||
404,
|
||||
500,
|
||||
];
|
||||
|
||||
$http_status_code = $http_status_codes[rand(0, 4)];
|
||||
$http_checked_at = null;
|
||||
if (! is_null($http_status_code)) {
|
||||
$http_checked_at = $faker->dateTimeThisMonth;
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => $faker->sentence,
|
||||
'content' => $faker->paragraph,
|
||||
'url' => $faker->url,
|
||||
'http_status' => $http_status_code,
|
||||
'http_checked_at' => $http_checked_at,
|
||||
];
|
||||
});
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddHealthChecksToLinksTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('links', function (Blueprint $table) {
|
||||
$table->unsignedSmallInteger('http_status')->after('url')->nullable();
|
||||
$table->timestamp('http_checked_at')->after('http_status')->nullable();
|
||||
|
||||
$table->index('http_status');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('links', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'http_status',
|
||||
'http_checked_at',
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class LinkSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
factory(\App\Link::class, 100)->create();
|
||||
}
|
||||
}
|
|
@ -76,7 +76,13 @@ return [
|
|||
'disabled' => 'Disabled',
|
||||
'whitelist' => 'White-listing',
|
||||
'all' => 'All',
|
||||
]
|
||||
],
|
||||
|
||||
'links' => [
|
||||
'title' => 'Link Health Checks',
|
||||
'health_checks_enabled' => 'Enable health checks',
|
||||
'health_checks_age' => 'Number of days between checks for each link',
|
||||
],
|
||||
],
|
||||
|
||||
// Mails
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
"Whoops, something went wrong on our servers.": "サーバー上にエラーが発生してしまった。",
|
||||
"Yes": "はい",
|
||||
"No": "いいえ",
|
||||
"Enabled": "有効",
|
||||
"Disabled": "無効",
|
||||
"Previous": "前",
|
||||
"Next": "次",
|
||||
|
||||
|
@ -240,5 +242,17 @@
|
|||
"Source code": "ソースコード",
|
||||
"RSS Feed": "RSSフィード",
|
||||
"Atom Feed": "ATOMフィード",
|
||||
"All new content of :title": ":titleの最新コンテンツ"
|
||||
"All new content of :title": ":titleの最新コンテンツ",
|
||||
|
||||
"Total": "合計",
|
||||
"Health Checks": "健康チェック",
|
||||
"Pending Checks": "チェック待ち",
|
||||
"Healthy (200)": "健康(200)",
|
||||
"Dead (4xx)": "URL切れ(4xx)",
|
||||
"Other (3xx, 5xx)": "他(3xx、5xx)",
|
||||
"Dead Links": "切れたURL",
|
||||
"No dead links found": "切れたURLはありません",
|
||||
"Other-status Links": "他ステータスのURL",
|
||||
"No other-status links found": "他ステータスのURLはありません",
|
||||
"Last Checked": "最後のチェック"
|
||||
}
|
||||
|
|
|
@ -75,7 +75,13 @@ return [
|
|||
'disabled' => '無効',
|
||||
'whitelist' => 'ホワイトリスト',
|
||||
'all' => 'すべて',
|
||||
]
|
||||
],
|
||||
|
||||
'links' => [
|
||||
'title' => 'URLの健康チェック',
|
||||
'health_checks_enabled' => '健康チェックを有効にしますか?',
|
||||
'health_checks_age' => '各URLの次のチェックを行うまで何日待ちますか?',
|
||||
],
|
||||
],
|
||||
|
||||
// Mails
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
class="list-group-item list-group-item-action{{ request()->is('manage/settings') ? ' active' : '' }}"
|
||||
><i class="fas fa-fw fa-cogs mr-1"></i> {{ __('Settings') }}
|
||||
</a>
|
||||
<a href="{{ route('manage.links') }}"
|
||||
class="list-group-item list-group-item-action{{ request()->is('manage/links') ? ' active' : '' }}"
|
||||
><i class="fas fa-fw fa-link mr-1"></i> {{ __('Links') }}</a>
|
||||
<a href="{{ route('manage.walls') }}"
|
||||
class="list-group-item list-group-item-action{{ request()->is('manage/walls') ? ' active' : '' }}"
|
||||
><i class="fas fa-fw fa-bookmark mr-1"></i> {{ __('Walls') }}</a>
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
@extends('layouts.manage')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="row text-uppercase">
|
||||
<div class="col-6 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<small class="card-title font-weight-bold text-muted">{{ __('Total') }}</small>
|
||||
<h3 class="text-right text-primary">{{ number_format($num_total, 0) }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<small class="card-title font-weight-bold text-muted">{{ __('Health Checks') }}</small>
|
||||
@if ($health_checks_enabled)
|
||||
<h3 class="text-right text-capitalize text-success">{{ __('Enabled') }}</h3>
|
||||
@else
|
||||
<h3 class="text-right text-capitalize text-muted">{{ __('Disabled') }}</h3>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 mt-4 col-lg-4 mt-lg-0">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<small class="card-title font-weight-bold text-muted">{{ __('Pending Checks') }}</small>
|
||||
<h3 class="text-right text-info">{{ number_format($num_pending, 0) }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 mt-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<small class="card-title font-weight-bold text-muted">{{ __('Healthy (200)') }}</small>
|
||||
<h3 class="text-right text-success">{{ number_format($num_healthy, 0) }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 mt-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<small class="card-title font-weight-bold text-muted">{{ __('Dead (4xx)') }}</small>
|
||||
<h3 class="text-right text-danger">{{ number_format($num_dead, 0) }}</h3>
|
||||
<a href="{{ route('manage.links.dead.view') }}" class="stretched-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 mt-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<small class="card-title font-weight-bold text-muted">{{ __('Other (3xx, 5xx)') }}</small>
|
||||
<h3 class="text-right text-muted">{{ number_format($num_other, 0) }}</h3>
|
||||
<a href="{{ route('manage.links.other.view') }}" class="stretched-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -0,0 +1,46 @@
|
|||
@extends('layouts.manage')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<i class="fas fa-link mr-1"></i> {{ __('Dead Links') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
@if ($dead_links->isEmpty())
|
||||
<div class="alert alert-info">
|
||||
{{ __('No dead links found') }}
|
||||
</div>
|
||||
@else
|
||||
<table class="table table-borderless table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Title') }}</th>
|
||||
<th>{{ __('Status') }}</th>
|
||||
<th>{{ __('Last Checked') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($dead_links as $dead_link)
|
||||
<tr>
|
||||
<td><a href="{{ $dead_link->permalink }}" target="_blank">{{ $dead_link->title }}</a></td>
|
||||
<td>{{ $dead_link->http_status }}</td>
|
||||
<td>{{ $dead_link->http_checked_at }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ $dead_links->links() }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -0,0 +1,46 @@
|
|||
@extends('layouts.manage')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<i class="fas fa-link mr-1"></i> {{ __('Other-status Links') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
@if ($other_links->isEmpty())
|
||||
<div class="alert alert-info">
|
||||
{{ __('No other-status links found') }}
|
||||
</div>
|
||||
@else
|
||||
<table class="table table-borderless table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Title') }}</th>
|
||||
<th>{{ __('Status') }}</th>
|
||||
<th>{{ __('Last Checked') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($other_links as $other_link)
|
||||
<tr>
|
||||
<td><a href="{{ $other_link->permalink }}" target="_blank">{{ $other_link->title }}</a></td>
|
||||
<td>{{ $other_link->http_status }}</td>
|
||||
<td>{{ $other_link->http_checked_at }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ $other_links->links() }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -387,6 +387,30 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4" id="links">
|
||||
<div class="card-header">{{ __('shaark.settings.links.title') }}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input"
|
||||
name="link_health_checks_enabled" id="link_health_checks_enabled" {{ old('link_health_checks_enabled', $settings['link_health_checks_enabled']) ? ' checked' : '' }}>
|
||||
<label class="custom-control-label" for="link_health_checks_enabled">{{ __('shaark.settings.links.health_checks_enabled') }}</label>
|
||||
</div>
|
||||
@error('link_health_checks_enabled')
|
||||
<span class="text-danger" role="alert">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">{{ __('shaark.settings.links.health_checks_age') }}</label>
|
||||
<input type="number" class="form-control {{ $errors->has('link_health_checks_age') ? ' is-invalid' : '' }}" step="1" min="7" max="365"
|
||||
name="link_health_checks_age" id="link_health_checks_age" value="{{ old('link_health_checks_age', $settings['link_health_checks_age']) }}">
|
||||
@error('link_health_checks_age')
|
||||
<span class="invalid-feedback" role="alert">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<button type="submit" class="btn btn-primary">{{ __('Save') }}</button>
|
||||
|
|
|
@ -60,6 +60,11 @@ Route::group([
|
|||
|
||||
$router->get('walls', 'WallsController@view')->name('walls');
|
||||
|
||||
$router->get('links', 'LinksController@view')->name('links');
|
||||
$router->post('links', 'LinksController@store');
|
||||
$router->get('links/dead', 'LinksController@viewDead')->name('links.dead.view');
|
||||
$router->get('links/other', 'LinksController@viewOther')->name('links.other.view');
|
||||
|
||||
$router->get('archives', 'ArchivesController@view')->name('archives');
|
||||
|
||||
$router->get('settings', 'SettingsController@form')->name('settings');
|
||||
|
|
Loading…
Reference in New Issue