total refactor WIP
This commit is contained in:
parent
3b10da1adb
commit
7505b4d9e4
|
|
@ -6,3 +6,12 @@
|
|||
|
||||
## utils
|
||||
Различные скрипты и утилиты
|
||||
|
||||
### Установка
|
||||
```shell
|
||||
apk add php php-session jq
|
||||
```
|
||||
|
||||
```shell
|
||||
chmod -R +x ./bin
|
||||
```
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import {JSONRPC} from "./jrpc.js";
|
||||
|
||||
const App = {
|
||||
config: {},
|
||||
networks: {},
|
||||
RPC: JSONRPC
|
||||
};
|
||||
|
||||
|
||||
App.auth = async function () {
|
||||
let authorized = await JSONRPC.__invoke("auth");
|
||||
if (!authorized) {
|
||||
do {
|
||||
let pass = prompt("Password");
|
||||
authorized = await JSONRPC.__invoke("auth", {
|
||||
"password": pass
|
||||
});
|
||||
|
||||
if (!authorized) {
|
||||
alert("Wrong password");
|
||||
}
|
||||
} while (!authorized);
|
||||
}
|
||||
|
||||
this.config = await JSONRPC.__invoke("getConfig");
|
||||
this.networks = await JSONRPC.__invoke("getNetworks");
|
||||
|
||||
|
||||
for (const key of this.config.plugins) {
|
||||
$("body").append("<" + "script type='module' src='/plugins/" + key + "/plugin.js'><" + "/script>");
|
||||
}
|
||||
|
||||
$("#loading").hide();
|
||||
|
||||
this.fillNetworks();
|
||||
|
||||
$("#panel").show();
|
||||
}
|
||||
|
||||
App.fillNetworks = function () {
|
||||
const that = this;
|
||||
let proto = $("#net-table tr");
|
||||
proto.detach();
|
||||
|
||||
for (const net in this.networks) {
|
||||
let item = proto.clone();
|
||||
item.find("input").prop('checked', this.config.networks.indexOf(net) !== -1).change(function () {
|
||||
if ($(this).prop('checked')) {
|
||||
that.config.networks.push(net);
|
||||
} else {
|
||||
that.config.networks = that.config.networks.filter(e => e !== net);
|
||||
}
|
||||
});
|
||||
item.find("span").text(net);
|
||||
$("#net-table").append(item);
|
||||
}
|
||||
}
|
||||
|
||||
App.render = async function () {
|
||||
await this.auth();
|
||||
$("#save").click(function () {
|
||||
const self = $(this);
|
||||
self.prop("disabled", true);
|
||||
(async function () {
|
||||
await JSONRPC.__invoke("setConfig", App.config);
|
||||
alert("Config saved!");
|
||||
self.prop("disabled", false);
|
||||
})();
|
||||
});
|
||||
$("#restart-quagga").click(function () {
|
||||
if (confirm("Are you sure?")) {
|
||||
const self = $(this);
|
||||
self.prop("disabled", true);
|
||||
(async function () {
|
||||
try {
|
||||
alert(await JSONRPC.__invoke("restart_quagga"));
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
self.prop("disabled", false);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
})();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export {App};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Routing config</title>
|
||||
<script src="jquery-3.7.1.min.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<span id="loading">Loading...</span>
|
||||
|
||||
<div style="display: none" id="panel">
|
||||
Selected networks:
|
||||
<table id="net-table">
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td><span>Network name</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div id="buttons">
|
||||
<button id="save">Save</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import {App} from "./App.js";
|
||||
|
||||
$(document).ready(function () {
|
||||
App.render();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
SELFDIR=`dirname $0`
|
||||
ROOT=`realpath $SELFDIR/..`
|
||||
CFGFILE=$ROOT/config.json
|
||||
|
||||
if ! test -f "$CFGFILE"; then
|
||||
echo Config file $CFGFILE not found
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HOST=`jq -r .web.host $CFGFILE`
|
||||
PORT=`jq -r .web.port $CFGFILE`
|
||||
php -S $HOST:$PORT $ROOT/server.php
|
||||
|
|
@ -16,7 +16,7 @@ class Config implements ArrayAccess
|
|||
|
||||
public function __construct()
|
||||
{
|
||||
$this->path = dirname(__DIR__, 2) . "/config.json";
|
||||
$this->path = dirname(__DIR__) . "/config.json";
|
||||
}
|
||||
|
||||
public function read(): void
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
interface IPluggable
|
||||
{
|
||||
const PROTECTED_NAMES = ["enable", "disable", "render", "init"];
|
||||
|
||||
public function enable();
|
||||
|
||||
public function disable();
|
||||
|
||||
|
||||
public function init();
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
<?php
|
||||
|
||||
|
||||
class NetworkConfigReader
|
||||
{
|
||||
private array $configs = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$path = dirname(__DIR__, 2) . "/networks";
|
||||
$path = dirname(__DIR__) . "/networks";
|
||||
foreach (new IteratorIterator(new DirectoryIterator($path)) as $file) {
|
||||
/**
|
||||
* @var SplFileInfo $file
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
class RPC
|
||||
{
|
||||
private Config $config;
|
||||
private array $plugins = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = new Config();
|
||||
$this->config->read();
|
||||
|
||||
foreach ($this->config["plugins"] as $plugin) {
|
||||
try {
|
||||
$meta = $this->getPluginMetadata($plugin);
|
||||
$this->loadPlugin($plugin, $meta["class"]);
|
||||
} catch (Error $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getPluginMetadata($name): array
|
||||
{
|
||||
$root = dirname(__DIR__) . "/plugins/" . $name;
|
||||
if (!file_exists($root)) {
|
||||
throw new RuntimeException("Plugin $name dir not found");
|
||||
}
|
||||
$metafile = $root . "/metadata.json";
|
||||
if (!file_exists($metafile)) {
|
||||
throw new RuntimeException("Plugin $name metadata not found");
|
||||
}
|
||||
|
||||
$meta = @json_decode(@file_get_contents($metafile), true);
|
||||
|
||||
if ($meta === null) {
|
||||
throw new RuntimeException("Unable to parse $name plugin metadata");
|
||||
}
|
||||
return $meta;
|
||||
}
|
||||
|
||||
private function loadPlugin($name, $classname): void
|
||||
{
|
||||
$file = dirname(__DIR__) . "/plugins/" . $name . "/" . $classname . ".php";
|
||||
if (!file_exists($file)) {
|
||||
throw new RuntimeException("Plugin $name class $classname not found");
|
||||
}
|
||||
require_once $file;
|
||||
|
||||
$instance = new $classname();
|
||||
if (!($instance instanceof IPluggable)) {
|
||||
throw new RuntimeException("Class $classname have to implement IPluggable");
|
||||
}
|
||||
|
||||
$this->plugins[$name] = $instance;
|
||||
}
|
||||
|
||||
public function getConfig(): array
|
||||
{
|
||||
$this->checkAuth();
|
||||
return $this->config->asArray();
|
||||
}
|
||||
|
||||
public function setConfig($config): bool
|
||||
{
|
||||
$this->config->fromArray($config);
|
||||
$this->config->save();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkAuth(): void
|
||||
{
|
||||
$auth = $_SESSION["auth"] ?? false;
|
||||
if (!$auth) {
|
||||
throw new RuntimeException("Unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
public function getNetworks(): array
|
||||
{
|
||||
$this->checkAuth();
|
||||
return (new NetworkConfigReader())->getConfigs();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function logout(): void
|
||||
{
|
||||
$_SESSION["auth"] = false;
|
||||
}
|
||||
|
||||
public function auth($params): bool
|
||||
{
|
||||
if (isset($params["password"])) {
|
||||
if ($this->comparePassword($params["password"])) {
|
||||
return $_SESSION["auth"] = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $_SESSION["auth"] ?? false;
|
||||
}
|
||||
|
||||
private function comparePassword($passwd): bool
|
||||
{
|
||||
$pass = $this->config["password"];
|
||||
if ($pass["type"] == "plaintext") {
|
||||
return $pass["data"] == $passwd;
|
||||
} else if ($pass["type"] == "hash") {
|
||||
return $this->hash($passwd) == $pass["data"];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hash($what): string
|
||||
{
|
||||
return md5(sha1($what) . md5($what));
|
||||
}
|
||||
|
||||
public function __invoke($method, $args)
|
||||
{
|
||||
$parts = explode("::", $method);
|
||||
$isPlugin = count($parts) > 1;
|
||||
|
||||
if ($isPlugin) {
|
||||
$this->checkAuth();
|
||||
$plugin = $this->plugins[$parts[0]];
|
||||
$methodname = $parts[1];
|
||||
if (in_array($methodname, IPluggable::PROTECTED_NAMES)) {
|
||||
throw new RuntimeException("Unable to invoke internal methods");
|
||||
}
|
||||
return call_user_func([$plugin, $methodname], $args);
|
||||
} else {
|
||||
$cls = new ReflectionClass(__CLASS__);
|
||||
$method = $cls->getMethod($method);
|
||||
if (!$method or !$method->isPublic()) {
|
||||
throw new RuntimeException("Unable to find method");
|
||||
}
|
||||
return $method->invoke($this, $args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
|
||||
class RoutingTableReader
|
||||
{
|
||||
private $routes = [];
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
class WebRouter
|
||||
{
|
||||
private string $requestBody;
|
||||
private string $requestedFile;
|
||||
private string $URI;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->requestBody = @file_get_contents('php://input');
|
||||
$this->requestedFile = dirname(__DIR__) . "/" . $_SERVER["REQUEST_URI"];
|
||||
$this->URI = $_SERVER["REQUEST_URI"];
|
||||
}
|
||||
|
||||
public function handleRequest(): void
|
||||
{
|
||||
@session_start();
|
||||
try {
|
||||
if (str_starts_with($this->URI, "/assets") or str_starts_with($this->URI, "/plugins") and str_ends_with($this->URI, ".js")) {
|
||||
$this->handleAsset();
|
||||
} elseif (!str_starts_with($this->URI, "/rpc")) {
|
||||
$this->redirect("/assets/index.html");
|
||||
} else {
|
||||
$this->handleJRPC();
|
||||
}
|
||||
} finally {
|
||||
session_write_close();
|
||||
}
|
||||
}
|
||||
|
||||
private function handleJRPC(): void
|
||||
{
|
||||
try {
|
||||
$request = @json_decode($this->requestBody, true);
|
||||
if ($request === null) {
|
||||
throw new RuntimeException("Failed to parse JRPC");
|
||||
}
|
||||
|
||||
foreach (["id", "jsonrpc", "method", "params"] as $param) {
|
||||
if (!isset($request[$param])) {
|
||||
throw new RuntimeException("Bad JRPC structure");
|
||||
}
|
||||
}
|
||||
|
||||
$rpc = new RPC();
|
||||
$response = $rpc($request["method"], $request["params"]);
|
||||
|
||||
header("content-type: application/json");
|
||||
|
||||
echo json_encode([
|
||||
"jsonrpc" => "2.0",
|
||||
"id" => $request["id"] ?? 0,
|
||||
"result" => $response
|
||||
]);
|
||||
|
||||
} catch (Error $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
"jsonrpc" => "2.0",
|
||||
"id" => $request["id"],
|
||||
"error" => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function handleAsset(): void
|
||||
{
|
||||
if (!file_exists($this->requestedFile)) {
|
||||
http_response_code(404);
|
||||
echo "File not found: " . $this->URI;
|
||||
} else {
|
||||
header("content-type: " . mime_content_type($this->requestedFile));
|
||||
echo file_get_contents($this->requestedFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function redirect($where): void
|
||||
{
|
||||
http_response_code(302);
|
||||
header("location: " . $where);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,5 +6,9 @@
|
|||
"networks": [
|
||||
"google"
|
||||
],
|
||||
"zebra_restart_cmd": "/etc/init.d/zebra restart"
|
||||
"web": {
|
||||
"port":8000,
|
||||
"host":"0.0.0.0"
|
||||
},
|
||||
"plugins":[]
|
||||
}
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
use classes\Config;
|
||||
use classes\NetworkConfigReader;
|
||||
|
||||
if (!isset($argv[1])) {
|
||||
exit(1);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/*
|
||||
*
|
||||
* public function restart_quagga()
|
||||
{
|
||||
return shell_exec("./zebracfg.php");
|
||||
}
|
||||
*/
|
||||
|
||||
class QuaggaPlugin implements IPluggable
|
||||
{
|
||||
public function restart()
|
||||
{
|
||||
return shell_exec("./zebracfg.php");
|
||||
}
|
||||
public function enable()
|
||||
{
|
||||
// TODO: Implement enable() method.
|
||||
}
|
||||
|
||||
public function disable()
|
||||
{
|
||||
// TODO: Implement disable() method.
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
// TODO: Implement init() method.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"class": "QuaggaPlugin",
|
||||
"config": {
|
||||
"quagga": {
|
||||
"restart_cmd": "/etc/init.d/zebra restart",
|
||||
"file": "/etc/quagga/zebra.conf"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
//<button id="restart-quagga">Restart quagga</button>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
class Updates implements IPluggable
|
||||
{
|
||||
|
||||
public function enable()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function disable()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return file_get_contents(__DIR__ . "/plugin.js");
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function check(): ?bool
|
||||
{
|
||||
$data = str_replace("=", "", @shell_exec("git --no-pager fetch --dry-run --porcelain --verbose 2>&1 | grep refs"));
|
||||
$parts = explode(" ", trim($data));
|
||||
if (count($parts) < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $parts[0] != $parts[1];
|
||||
}
|
||||
|
||||
public function install(): string|bool|null
|
||||
{
|
||||
return @shell_exec("git --no-pager pull --verbose 2>&1");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"class": "Updates",
|
||||
"config": {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import {JSONRPC} from "/assets/jrpc.js";
|
||||
|
||||
(async function () {
|
||||
$("#panel").prepend(`<div id="update-panel">Checking for updates...</div><hr>`);
|
||||
let state = (await JSONRPC.__invoke("updates::check"));
|
||||
if (state === null) {
|
||||
$("#update-panel").html(`<span style="color:red;">Error checking updates</span>`);
|
||||
} else if (state === false) {
|
||||
$("#update-panel").html(`<span style="color:black;">There is no updates</span>`);
|
||||
} else if (state === true) {
|
||||
$("#update-panel").html(`<span style="color:green;">Some updates are available</span> <button>Update</button>`);
|
||||
$("#update-panel button").click(async function () {
|
||||
$("#panel").hide();
|
||||
$("#loading").show().text("Installing updates...");
|
||||
try {
|
||||
console.log(await JSONRPC.__invoke("updates::install"));
|
||||
} finally {
|
||||
setTimeout(() => location.reload(), 10000);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
require_once __DIR__ . "/loader.php";
|
||||
$router = new WebRouter();
|
||||
$router->handleRequest();
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Routing config</title>
|
||||
<script src="jquery-3.7.1.min.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<span id="loading">Loading...</span>
|
||||
|
||||
<div style="display: none" id="panel">
|
||||
|
||||
<div id="update-panel">Checking for updates...</div>
|
||||
<hr>
|
||||
Selected networks:
|
||||
<table id="net-table">
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td><span>Network name</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<button id="save">Save</button>
|
||||
<button id="restart-quagga">Restart quagga</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import {JSONRPC} from "./jrpc.js";
|
||||
|
||||
let config = {};
|
||||
let networks = {};
|
||||
|
||||
async function auth() {
|
||||
let authorized = await JSONRPC.__invoke("auth");
|
||||
if (!authorized) {
|
||||
do {
|
||||
let pass = prompt("Password");
|
||||
authorized = await JSONRPC.__invoke("auth", {
|
||||
"password": pass
|
||||
});
|
||||
|
||||
if (!authorized) {
|
||||
alert("Wrong password");
|
||||
}
|
||||
} while (!authorized);
|
||||
}
|
||||
|
||||
config = await JSONRPC.__invoke("getConfig");
|
||||
networks = await JSONRPC.__invoke("getNetworks");
|
||||
|
||||
$("#loading").hide();
|
||||
|
||||
fillNetworks();
|
||||
|
||||
$("#panel").show();
|
||||
}
|
||||
|
||||
|
||||
function fillNetworks() {
|
||||
let proto = $("#net-table tr");
|
||||
proto.detach();
|
||||
|
||||
for (const net in networks) {
|
||||
let item = proto.clone();
|
||||
item.find("input").prop('checked', config.networks.indexOf(net) !== -1).change(function () {
|
||||
if ($(this).prop('checked')) {
|
||||
config.networks.push(net);
|
||||
} else {
|
||||
config.networks = config.networks.filter(e => e !== net);
|
||||
}
|
||||
});
|
||||
item.find("span").text(net);
|
||||
$("#net-table").append(item);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
auth();
|
||||
$("#save").click(function () {
|
||||
const self = $(this);
|
||||
self.prop("disabled", true);
|
||||
(async function () {
|
||||
await JSONRPC.__invoke("setConfig", config);
|
||||
alert("Config saved!");
|
||||
self.prop("disabled", false);
|
||||
})();
|
||||
});
|
||||
$("#restart-quagga").click(function () {
|
||||
if (confirm("Are you sure?")) {
|
||||
const self = $(this);
|
||||
self.prop("disabled", true);
|
||||
(async function () {
|
||||
try {
|
||||
alert(await JSONRPC.__invoke("restart_quagga"));
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
self.prop("disabled", false);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
})();
|
||||
}
|
||||
});
|
||||
(async function () {
|
||||
let state = (await JSONRPC.__invoke("checkUpdates"));
|
||||
if (state === null) {
|
||||
$("#update-panel").html(`<span style="color:red;">Error checking updates</span>`);
|
||||
} else if (state === false) {
|
||||
$("#update-panel").html(`<span style="color:black;">There is no updates</span>`);
|
||||
} else if (state === true) {
|
||||
$("#update-panel").html(`<span style="color:green;">Some updates are available</span> <button>Update</button>`);
|
||||
$("#update-panel button").click(async function () {
|
||||
$("#panel").hide();
|
||||
$("#loading").show().text("Installing updates...");
|
||||
try {
|
||||
console.log(await JSONRPC.__invoke("installUpdates"));
|
||||
} finally {
|
||||
setTimeout(() => location.reload(), 10000);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
165
utils/server.php
165
utils/server.php
|
|
@ -1,165 +0,0 @@
|
|||
<?php
|
||||
require_once __DIR__ . "/loader.php";
|
||||
|
||||
if (str_starts_with($_SERVER["REQUEST_URI"], "/assets")) {
|
||||
$file = __DIR__ . $_SERVER["REQUEST_URI"];
|
||||
if (!file_exists($file)) {
|
||||
http_response_code(404);
|
||||
echo "File not found: " . $_SERVER["REQUEST_URI"];
|
||||
exit();
|
||||
}
|
||||
header("content-type: " . mime_content_type($file));
|
||||
echo file_get_contents($file);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!str_starts_with($_SERVER["REQUEST_URI"], "/rpc")) {
|
||||
http_response_code(302);
|
||||
header("location: /assets/index.html");
|
||||
exit();
|
||||
}
|
||||
|
||||
$request = json_decode(@file_get_contents('php://input'), true);
|
||||
|
||||
try {
|
||||
if ($request === null) {
|
||||
throw new RuntimeException("Failed to parse JRPC");
|
||||
}
|
||||
|
||||
foreach (["id", "jsonrpc", "method", "params"] as $param) {
|
||||
if (!isset($request[$param])) {
|
||||
throw new RuntimeException("Bad JRPC structure");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* {"jsonrpc":"2.0","method":"auth","params":{},"id":1}
|
||||
*/
|
||||
|
||||
@session_start();
|
||||
|
||||
class RPC
|
||||
{
|
||||
private Config $config;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = new Config();
|
||||
$this->config->read();
|
||||
}
|
||||
|
||||
private function hash($what): string
|
||||
{
|
||||
return md5(sha1($what) . md5($what));
|
||||
}
|
||||
|
||||
public function getConfig(): array
|
||||
{
|
||||
$this->checkAuth();
|
||||
return $this->config->asArray();
|
||||
}
|
||||
|
||||
|
||||
public function restart_quagga()
|
||||
{
|
||||
return shell_exec("./zebracfg.php");
|
||||
}
|
||||
|
||||
public function checkUpdates()
|
||||
{
|
||||
$this->checkAuth();
|
||||
$data = str_replace("=","",@shell_exec("git --no-pager fetch --dry-run --porcelain --verbose 2>&1 | grep refs"));
|
||||
$parts = explode(" ", trim($data));
|
||||
if(count($parts) < 3){
|
||||
return null;
|
||||
}
|
||||
|
||||
return $parts[0] != $parts[1];
|
||||
}
|
||||
|
||||
public function installUpdates()
|
||||
{
|
||||
return @shell_exec("git --no-pager pull --verbose 2>&1");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getNetworks(): array
|
||||
{
|
||||
$this->checkAuth();
|
||||
return (new NetworkConfigReader())->getConfigs();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function setConfig($config): bool
|
||||
{
|
||||
$this->config->fromArray($config);
|
||||
$this->config->save();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkAuth(): void
|
||||
{
|
||||
$auth = $_SESSION["auth"] ?? false;
|
||||
if (!$auth) {
|
||||
throw new RuntimeException("Unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
private function comparePassword($passwd): bool
|
||||
{
|
||||
$pass = $this->config["password"];
|
||||
if ($pass["type"] == "plaintext") {
|
||||
return $pass["data"] == $passwd;
|
||||
} else if ($pass["type"] == "hash") {
|
||||
return $this->hash($passwd) == $pass["data"];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function logout(): void
|
||||
{
|
||||
$_SESSION["auth"] = false;
|
||||
}
|
||||
|
||||
public function auth($params): bool
|
||||
{
|
||||
if (isset($params["password"])) {
|
||||
if ($this->comparePassword($params["password"])) {
|
||||
return $_SESSION["auth"] = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $_SESSION["auth"] ?? false;
|
||||
}
|
||||
|
||||
public function __invoke($method, $args)
|
||||
{
|
||||
$cls = new ReflectionClass(__CLASS__);
|
||||
$method = $cls->getMethod($method);
|
||||
if (!$method or !$method->isPublic()) {
|
||||
throw new RuntimeException("Unable to find method");
|
||||
}
|
||||
return $method->invoke($this, $args);
|
||||
}
|
||||
}
|
||||
|
||||
$rpc = new RPC();
|
||||
$response = $rpc($request["method"], $request["params"]);
|
||||
|
||||
header("content-type: application/json");
|
||||
@session_write_close();
|
||||
|
||||
echo json_encode([
|
||||
"jsonrpc" => "2.0",
|
||||
"id" => $request["id"],
|
||||
"result" => $response
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
SELF=`dirname $0`
|
||||
DIR=`realpath $SELF`
|
||||
cd $DIR
|
||||
killall php
|
||||
php -S 0.0.0.0:8000 server.php
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
use classes\Config;
|
||||
use classes\NetworkConfigReader;
|
||||
use classes\RoutingTableReader;
|
||||
|
||||
require_once __DIR__ . "/loader.php";
|
||||
const CFGFILE = "/etc/quagga/zebra.conf";
|
||||
const REM_PREFIX = "! routes from file ";
|
||||
Loading…
Reference in New Issue