revert(frontend): keep entry HTML files at frontend/ root

The earlier move to frontend/html/ made dev-mode URLs ugly
(http://localhost:5173/html/index.html instead of plain /). The folder
didn't add real value — it just hid 6 files behind a non-conventional
layout. Reverting that piece while keeping src/entries/ (which is a
genuine separation between page bootstrap and the rest of src/).

- HTML files back at frontend/<page>.html
- Vite rollupOptions.input + MIGRATED_ROUTES restored to flat paths
- Build output is web/dist/<page>.html again
- web/controller/dist.go and sub/subController.go read from dist/<name>

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
MHSanaei 2026-05-09 02:36:26 +02:00
parent 0113ca4005
commit a5083f02e1
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
10 changed files with 28 additions and 28 deletions

View file

@ -6,20 +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/html/<route>.html. // In production the Go binary serves /panel/<route> from web/dist/<route>.html.
// In dev the Vue app lives at /html/index.html, /html/settings.html, ... while // In dev the Vue app lives at /index.html, /settings.html, ... while AppSidebar
// AppSidebar links use the production-style /panel/<route> URLs. Map each // links use the production-style /panel/<route> URLs. Map each migrated route
// migrated route to its Vite entry so the sidebar works without relying on // to its Vite entry so the sidebar works without relying on the Go backend
// the Go backend for already-ported pages. // for already-ported pages.
const MIGRATED_ROUTES = { const MIGRATED_ROUTES = {
'/panel': '/html/index.html', '/panel': '/index.html',
'/panel/': '/html/index.html', '/panel/': '/index.html',
'/panel/settings': '/html/settings.html', '/panel/settings': '/settings.html',
'/panel/settings/': '/html/settings.html', '/panel/settings/': '/settings.html',
'/panel/inbounds': '/html/inbounds.html', '/panel/inbounds': '/inbounds.html',
'/panel/inbounds/': '/html/inbounds.html', '/panel/inbounds/': '/inbounds.html',
'/panel/xray': '/html/xray.html', '/panel/xray': '/xray.html',
'/panel/xray/': '/html/xray.html', '/panel/xray/': '/xray.html',
}; };
// Build a proxy config that suppresses ECONNREFUSED noise when the Go // Build a proxy config that suppresses ECONNREFUSED noise when the Go
@ -102,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, 'html/index.html'), index: path.resolve(__dirname, 'index.html'),
login: path.resolve(__dirname, 'html/login.html'), login: path.resolve(__dirname, 'login.html'),
settings: path.resolve(__dirname, 'html/settings.html'), settings: path.resolve(__dirname, 'settings.html'),
inbounds: path.resolve(__dirname, 'html/inbounds.html'), inbounds: path.resolve(__dirname, 'inbounds.html'),
xray: path.resolve(__dirname, 'html/xray.html'), xray: path.resolve(__dirname, 'xray.html'),
subpage: path.resolve(__dirname, 'html/subpage.html'), subpage: path.resolve(__dirname, '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
@ -143,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 /html/login.html, /html/index.html // Patterns are anchored regex so /login.html and /index.html
// etc. (which Vite serves itself) are NOT forwarded — only the // (which Vite serves itself) are NOT forwarded — only the bare
// bare backend paths and their sub-routes. // backend paths and their sub-routes.
'^/(login|logout|getTwoFactorEnable|csrf-token)$', '^/(login|logout|getTwoFactorEnable|csrf-token)$',
'^/(panel|server)(/|$)', '^/(panel|server)(/|$)',
]), ]),

View file

@ -148,14 +148,14 @@ func (a *SUBController) subs(c *gin.Context) {
} }
} }
// serveSubPage renders web/dist/html/subpage.html for the current subscription // serveSubPage renders web/dist/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/html/subpage.html") body, err := dist.ReadFile("dist/subpage.html")
if err != nil { if err != nil {
c.String(http.StatusInternalServerError, "missing embedded subpage") c.String(http.StatusInternalServerError, "missing embedded subpage")
return return

View file

@ -1350,7 +1350,7 @@ func searchHost(headers any) string {
return "" return ""
} }
// PageData is a view model for html/subpage.html // PageData is a view model for 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

View file

@ -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/html/<name>` from the embedded FS and writes // serveDistPage reads `dist/<name>` from the embedded FS and writes it
// it to the response. Two transforms run before send: // 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/html/" + name) body, err := distFS.ReadFile("dist/" + 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