mirror of
https://github.com/nidebr/as-stats-gui.git
synced 2025-02-20 11:23:18 +08:00
save
This commit is contained in:
parent
efd321a775
commit
d2bf49f76e
@ -33,3 +33,8 @@
|
||||
.card-title {
|
||||
font-weight: var(--tblr-font-weight-bold) !important;
|
||||
}
|
||||
|
||||
.auto-search-wrapper input {
|
||||
padding: .5625rem .75rem !important;
|
||||
font-size: .875rem !important;
|
||||
}
|
||||
|
1
public/assets/js/autocomplete/autocomplete.min.css
vendored
Normal file
1
public/assets/js/autocomplete/autocomplete.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
:root{--close-button:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18.984 6.422 13.406 12l5.578 5.578-1.406 1.406L12 13.406l-5.578 5.578-1.406-1.406L10.594 12 5.016 6.422l1.406-1.406L12 10.594l5.578-5.578z'/%3E%3C/svg%3E");--loupe-icon:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23929292' d='M16.041 15.856a.995.995 0 0 0-.186.186A6.97 6.97 0 0 1 11 18c-1.933 0-3.682-.782-4.95-2.05S4 12.933 4 11s.782-3.682 2.05-4.95S9.067 4 11 4s3.682.782 4.95 2.05S18 9.067 18 11a6.971 6.971 0 0 1-1.959 4.856zm5.666 4.437-3.675-3.675A8.967 8.967 0 0 0 20 11c0-2.485-1.008-4.736-2.636-6.364S13.485 2 11 2 6.264 3.008 4.636 4.636 2 8.515 2 11s1.008 4.736 2.636 6.364S8.515 20 11 20a8.967 8.967 0 0 0 5.618-1.968l3.675 3.675a.999.999 0 1 0 1.414-1.414z'/%3E%3C/svg%3E")}.auto-search-wrapper{display:block;position:relative;width:100%}.auto-search-wrapper input{border:1px solid #d7d7d7;box-shadow:none;box-sizing:border-box;font-size:16px;padding:12px 45px 12px 10px;width:100%}.auto-search-wrapper input:focus{border:1px solid #858585;outline:none}.auto-search-wrapper input::-ms-clear{display:none}.auto-search-wrapper ul{list-style:none;margin:0;overflow:auto;padding:0}.auto-search-wrapper ul li{cursor:pointer;margin:0;overflow:hidden;padding:10px;position:relative}.auto-search-wrapper ul li:not(:last-child){border-top:none}.auto-search-wrapper ul li[disabled]{background:#ececec;opacity:.5;pointer-events:none}.auto-search-wrapper .auto-expanded{border:1px solid #858585;outline:none}.auto-search-wrapper.loupe:before{filter:invert(60%)}.auto-is-loading:after{animation:auto-spinner .6s linear infinite;border-color:#d9d9d9 grey grey #d9d9d9;border-radius:50%;border-style:solid;border-width:2px;bottom:0;box-sizing:border-box;content:"";height:20px;margin:auto;position:absolute;right:10px;top:0;width:20px}.auto-is-loading .auto-clear{display:none}@keyframes auto-spinner{to{transform:rotate(1turn)}}li.loupe:before{bottom:auto;top:15px}.loupe input{padding:12px 45px 12px 35px}.loupe:before{background-image:var(--loupe-icon);bottom:0;content:"";height:17px;left:10px;margin:auto;position:absolute;top:0;width:17px}.auto-selected:before{opacity:1}.auto-clear{align-items:center;background-color:transparent;border:none;bottom:0;cursor:pointer;display:flex;height:auto;justify-content:center;margin:auto;position:absolute;right:0;top:0;width:40px}.auto-clear:before{content:var(--close-button);height:24px;line-height:100%;width:24px}.auto-clear span{display:none}.auto-results-wrapper{background-color:#fff;border:1px solid #858585;border-top:none;box-sizing:border-box;display:none;overflow:hidden}.auto-results-wrapper ul>.loupe{padding-left:40px}.auto-results-wrapper.auto-is-active{display:block;margin-top:-1px;position:absolute;width:100%;z-index:99999}.auto-selected{background-color:#e6e6e6}.auto-selected+li:before{border-top:none}.auto-error{border:1px solid #ff3838}.auto-error::-moz-placeholder{color:#f66;opacity:1}.auto-error::placeholder{color:#f66;opacity:1}.hidden{display:none}
|
8
public/assets/js/autocomplete/autocomplete.min.js
vendored
Normal file
8
public/assets/js/autocomplete/autocomplete.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/assets/js/axios/axios.min.js
vendored
Normal file
2
public/assets/js/axios/axios.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -13,7 +13,9 @@ use App\Repository\GetAsDataRepository;
|
||||
use App\Repository\KnowlinksRepository;
|
||||
use App\Repository\PeeringDBRepository;
|
||||
use App\Util\Annotation\Menu;
|
||||
use App\Util\GetJsonParameters;
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
@ -93,6 +95,9 @@ class IXStatsController extends BaseController
|
||||
)]
|
||||
public function searchIX(
|
||||
Request $request,
|
||||
PeeringDBRepository $peeringDBRepository,
|
||||
GetAsDataRepository $asDataRepository,
|
||||
ConfigApplication $Config,
|
||||
): Response {
|
||||
$this->base_data['content_wrapper']['titre'] = 'Search IX Stats';
|
||||
|
||||
@ -100,7 +105,35 @@ class IXStatsController extends BaseController
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$form->getData();
|
||||
$this->base_data['content_wrapper']['titre'] = \sprintf(
|
||||
'Top %s (%s)',
|
||||
$this->base_data['top'],
|
||||
'24 hours'
|
||||
);
|
||||
|
||||
$this->base_data['content_wrapper']['small'] = $form->get('ix')->getData();
|
||||
|
||||
$this->data['data'] = $asDataRepository::get(
|
||||
$this->base_data['top'],
|
||||
'',
|
||||
[],
|
||||
$peeringDBRepository->getIXMembers((int) $form->get('ix_hidden')->getData()),
|
||||
);
|
||||
|
||||
$this->data['start'] = time() - 24 * 3600;
|
||||
$this->data['end'] = time();
|
||||
$this->data['graph_size'] = [
|
||||
'width' => $Config::getAsStatsConfigGraph()['top_graph_width'],
|
||||
'height' => $Config::getAsStatsConfigGraph()['top_graph_height'],
|
||||
];
|
||||
$this->data['selectedLinks'] = [];
|
||||
|
||||
return $this->render('pages/ix/search_ix/show.html.twig', [
|
||||
'base_data' => $this->base_data,
|
||||
'data' => $this->data,
|
||||
'knownlinks' => KnowlinksRepository::get(),
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('pages/ix/search_ix/index.html.twig', [
|
||||
@ -108,4 +141,40 @@ class IXStatsController extends BaseController
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
path: '/search/get-ixname',
|
||||
name: 'ix.search.get_ixname',
|
||||
methods: ['POST'],
|
||||
)]
|
||||
public function getIXName(
|
||||
Request $request,
|
||||
PeeringDBRepository $peeringDBRepository,
|
||||
): JsonResponse {
|
||||
$req = GetJsonParameters::getAll($request);
|
||||
|
||||
if (!\array_key_exists('name', $req)) {
|
||||
return new JsonResponse(['message' => 'Bad JSON request.'], Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $peeringDBRepository->getIXName($req['name']);
|
||||
|
||||
if (200 !== $data['status_code']) {
|
||||
throw new \Exception('No data from API.');
|
||||
}
|
||||
|
||||
$return = [];
|
||||
foreach ($data['response']['data'] as $value) {
|
||||
$return[] = [
|
||||
'id' => $value['id'],
|
||||
'name' => \sprintf('%s (%s / %s', $value['name'], $value['city'], $value['country']),
|
||||
];
|
||||
}
|
||||
|
||||
return new JsonResponse($return);
|
||||
} catch (\Exception $e) {
|
||||
return new JsonResponse(['message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@ -20,7 +21,8 @@ class SearchIXForm extends AbstractType
|
||||
->add('ix', TextType::class, [
|
||||
'label' => false,
|
||||
'translation_domain' => false,
|
||||
]);
|
||||
])
|
||||
->add('ix_hidden', HiddenType::class);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
|
@ -41,4 +41,13 @@ class PeeringDBRepository
|
||||
|
||||
return $data['response']['data'][0];
|
||||
}
|
||||
|
||||
public function getIXName(string $regex): array
|
||||
{
|
||||
if ('' === $regex || '0' === $regex) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->peeringDbClient->get(\sprintf('ix?name__contains=%s', $regex));
|
||||
}
|
||||
}
|
||||
|
20
src/Util/GetJsonParameters.php
Normal file
20
src/Util/GetJsonParameters.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Util;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class GetJsonParameters
|
||||
{
|
||||
public static function getAll(Request $request): array
|
||||
{
|
||||
if (\str_starts_with(\sprintf('%s', $request->headers->get('Content-Type')), 'application/json')) {
|
||||
$data = json_decode($request->getContent(), true);
|
||||
$request->request->replace(is_array($data) ? $data : []);
|
||||
}
|
||||
|
||||
return $request->request->all();
|
||||
}
|
||||
}
|
@ -29,13 +29,13 @@
|
||||
</h3>
|
||||
{% endblock %}
|
||||
|
||||
{#% block customlinks %}
|
||||
{% block customlinks %}
|
||||
<div class="col-md-auto">
|
||||
<div class="mt-3 badges">
|
||||
{{ custom_links_top(as)|raw }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %#}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
@ -4,7 +4,9 @@
|
||||
<h3 class="card-title">Search IX</h3>
|
||||
<div class="row g2">
|
||||
<div class="col">
|
||||
{{ form_widget(form.ix) }}
|
||||
<div class="auto-search-wrapper">
|
||||
{{ form_widget(form.ix) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-icon">{{ icon('search', 'icon') }}</button>
|
||||
|
@ -1,5 +1,11 @@
|
||||
{% extends "base/_layout.html.twig" %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
|
||||
<link rel="stylesheet" href="{{ asset('assets/js/autocomplete/autocomplete.min.css') }}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row row-cards">
|
||||
<div class="col-lg-2 col-sm-12 space-y">
|
||||
@ -14,10 +20,46 @@
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
<script src="{{ asset('assets/js/autocomplete/autocomplete.min.js') }}"></script>
|
||||
<script src="{{ asset('assets/js/axios/axios.min.js') }}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var input = document.querySelector('#{{ form.ix.vars.id }}');
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
var hidden_ix_name = document.querySelector('#{{ form.ix_hidden.vars.id }}');
|
||||
|
||||
console.log(input);
|
||||
new Autocomplete('{{ form.ix.vars.id }}', {
|
||||
cache: true,
|
||||
howManyCharacters: 2,
|
||||
onSearch: ({ currentValue }) => {
|
||||
const api = '{{ path('ix.search.get_ixname') }}';
|
||||
var json_data = {name: currentValue};
|
||||
|
||||
return axios
|
||||
.post(api, json_data)
|
||||
.then((response) => {
|
||||
if (!response.data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return response.data;
|
||||
})
|
||||
.catch(() => {
|
||||
return [];
|
||||
});
|
||||
},
|
||||
|
||||
onResults: ({ matches }) => {
|
||||
return matches
|
||||
.map((el) => {
|
||||
return `<li>${el.name}</li>`;
|
||||
})
|
||||
.join('');
|
||||
},
|
||||
|
||||
onSelectedItem: ({ object }) => {
|
||||
hidden_ix_name.value = object.id;
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
44
templates/pages/ix/search_ix/show.html.twig
Normal file
44
templates/pages/ix/search_ix/show.html.twig
Normal file
@ -0,0 +1,44 @@
|
||||
{% extends "pages/ix/search_ix/index.html.twig" %}
|
||||
|
||||
{% set counter = 0 %}
|
||||
|
||||
{% block legend %}
|
||||
{% if knownlinks is defined %}
|
||||
{% include 'core/legend_simple.html.twig' %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block graph %}
|
||||
<div class="col">
|
||||
<div class="row row-cards">
|
||||
<div class="space-y">
|
||||
{% for as, as_data in data.data.asinfo %}
|
||||
{% set counter = counter + 1 %}
|
||||
{% embed 'core/card_top.html.twig' with {as: as, as_data: as_data, data: data, counter: counter, hours: hours|default('24 hours')} only %}
|
||||
{% block rank %}
|
||||
<div class="display-6 fw-bold rank">
|
||||
# {{ counter }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
<h3 class="mb-0">
|
||||
<span class="me-2 flag flag-{{ as_data.info.country|lower }}"></span>
|
||||
<strong>AS{{ as }}:</strong>
|
||||
<span class="small text-uppercase"><i>{{ as_data.info.name|default(as_data.info.description) }}</i></span>
|
||||
</h3>
|
||||
{% endblock %}
|
||||
|
||||
{% block customlinks %}
|
||||
<div class="col-md-auto">
|
||||
<div class="mt-3 badges">
|
||||
{{ custom_links_top(as)|raw }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user