Redirect POST/PUT requests using config file
s-ol
1 year, 2 months ago
0 | 0 | package main |
1 | 1 | |
2 | 2 | import ( |
3 | "bytes" | |
3 | 4 | "encoding/json" |
4 | "github.com/google/uuid" | |
5 | jsonschema "github.com/santhosh-tekuri/jsonschema/v5" | |
6 | 5 | "log" |
7 | 6 | "net/http" |
8 | 7 | "os" |
9 | 8 | "path/filepath" |
9 | "regexp" | |
10 | 10 | "strconv" |
11 | 11 | "strings" |
12 | "text/template" | |
12 | 13 | "time" |
14 | ||
15 | "github.com/google/uuid" | |
16 | jsonschema "github.com/santhosh-tekuri/jsonschema/v5" | |
13 | 17 | ) |
14 | 18 | |
15 | 19 | type HTTPError struct { |
26 | 30 | } |
27 | 31 | |
28 | 32 | type SiteHandler struct { |
29 | name string | |
30 | path string | |
31 | schema *jsonschema.Schema | |
33 | name string | |
34 | path string | |
35 | schema *jsonschema.Schema | |
36 | referer_pat *regexp.Regexp | |
37 | redirect_tpl *template.Template | |
38 | } | |
39 | ||
40 | type SiteConfig struct { | |
41 | RefererPattern string | |
42 | RedirectTemplate string | |
43 | } | |
44 | ||
45 | type TemplateParams struct { | |
46 | Id string | |
47 | Key string | |
32 | 48 | } |
33 | 49 | |
34 | 50 | func NewSiteHandler(site_name string, site_path string) (*SiteHandler, error) { |
36 | 52 | if err != nil { |
37 | 53 | return nil, err |
38 | 54 | } |
39 | return &SiteHandler{site_name, site_path, schema}, nil | |
55 | ||
56 | var cfg SiteConfig | |
57 | cfg_data, err := os.ReadFile(filepath.Join(site_path, ".config.json")) | |
58 | if err != nil { | |
59 | return nil, err | |
60 | } | |
61 | if err := json.Unmarshal(cfg_data, &cfg); err != nil { | |
62 | return nil, err | |
63 | } | |
64 | ||
65 | referer := regexp.MustCompile(cfg.RefererPattern) | |
66 | template := template.Must(template.New(site_name + "_redirect").Parse(cfg.RedirectTemplate)) | |
67 | ||
68 | return &SiteHandler{site_name, site_path, schema, referer, template}, nil | |
40 | 69 | } |
41 | 70 | |
42 | 71 | func NewMeta() (map[string]interface{}, error) { |
139 | 168 | } |
140 | 169 | data["$meta"] = meta |
141 | 170 | |
142 | bytes, err := json.Marshal(data) | |
143 | if err != nil { | |
144 | return err | |
145 | } | |
146 | ||
171 | raw_data, err := json.Marshal(data) | |
172 | if err != nil { | |
173 | return err | |
174 | } | |
175 | ||
176 | id, key := meta["id"].(string), meta["key"].(string) | |
147 | 177 | if err := os.WriteFile( |
148 | filepath.Join(h.path, meta["id"].(string)+".json"), | |
149 | bytes, | |
178 | filepath.Join(h.path, id+".json"), | |
179 | raw_data, | |
150 | 180 | 0644, |
151 | 181 | ); err != nil { |
152 | 182 | return err |
153 | 183 | } |
154 | 184 | |
155 | 185 | log.Printf("%s %s", r.Method, meta["id"]) |
186 | ||
187 | var redirect_buf bytes.Buffer | |
188 | h.redirect_tpl.Execute(&redirect_buf, TemplateParams{id, key}) | |
189 | ||
156 | 190 | w.Header().Set("Content-Type", "application/json") |
157 | w.Write(bytes) | |
191 | w.Header().Set("Location", redirect_buf.String()) | |
192 | w.WriteHeader(http.StatusCreated) | |
193 | w.Write(raw_data) | |
158 | 194 | return nil |
159 | 195 | } |
160 | 196 | |
163 | 199 | |
164 | 200 | if len(parts) != 3 || parts[1] != h.name { |
165 | 201 | return &HTTPError{http.StatusInternalServerError, nil} |
202 | } | |
203 | ||
204 | if !h.referer_pat.MatchString(r.Header.Get("Referer")) { | |
205 | return &HTTPError{http.StatusUnauthorized, nil} | |
166 | 206 | } |
167 | 207 | |
168 | 208 | id := parts[2] |
176 | 216 | } |
177 | 217 | |
178 | 218 | var data map[string]interface{} |
179 | bytes, err := os.ReadFile(filepath.Join(h.path, id+".json")) | |
219 | raw_data, err := os.ReadFile(filepath.Join(h.path, id+".json")) | |
180 | 220 | if err != nil { |
181 | 221 | return &HTTPError{http.StatusNotFound, nil} |
182 | 222 | } |
183 | 223 | |
184 | if err := json.Unmarshal(bytes, &data); err != nil { | |
224 | if err := json.Unmarshal(raw_data, &data); err != nil { | |
185 | 225 | return err |
186 | 226 | } |
187 | 227 | |
195 | 235 | return h.handlePUTPOST(w, r, meta) |
196 | 236 | } else if r.Method == "GET" { |
197 | 237 | w.Header().Set("Content-Type", "application/json") |
198 | w.Write(bytes) | |
238 | w.Write(raw_data) | |
199 | 239 | return nil |
200 | 240 | } |
201 | 241 | |
210 | 250 | |
211 | 251 | switch e := err.(type) { |
212 | 252 | case *HTTPError: |
213 | code = e.code | |
214 | if e.root != nil { | |
215 | log.Printf("Error: %#v", err) | |
216 | } | |
253 | code = e.code | |
254 | if e.root != nil { | |
255 | log.Printf("Error: %#v", err) | |
256 | } | |
217 | 257 | default: |
218 | 258 | code = http.StatusInternalServerError |
219 | log.Printf("Error: %#v", err) | |
259 | log.Printf("Error: %#v", err) | |
220 | 260 | } |
221 | 261 | |
222 | 262 | http.Error(w, http.StatusText(code), code) |