package middleware import ( "bytes" "fmt" "net/http" "os" "strings" "text/template" _ "embed" "github.com/yusing/go-proxy/internal/gperr" ) type themed struct { FontURL string `json:"font_url"` FontFamily string `json:"font_family"` Theme Theme `json:"theme"` // predefined themes CSS string `json:"css"` // css url or content m modifyHTML } var Themed = NewMiddleware[themed]() type Theme string const ( DarkTheme Theme = "dark" DarkGreyTheme Theme = "dark-grey" SolarizedDarkTheme Theme = "solarized-dark" ) var ( //go:embed themes/dark.css darkModeCSS string //go:embed themes/dark-grey.css darkGreyModeCSS string //go:embed themes/solarized-dark.css solarizedDarkModeCSS string //go:embed themes/font.css fontCSS string ) var fontCSSTemplate = template.Must(template.New("fontCSS").Parse(fontCSS)) const overAllocate = 256 func (m *themed) before(w http.ResponseWriter, req *http.Request) bool { return m.m.before(w, req) } func (m *themed) modifyResponse(resp *http.Response) error { return m.m.modifyResponse(resp) } func (m *themed) finalize() error { m.m.Target = "body" if m.FontURL != "" && m.FontFamily != "" { buf := bytes.NewBuffer(bytePool.GetSized(len(fontCSS) + overAllocate)) buf.WriteString(`") m.m.HTML += buf.String() } if m.CSS != "" && m.Theme != "" { return gperr.New("css and theme are mutually exclusive") } // credit: https://hackcss.egoist.dev if m.Theme != "" { switch m.Theme { case DarkTheme: m.m.HTML += wrapStyleTag(darkModeCSS) case DarkGreyTheme: m.m.HTML += wrapStyleTag(darkGreyModeCSS) case SolarizedDarkTheme: m.m.HTML += wrapStyleTag(solarizedDarkModeCSS) default: return gperr.New("invalid theme").Subject(string(m.Theme)) } } if m.CSS != "" { switch { case strings.HasPrefix(m.CSS, "https://"), strings.HasPrefix(m.CSS, "http://"), strings.HasPrefix(m.CSS, "/"): m.m.HTML += wrapStylesheetLinkTag(m.CSS) case strings.HasPrefix(m.CSS, "file://"): css, err := os.ReadFile(strings.TrimPrefix(m.CSS, "file://")) if err != nil { return err } m.m.HTML += wrapStyleTag(string(css)) default: // inline css m.m.HTML += wrapStyleTag(m.CSS) } } return nil } func wrapStyleTag(css string) string { return fmt.Sprintf(``, css) } func wrapStylesheetLinkTag(href string) string { return fmt.Sprintf(``, href) }