From c86a2453096d6071fcb2e15f5f846d89411f97fb Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Mon, 22 Feb 2016 11:44:56 -0500 Subject: [PATCH] Initial commit --- .gitlab-ci.yml | 25 +++++++++++ Dockerfile | 12 ++++++ main.go | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 .gitlab-ci.yml create mode 100644 Dockerfile create mode 100644 main.go diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..eda5caa --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,25 @@ +stages: + - build + - deploy + +build: + stage: build + script: + - docker build -t atomaka/punaday-api . + except: + - tags + tags: + - docker +deploy: + stage: deploy + script: + - VERSION=$(git describe --tags) + - docker build -t atomaka/punaday-api . + - docker tag atomaka/punaday-api:latest docker.atomaka.com/atomaka/punaday-api:$VERSION + - docker tag atomaka/punaday-api:latest docker.atomaka.com/atomaka/punaday-api:latest + - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD -e me@atomaka.com docker.atomaka.com + - docker push docker.atomaka.com/atomaka/punaday-api + only: + - tags + tags: + - docker diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9f23623 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:latest +COPY . /go/src/github.com/atomaka/punaday-api +RUN apk update \ + && apk add --no-cache go git \ + && cd /go/src/github.com/atomaka/punaday-api \ + && export GOPATH=/go \ + && go get \ + && go build -o /bin/punaday-api \ + && rm -rf /go \ + && apk del --purge git go \ + && rm -rf /var/cache/apk* +ENTRYPOINT ["/bin/punaday-api"] diff --git a/main.go b/main.go new file mode 100644 index 0000000..c9201ac --- /dev/null +++ b/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + + "golang.org/x/net/html" +) + +const PUN_BASE = "http://www.punoftheday.com" +const RANDOM_PUN = PUN_BASE + "/cgi-bin/randompun.pl" +const SELECT_PUN = PUN_BASE + "/pun" + +type Pun struct { + Id int `json:"-"` + Url string `json:"url"` + Text string `json:"text"` +} + +type Error struct { + Status int `json:"-"` + Detail string `json:"error"` +} + +func main() { + http.HandleFunc("/puns/random", RandomPun) + http.HandleFunc("/puns/", ShowPun) + http.HandleFunc("/", NotFound) + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func NotFound(w http.ResponseWriter, r *http.Request) { + log.Printf("404 Not Found: %v\n", r.URL.Path) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + err := &Error{404, "not_found"} + w.WriteHeader(err.Status) + json.NewEncoder(w).Encode(err) +} + +func WritePun(w http.ResponseWriter, r *http.Request, pun Pun) { + log.Printf("200 OK: %v\n", r.URL.Path) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + json.NewEncoder(w).Encode(pun) +} + +func ShowPun(w http.ResponseWriter, r *http.Request) { + id := r.URL.Path[len("/puns/"):] + + pun := getPun(SELECT_PUN + "/" + id) + + if id != strconv.Itoa(pun.Id) { + NotFound(w, r) + } else { + WritePun(w, r, pun) + } +} + +func RandomPun(w http.ResponseWriter, r *http.Request) { + pun := getPun(RANDOM_PUN) + + WritePun(w, r, pun) +} + +func getPun(url string) Pun { + resp, err := http.Get(url) + if err != nil { + log.Println("ERROR: Unable to access " + url) + } + + b := resp.Body + defer b.Close() + + pun := Pun{} + + z := html.NewTokenizer(b) + + for { + tt := z.Next() + + switch { + case tt == html.ErrorToken: + return pun + case tt == html.StartTagToken: + t := z.Token() + + isParagraph := t.Data == "p" + isInput := t.Data == "input" + + if isParagraph && pun.Text == "" { + z.Next() + t := z.Token() + pun.Text = t.Data + } + if isInput { + if getAttr("name", t) == "PunID" { + pun.Id, _ = strconv.Atoi(getAttr("value", t)) + pun.Url = SELECT_PUN + "/" + strconv.Itoa(pun.Id) + } + } + } + } +} + +func getAttr(at string, t html.Token) string { + var val string + for _, a := range t.Attr { + if a.Key == at { + val = a.Val + } + } + + return val +}