Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: CI/CD
on:
push:
branches: [main]

permissions:
contents: read
packages: write

jobs:
test-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: frontend/go.mod

- name: Run frontend tests
working-directory: frontend
run: go test ./... -v

test-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: backend/go.mod

- name: Run backend tests
working-directory: backend
run: go test ./... -v

build-and-deploy:
runs-on: ubuntu-latest
needs: [test-frontend, test-backend]
steps:
- uses: actions/checkout@v4

- name: Build and push Backend
uses: docker/build-push-action@v5
with:
context: ./backend
push: true
tags: ghcr.io/${{ github.repository_owner }}/simple-fortune-backend:latest

- name: Build and push Frontend
uses: docker/build-push-action@v5
with:
context: ./frontend
push: true
tags: ghcr.io/${{ github.repository_owner }}/simple-fortune-frontend:latest

- name: Deploy
run: |
docker pull ghcr.io/${{ github.repository_owner }}/simple-fortune-backend:latest
docker pull ghcr.io/${{ github.repository_owner }}/simple-fortune-frontend:latest
echo "Deployed from GHCR"
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/simple-fortune-cookie.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM golang:1.22
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o main .
EXPOSE 9000
CMD ["./main"]
Binary file added backend/backend.exe
Binary file not shown.
2 changes: 1 addition & 1 deletion backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,5 +178,5 @@ func main() {
mux.Handle("/fortunes/", fortuneH)

err := http.ListenAndServe(":9000", mux)
fmt.Println("%v", err)
fmt.Printf("Result: %v\n", err)
}
17 changes: 17 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: '3.8'

services:
backend:
build:
context: ./backend
ports:
- "9000:9000"

frontend:
build:
context: ./frontend
ports:
- "8080:8080"
environment:
BACKEND_DNS: backend
BACKEND_PORT: 9000
7 changes: 7 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM golang:1.22
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o main .
EXPOSE 8080
CMD ["./main"]
Binary file added frontend/frontend.exe
Binary file not shown.
132 changes: 66 additions & 66 deletions frontend/main.go
Original file line number Diff line number Diff line change
@@ -1,105 +1,105 @@
package main

import (
"io"
"fmt"
"encoding/json"
"html/template"
"net/http"
"log"
"time"
"bytes"
"math/rand"
"bytes"
"encoding/json"
"fmt"
"html/template"
"io"
"log"
"math/rand"
"net/http"
"time"
)

var BACKEND_DNS=getEnv("BACKEND_DNS", "localhost")
var BACKEND_PORT=getEnv("BACKEND_PORT", "9000")
var BACKEND_DNS = getEnv("BACKEND_DNS", "localhost")
var BACKEND_PORT = getEnv("BACKEND_PORT", "9000")

type fortune struct {
ID string `json:"id" redis:"id"`
Message string `json:"message" redis:"message"`
}

type newFortune struct {
Message string `json:"message"`
Message string `json:"message"`
}

// use a custom client, because we don't do blocking operations wihout timeouts
var myClient = &http.Client{Timeout: 10 * time.Second}

func HealthzHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
io.WriteString(w, "healthy")
w.WriteHeader(http.StatusOK)
io.WriteString(w, "healthy")
}

func main() {

http.HandleFunc("/healthz", HealthzHandler)
http.HandleFunc("/healthz", HealthzHandler)

http.HandleFunc("/api/random", func (w http.ResponseWriter, r *http.Request) {
resp, err := myClient.Get(fmt.Sprintf("http://%s:%s/fortunes/random", BACKEND_DNS, BACKEND_PORT))
if err != nil {
log.Fatalln(err)
fmt.Fprint(w, err)
return
}
http.HandleFunc("/api/random", func(w http.ResponseWriter, r *http.Request) {
resp, err := myClient.Get(fmt.Sprintf("http://%s:%s/fortunes/random", BACKEND_DNS, BACKEND_PORT))
if err != nil {
log.Fatalln(err)
fmt.Fprint(w, err)
return
}

f := new(fortune)
json.NewDecoder(resp.Body).Decode(f)
f := new(fortune)
json.NewDecoder(resp.Body).Decode(f)

fmt.Fprint(w, f.Message)
return
})
fmt.Fprint(w, f.Message)
return
})

http.HandleFunc("/api/all", func (w http.ResponseWriter, r *http.Request) {
resp, err := myClient.Get(fmt.Sprintf("http://%s:%s/fortunes", BACKEND_DNS, BACKEND_PORT))
if err != nil {
log.Fatalln(err)
fmt.Fprint(w, err)
return
}
http.HandleFunc("/api/all", func(w http.ResponseWriter, r *http.Request) {
resp, err := myClient.Get(fmt.Sprintf("http://%s:%s/fortunes", BACKEND_DNS, BACKEND_PORT))
if err != nil {
log.Fatalln(err)
fmt.Fprint(w, err)
return
}

fortunes := new([]fortune)
json.NewDecoder(resp.Body).Decode(fortunes)
fortunes := new([]fortune)
json.NewDecoder(resp.Body).Decode(fortunes)

tmpl, err := template.ParseFiles("./templates/fortunes.html")
tmpl, err := template.ParseFiles("./templates/fortunes.html")

if err != nil {
log.Fatalln(err)
fmt.Fprint(w, err)
return
}
if err != nil {
log.Fatalln(err)
fmt.Fprint(w, err)
return
}

tmpl.Execute(w, fortunes)
return
})
tmpl.Execute(w, fortunes)
return
})

http.HandleFunc("/api/add", func (w http.ResponseWriter, r *http.Request) {
http.HandleFunc("/api/add", func(w http.ResponseWriter, r *http.Request) {

if r.Method != "POST" {
http.Error(w, "Use POST", http.StatusMethodNotAllowed)
return
}
if r.Method != "POST" {
http.Error(w, "Use POST", http.StatusMethodNotAllowed)
return
}

f := new(newFortune)
json.NewDecoder(r.Body).Decode(f)
f := new(newFortune)
json.NewDecoder(r.Body).Decode(f)

var postUrl = fmt.Sprintf("http://%s:%s/fortunes", BACKEND_DNS, BACKEND_PORT)
var jsonStr = []byte(fmt.Sprintf(`{"id": "%d", "message": "%s"}`, rand.Intn(10000), f.Message))
var postUrl = fmt.Sprintf("http://%s:%s/fortunes", BACKEND_DNS, BACKEND_PORT)
var jsonStr = []byte(fmt.Sprintf(`{"id": "%d", "message": "%s"}`, rand.Intn(10000), f.Message))

_, err := myClient.Post(postUrl, "application/json", bytes.NewBuffer(jsonStr))
if err != nil {
log.Fatalln(err)
fmt.Fprint(w, err)
return
}
_, err := myClient.Post(postUrl, "application/json", bytes.NewBuffer(jsonStr))
if err != nil {
log.Fatalln(err)
fmt.Fprint(w, err)
return
}

fmt.Fprint(w, "Cookie added!")
fmt.Fprint(w, "Cookie added!") // comment

return
})
return
})

http.Handle("/", http.FileServer(http.Dir("./static")))
err := http.ListenAndServe(":8080", nil)
fmt.Println("%v", err)
http.Handle("/", http.FileServer(http.Dir("./static")))
err := http.ListenAndServe(":8080", nil)
fmt.Printf("Result: %v\n", err)
}