1
0
mirror of https://github.com/nitezs/sub2sing-box.git synced 2024-12-24 12:24:41 -05:00

add: web ui

This commit is contained in:
Nite07 2024-03-19 13:29:40 +08:00
parent 7a5325187a
commit 8b3c590364
2 changed files with 227 additions and 1 deletions

View File

@ -1,21 +1,47 @@
package api package api
import ( import (
"embed"
"fmt" "fmt"
"html/template"
"net/http"
"strconv" "strconv"
"sub2sing-box/api/handler" "sub2sing-box/api/handler"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
//go:embed static
var staticFiles embed.FS
func RunServer(port uint16) { func RunServer(port uint16) {
tpl, err := template.ParseFS(staticFiles, "static/*")
if err != nil {
println(err.Error())
}
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
r := gin.Default() r := gin.Default()
r.SetHTMLTemplate(tpl)
r.GET(
"/static/*path", func(c *gin.Context) {
c.FileFromFS("static/"+c.Param("path"), http.FS(staticFiles))
},
)
r.GET(
"/", func(c *gin.Context) {
c.HTML(
200, "index.html", nil,
)
},
)
r.GET("/convert", handler.Convert) r.GET("/convert", handler.Convert)
fmt.Println("Server is running on port", port) fmt.Println("Server is running on port", port)
err := r.Run(":" + strconv.Itoa(int(port))) err = r.Run(":" + strconv.Itoa(int(port)))
if err != nil { if err != nil {
fmt.Println("Run server failed: ", err) fmt.Println("Run server failed: ", err)
} }

200
api/static/index.html Normal file
View File

@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>sub2sing-box</title>
<!-- 引入 Bootstrap CSS -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
/>
<style>
.rename-group {
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container mt-5">
<h2>sub2sing-box</h2>
<div id="form">
<!-- Subscription -->
<div class="form-group">
<label for="subscription">Subscription:</label>
<textarea
class="form-control"
id="subscription"
name="subscription"
placeholder="一行一个"
></textarea>
</div>
<!-- Proxy -->
<div class="form-group">
<label for="proxy">Proxy:</label>
<textarea
class="form-control"
id="proxy"
name="proxy"
placeholder="一行一个"
></textarea>
</div>
<!-- Delete -->
<div class="form-group">
<label for="delete">Delete:</label>
<input
type="text"
class="form-control"
id="delete"
name="delete"
placeholder="支持正则表达式"
/>
</div>
<!-- Template -->
<div class="form-group">
<label for="template">Template:</label>
<input
type="text"
class="form-control"
id="template"
name="template"
/>
</div>
<!-- Rename -->
<label for="renameContainer">Rename:</label>
<button
type="button"
class="btn btn-primary mb-2"
onclick="addRenameField()"
>
+
</button>
<div id="renameContainer"></div>
</div>
<!-- Output -->
<div class="form-group">
<label for="output">Link:</label>
<textarea class="form-control" id="output" name="output"></textarea>
</div>
</div>
<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>
<script>
init();
function init() {
listenInput();
}
function encodeBase64(str) {
return btoa(
encodeURIComponent(str).replace(
/%([0-9A-F]{2})/g,
function (match, p1) {
return String.fromCharCode("0x" + p1);
}
)
)
.replace(/\+/g, "-")
.replace(/\//g, "_");
}
function decodeBase64(str) {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
})
.join("")
);
}
function listenInput() {
const inputs = document.querySelectorAll("#form input, #form textarea");
for (let input of inputs) {
input.addEventListener("input", generateLink);
}
}
function cleanLisnter() {
const inputs = document.querySelectorAll("#form input, #form textarea");
for (let input of inputs) {
input.removeEventListener("input", generateLink);
}
}
function addRenameField() {
cleanLisnter();
const container = document.getElementById("renameContainer");
const fieldHTML = `<div class="rename-group d-flex align-items-center">
<input type="text" class="form-control mr-2" name="rename_from[]" placeholder="Old Name">
<input type="text" class="form-control mr-2" name="rename_to[]" placeholder="New Name">
<button type="button" class="btn btn-danger" onclick="removeThisField(this)">-</button>
</div>`;
container.insertAdjacentHTML("beforeend", fieldHTML);
listenInput();
}
function removeThisField(button) {
cleanLisnter();
button.parentElement.remove();
generateLink();
listenInput();
}
function generateLink() {
const subscription = document
.getElementById("subscription")
.value.split("\n")
.filter((i) => i);
const proxy = document
.getElementById("proxy")
.value.split("\n")
.filter((i) => i);
const deleteRule = document.getElementById("delete").value;
const template = document.getElementById("template").value;
const renameFrom = Array.from(
document.getElementsByName("rename_from[]")
).map((input) => input.value);
const renameTo = Array.from(
document.getElementsByName("rename_to[]")
).map((input) => input.value);
const output = document.getElementById("output");
let rename = {};
for (let i = 0; i < renameFrom.length; i++) {
if (renameFrom[i] && renameTo[i]) {
rename[renameFrom[i]] = renameTo[i];
}
}
const data = {
subscription,
proxy,
delete: deleteRule,
template,
rename,
};
output.value = `${window.location.origin}/convert?data=${encodeBase64(
JSON.stringify(data)
)}`;
}
</script>
</body>
</html>