github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/themes/themetester/main.go (about) 1 // The themetester is a standalone executable intended to help internal dev and testing of Caveman themes. 2 // It is also intended to be easy to copy or modify for your own theme development. 3 package main 4 5 import ( 6 "context" 7 "fmt" 8 "log" 9 "net/http" 10 "sort" 11 "sync" 12 13 "github.com/gocaveman/caveman/autowire" 14 "github.com/gocaveman/caveman/renderer" 15 "github.com/gocaveman/caveman/tmpl" 16 "github.com/gocaveman/caveman/webutil" 17 "github.com/gocaveman/caveman/webutil/handlerregistry" 18 "github.com/spf13/pflag" 19 "github.com/spf13/viper" 20 21 "github.com/gocaveman/caveman/themes/paleolithic" 22 "github.com/gocaveman/caveman/themes/pleistocene" 23 ) 24 25 func main() { 26 27 themes := make(map[string]tmpl.Store) 28 29 // add each theme you want to be available here 30 defaultTheme := "paleolithic" 31 themes["paleolithic"] = paleolithic.NewTmplStore() 32 themes["pleistocene"] = pleistocene.NewTmplStore() 33 34 pflag.StringP("http-listen", "l", ":2666", "IP:Port to listen on for HTTP") 35 pflag.StringP("default-theme", "", defaultTheme, "Default theme name on startup") 36 pflag.Parse() 37 viper.BindPFlags(pflag.CommandLine) 38 39 defaultTheme = viper.GetString("default-theme") 40 syncStore := tmpl.NewSyncStore(themes[defaultTheme]) 41 42 themeSwitchHandler := &ThemeSwitchHandler{ 43 SyncStore: syncStore, 44 DefaultTheme: defaultTheme, 45 ThemeMap: themes, 46 } 47 48 hl := webutil.NewDefaultHandlerList() 49 hl = append(hl, themeSwitchHandler) 50 for _, item := range handlerregistry.Contents() { 51 hl = append(hl, item.Value) 52 } 53 54 rend := renderer.NewFromTemplateReader(syncStore) 55 autowire.Provide("", rend) 56 rendHandler := renderer.NewHandler(rend) 57 hl = append(hl, rendHandler) 58 hl = append(hl, renderer.NotFoundHandler(rend, "/_404.gohtml")) 59 60 err := autowire.Contents().Run() 61 if err != nil { 62 log.Fatalf("autowire error: %v", err) 63 } 64 65 var wg sync.WaitGroup 66 67 httpListen := viper.GetString("http-listen") 68 webutil.StartHTTPServer(&http.Server{ 69 Addr: httpListen, 70 Handler: hl, 71 }, &wg) 72 73 log.Printf("Web server starting at %q; use %q to set the theme", httpListen, `/api/theme-switch?id=`+defaultTheme) 74 75 wg.Wait() 76 } 77 78 type ThemeSwitchHandler struct { 79 SyncStore *tmpl.SyncStore 80 DefaultTheme string 81 CurrentTheme string 82 ThemeMap map[string]tmpl.Store 83 sync.RWMutex 84 } 85 86 func (h *ThemeSwitchHandler) ServeHTTPChain(w http.ResponseWriter, r *http.Request) (w2 http.ResponseWriter, r2 *http.Request) { 87 88 w2, r2 = w, r 89 90 if r.URL.Path == "/api/theme-switch" { 91 92 h.Lock() 93 defer h.Unlock() 94 95 w.Header().Set("content-type", "application/json") 96 id := r.FormValue("id") 97 if _, ok := h.ThemeMap[id]; ok && id != "" { 98 h.CurrentTheme = id 99 h.SyncStore.SetStore(h.ThemeMap[h.CurrentTheme]) 100 } else { 101 w.WriteHeader(400) 102 fmt.Fprint(w, `{"error":{"code":400,"message":"invalid id"}}`) 103 return 104 } 105 fmt.Fprint(w, `{"result":true}`) 106 return 107 } 108 109 h.RLock() 110 defer h.RUnlock() 111 112 currentTheme := h.DefaultTheme 113 114 // provide the theme info in the context 115 var themeNameList []string 116 for name := range h.ThemeMap { 117 themeNameList = append(themeNameList, name) 118 } 119 sort.Strings(themeNameList) 120 121 ctx := r2.Context() 122 ctx = context.WithValue(ctx, "themetester.CurrentTheme", currentTheme) 123 ctx = context.WithValue(ctx, "themetester.DefaultTheme", h.DefaultTheme) 124 ctx = context.WithValue(ctx, "themetester.ThemeNameList", themeNameList) 125 r2 = r2.WithContext(ctx) 126 127 return 128 }