update 2025-03-19 20:41:12

This commit is contained in:
kenzok8 2025-03-19 20:41:12 +08:00
parent 9c4bf66527
commit 8ad46f0bdf
9 changed files with 205 additions and 120 deletions

View File

@ -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>

View File

@ -0,0 +1 @@
/etc/neko/config/Puernya.json

View File

@ -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

View File

@ -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>

View File

@ -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} didnt 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} didnt 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")))}));

View File

@ -1 +1 @@
v1.74.0
v1.75.0