diff --git a/.gitignore b/.gitignore index b016f34..8373007 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ dumb +views/*_templ.go diff --git a/Dockerfile b/Dockerfile index d063447..9e08192 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN go build +RUN make build EXPOSE 5555/tcp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..009f4d6 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +gentempl: + @command -v templ &> /dev/null || go install github.com/a-h/templ/cmd/templ@latest +build:gentempl + templ generate && go build -o dumb diff --git a/README.md b/README.md index 87d03ba..6416959 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # dumb -With the massive daily increase of useless scripts on Genius's web frontend and having to download megabytes of clutter, [dumb](https://github.com/rramiachraf/dumb) tries to make reading lyrics from Genius a pleasant experience and as lightweight as possible. +With the massive daily increase of useless scripts on Genius's web frontend, and having to download megabytes of clutter, [dumb](https://github.com/rramiachraf/dumb) tries to make reading lyrics from Genius a pleasant experience, and as lightweight as possible. @@ -10,7 +10,7 @@ With the massive daily increase of useless scripts on Genius's web frontend and ```bash git clone https://github.com/rramiachraf/dumb cd dumb -go build +make build ./dumb ``` @@ -38,9 +38,8 @@ The default port is 5555, you can use other ports by setting the `PORT` environm | --- | --- | | | https://vern.cc | -Instances list in JSON format can be found in [instances.json](instances.json) file. - -For people who might be capable and interested in hosting a public instance feel free to do so and don't forget to open a pull request so your instance can be included here. +- Instances list in JSON format can be found in [instances.json](instances.json) file. +- For people who might be capable and interested in hosting a public instance feel free to do so, and don't forget to open a pull request, so your instance can be included here. ## Contributing Contributions are welcome. diff --git a/album.go b/album.go deleted file mode 100644 index e21d8a2..0000000 --- a/album.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "net/http" - "strings" - - "github.com/PuerkitoBio/goquery" - "github.com/gorilla/mux" -) - -type album struct { - Artist string - Name string - Image string - About [2]string - - Tracks []Track -} - -type Track struct { - Title string - Url string -} - -type albumMetadata struct { - Album struct { - Id int `json:"id"` - Image string `json:"cover_art_thumbnail_url"` - Name string `json:"name"` - Description string `json:"description_preview"` - Artist `json:"artist"` - } - AlbumAppearances []AlbumAppearances `json:"album_appearances"` -} - -type AlbumAppearances struct { - Id int `json:"id"` - TrackNumber int `json:"track_number"` - Song struct { - Title string `json:"title"` - Url string `json:"url"` - } -} - -type Artist struct { - Name string `json:"name"` -} - -func (a *album) parseAlbumData(doc *goquery.Document) { - pageMetadata, exists := doc.Find("meta[itemprop='page_data']").Attr("content") - if !exists { - return - } - - var albumMetadataFromPage albumMetadata - json.Unmarshal([]byte(pageMetadata), &albumMetadataFromPage) - - albumData := albumMetadataFromPage.Album - a.Artist = albumData.Artist.Name - a.Name = albumData.Name - a.Image = albumData.Image - a.About[0] = albumData.Description - a.About[1] = truncateText(albumData.Description) - - for _, track := range albumMetadataFromPage.AlbumAppearances { - url := strings.Replace(track.Song.Url, "https://genius.com", "", -1) - a.Tracks = append(a.Tracks, Track{Title: track.Song.Title, Url: url}) - } -} - -func (a *album) parse(doc *goquery.Document) { - a.parseAlbumData(doc) -} - -func albumHandler(w http.ResponseWriter, r *http.Request) { - artist := mux.Vars(r)["artist"] - albumName := mux.Vars(r)["albumName"] - - id := fmt.Sprintf("%s/%s", artist, albumName) - - if data, err := getCache(id); err == nil { - render("album", w, data) - return - } - - url := fmt.Sprintf("https://genius.com/albums/%s/%s", artist, albumName) - - resp, err := sendRequest(url) - if err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "cannot reach genius servers", - }) - return - } - - defer resp.Body.Close() - - if resp.StatusCode == http.StatusNotFound { - w.WriteHeader(http.StatusNotFound) - render("error", w, map[string]string{ - "Status": "404", - "Error": "page not found", - }) - return - } - - doc, err := goquery.NewDocumentFromReader(resp.Body) - if err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "something went wrong", - }) - return - } - - cf := doc.Find(".cloudflare_content").Length() - if cf > 0 { - logger.Errorln("cloudflare got in the way") - render("error", w, map[string]string{ - "Status": "500", - "Error": "damn cloudflare, issue #21 on GitHub", - }) - return - } - - var a album - a.parse(doc) - - render("album", w, a) - - setCache(id, a) -} diff --git a/annotations.go b/annotations.go deleted file mode 100644 index f77d35c..0000000 --- a/annotations.go +++ /dev/null @@ -1,127 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "regexp" - "strings" - - "github.com/gorilla/mux" -) - -type annotationsResponse struct { - Response struct { - Referent struct { - Annotations []struct { - Body struct { - Html string `json:"html"` - } `json:"body"` - } `json:"annotations"` - } `json:"referent"` - } `json:"response"` -} - -func annotationsHandler(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - - if data, err := getCache(id); err == nil { - - response, err := json.Marshal(data) - - if err != nil { - logger.Errorf("could not marshal json: %s\n", err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "Could not parse genius api response", - }) - return - } - w.Header().Set("content-type", "application/json") - _, err = w.Write(response) - if err != nil { - logger.Errorln("Error sending response: ", err) - } - return - } - - url := fmt.Sprintf("https://genius.com/api/referents/%s?text_format=html", id) - resp, err := sendRequest(url) - - if err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "cannot reach genius servers", - }) - return - } - - defer resp.Body.Close() - - if resp.StatusCode == http.StatusNotFound { - w.WriteHeader(http.StatusNotFound) - render("error", w, map[string]string{ - "Status": "404", - "Error": "page not found", - }) - return - } - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(resp.Body) - if err != nil { - logger.Errorln("Error paring genius api response", err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "Parsing error", - }) - return - } - - var data annotationsResponse - err = json.Unmarshal(buf.Bytes(), &data) - if err != nil { - logger.Errorf("could not unmarshal json: %s\n", err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "Could not parse genius api response", - }) - return - } - - w.Header().Set("content-type", "application/json") - body := data.Response.Referent.Annotations[0].Body - body.Html = cleanBody(body.Html) - response, err := json.Marshal(body) - - if err != nil { - logger.Errorf("could not marshal json: %s\n", err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "Could not parse genius api response", - }) - return - } - - setCache(id, body) - _, err = w.Write(response) - if err != nil { - logger.Errorln("Error sending response: ", err) - } -} - -func cleanBody(body string) string { - var withCleanedImageLinks = strings.Replace(body, "https://images.rapgenius.com/", "/images/", -1) - - var re = regexp.MustCompile(`https?:\/\/[a-z]*.?genius.com`) - var withCleanedLinks = re.ReplaceAllString(withCleanedImageLinks, "") - - return withCleanedLinks -} diff --git a/data/album.go b/data/album.go new file mode 100644 index 0000000..65dc60f --- /dev/null +++ b/data/album.go @@ -0,0 +1,77 @@ +package data + +import ( + "encoding/json" + "strings" + + "github.com/PuerkitoBio/goquery" +) + +type Album struct { + Artist string + Name string + Image string + About [2]string + + Tracks []Track +} + +type Track struct { + Title string + Url string +} + +type albumMetadata struct { + Album struct { + Id int `json:"id"` + Image string `json:"cover_art_thumbnail_url"` + Name string `json:"name"` + Description string `json:"description_preview"` + Artist `json:"artist"` + } + AlbumAppearances []AlbumAppearances `json:"album_appearances"` +} + +type AlbumAppearances struct { + Id int `json:"id"` + TrackNumber int `json:"track_number"` + Song struct { + Title string `json:"title"` + Url string `json:"url"` + } +} + +type Artist struct { + Name string `json:"name"` +} + +func (a *Album) parseAlbumData(doc *goquery.Document) error { + pageMetadata, exists := doc.Find("meta[itemprop='page_data']").Attr("content") + if !exists { + return nil + } + + var albumMetadataFromPage albumMetadata + if err := json.Unmarshal([]byte(pageMetadata), &albumMetadataFromPage); err != nil { + return err + } + + albumData := albumMetadataFromPage.Album + a.Artist = albumData.Artist.Name + a.Name = albumData.Name + a.Image = albumData.Image + a.About[0] = albumData.Description + a.About[1] = truncateText(albumData.Description) + a.About[1] = "" + + for _, track := range albumMetadataFromPage.AlbumAppearances { + url := strings.Replace(track.Song.Url, "https://genius.com", "", -1) + a.Tracks = append(a.Tracks, Track{Title: track.Song.Title, Url: url}) + } + + return nil +} + +func (a *Album) Parse(doc *goquery.Document) error { + return a.parseAlbumData(doc) +} diff --git a/data/annotation.go b/data/annotation.go new file mode 100644 index 0000000..9e8bd66 --- /dev/null +++ b/data/annotation.go @@ -0,0 +1,15 @@ +package data + +type AnnotationsResponse struct { + Response struct { + Referent struct { + Annotations []struct { + Body Annotation `json:"body"` + } `json:"annotations"` + } `json:"referent"` + } `json:"response"` +} + +type Annotation struct { + HTML string `json:"html"` +} diff --git a/data/lyrics.go b/data/lyrics.go new file mode 100644 index 0000000..a2f3c8d --- /dev/null +++ b/data/lyrics.go @@ -0,0 +1,117 @@ +package data + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/PuerkitoBio/goquery" + "github.com/sirupsen/logrus" +) + +type Song struct { + Artist string + Title string + Image string + Lyrics string + Credits map[string]string + About [2]string + Album struct { + URL string + Name string + Image string + } +} + +type songResponse struct { + Response struct { + Song struct { + ArtistNames string `json:"artist_names"` + Image string `json:"song_art_image_thumbnail_url"` + Title string + Description struct { + Plain string + } + Album struct { + URL string `json:"url"` + Name string `json:"name"` + Image string `json:"cover_art_url"` + } + CustomPerformances []customPerformance `json:"custom_performances"` + } + } +} + +type customPerformance struct { + Label string + Artists []struct { + Name string + } +} + +func (s *Song) parseLyrics(doc *goquery.Document) { + doc.Find("[data-lyrics-container='true']").Each(func(i int, ss *goquery.Selection) { + h, err := ss.Html() + if err != nil { + logrus.Errorln("unable to parse lyrics", err) + } + s.Lyrics += h + }) +} + +func (s *Song) parseSongData(doc *goquery.Document) { + attr, exists := doc.Find("meta[property='twitter:app:url:iphone']").Attr("content") + if exists { + songID := strings.Replace(attr, "genius://songs/", "", 1) + + u := fmt.Sprintf("https://genius.com/api/songs/%s?text_format=plain", songID) + + res, err := sendRequest(u) + if err != nil { + logrus.Errorln(err) + } + + defer res.Body.Close() + + var data songResponse + decoder := json.NewDecoder(res.Body) + err = decoder.Decode(&data) + if err != nil { + logrus.Errorln(err) + } + + songData := data.Response.Song + s.Artist = songData.ArtistNames + s.Image = songData.Image + s.Title = songData.Title + s.About[0] = songData.Description.Plain + s.About[1] = truncateText(songData.Description.Plain) + s.Credits = make(map[string]string) + s.Album.Name = songData.Album.Name + s.Album.URL = strings.Replace(songData.Album.URL, "https://genius.com", "", -1) + s.Album.Image = ExtractImageURL(songData.Album.Image) + + for _, perf := range songData.CustomPerformances { + var artists []string + for _, artist := range perf.Artists { + artists = append(artists, artist.Name) + } + s.Credits[perf.Label] = strings.Join(artists, ", ") + } + } +} + +func truncateText(text string) string { + textArr := strings.Split(text, "") + + if len(textArr) > 250 { + return strings.Join(textArr[0:250], "") + "..." + } + + return text +} + +func (s *Song) Parse(doc *goquery.Document) { + s.parseLyrics(doc) + s.parseSongData(doc) +} diff --git a/data/proxy.go b/data/proxy.go new file mode 100644 index 0000000..4b432d8 --- /dev/null +++ b/data/proxy.go @@ -0,0 +1,16 @@ +package data + +import ( + "fmt" + "net/url" +) + +func ExtractImageURL(image string) string { + u, err := url.Parse(image) + if err != nil { + return "" + } + + return fmt.Sprintf("/images%s", u.Path) +} + diff --git a/data/search.go b/data/search.go new file mode 100644 index 0000000..d78ce41 --- /dev/null +++ b/data/search.go @@ -0,0 +1,28 @@ +package data + +type SearchResponse struct { + Response struct { + Sections sections + } +} + +type result struct { + ArtistNames string `json:"artist_names"` + Title string + Path string + Thumbnail string `json:"song_art_image_thumbnail_url"` +} + +type hits []struct { + Result result +} + +type sections []struct { + Type string + Hits hits +} + +type SearchResults struct { + Query string + Sections sections +} diff --git a/data/utils.go b/data/utils.go new file mode 100644 index 0000000..022533a --- /dev/null +++ b/data/utils.go @@ -0,0 +1,46 @@ +package data + +import ( + "net" + "net/http" + "net/url" + "time" + + "github.com/caffix/cloudflare-roundtripper/cfrt" +) + +const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" + +var client = &http.Client{ + Timeout: 20 * time.Second, + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + Timeout: 15 * time.Second, + KeepAlive: 15 * time.Second, + DualStack: true, + }).DialContext, + }, +} + +func sendRequest(u string) (*http.Response, error) { + url, err := url.Parse(u) + if err != nil { + return nil, err + } + + client.Transport, err = cfrt.New(client.Transport) + if err != nil { + return nil, err + } + + req := &http.Request{ + Method: http.MethodGet, + URL: url, + Header: map[string][]string{ + "Accept-Language": {"en-US"}, + "User-Agent": {UA}, + }, + } + + return client.Do(req) +} diff --git a/go.mod b/go.mod index 6b46600..69e6ca8 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,33 @@ module github.com/rramiachraf/dumb -go 1.18 +go 1.21 + +toolchain go1.22.0 require ( - github.com/PuerkitoBio/goquery v1.8.0 + github.com/Danny-Dasilva/CycleTLS/cycletls v1.0.26 + github.com/Danny-Dasilva/fhttp v0.0.0-20240217042913-eeeb0b347ce1 + github.com/PuerkitoBio/goquery v1.8.1 + github.com/a-h/templ v0.2.598 github.com/allegro/bigcache/v3 v3.0.2 + github.com/caffix/cloudflare-roundtripper v0.0.0-20181218223503-4c29d231c9cb github.com/gorilla/mux v1.8.0 github.com/sirupsen/logrus v1.9.3 ) require ( + github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect - github.com/caffix/cloudflare-roundtripper v0.0.0-20181218223503-4c29d231c9cb // indirect + github.com/cloudflare/circl v1.3.7 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/klauspost/compress v1.17.6 // indirect + github.com/quic-go/quic-go v0.41.0 // indirect + github.com/refraction-networking/utls v1.6.2 // indirect github.com/robertkrimen/otto v0.2.1 // indirect - golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect gopkg.in/sourcemap.v1 v1.0.5 // indirect + h12.io/socks v1.0.3 // indirect ) diff --git a/go.sum b/go.sum index 3eb7ee1..3c264e7 100644 --- a/go.sum +++ b/go.sum @@ -1,43 +1,425 @@ -github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= -github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Danny-Dasilva/CycleTLS/cycletls v1.0.26 h1:6fexoGmvzoXMSk14BZ0AirapVm5c3KUsEjE0jLlVKi8= +github.com/Danny-Dasilva/CycleTLS/cycletls v1.0.26/go.mod h1:QFi/EVO7qqru3Ftxz1LR+96jIc91Tifv0DnskF/gWQ8= +github.com/Danny-Dasilva/fhttp v0.0.0-20240217042913-eeeb0b347ce1 h1:/lqhaiz7xdPr6kuaW1tQ/8DdpWdxkdyd9W/6EHz4oRw= +github.com/Danny-Dasilva/fhttp v0.0.0-20240217042913-eeeb0b347ce1/go.mod h1:Hvab/V/YKCDXsEpKYKHjAXH5IFOmoq9FsfxjztEqvDc= +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= +github.com/a-h/templ v0.2.598 h1:6jMIHv6wQZvdPxTuv87erW4RqN/FPU0wk7ZHN5wVuuo= +github.com/a-h/templ v0.2.598/go.mod h1:SA7mtYwVEajbIXFRh3vKdYm/4FYyLQAtPH1+KxzGPA8= github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9IrprcQfI= github.com/allegro/bigcache/v3 v3.0.2/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/caffix/cloudflare-roundtripper v0.0.0-20181218223503-4c29d231c9cb h1:RKySaWkjoE/ECY1FFk4JbcfG9dTrJmoqp08rQ2oA51Y= github.com/caffix/cloudflare-roundtripper v0.0.0-20181218223503-4c29d231c9cb/go.mod h1:LkIRP8n1KY5Ew4Y7S+V7ooavIXNrraFZ1IKmI4SNMuE= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI= +github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= +github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= +github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= +github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= +github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= +github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.37.4/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU= +github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k= +github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= +github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw= +github.com/refraction-networking/utls v1.6.2 h1:iTeeGY0o6nMNcGyirxkD5bFIsVctP5InGZ3E0HrzS7k= +github.com/refraction-networking/utls v1.6.2/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs= github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0= github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo= +h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/handlers/album.go b/handlers/album.go new file mode 100644 index 0000000..e9aa8bc --- /dev/null +++ b/handlers/album.go @@ -0,0 +1,71 @@ +package handlers + +import ( + "context" + "fmt" + "net/http" + + "github.com/PuerkitoBio/goquery" + "github.com/gorilla/mux" + "github.com/rramiachraf/dumb/data" + "github.com/rramiachraf/dumb/views" + "github.com/sirupsen/logrus" +) + +func Album(l *logrus.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + artist := mux.Vars(r)["artist"] + albumName := mux.Vars(r)["albumName"] + + id := fmt.Sprintf("%s/%s", artist, albumName) + + if a, err := getCache[data.Album](id); err == nil { + views.AlbumPage(a).Render(context.Background(), w) + return + } + + url := fmt.Sprintf("https://genius.com/albums/%s/%s", artist, albumName) + + resp, err := sendRequest(url) + if err != nil { + l.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "cannot reach Genius servers").Render(context.Background(), w) + return + } + + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + w.WriteHeader(http.StatusNotFound) + views.ErrorPage(404, "page not found").Render(context.Background(), w) + return + } + + doc, err := goquery.NewDocumentFromReader(resp.Body) + if err != nil { + l.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "something went wrong").Render(context.Background(), w) + return + } + + cf := doc.Find(".cloudflare_content").Length() + if cf > 0 { + l.Errorln("cloudflare got in the way") + views.ErrorPage(500, "i'll fix this later #21").Render(context.Background(), w) + return + } + + var a data.Album + if err = a.Parse(doc); err != nil { + l.Error(err) + } + + views.AlbumPage(a).Render(context.Background(), w) + + if err = setCache(id, a); err != nil { + l.Errorln(err) + } + } +} diff --git a/handlers/annotations.go b/handlers/annotations.go new file mode 100644 index 0000000..ccec630 --- /dev/null +++ b/handlers/annotations.go @@ -0,0 +1,93 @@ +package handlers + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "regexp" + "strings" + + "github.com/gorilla/mux" + "github.com/rramiachraf/dumb/data" + "github.com/rramiachraf/dumb/views" + "github.com/sirupsen/logrus" +) + +func Annotations(l *logrus.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + if a, err := getCache[data.Annotation]("annotation:" + id); err == nil { + encoder := json.NewEncoder(w) + + w.Header().Set("content-type", "application/json") + if err = encoder.Encode(&a); err != nil { + l.Errorln(err) + } + + return + } + + url := fmt.Sprintf("https://genius.com/api/referents/%s?text_format=html", id) + resp, err := sendRequest(url) + + if err != nil { + l.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "cannot reach genius servers").Render(context.Background(), w) + return + } + + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + w.WriteHeader(http.StatusNotFound) + views.ErrorPage(404, "page not found").Render(context.Background(), w) + return + } + + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(resp.Body) + if err != nil { + l.Errorln("Error paring genius api response", err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "something went wrong").Render(context.Background(), w) + return + } + + var data data.AnnotationsResponse + err = json.Unmarshal(buf.Bytes(), &data) + if err != nil { + l.Errorf("could not unmarshal json: %s\n", err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "something went wrong").Render(context.Background(), w) + return + } + + body := data.Response.Referent.Annotations[0].Body + body.HTML = cleanBody(body.HTML) + + w.Header().Set("content-type", "application/json") + encoder := json.NewEncoder(w) + + if err = encoder.Encode(&body); err != nil { + l.Errorln("Error sending response: ", err) + return + } + + if err = setCache("annotation:"+id, body); err != nil { + l.Errorln(err) + } + } +} + +func cleanBody(body string) string { + var withCleanedImageLinks = strings.ReplaceAll(body, "https://images.rapgenius.com/", "/images/") + withCleanedImageLinks = strings.ReplaceAll(body, "https://images.genius.com/", "/images/") + + var re = regexp.MustCompile(`https?:\/\/[a-z]*.?genius.com`) + var withCleanedLinks = re.ReplaceAllString(withCleanedImageLinks, "") + + return withCleanedLinks +} diff --git a/handlers/cache.go b/handlers/cache.go new file mode 100644 index 0000000..af1d684 --- /dev/null +++ b/handlers/cache.go @@ -0,0 +1,39 @@ +package handlers + +import ( + "encoding/json" + "time" + + "github.com/allegro/bigcache/v3" + "github.com/rramiachraf/dumb/data" +) + +type cachable interface { + data.Album | data.Song | data.Annotation +} + +var c, _ = bigcache.NewBigCache(bigcache.DefaultConfig(time.Hour * 24)) + +func setCache(key string, entry interface{}) error { + data, err := json.Marshal(&entry) + if err != nil { + return err + } + + return c.Set(key, data) +} + +func getCache[v cachable](key string) (v, error) { + var decoded v + + data, err := c.Get(key) + if err != nil { + return decoded, err + } + + if err = json.Unmarshal(data, &decoded); err != nil { + return decoded, err + } + + return decoded, nil +} diff --git a/handlers/lyrics.go b/handlers/lyrics.go new file mode 100644 index 0000000..d0e1c34 --- /dev/null +++ b/handlers/lyrics.go @@ -0,0 +1,64 @@ +package handlers + +import ( + "context" + "fmt" + "net/http" + + "github.com/PuerkitoBio/goquery" + "github.com/gorilla/mux" + "github.com/rramiachraf/dumb/data" + "github.com/rramiachraf/dumb/views" + "github.com/sirupsen/logrus" +) + +func Lyrics(l *logrus.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + + if s, err := getCache[data.Song](id); err == nil { + views.LyricsPage(s).Render(context.Background(), w) + return + } + + url := fmt.Sprintf("https://genius.com/%s-lyrics", id) + resp, err := sendRequest(url) + if err != nil { + l.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "cannot reach Genius servers").Render(context.Background(), w) + return + } + + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + w.WriteHeader(http.StatusNotFound) + views.ErrorPage(404, "page not found").Render(context.Background(), w) + return + } + + doc, err := goquery.NewDocumentFromReader(resp.Body) + if err != nil { + l.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "something went wrong").Render(context.Background(), w) + return + } + + cf := doc.Find(".cloudflare_content").Length() + if cf > 0 { + l.Errorln("cloudflare got in the way") + views.ErrorPage(500, "TODO: fix Cloudflare #21").Render(context.Background(), w) + return + } + + var s data.Song + s.Parse(doc) + + views.LyricsPage(s).Render(context.Background(), w) + if err = setCache(id, s); err != nil { + l.Errorln(err) + } + } +} diff --git a/handlers/proxy.go b/handlers/proxy.go new file mode 100644 index 0000000..3f85baa --- /dev/null +++ b/handlers/proxy.go @@ -0,0 +1,60 @@ +package handlers + +import ( + "context" + "fmt" + "io" + "net/http" + "strings" + + "github.com/gorilla/mux" + "github.com/rramiachraf/dumb/views" + "github.com/sirupsen/logrus" +) + +func isValidExt(ext string) bool { + valid := []string{"jpg", "jpeg", "png", "gif"} + for _, c := range valid { + if strings.ToLower(ext) == c { + return true + } + } + + return false +} + +func ImageProxy(l *logrus.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + v := mux.Vars(r) + f := v["filename"] + ext := v["ext"] + + if !isValidExt(ext) { + w.WriteHeader(http.StatusBadRequest) + views.ErrorPage(400, "something went wrong").Render(context.Background(), w) + return + } + + // first segment of URL resize the image to reduce bandwith usage. + url := fmt.Sprintf("https://t2.genius.com/unsafe/300x300/https://images.genius.com/%s.%s", f, ext) + + res, err := sendRequest(url) + if err != nil { + l.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "cannot reach Genius servers").Render(context.Background(), w) + return + } + + if res.StatusCode != http.StatusOK { + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "something went wrong").Render(context.Background(), w) + return + } + + w.Header().Add("Content-type", fmt.Sprintf("image/%s", ext)) + if _, err = io.Copy(w, res.Body); err != nil { + l.Errorln("unable to write image", err) + } + } +} diff --git a/handlers/search.go b/handlers/search.go new file mode 100644 index 0000000..b2a83cd --- /dev/null +++ b/handlers/search.go @@ -0,0 +1,44 @@ +package handlers + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/rramiachraf/dumb/data" + "github.com/rramiachraf/dumb/views" + "github.com/sirupsen/logrus" +) + +func Search(l *logrus.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query().Get("q") + url := fmt.Sprintf(`https://genius.com/api/search/multi?q=%s`, url.QueryEscape(query)) + + res, err := sendRequest(url) + if err != nil { + l.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "cannot reach Genius servers").Render(context.Background(), w) + return + } + + defer res.Body.Close() + + var sRes data.SearchResponse + + d := json.NewDecoder(res.Body) + if err = d.Decode(&sRes); err != nil { + l.Errorln(err) + w.WriteHeader(http.StatusInternalServerError) + views.ErrorPage(500, "something went wrong").Render(context.Background(), w) + } + + results := data.SearchResults{Query: query, Sections: sRes.Response.Sections} + + views.SearchPage(results).Render(context.Background(), w) + } + +} diff --git a/handlers/utils.go b/handlers/utils.go new file mode 100644 index 0000000..5873190 --- /dev/null +++ b/handlers/utils.go @@ -0,0 +1,42 @@ +package handlers + +import ( + "net/http" + "net/url" + "time" + + "github.com/Danny-Dasilva/CycleTLS/cycletls" + fhttp "github.com/Danny-Dasilva/fhttp" +) + +func MustHeaders(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + csp := "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; object-src 'none'" + w.Header().Add("content-security-policy", csp) + w.Header().Add("referrer-policy", "no-referrer") + w.Header().Add("x-content-type-options", "nosniff") + next.ServeHTTP(w, r) + }) +} + +const UA = "Mozilla/5.0 (Windows NT 10.0; rv:123.0) Gecko/20100101 Firefox/123.0" +const JA3 = "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-23-65281-10-11-16-5-34-51-43-13-45-28-65037-41,29-23-24-25-256-257,0" + +func sendRequest(u string) (*fhttp.Response, error) { + url, err := url.Parse(u) + if err != nil { + return nil, err + } + + client := &fhttp.Client{ + Transport: cycletls.NewTransport(JA3, UA), + Timeout: 20 * time.Second, + } + + req := &fhttp.Request{ + Method: http.MethodGet, + URL: url, + } + + return client.Do(req) +} diff --git a/lyrics.go b/lyrics.go deleted file mode 100644 index 0fdcb8c..0000000 --- a/lyrics.go +++ /dev/null @@ -1,172 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "net/http" - "strings" - - "github.com/PuerkitoBio/goquery" - "github.com/gorilla/mux" -) - -type song struct { - Artist string - Title string - Image string - Lyrics string - Credits map[string]string - About [2]string - Album string - LinkToAlbum string -} - -type songResponse struct { - Response struct { - Song struct { - ArtistNames string `json:"artist_names"` - Image string `json:"song_art_image_thumbnail_url"` - Title string - Description struct { - Plain string - } - Album struct { - Url string `json:"url"` - Name string `json:"name"` - } - CustomPerformances []customPerformance `json:"custom_performances"` - } - } -} - -type customPerformance struct { - Label string - Artists []struct { - Name string - } -} - -func (s *song) parseLyrics(doc *goquery.Document) { - doc.Find("[data-lyrics-container='true']").Each(func(i int, ss *goquery.Selection) { - h, err := ss.Html() - if err != nil { - logger.Errorln("unable to parse lyrics", err) - } - s.Lyrics += h - }) -} - -func (s *song) parseSongData(doc *goquery.Document) { - attr, exists := doc.Find("meta[property='twitter:app:url:iphone']").Attr("content") - if exists { - songID := strings.Replace(attr, "genius://songs/", "", 1) - - u := fmt.Sprintf("https://genius.com/api/songs/%s?text_format=plain", songID) - - res, err := sendRequest(u) - if err != nil { - logger.Errorln(err) - } - - defer res.Body.Close() - - var data songResponse - decoder := json.NewDecoder(res.Body) - err = decoder.Decode(&data) - if err != nil { - logger.Errorln(err) - } - - songData := data.Response.Song - s.Artist = songData.ArtistNames - s.Image = songData.Image - s.Title = songData.Title - s.About[0] = songData.Description.Plain - s.About[1] = truncateText(songData.Description.Plain) - s.Credits = make(map[string]string) - s.Album = songData.Album.Name - s.LinkToAlbum = strings.Replace(songData.Album.Url, "https://genius.com", "", -1) - - for _, perf := range songData.CustomPerformances { - var artists []string - for _, artist := range perf.Artists { - artists = append(artists, artist.Name) - } - s.Credits[perf.Label] = strings.Join(artists, ", ") - } - } -} - -func truncateText(text string) string { - textArr := strings.Split(text, "") - - if len(textArr) > 250 { - return strings.Join(textArr[0:250], "") + "..." - } - - return text -} - -func (s *song) parse(doc *goquery.Document) { - s.parseLyrics(doc) - s.parseSongData(doc) -} - -func lyricsHandler(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - - if data, err := getCache(id); err == nil { - render("lyrics", w, data) - return - } - - url := fmt.Sprintf("https://genius.com/%s-lyrics", id) - resp, err := sendRequest(url) - if err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "cannot reach genius servers", - }) - return - } - - defer resp.Body.Close() - - if resp.StatusCode == http.StatusNotFound { - w.WriteHeader(http.StatusNotFound) - render("error", w, map[string]string{ - "Status": "404", - "Error": "page not found", - }) - return - } - - doc, err := goquery.NewDocumentFromReader(resp.Body) - if err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "something went wrong", - }) - return - } - - cf := doc.Find(".cloudflare_content").Length() - if cf > 0 { - logger.Errorln("cloudflare got in the way") - render("error", w, map[string]string{ - "Status": "500", - "Error": "damn cloudflare, issue #21 on GitHub", - }) - return - } - - var s song - s.parse(doc) - - render("lyrics", w, s) - setCache(id, s) -} diff --git a/main.go b/main.go index 69799f9..39c9c21 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "net" "net/http" @@ -8,38 +9,30 @@ import ( "strconv" "time" - "github.com/allegro/bigcache/v3" + "github.com/a-h/templ" "github.com/gorilla/mux" + "github.com/rramiachraf/dumb/handlers" + "github.com/rramiachraf/dumb/views" "github.com/sirupsen/logrus" ) var logger = logrus.New() func main() { - c, err := bigcache.NewBigCache(bigcache.DefaultConfig(time.Hour * 24)) - if err != nil { - logger.Fatalln("can't initialize caching") - } - cache = c - r := mux.NewRouter() - r.Use(securityHeaders) + r.Use(handlers.MustHeaders) - r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { render("home", w, nil) }) - r.HandleFunc("/search", searchHandler).Methods("GET") - r.HandleFunc("/{id}-lyrics", lyricsHandler) - r.HandleFunc("/{id}/{artist-song}/{verse}/annotations", annotationsHandler) - r.HandleFunc("/images/{filename}.{ext}", proxyHandler) + r.Handle("/", templ.Handler(views.HomePage())) + r.HandleFunc("/{id}-lyrics", handlers.Lyrics(logger)).Methods("GET") + r.HandleFunc("/albums/{artist}/{albumName}", handlers.Album(logger)).Methods("GET") + r.HandleFunc("/images/{filename}.{ext}", handlers.ImageProxy(logger)).Methods("GET") + r.HandleFunc("/search", handlers.Search(logger)).Methods("GET") + r.HandleFunc("/{id}/{artist-song}/{verse}/annotations", handlers.Annotations(logger)).Methods("GET") r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) - r.HandleFunc("/albums/{artist}/{albumName}", albumHandler) r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) - render("error", w, map[string]string{ - "Status": "404", - "Error": "page not found", - }) - + views.ErrorPage(404, "page not found").Render(context.Background(), w) }) server := &http.Server{ diff --git a/proxy.go b/proxy.go deleted file mode 100644 index 006c932..0000000 --- a/proxy.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "fmt" - "io" - "net/http" - "net/url" - "strings" - - "github.com/gorilla/mux" -) - -func isValidExt(ext string) bool { - valid := []string{"jpg", "jpeg", "png", "gif"} - for _, c := range valid { - if strings.ToLower(ext) == c { - return true - } - } - - return false -} - -func extractURL(image string) string { - u, err := url.Parse(image) - if err != nil { - return "" - } - - return fmt.Sprintf("/images%s", u.Path) -} - -func proxyHandler(w http.ResponseWriter, r *http.Request) { - v := mux.Vars(r) - f := v["filename"] - ext := v["ext"] - - if !isValidExt(ext) { - w.WriteHeader(http.StatusBadRequest) - render("error", w, map[string]string{ - "Status": "400", - "Error": "Something went wrong", - }) - return - } - - // first segment of URL resize the image to reduce bandwith usage. - url := fmt.Sprintf("https://t2.genius.com/unsafe/300x300/https://images.genius.com/%s.%s", f, ext) - - res, err := sendRequest(url) - if err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "cannot reach genius servers", - }) - return - } - - if res.StatusCode != http.StatusOK { - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "something went wrong", - }) - - return - } - - w.Header().Add("Content-type", fmt.Sprintf("image/%s", ext)) - io.Copy(w, res.Body) -} diff --git a/screenshot.png b/screenshot.png index 675667e..168c657 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/scripts/dumb.service b/scripts/dumb.service deleted file mode 100644 index 09d242f..0000000 --- a/scripts/dumb.service +++ /dev/null @@ -1,21 +0,0 @@ -[Unit] -Description=ListMonk -Documentation=https://github.com/rramiachraf/dumb -After=system.slice multi-user.target postgresql.service network.target - -[Service] -User=git -Type=simple - -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=listmonk - -WorkingDirectory=/etc/dumb -ExecStart=/etc/dumb/dumb - -Restart=always -RestartSec=5 - -[Install] -WantedBy=multi-user.target diff --git a/scripts/nginx-config b/scripts/nginx-config deleted file mode 100644 index 7cafb9d..0000000 --- a/scripts/nginx-config +++ /dev/null @@ -1,35 +0,0 @@ -server { - # root /var/www/dumb.yoursite.com/html; - # index index.html index.htm index.nginx-debian.html; - - server_name dumb.yoursite.com; - # www.dumb.yoursite.com; - - location / { - try_files $uri $uri/ =404; - proxy_pass http://localhost:5555; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - listen 443 ssl; # managed by Certbot - ssl_certificate /etc/letsencrypt/live/dumb.yoursite.com/fullchain.pem; # managed by Certbot - ssl_certificate_key /etc/letsencrypt/live/dumb.yoursite.com/privkey.pem; # managed by Certbot - include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot - -} -server { - if ($host = dumb.yoursite.com) { - return 301 https://$host$request_uri; - } # managed by Certbot - - server_name dumb.yoursite.com; - - listen 80; - return 404; # managed by Certbot - -} - diff --git a/search.go b/search.go deleted file mode 100644 index 1392e8d..0000000 --- a/search.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "net/http" - "net/url" -) - -type response struct { - Response struct { - Sections sections - } -} - -type result struct { - ArtistNames string `json:"artist_names"` - Title string - Path string - Thumbnail string `json:"song_art_image_thumbnail_url"` -} - -type hits []struct { - Result result -} - -type sections []struct { - Type string - Hits hits -} - -type renderVars struct { - Query string - Sections sections -} - -func searchHandler(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query().Get("q") - url := fmt.Sprintf(`https://genius.com/api/search/multi?q=%s`, url.QueryEscape(query)) - - res, err := sendRequest(url) - if err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - render("error", w, map[string]string{ - "Status": "500", - "Error": "cannot reach genius servers", - }) - } - - defer res.Body.Close() - - var data response - - d := json.NewDecoder(res.Body) - d.Decode(&data) - - vars := renderVars{query, data.Response.Sections} - - render("search", w, vars) -} diff --git a/static/script.js b/static/script.js index 327ae5a..cecad3b 100644 --- a/static/script.js +++ b/static/script.js @@ -14,6 +14,7 @@ document.querySelectorAll("#lyrics a").forEach(item => { function getAnnotation(e) { e.preventDefault() + //document.querySelector('.annotation')?.remove() const uri = e.target.parentElement.getAttribute("href") const presentAnnotation = document.getElementById(uri) if (presentAnnotation) { diff --git a/static/style.css b/static/style.css index 530484d..2486873 100644 --- a/static/style.css +++ b/static/style.css @@ -53,7 +53,6 @@ body { #lyrics a { color: inherit; - cursor: initial; } #lyrics a, .annotation { @@ -84,7 +83,7 @@ a { #metadata { display: flex; flex-direction: column; - gap: 0.5rem; + gap: 2rem; flex-basis: 0; } @@ -99,17 +98,26 @@ a { font-weight: 500; } +#metadata-info { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + #album-artwork { - width: 20rem; + width: 24rem; border-radius: 3px; box-shadow: 0 1px 1px #ddd; - max-width: 200px; } #container { - display: flex; - padding: 5rem 10rem; - gap: 5rem; + display: grid; + padding: 5rem 0; + grid-template-columns: 24rem 1fr 24rem; + width: 1024px; + margin: 0 auto; + gap: 4rem; + flex: 1; } #credits { @@ -129,6 +137,12 @@ a { color: #1e1e1e; } +#lyrics-album-container { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + #album-tracklist{ display: flex; flex-direction: column; @@ -214,8 +228,10 @@ a { @media screen and (max-width: 900px) { #container { padding: 3rem 2rem; + display: flex; flex-direction: column; gap: 3rem; + width: calc(100vw - 4rem);; } #metadata { diff --git a/utils.go b/utils.go deleted file mode 100644 index 94a686a..0000000 --- a/utils.go +++ /dev/null @@ -1,119 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "net" - "net/http" - "net/url" - "path" - "text/template" - "time" - - "github.com/allegro/bigcache/v3" - "github.com/caffix/cloudflare-roundtripper/cfrt" -) - -var cache *bigcache.BigCache - -func setCache(key string, entry interface{}) error { - data, err := json.Marshal(&entry) - if err != nil { - return err - } - - return cache.Set(key, data) -} - -func getCache(key string) (interface{}, error) { - data, err := cache.Get(key) - if err != nil { - return nil, err - } - - var decoded interface{} - - if err = json.Unmarshal(data, &decoded); err != nil { - return nil, err - } - - return decoded, nil -} - -func write(w http.ResponseWriter, status int, data []byte) { - w.WriteHeader(status) - w.Write(data) -} - -func securityHeaders(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - csp := "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; object-src 'none'" - w.Header().Add("content-security-policy", csp) - w.Header().Add("referrer-policy", "no-referrer") - w.Header().Add("x-content-type-options", "nosniff") - next.ServeHTTP(w, r) - }) -} - -func getTemplates(templates ...string) []string { - var pths []string - for _, t := range templates { - tmpl := path.Join("views", fmt.Sprintf("%s.tmpl", t)) - pths = append(pths, tmpl) - } - return pths -} - -func render(n string, w http.ResponseWriter, data interface{}) { - w.Header().Set("content-type", "text/html") - t := template.New(n + ".tmpl").Funcs(template.FuncMap{"extractURL": extractURL}) - t, err := t.ParseFiles(getTemplates(n, "navbar", "footer")...) - if err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if err = t.Execute(w, data); err != nil { - logger.Errorln(err) - w.WriteHeader(http.StatusInternalServerError) - return - } -} - -const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" - -var client = &http.Client{ - Timeout: 20 * time.Second, - Transport: &http.Transport{ - DialContext: (&net.Dialer{ - Timeout: 15 * time.Second, - KeepAlive: 15 * time.Second, - DualStack: true, - }).DialContext, - }, -} - -func sendRequest(u string) (*http.Response, error) { - url, err := url.Parse(u) - if err != nil { - return nil, err - } - - client.Transport, err = cfrt.New(client.Transport) - if err != nil { - return nil, err - } - - - req := &http.Request{ - Method: http.MethodGet, - URL: url, - Header: map[string][]string{ - "Accept-Language": {"en-US"}, - "User-Agent": {UA}, - }, - } - - return client.Do(req) -} diff --git a/views/album.templ b/views/album.templ new file mode 100644 index 0000000..5e61a04 --- /dev/null +++ b/views/album.templ @@ -0,0 +1,32 @@ +package views + +import ( + "fmt" + "github.com/rramiachraf/dumb/data" +) + +templ AlbumPage(a data.Album) { + @layout(fmt.Sprintf("%s - %s", a.Artist, a.Name)) { +
+
+ +

{ a.Artist }

+

{ a.Name }

+
+
+ for _, t := range a.Tracks { + +

{ t.Title }

+
+ } +
+
+
+

About

+ +

{ a.About[1] }

+
+
+
+ } +} diff --git a/views/album.tmpl b/views/album.tmpl deleted file mode 100644 index db35d77..0000000 --- a/views/album.tmpl +++ /dev/null @@ -1,35 +0,0 @@ - - - - {{.Artist}} - {{.Name}} - - - - - - - {{template "navbar"}} -
-
- -

{{.Artist}}

-

{{.Name}}

-
-
- {{range .Tracks}} - -

{{.Title}}

-
- {{end}} -
-
-
-

About

- -

{{index .About 1}}

-
-
-
- {{template "footer"}} - - diff --git a/views/error.templ b/views/error.templ new file mode 100644 index 0000000..7bfe3ec --- /dev/null +++ b/views/error.templ @@ -0,0 +1,12 @@ +package views + +import "strconv" + +templ ErrorPage(code int, display string) { + @layout("Error - dumb") { +
+

{ strconv.Itoa(code) }

+

{ display }

+
+ } +} diff --git a/views/error.tmpl b/views/error.tmpl deleted file mode 100644 index 39e0729..0000000 --- a/views/error.tmpl +++ /dev/null @@ -1,20 +0,0 @@ - - - - dumb - - - - - - -
- {{template "navbar"}} -
-

{{.Status}}

-

{{.Error}}

-
- {{template "footer"}} - - - diff --git a/views/footer.templ b/views/footer.templ new file mode 100644 index 0000000..1701078 --- /dev/null +++ b/views/footer.templ @@ -0,0 +1,7 @@ +package views + +templ footer() { + +} diff --git a/views/footer.tmpl b/views/footer.tmpl deleted file mode 100644 index 394117c..0000000 --- a/views/footer.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{define "footer"}} - -{{end}} diff --git a/views/head.templ b/views/head.templ new file mode 100644 index 0000000..f82ffd1 --- /dev/null +++ b/views/head.templ @@ -0,0 +1,12 @@ +package views + +templ head(title string) { + + { title } + + + + + + +} diff --git a/views/home.templ b/views/home.templ new file mode 100644 index 0000000..3890c20 --- /dev/null +++ b/views/home.templ @@ -0,0 +1,15 @@ +package views + +templ HomePage() { + @layout("dumb") { +
+
+

Welcome to dumb

+

An alternative frontend for genius.com

+
+
+ +
+
+ } +} diff --git a/views/home.tmpl b/views/home.tmpl deleted file mode 100644 index 0b3397e..0000000 --- a/views/home.tmpl +++ /dev/null @@ -1,26 +0,0 @@ - - - - dumb - - - - - - -
- {{template "navbar"}} -
-
-

Welcome to dumb

-

An alternative frontend for genius.com

-
-
- -
- -
- {{template "footer"}} - - - diff --git a/views/layout.templ b/views/layout.templ new file mode 100644 index 0000000..b02da33 --- /dev/null +++ b/views/layout.templ @@ -0,0 +1,15 @@ +package views + +templ layout(title string) { + + + @head(title) + +
+ @navbar() + { children... } + @footer() +
+ + +} diff --git a/views/lyrics.templ b/views/lyrics.templ new file mode 100644 index 0000000..d03256b --- /dev/null +++ b/views/lyrics.templ @@ -0,0 +1,51 @@ +package views + +import ( + "fmt" + "github.com/rramiachraf/dumb/data" +) + +templ LyricsPage(s data.Song) { + @layout(fmt.Sprintf("%s - %s lyrics", s.Artist, s.Title)) { +
+
+ +
+

{ s.Artist }

+

{ s.Title }

+
+
+
+ @templ.Raw(s.Lyrics) +
+
+ if s.About[0] != "" { +
+

About

+ +

{ s.About[1] }

+
+ } + if len(s.Credits) > 0 { +
+

Credits

+ for key, val := range s.Credits { +
+ { key } +

{ val }

+
+ } +
+ } + if s.Album.Name != "" { +
+

{ s.Album.Name }

+ + + +
+ } +
+
+ } +} diff --git a/views/lyrics.tmpl b/views/lyrics.tmpl deleted file mode 100644 index 5b9505d..0000000 --- a/views/lyrics.tmpl +++ /dev/null @@ -1,40 +0,0 @@ - - - - {{.Artist}} - {{.Title}} lyrics - - - - - - - - {{template "navbar"}} -
-
- -

{{.Artist}}

-

{{.Title}}

-

{{.Album}}

-
-
{{.Lyrics}}
-
-
-

About

- -

{{index .About 1}}

-
-
-

Credits

- {{range $key, $val := .Credits}} -
- {{$key}} -

{{$val}}

-
- {{end}} -
-
-
- {{template "footer"}} - - diff --git a/views/navbar.templ b/views/navbar.templ new file mode 100644 index 0000000..66c7255 --- /dev/null +++ b/views/navbar.templ @@ -0,0 +1,7 @@ +package views + +templ navbar() { + +} diff --git a/views/navbar.tmpl b/views/navbar.tmpl deleted file mode 100644 index bf117cd..0000000 --- a/views/navbar.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{define "navbar"}} - -{{end}} diff --git a/views/search.templ b/views/search.templ new file mode 100644 index 0000000..9a48b4e --- /dev/null +++ b/views/search.templ @@ -0,0 +1,29 @@ +package views + +import "github.com/rramiachraf/dumb/data" + +templ SearchPage(r data.SearchResults) { + @layout("Search - dumb") { +
+
+ +
+
+ for _, s := range r.Sections { + if s.Type == "song" { +

Songs

+ for _, s := range s.Hits { + + +
+ { s.Result.ArtistNames } +

{ s.Result.Title }

+
+
+ } + } + } +
+
+ } +} diff --git a/views/search.tmpl b/views/search.tmpl deleted file mode 100644 index eecea1b..0000000 --- a/views/search.tmpl +++ /dev/null @@ -1,37 +0,0 @@ - - - - Search - dumb - - - - - - -
- {{template "navbar"}} -
-
- -
-
- {{range .Sections}} - {{if eq .Type "song"}} -

Songs

- {{range .Hits}} - - -
- {{.Result.ArtistNames}} -

{{.Result.Title}}

-
-
- {{end}} - {{end}} - {{end}} -
-
- {{template "footer"}} - - -