Skip to content

Commit 6a986a8

Browse files
authored
Merge pull request #18 from mmorejon/website-service
Add website microservice
2 parents 2995f73 + 716c25d commit 6a986a8

27 files changed

+1114
-0
lines changed

.github/workflows/release.yml

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ env:
1010
MOVIES_IMAGE_NAME: "cinema-movies"
1111
SHOWTIMES_IMAGE_NAME: "cinema-showtimes"
1212
BOOKINGS_IMAGE_NAME: "cinema-bookings"
13+
WEBSITE_IMAGE_NAME: "cinema-website"
1314

1415
jobs:
1516
multi-registries:
@@ -43,6 +44,15 @@ jobs:
4344
tags: |
4445
mmorejon/${{ env.USERS_IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}
4546
ghcr.io/mmorejon/${{ env.USERS_IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}
47+
- name: Build and push website microservice
48+
uses: docker/build-push-action@v2
49+
with:
50+
push: true
51+
context: ./website/
52+
file: ./website/Dockerfile
53+
tags: |
54+
mmorejon/${{ env.WEBSITE_IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}
55+
ghcr.io/mmorejon/${{ env.WEBSITE_IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}
4656
- name: Build and push movies microservice
4757
uses: docker/build-push-action@v2
4858
with:

website/.dockerignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Git repo
2+
.git

website/.gitignore

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
###Go###
2+
3+
# Compiled Object files, Static and Dynamic libs (Shared Objects)
4+
*.o
5+
*.a
6+
*.so
7+
8+
# Folders
9+
_obj
10+
_test
11+
12+
# Architecture specific extensions/prefixes
13+
*.[568vq]
14+
[568vq].out
15+
16+
*.cgo1.go
17+
*.cgo2.c
18+
_cgo_defun.c
19+
_cgo_gotypes.go
20+
_cgo_export.*
21+
22+
_testmain.go
23+
24+
*.exe
25+
*.test
26+
*.prof

website/Dockerfile

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# golang alpine 1.15.6-alpine as base image
2+
FROM golang:1.15.6-alpine AS builder
3+
# create appuser.
4+
RUN adduser -D -g '' elf
5+
# create workspace
6+
WORKDIR /opt/web/
7+
COPY go.mod .
8+
COPY go.sum .
9+
# fetch dependancies
10+
RUN go mod download
11+
RUN go mod verify
12+
# copy the source code as the last step
13+
COPY . .
14+
# build binary
15+
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -a -installsuffix cgo -o /go/bin/cinema-website ./cmd/web
16+
17+
18+
# build a small image
19+
FROM alpine:3.12.3
20+
LABEL language="golang"
21+
LABEL org.opencontainers.image.source https://github.com/mmorejon/microservices-docker-go-mongodb
22+
# import the user and group files from the builder
23+
COPY --from=builder /etc/passwd /etc/passwd
24+
# create workspace
25+
WORKDIR /opt/web/
26+
# copy the static executable
27+
COPY --from=builder --chown=elf:1000 /go/bin/cinema-website cinema-website
28+
# copy static files
29+
COPY ./ui ui
30+
# use a non-root user
31+
USER elf
32+
# run app
33+
ENTRYPOINT ["./cinema-website"]

website/cmd/web/handlers.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"io/ioutil"
6+
"net/http"
7+
"path/filepath"
8+
"text/template"
9+
)
10+
11+
func (app *application) home(w http.ResponseWriter, r *http.Request) {
12+
13+
files := []string{
14+
"./ui/html/home.page.tmpl",
15+
"./ui/html/base.layout.tmpl",
16+
"./ui/html/footer.partial.tmpl",
17+
}
18+
19+
ts, err := template.ParseFiles(files...)
20+
if err != nil {
21+
app.errorLog.Println(err.Error())
22+
http.Error(w, "Internal Server Error", 500)
23+
return
24+
}
25+
26+
err = ts.Execute(w, nil)
27+
if err != nil {
28+
app.errorLog.Println(err.Error())
29+
http.Error(w, "Internal Server Error", 500)
30+
}
31+
}
32+
33+
func (app *application) getAPIContent(url string, templateData interface{}) error {
34+
resp, err := http.Get(url)
35+
if err != nil {
36+
return err
37+
}
38+
defer resp.Body.Close()
39+
40+
bodyBytes, err := ioutil.ReadAll(resp.Body)
41+
if err != nil {
42+
return err
43+
}
44+
45+
json.Unmarshal(bodyBytes, templateData)
46+
return nil
47+
}
48+
49+
func (app *application) static(dir string) http.Handler {
50+
dirCleaned := filepath.Clean(dir)
51+
return http.StripPrefix("/static/", http.FileServer(http.Dir(dirCleaned)))
52+
}

website/cmd/web/handlers_bookings.go

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"text/template"
7+
8+
"github.com/gorilla/mux"
9+
"github.com/mmorejon/microservices-docker-go-mongodb/bookings/pkg/models"
10+
modelsShowTime "github.com/mmorejon/microservices-docker-go-mongodb/showtimes/pkg/models"
11+
modelsUser "github.com/mmorejon/microservices-docker-go-mongodb/users/pkg/models"
12+
)
13+
14+
type bookingTemplateData struct {
15+
Booking models.Booking
16+
Bookings []models.Booking
17+
BookingData bookingData
18+
BookingsData []bookingData
19+
}
20+
21+
type bookingData struct {
22+
ID string
23+
UserFullName string
24+
ShowTimeDate string
25+
}
26+
27+
func (app *application) loadBookingData(btd *bookingTemplateData, isList bool) {
28+
// Clean booking data
29+
btd.BookingsData = []bookingData{}
30+
btd.BookingData = bookingData{}
31+
32+
// Load booking data
33+
if isList {
34+
for _, b := range btd.Bookings {
35+
// Load user data
36+
userURL := fmt.Sprintf("%s/%s", app.apis.users, b.UserID)
37+
var user modelsUser.User
38+
err := app.getAPIContent(userURL, &user)
39+
if err != nil {
40+
app.errorLog.Println(err.Error())
41+
}
42+
43+
// Load showtime data
44+
showtimeURL := fmt.Sprintf("%s/%s", app.apis.showtimes, b.ShowtimeID)
45+
var showtime modelsShowTime.ShowTime
46+
err = app.getAPIContent(showtimeURL, &showtime)
47+
if err != nil {
48+
app.errorLog.Println(err.Error())
49+
}
50+
51+
bookingData := bookingData{
52+
ID: b.ID.Hex(),
53+
UserFullName: fmt.Sprintf("%s %s", user.Name, user.LastName),
54+
ShowTimeDate: showtime.Date,
55+
}
56+
btd.BookingsData = append(btd.BookingsData, bookingData)
57+
app.infoLog.Println(b.UserID)
58+
}
59+
} else {
60+
b := btd.Booking
61+
62+
// Load user data
63+
userURL := fmt.Sprintf("%s/%s", app.apis.users, b.UserID)
64+
var user modelsUser.User
65+
err := app.getAPIContent(userURL, &user)
66+
if err != nil {
67+
app.errorLog.Println(err.Error())
68+
}
69+
70+
// Load showtime data
71+
showtimeURL := fmt.Sprintf("%s/%s", app.apis.showtimes, b.ShowtimeID)
72+
var showtime modelsShowTime.ShowTime
73+
74+
err = app.getAPIContent(showtimeURL, &showtime)
75+
if err != nil {
76+
app.errorLog.Println(err.Error())
77+
}
78+
79+
btd.BookingData = bookingData{
80+
ID: b.ID.Hex(),
81+
UserFullName: fmt.Sprintf("%s %s", user.Name, user.LastName),
82+
ShowTimeDate: showtime.Date,
83+
}
84+
}
85+
}
86+
87+
func (app *application) bookingsList(w http.ResponseWriter, r *http.Request) {
88+
89+
// Get bookings list from API
90+
var td bookingTemplateData
91+
app.infoLog.Println("Calling bookings API...")
92+
93+
err := app.getAPIContent(app.apis.bookings, &td.Bookings)
94+
if err != nil {
95+
app.errorLog.Println(err.Error())
96+
}
97+
app.infoLog.Println(td.Bookings)
98+
app.infoLog.Println(td)
99+
100+
app.loadBookingData(&td, true)
101+
102+
// Load template files
103+
files := []string{
104+
"./ui/html/bookings/list.page.tmpl",
105+
"./ui/html/base.layout.tmpl",
106+
"./ui/html/footer.partial.tmpl",
107+
}
108+
109+
ts, err := template.ParseFiles(files...)
110+
if err != nil {
111+
app.errorLog.Println(err.Error())
112+
http.Error(w, "Internal Server Error", 500)
113+
return
114+
}
115+
116+
err = ts.Execute(w, td)
117+
if err != nil {
118+
app.errorLog.Println(err.Error())
119+
http.Error(w, "Internal Server Error", 500)
120+
}
121+
}
122+
123+
func (app *application) bookingsView(w http.ResponseWriter, r *http.Request) {
124+
// Get id from incoming url
125+
vars := mux.Vars(r)
126+
bookingID := vars["id"]
127+
128+
// Get bookings list from API
129+
var td bookingTemplateData
130+
app.infoLog.Println("Calling bookings API...")
131+
url := fmt.Sprintf("%s/%s", app.apis.bookings, bookingID)
132+
133+
err := app.getAPIContent(url, &td.Booking)
134+
if err != nil {
135+
app.errorLog.Println(err.Error())
136+
}
137+
app.infoLog.Println(td.Booking)
138+
app.infoLog.Println(url)
139+
140+
app.loadBookingData(&td, false)
141+
142+
// Load template files
143+
files := []string{
144+
"./ui/html/bookings/view.page.tmpl",
145+
"./ui/html/base.layout.tmpl",
146+
"./ui/html/footer.partial.tmpl",
147+
}
148+
149+
ts, err := template.ParseFiles(files...)
150+
if err != nil {
151+
app.errorLog.Println(err.Error())
152+
http.Error(w, "Internal Server Error", 500)
153+
return
154+
}
155+
156+
err = ts.Execute(w, td)
157+
if err != nil {
158+
app.errorLog.Println(err.Error())
159+
http.Error(w, "Internal Server Error", 500)
160+
}
161+
}

website/cmd/web/handlers_movies.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"net/http"
8+
"text/template"
9+
10+
"github.com/gorilla/mux"
11+
"github.com/mmorejon/microservices-docker-go-mongodb/movies/pkg/models"
12+
)
13+
14+
type movieTemplateData struct {
15+
Movie models.Movie
16+
Movies []models.Movie
17+
}
18+
19+
func (app *application) moviesList(w http.ResponseWriter, r *http.Request) {
20+
21+
// Get movies list from API
22+
var mtd movieTemplateData
23+
app.infoLog.Println("Calling movies API...")
24+
app.getAPIContent(app.apis.movies, &mtd.Movies)
25+
app.infoLog.Println(mtd.Movies)
26+
27+
// Load template files
28+
files := []string{
29+
"./ui/html/movies/list.page.tmpl",
30+
"./ui/html/base.layout.tmpl",
31+
"./ui/html/footer.partial.tmpl",
32+
}
33+
34+
ts, err := template.ParseFiles(files...)
35+
if err != nil {
36+
app.errorLog.Println(err.Error())
37+
http.Error(w, "Internal Server Error", 500)
38+
return
39+
}
40+
41+
err = ts.Execute(w, mtd)
42+
if err != nil {
43+
app.errorLog.Println(err.Error())
44+
http.Error(w, "Internal Server Error", 500)
45+
}
46+
}
47+
48+
func (app *application) moviesView(w http.ResponseWriter, r *http.Request) {
49+
// Get id from incoming url
50+
vars := mux.Vars(r)
51+
movieID := vars["id"]
52+
53+
// Get movies list from API
54+
app.infoLog.Println("Calling movies API...")
55+
url := fmt.Sprintf("%s/%s", app.apis.movies, movieID)
56+
57+
resp, err := http.Get(url)
58+
if err != nil {
59+
fmt.Print(err.Error())
60+
}
61+
defer resp.Body.Close()
62+
63+
bodyBytes, err := ioutil.ReadAll(resp.Body)
64+
if err != nil {
65+
fmt.Print(err.Error())
66+
}
67+
68+
var td movieTemplateData
69+
json.Unmarshal(bodyBytes, &td.Movie)
70+
app.infoLog.Println(td.Movie)
71+
72+
// Load template files
73+
files := []string{
74+
"./ui/html/movies/view.page.tmpl",
75+
"./ui/html/base.layout.tmpl",
76+
"./ui/html/footer.partial.tmpl",
77+
}
78+
79+
ts, err := template.ParseFiles(files...)
80+
if err != nil {
81+
app.errorLog.Println(err.Error())
82+
http.Error(w, "Internal Server Error", 500)
83+
return
84+
}
85+
86+
err = ts.Execute(w, td.Movie)
87+
if err != nil {
88+
app.errorLog.Println(err.Error())
89+
http.Error(w, "Internal Server Error", 500)
90+
}
91+
}

0 commit comments

Comments
 (0)