mirror of
https://github.com/rekryt/iplist.git
synced 2025-10-13 00:49:36 +03:00
Initial commit
This commit is contained in:
60
src/App/Controller/AbstractController.php
Normal file
60
src/App/Controller/AbstractController.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace OpenCCK\App\Controller;
|
||||
|
||||
use Amp\Http\HttpStatus;
|
||||
use Amp\Http\Server\Request;
|
||||
use Amp\Http\Server\Response;
|
||||
|
||||
use Throwable;
|
||||
|
||||
abstract class AbstractController implements ControllerInterface {
|
||||
private int $httpStatus = HttpStatus::OK;
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param array $headers
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __construct(protected Request $request, protected array $headers = []) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response
|
||||
*/
|
||||
public function __invoke(): Response {
|
||||
return new Response(status: $this->httpStatus, headers: $this->headers, body: $this->getBody());
|
||||
}
|
||||
|
||||
abstract public function getBody(): string;
|
||||
|
||||
public function setHeaders(array $headers): AbstractController {
|
||||
$this->headers = $headers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $httpStatus
|
||||
*/
|
||||
public function setHttpStatus(int $httpStatus): void {
|
||||
$this->httpStatus = $httpStatus;
|
||||
}
|
||||
|
||||
public function redirect(string $url, bool $permanently = false): void {
|
||||
$this->httpStatus = $permanently ? HttpStatus::MOVED_PERMANENTLY : HttpStatus::SEE_OTHER;
|
||||
$this->headers = array_merge($this->headers ?? [], ['location' => $url]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseURL(): string {
|
||||
$schemePort = ['http' => 80, 'https' => 443];
|
||||
return $this->request->getUri()->getScheme() .
|
||||
'://' .
|
||||
$this->request->getUri()->getHost() .
|
||||
($schemePort[$this->request->getUri()->getScheme()] !== $this->request->getUri()->getPort()
|
||||
? ':' . $this->request->getUri()->getPort()
|
||||
: '');
|
||||
}
|
||||
}
|
35
src/App/Controller/AbstractIPListController.php
Normal file
35
src/App/Controller/AbstractIPListController.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace OpenCCK\App\Controller;
|
||||
|
||||
use Amp\ByteStream\BufferException;
|
||||
use Amp\Http\Server\Request;
|
||||
|
||||
use OpenCCK\App\Service\IPListService;
|
||||
use OpenCCK\Infrastructure\API\App;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Throwable;
|
||||
|
||||
abstract class AbstractIPListController extends AbstractController {
|
||||
protected Logger $logger;
|
||||
protected IPListService $service;
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param array $headers
|
||||
* @throws BufferException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __construct(protected Request $request, protected array $headers = []) {
|
||||
parent::__construct($request, $this->headers);
|
||||
|
||||
$this->logger = App::getLogger();
|
||||
$this->service = IPListService::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getBody(): string;
|
||||
}
|
19
src/App/Controller/ControllerInterface.php
Normal file
19
src/App/Controller/ControllerInterface.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace OpenCCK\App\Controller;
|
||||
|
||||
use Amp\Http\Server\Request;
|
||||
use Amp\Http\Server\Response;
|
||||
|
||||
interface ControllerInterface {
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param array $headers
|
||||
*/
|
||||
public function __construct(Request $request, array $headers = []);
|
||||
|
||||
/**
|
||||
* @return Response
|
||||
*/
|
||||
public function __invoke(): Response;
|
||||
}
|
32
src/App/Controller/JsonController.php
Normal file
32
src/App/Controller/JsonController.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace OpenCCK\App\Controller;
|
||||
|
||||
class JsonController extends AbstractIPListController {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBody(): string {
|
||||
$this->setHeaders(['content-type' => 'application/json']);
|
||||
|
||||
$site = $this->request->getQueryParameter('site') ?? '';
|
||||
$data = $this->request->getQueryParameter('data') ?? '';
|
||||
if ($site == '') {
|
||||
if ($data == '') {
|
||||
return json_encode($this->service->sites);
|
||||
} else {
|
||||
$result = [];
|
||||
foreach ($this->service->sites as $site) {
|
||||
$result[$site->name] = $site->$data;
|
||||
}
|
||||
return json_encode($result);
|
||||
}
|
||||
} else {
|
||||
if ($data == '') {
|
||||
return json_encode($this->service->sites[$site]);
|
||||
} else {
|
||||
return json_encode($this->service->sites[$site]->$data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
src/App/Controller/MainController.php
Normal file
23
src/App/Controller/MainController.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace OpenCCK\App\Controller;
|
||||
|
||||
class MainController extends AbstractIPListController {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBody(): string {
|
||||
$this->setHeaders(['content-type' => 'text/html; charset=utf-8']);
|
||||
return $this->renderTemplate('index');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template
|
||||
* @return string
|
||||
*/
|
||||
private function renderTemplate(string $template): string {
|
||||
ob_start();
|
||||
include PATH_ROOT . '/src/App/Template/' . ucfirst($template) . 'Template.php';
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
41
src/App/Controller/MikrotikController.php
Normal file
41
src/App/Controller/MikrotikController.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace OpenCCK\App\Controller;
|
||||
|
||||
class MikrotikController extends AbstractIPListController {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBody(): string {
|
||||
$this->setHeaders(['content-type' => 'text/plain']);
|
||||
|
||||
$site = $this->request->getQueryParameter('site') ?? '';
|
||||
$data = $this->request->getQueryParameter('data') ?? '';
|
||||
if ($data == '') {
|
||||
return "# Error: The 'data' GET parameter is required in the URL to access this page, but it cannot have the value 'All'";
|
||||
}
|
||||
$response = '/ip firewall address-list' . "\n";
|
||||
if ($site == '') {
|
||||
foreach ($this->service->sites as $site) {
|
||||
$response .= $this->render($site->name, $site->$data);
|
||||
}
|
||||
return $response;
|
||||
} else {
|
||||
return $response . $this->render($site, $this->service->sites[$site]->$data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $site
|
||||
* @param iterable $array
|
||||
* @return string
|
||||
*/
|
||||
private function render(string $site, iterable $array): string {
|
||||
$response = '';
|
||||
$listName = str_replace(' ', '', $site);
|
||||
foreach ($array as $item) {
|
||||
$response .= 'add list=' . $listName . ' address=' . $item . ' comment=' . $listName . "\n";
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
36
src/App/Controller/TextController.php
Normal file
36
src/App/Controller/TextController.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace OpenCCK\App\Controller;
|
||||
|
||||
class TextController extends AbstractIPListController {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBody(): string {
|
||||
$this->setHeaders(['content-type' => 'text/plain']);
|
||||
|
||||
$site = $this->request->getQueryParameter('site') ?? '';
|
||||
$data = $this->request->getQueryParameter('data') ?? '';
|
||||
if ($data == '') {
|
||||
return "# Error: The 'data' GET parameter is required in the URL to access this page, but it cannot have the value 'All'";
|
||||
}
|
||||
|
||||
$response = '';
|
||||
if ($site == '') {
|
||||
foreach ($this->service->sites as $site) {
|
||||
$response .= $this->render($site->name, $site->$data);
|
||||
}
|
||||
return $response;
|
||||
} else {
|
||||
return $this->render($site, $this->service->sites[$site]->$data);
|
||||
}
|
||||
}
|
||||
|
||||
private function render(string $name, iterable $array): string {
|
||||
$response = '# ' . $name . ' ' . date('Y-m-d H:i:s') . "\n";
|
||||
foreach ($array as $item) {
|
||||
$response .= $item . "\n";
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
50
src/App/Service/IPListService.php
Normal file
50
src/App/Service/IPListService.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace OpenCCK\App\Service;
|
||||
|
||||
use OpenCCK\Domain\Entity\Site;
|
||||
use OpenCCK\Domain\Factory\SiteFactory;
|
||||
use OpenCCK\Infrastructure\API\App;
|
||||
|
||||
use Exception;
|
||||
use Monolog\Logger;
|
||||
|
||||
class IPListService {
|
||||
private static IPListService $_instance;
|
||||
private ?Logger $logger;
|
||||
|
||||
/**
|
||||
* @var array<string, Site>
|
||||
*/
|
||||
public array $sites = [];
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __construct(Logger $logger = null) {
|
||||
$this->logger = $logger ?? App::getLogger();
|
||||
|
||||
$dir = PATH_ROOT . '/config/';
|
||||
if (!is_dir($dir)) {
|
||||
throw new Exception('config directory not found');
|
||||
}
|
||||
foreach (scandir($dir) as $file) {
|
||||
if (str_ends_with($file, '.json')) {
|
||||
$this->loadConfig(substr($file, 0, -5), json_decode(file_get_contents($dir . $file)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ?Logger $logger
|
||||
* @return IPListService
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getInstance(Logger $logger = null): IPListService {
|
||||
return self::$_instance ??= new self($logger);
|
||||
}
|
||||
|
||||
private function loadConfig(string $name, object $config): void {
|
||||
$this->sites[$name] = SiteFactory::create($name, $config);
|
||||
}
|
||||
}
|
588
src/App/Template/IndexTemplate.php
Normal file
588
src/App/Template/IndexTemplate.php
Normal file
@@ -0,0 +1,588 @@
|
||||
<?php
|
||||
use OpenCCK\App\Controller\TextController;
|
||||
/** @var TextController $this */
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>IPList</title>
|
||||
<style>
|
||||
/* MVP.css v1.15 - https://github.com/andybrewer/mvp */
|
||||
:root {
|
||||
--active-brightness: 0.85;
|
||||
--border-radius: 5px;
|
||||
--box-shadow: 2px 2px 10px;
|
||||
--color-accent: #118bee15;
|
||||
--color-bg: #fff;
|
||||
--color-bg-secondary: #e9e9e9;
|
||||
--color-link: #118bee;
|
||||
--color-secondary: #920de9;
|
||||
--color-secondary-accent: #920de90b;
|
||||
--color-shadow: #f4f4f4;
|
||||
--color-table: #118bee;
|
||||
--color-text: #000;
|
||||
--color-text-secondary: #999;
|
||||
--color-scrollbar: #cacae8;
|
||||
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
--hover-brightness: 1.2;
|
||||
--justify-important: center;
|
||||
--justify-normal: left;
|
||||
--line-height: 1.5;
|
||||
--width-card: 285px;
|
||||
--width-card-medium: 460px;
|
||||
--width-card-wide: 800px;
|
||||
--width-content: 1080px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root[color-mode="user"] {
|
||||
--color-accent: #0097fc4f;
|
||||
--color-bg: #333;
|
||||
--color-bg-secondary: #555;
|
||||
--color-link: #0097fc;
|
||||
--color-secondary: #e20de9;
|
||||
--color-secondary-accent: #e20de94f;
|
||||
--color-shadow: #bbbbbb20;
|
||||
--color-table: #0097fc;
|
||||
--color-text: #f7f7f7;
|
||||
--color-text-secondary: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
article aside {
|
||||
background: var(--color-secondary-accent);
|
||||
border-left: 4px solid var(--color-secondary);
|
||||
padding: 0.01rem 0.8rem;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-family: var(--font-family);
|
||||
line-height: var(--line-height);
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
footer,
|
||||
header,
|
||||
main {
|
||||
margin: 0 auto;
|
||||
max-width: var(--width-content);
|
||||
padding: 3rem 1rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: none;
|
||||
height: 1px;
|
||||
margin: 4rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: var(--justify-important);
|
||||
}
|
||||
|
||||
section img,
|
||||
article img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
section pre {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
section aside {
|
||||
border: 1px solid var(--color-bg-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow) var(--color-shadow);
|
||||
margin: 1rem;
|
||||
padding: 1.25rem;
|
||||
width: var(--width-card);
|
||||
}
|
||||
|
||||
section aside:hover {
|
||||
box-shadow: var(--box-shadow) var(--color-bg-secondary);
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Headers */
|
||||
article header,
|
||||
div header,
|
||||
main header {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: var(--justify-important);
|
||||
}
|
||||
|
||||
header a b,
|
||||
header a em,
|
||||
header a i,
|
||||
header a strong {
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
header nav img {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
section header {
|
||||
padding-top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
nav {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 7rem;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
display: inline-block;
|
||||
margin: 0 0.5rem;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Nav Dropdown */
|
||||
nav ul li:hover ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
nav ul li ul {
|
||||
background: var(--color-bg);
|
||||
border: 1px solid var(--color-bg-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow) var(--color-shadow);
|
||||
display: none;
|
||||
height: auto;
|
||||
left: -2px;
|
||||
padding: .5rem 1rem;
|
||||
position: absolute;
|
||||
top: 1.7rem;
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
nav ul li ul::before {
|
||||
/* fill gap above to make mousing over them easier */
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: -0.5rem;
|
||||
height: 0.5rem;
|
||||
}
|
||||
|
||||
nav ul li ul li,
|
||||
nav ul li ul li a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
code,
|
||||
samp {
|
||||
background-color: var(--color-accent);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-text);
|
||||
display: inline-block;
|
||||
margin: 0 0.1rem;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
details {
|
||||
margin: 1.3rem 0;
|
||||
}
|
||||
|
||||
details summary {
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
line-height: var(--line-height);
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.1rem;
|
||||
}
|
||||
|
||||
ol li,
|
||||
ul li {
|
||||
padding: 0.2rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.75rem 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 1rem 0;
|
||||
max-width: var(--width-card-wide);
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
pre code,
|
||||
pre samp {
|
||||
display: block;
|
||||
max-width: var(--width-card-wide);
|
||||
padding: 0.5rem 2rem;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
small {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
sup {
|
||||
background-color: var(--color-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-bg);
|
||||
font-size: xx-small;
|
||||
font-weight: bold;
|
||||
margin: 0.2rem;
|
||||
padding: 0.2rem 0.3rem;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a {
|
||||
color: var(--color-link);
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
filter: brightness(var(--hover-brightness));
|
||||
}
|
||||
|
||||
a:active {
|
||||
filter: brightness(var(--active-brightness));
|
||||
}
|
||||
|
||||
a b,
|
||||
a em,
|
||||
a i,
|
||||
a strong,
|
||||
button,
|
||||
input[type="submit"] {
|
||||
border-radius: var(--border-radius);
|
||||
display: inline-block;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
line-height: var(--line-height);
|
||||
margin: 0.5rem 0;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type="submit"] {
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
button:hover,
|
||||
input[type="submit"]:hover {
|
||||
cursor: pointer;
|
||||
filter: brightness(var(--hover-brightness));
|
||||
}
|
||||
|
||||
button:active,
|
||||
input[type="submit"]:active {
|
||||
filter: brightness(var(--active-brightness));
|
||||
}
|
||||
|
||||
a b,
|
||||
a strong,
|
||||
button,
|
||||
input[type="submit"] {
|
||||
background-color: var(--color-link);
|
||||
border: 2px solid var(--color-link);
|
||||
color: var(--color-bg);
|
||||
}
|
||||
|
||||
a em,
|
||||
a i {
|
||||
border: 2px solid var(--color-link);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-link);
|
||||
display: inline-block;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
article aside a {
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
/* Images */
|
||||
figure {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
figure img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
figure figcaption {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
button:disabled,
|
||||
input:disabled {
|
||||
background: var(--color-bg-secondary);
|
||||
border-color: var(--color-bg-secondary);
|
||||
color: var(--color-text-secondary);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
button[disabled]:hover,
|
||||
input[type="submit"][disabled]:hover {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
form {
|
||||
border: 1px solid var(--color-bg-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow) var(--color-shadow);
|
||||
display: block;
|
||||
max-width: var(--width-card-wide);
|
||||
min-width: var(--width-card);
|
||||
padding: 1.5rem;
|
||||
text-align: var(--justify-normal);
|
||||
}
|
||||
|
||||
form header {
|
||||
margin: 1.5rem 0;
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
input,
|
||||
label,
|
||||
select,
|
||||
textarea {
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
max-width: var(--width-card-wide);
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input[type="checkbox"]+label,
|
||||
input[type="radio"]+label {
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
padding: 0.4rem 0;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
border: 1px solid var(--color-bg-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.4rem 0.8rem;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
textarea {
|
||||
width: calc(100% - 1.6rem);
|
||||
}
|
||||
|
||||
input[readonly],
|
||||
textarea[readonly] {
|
||||
background-color: var(--color-bg-secondary);
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
/* Popups */
|
||||
dialog {
|
||||
border: 1px solid var(--color-bg-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow) var(--color-shadow);
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 50%;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
border: 1px solid var(--color-bg-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
border-spacing: 0;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table td,
|
||||
table th,
|
||||
table tr {
|
||||
padding: 0.4rem 0.8rem;
|
||||
text-align: var(--justify-important);
|
||||
}
|
||||
|
||||
table thead {
|
||||
background-color: var(--color-table);
|
||||
border-collapse: collapse;
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-bg);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table thead tr:first-child th:first-child {
|
||||
border-top-left-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
table thead tr:first-child th:last-child {
|
||||
border-top-right-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
table thead th:first-child,
|
||||
table tr td:first-child {
|
||||
text-align: var(--justify-normal);
|
||||
}
|
||||
|
||||
table tr:nth-child(even) {
|
||||
background-color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* Quotes */
|
||||
blockquote {
|
||||
display: block;
|
||||
font-size: x-large;
|
||||
line-height: var(--line-height);
|
||||
margin: 1rem auto;
|
||||
max-width: var(--width-card-medium);
|
||||
padding: 1.5rem 1rem;
|
||||
text-align: var(--justify-important);
|
||||
}
|
||||
|
||||
blockquote footer {
|
||||
color: var(--color-text-secondary);
|
||||
display: block;
|
||||
font-size: small;
|
||||
line-height: var(--line-height);
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
/* Scrollbars */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--color-scrollbar) transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: var(--color-scrollbar);
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form action="" method="get">
|
||||
<section>
|
||||
<label>
|
||||
Format:
|
||||
<select name="format">
|
||||
<option value="json">JSON</option>
|
||||
<option value="text">Text</option>
|
||||
<option value="mikrotik">MikroTik</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Site:
|
||||
<select name="site">
|
||||
<option value="">All</option>
|
||||
<?php foreach ($this->service->sites as $site): ?>
|
||||
<option value="<?= $site->name ?>"><?= $site->name ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Data:
|
||||
<select name="data">
|
||||
<option value="">All</option>
|
||||
<option value="domains">domains</option>
|
||||
<option value="ip4">ip4</option>
|
||||
<option value="cidr4">cidr4</option>
|
||||
<option value="ip6">ip6</option>
|
||||
<option value="cidr6">cidr6</option>
|
||||
</select>
|
||||
</label>
|
||||
</section>
|
||||
<section>
|
||||
<button type="submit">Submit</button>
|
||||
</section>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user