From 6196fe338f40c92f78d1702c9664af4e3dba6361 Mon Sep 17 00:00:00 2001 From: ston1th Date: Sat, 12 Apr 2025 22:09:56 +0200 Subject: [PATCH] cleanup --- VERSION | 2 +- main.go | 467 ++++++++++++++++++++++++++++---------------------------- 2 files changed, 235 insertions(+), 234 deletions(-) diff --git a/VERSION b/VERSION index 085135e..49d5957 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.1 +0.1 diff --git a/main.go b/main.go index 6d5ebeb..0a00c93 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,192 @@ import ( "time" ) +var ( + version string + listen string +) + +func main() { + flag.StringVar(&listen, "l", ":7878", "listen addr") + flag.Parse() + + ic := &ImageCrawler{ + url: "https://uvi.bfs.de/Tagesgrafiken", + c: &http.Client{}, + m: make(map[string]*Image), + } + http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + r.ParseForm() + loc := r.PostForm.Get("loc") + if _, ok := locations[loc]; ok { + c := &http.Cookie{ + Name: "loc", + Path: "/", + Value: loc, + Secure: true, + HttpOnly: true, + SameSite: http.SameSiteStrictMode, + } + http.SetCookie(w, c) + http.Redirect(w, r, "/", http.StatusMovedPermanently) + return + } + } + w.Write([]byte(locPage)) + }) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + cc := r.CookiesNamed("loc") + var c *http.Cookie + if len(cc) == 0 { + c = &http.Cookie{ + Name: "loc", + Path: "/", + Value: DefaultCity, + Secure: true, + HttpOnly: true, + SameSite: http.SameSiteStrictMode, + } + http.SetCookie(w, c) + } else { + c = cc[0] + } + loc := c.Value + img, mod, err := ic.getImage(loc) + mod = mod.Local() + location := selectListReverse[loc] + if err != nil { + fmt.Fprintf(w, indexPage, + "000000", + "Err", + mod.Format(time.RFC1123), + time.Now().Format(time.RFC1123), + location, + version, + ) + return + } + vals := getUV(img) + if len(vals) == 0 { + fmt.Fprintf(w, indexPage, + "000000", + "Err", + mod.Format(time.RFC1123), + time.Now().Format(time.RFC1123), + location, + version, + ) + return + } + last := vals[len(vals)-1] + fmt.Fprintf(w, indexPage, + getColor(last), + fmt.Sprintf("%.2f", last), + mod.Format(time.RFC1123), + time.Now().Format(time.RFC1123), + location, + version, + ) + }) + err := http.ListenAndServe(listen, nil) + if err != nil { + fatal(err) + } +} + +type ImageCrawler struct { + mu sync.RWMutex + url string + c *http.Client + m map[string]*Image +} + +type Image struct { + mu sync.RWMutex + ic *ImageCrawler + file string + img image.Image + etag string + modified time.Time + lastGet time.Time +} + +func (ic *ImageCrawler) getImage(loc string) (img image.Image, mod time.Time, err error) { + file, ok := locations[loc] + if !ok { + return nil, time.Time{}, errors.New("invalid location") + } + ic.mu.RLock() + if i, ok := ic.m[loc]; ok { + ic.mu.RUnlock() + return i.getImage() + } + ic.mu.RUnlock() + ic.mu.Lock() + i := &Image{ + ic: ic, + file: file, + } + ic.m[loc] = i + ic.mu.Unlock() + return i.getImage() +} + +func (i *Image) getImage() (img image.Image, mod time.Time, err error) { + i.mu.RLock() + if !i.lastGet.IsZero() && + time.Now().Sub(i.lastGet) < time.Minute*3 { + defer i.mu.RUnlock() + return i.img, i.modified, nil + } + i.mu.RUnlock() + req, err := http.NewRequest("GET", i.ic.url+"/"+i.file, nil) + if err != nil { + return + } + req.Header.Set("user-agent", "uvgo/"+version) + i.mu.RLock() + if !i.modified.IsZero() { + req.Header.Set("if-modified-since", i.modified.Format(time.RFC1123)) + } + if i.etag != "" { + req.Header.Set("if-none-match", i.etag) + } + i.mu.RUnlock() + resp, err := i.ic.c.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotModified { + i.mu.RLock() + defer i.mu.RUnlock() + return i.img, i.modified, nil + } + if resp.StatusCode == http.StatusOK { + img, err = png.Decode(resp.Body) + if err != nil { + return + } + i.mu.Lock() + i.img = img + modTime, err := time.Parse(time.RFC1123, resp.Header.Get("last-modified")) + if err == nil { + i.modified = modTime + } + i.etag = resp.Header.Get("etag") + i.lastGet = time.Now() + defer i.mu.Unlock() + return i.img, i.modified, nil + } + i.mu.RLock() + defer i.mu.RUnlock() + if i.img != nil { + return i.img, i.modified, nil + } + return nil, time.Time{}, errors.New("error getting image") +} + const ( X = 82 maxX = 574 @@ -98,33 +284,6 @@ func getUV(img image.Image) (vals []float64) { return } -var tmp = ` - - -UV Index - - - - -
-
UV Index
-

%s

-
Updated: %s
-
Server: %s
-
Location: %s
-
UV data copyright belongs to Bundesamt für Strahlenschutz
-Use this service at your own risk.
-I take no responsibility for missing or incorrect data.
uvgo %s

-
-
- -` - func getColor(v float64) string { if v < 1.0 { return "006300" @@ -152,195 +311,6 @@ func getColor(v float64) string { return "000000" } -var ( - locPage string - version string - listen string -) - -func main() { - flag.StringVar(&listen, "l", ":7878", "listen addr") - flag.Parse() - - url := "https://uvi.bfs.de/Tagesgrafiken" - generateLocPage() - - ic := &ImageCrawler{ - url: url, - c: &http.Client{}, - m: make(map[string]*Image), - } - http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) { - if r.Method == "POST" { - r.ParseForm() - loc := r.PostForm.Get("loc") - if _, ok := locations[loc]; ok { - c := &http.Cookie{ - Name: "loc", - Path: "/", - Value: loc, - Secure: true, - HttpOnly: true, - SameSite: http.SameSiteStrictMode, - } - http.SetCookie(w, c) - http.Redirect(w, r, "/", http.StatusMovedPermanently) - return - } - } - w.Write([]byte(locPage)) - }) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - cc := r.CookiesNamed("loc") - var c *http.Cookie - if len(cc) == 0 { - c = &http.Cookie{ - Name: "loc", - Path: "/", - Value: "24", - Secure: true, - HttpOnly: true, - SameSite: http.SameSiteStrictMode, - } - http.SetCookie(w, c) - } else { - c = cc[0] - } - loc := c.Value - img, mod, err := ic.getImage(loc) - mod = mod.Local() - location := selectListReverse[loc] - if err != nil { - fmt.Fprintf(w, tmp, - "000000", - "Err", - mod.Format(time.RFC1123), - time.Now().Format(time.RFC1123), - location, - version, - ) - return - } - vals := getUV(img) - if len(vals) == 0 { - fmt.Fprintf(w, tmp, - "000000", - "Err", - mod.Format(time.RFC1123), - time.Now().Format(time.RFC1123), - location, - version, - ) - return - } - last := vals[len(vals)-1] - fmt.Fprintf(w, tmp, - getColor(last), - fmt.Sprintf("%.2f", last), - mod.Format(time.RFC1123), - time.Now().Format(time.RFC1123), - location, - version, - ) - }) - err := http.ListenAndServe(listen, nil) - if err != nil { - fatal(err) - } -} - -type ImageCrawler struct { - mu sync.RWMutex - url string - c *http.Client - m map[string]*Image -} - -type Image struct { - mu sync.RWMutex - ic *ImageCrawler - file string - img image.Image - etag string - modified time.Time - lastGet time.Time -} - -func (ic *ImageCrawler) getImage(loc string) (img image.Image, mod time.Time, err error) { - file, ok := locations[loc] - if !ok { - return nil, time.Time{}, errors.New("invalid location") - } - ic.mu.RLock() - if i, ok := ic.m[loc]; ok { - ic.mu.RUnlock() - return i.getImage() - } - ic.mu.RUnlock() - ic.mu.Lock() - i := &Image{ - ic: ic, - file: file, - } - ic.m[loc] = i - ic.mu.Unlock() - return i.getImage() -} - -func (i *Image) getImage() (img image.Image, mod time.Time, err error) { - i.mu.RLock() - if !i.lastGet.IsZero() && - time.Now().Sub(i.lastGet) < time.Minute*3 { - defer i.mu.RUnlock() - return i.img, i.modified, nil - } - i.mu.RUnlock() - req, err := http.NewRequest("GET", i.ic.url+"/"+i.file, nil) - if err != nil { - return - } - i.mu.RLock() - if !i.modified.IsZero() { - req.Header.Set("if-modified-since", i.modified.Format(time.RFC1123)) - } - if i.etag != "" { - req.Header.Set("if-none-match", i.etag) - } - i.mu.RUnlock() - resp, err := i.ic.c.Do(req) - if err != nil { - return - } - defer resp.Body.Close() - if resp.StatusCode == http.StatusNotModified { - i.mu.RLock() - defer i.mu.RUnlock() - return i.img, i.modified, nil - } - if resp.StatusCode == http.StatusOK { - img, err = png.Decode(resp.Body) - if err != nil { - return - } - i.mu.Lock() - i.img = img - modTime, err := time.Parse(time.RFC1123, resp.Header.Get("last-modified")) - if err == nil { - i.modified = modTime - } - i.etag = resp.Header.Get("etag") - i.lastGet = time.Now() - defer i.mu.Unlock() - return i.img, i.modified, nil - } - i.mu.RLock() - defer i.mu.RUnlock() - if i.img != nil { - return i.img, i.modified, nil - } - return nil, time.Time{}, errors.New("error getting image") -} - func isWhite(col color.Color) bool { col = color.RGBAModel.Convert(col) c, ok := col.(color.RGBA) @@ -419,23 +389,7 @@ func fatal(err error) { } } -func generateLocPage() { - loc := ` - -UV Index Change Location - - - -
-
Change Location
-
-
` - locPage = loc -} +const DefaultCity = "24" var locations = map[string]string{ "0": "EEr_Andernach_today.png", @@ -556,3 +510,50 @@ func sortedMap[K cmp.Ordered, V any](m map[K]V) iter.Seq2[K, V] { } } } + +func generateLocPage() string { + loc := ` + +UV Index Change Location + + + +
+
Change Location
+
+
` + return loc +} + +var locPage = generateLocPage() + +var indexPage = ` + + +UV Index + + + + +
+
UV Index
+

%s

+
Updated: %s
+
Server: %s
+
Location: %s
+ +
+ +`