@@ -3,16 +3,18 @@ package sub
33import (
44 "context"
55 "crypto/tls"
6+ "html/template"
67 "io"
8+ "io/fs"
79 "net"
810 "net/http"
911 "os"
1012 "path/filepath"
1113 "strconv"
1214
13- "x-ui/config"
1415 "x-ui/logger"
1516 "x-ui/util/common"
17+ webpkg "x-ui/web"
1618 "x-ui/web/locale"
1719 "x-ui/web/middleware"
1820 "x-ui/web/network"
@@ -21,6 +23,21 @@ import (
2123 "github.com/gin-gonic/gin"
2224)
2325
26+ // setEmbeddedTemplates parses and sets embedded templates on the engine
27+ func setEmbeddedTemplates (engine * gin.Engine ) error {
28+ t , err := template .New ("" ).Funcs (engine .FuncMap ).ParseFS (
29+ webpkg .EmbeddedHTML (),
30+ "html/common/page.html" ,
31+ "html/component/aThemeSwitch.html" ,
32+ "html/subscription.html" ,
33+ )
34+ if err != nil {
35+ return err
36+ }
37+ engine .SetHTMLTemplate (t )
38+ return nil
39+ }
40+
2441type Server struct {
2542 httpServer * http.Server
2643 listener net.Listener
@@ -41,13 +58,10 @@ func NewServer() *Server {
4158}
4259
4360func (s * Server ) initRouter () (* gin.Engine , error ) {
44- if config .IsDebug () {
45- gin .SetMode (gin .DebugMode )
46- } else {
47- gin .DefaultWriter = io .Discard
48- gin .DefaultErrorWriter = io .Discard
49- gin .SetMode (gin .ReleaseMode )
50- }
61+ // Always run in release mode for the subscription server
62+ gin .DefaultWriter = io .Discard
63+ gin .DefaultErrorWriter = io .Discard
64+ gin .SetMode (gin .ReleaseMode )
5165
5266 engine := gin .Default ()
5367
@@ -120,28 +134,35 @@ func (s *Server) initRouter() (*gin.Engine, error) {
120134 SubTitle = ""
121135 }
122136
123- // init i18n for sub server using disk FS so templates can use {{ i18n }}
124- // Root FS is project root; translation files are under web/translation
125- if err := locale .InitLocalizerFS (os .DirFS ("web" ), & s .settingService ); err != nil {
126- logger .Warning ("sub: i18n init failed:" , err )
127- }
128137 // set per-request localizer from headers/cookies
129138 engine .Use (locale .LocalizerMiddleware ())
130139
131- // load HTML templates needed for subscription page (common layout + page + component + subscription)
132- if files , err := s .getHtmlFiles (); err != nil {
133- logger .Warning ("sub: getHtmlFiles failed:" , err )
134- } else {
135- // register i18n function similar to web server
136- i18nWebFunc := func (key string , params ... string ) string {
137- return locale .I18n (locale .Web , key , params ... )
140+ // register i18n function similar to web server
141+ i18nWebFunc := func (key string , params ... string ) string {
142+ return locale .I18n (locale .Web , key , params ... )
143+ }
144+ engine .SetFuncMap (map [string ]any {"i18n" : i18nWebFunc })
145+
146+ // Templates: prefer embedded; fallback to disk if necessary
147+ if err := setEmbeddedTemplates (engine ); err != nil {
148+ logger .Warning ("sub: failed to parse embedded templates:" , err )
149+ if files , derr := s .getHtmlFiles (); derr == nil {
150+ engine .LoadHTMLFiles (files ... )
151+ } else {
152+ logger .Error ("sub: no templates available (embedded parse and disk load failed)" , err , derr )
138153 }
139- engine .SetFuncMap (map [string ]any {"i18n" : i18nWebFunc })
140- engine .LoadHTMLFiles (files ... )
141154 }
142155
143- // serve assets from web/assets to use shared JS/CSS like other pages
144- engine .StaticFS ("/assets" , http .FS (os .DirFS ("web/assets" )))
156+ // Assets: use disk if present, fallback to embedded
157+ if _ , err := os .Stat ("web/assets" ); err == nil {
158+ engine .StaticFS ("/assets" , http .FS (os .DirFS ("web/assets" )))
159+ } else {
160+ if subFS , err := fs .Sub (webpkg .EmbeddedAssets (), "assets" ); err == nil {
161+ engine .StaticFS ("/assets" , http .FS (subFS ))
162+ } else {
163+ logger .Error ("sub: failed to mount embedded assets:" , err )
164+ }
165+ }
145166
146167 g := engine .Group ("/" )
147168
0 commit comments