From 97491997184fa572073157133b0cd719404f8e80 Mon Sep 17 00:00:00 2001 From: Quang Pham Date: Sat, 8 Feb 2025 10:11:09 +0100 Subject: [PATCH 1/3] chore: upgrade to jwt v5 --- go.mod | 4 ++-- go.sum | 3 ++- token/jwt_maker.go | 5 ++--- token/jwt_maker_test.go | 2 +- token/payload.go | 31 +++++++++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 8ff1ad77..6ae7ee9e 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,10 @@ go 1.22 require ( github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 - github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gin-gonic/gin v1.7.7 github.com/go-playground/validator/v10 v10.10.1 github.com/go-redis/redis/v8 v8.11.5 + github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang-migrate/migrate/v4 v4.15.1 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 @@ -17,6 +17,7 @@ require ( github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible github.com/o1egl/paseto v1.0.0 github.com/rakyll/statik v0.1.7 + github.com/rs/cors v1.10.1 github.com/rs/zerolog v1.28.0 github.com/spf13/viper v1.10.1 github.com/stretchr/testify v1.8.1 @@ -57,7 +58,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/rs/cors v1.10.1 // indirect github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index 439bd9d3..31ccefdd 100644 --- a/go.sum +++ b/go.sum @@ -303,7 +303,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= @@ -448,6 +447,8 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.15.1 h1:Sakl3Nm6+wQKq0Q62tpFMi5a503bgGhceo2icrgQ9vM= github.com/golang-migrate/migrate/v4 v4.15.1/go.mod h1:/CrBenUbcDqsW29jGTR/XFqCfVi/Y6mHXlooCcSOJMQ= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= diff --git a/token/jwt_maker.go b/token/jwt_maker.go index 02d7cdd7..c130dfae 100644 --- a/token/jwt_maker.go +++ b/token/jwt_maker.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/dgrijalva/jwt-go" + "github.com/golang-jwt/jwt/v5" ) const minSecretKeySize = 32 @@ -47,8 +47,7 @@ func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) { jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc) if err != nil { - verr, ok := err.(*jwt.ValidationError) - if ok && errors.Is(verr.Inner, ErrExpiredToken) { + if errors.Is(err, jwt.ErrTokenExpired) { return nil, ErrExpiredToken } return nil, ErrInvalidToken diff --git a/token/jwt_maker_test.go b/token/jwt_maker_test.go index 4fd8b179..5ab81777 100644 --- a/token/jwt_maker_test.go +++ b/token/jwt_maker_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/dgrijalva/jwt-go" + "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/techschool/simplebank/util" ) diff --git a/token/payload.go b/token/payload.go index dccae115..05ec6397 100644 --- a/token/payload.go +++ b/token/payload.go @@ -4,6 +4,7 @@ import ( "errors" "time" + "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" ) @@ -46,3 +47,33 @@ func (payload *Payload) Valid() error { } return nil } + +func (payload *Payload) GetExpirationTime() (*jwt.NumericDate, error) { + return &jwt.NumericDate{ + Time: payload.ExpiredAt, + }, nil +} + +func (payload *Payload) GetIssuedAt() (*jwt.NumericDate, error) { + return &jwt.NumericDate{ + Time: payload.IssuedAt, + }, nil +} + +func (payload *Payload) GetNotBefore() (*jwt.NumericDate, error) { + return &jwt.NumericDate{ + Time: payload.IssuedAt, + }, nil +} + +func (payload *Payload) GetIssuer() (string, error) { + return "", nil +} + +func (payload *Payload) GetSubject() (string, error) { + return "", nil +} + +func (payload *Payload) GetAudience() (jwt.ClaimStrings, error) { + return jwt.ClaimStrings{}, nil +} From 7b94234364872d031cdb911b65e28fff95c72f07 Mon Sep 17 00:00:00 2001 From: Quang Pham Date: Sat, 8 Feb 2025 10:30:23 +0100 Subject: [PATCH 2/3] chore: update README file --- README.md | 150 +++++++++++++++++++++++++++++------------------------- 1 file changed, 82 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 32fe1ab5..ae960011 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The programming language we will use to develop the service is Golang, but the c 1. In the 1st section, you will learn deeply about how to design the database, generate codes to talk to the DB in a consistent and reliable way using transactions, understand the DB isolation levels, and how to use it correctly in production. Besides the database, you will also learn how to use docker for local development, how to use Git to manage your codes, and how to use GitHub Action to run unit tests automatically. -2. In the 2nd section, you will learn how to build a set of RESTful HTTP APIs using Gin - one of the most popular Golang frameworks for building web services. This includes everything from loading app configs, mocking DB for more robust unit tests, handling errors, authenticating users, and securing the APIs with JWT and PASETO access tokens.  +2. In the 2nd section, you will learn how to build a set of RESTful HTTP APIs using Gin - one of the most popular Golang frameworks for building web services. This includes everything from loading app configs, mocking DB for more robust unit tests, handling errors, authenticating users, and securing the APIs with JWT and PASETO access tokens. 3. In the 3rd section, you will learn how to build your app with Docker and deploy it to a production Kubernetes cluster on AWS. The lectures are very detailed with a step-by-step guide, from how to build a minimal docker image, set up a free-tier AWS account, create a production database, store and retrieve production secrets, create a Kubernetes cluster with EKS, use GitHub Action to automatically build and deploy the image to the EKS cluster, buy a domain name and route the traffics to the service, secure the connection with HTTPS and auto-renew TLS certificate from Let's Encrypt. @@ -30,7 +30,7 @@ The programming language we will use to develop the service is Golang, but the c This course is designed with a lot of details, so that everyone, even with very little programming experience can understand and do it by themselves. I strongly believe that after the course, you would be able to work much more confidently and effectively on your projects. -## Course videos +## Backend course videos (Golang) - Lecture #0: [Setup development environment on Windows: WSL2 + Go + VSCode + Docker + Make + Sqlc](https://www.youtube.com/watch?v=TtCfDXfSw_0&list=PLy_6D98if3ULEtXtNSY_2qN21VCKgoQAE) @@ -124,7 +124,21 @@ This course is designed with a lot of details, so that everyone, even with very - Lecture #71: [Grant AWS EKS cluster access to Postgres and Redis using security group](https://www.youtube.com/watch?v=pPXYu6QQGE8&list=PLy_6D98if3ULEtXtNSY_2qN21VCKgoQAE) - Lecture #72: [Deploy gRPC + HTTP server to AWS EKS cluster](https://www.youtube.com/watch?v=Pd7aeh014nU&list=PLy_6D98if3ULEtXtNSY_2qN21VCKgoQAE) - Lecture #73: [Don't lose money on AWS](https://www.youtube.com/watch?v=VEf7IpUn6BQ&list=PLy_6D98if3ULEtXtNSY_2qN21VCKgoQAE) -- Lecture #74: [Go 1.22 fixed the most common for-loop trap](https://www.youtube.com/watch?v=rIHO9TqJtQQ&list=PLy_6D98if3ULEtXtNSY_2qN21VCKgoQAE) +- Lecture #74: [Graceful shutdown gRPC/HTTP servers and async worker](https://www.youtube.com/watch?v=TdB2W8l4dHw&list=PLy_6D98if3ULEtXtNSY_2qN21VCKgoQAE) +- Lecture #75: [Go 1.22 fixed the most common for-loop trap](https://www.youtube.com/watch?v=rIHO9TqJtQQ&list=PLy_6D98if3ULEtXtNSY_2qN21VCKgoQAE) +- Lecture #76: [Setup CORS policy with Go and VueJS](https://www.youtube.com/watch?v=hOz4f4SdArc&list=PLy_6D98if3ULEtXtNSY_2qN21VCKgoQAE) + +## Frontend course videos (Vue.JS) + +- Lecture #1: [Build reactive web app with VueJS](https://www.youtube.com/watch?v=fRGgDBCWQJg&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) +- Lecture #2: [Introduction to Vue router and Vue component](https://www.youtube.com/watch?v=4rv484TofFA&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) +- Lecture #3: [Build a login form with VueJS, PrimeVue and PrimeFlex](https://www.youtube.com/watch?v=K7p7QeUkGpE&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) +- Lecture #4: [Vue reactivity with v-model and computed](https://www.youtube.com/watch?v=hZmmcvGsyyk&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) +- Lecture #5: [Send HTTP request from Vue to backend API](https://www.youtube.com/watch?v=vkAhInOcWsw&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) +- Lecture #6: [Setup CORS policy with Go and VueJS](https://www.youtube.com/watch?v=hOz4f4SdArc&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) +- Lecture #7: [How to store auth state in Vue.JS](https://www.youtube.com/watch?v=A0YSEZPxKU0&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) +- Lecture #8: [Use Vue props to pass values to child component](https://www.youtube.com/watch?v=-mJzUkpKSEg&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) +- Lecture #9: [Logout user with Vue emit](https://www.youtube.com/watch?v=Jp09xJgpU-4&list=PLy_6D98if3UI3rsFRTHM1LMtVprYMp-GT) ## Simple bank service @@ -144,87 +158,87 @@ The service that we’re going to build is a simple bank. It will provide APIs f - [Homebrew](https://brew.sh/) - [Migrate](https://github.com/golang-migrate/migrate/tree/master/cmd/migrate) - ```bash - brew install golang-migrate - ``` + ```bash + brew install golang-migrate + ``` - [DB Docs](https://dbdocs.io/docs) - ```bash - npm install -g dbdocs - dbdocs login - ``` + ```bash + npm install -g dbdocs + dbdocs login + ``` - [DBML CLI](https://www.dbml.org/cli/#installation) - ```bash - npm install -g @dbml/cli - dbml2sql --version - ``` + ```bash + npm install -g @dbml/cli + dbml2sql --version + ``` - [Sqlc](https://github.com/kyleconroy/sqlc#installation) - ```bash - brew install sqlc - ``` + ```bash + brew install sqlc + ``` - [Gomock](https://github.com/golang/mock) - ``` bash - go install github.com/golang/mock/mockgen@v1.6.0 - ``` + ```bash + go install github.com/golang/mock/mockgen@v1.6.0 + ``` ### Setup infrastructure - Create the bank-network - ``` bash - make network - ``` + ```bash + make network + ``` - Start postgres container: - ```bash - make postgres - ``` + ```bash + make postgres + ``` - Create simple_bank database: - ```bash - make createdb - ``` + ```bash + make createdb + ``` - Run db migration up all versions: - ```bash - make migrateup - ``` + ```bash + make migrateup + ``` - Run db migration up 1 version: - ```bash - make migrateup1 - ``` + ```bash + make migrateup1 + ``` - Run db migration down all versions: - ```bash - make migratedown - ``` + ```bash + make migratedown + ``` - Run db migration down 1 version: - ```bash - make migratedown1 - ``` + ```bash + make migratedown1 + ``` ### Documentation - Generate DB documentation: - ```bash - make db_docs - ``` + ```bash + make db_docs + ``` - Access the DB documentation at [this address](https://dbdocs.io/techschool.guru/simple_bank). Password: `secret` @@ -232,52 +246,52 @@ The service that we’re going to build is a simple bank. It will provide APIs f - Generate schema SQL file with DBML: - ```bash - make db_schema - ``` + ```bash + make db_schema + ``` - Generate SQL CRUD with sqlc: - ```bash - make sqlc - ``` + ```bash + make sqlc + ``` - Generate DB mock with gomock: - ```bash - make mock - ``` + ```bash + make mock + ``` - Create a new db migration: - ```bash - make new_migration name= - ``` + ```bash + make new_migration name= + ``` ### How to run - Run server: - ```bash - make server - ``` + ```bash + make server + ``` - Run test: - ```bash - make test - ``` + ```bash + make test + ``` ## Deploy to kubernetes cluster - [Install nginx ingress controller](https://kubernetes.github.io/ingress-nginx/deploy/#aws): - ```bash - kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.48.1/deploy/static/provider/aws/deploy.yaml - ``` + ```bash + kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.48.1/deploy/static/provider/aws/deploy.yaml + ``` - [Install cert-manager](https://cert-manager.io/docs/installation/kubernetes/): - ```bash - kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.0/cert-manager.yaml - ``` + ```bash + kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.0/cert-manager.yaml + ``` From e67e399734e3eeaa25f1d5b93228c497e72d7792 Mon Sep 17 00:00:00 2001 From: Quang Pham Date: Sat, 8 Feb 2025 10:32:46 +0100 Subject: [PATCH 3/3] chore: update github workflows --- .github/workflows/deploy.yml | 91 ++++++++++++++++++------------------ .github/workflows/test.yml | 46 +++++++++--------- 2 files changed, 67 insertions(+), 70 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 949d7a78..a887662e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,56 +2,55 @@ name: Deploy to production on: push: - branches: [ master ] + branches: [release] jobs: - deploy: name: Build image runs-on: ubuntu-latest steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Install kubectl - uses: azure/setup-kubectl@v1 - with: - version: 'v1.21.3' - id: install - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-west-1 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - - name: Load secrets and save to app.env - run: aws secretsmanager get-secret-value --secret-id simple_bank --query SecretString --output text | jq -r 'to_entries|map("\(.key)=\(.value)")|.[]' > app.env - - - name: Build, tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: simplebank - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -t $ECR_REGISTRY/$ECR_REPOSITORY:latest . - docker push -a $ECR_REGISTRY/$ECR_REPOSITORY - - - name: Update kube config - run: aws eks update-kubeconfig --name simple-bank-eks --region eu-west-1 - - - name: Deploy image to Amazon EKS - run: | - kubectl apply -f eks/aws-auth.yaml - kubectl apply -f eks/deployment.yaml - kubectl apply -f eks/service.yaml - kubectl apply -f eks/issuer.yaml - kubectl apply -f eks/ingress-nginx.yaml - kubectl apply -f eks/ingress-http.yaml - kubectl apply -f eks/ingress-grpc.yaml + - name: Check out code + uses: actions/checkout@v2 + + - name: Install kubectl + uses: azure/setup-kubectl@v1 + with: + version: "v1.21.3" + id: install + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-1 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Load secrets and save to app.env + run: aws secretsmanager get-secret-value --secret-id simple_bank --query SecretString --output text | jq -r 'to_entries|map("\(.key)=\(.value)")|.[]' > app.env + + - name: Build, tag, and push image to Amazon ECR + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: simplebank + IMAGE_TAG: ${{ github.sha }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -t $ECR_REGISTRY/$ECR_REPOSITORY:latest . + docker push -a $ECR_REGISTRY/$ECR_REPOSITORY + + - name: Update kube config + run: aws eks update-kubeconfig --name simple-bank-eks --region eu-west-1 + + - name: Deploy image to Amazon EKS + run: | + kubectl apply -f eks/aws-auth.yaml + kubectl apply -f eks/deployment.yaml + kubectl apply -f eks/service.yaml + kubectl apply -f eks/issuer.yaml + kubectl apply -f eks/ingress-nginx.yaml + kubectl apply -f eks/ingress-http.yaml + kubectl apply -f eks/ingress-grpc.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7ecf9b5f..3472969d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,12 +2,11 @@ name: Run unit tests on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] jobs: - test: name: Test runs-on: ubuntu-latest @@ -28,24 +27,23 @@ jobs: --health-retries 5 steps: - - - name: Set up Go 1.x - uses: actions/setup-go@v2 - with: - go-version: ^1.22 - id: go - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Install golang-migrate - run: | - curl -L https://github.com/golang-migrate/migrate/releases/download/v4.14.1/migrate.linux-amd64.tar.gz | tar xvz - sudo mv migrate.linux-amd64 /usr/bin/migrate - which migrate - - - name: Run migrations - run: make migrateup - - - name: Test - run: make test + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.22 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Install golang-migrate + run: | + curl -L https://github.com/golang-migrate/migrate/releases/download/v4.14.1/migrate.linux-amd64.tar.gz | tar xvz + sudo mv migrate.linux-amd64 /usr/bin/migrate + which migrate + + - name: Run migrations + run: make migrateup + + - name: Test + run: make test