Skip to content

Commit 40c3ccf

Browse files
author
Stephen Cathcart
committed
initial
0 parents  commit 40c3ccf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4554
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.sqlite3
2+
vendor/*/

Procfile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: sanitarium

README.md

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# Sanitarium.io
2+
3+
[TOC]
4+
5+
## Outline
6+
7+
Welcome to Sanitarium.io, a facility for holding the criminally insane. Our archive exposes a JSON API, enabling you to query hand-curated data on horror movie antagonists.
8+
9+
Patient records contain an in-depth profile and previous criminal activity, including a list of known victims. Our facility basement is used to house our aquatic killers.
10+
11+
### Go Commands
12+
13+
#### Install
14+
15+
**Installer:** *https://dl.google.com/go/go1.11.4.darwin-amd64.pkg*
16+
17+
**Workspace:** /Users/stephencathcart/go/src
18+
19+
##### Update PATH
20+
21+
```
22+
# /Users/stephencathcart/.bash_profile
23+
export GOPATH=$HOME/go
24+
export PATH=$GOPATH/bin:$PATH
25+
```
26+
27+
#### Packages
28+
29+
```
30+
# Install vendor
31+
go get -u github.com/kardianos/govendor
32+
33+
# Install packages
34+
govendor sync
35+
36+
# Ensure build installs to bin
37+
go install
38+
```
39+
40+
#### Format
41+
42+
```
43+
cd {workspace}
44+
go fmt ./...
45+
```
46+
Which will format all code in current directory and all of its subdirectories.
47+
48+
#### Test
49+
50+
```
51+
cd {workspace}
52+
go test ./...
53+
```
54+
Which will run all tests in current directory and all of its subdirectories.
55+
56+
#### Build & Run
57+
58+
Environment variables:
59+
60+
- **PORT:** Set port number for local development. Heroku supplies a dynamic port.
61+
- **ENV:** Set to *DEV* for local development. Used to configure *api* subdomain host.
62+
63+
```
64+
cd {workspace}
65+
PORT=8000 ENV=DEV go run main.go
66+
```
67+
Which will run the application.
68+
69+
### Localhost Access
70+
71+
**Frontend:** *http://lvh.me:8000*
72+
73+
**API:** *http://api.lvh.me:8000/killers/random*
74+
75+
### Heroku Setup
76+
77+
**User:** *[HIDDEN]*
78+
79+
#### Install Heroku CLI (requires Git)
80+
81+
**Installer:** *https://cli-assets.heroku.com/heroku.pkg*
82+
83+
#### Heroku CLI Commands
84+
85+
##### Verify Installation
86+
87+
```
88+
heroku --version
89+
```
90+
91+
##### Login
92+
93+
```
94+
heroku login
95+
```
96+
97+
##### Create EU Dyno with Go Buildpack
98+
99+
```
100+
heroku create sanitarium --region eu --buildpack heroku/go
101+
```
102+
103+
##### Create Domains
104+
```
105+
heroku domains:add sanitarium.io --app sanitarium
106+
heroku domains:add www.sanitarium.io --app sanitarium
107+
heroku domains:add api.sanitarium.io --app sanitarium
108+
```
109+
110+
##### List Hosts
111+
```
112+
host sanitarium.io
113+
host www.sanitarium.io
114+
```
115+
116+
##### Deploy
117+
```
118+
git push https://heroku:{api-key}@git.heroku.com/sanitarium.git HEAD
119+
```
120+
121+
### Cloudflare
122+
123+
**User:** *[HIDDEN]*
124+
125+
**SSL:** Flexible
126+
127+
**Universal SSL Status:** Active Certificate
128+
129+
#### Manage DNS settings
130+
131+
| Type | Name | *Value | TTL |
132+
|------ | ------------- | --------------------------------------- | ------------- |
133+
| CNAME | api | [HIDDEN] | Automatic |
134+
| CNAME | sanitarium.io | [HIDDEN] | Automatic |
135+
| CNAME | www | [HIDDEN] | Automatic |
136+
| MX | sanitarium.io | [HIDDEN], priority: 10 | 1h |
137+
| MX | sanitarium.io | [HIDDEN], priority: 10 | 1h |
138+
| MX | sanitarium.io | [HIDDEN], priority: 5 | 1h |
139+
| MX | sanitarium.io | [HIDDEN], priority: 5 | 1h |
140+
| MX | sanitarium.io | [HIDDEN], priority: 1 | 1h |
141+
| TXT | sanitarium.io | [HIDDEN] | Automatic |
142+
143+
> **Note:** CNAME values are dynamically generated by Heroku when a new domain is added.
144+
145+
#### Configure Page Rules
146+
147+
| Rule | URL Match | Type | Sub Type | Value |
148+
|------ | ----------------------- | ---------------- | ------------------------ | -------------------- |
149+
| 1 | *www.sanitarium.io/ | Forwarding URL | 301 - Permanent Redirect | http://sanitarium.io |
150+
| 2 | http://*sanitarium.io/* | Always Use HTTPS | ... | ... |
151+
152+
### Domain Registrar
153+
154+
**Registrar:** name.com
155+
156+
**User:** *[HIDDEN]*
157+
158+
#### Configure Nameservers
159+
160+
| Nameserver |
161+
|--------------- |
162+
| [HIDDEN] |
163+
| [HIDDEN] |
164+
165+
### Bitbucket CI/CD Pipeline
166+
167+
**Enable Pipelines:** true
168+
169+
Commiting to *master* will automatically deploy to Heroku.
170+
171+
#### Repository Variables
172+
173+
| Names | Value |
174+
|---------------- | ---------- |
175+
| HEROKU_API_KEY | *** |
176+
| HEROKU_APP_NAME | sanitarium |
177+
178+
### Mailchimp
179+
180+
**User:** *[HIDDEN]*
181+
182+
**Email:** *[HIDDEN]*
183+
184+
### GSuite
185+
186+
**User:** *[HIDDEN]*

api/classification-api.go

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package api
2+
3+
import (
4+
"bitbucket.org/stephen_cathcart/sanitarium/exception"
5+
"bitbucket.org/stephen_cathcart/sanitarium/storage"
6+
"bitbucket.org/stephen_cathcart/sanitarium/storage/entity"
7+
"encoding/json"
8+
"net/http"
9+
)
10+
11+
// GetAllClassifications returns all classifications
12+
func GetAllClassifications(w http.ResponseWriter, r *http.Request) {
13+
classifications, err := entity.NewEntityRepository(storage.DB).GetAllClassifications()
14+
if err != nil {
15+
message, _ := json.Marshal(&exception.Error{Code: http.StatusNotFound, Message: exception.KillerNotFound})
16+
http.Error(w, string(message), http.StatusNotFound)
17+
return
18+
}
19+
json.NewEncoder(w).Encode(classifications)
20+
}

api/killer-api.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package api
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"net/url"
7+
"strings"
8+
9+
"bitbucket.org/stephen_cathcart/sanitarium/exception"
10+
"bitbucket.org/stephen_cathcart/sanitarium/request"
11+
"bitbucket.org/stephen_cathcart/sanitarium/storage"
12+
"bitbucket.org/stephen_cathcart/sanitarium/storage/entity"
13+
"github.com/gorilla/mux"
14+
)
15+
16+
// GetKillerBySlug returns a single killer matching the slug
17+
func GetKillerBySlug(w http.ResponseWriter, r *http.Request) {
18+
killer, err := entity.NewEntityRepository(storage.DB).GetKillerBySlug(getParameter(r, "slug"))
19+
if err != nil {
20+
message, _ := json.Marshal(&exception.Error{Code: http.StatusNotFound, Message: exception.KillerNotFound})
21+
http.Error(w, string(message), http.StatusNotFound)
22+
return
23+
}
24+
json.NewEncoder(w).Encode(killer)
25+
}
26+
27+
// GetRandomKiller returns a random killer
28+
func GetRandomKiller(w http.ResponseWriter, r *http.Request) {
29+
query := extractQuery(r.URL.Query())
30+
killer, err := entity.NewEntityRepository(storage.DB).GetRandomKiller(query)
31+
if err != nil {
32+
message, _ := json.Marshal(&exception.Error{Code: http.StatusNotFound, Message: err.Error()})
33+
http.Error(w, string(message), http.StatusNotFound)
34+
return
35+
}
36+
json.NewEncoder(w).Encode(killer)
37+
}
38+
39+
func getParameter(r *http.Request, key string) (value string) {
40+
params := mux.Vars(r)
41+
value = params[key]
42+
value = strings.TrimRight(value, "/")
43+
return value
44+
}
45+
46+
func extractQuery(values url.Values) *request.Query {
47+
return request.NewQuery(
48+
values.Get("name"),
49+
values.Get("weapon"),
50+
values.Get("classification"),
51+
values.Get("residence"),
52+
)
53+
}

bitbucket-pipelines.yml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
image: golang:1.11-alpine
2+
3+
clone:
4+
depth: full
5+
6+
stepdefinitions:
7+
- buildtest: &buildtest
8+
name: Build and test
9+
script:
10+
- apk add --update --no-cache gcc musl-dev git tar
11+
- PACKAGE_PATH="${GOPATH}/src/bitbucket.org/${BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}"
12+
- mkdir -pv "${PACKAGE_PATH}"
13+
- tar -cO --exclude-vcs --exclude=bitbucket-pipelines.yml . | tar -xv -C "${PACKAGE_PATH}"
14+
- cd "${PACKAGE_PATH}"
15+
- go get -v
16+
- go build -v
17+
- go test -v
18+
- deploy: &deploy
19+
name: Deploy to Heroku
20+
script:
21+
- apk add --update --no-cache git
22+
- git push https://heroku:[email protected]/$HEROKU_APP_NAME.git HEAD
23+
24+
pipelines:
25+
branches:
26+
default:
27+
- step: *buildtest
28+
master:
29+
- step: *buildtest
30+
- step: *deploy

config/config.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package config
2+
3+
import (
4+
"log"
5+
"os"
6+
)
7+
8+
// GetAPIHost todo
9+
func GetAPIHost() string {
10+
if os.Getenv("ENV") == "DEV" {
11+
return "api.lvh.me"
12+
}
13+
return "api.sanitarium.io"
14+
}
15+
16+
// GetPort todo
17+
func GetPort() string {
18+
port := os.Getenv("PORT")
19+
if port == "" {
20+
log.Fatal("$PORT not set")
21+
}
22+
return ":" + port
23+
}

exception/error.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package exception
2+
3+
type Error struct {
4+
Code uint `json:"code"`
5+
Message string `json:"message"`
6+
}

exception/killer-exception.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package exception
2+
3+
const (
4+
KillerNotFound = "Killer not found"
5+
ClassificationsNotFound = "Classifications not found"
6+
)

main.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net/http"
6+
7+
"bitbucket.org/stephen_cathcart/sanitarium/config"
8+
"bitbucket.org/stephen_cathcart/sanitarium/middleware"
9+
"bitbucket.org/stephen_cathcart/sanitarium/routing"
10+
"bitbucket.org/stephen_cathcart/sanitarium/storage"
11+
)
12+
13+
func main() {
14+
// Start the database
15+
storage.DB = storage.NewRepository()
16+
storage.DB.Initialise()
17+
defer storage.DB.Close()
18+
// Create rate limiter middleware
19+
rateLimiter := middleware.RateLimiter()
20+
// Start the API server
21+
http.Handle(config.GetAPIHost()+"/killers", rateLimiter.Handler(routing.Killers()))
22+
http.Handle(config.GetAPIHost()+"/killers/", rateLimiter.Handler(routing.Killers()))
23+
http.Handle(config.GetAPIHost()+"/classifications", rateLimiter.Handler(routing.Classifications()))
24+
http.Handle(config.GetAPIHost()+"/classifications/", rateLimiter.Handler(routing.Classifications()))
25+
// Start the File server
26+
http.Handle("/", routing.Home())
27+
// Start the TCP server
28+
log.Fatal(http.ListenAndServe(config.GetPort(), nil))
29+
}

0 commit comments

Comments
 (0)