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
3af1dc7460
commit
b84fc03c4f
17
asstats.yml
17
asstats.yml
@ -0,0 +1,17 @@
|
||||
config:
|
||||
top: 20
|
||||
daystatsfile: /home/nidebr/data/DEV/asstats/stats/asstats_day.txt"
|
||||
|
||||
top_intervals:
|
||||
30days:
|
||||
hours: 720 #30*24
|
||||
statsfile: /home/nidebr/data/DEV/asstats/stats/asstats_month.txt
|
||||
label: 30 days
|
||||
3months:
|
||||
hours: 2160 #30*24*3
|
||||
statsfile: /home/nidebr/data/DEV/asstats/stats/asstats_3month.txt
|
||||
label: 3 months
|
||||
1year:
|
||||
hours: 8760 #30*24*3
|
||||
statsfile: /home/nidebr/data/DEV/asstats/stats/asstats_year.txt
|
||||
label: 1 year
|
@ -11,12 +11,12 @@
|
||||
"symfony/console": "7.0.*",
|
||||
"symfony/dotenv": "7.0.*",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/form": "7.0.*",
|
||||
"symfony/framework-bundle": "7.0.*",
|
||||
"symfony/runtime": "7.0.*",
|
||||
"symfony/twig-bundle": "7.0.*",
|
||||
"symfony/yaml": "7.0.*",
|
||||
"twig/extra-bundle": "^3.0",
|
||||
"twig/intl-extra": "^3.0",
|
||||
"twig/string-extra": "^3.0",
|
||||
"twig/twig": "^3.0"
|
||||
},
|
||||
|
BIN
public/assets/tabler-icons/fonts/tabler-icons.eot
Normal file
BIN
public/assets/tabler-icons/fonts/tabler-icons.eot
Normal file
Binary file not shown.
BIN
public/assets/tabler-icons/fonts/tabler-icons.ttf
Normal file
BIN
public/assets/tabler-icons/fonts/tabler-icons.ttf
Normal file
Binary file not shown.
BIN
public/assets/tabler-icons/fonts/tabler-icons.woff
Normal file
BIN
public/assets/tabler-icons/fonts/tabler-icons.woff
Normal file
Binary file not shown.
BIN
public/assets/tabler-icons/fonts/tabler-icons.woff2
Normal file
BIN
public/assets/tabler-icons/fonts/tabler-icons.woff2
Normal file
Binary file not shown.
@ -16,14 +16,43 @@ class ConfigApplication
|
||||
return \sprintf('%s/../../', __DIR__);
|
||||
}
|
||||
|
||||
private static function getConfig(): mixed
|
||||
private static function getConfig(): array
|
||||
{
|
||||
$input = file_get_contents(self::getRootPathApp().self::CONFIG_APP_FILE);
|
||||
return Yaml::parse((string) $input);
|
||||
return (array) Yaml::parse((string) $input);
|
||||
}
|
||||
|
||||
private static function getConfigAsStats(): array
|
||||
{
|
||||
$input = file_get_contents(self::getRootPathApp().self::CONFIG_ASSTATS_FILE);
|
||||
return (array) Yaml::parse((string) $input);
|
||||
}
|
||||
|
||||
public static function getRelease(): mixed
|
||||
{
|
||||
return self::getConfig()['application']['release'];
|
||||
}
|
||||
|
||||
public static function getLocale(): mixed
|
||||
{
|
||||
return self::getConfig()['application']['locale'];
|
||||
}
|
||||
|
||||
public static function getAsStatsTopInterval(): mixed
|
||||
{
|
||||
if (false === \array_key_exists('top_intervals', self::getConfigAsStats())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::getConfigAsStats()['top_intervals'];
|
||||
}
|
||||
|
||||
public static function getAsStatsConfig(): mixed
|
||||
{
|
||||
if (false === \array_key_exists('config', self::getConfigAsStats())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::getConfigAsStats()['config'];
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ namespace App\Controller;
|
||||
|
||||
use App\Application\ConfigApplication;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
class BaseController extends AbstractController
|
||||
{
|
||||
@ -13,7 +14,10 @@ class BaseController extends AbstractController
|
||||
|
||||
public function __construct(
|
||||
ConfigApplication $Config,
|
||||
RequestStack $requestStack,
|
||||
) {
|
||||
$this->base_data['release'] = $Config::getRelease();
|
||||
$this->base_data['top_interval'] = $Config::getAsStatsTopInterval();
|
||||
$this->base_data['request'] = $requestStack->getCurrentRequest()->query->all();
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Application\ConfigApplication;
|
||||
use App\Util\Annotation\Menu;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
#[Menu('top_as')]
|
||||
class IndexController extends BaseController
|
||||
{
|
||||
#[Route(
|
||||
@ -14,11 +17,49 @@ class IndexController extends BaseController
|
||||
name: 'index',
|
||||
methods: ['GET'],
|
||||
)]
|
||||
public function index(): Response
|
||||
public function index(
|
||||
ConfigApplication $Config,
|
||||
): Response
|
||||
{
|
||||
$this->base_data['content_wrapper']['titre'] = 'dede';
|
||||
/*if(\isset($this->base_data['request']['top'])) {
|
||||
$top = $this->base_data['request']['top'];
|
||||
} else {
|
||||
$top = $Config::getAsStatsConfig()['top'];
|
||||
}*/
|
||||
|
||||
dump($this->base_data);
|
||||
dump($this->base_data['request']);
|
||||
|
||||
/*if (\array_key_exists('top', $this->base_data['request'])) {
|
||||
dump(1);
|
||||
} else {
|
||||
dump(2);
|
||||
}*/
|
||||
|
||||
$this->base_data['content_wrapper']['titre'] = \sprintf(
|
||||
'Top %s (%s)',
|
||||
\array_key_exists('top', $this->base_data['request']) :: $this->base_data['request']['top'] ?? $Config::getAsStatsConfig()['top'],
|
||||
'24 hours'
|
||||
);
|
||||
|
||||
return $this->render('pages/index.html.twig', [
|
||||
'base_data' => $this->base_data,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
path: '/{topinterval}',
|
||||
name: 'index_topinterval',
|
||||
methods: ['GET'],
|
||||
)]
|
||||
public function indexTopInterval(
|
||||
ConfigApplication $Config,
|
||||
string $topinterval,
|
||||
): Response{
|
||||
$this->base_data['content_wrapper']['titre'] = \sprintf(
|
||||
'Top %s (%s)',
|
||||
$Config::getAsStatsConfig()['top'],
|
||||
$Config::getAsStatsTopInterval()[$topinterval]['label']
|
||||
);
|
||||
|
||||
return $this->render('pages/index.html.twig', [
|
||||
'base_data' => $this->base_data,
|
||||
|
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Twig\EventSubscriber;
|
||||
|
||||
use App\Util\Annotation\Menu;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\ControllerEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Twig\Environment;
|
||||
|
||||
readonly class MenuAnnotationControllerEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(private Environment $twig)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
KernelEvents::CONTROLLER => 'handle',
|
||||
];
|
||||
}
|
||||
|
||||
public function handle(ControllerEvent $event): void
|
||||
{
|
||||
$controllers = $event->getController();
|
||||
|
||||
if (!is_array($controllers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$controller, $methodName] = $controllers;
|
||||
|
||||
if (!$controller instanceof AbstractController) {
|
||||
return;
|
||||
}
|
||||
|
||||
$methodAttribute = $this->getMethodeAttribute($controller, $methodName);
|
||||
|
||||
if (null !== $methodAttribute) {
|
||||
$this->registerMenuToTwig($methodAttribute);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$classAttribute = $this->getClassAttribute($controller);
|
||||
|
||||
if (null !== $classAttribute) {
|
||||
$this->registerMenuToTwig($classAttribute);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->registerNullMenuToTwig();
|
||||
}
|
||||
|
||||
private function registerNullMenuToTwig(): void
|
||||
{
|
||||
$this->twig->addGlobal('current_menu', null);
|
||||
}
|
||||
|
||||
private function registerMenuToTwig(Menu $menu): void
|
||||
{
|
||||
$this->twig->addGlobal('current_menu', $menu->getDomain());
|
||||
}
|
||||
|
||||
private function getClassAttribute(AbstractController $controller): ?Menu
|
||||
{
|
||||
$reflectionClass = new \ReflectionClass($controller);
|
||||
|
||||
$attributes = $reflectionClass->getAttributes(Menu::class);
|
||||
|
||||
if (1 !== \count($attributes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $attributes[0]->newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
private function getMethodeAttribute(AbstractController $controller, string $methodName): ?Menu
|
||||
{
|
||||
$reflectionObject = new \ReflectionObject($controller);
|
||||
$reflectionMethod = $reflectionObject->getMethod($methodName);
|
||||
|
||||
$attributes = $reflectionMethod->getAttributes(Menu::class);
|
||||
|
||||
if (1 !== \count($attributes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $attributes[0]->newInstance();
|
||||
}
|
||||
}
|
33
src/Twig/IconExtension.php
Normal file
33
src/Twig/IconExtension.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Util\Theme\Icon;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class IconExtension extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @return array<TwigFunction>
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('icon', [$this, 'icon'], ['is_safe' => ['html']]),
|
||||
];
|
||||
}
|
||||
|
||||
public function icon(string $name, ?string $addClass = null): string
|
||||
{
|
||||
$icon = Icon::byName($name);
|
||||
|
||||
if ($addClass) {
|
||||
return \sprintf('<i class="ti %s %s"></i>', $icon->value, $addClass);
|
||||
}
|
||||
|
||||
return \sprintf('<i class="ti %s"></i>', $icon->value);
|
||||
}
|
||||
}
|
19
src/Util/Annotation/Menu.php
Normal file
19
src/Util/Annotation/Menu.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Util\Annotation;
|
||||
|
||||
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
|
||||
class Menu
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $domain,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getDomain(): string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
}
|
35
src/Util/Theme/Icon.php
Normal file
35
src/Util/Theme/Icon.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Util\Theme;
|
||||
|
||||
enum Icon: string
|
||||
{
|
||||
// Menu
|
||||
case menu_home = 'ti-home icon';
|
||||
|
||||
// Form
|
||||
case filter = 'ti-filter';
|
||||
|
||||
/**
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function names(): array
|
||||
{
|
||||
return \array_map(static function (self $icon) {
|
||||
return $icon->name;
|
||||
}, self::cases());
|
||||
}
|
||||
|
||||
public static function byName(string $name): self
|
||||
{
|
||||
foreach (self::cases() as $icon) {
|
||||
if ($name === $icon->name) {
|
||||
return $icon;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \LogicException(\sprintf('Icône "%s" inconnue, merci d’utiliser une des icônes suivantes : %s', $name, \implode(', ', self::names())));
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<header class="navbar navbar-expand-md navbar-light d-print-none">
|
||||
<header class="navbar navbar-expand-md d-print-none">
|
||||
<div class="container-xl">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-menu">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@ -8,15 +8,20 @@
|
||||
<span class="text-uppercase">AS-Stats</span>
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
<div class="navbar-nav flex-row order-md-last">
|
||||
<div class="nav-item">
|
||||
<span class="nav-link text-danger fw-bold" id="branch"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-nav flex-row order-md-last">
|
||||
<div class="d-none d-md-flex">
|
||||
{#% include "navigation/menu/_user_menu.html.twig" %#}
|
||||
<div class="my-1 flex-grow-1 flex-md-grow-0 order-first">
|
||||
<form method="get" autocomplete="off" novalidate="">
|
||||
<div class="input-icon">
|
||||
<span class="input-icon-addon">
|
||||
{{ icon('filter') }}
|
||||
</span>
|
||||
<input type="text" name='top' class="form-control" value="{{ base_data.request.top|default('') }}" placeholder="Top AS">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include "navigation/_menu.html.twig" %}
|
||||
</div>
|
||||
</header>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<title>AS-Stats | {% block title %}{{ title|default('') }}{% endblock %}</title>
|
||||
|
||||
<link rel="stylesheet" href="{{ asset('assets/templates/tabler.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('assets/plugins/tabler-icons/tabler-icons.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('assets/tabler-icons/tabler-icons.min.css') }}">
|
||||
|
||||
{% block css %}{% endblock %}
|
||||
</head>
|
||||
|
39
templates/navigation/_menu.html.twig
Normal file
39
templates/navigation/_menu.html.twig
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="collapse navbar-collapse" id="navbar-menu">
|
||||
<div class="d-flex flex-column flex-md-row flex-fill align-items-stretch align-items-md-center">
|
||||
<ul class="navbar-nav">
|
||||
{% if base_data.top_interval %}
|
||||
<li class="nav-item dropdown {% if "top_as" == current_menu %}active{% endif %}">
|
||||
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" data-bs-auto-close="outside" role="button" aria-expanded="false" >
|
||||
<span class="nav-link-icon d-md-none d-lg-inline-block">
|
||||
{{ icon('menu_home') }}
|
||||
</span>
|
||||
<span class="nav-link-title">
|
||||
Top AS
|
||||
</span>
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="{{ path('index') }}">
|
||||
Top AS - 24 hours
|
||||
</a>
|
||||
{% for key, item in base_data.top_interval %}
|
||||
<a class="dropdown-item" href="{{ path('index_topinterval', {'topinterval': key}) }}">
|
||||
Top AS - {{ item.label }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ path('index') }}">
|
||||
<span class="nav-link-icon d-md-none d-lg-inline-block">
|
||||
{{ icon('menu_home') }}
|
||||
</span>
|
||||
<span class="nav-link-title">
|
||||
Top AS
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
Loading…
x
Reference in New Issue
Block a user