@@ -1,10 +1,11 @@ // gido is the command that powers the https://goissues.org website. package main import ( "context" "encoding/json" "flag" "fmt" "io" "io/ioutil" "log" @@ -116,10 +117,15 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) error { if req.URL.Path == "/assets" || strings.HasPrefix(req.URL.Path, "/assets/") { h.assetsHandler.ServeHTTP(w, req) return nil } // Handle "/-/packages". if req.URL.Path == "/-/packages" { return h.ServePackages(w, req) } // Handle "/..." URLs. if canonicalPath := path.Clean(req.URL.Path); req.URL.Path != canonicalPath { // Redirect to canonical path (no trailing slash, etc.). if req.URL.RawQuery != "" { canonicalPath += "?" + req.URL.RawQuery @@ -139,11 +145,12 @@ const htmlPart1, htmlPart2, htmlPart3 = `<html> <link href="/assets/style.css" rel="stylesheet" type="text/css"> </head> <body style="margin: 0; position: relative;"> <header style="background-color: hsl(209, 51%, 92%);"> <div style="max-width: 800px; margin: 0 auto 0 auto; padding: 0 15px 0 15px;"> <a class="black" href="/"><strong style="padding: 15px 0 15px 0; display: inline-block;">Go Issues</strong></a> <a class="black" href="/" ><strong style="padding: 15px 0 15px 0; display: inline-block;">Go Issues</strong></a> <a class="black" href="/-/packages" style="padding-left: 30px;"><span style="padding: 15px 0 15px 0; display: inline-block;">Packages</span></a> </div> </header> <main style="max-width: 800px; margin: 0 auto 0 auto; padding: 0 15px 120px 15px;">`, `</main> @@ -154,10 +161,11 @@ const htmlPart1, htmlPart2, htmlPart3 = `<html> </footer> </body> </html> ` // ServeIndex serves the index page. func (h *handler) ServeIndex(w http.ResponseWriter, req *http.Request) error { if req.Method != http.MethodGet { return httperror.Method{Allowed: []string{http.MethodGet}} } @@ -185,15 +193,15 @@ func (h *handler) ServeIndex(w http.ResponseWriter, req *http.Request) error { using your browser's address bar (<kbd>⌘Cmd</kbd>+<kbd>L</kbd> or <kbd>Ctrl</kbd>+<kbd>L</kbd>).</p> <p>Supported import paths include:</p> <ul> <li><a href="https://golang.org/pkg/#stdlib">Standard library</a> (e.g., <code>io</code>, <code>net/http</code>, etc.),</li> <li><a href="https://golang.org/pkg/#subrepo">Sub-repositories</a> (i.e., <code>golang.org/x/...</code>).</li> <li><a href="/-/packages#stdlib">Standard library</a> (e.g., <code>io</code>, <code>net/http</code>, etc.),</li> <li><a href="/-/packages#subrepo">Sub-repositories</a> (i.e., <code>golang.org/x/...</code>).</li> </ul> <p>Import paths of 3rd party packages (e.g., <code>github.com/...</code>) are not supported.</p> <p>Import paths of 3rd party packages (e.g., <code>github.com/...</code>) are not supported at this time.</p> <p>It's a simple website with a narrow scope. Enjoy. ʕ◔ϖ◔ʔ</p> `) if err != nil { return err @@ -226,13 +234,97 @@ func (h *handler) ServeIndex(w http.ResponseWriter, req *http.Request) error { _, err = io.WriteString(w, htmlPart3) return err } // ServePackages serves a list of all known packages. func (h *handler) ServePackages(w http.ResponseWriter, req *http.Request) error { if req.Method != http.MethodGet { return httperror.Method{Allowed: []string{http.MethodGet}} } // Gather all packages in sorted order. h.s.PackageIssuesMu.RLock() pis := h.s.PackageIssues h.s.PackageIssuesMu.RUnlock() var stdlib, subrepo []pkg for _, p := range h.s.Packages { switch isStandard(p) { case true: stdlib = append(stdlib, pkg{ Path: p, Open: len(pis[p].Open), }) case false: subrepo = append(subrepo, pkg{ Path: p, Open: len(pis[p].Open), }) } } if req.Header.Get("Accept") == "application/json" { w.Header().Set("Content-Type", "application/json") e := json.NewEncoder(w) e.SetIndent("", "\t") err := e.Encode(append(stdlib, subrepo...)) // TODO: Measure if slow, optimize if needed. return err } w.Header().Set("Content-Type", "text/html; charset=utf-8") _, err := io.WriteString(w, htmlPart1) if err != nil { return err } _, err = w.Write(h.analyticsHTML) if err != nil { return err } _, err = io.WriteString(w, htmlPart2) if err != nil { return err } // Render the stdlib section. _, err = io.WriteString(w, `<h3 id="stdlib" style="margin-top: 30px;">Standard Library Packages</h3>`) if err != nil { return err } err = renderTable(w, stdlib) if err != nil { return err } // Render the subrepo section. _, err = io.WriteString(w, `<h3 id="subrepo" style="margin-top: 30px;">Sub-repository Packages</h3>`) if err != nil { return err } err = renderTable(w, subrepo) if err != nil { return err } // Write note about other issues. _, err = io.WriteString(w, `<h3 id="other" style="margin-top: 30px;">Other Issues</h3> <p> Issues from the <a href="https://golang.org/issues">Go issue tracker</a> that don't fit into any existing Go package can be seen under <a href="/other">other</a>. </p>`) if err != nil { return err } _, err = io.WriteString(w, htmlPart3) return err } type pkg struct { Path string Open int Path string `json:"ImportPath"` Open int `json:"OpenIssues"` } func renderTable(w io.Writer, pkgs []pkg) error { _, err := io.WriteString(w, `<table class="table table-sm"> <thead> @@ -300,14 +392,16 @@ func (h *handler) ServeIssues(w http.ResponseWriter, req *http.Request, pkg stri if err != nil { return err } heading := htmlg.NodeComponent{ Type: html.ElementNode, Data: atom.H2.String(), Attr: []html.Attribute{{Key: atom.Style.String(), Val: "margin-top: 30px;"}}, FirstChild: htmlg.Text(pkg), } title := pkg + ": " if pkg == otherPackages { heading.Data, heading.FirstChild = atom.H3.String(), htmlg.Text("Other Go Issues") title = "" } newIssue := htmlg.NodeComponent{ Type: html.ElementNode, Data: atom.Div.String(), Attr: []html.Attribute{{Key: atom.Style.String(), Val: "text-align: right;"}},