mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-09 06:34:18 +00:00
refactor(frontend): organize entry HTML + bootstrap JS into folders
- Move entry HTML files: frontend/*.html -> frontend/html/*.html
- Move per-page bootstrap modules: src/{index,login,settings,inbounds,xray,subpage}.js -> src/entries/
- Update vite.config rollup inputs and dev-mode MIGRATED_ROUTES to /html/<page>.html
- Build output now lands at web/dist/html/<page>.html
- serveDistPage and subController updated to read from dist/html/
Cleans up the flat frontend/ root which previously interleaved 6 HTML
files with package.json, README, src/, etc. The src/ root similarly
gets rid of 6 entry .js files mixed in alongside api/, components/,
models/, etc.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
3029155974
commit
2616f25638
16 changed files with 34 additions and 35 deletions
|
|
@ -8,6 +8,6 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="message"></div>
|
<div id="message"></div>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/inbounds.js"></script>
|
<script type="module" src="/src/entries/inbounds.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -8,6 +8,6 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="message"></div>
|
<div id="message"></div>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/index.js"></script>
|
<script type="module" src="/src/entries/index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -9,6 +9,6 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="message"></div>
|
<div id="message"></div>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/login.js"></script>
|
<script type="module" src="/src/entries/login.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -8,6 +8,6 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="message"></div>
|
<div id="message"></div>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/settings.js"></script>
|
<script type="module" src="/src/entries/settings.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -9,6 +9,6 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="message"></div>
|
<div id="message"></div>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/subpage.js"></script>
|
<script type="module" src="/src/entries/subpage.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -8,6 +8,6 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="message"></div>
|
<div id="message"></div>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/xray.js"></script>
|
<script type="module" src="/src/entries/xray.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -6,21 +6,20 @@ import path from 'node:path';
|
||||||
// via embed.FS without reaching outside the web/ tree.
|
// via embed.FS without reaching outside the web/ tree.
|
||||||
const outDir = path.resolve(__dirname, '../web/dist');
|
const outDir = path.resolve(__dirname, '../web/dist');
|
||||||
|
|
||||||
// In production the Go binary serves /panel/<route> from web/dist/<route>.html.
|
// In production the Go binary serves /panel/<route> from web/dist/html/<route>.html.
|
||||||
// In dev the Vue app lives at /index.html, /settings.html, ... while AppSidebar
|
// In dev the Vue app lives at /html/index.html, /html/settings.html, ... while
|
||||||
// links use the production-style /panel/<route> URLs. Map each migrated route
|
// AppSidebar links use the production-style /panel/<route> URLs. Map each
|
||||||
// to its Vite entry so the sidebar works without relying on the Go backend
|
// migrated route to its Vite entry so the sidebar works without relying on
|
||||||
// for already-ported pages. Unmigrated routes (inbounds, xray) fall through
|
// the Go backend for already-ported pages.
|
||||||
// to the proxy.
|
|
||||||
const MIGRATED_ROUTES = {
|
const MIGRATED_ROUTES = {
|
||||||
'/panel': '/index.html',
|
'/panel': '/html/index.html',
|
||||||
'/panel/': '/index.html',
|
'/panel/': '/html/index.html',
|
||||||
'/panel/settings': '/settings.html',
|
'/panel/settings': '/html/settings.html',
|
||||||
'/panel/settings/': '/settings.html',
|
'/panel/settings/': '/html/settings.html',
|
||||||
'/panel/inbounds': '/inbounds.html',
|
'/panel/inbounds': '/html/inbounds.html',
|
||||||
'/panel/inbounds/': '/inbounds.html',
|
'/panel/inbounds/': '/html/inbounds.html',
|
||||||
'/panel/xray': '/xray.html',
|
'/panel/xray': '/html/xray.html',
|
||||||
'/panel/xray/': '/xray.html',
|
'/panel/xray/': '/html/xray.html',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build a proxy config that suppresses ECONNREFUSED noise when the Go
|
// Build a proxy config that suppresses ECONNREFUSED noise when the Go
|
||||||
|
|
@ -103,12 +102,12 @@ export default defineConfig({
|
||||||
// As pages get ported in later phases, add their entrypoints here.
|
// As pages get ported in later phases, add their entrypoints here.
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: {
|
input: {
|
||||||
index: path.resolve(__dirname, 'index.html'),
|
index: path.resolve(__dirname, 'html/index.html'),
|
||||||
login: path.resolve(__dirname, 'login.html'),
|
login: path.resolve(__dirname, 'html/login.html'),
|
||||||
settings: path.resolve(__dirname, 'settings.html'),
|
settings: path.resolve(__dirname, 'html/settings.html'),
|
||||||
inbounds: path.resolve(__dirname, 'inbounds.html'),
|
inbounds: path.resolve(__dirname, 'html/inbounds.html'),
|
||||||
xray: path.resolve(__dirname, 'xray.html'),
|
xray: path.resolve(__dirname, 'html/xray.html'),
|
||||||
subpage: path.resolve(__dirname, 'subpage.html'),
|
subpage: path.resolve(__dirname, 'html/subpage.html'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
// Split vendor deps into stable chunks so each page only pulls
|
// Split vendor deps into stable chunks so each page only pulls
|
||||||
|
|
@ -144,9 +143,9 @@ export default defineConfig({
|
||||||
port: 5173,
|
port: 5173,
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
proxy: makeBackendProxy('http://localhost:2053', [
|
proxy: makeBackendProxy('http://localhost:2053', [
|
||||||
// Patterns are anchored regex so /login.html and /index.html
|
// Patterns are anchored regex so /html/login.html, /html/index.html
|
||||||
// (which Vite serves itself) are NOT forwarded — only the bare
|
// etc. (which Vite serves itself) are NOT forwarded — only the
|
||||||
// backend paths and their sub-routes.
|
// bare backend paths and their sub-routes.
|
||||||
'^/(login|logout|getTwoFactorEnable|csrf-token)$',
|
'^/(login|logout|getTwoFactorEnable|csrf-token)$',
|
||||||
'^/(panel|server)(/|$)',
|
'^/(panel|server)(/|$)',
|
||||||
]),
|
]),
|
||||||
|
|
|
||||||
|
|
@ -148,14 +148,14 @@ func (a *SUBController) subs(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveSubPage renders web/dist/subpage.html for the current subscription
|
// serveSubPage renders web/dist/html/subpage.html for the current subscription
|
||||||
// request. The Vite-built SPA reads window.__SUB_PAGE_DATA__ on mount —
|
// request. The Vite-built SPA reads window.__SUB_PAGE_DATA__ on mount —
|
||||||
// we inject that here, along with window.__X_UI_BASE_PATH__ so the
|
// we inject that here, along with window.__X_UI_BASE_PATH__ so the
|
||||||
// page's static asset references resolve correctly when the panel runs
|
// page's static asset references resolve correctly when the panel runs
|
||||||
// behind a URL prefix.
|
// behind a URL prefix.
|
||||||
func (a *SUBController) serveSubPage(c *gin.Context, basePath string, page PageData) {
|
func (a *SUBController) serveSubPage(c *gin.Context, basePath string, page PageData) {
|
||||||
dist := webpkg.EmbeddedDist()
|
dist := webpkg.EmbeddedDist()
|
||||||
body, err := dist.ReadFile("dist/subpage.html")
|
body, err := dist.ReadFile("dist/html/subpage.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, "missing embedded subpage")
|
c.String(http.StatusInternalServerError, "missing embedded subpage")
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -1350,7 +1350,7 @@ func searchHost(headers any) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageData is a view model for subpage.html
|
// PageData is a view model for html/subpage.html
|
||||||
// PageData contains data for rendering the subscription information page.
|
// PageData contains data for rendering the subscription information page.
|
||||||
type PageData struct {
|
type PageData struct {
|
||||||
Host string
|
Host string
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ func SetDistFS(fs embed.FS) {
|
||||||
// GETs can hit the 304 path on repeat loads.
|
// GETs can hit the 304 path on repeat loads.
|
||||||
var distPageBuildTime = time.Now()
|
var distPageBuildTime = time.Now()
|
||||||
|
|
||||||
// serveDistPage reads `dist/<name>` from the embedded FS and writes it
|
// serveDistPage reads `dist/html/<name>` from the embedded FS and writes
|
||||||
// to the response. Two transforms run before send:
|
// it to the response. Two transforms run before send:
|
||||||
//
|
//
|
||||||
// 1. `<script>window.__X_UI_BASE_PATH__ = "..."</script>` is injected
|
// 1. `<script>window.__X_UI_BASE_PATH__ = "..."</script>` is injected
|
||||||
// just before </head> so the AppSidebar's link generator sees the
|
// just before </head> so the AppSidebar's link generator sees the
|
||||||
|
|
@ -51,7 +51,7 @@ var distPageBuildTime = time.Now()
|
||||||
// reaches users on the next reload; the long-hashed JS/CSS files
|
// reaches users on the next reload; the long-hashed JS/CSS files
|
||||||
// under /assets/ stay cacheable indefinitely.
|
// under /assets/ stay cacheable indefinitely.
|
||||||
func serveDistPage(c *gin.Context, name string) {
|
func serveDistPage(c *gin.Context, name string) {
|
||||||
body, err := distFS.ReadFile("dist/" + name)
|
body, err := distFS.ReadFile("dist/html/" + name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, "missing embedded page: %s", name)
|
c.String(http.StatusInternalServerError, "missing embedded page: %s", name)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue