Skip to content

Commit 89704a9

Browse files
committed
Development commit
0 parents  commit 89704a9

28 files changed

+2938
-0
lines changed

.env

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Postgres Live
2+
DB_HOST=postgres # used when running the app with docker
3+
# DB_HOST=127.0.0.1 # when running the app without docker
4+
DB_DRIVER=postgres
5+
API_SECRET=98hbun98h # Used for creating a JWT. Can be anything
6+
DB_USER=steven
7+
DB_PASSWORD=
8+
DB_NAME=fullstack_api
9+
DB_PORT=5432 #Default postgres port
10+
11+
PGADMIN_DEFAULT_EMAIL=live@admin.com
12+
PGADMIN_DEFAULT_PASSWORD=password
13+
14+
# Postgres Test
15+
TEST_DB_HOST=postgres_test # used when running the app with docker
16+
# TEST_DB_HOST=127.0.0.1 # when running the app without docker
17+
TEST_DB_DRIVER=postgres
18+
TEST_API_SECRET=98hbun98h
19+
TEST_DB_USER=steven
20+
TEST_DB_PASSWORD=
21+
TEST_DB_NAME=fullstack_api_test
22+
TEST_DB_PORT=5432
23+
24+
25+
# # Mysql Live
26+
# DB_HOST=mysql # used when running the app with docker
27+
# # DB_HOST=127.0.0.1 # when running the app without docker
28+
# DB_DRIVER=mysql
29+
# API_SECRET=98hbun98h #Used for creating a JWT. Can be anything
30+
# DB_USER=steven
31+
# DB_PASSWORD=here
32+
# DB_NAME=fullstack_api
33+
# DB_PORT=3306 #Default mysql port
34+
35+
36+
# # Mysql Test
37+
# TEST_DB_HOST=mysql_test # used when running the app with docker
38+
# # TEST_DB_HOST=127.0.0.1 # when running the app without docker
39+
# TEST_DB_DRIVER=mysql
40+
# TEST_API_SECRET=98hbun98h
41+
# TEST_DB_USER=steven
42+
# TEST_DB_PASSWORD=here
43+
# TEST_DB_NAME=fullstack_api_test
44+
# TEST_DB_PORT=3306

Dockerfile

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Start from golang base image
2+
FROM golang:alpine as builder
3+
4+
# ENV GO111MODULE=on
5+
6+
# Add Maintainer info
7+
LABEL maintainer="Steven Victor <chikodi543@gmail.com>"
8+
9+
# Install git.
10+
# Git is required for fetching the dependencies.
11+
RUN apk update && apk add --no-cache git
12+
13+
# Set the current working directory inside the container
14+
WORKDIR /app
15+
16+
# Copy go mod and sum files
17+
COPY go.mod go.sum ./
18+
19+
# RUN go mod tidy
20+
21+
# Download all dependencies. Dependencies will be cached if the go.mod and the go.sum files are not changed
22+
RUN go mod download
23+
24+
# Copy the source from the current directory to the working Directory inside the container
25+
COPY . .
26+
# Build the Go app
27+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
28+
29+
# Start a new stage from scratch
30+
FROM alpine:latest
31+
RUN apk --no-cache add ca-certificates
32+
33+
WORKDIR /root/
34+
35+
# Copy the Pre-built binary file from the previous stage
36+
COPY --from=builder /app/main .
37+
COPY --from=builder /app/.env .
38+
39+
# Expose port 8080 to the outside world
40+
EXPOSE 8080
41+
42+
#Command to run the executable
43+
CMD ["./main"]

Dockerfile.test

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM golang:1.12-alpine
2+
3+
# Install git
4+
5+
RUN apk update && apk add --no-cache git
6+
7+
WORKDIR /app
8+
9+
COPY go.mod go.sum ./
10+
11+
RUN go mod download
12+
13+
# Copy the source from the current directory to the working Directory inside the container
14+
COPY . .
15+
16+
# Run tests
17+
CMD CGO_ENABLED=0 go test -v ./...

api/auth/token.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package auth
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"os"
9+
"strconv"
10+
"strings"
11+
"time"
12+
13+
jwt "github.com/dgrijalva/jwt-go"
14+
)
15+
16+
func CreateToken(user_id uint32) (string, error) {
17+
claims := jwt.MapClaims{}
18+
claims["authorized"] = true
19+
claims["user_id"] = user_id
20+
claims["exp"] = time.Now().Add(time.Hour * 1).Unix() //Token expires after 1 hour
21+
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
22+
return token.SignedString([]byte(os.Getenv("API_SECRET")))
23+
24+
}
25+
26+
func TokenValid(r *http.Request) error {
27+
tokenString := ExtractToken(r)
28+
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
29+
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
30+
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
31+
}
32+
return []byte(os.Getenv("API_SECRET")), nil
33+
})
34+
if err != nil {
35+
return err
36+
}
37+
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
38+
Pretty(claims)
39+
}
40+
return nil
41+
}
42+
43+
func ExtractToken(r *http.Request) string {
44+
keys := r.URL.Query()
45+
token := keys.Get("token")
46+
if token != "" {
47+
return token
48+
}
49+
bearerToken := r.Header.Get("Authorization")
50+
if len(strings.Split(bearerToken, " ")) == 2 {
51+
return strings.Split(bearerToken, " ")[1]
52+
}
53+
return ""
54+
}
55+
56+
func ExtractTokenID(r *http.Request) (uint32, error) {
57+
58+
tokenString := ExtractToken(r)
59+
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
60+
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
61+
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
62+
}
63+
return []byte(os.Getenv("API_SECRET")), nil
64+
})
65+
if err != nil {
66+
return 0, err
67+
}
68+
claims, ok := token.Claims.(jwt.MapClaims)
69+
if ok && token.Valid {
70+
uid, err := strconv.ParseUint(fmt.Sprintf("%.0f", claims["user_id"]), 10, 32)
71+
if err != nil {
72+
return 0, err
73+
}
74+
return uint32(uid), nil
75+
}
76+
return 0, nil
77+
}
78+
79+
//Pretty display the claims licely in the terminal
80+
func Pretty(data interface{}) {
81+
b, err := json.MarshalIndent(data, "", " ")
82+
if err != nil {
83+
log.Println(err)
84+
return
85+
}
86+
87+
fmt.Println(string(b))
88+
}

api/controllers/base.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package controllers
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/http"
7+
8+
"github.com/gorilla/mux"
9+
"github.com/jinzhu/gorm"
10+
11+
_ "github.com/jinzhu/gorm/dialects/mysql" //mysql database driver
12+
_ "github.com/jinzhu/gorm/dialects/postgres" //postgres database driver
13+
14+
"github.com/victorsteven/fullstack/api/models"
15+
)
16+
17+
type Server struct {
18+
DB *gorm.DB
19+
Router *mux.Router
20+
}
21+
22+
func (server *Server) Initialize(Dbdriver, DbUser, DbPassword, DbPort, DbHost, DbName string) {
23+
24+
var err error
25+
26+
if Dbdriver == "mysql" {
27+
DBURL := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local", DbUser, DbPassword, DbHost, DbPort, DbName)
28+
server.DB, err = gorm.Open(Dbdriver, DBURL)
29+
if err != nil {
30+
fmt.Printf("Cannot connect to %s database", Dbdriver)
31+
log.Fatal("This is the error:", err)
32+
} else {
33+
fmt.Printf("We are connected to the %s database", Dbdriver)
34+
}
35+
}
36+
if Dbdriver == "postgres" {
37+
DBURL := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable password=%s", DbHost, DbPort, DbUser, DbName, DbPassword)
38+
server.DB, err = gorm.Open(Dbdriver, DBURL)
39+
if err != nil {
40+
fmt.Printf("Cannot connect to %s database", Dbdriver)
41+
log.Fatal("This is the error:", err)
42+
} else {
43+
fmt.Printf("We are connected to the %s database", Dbdriver)
44+
}
45+
}
46+
47+
server.DB.Debug().AutoMigrate(&models.User{}, &models.Post{}) //database migration
48+
49+
server.Router = mux.NewRouter()
50+
51+
server.initializeRoutes()
52+
}
53+
54+
func (server *Server) Run(addr string) {
55+
fmt.Println("Listening to port 8080")
56+
log.Fatal(http.ListenAndServe(addr, server.Router))
57+
}

api/controllers/home_controller.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package controllers
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/victorsteven/fullstack/api/responses"
7+
)
8+
9+
func (server *Server) Home(w http.ResponseWriter, r *http.Request) {
10+
responses.JSON(w, http.StatusOK, "Welcome To This Awesome API")
11+
12+
}

api/controllers/login_controller.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package controllers
2+
3+
import (
4+
"encoding/json"
5+
"io/ioutil"
6+
"net/http"
7+
8+
"github.com/victorsteven/fullstack/api/auth"
9+
"github.com/victorsteven/fullstack/api/models"
10+
"github.com/victorsteven/fullstack/api/responses"
11+
"github.com/victorsteven/fullstack/api/utils/formaterror"
12+
"golang.org/x/crypto/bcrypt"
13+
)
14+
15+
func (server *Server) Login(w http.ResponseWriter, r *http.Request) {
16+
body, err := ioutil.ReadAll(r.Body)
17+
if err != nil {
18+
responses.ERROR(w, http.StatusUnprocessableEntity, err)
19+
return
20+
}
21+
user := models.User{}
22+
err = json.Unmarshal(body, &user)
23+
if err != nil {
24+
responses.ERROR(w, http.StatusUnprocessableEntity, err)
25+
return
26+
}
27+
28+
user.Prepare()
29+
err = user.Validate("login")
30+
if err != nil {
31+
responses.ERROR(w, http.StatusUnprocessableEntity, err)
32+
return
33+
}
34+
token, err := server.SignIn(user.Email, user.Password)
35+
if err != nil {
36+
formattedError := formaterror.FormatError(err.Error())
37+
responses.ERROR(w, http.StatusUnprocessableEntity, formattedError)
38+
return
39+
}
40+
responses.JSON(w, http.StatusOK, token)
41+
}
42+
43+
func (server *Server) SignIn(email, password string) (string, error) {
44+
45+
var err error
46+
47+
user := models.User{}
48+
49+
err = server.DB.Debug().Model(models.User{}).Where("email = ?", email).Take(&user).Error
50+
if err != nil {
51+
return "", err
52+
}
53+
err = models.VerifyPassword(user.Password, password)
54+
if err != nil && err == bcrypt.ErrMismatchedHashAndPassword {
55+
return "", err
56+
}
57+
return auth.CreateToken(user.ID)
58+
}

0 commit comments

Comments
 (0)