update 2025-03-19 20:41:12
This commit is contained in:
parent
9c4bf66527
commit
8ad46f0bdf
|
@ -607,6 +607,48 @@ $neko_log_content = readLogFile("$neko_dir/tmp/neko_log.txt");
|
|||
$singbox_log_content = readLogFile($singbox_log);
|
||||
?>
|
||||
|
||||
<?php
|
||||
$confDirectory = '/etc/neko/config';
|
||||
$storageFile = '/www/nekobox/lib/singbox.txt';
|
||||
|
||||
$storageDir = dirname($storageFile);
|
||||
if (!is_dir($storageDir)) {
|
||||
mkdir($storageDir, 0755, true);
|
||||
}
|
||||
|
||||
$currentConfigPath = '';
|
||||
if (file_exists($storageFile)) {
|
||||
$rawPath = trim(file_get_contents($storageFile));
|
||||
$currentConfigPath = realpath($rawPath) ?: $rawPath;
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['config_file'])) {
|
||||
$submittedPath = trim($_POST['config_file']);
|
||||
$normalizedPath = realpath($submittedPath);
|
||||
|
||||
if ($normalizedPath &&
|
||||
strpos($normalizedPath, realpath($confDirectory)) === 0 &&
|
||||
file_exists($normalizedPath)
|
||||
) {
|
||||
if (file_put_contents($storageFile, $normalizedPath) !== false) {
|
||||
$currentConfigPath = $normalizedPath;
|
||||
} else {
|
||||
error_log("Write failed: $storageFile");
|
||||
}
|
||||
} else {
|
||||
error_log("Invalid path: $submittedPath");
|
||||
}
|
||||
}
|
||||
|
||||
function fetchConfigFiles() {
|
||||
global $confDirectory;
|
||||
$baseDir = rtrim($confDirectory, '/') . '/';
|
||||
return glob($baseDir . '*.json') ?: [];
|
||||
}
|
||||
|
||||
$foundConfigs = fetchConfigFiles();
|
||||
?>
|
||||
|
||||
<?php
|
||||
$isNginx = false;
|
||||
if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
|
||||
|
@ -980,7 +1022,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="mb-4" id="mihomoControl" class="control-box">
|
||||
<h6 class="mb-2"><i class="fas fa-box custom-icon"></i> <span data-translate="mihomoControl">Mihomo Control</span></h6>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<form action="index.php" method="post">
|
||||
|
@ -1026,19 +1068,22 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="mb-4" id="singboxControl" class="control-box">
|
||||
<h6 class="mb-2"><i data-feather="codesandbox"></i> <span data-translate="singboxControl">Singbox Control</span></h6>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<form action="index.php" method="post">
|
||||
<select name="config_file" id="config_file" class="form-select mb-2"
|
||||
onchange="saveConfigSelection()">
|
||||
<select name="config_file" class="form-select mb-2" onchange="this.form.submit()">
|
||||
<option value="">
|
||||
<span data-translate="selectConfig">Please select a configuration file</span>
|
||||
</option>
|
||||
<?php foreach ($availableConfigs as $config): ?>
|
||||
<option value="<?= htmlspecialchars($config) ?>"
|
||||
<?= (isset($_POST['config_file']) && $_POST['config_file'] === $config) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars(basename($config)) ?>
|
||||
<?php foreach ($foundConfigs as $configPath): ?>
|
||||
<?php
|
||||
$cleanPath = str_replace('//', '/', $configPath);
|
||||
$displayName = basename($cleanPath);
|
||||
?>
|
||||
<option value="<?= htmlspecialchars($cleanPath) ?>"
|
||||
<?= ($currentConfigPath === realpath($cleanPath)) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($displayName) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
@ -1084,18 +1129,28 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const savedConfig = localStorage.getItem("configSelection");
|
||||
if (savedConfig) {
|
||||
document.getElementById("config_file").value = savedConfig;
|
||||
}
|
||||
});
|
||||
function saveConfigSelection() {
|
||||
const selectedConfig = document.getElementById("config_file").value;
|
||||
localStorage.setItem("configSelection", selectedConfig);
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const mihomoControl = document.getElementById('mihomoControl');
|
||||
const singboxControl = document.getElementById('singboxControl');
|
||||
|
||||
const mihomoStatus = <?php echo $neko_status; ?>;
|
||||
const singboxStatus = <?php echo $singbox_status; ?>;
|
||||
|
||||
if (mihomoStatus === 1 && singboxStatus === 1) {
|
||||
mihomoControl.style.display = 'block';
|
||||
singboxControl.style.display = 'block';
|
||||
} else if (mihomoStatus === 1) {
|
||||
mihomoControl.style.display = 'block';
|
||||
singboxControl.style.display = 'none';
|
||||
} else if (singboxStatus === 1) {
|
||||
mihomoControl.style.display = 'none';
|
||||
singboxControl.style.display = 'block';
|
||||
} else {
|
||||
mihomoControl.style.display = 'block';
|
||||
singboxControl.style.display = 'block';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
@ -1462,3 +1517,4 @@ window.onload = function() {
|
|||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
/etc/neko/config/Puernya.json
|
|
@ -1,6 +1,6 @@
|
|||
<?php include './language.php'; ?>
|
||||
<html lang="<?php echo $currentLang; ?>">
|
||||
<div class="modal fade" id="langModal" tabindex="-1" aria-labelledby="langModalLabel" aria-hidden="true">
|
||||
<div class="modal fade" id="langModal" tabindex="-1" aria-labelledby="langModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
|
@ -30,7 +30,7 @@
|
|||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
interact('.modal-dialog.draggable').draggable({
|
||||
allowFrom: '.modal-header',
|
||||
modifiers: [
|
||||
|
@ -40,35 +40,53 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
})
|
||||
],
|
||||
listeners: {
|
||||
start: function(event) {
|
||||
start(event) {
|
||||
event.target.style.transition = 'none';
|
||||
event.target.classList.add('dragging');
|
||||
},
|
||||
move: function(event) {
|
||||
move(event) {
|
||||
const target = event.target;
|
||||
const x = (parseFloat(target.style.left) || 0) + event.dx;
|
||||
const y = (parseFloat(target.style.top) || 0) + event.dy;
|
||||
|
||||
target.style.position = 'absolute';
|
||||
target.style.left = `${x}px`;
|
||||
target.style.top = `${y}px`;
|
||||
},
|
||||
end: function(event) {
|
||||
end(event) {
|
||||
event.target.style.transition = '';
|
||||
event.target.classList.remove('dragging');
|
||||
}
|
||||
}
|
||||
})
|
||||
.resizable({
|
||||
edges: { right: true, bottom: true, left: true },
|
||||
listeners: {
|
||||
move(event) {
|
||||
let { x, y } = event.target.dataset;
|
||||
x = (parseFloat(x) || 0) + event.deltaRect.left;
|
||||
y = (parseFloat(y) || 0) + event.deltaRect.top;
|
||||
|
||||
Object.assign(event.target.style, {
|
||||
width: `${event.rect.width}px`,
|
||||
height: `${event.rect.height}px`,
|
||||
transform: `translate(${x}px, ${y}px)`
|
||||
});
|
||||
|
||||
Object.assign(event.target.dataset, { x, y });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.modal').forEach(modal => {
|
||||
const dialog = modal.querySelector('.modal-dialog');
|
||||
dialog.classList.add('draggable');
|
||||
|
||||
const originalWidth = dialog.style.width;
|
||||
const originalMaxWidth = dialog.style.maxWidth;
|
||||
|
||||
modal.addEventListener('show.bs.modal', () => {
|
||||
dialog.style.width = originalWidth;
|
||||
dialog.style.maxWidth = originalMaxWidth;
|
||||
dialog.style.width = '';
|
||||
dialog.style.maxWidth = '';
|
||||
dialog.style.left = '';
|
||||
dialog.style.top = '';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -633,6 +651,16 @@ $lang = $_GET['lang'] ?? 'en';
|
|||
</div>
|
||||
<?php endif; ?>
|
||||
<style>
|
||||
.modal-dialog.draggable {
|
||||
resize: both;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
min-height: 0 !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#leafletMap {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
|
@ -1469,7 +1497,7 @@ let IP = {
|
|||
|
||||
const modalHTML = `
|
||||
<div class="modal fade custom-modal" id="ipDetailModal" tabindex="-1" role="dialog" aria-labelledby="ipDetailModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
|
||||
<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
|
||||
<div class="modal-dialog modal-xl draggable" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="ipDetailModalLabel">${translations['ip_info']}</h5>
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -33,8 +33,8 @@
|
|||
name="theme-color"
|
||||
content="#FFFFFF"
|
||||
/>
|
||||
<script type="module" crossorigin src="./assets/index-CmdZCl_h.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-BmPjzLVa.css">
|
||||
<script type="module" crossorigin src="./assets/index-CkU3ZoGL.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-BOZ17cMU.css">
|
||||
<link rel="manifest" href="./manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="./registerSW.js"></script></head>
|
||||
<body class="overflow-hidden overscroll-none">
|
||||
<div id="app"></div>
|
||||
|
|
|
@ -1 +1 @@
|
|||
if(!self.define){let e,i={};const s=(s,n)=>(s=new URL(s+".js",n).href,i[s]||new Promise((i=>{if("document"in self){const e=document.createElement("script");e.src=s,e.onload=i,document.head.appendChild(e)}else e=s,importScripts(s),i()})).then((()=>{let e=i[s];if(!e)throw new Error(`Module ${s} didn’t register its module`);return e})));self.define=(n,r)=>{const f=e||("document"in self?document.currentScript.src:"")||location.href;if(i[f])return;let d={};const o=e=>s(e,f),t={module:{uri:f},exports:d,require:o};i[f]=Promise.all(n.map((e=>t[e]||o(e)))).then((e=>(r(...e),d)))}}define(["./workbox-3e8df8c8"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"assets/index-BmPjzLVa.css",revision:null},{url:"assets/index-CmdZCl_h.js",revision:null},{url:"index.html",revision:"b2f366aa40466785f96d382868e6c188"},{url:"registerSW.js",revision:"402b66900e731ca748771b6fc5e7a068"},{url:"favicon.svg",revision:"7f1c4521acc10694fefef8f72dd2ea5f"},{url:"pwa-192x192.png",revision:"021df52501f4357c03eebd808f40dc6a"},{url:"pwa-512x512.png",revision:"d2f759aaabcb2c44ff52b27fde3de6e0"},{url:"pwa-maskable-192x192.png",revision:"7cd11dc5f0490b349d23eef5591d10e5"},{url:"pwa-maskable-512x512.png",revision:"8c97dc367a85a5a1eba523b24f79d03b"},{url:"manifest.webmanifest",revision:"c452912633990899ffe790f985ad0db9"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))}));
|
||||
if(!self.define){let e,i={};const s=(s,n)=>(s=new URL(s+".js",n).href,i[s]||new Promise((i=>{if("document"in self){const e=document.createElement("script");e.src=s,e.onload=i,document.head.appendChild(e)}else e=s,importScripts(s),i()})).then((()=>{let e=i[s];if(!e)throw new Error(`Module ${s} didn’t register its module`);return e})));self.define=(n,r)=>{const f=e||("document"in self?document.currentScript.src:"")||location.href;if(i[f])return;let o={};const c=e=>s(e,f),d={module:{uri:f},exports:o,require:c};i[f]=Promise.all(n.map((e=>d[e]||c(e)))).then((e=>(r(...e),o)))}}define(["./workbox-3e8df8c8"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"assets/index-BOZ17cMU.css",revision:null},{url:"assets/index-CkU3ZoGL.js",revision:null},{url:"index.html",revision:"3d28b0e9037259e2fc1ef67e566f0bee"},{url:"registerSW.js",revision:"402b66900e731ca748771b6fc5e7a068"},{url:"favicon.svg",revision:"7f1c4521acc10694fefef8f72dd2ea5f"},{url:"pwa-192x192.png",revision:"021df52501f4357c03eebd808f40dc6a"},{url:"pwa-512x512.png",revision:"d2f759aaabcb2c44ff52b27fde3de6e0"},{url:"pwa-maskable-192x192.png",revision:"7cd11dc5f0490b349d23eef5591d10e5"},{url:"pwa-maskable-512x512.png",revision:"8c97dc367a85a5a1eba523b24f79d03b"},{url:"manifest.webmanifest",revision:"c452912633990899ffe790f985ad0db9"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))}));
|
||||
|
|
|
@ -1 +1 @@
|
|||
v1.74.0
|
||||
v1.75.0
|
Loading…
Reference in New Issue