modified: xbotcontrol.php
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
/vendor
|
/vendor
|
||||||
.env
|
.env
|
||||||
|
composer.lock
|
||||||
requests.sqlite3
|
requests.sqlite3
|
||||||
|
requests.sqlite3-shm
|
||||||
|
requests.sqlite3-wal
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
"vlucas/phpdotenv": "^5.6",
|
"vlucas/phpdotenv": "^5.6",
|
||||||
"react/cache": "^1.2",
|
"react/cache": "^1.2",
|
||||||
"clue/mq-react": "^1.6",
|
"clue/mq-react": "^1.6",
|
||||||
"smarty/smarty": "^5.4"
|
"smarty/smarty": "^5.4",
|
||||||
|
"react/promise-timer": "^1.11"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|||||||
81
composer.lock
generated
81
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "2ff629509c131622c46d8c67505d54b2",
|
"content-hash": "8e299b08324c21b9f02c215ffc70c444",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "clue/framework-x",
|
"name": "clue/framework-x",
|
||||||
@@ -1160,6 +1160,85 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-05-24T10:39:05+00:00"
|
"time": "2024-05-24T10:39:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "react/promise-timer",
|
||||||
|
"version": "v1.11.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reactphp/promise-timer.git",
|
||||||
|
"reference": "4f70306ed66b8b44768941ca7f142092600fafc1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reactphp/promise-timer/zipball/4f70306ed66b8b44768941ca7f142092600fafc1",
|
||||||
|
"reference": "4f70306ed66b8b44768941ca7f142092600fafc1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3",
|
||||||
|
"react/event-loop": "^1.2",
|
||||||
|
"react/promise": "^3.2 || ^2.7.0 || ^1.2.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/functions_include.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"React\\Promise\\Timer\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christian Lück",
|
||||||
|
"email": "christian@clue.engineering",
|
||||||
|
"homepage": "https://clue.engineering/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cees-Jan Kiewiet",
|
||||||
|
"email": "reactphp@ceesjankiewiet.nl",
|
||||||
|
"homepage": "https://wyrihaximus.net/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jan Sorgalla",
|
||||||
|
"email": "jsorgalla@gmail.com",
|
||||||
|
"homepage": "https://sorgalla.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Chris Boden",
|
||||||
|
"email": "cboden@gmail.com",
|
||||||
|
"homepage": "https://cboden.dev/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
|
||||||
|
"homepage": "https://github.com/reactphp/promise-timer",
|
||||||
|
"keywords": [
|
||||||
|
"async",
|
||||||
|
"event-loop",
|
||||||
|
"promise",
|
||||||
|
"reactphp",
|
||||||
|
"timeout",
|
||||||
|
"timer"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/reactphp/promise-timer/issues",
|
||||||
|
"source": "https://github.com/reactphp/promise-timer/tree/v1.11.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://opencollective.com/reactphp",
|
||||||
|
"type": "open_collective"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-06-04T14:27:45+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "react/socket",
|
"name": "react/socket",
|
||||||
"version": "v1.16.0",
|
"version": "v1.16.0",
|
||||||
|
|||||||
@@ -0,0 +1,326 @@
|
|||||||
|
<?php
|
||||||
|
/* Smarty version 5.4.2, created on 2024-12-17 11:29:28
|
||||||
|
from 'file:index.tpl' */
|
||||||
|
|
||||||
|
/* @var \Smarty\Template $_smarty_tpl */
|
||||||
|
if ($_smarty_tpl->getCompiled()->isFresh($_smarty_tpl, array (
|
||||||
|
'version' => '5.4.2',
|
||||||
|
'unifunc' => 'content_676160989e3635_07407148',
|
||||||
|
'has_nocache_code' => false,
|
||||||
|
'file_dependency' =>
|
||||||
|
array (
|
||||||
|
'affb24851ed623b62affa076808377b28b01c478' =>
|
||||||
|
array (
|
||||||
|
0 => 'index.tpl',
|
||||||
|
1 => 1734434957,
|
||||||
|
2 => 'file',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'includes' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
))) {
|
||||||
|
function content_676160989e3635_07407148 (\Smarty\Template $_smarty_tpl) {
|
||||||
|
$_smarty_current_dir = '/home/l/public_html/xbotcontrol/smarty/template';
|
||||||
|
?><!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>XBotControl</title>
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<?php echo '<script'; ?>
|
||||||
|
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous">
|
||||||
|
<?php echo '</script'; ?>
|
||||||
|
>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/bootstrap-table.min.css">
|
||||||
|
<?php echo '<script'; ?>
|
||||||
|
src="https://code.jquery.com/jquery-3.7.1.min.js"><?php echo '</script'; ?>
|
||||||
|
>
|
||||||
|
<!-- Latest compiled and minified JavaScript -->
|
||||||
|
<?php echo '<script'; ?>
|
||||||
|
src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/bootstrap-table.min.js"><?php echo '</script'; ?>
|
||||||
|
>
|
||||||
|
<!-- Latest compiled and minified Locales -->
|
||||||
|
<?php echo '<script'; ?>
|
||||||
|
src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/locale/bootstrap-table-zh-CN.min.js"><?php echo '</script'; ?>
|
||||||
|
>
|
||||||
|
<link rel="stylesheet" type="text/css"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/extensions/filter-control/bootstrap-table-filter-control.css">
|
||||||
|
<?php echo '<script'; ?>
|
||||||
|
|
||||||
|
src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/extensions/filter-control/bootstrap-table-filter-control.js">
|
||||||
|
<?php echo '</script'; ?>
|
||||||
|
>
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.1/css/all.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="main-menu " style="background-color: #003366;">
|
||||||
|
|
||||||
|
<div class="container text-center text-light">
|
||||||
|
<nav class="navbar navbar-expand-lg text-light">
|
||||||
|
<div class="container">
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" onclick="initializeTable('latest_requests');">Latest</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" href="<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/contacts"><?php echo htmlspecialchars((string) ($_smarty_tpl->getConfigVariable('linkContacts')), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" href="<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/lists/read"><?php echo htmlspecialchars((string) ($_smarty_tpl->getConfigVariable('linkLists')), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php if ($_SESSION['user_role'] == 'admin') {?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light"
|
||||||
|
href="<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/lists/show_membership"><?php echo htmlspecialchars((string) ($_smarty_tpl->getConfigVariable('linkMembership')), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" href="<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/users/read"><?php echo htmlspecialchars((string) ($_smarty_tpl->getConfigVariable('linkUsers')), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" href="<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/log/read"><?php echo htmlspecialchars((string) ($_smarty_tpl->getConfigVariable('linkLog')), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php }?>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn btn-outline-success" href="<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/login?logout=true"><?php echo htmlspecialchars((string) ($_smarty_tpl->getConfigVariable('logOutbtn')), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
|
||||||
|
(<?php echo htmlspecialchars((string) ($_SESSION['username']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
)</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div id="main-body">
|
||||||
|
<div class="container" style="max-width: 95%;">
|
||||||
|
<div class="row p-3">
|
||||||
|
<div class="col-10">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="toolbar" class="row ">
|
||||||
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">Limit</div>
|
||||||
|
|
||||||
|
|
||||||
|
<select id="limit" name="limit" class="form-control mr-3">
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
<option value="100" selected>100</option>
|
||||||
|
<option value="200">200</option>
|
||||||
|
<option value="500">500</option>
|
||||||
|
<option value="1000">1000</option>
|
||||||
|
<option value="100000">100000</option>
|
||||||
|
<option value="0">0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">From</div>
|
||||||
|
|
||||||
|
<input type="datetime-local" id="date-from" name="date-from" class="form-control mr-3" >
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">To</div>
|
||||||
|
|
||||||
|
<input type="datetime-local" id="date-to" name="date-to" class="form-control mr-3">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table id="table">
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php echo '<script'; ?>
|
||||||
|
>
|
||||||
|
document.getElementById('date-from').addEventListener('change', refreshTable);
|
||||||
|
|
||||||
|
document.getElementById('limit').addEventListener('change', refreshTable);
|
||||||
|
|
||||||
|
function refreshTable() {
|
||||||
|
$('#table').bootstrapTable('refresh');
|
||||||
|
}
|
||||||
|
window.onload = function() {
|
||||||
|
const dateFrom = document.getElementById('date-from');
|
||||||
|
const dateTo = document.getElementById('date-to');
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const yesterday = new Date(today);
|
||||||
|
yesterday.setDate(today.getDate() - 1);
|
||||||
|
|
||||||
|
const tomorrow = new Date(today);
|
||||||
|
tomorrow.setDate(today.getDate() + 1);
|
||||||
|
|
||||||
|
dateFrom.value = yesterday.toISOString().slice(0, 16);
|
||||||
|
dateTo.value = tomorrow.toISOString().slice(0, 16);
|
||||||
|
};
|
||||||
|
document.getElementById('date-to').addEventListener('change', refreshTable);
|
||||||
|
|
||||||
|
|
||||||
|
function initializeTable(latest_requests) {
|
||||||
|
var url = location.pathname + '/api/report/' + latest_requests;
|
||||||
|
var $table = $('#table');
|
||||||
|
|
||||||
|
if ($table.length) {
|
||||||
|
$table.bootstrapTable('destroy');
|
||||||
|
}
|
||||||
|
|
||||||
|
$.get(url, function(response) {
|
||||||
|
$table.bootstrapTable({
|
||||||
|
url: url,
|
||||||
|
sortable: true,
|
||||||
|
toolbar: '#toolbar',
|
||||||
|
showRefresh: true,
|
||||||
|
iconsPrefix: 'fa',
|
||||||
|
showColumns: true,
|
||||||
|
classes: ['table', 'table-borderless', 'table-hover', 'table-striped'],
|
||||||
|
filterControl: true,
|
||||||
|
searchable: true,
|
||||||
|
pagination: false,
|
||||||
|
sidePagination: "server",
|
||||||
|
serverSort: false,
|
||||||
|
columns: response.columns,
|
||||||
|
queryParams: queryParams,
|
||||||
|
loadingFontSize: '12px'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryParams(params) {
|
||||||
|
const limit = document.getElementById('limit').value;
|
||||||
|
const from = document.getElementById('date-from').value;
|
||||||
|
const to = document.getElementById('date-to').value;
|
||||||
|
|
||||||
|
params.limit = limit;
|
||||||
|
params.from = from;
|
||||||
|
params.to = to;
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function buttons() {
|
||||||
|
<?php if ($_SESSION['user_role'] == 'admin') {?>
|
||||||
|
return {
|
||||||
|
btnAdd: {
|
||||||
|
text: 'Add new list',
|
||||||
|
icon: 'fa-plus',
|
||||||
|
event: function() {
|
||||||
|
// Prompt the user for a new list name
|
||||||
|
const newListName = prompt('Enter new list name:');
|
||||||
|
|
||||||
|
// Only proceed if the user provides a valid list name
|
||||||
|
if (newListName) {
|
||||||
|
// Define the URL where the form needs to be posted
|
||||||
|
const url = '<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/lists/create'; // Replace with actual URL
|
||||||
|
|
||||||
|
// Create a new hidden form element
|
||||||
|
const form = document.createElement('form');
|
||||||
|
form.method = 'POST';
|
||||||
|
form.action = url;
|
||||||
|
|
||||||
|
// Create hidden input to store the list name
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'hidden';
|
||||||
|
input.name = 'listName'; // The name expected by the server
|
||||||
|
input.value = newListName;
|
||||||
|
|
||||||
|
// Append the input to the form
|
||||||
|
form.appendChild(input);
|
||||||
|
|
||||||
|
// Append the form to the body to make it part of the DOM
|
||||||
|
document.body.appendChild(form);
|
||||||
|
|
||||||
|
// Submit the form automatically
|
||||||
|
form.submit();
|
||||||
|
} else {
|
||||||
|
// Handle case where user cancels or enters an empty name
|
||||||
|
alert('List creation was cancelled or name was empty.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attributes: {
|
||||||
|
title: 'Add a new list to the table'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<?php } else { ?>
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<?php }?>
|
||||||
|
}
|
||||||
|
|
||||||
|
function listFormatter(value, row, index) {
|
||||||
|
var editBtn = '<a class="btn" href="<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/lists/edit/' + row.list_id + '" title="Edit"><i class="fa-solid fa-pen-to-square"></i></a> ';
|
||||||
|
|
||||||
|
var showBtn = '<a class="btn" href="<?php echo htmlspecialchars((string) ($_ENV['BASEURI']), ENT_QUOTES, 'UTF-8');?>
|
||||||
|
/lists/show/' + row.list_id + '" title="Show"><i class="fa-solid fa-eye"></i></a>';
|
||||||
|
<?php if ($_SESSION['user_role'] == 'admin') {?>
|
||||||
|
return [showBtn, editBtn, value, ].join('')
|
||||||
|
<?php } else { ?>
|
||||||
|
return [showBtn, value, ].join('')
|
||||||
|
|
||||||
|
<?php }?>
|
||||||
|
}
|
||||||
|
<?php echo '</script'; ?>
|
||||||
|
>
|
||||||
|
<footer class="centro-blue text-white text-center py-3">
|
||||||
|
<div class="footer">
|
||||||
|
<div class="container text-center centro-blue text-light">
|
||||||
|
<h6>Copyright 2024
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body><?php }
|
||||||
|
}
|
||||||
273
smarty/template/index.tpl
Normal file
273
smarty/template/index.tpl
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>XBotControl</title>
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous">
|
||||||
|
</script>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/bootstrap-table.min.css">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||||
|
<!-- Latest compiled and minified JavaScript -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/bootstrap-table.min.js"></script>
|
||||||
|
<!-- Latest compiled and minified Locales -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/locale/bootstrap-table-zh-CN.min.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/extensions/filter-control/bootstrap-table-filter-control.css">
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/extensions/filter-control/bootstrap-table-filter-control.js">
|
||||||
|
</script>
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.1/css/all.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="main-menu " style="background-color: #003366;">
|
||||||
|
|
||||||
|
<div class="container text-center text-light">
|
||||||
|
<nav class="navbar navbar-expand-lg text-light">
|
||||||
|
<div class="container">
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" onclick="initializeTable('latest_requests');">Latest</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" href="{$smarty.env.BASEURI}/contacts">{#linkContacts#}</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" href="{$smarty.env.BASEURI}/lists/read">{#linkLists#}</a>
|
||||||
|
</li>
|
||||||
|
{if $smarty.session.user_role == 'admin'}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light"
|
||||||
|
href="{$smarty.env.BASEURI}/lists/show_membership">{#linkMembership#}</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" href="{$smarty.env.BASEURI}/users/read">{#linkUsers#}</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn text-light" href="{$smarty.env.BASEURI}/log/read">{#linkLog#}</a>
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn btn-outline-success" href="{$smarty.env.BASEURI}/login?logout=true">{#logOutbtn#}
|
||||||
|
({$smarty.session.username})</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div id="main-body">
|
||||||
|
<div class="container" style="max-width: 95%;">
|
||||||
|
<div class="row p-3">
|
||||||
|
<div class="col-10">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="toolbar" class="row ">
|
||||||
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">Limit</div>
|
||||||
|
|
||||||
|
|
||||||
|
<select id="limit" name="limit" class="form-control mr-3">
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
<option value="100" selected>100</option>
|
||||||
|
<option value="200">200</option>
|
||||||
|
<option value="500">500</option>
|
||||||
|
<option value="1000">1000</option>
|
||||||
|
<option value="100000">100000</option>
|
||||||
|
<option value="0">0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">From</div>
|
||||||
|
|
||||||
|
<input type="datetime-local" id="date-from" name="date-from" class="form-control mr-3" >
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">To</div>
|
||||||
|
|
||||||
|
<input type="datetime-local" id="date-to" name="date-to" class="form-control mr-3">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table id="table">
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById('date-from').addEventListener('change', refreshTable);
|
||||||
|
|
||||||
|
document.getElementById('limit').addEventListener('change', refreshTable);
|
||||||
|
|
||||||
|
function refreshTable() {
|
||||||
|
$('#table').bootstrapTable('refresh');
|
||||||
|
}
|
||||||
|
window.onload = function() {
|
||||||
|
const dateFrom = document.getElementById('date-from');
|
||||||
|
const dateTo = document.getElementById('date-to');
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const yesterday = new Date(today);
|
||||||
|
yesterday.setDate(today.getDate() - 1);
|
||||||
|
|
||||||
|
const tomorrow = new Date(today);
|
||||||
|
tomorrow.setDate(today.getDate() + 1);
|
||||||
|
|
||||||
|
dateFrom.value = yesterday.toISOString().slice(0, 16);
|
||||||
|
dateTo.value = tomorrow.toISOString().slice(0, 16);
|
||||||
|
};
|
||||||
|
document.getElementById('date-to').addEventListener('change', refreshTable);
|
||||||
|
|
||||||
|
|
||||||
|
function initializeTable(latest_requests) {
|
||||||
|
var url = location.pathname + '/api/report/' + latest_requests;
|
||||||
|
var $table = $('#table');
|
||||||
|
|
||||||
|
if ($table.length) {
|
||||||
|
$table.bootstrapTable('destroy');
|
||||||
|
}
|
||||||
|
|
||||||
|
$.get(url, function(response) {
|
||||||
|
$table.bootstrapTable({
|
||||||
|
url: url,
|
||||||
|
sortable: true,
|
||||||
|
toolbar: '#toolbar',
|
||||||
|
showRefresh: true,
|
||||||
|
iconsPrefix: 'fa',
|
||||||
|
showColumns: true,
|
||||||
|
classes: ['table', 'table-borderless', 'table-hover', 'table-striped'],
|
||||||
|
filterControl: true,
|
||||||
|
searchable: true,
|
||||||
|
pagination: false,
|
||||||
|
sidePagination: "server",
|
||||||
|
serverSort: false,
|
||||||
|
columns: response.columns,
|
||||||
|
queryParams: queryParams,
|
||||||
|
loadingFontSize: '12px'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryParams(params) {
|
||||||
|
const limit = document.getElementById('limit').value;
|
||||||
|
const from = document.getElementById('date-from').value;
|
||||||
|
const to = document.getElementById('date-to').value;
|
||||||
|
|
||||||
|
params.limit = limit;
|
||||||
|
params.from = from;
|
||||||
|
params.to = to;
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function buttons() {
|
||||||
|
{if $smarty.session.user_role == 'admin'}
|
||||||
|
return {
|
||||||
|
btnAdd: {
|
||||||
|
text: 'Add new list',
|
||||||
|
icon: 'fa-plus',
|
||||||
|
event: function() {
|
||||||
|
// Prompt the user for a new list name
|
||||||
|
const newListName = prompt('Enter new list name:');
|
||||||
|
|
||||||
|
// Only proceed if the user provides a valid list name
|
||||||
|
if (newListName) {
|
||||||
|
// Define the URL where the form needs to be posted
|
||||||
|
const url = '{$smarty.env.BASEURI}/lists/create'; // Replace with actual URL
|
||||||
|
|
||||||
|
// Create a new hidden form element
|
||||||
|
const form = document.createElement('form');
|
||||||
|
form.method = 'POST';
|
||||||
|
form.action = url;
|
||||||
|
|
||||||
|
// Create hidden input to store the list name
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'hidden';
|
||||||
|
input.name = 'listName'; // The name expected by the server
|
||||||
|
input.value = newListName;
|
||||||
|
|
||||||
|
// Append the input to the form
|
||||||
|
form.appendChild(input);
|
||||||
|
|
||||||
|
// Append the form to the body to make it part of the DOM
|
||||||
|
document.body.appendChild(form);
|
||||||
|
|
||||||
|
// Submit the form automatically
|
||||||
|
form.submit();
|
||||||
|
} else {
|
||||||
|
// Handle case where user cancels or enters an empty name
|
||||||
|
alert('List creation was cancelled or name was empty.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attributes: {
|
||||||
|
title: 'Add a new list to the table'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{else}
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{/if}
|
||||||
|
}
|
||||||
|
|
||||||
|
function listFormatter(value, row, index) {
|
||||||
|
var editBtn = '<a class="btn" href="{$smarty.env.BASEURI}/lists/edit/' + row.list_id + '" title="Edit"><i class="fa-solid fa-pen-to-square"></i></a> ';
|
||||||
|
|
||||||
|
var showBtn = '<a class="btn" href="{$smarty.env.BASEURI}/lists/show/' + row.list_id + '" title="Show"><i class="fa-solid fa-eye"></i></a>';
|
||||||
|
{if $smarty.session.user_role == 'admin'}
|
||||||
|
return [showBtn, editBtn, value, ].join('')
|
||||||
|
{else}
|
||||||
|
return [showBtn, value, ].join('')
|
||||||
|
|
||||||
|
{/if}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<footer class="centro-blue text-white text-center py-3">
|
||||||
|
<div class="footer">
|
||||||
|
<div class="container text-center centro-blue text-light">
|
||||||
|
<h6>Copyright 2024
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
25
src/Classes/LoadStat.php
Normal file
25
src/Classes/LoadStat.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace XBotControl\Classes;
|
||||||
|
|
||||||
|
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
|
||||||
|
|
||||||
|
class LoadStat
|
||||||
|
{
|
||||||
|
|
||||||
|
public static function saveLoad1()
|
||||||
|
{
|
||||||
|
$load = sys_getloadavg();
|
||||||
|
if (!$load) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$query = "INSERT OR IGNORE INTO load (load1, rowid) VALUES (?,?);";
|
||||||
|
$params = [$load['0'], time()];
|
||||||
|
|
||||||
|
\XBotControl\Storage::getInstance()->db->query($query, $params);
|
||||||
|
}
|
||||||
|
}
|
||||||
132
src/Classes/Report.php
Normal file
132
src/Classes/Report.php
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace XBotControl\Classes;
|
||||||
|
|
||||||
|
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class Report
|
||||||
|
{
|
||||||
|
|
||||||
|
public static function latest_requests(ServerRequestInterface $request): PromiseInterface
|
||||||
|
{
|
||||||
|
$columnsDefinition = [
|
||||||
|
[
|
||||||
|
'title' => 'id',
|
||||||
|
'field' => 'id',
|
||||||
|
'visible' => false,
|
||||||
|
'sortable' => true,
|
||||||
|
'filterControl' => 'input',
|
||||||
|
'widthUnit' => 'input',
|
||||||
|
'width' => 'input',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => 'ip',
|
||||||
|
'field' => 'ip',
|
||||||
|
'sortable' => true,
|
||||||
|
'filterControl' => 'input',
|
||||||
|
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => 'domain',
|
||||||
|
'field' => 'domain',
|
||||||
|
'sortable' => true,
|
||||||
|
'visible' => false,
|
||||||
|
'filterControl' => 'input',
|
||||||
|
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => 'path',
|
||||||
|
'field' => 'path',
|
||||||
|
'sortable' => true,
|
||||||
|
'filterControl' => 'input',
|
||||||
|
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => 'useragent',
|
||||||
|
'field' => 'useragent',
|
||||||
|
'sortable' => true,
|
||||||
|
'filterControl' => 'input',
|
||||||
|
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => 'load',
|
||||||
|
'field' => 'load',
|
||||||
|
'sortable' => true,
|
||||||
|
'filterControl' => 'input',
|
||||||
|
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => 'datetime',
|
||||||
|
'field' => 'datetime',
|
||||||
|
'sortable' => true,
|
||||||
|
'filterControl' => 'input',
|
||||||
|
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
$sql = "SELECT
|
||||||
|
req.rowid AS id,
|
||||||
|
ip.data AS ip,
|
||||||
|
domain.data AS domain,
|
||||||
|
path.data AS path,
|
||||||
|
useragent.data AS useragent,
|
||||||
|
headers.data AS headers ,
|
||||||
|
(SELECT load.load1
|
||||||
|
FROM load
|
||||||
|
WHERE load.rowid >= req.timestamp
|
||||||
|
ORDER BY load.rowid DESC LIMIT 1) AS load,
|
||||||
|
datetime(req.timestamp, 'auto') AS datetime
|
||||||
|
|
||||||
|
FROM
|
||||||
|
request req
|
||||||
|
LEFT JOIN
|
||||||
|
ip ON req.id_ip = ip.rowid
|
||||||
|
LEFT JOIN
|
||||||
|
domain ON req.id_domain = domain.rowid
|
||||||
|
LEFT JOIN
|
||||||
|
path ON req.id_path = path.rowid
|
||||||
|
LEFT JOIN
|
||||||
|
useragent ON req.id_useragent = useragent.rowid
|
||||||
|
LEFT JOIN
|
||||||
|
headers ON req.id_headers = headers.rowid
|
||||||
|
WHERE 1=1 ";
|
||||||
|
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
$query = $request->getQueryParams();
|
||||||
|
if (isset($query['filter'])) {
|
||||||
|
$filter = json_decode($request->getQueryParams()['filter'], true);
|
||||||
|
} else {
|
||||||
|
$filter = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($filter as $field => $value) {
|
||||||
|
$sql .= 'AND ' . $field . ' LIKE ? ';
|
||||||
|
$params[] = '%' . $value . '%';
|
||||||
|
}
|
||||||
|
$sql .= " AND req.timestamp BETWEEN ? AND ? ";
|
||||||
|
$sql .= ' ORDER BY req.rowid DESC ';
|
||||||
|
$sql .= ' LIMIT ? ;';
|
||||||
|
$params[] = strtotime($request->getQueryParams()['from'] ?? 'yesterday');
|
||||||
|
$params[] = strtotime($request->getQueryParams()['to'] ?? 'now');
|
||||||
|
$params[] = (int)$request->getQueryParams()['limit'] ?? 100;
|
||||||
|
|
||||||
|
return \XBotControl\Storage::getInstance()->db->query($sql, $params)->then(function ($result) use ($columnsDefinition) {
|
||||||
|
return [
|
||||||
|
"columns" => $columnsDefinition,
|
||||||
|
"rows" => $result->rows,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/Classes/Schedule.php
Normal file
62
src/Classes/Schedule.php
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace XBotControl\Classes;
|
||||||
|
|
||||||
|
use React\EventLoop\Loop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Schedule
|
||||||
|
*
|
||||||
|
* Manages the scheduling and execution of periodic tasks, ensuring tasks with the same interval
|
||||||
|
* are distributed to avoid simultaneous execution.
|
||||||
|
*/
|
||||||
|
class Schedule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A predefined schedule of tasks to be executed.
|
||||||
|
*
|
||||||
|
* @const array SCHEDULE
|
||||||
|
* - Each item is an array with:
|
||||||
|
* - 'interval': Interval time in seconds.
|
||||||
|
* - 'task': The callable to execute.
|
||||||
|
*/
|
||||||
|
private const SCHEDULE = [
|
||||||
|
['interval' => 60, 'task' => [\XBotControl\Classes\LoadStat::class, 'saveLoad1']],
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes and runs the task scheduler.
|
||||||
|
*
|
||||||
|
* Loops through the SCHEDULE array, setting up periodic timers
|
||||||
|
* using React's event loop. If multiple tasks share the same interval,
|
||||||
|
* they are distributed by adding evenly spaced offsets to avoid collisions.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function run(): void
|
||||||
|
{
|
||||||
|
$tasksByInterval = [];
|
||||||
|
|
||||||
|
// Group tasks by interval
|
||||||
|
foreach (self::SCHEDULE as $schedule) {
|
||||||
|
$interval = $schedule['interval'];
|
||||||
|
$tasksByInterval[$interval][] = $schedule['task'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule tasks for each interval
|
||||||
|
foreach ($tasksByInterval as $interval => $tasks) {
|
||||||
|
$taskCount = count($tasks);
|
||||||
|
foreach ($tasks as $index => $task) {
|
||||||
|
// Distribute tasks evenly within the interval
|
||||||
|
$offset = ($index / $taskCount) * $interval;
|
||||||
|
|
||||||
|
Loop::addTimer($offset, function () use ($interval, $task) {
|
||||||
|
Loop::addPeriodicTimer($interval, fn() => call_user_func($task));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/Controllers/APIController.php
Normal file
28
src/Controllers/APIController.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace XBotControl\Controllers;
|
||||||
|
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class APIController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request): PromiseInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
switch ($request->getAttribute('action')) {
|
||||||
|
case 'report':
|
||||||
|
return call_user_func([\XBotControl\Classes\Report::class, $request->getAttribute('resource')], $request)->then(function ($result) {
|
||||||
|
return \React\Http\Message\Response::json($result);
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return \React\Http\Message\Response::json(
|
||||||
|
['empty_response']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/Controllers/IndexController.php
Normal file
25
src/Controllers/IndexController.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace XBotControl\Controllers;
|
||||||
|
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class IndexController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request): \React\Http\Message\Response
|
||||||
|
{
|
||||||
|
$smarty = \XBotControl\Config::getInstance()->smarty;
|
||||||
|
$smarty->assign([
|
||||||
|
|
||||||
|
]);
|
||||||
|
|
||||||
|
return \React\Http\Message\Response::html(
|
||||||
|
$smarty->fetch('index.tpl')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ class InitTables
|
|||||||
|
|
||||||
public static function create():PromiseInterface
|
public static function create():PromiseInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
$db = Storage::getInstance()->db;
|
$db = Storage::getInstance()->db;
|
||||||
return $db->exec("CREATE TABLE IF NOT EXISTS ip (data TEXT UNIQUE NOT NULL CHECK (data LIKE '%'), CONSTRAINT valid_ip CHECK (data LIKE '%.%' OR data LIKE '%:%')) STRICT ;")
|
return $db->exec("CREATE TABLE IF NOT EXISTS ip (data TEXT UNIQUE NOT NULL CHECK (data LIKE '%'), CONSTRAINT valid_ip CHECK (data LIKE '%.%' OR data LIKE '%:%')) STRICT ;")
|
||||||
->then(function () use ($db) {
|
->then(function () use ($db) {
|
||||||
@@ -27,27 +28,13 @@ class InitTables
|
|||||||
return $db->exec('CREATE TABLE IF NOT EXISTS request ( id_ip INTEGER NOT NULL, id_method INTEGER NOT NULL, id_domain INTEGER NOT NULL, id_path INTEGER NOT NULL, id_useragent INTEGER NOT NULL, id_headers INTEGER NOT NULL, timestamp INTEGER NOT NULL, FOREIGN KEY (id_ip) REFERENCES ip(rowid), FOREIGN KEY (id_domain) REFERENCES domain(rowid), FOREIGN KEY (id_path) REFERENCES path(rowid), FOREIGN KEY (id_useragent) REFERENCES useragent(rowid), FOREIGN KEY (id_headers) REFERENCES headers(rowid) ) STRICT ;');
|
return $db->exec('CREATE TABLE IF NOT EXISTS request ( id_ip INTEGER NOT NULL, id_method INTEGER NOT NULL, id_domain INTEGER NOT NULL, id_path INTEGER NOT NULL, id_useragent INTEGER NOT NULL, id_headers INTEGER NOT NULL, timestamp INTEGER NOT NULL, FOREIGN KEY (id_ip) REFERENCES ip(rowid), FOREIGN KEY (id_domain) REFERENCES domain(rowid), FOREIGN KEY (id_path) REFERENCES path(rowid), FOREIGN KEY (id_useragent) REFERENCES useragent(rowid), FOREIGN KEY (id_headers) REFERENCES headers(rowid) ) STRICT ;');
|
||||||
})->then(function () use ($db) {
|
})->then(function () use ($db) {
|
||||||
return $db->exec('CREATE TABLE IF NOT EXISTS bot ( name TEXT NOT NULL, keyword TEXT NULL ) STRICT ;');
|
return $db->exec('CREATE TABLE IF NOT EXISTS bot ( name TEXT NOT NULL, keyword TEXT NULL ) STRICT ;');
|
||||||
|
})->then(function () use ($db) {
|
||||||
|
return $db->exec('CREATE TABLE IF NOT EXISTS settings ( key TEXT UNIQUE NOT NULL, value TEXT NULL ) STRICT ;');
|
||||||
|
})->then(function () use ($db) {
|
||||||
|
return $db->exec('CREATE TABLE IF NOT EXISTS load (load1 REAL NOT NULL) STRICT ;');
|
||||||
})->then(function () use ($db) {
|
})->then(function () use ($db) {
|
||||||
return $db->exec('PRAGMA journal_mode=WAL;');
|
return $db->exec('PRAGMA journal_mode=WAL;');
|
||||||
});
|
});
|
||||||
|
|
||||||
return $db->exec("CREATE TABLE IF NOT EXISTS ip (id_ip INTEGER PRIMARY KEY AUTOINCREMENT, ip TEXT UNIQUE NOT NULL CHECK (ip LIKE '%'), CONSTRAINT valid_ip CHECK (ip LIKE '%.%' OR ip LIKE '%:%')) STRICT ;")
|
|
||||||
->then(function () use ($db) {
|
|
||||||
return $db->exec('CREATE TABLE IF NOT EXISTS domain (id_domain INTEGER PRIMARY KEY AUTOINCREMENT, domain TEXT UNIQUE NOT NULL) STRICT ;');
|
|
||||||
})->then(function () use ($db) {
|
|
||||||
return $db->exec('CREATE TABLE IF NOT EXISTS path ( id_path INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT UNIQUE NOT NULL) STRICT ;');
|
|
||||||
})->then(function () use ($db) {
|
|
||||||
return $db->exec('CREATE TABLE IF NOT EXISTS useragent ( id_useragent INTEGER PRIMARY KEY AUTOINCREMENT, useragent TEXT UNIQUE NOT NULL) STRICT ;');
|
|
||||||
})->then(function () use ($db) {
|
|
||||||
return $db->exec('CREATE TABLE IF NOT EXISTS headers ( id_headers INTEGER PRIMARY KEY AUTOINCREMENT, headers TEXT UNIQUE NOT NULL) STRICT ;');
|
|
||||||
})->then(function () use ($db) {
|
|
||||||
return $db->exec("CREATE TABLE IF NOT EXISTS networkwhitelist ( id_networkwhitelist INTEGER PRIMARY KEY AUTOINCREMENT, network TEXT UNIQUE NOT NULL CHECK (network LIKE '%/%'), CONSTRAINT valid_network CHECK ( network LIKE '%.%/%' OR network LIKE '%:%/%' )) STRICT WITHOUT ROWID ;");
|
|
||||||
})->then(function () use ($db) {
|
|
||||||
return $db->exec('CREATE TABLE IF NOT EXISTS request ( id_request INTEGER PRIMARY KEY AUTOINCREMENT, id_ip INTEGER NOT NULL, id_method INTEGER NOT NULL, id_domain INTEGER NOT NULL, id_path INTEGER NOT NULL, id_useragent INTEGER NOT NULL, id_headers INTEGER NOT NULL, timestamp INTEGER NOT NULL, FOREIGN KEY (id_ip) REFERENCES ip(id_ip), FOREIGN KEY (id_domain) REFERENCES domain(id_domain), FOREIGN KEY (id_path) REFERENCES path(id_path), FOREIGN KEY (id_useragent) REFERENCES useragent(id_useragent), FOREIGN KEY (id_headers) REFERENCES headers(id_headers) ) STRICT WITHOUT ROWID ;');
|
|
||||||
})->then(function () use ($db) {
|
|
||||||
return $db->exec('CREATE TABLE IF NOT EXISTS bot ( id_bot INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, keyword TEXT NULL ) STRICT ;');
|
|
||||||
})->then(function () use ($db) {
|
|
||||||
return $db->exec('PRAGMA journal_mode=WAL;');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/Instances/Whitelist.php
Normal file
33
src/Instances/Whitelist.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace XBotControl;
|
||||||
|
|
||||||
|
use Clue\React\SQLite\DatabaseInterface;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
use Clue\React\SQLite\Result;
|
||||||
|
|
||||||
|
class Whitelist
|
||||||
|
{
|
||||||
|
private static ?Whitelist $instance = null;
|
||||||
|
|
||||||
|
public $googleUrls = [
|
||||||
|
'https://developers.google.com/static/search/apis/ipranges/googlebot.json',
|
||||||
|
'https://developers.google.com/static/search/apis/ipranges/special-crawlers.json',
|
||||||
|
'https://developers.google.com/static/search/apis/ipranges/user-triggered-fetchers.json',
|
||||||
|
'https://developers.google.com/static/search/apis/ipranges/user-triggered-fetchers-google.json'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function __construct() {}
|
||||||
|
|
||||||
|
public static function getInstance(): Whitelist
|
||||||
|
{
|
||||||
|
if (self::$instance === null) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ use Clue\React\SQLite\DatabaseInterface;
|
|||||||
use Clue\React\SQLite\Result;
|
use Clue\React\SQLite\Result;
|
||||||
use React\Promise\PromiseInterface;
|
use React\Promise\PromiseInterface;
|
||||||
use React\Promise\Promise;
|
use React\Promise\Promise;
|
||||||
|
use function React\Promise\Timer\sleep;
|
||||||
|
|
||||||
class Request
|
class Request
|
||||||
|
|
||||||
@@ -41,11 +42,17 @@ class Request
|
|||||||
$idPromises = [
|
$idPromises = [
|
||||||
'id_ip' => $storage::getId('ip', $realIp),
|
'id_ip' => $storage::getId('ip', $realIp),
|
||||||
'id_domain' => $storage::getId('domain', $uri->getHost()),
|
'id_domain' => $storage::getId('domain', $uri->getHost()),
|
||||||
'id_path' => $storage::getId('path', $uri->getPath()),
|
'id_path' => $storage::getId('path', '/' . $request->getAttribute('original_uri', '')),
|
||||||
'id_useragent' => $storage::getId('useragent', $userAgent),
|
'id_useragent' => $storage::getId('useragent', $userAgent),
|
||||||
'id_headers' => $storage::getId('headers', md5($headers)),
|
'id_headers' => 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($_ENV['SAVE_HEADERS'] === true) {
|
||||||
|
$idPromises['id_headers'] = $storage::getId('headers', $headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return \React\Promise\all($idPromises)
|
return \React\Promise\all($idPromises)
|
||||||
->then(function ($resolvedValues) use ($request, $storage) {
|
->then(function ($resolvedValues) use ($request, $storage) {
|
||||||
// Set resolved values efficiently
|
// Set resolved values efficiently
|
||||||
@@ -58,6 +65,7 @@ class Request
|
|||||||
->then(function () {
|
->then(function () {
|
||||||
return \React\Http\Message\Response::plaintext('');
|
return \React\Http\Message\Response::plaintext('');
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getRealIP(ServerRequestInterface $request): string
|
public static function getRealIP(ServerRequestInterface $request): string
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ use Clue\React\SQLite\Result;
|
|||||||
|
|
||||||
class Storage
|
class Storage
|
||||||
{
|
{
|
||||||
protected static $instance;
|
private static ?Storage $instance = null;
|
||||||
|
|
||||||
/** @var DatabaseInterface $db */
|
/** @var DatabaseInterface $db */
|
||||||
|
|
||||||
public $db;
|
public $db;
|
||||||
public $cache = [];
|
public $cache = [];
|
||||||
|
|
||||||
@@ -23,7 +25,7 @@ class Storage
|
|||||||
'path'
|
'path'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct()
|
private function __construct()
|
||||||
{
|
{
|
||||||
$this->db = (new \Clue\React\SQLite\Factory())->openLazy($_ENV['APP_DIR'] . '/requests.sqlite3');
|
$this->db = (new \Clue\React\SQLite\Factory())->openLazy($_ENV['APP_DIR'] . '/requests.sqlite3');
|
||||||
|
|
||||||
|
|||||||
@@ -7,35 +7,33 @@ require __DIR__ . '/vendor/autoload.php';
|
|||||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
$_ENV['APP_DIR'] = __DIR__;
|
$_ENV['APP_DIR'] = __DIR__;
|
||||||
$_ENV['X_LISTEN'] = '0.0.0.0:7500';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
XBotControl\InitTables::create();
|
XBotControl\InitTables::create();
|
||||||
$app = new FrameworkX\App();
|
$app = new FrameworkX\App();
|
||||||
|
|
||||||
$app->get(
|
$app->any(
|
||||||
'/mirror',
|
'/mirror[/{original_uri:.*}]',
|
||||||
function (Psr\Http\Message\ServerRequestInterface $request) {
|
function (Psr\Http\Message\ServerRequestInterface $request) {
|
||||||
return XBotControl\Request::save($request);
|
return XBotControl\Request::save($request);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
$app->get('/mirror1', function () {
|
$app->any('/', XBotControl\Controllers\IndexController::class);
|
||||||
return React\Http\Message\Response::plaintext(
|
|
||||||
"Hello wörld!\n"
|
$app->any('/api/{action}/{resource}', XBotControl\Controllers\APIController::class);
|
||||||
);
|
|
||||||
});
|
|
||||||
|
XBotControl\Classes\Schedule::run();
|
||||||
|
|
||||||
$app->get('/cp', function (Psr\Http\Message\ServerRequestInterface $request) {
|
|
||||||
return React\Http\Message\Response::plaintext(
|
|
||||||
"Hello " . $request->getAttribute('name') . "!\n"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->run();
|
$app->run();
|
||||||
|
|
||||||
XBotControl\Storage::getInstance()->db->query('PRAGMA main.wal_checkpoint;')
|
XBotControl\Storage::getInstance()->db->query('VACUUM;')
|
||||||
->then(function () {
|
->then(function () {
|
||||||
|
XBotControl\Storage::getInstance()->db->query('PRAGMA main.wal_checkpoint;');
|
||||||
|
})->then(function () {
|
||||||
XBotControl\Storage::getInstance()->db->quit();
|
XBotControl\Storage::getInstance()->db->quit();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user