aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar alecdwm 2015-10-21 21:39:26 +1000
committerGravatar alecdwm 2015-10-21 21:39:26 +1000
commit56b69f60815aa18a19c2471e5386dda290662673 (patch)
treed59efdcf8ca024024619ba6a8640f21c6d4dac5d
parent461fcf886649c18b23cc3915c1c3de2072883b9e (diff)
Multiple Improvements
* Improved logging output * Moved GitHub functions into separate file * Added GitHubUserRepos function * Added minimal GitHubUserRepo type * Moved Todos to top of gogetme.go * Moved HTML & CSS data to separate file * Added functions to return HTML & CSS data from variables * Added UserProfile for root path '/' * Added Redirect to UserProfile for invalid project URLs
-rw-r--r--lib/errors.go5
-rw-r--r--lib/github.go92
-rw-r--r--lib/gogetme.go223
-rw-r--r--lib/pages.go266
4 files changed, 405 insertions, 181 deletions
diff --git a/lib/errors.go b/lib/errors.go
index 90969e2..6d7dd0e 100644
--- a/lib/errors.go
+++ b/lib/errors.go
@@ -20,6 +20,9 @@ import log "github.com/Sirupsen/logrus"
func handleError(str string, err error) {
if err != nil {
- log.Fatal(str, " | ", err)
+ log.WithFields(log.Fields{
+ "error": err,
+ "during": str,
+ }).Fatal("An error occured")
}
}
diff --git a/lib/github.go b/lib/github.go
new file mode 100644
index 0000000..5ca924e
--- /dev/null
+++ b/lib/github.go
@@ -0,0 +1,92 @@
+// GoGetMe - go get your GitHub-hosted Go packages via your own domain
+// Copyright (C) 2015 alecdwm
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package lib
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+)
+
+type GitHubUserData struct {
+ Login string `json:"login"`
+ Id int `json:"id"`
+ AvatarUrl string `json:"avatar_url"`
+ GravatarId string `json:"gravatar_id"`
+ Url string `json:"url"`
+ HtmlUrl string `json:"html_url"`
+ FollowersUrl string `json:"followers_url"`
+ FollowingUrl string `json:"following_url"`
+ GistsUrl string `json:"gists_url"`
+ StarredUrl string `json:"starred_url"`
+ SubscriptionsUrl string `json:"subscriptions_url"`
+ OrganizationsUrl string `json:"organizations_url"`
+ ReposUrl string `json:"repos_url"`
+ EventsUrl string `json:"events_url"`
+ ReceivedEventsUrl string `json:"received_events_url"`
+ Type string `json:"type"`
+ SiteAdmin bool `json:"site_admin"`
+ Name string `json:"name"`
+ Company string `json:"company"`
+ Blog string `json:"blog"`
+ Location string `json:"location"`
+ Email string `json:"email"`
+ Hireable bool `json:"hireable"`
+ Bio bool `json:"bio"`
+ PublicRepos int `json:"public_repos"`
+ PublicGists int `json:"public_gists"`
+ Followers int `json:"followers"`
+ Following int `json:"following"`
+ CreatedAt string `json:"created_at"`
+ UpdatedAt string `json:"updated_at"`
+}
+
+func getGitHubUserData(username string) GitHubUserData {
+ // Fetch
+ response, err := http.Get(fmt.Sprintf(
+ "https://api.github.com/users/%s",
+ username))
+ handleError("Fetching GitHub user data", err)
+
+ // Decode
+ var githubUserData GitHubUserData
+ err = json.NewDecoder(response.Body).Decode(&githubUserData)
+ handleError("Parsing response as JSON", err)
+
+ // Return
+ return githubUserData
+}
+
+type GitHubUserRepo struct {
+ CloneUrl string `json:"clone_url"`
+}
+
+func getGitHubUserRepos(username string) []GitHubUserRepo {
+ // Fetch
+ response, err := http.Get(fmt.Sprintf(
+ "https://api.github.com/users/%s/repos",
+ username))
+ handleError("Fetching GitHub user repos", err)
+
+ // Decode
+ var githubUserRepos []GitHubUserRepo
+ err = json.NewDecoder(response.Body).Decode(&githubUserRepos)
+ handleError("Parsing response as JSON", err)
+
+ // Return
+ return githubUserRepos
+}
diff --git a/lib/gogetme.go b/lib/gogetme.go
index 7fe379d..5d60c7f 100644
--- a/lib/gogetme.go
+++ b/lib/gogetme.go
@@ -16,8 +16,16 @@
package lib
+// Todo: detect whether project exists on GitHub or not,
+// and forward through to GitHub user page if not
+// Todo: offer link to package documentation at godoc.org
+// Todo: versioning URLs e.g. go.owls.io/gogetme/v1
+// go.owls.io/gogetme.v1
+// constructed from git branches and tags
+// Todo: logrus
+// Todo: More customisation
+
import (
- "encoding/json"
"fmt"
"net/http"
"time"
@@ -25,192 +33,14 @@ import (
log "github.com/Sirupsen/logrus"
)
-type GitHubUserData struct {
- Login string `json:"login"`
- Id int `json:"id"`
- AvatarUrl string `json:"avatar_url"`
- GravatarId string `json:"gravatar_id"`
- Url string `json:"url"`
- HtmlUrl string `json:"html_url"`
- FollowersUrl string `json:"followers_url"`
- FollowingUrl string `json:"following_url"`
- GistsUrl string `json:"gists_url"`
- StarredUrl string `json:"starred_url"`
- SubscriptionsUrl string `json:"subscriptions_url"`
- OrganizationsUrl string `json:"organizations_url"`
- ReposUrl string `json:"repos_url"`
- EventsUrl string `json:"events_url"`
- ReceivedEventsUrl string `json:"received_events_url"`
- Type string `json:"type"`
- SiteAdmin bool `json:"site_admin"`
- Name string `json:"name"`
- Company string `json:"company"`
- Blog string `json:"blog"`
- Location string `json:"location"`
- Email string `json:"email"`
- Hireable bool `json:"hireable"`
- Bio bool `json:"bio"`
- PublicRepos int `json:"public_repos"`
- PublicGists int `json:"public_gists"`
- Followers int `json:"followers"`
- Following int `json:"following"`
- CreatedAt string `json:"created_at"`
- UpdatedAt string `json:"updated_at"`
-}
-
-func getGitHubUserData(username string) GitHubUserData {
- // Fetch
- response, err := http.Get(fmt.Sprintf(
- "https://api.github.com/users/%s",
- username))
- handleError("Fetching GitHub user data", err)
-
- // Decode
- var githubUserData GitHubUserData
- err = json.NewDecoder(response.Body).Decode(&githubUserData)
- handleError("Parsing response as JSON", err)
-
- // Return
- return githubUserData
-}
-
-func handler(w http.ResponseWriter, r *http.Request,
- customUrl, githubUser string, githubUserData GitHubUserData) {
- t := time.Now()
- log.WithFields(log.Fields{
- "at": t.Format(time.RFC850),
- "from": r.RemoteAddr,
- }).Info("Handled Connection")
-
- // Todo: detect whether project exists on GitHub or not,
- // and forward through to GitHub user page if not
- // Todo: offer link to package documentation at godoc.org
- // Todo: versioning URLs e.g. go.owls.io/gogetme/v1
- // go.owls.io/gogetme.v1
- // constructed from git branches and tags
- pageCss := `
-.card {
- padding-top: 20px;
- margin: 10px 0 20px 0;
- background-color: rgba(214, 224, 226, 0.2);
- border-top-width: 0;
- border-bottom-width: 2px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-.card.hovercard {
- position: relative;
- padding-top: 0;
- overflow: hidden;
- text-align: center;
- background-color: rgba(214, 224, 226, 0.2);
-}
-.card.hovercard .cardheader {
- background: url("http://blog.golang.org/gopher/header.jpg");
- background-size: cover;
- height: 135px;
-}
-.card.hovercard .avatar {
- position: relative;
- top: -50px;
- margin-bottom: -50px;
-}
-.card.hovercard .avatar img {
- width: 100px;
- height: 100px;
- max-width: 100px;
- max-height: 100px;
- -webkit-border-radius: 50%;
- -moz-border-radius: 50%;
- border-radius: 50%;
- border: 5px solid rgba(255,255,255,0.5);
-}
-.card.hovercard .info {
- padding: 4px 8px 10px;
-}
-.card.hovercard .info .title {
- margin-bottom: 4px;
- font-size: 24px;
- line-height: 1;
- color: #262626;
- vertical-align: middle;
-}
-.card.hovercard .info .desc {
- overflow: hidden;
- font-size: 12px;
- line-height: 20px;
- color: #737373;
- text-overflow: ellipsis;
-}
-.card.hovercard .bottom {
- padding: 0 20px;
- margin-bottom: 17px;
-}
-`
-
- pageHtml := fmt.Sprintf(`<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta charset="utf-8">
- <meta name="go-import" content="%[1]s/%[2]s git https://github.com/%[3]s/%[2]s.git">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>%[1]s/%[2]s</title>
- <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
- <link href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-social/4.10.1/bootstrap-social.min.css" rel="stylesheet">
- <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
- <style>%[5]s</style>
- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
- <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
- </head>
- <body>
- <div class="container">
- <div class="card hovercard">
- <div class="cardheader"></div>
- <div class="avatar">
- <img alt="%[3]s's GitHub Avatar" src="%[4]s">
- </div>
- <div class="info">
- <div class="title">
- <a href="https://github.com/%[3]s">%[3]s</a>
- </div>
- <div class="desc">go get -u %[1]s/%[2]s</div>
- </div>
- </div>
- <div class="bottom">
- <a class="btn btn-block btn-social btn-github" href="https://github.com/%[3]s/%[2]s">
- <i class="fa fa-github"></i> Visit the project on GitHub
- </a>
- </div>
- </div>
- </body>
-</html>
-`,
- customUrl, // %[1]s
- r.URL.Path[1:], // %[2]s
- githubUser, // %[3]s
- githubUserData.AvatarUrl, // %[4]s
- pageCss, // %[5]s
- )
-
- fmt.Fprint(w, pageHtml)
-}
-
func Serve(customUrl, githubUser, listenPort string) {
// Process inputs
githubUserData := getGitHubUserData(githubUser)
listenPort = ":" + listenPort
// Prepare handler
- // Todo: Add separate handler func for / vs /*, / is profile page for user
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- handler(w, r, customUrl, githubUser, githubUserData)
+ handler(w, r, customUrl, githubUserData)
})
// Listen and SERVE!
@@ -218,3 +48,36 @@ func Serve(customUrl, githubUser, listenPort string) {
err := http.ListenAndServe(listenPort, nil)
handleError("Serving HTTP requests", err)
}
+
+func handler(w http.ResponseWriter, r *http.Request, customUrl string, githubUserData GitHubUserData) {
+ t := time.Now()
+ log.WithFields(log.Fields{
+ "at": t.Format(time.RFC850),
+ "from": r.RemoteAddr,
+ }).Info("Handled Connection")
+
+ thisPackage := r.URL.Path[1:]
+
+ if thisPackage == "" {
+ fmt.Fprintf(w, userProfilePage(customUrl, githubUserData))
+
+ } else {
+ requestRepo := fmt.Sprintf(
+ "https://github.com/%s.git", githubUserData.Login+"/"+thisPackage)
+ githubUserRepos := getGitHubUserRepos(githubUserData.Login)
+
+ repoExists := false
+ for _, repo := range githubUserRepos {
+ if repo.CloneUrl == requestRepo {
+ repoExists = true
+ }
+ }
+
+ if !repoExists {
+ fmt.Fprint(w, redirectPage("Repository doesn't exist", "/"))
+
+ } else {
+ fmt.Fprint(w, packageProfilePage(customUrl, thisPackage, githubUserData))
+ }
+ }
+}
diff --git a/lib/pages.go b/lib/pages.go
new file mode 100644
index 0000000..aad35bf
--- /dev/null
+++ b/lib/pages.go
@@ -0,0 +1,266 @@
+// GoGetMe - go get your GitHub-hosted Go packages via your own domain
+// Copyright (C) 2015 alecdwm
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package lib
+
+import "fmt"
+
+var userProfileCss = `
+.card {
+ padding-top: 20px;
+ margin: 10px 0 20px 0;
+ background-color: rgba(214, 224, 226, 0.2);
+ border-top-width: 0;
+ border-bottom-width: 2px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.card.hovercard {
+ position: relative;
+ padding-top: 0;
+ overflow: hidden;
+ text-align: center;
+ background-color: rgba(214, 224, 226, 0.2);
+}
+.card.hovercard .cardheader {
+ background: url("http://blog.golang.org/gopher/header.jpg");
+ background-size: cover;
+ height: 280px;
+}
+.card.hovercard .avatar {
+ position: relative;
+ top: -50px;
+ margin-bottom: -50px;
+}
+.card.hovercard .avatar img {
+ width: 100px;
+ height: 100px;
+ max-width: 100px;
+ max-height: 100px;
+ -webkit-border-radius: 50%%;
+ -moz-border-radius: 50%%;
+ border-radius: 50%%;
+ border: 5px solid rgba(255,255,255,0.5);
+}
+.card.hovercard .info {
+ padding: 4px 8px 10px;
+}
+.card.hovercard .info .title {
+ margin-bottom: 4px;
+ font-size: 24px;
+ line-height: 1;
+ color: #262626;
+ vertical-align: middle;
+}
+.card.hovercard .info .desc {
+ overflow: hidden;
+ font-size: 12px;
+ line-height: 20px;
+ color: #737373;
+ text-overflow: ellipsis;
+}
+.card.hovercard .bottom {
+ padding: 0 20px;
+ margin-bottom: 17px;
+}
+`
+
+var packageProfileCss = `
+.card {
+ padding-top: 20px;
+ margin: 10px 0 20px 0;
+ background-color: rgba(214, 224, 226, 0.2);
+ border-top-width: 0;
+ border-bottom-width: 2px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.card.hovercard {
+ position: relative;
+ padding-top: 0;
+ overflow: hidden;
+ text-align: center;
+ background-color: rgba(214, 224, 226, 0.2);
+}
+.card.hovercard .cardheader {
+ background: url("http://blog.golang.org/gopher/header.jpg");
+ background-size: cover;
+ height: 280px;
+}
+.card.hovercard .avatar {
+ position: relative;
+ top: -50px;
+ margin-bottom: -50px;
+}
+.card.hovercard .avatar img {
+ width: 100px;
+ height: 100px;
+ max-width: 100px;
+ max-height: 100px;
+ -webkit-border-radius: 50%;
+ -moz-border-radius: 50%;
+ border-radius: 50%;
+ border: 5px solid rgba(255,255,255,0.5);
+}
+.card.hovercard .info {
+ padding: 4px 8px 10px;
+}
+.card.hovercard .info .title {
+ margin-bottom: 4px;
+ font-size: 24px;
+ line-height: 1;
+ color: #262626;
+ vertical-align: middle;
+}
+.card.hovercard .info .desc {
+ overflow: hidden;
+ font-size: 12px;
+ line-height: 20px;
+ color: #737373;
+ text-overflow: ellipsis;
+}
+.card.hovercard .bottom {
+ padding: 0 20px;
+ margin-bottom: 17px;
+}
+`
+
+func redirectPage(msg, to string) (pageHtml string) {
+ pageHtml = fmt.Sprintf(`
+<!DOCTYPE html>
+<html>
+ <head>
+ <!-- HTML meta refresh URL redirection -->
+ <meta http-equiv="refresh" content="0; url=%[1]s">
+ </head>
+ <body>
+ <p>%[2]s, redirecting to:
+ <a href="%[1]s">%[1]s</a></p>
+ </body>
+</html>
+`,
+ to, msg)
+
+ return pageHtml
+}
+
+func userProfilePage(customUrl string, githubUserData GitHubUserData) (pageHtml string) {
+ pageHtml = fmt.Sprintf(`<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>%[1]s</title>
+ <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
+ <link href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-social/4.10.1/bootstrap-social.min.css" rel="stylesheet">
+ <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
+ <style>%[4]s</style>
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+ <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
+ </head>
+ <body>
+ <div class="container">
+ <div class="card hovercard">
+ <div class="cardheader"></div>
+ <div class="avatar">
+ <img alt="%[2]s's GitHub Avatar" src="%[3]s">
+ </div>
+ <div class="info">
+ <div class="title">
+ <a href="https://github.com/%[2]s">%[2]s</a>
+ </div>
+ <div class="desc">%[1]s<br />a host for %[2]s's golang packages</div>
+ </div>
+ </div>
+ <div class="bottom">
+ <a class="btn btn-block btn-social btn-github" href="https://github.com/%[2]s">
+ <i class="fa fa-github"></i> Visit the user on GitHub
+ </a>
+ </div>
+ </div>
+ </body>
+</html>
+`,
+ customUrl, // %[1]s
+ githubUserData.Login, // %[2]s
+ githubUserData.AvatarUrl, // %[3]s
+ userProfileCss, // %[4]s
+ )
+
+ return pageHtml
+}
+
+func packageProfilePage(customUrl, thisPackage string, githubUserData GitHubUserData) (pageHtml string) {
+ pageHtml = fmt.Sprintf(`<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="go-import" content="%[1]s/%[2]s git https://github.com/%[3]s/%[2]s.git">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>%[1]s/%[2]s</title>
+ <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
+ <link href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-social/4.10.1/bootstrap-social.min.css" rel="stylesheet">
+ <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
+ <style>%[5]s</style>
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+ <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
+ </head>
+ <body>
+ <div class="container">
+ <div class="card hovercard">
+ <div class="cardheader"></div>
+ <div class="avatar">
+ <img alt="%[3]s's GitHub Avatar" src="%[4]s">
+ </div>
+ <div class="info">
+ <div class="title">
+ <a href="https://github.com/%[3]s">%[3]s</a>
+ </div>
+ <div class="desc">go get -u %[1]s/%[2]s</div>
+ </div>
+ </div>
+ <div class="bottom">
+ <a class="btn btn-block btn-social btn-github" href="https://github.com/%[3]s/%[2]s">
+ <i class="fa fa-github"></i> Visit the project on GitHub
+ </a>
+ </div>
+ </div>
+ </body>
+</html>
+`,
+ customUrl, // %[1]s
+ thisPackage, // %[2]s
+ githubUserData.Login, // %[3]s
+ githubUserData.AvatarUrl, // %[4]s
+ packageProfileCss, // %[5]s
+ )
+
+ return pageHtml
+}