Skip to content

Commit 66fd811

Browse files
authored
Merge pull request src-d#7 from carlosms/backend-skeleton
Backend skeleton
2 parents b8d3dab + 58b2d0b commit 66fd811

File tree

346 files changed

+167734
-0
lines changed

Some content is hidden

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

346 files changed

+167734
-0
lines changed

Gopkg.lock

+57
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Gopkg.toml example
2+
#
3+
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
4+
# for detailed Gopkg.toml documentation.
5+
#
6+
# required = ["github.com/user/thing/cmd/thing"]
7+
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
8+
#
9+
# [[constraint]]
10+
# name = "github.com/user/project"
11+
# version = "1.0.0"
12+
#
13+
# [[constraint]]
14+
# name = "github.com/user/project2"
15+
# branch = "dev"
16+
# source = "github.com/myfork/project2"
17+
#
18+
# [[override]]
19+
# name = "github.com/x/y"
20+
# version = "2.4.0"
21+
#
22+
# [prune]
23+
# non-go = false
24+
# go-tests = true
25+
# unused-packages = true
26+
27+
28+
[[constraint]]
29+
name = "github.com/go-chi/chi"
30+
version = "3.3.2"
31+
32+
[[constraint]]
33+
name = "github.com/kelseyhightower/envconfig"
34+
version = "1.3.0"
35+
36+
[[constraint]]
37+
name = "github.com/pressly/lg"
38+
version = "1.0.2"
39+
40+
[[constraint]]
41+
name = "github.com/rs/cors"
42+
version = "1.3.0"
43+
44+
[[constraint]]
45+
name = "github.com/sirupsen/logrus"
46+
version = "1.0.5"
47+
48+
[prune]
49+
go-tests = true
50+
unused-packages = true

build/index.html

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<p>It works.</p>
5+
</body>
6+
</html>

cmd/server/main.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/src-d/gitbase-playground/server"
8+
"github.com/src-d/gitbase-playground/server/handler"
9+
"github.com/src-d/gitbase-playground/server/service"
10+
11+
"github.com/kelseyhightower/envconfig"
12+
)
13+
14+
// version will be replaced automatically by the CI build.
15+
// See https://github.com/src-d/ci/blob/v1/Makefile.main#L56
16+
var version = "dev"
17+
18+
type appConfig struct {
19+
Env string `envconfig:"ENV" default:"production"`
20+
Host string `envconfig:"HOST" default:"0.0.0.0"`
21+
Port int `envconfig:"PORT" default:"8080"`
22+
ServerURL string `envconfig:"SERVER_URL"`
23+
}
24+
25+
func main() {
26+
// main configuration
27+
var conf appConfig
28+
envconfig.MustProcess("GITBASEPG", &conf)
29+
if conf.ServerURL == "" {
30+
conf.ServerURL = fmt.Sprintf("//localhost:%d", conf.Port)
31+
}
32+
33+
// logger
34+
logger := service.NewLogger(conf.Env)
35+
36+
static := handler.NewStatic("build", conf.ServerURL)
37+
38+
// start the router
39+
router := server.Router(logger, static, version)
40+
logger.Infof("listening on %s:%d", conf.Host, conf.Port)
41+
err := http.ListenAndServe(fmt.Sprintf("%s:%d", conf.Host, conf.Port), router)
42+
logger.Fatal(err)
43+
}

server/assets/asset.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// satisfies go-bindata interface
2+
// this file is replaced by auto generated code for production usage
3+
// we use it instead of go-bindata -dev because it's easier to write wrapper
4+
// than change webpack configuration in create-react-app _O.O_
5+
6+
package assets
7+
8+
import (
9+
"fmt"
10+
"io/ioutil"
11+
"os"
12+
)
13+
14+
// Asset loads and returns the asset for the given name.
15+
// It returns an error if the asset could not be found or
16+
// could not be loaded.
17+
func Asset(name string) ([]byte, error) {
18+
return ioutil.ReadFile(name)
19+
}
20+
21+
// MustAsset is like Asset but panics when Asset would return an error.
22+
// It simplifies safe initialization of global variables.
23+
func MustAsset(name string) []byte {
24+
b, err := Asset(name)
25+
if err != nil {
26+
panic(fmt.Errorf("can't read file %s: %s", name, err))
27+
}
28+
return b
29+
}
30+
31+
// AssetInfo loads and returns the asset info for the given name.
32+
// It returns an error if the asset could not be found or
33+
// could not be loaded.
34+
func AssetInfo(name string) (os.FileInfo, error) {
35+
f, err := os.Open(name)
36+
if err != nil {
37+
return nil, err
38+
}
39+
return f.Stat()
40+
}

server/handler/render.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package handler
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"strconv"
8+
9+
"github.com/go-chi/chi"
10+
"github.com/pressly/lg"
11+
"github.com/src-d/gitbase-playground/server/serializer"
12+
)
13+
14+
// RequestProcessFunc is a function that takes an http.Request, and returns a serializer.Response and an error
15+
type RequestProcessFunc func(*http.Request) (*serializer.Response, error)
16+
17+
// APIHandlerFunc returns an http.HandlerFunc that will serve the user request taking the serializer.Response and errors
18+
// from the passed RequestProcessFunc
19+
func APIHandlerFunc(rp RequestProcessFunc) http.HandlerFunc {
20+
return func(w http.ResponseWriter, r *http.Request) {
21+
response, err := rp(r)
22+
if response == nil {
23+
response = serializer.NewEmptyResponse()
24+
}
25+
26+
write(w, r, response, err)
27+
}
28+
}
29+
30+
// write is the responsible of writing the response with the data from the passed *serializer.Response and error
31+
// If the passed error has StatusCode, the http.Response will be returned with the StatusCode of the passed error
32+
// If the passed error has not StatusCode, the http.Response will be returned as a http.StatusInternalServerError
33+
func write(w http.ResponseWriter, r *http.Request, response *serializer.Response, err error) {
34+
var statusCode int
35+
36+
// TODO: There should be no ppl calling write from the outside
37+
if response == nil {
38+
response = serializer.NewEmptyResponse()
39+
}
40+
41+
if err == nil {
42+
statusCode = http.StatusOK
43+
} else if httpError, ok := err.(serializer.HTTPError); ok {
44+
statusCode = httpError.StatusCode()
45+
response.Status = statusCode
46+
response.Errors = []serializer.HTTPError{httpError}
47+
} else {
48+
statusCode = http.StatusInternalServerError
49+
response.Status = statusCode
50+
response.Errors = []serializer.HTTPError{serializer.NewHTTPError(statusCode, http.StatusText(statusCode))}
51+
}
52+
53+
if statusCode >= http.StatusBadRequest {
54+
lg.RequestLog(r).Error(err.Error())
55+
}
56+
57+
content, err := json.Marshal(response)
58+
if err != nil {
59+
err = fmt.Errorf("response could not be marshalled; %s", err.Error())
60+
http.Error(w, err.Error(), http.StatusInternalServerError)
61+
lg.RequestLog(r).Error(err.Error())
62+
return
63+
}
64+
65+
w.Header().Add("content-type", "application/json")
66+
w.WriteHeader(statusCode)
67+
w.Write(content)
68+
}
69+
70+
// urlParamInt returns the url parameter from an http.Request object. If the
71+
// param cannot be converted to int, it returns a serializer.NewHTTPError
72+
func urlParamInt(r *http.Request, key string) (int, error) {
73+
str := chi.URLParam(r, key)
74+
val, err := strconv.Atoi(str)
75+
76+
if err != nil {
77+
err = serializer.NewHTTPError(
78+
http.StatusBadRequest,
79+
fmt.Sprintf("Wrong format for URL parameter %q; received %q", key, str))
80+
}
81+
82+
return val, err
83+
}

server/handler/static_files.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package handler
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"net/http"
7+
8+
"github.com/src-d/gitbase-playground/server/assets"
9+
)
10+
11+
// Static contains handlers to serve static using go-bindata
12+
type Static struct {
13+
dir string
14+
options options
15+
}
16+
17+
// NewStatic creates new Static
18+
func NewStatic(dir, serverURL string) *Static {
19+
return &Static{
20+
dir: dir,
21+
options: options{
22+
ServerURL: serverURL,
23+
},
24+
}
25+
}
26+
27+
// struct which will be marshalled and exposed to frontend
28+
type options struct {
29+
ServerURL string `json:"SERVER_URL"`
30+
}
31+
32+
// ServeHTTP serves any static file from static directory or fallbacks on index.hml
33+
func (s *Static) ServeHTTP(w http.ResponseWriter, r *http.Request) {
34+
filepath := s.dir + r.URL.Path
35+
b, err := assets.Asset(filepath)
36+
if err != nil {
37+
s.ServeIndexHTML(nil)(w, r)
38+
return
39+
}
40+
s.serveAsset(w, r, filepath, b)
41+
}
42+
43+
// ServeIndexHTML serves index.html file
44+
func (s *Static) ServeIndexHTML(initialState interface{}) http.HandlerFunc {
45+
return func(w http.ResponseWriter, r *http.Request) {
46+
filepath := s.dir + "/index.html"
47+
b, err := assets.Asset(filepath)
48+
if err != nil {
49+
http.NotFound(w, r)
50+
return
51+
}
52+
53+
options := s.options
54+
bData, err := json.Marshal(options)
55+
if err != nil {
56+
http.Error(w, err.Error(), http.StatusInternalServerError)
57+
return
58+
}
59+
b = bytes.Replace(b, []byte("window.REPLACE_BY_SERVER"), bData, 1)
60+
s.serveAsset(w, r, filepath, b)
61+
}
62+
}
63+
64+
func (s *Static) serveAsset(w http.ResponseWriter, r *http.Request, filepath string, content []byte) {
65+
info, err := assets.AssetInfo(filepath)
66+
if err != nil {
67+
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
68+
}
69+
http.ServeContent(w, r, info.Name(), info.ModTime(), bytes.NewReader(content))
70+
}

server/handler/version.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package handler
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/src-d/gitbase-playground/server/serializer"
7+
)
8+
9+
// Version returns a function that returns a *serializer.Response
10+
// with a current version of server
11+
func Version(version string) RequestProcessFunc {
12+
return func(r *http.Request) (*serializer.Response, error) {
13+
return serializer.NewVersionResponse(version), nil
14+
}
15+
}

0 commit comments

Comments
 (0)