1 package main 2 3 import ( 4 "crypto/sha256" 5 "encoding/json" 6 "flag" 7 "fmt" 8 "io" 9 "net/http" 10 "os" 11 "strings" 12 13 "vimagination.zapto.org/httpgzip" 14 "vimagination.zapto.org/tsserver" 15 ) 16 17 type Options struct { 18 MarkdownHTML json.RawMessage `json:"markdownHTML,omitempty"` 19 Embed json.RawMessage `json:"embed,omitempty"` 20 } 21 22 type Config struct { 23 AllowUnsigned bool `json:"allowUnsigned"` 24 Options 25 Keys []struct { 26 Options 27 Name string `json:"name"` 28 Hash string `json:"hash"` 29 Key struct { 30 Alg string `json:"alg"` 31 CRV string `json:"crv"` 32 Ext bool `json:"ext"` 33 KeyOps []string `json:"key_ops"` 34 KTY string `json:"kty"` 35 X string `json:"x"` 36 Y string `json:"y"` 37 } `json:"key"` 38 } `json:"keys"` 39 } 40 41 func main() { 42 var config, pass string 43 44 flag.StringVar(&pass, "p", os.Getenv("CONFIG_PASS"), "SHA256 password hash for config changes") 45 flag.StringVar(&config, "c", os.Getenv("CONFIG_FILE"), "Configuration File") 46 flag.Parse() 47 48 pass = strings.ToUpper(pass) 49 50 if config != "" { 51 http.Handle(http.MethodGet+" /config.json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 52 http.ServeFile(w, r, config) 53 })) 54 55 options := "OPTIONS, GET, HEAD" 56 57 if pass != "" { 58 http.Handle(http.MethodPost+" /config.json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 59 _, password, ok := r.BasicAuth() 60 if !ok { 61 w.WriteHeader(http.StatusUnauthorized) 62 63 io.WriteString(w, "Password Required") 64 65 return 66 } 67 68 if pass != fmt.Sprintf("%X", sha256.Sum256([]byte(password))) { 69 w.WriteHeader(http.StatusForbidden) 70 71 io.WriteString(w, "Invalid Password") 72 73 return 74 } 75 76 var c Config 77 if err := json.NewDecoder(r.Body).Decode(&c); err != nil { 78 w.WriteHeader(http.StatusBadRequest) 79 80 io.WriteString(w, err.Error()) 81 82 return 83 } 84 85 f, err := os.Create(config) 86 if err != nil { 87 w.WriteHeader(http.StatusInternalServerError) 88 89 io.WriteString(w, err.Error()) 90 91 return 92 } 93 94 defer f.Close() 95 96 if err := json.NewEncoder(f).Encode(c); err != nil { 97 w.WriteHeader(http.StatusInternalServerError) 98 99 io.WriteString(w, err.Error()) 100 101 return 102 } 103 104 w.WriteHeader(http.StatusNoContent) 105 })) 106 107 options = "OPTIONS, GET, HEAD, POST" 108 } 109 110 http.Handle(http.MethodOptions+" /config.json", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { 111 w.Header().Set("Allow", options) 112 w.WriteHeader(http.StatusNoContent) 113 })) 114 } 115 116 http.Handle("/", httpgzip.FileServer(http.FS(tsserver.WrapFS(os.DirFS("./src"))))) 117 118 http.ListenAndServe(":8080", nil) 119 } 120