From 50ea018252ce69542eab6a107b99ea8179810d1e Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Fri, 11 Apr 2025 16:33:59 +0200 Subject: refactor: introduce lex-serve package --- lex-serve/main.go | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 lex-serve/main.go (limited to 'lex-serve/main.go') diff --git a/lex-serve/main.go b/lex-serve/main.go new file mode 100644 index 0000000..d0fb690 --- /dev/null +++ b/lex-serve/main.go @@ -0,0 +1,144 @@ +package main + +import ( + "embed" + "encoding/json" + "io/ioutil" + "log" + "net/http" + "net/url" + "os" + "strings" + "text/template" +) + +//go:embed countries.json +var countriesJSON []byte + +func main() { + domain := os.Getenv("DOMAIN") + if domain == "" { + log.Fatal("DOMAIN environment variable must be set") + } + + var handler = handler{domain: domain, lawsByCC: map[string]map[string]law{}} + err := json.Unmarshal(countriesJSON, &handler.countries) + if err != nil { + log.Fatal("countries.json ", err) + } + + lawFiles, err := ioutil.ReadDir("laws") + if err != nil { + log.Fatal(err) + } + for _, file := range lawFiles { + text, err := ioutil.ReadFile("laws/" + file.Name()) + if err != nil { + log.Fatal(file.Name(), err) + } + var laws []law + err = json.Unmarshal([]byte(text), &laws) + if err != nil { + log.Fatal(file.Name(), err) + } + cc := strings.SplitN(file.Name(), ".", 2)[0] + handler.lawsByCC[cc] = map[string]law{} + for _, law := range laws { + if law.Redir != "" { + handler.lawsByCC[cc][law.Redir] = law + } + } + } + http.HandleFunc("/", handler.handle) + println("listening on 8000") + log.Fatal(http.ListenAndServe(":8000", nil)) +} + +//go:embed templates +var templates embed.FS + +var tpl, _ = template.New("").Funcs(template.FuncMap{ + "ToUpper": strings.ToUpper, +}).ParseFS(templates, "templates/*") + +type handler struct { + domain string + countries map[string]country + lawsByCC map[string]map[string]law +} + +func (h *handler) handle(w http.ResponseWriter, r *http.Request) { + if r.Host == h.domain { + if r.URL.Path != "/" { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("page not found")) + return + } + err := tpl.ExecuteTemplate(w, "index.html.tmpl", map[string]any{ + "Countries": h.countries, + "Domain": r.Host, + }) + if err != nil { + log.Fatal(err) + } + return + } + + cc, isSubdomain := strings.CutSuffix(r.Host, "."+h.domain) + if !isSubdomain { + w.Write([]byte("unknown host")) + return + } + key := strings.TrimLeft(r.URL.Path, "/") + if len(key) > 0 { + val, ok := h.lawsByCC[cc][key] + if !ok { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("unknown law")) + return + } + http.Redirect(w, r, val.URL, 302) + } else { + query := r.URL.Query().Get("q") + if query != "" { + country, ok := h.countries[cc] + if !ok { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("search not implemented for this country")) + return + } + if country.HasPlaceholder() { + http.Redirect(w, r, strings.Replace(country.SearchURL, "%s", url.QueryEscape(query), 1), 302) + return + } else { + w.WriteHeader(http.StatusBadRequest) + } + } + _, hasJSONLaws := h.lawsByCC[cc] + err := tpl.ExecuteTemplate(w, "search.html.tmpl", map[string]any{ + "TLD": cc, + "Domain": h.domain, + "Country": h.countries[cc], + "HasJSONLaws": hasJSONLaws, + }) + if err != nil { + log.Fatal(err) + } + } +} + +type law struct { + URL string + Title string + Abbr string + Redir string +} + +type country struct { + Name string + SearchURL string `json:"search_url"` +} + +func (c country) HasPlaceholder() bool { + return strings.Contains(c.SearchURL, "%s") +} -- cgit v1.2.3