You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
2.3 KiB
102 lines
2.3 KiB
package main |
|
|
|
import ( |
|
"embed" |
|
"io" |
|
"io/fs" |
|
"net/http" |
|
"path" |
|
"strings" |
|
) |
|
|
|
//go:embed all:web/dist all:web/public |
|
var adminFS embed.FS |
|
|
|
// getAdminSubFS returns the embedded filesystem for the admin UI. |
|
func getAdminSubFS() (fs.FS, error) { |
|
// Try dist first (built assets) |
|
distFS, err := fs.Sub(adminFS, "web/dist") |
|
if err == nil { |
|
// Check if dist has content |
|
entries, _ := fs.ReadDir(distFS, ".") |
|
if len(entries) > 0 { |
|
return distFS, nil |
|
} |
|
} |
|
|
|
// Fall back to public (template) |
|
return fs.Sub(adminFS, "web/public") |
|
} |
|
|
|
// serveAdminUI serves the embedded admin web UI. |
|
func (s *AdminServer) serveAdminUI(w http.ResponseWriter, r *http.Request) { |
|
fsys, err := getAdminSubFS() |
|
if err != nil { |
|
http.Error(w, "Admin UI not available", http.StatusInternalServerError) |
|
return |
|
} |
|
|
|
// Strip /admin prefix from path |
|
urlPath := r.URL.Path |
|
if strings.HasPrefix(urlPath, "/admin") { |
|
urlPath = strings.TrimPrefix(urlPath, "/admin") |
|
} |
|
urlPath = strings.TrimPrefix(urlPath, "/") |
|
|
|
// Default to index.html |
|
if urlPath == "" { |
|
urlPath = "index.html" |
|
} |
|
|
|
// Try to open the file |
|
f, err := fsys.Open(urlPath) |
|
if err != nil { |
|
// For SPA routing, serve index.html for non-existent paths |
|
urlPath = "index.html" |
|
f, err = fsys.Open(urlPath) |
|
if err != nil { |
|
http.Error(w, "Not found", http.StatusNotFound) |
|
return |
|
} |
|
} |
|
defer f.Close() |
|
|
|
// Check if it's a directory |
|
stat, err := f.Stat() |
|
if err != nil { |
|
http.Error(w, "Internal error", http.StatusInternalServerError) |
|
return |
|
} |
|
if stat.IsDir() { |
|
// Try index.html in the directory |
|
f.Close() |
|
urlPath = path.Join(urlPath, "index.html") |
|
f, err = fsys.Open(urlPath) |
|
if err != nil { |
|
http.Error(w, "Not found", http.StatusNotFound) |
|
return |
|
} |
|
defer f.Close() |
|
} |
|
|
|
// Set content type based on extension |
|
switch path.Ext(urlPath) { |
|
case ".html": |
|
w.Header().Set("Content-Type", "text/html; charset=utf-8") |
|
case ".css": |
|
w.Header().Set("Content-Type", "text/css; charset=utf-8") |
|
case ".js": |
|
w.Header().Set("Content-Type", "application/javascript; charset=utf-8") |
|
case ".json": |
|
w.Header().Set("Content-Type", "application/json; charset=utf-8") |
|
case ".svg": |
|
w.Header().Set("Content-Type", "image/svg+xml") |
|
case ".png": |
|
w.Header().Set("Content-Type", "image/png") |
|
case ".ico": |
|
w.Header().Set("Content-Type", "image/x-icon") |
|
} |
|
|
|
// Serve the file content directly |
|
io.Copy(w, f) |
|
}
|
|
|