26
loading...
This website collects cookies to deliver better user experience
npm start
command, we will build optimized frontend files for production, using command npm run build
inside of assets/
directory. This will create new directory assets/build/
along with all files needed for production. Now we also have to instruct our backend where to find these files to be able to serve them. That is simply done using command router.Use(static.Serve("/", static.LocalFile("./assets/build", true)))
. Of course, we want to do that only if server is started in prod
environment, so we need to slightly update few files.Parse()
function in internal/cli/cli.go
to return environment value as a string:func Parse() string {
flag.Usage = usage
env := flag.String("env", "dev", `Sets run environment. Possible values are "dev" and "prod"`)
flag.Parse()
logging.ConfigureLogger(*env)
if *env == "prod" {
logging.SetGinLogToFile()
}
return *env
}
Config
struct NewConfig()
function to be able to receive and set environment value:type Config struct {
Host string
Port string
DbHost string
DbPort string
DbName string
DbUser string
DbPassword string
JwtSecret string
Env string
}
func NewConfig(env string) Config {
...
return Config{
Host: host,
Port: port,
DbHost: dbHost,
DbPort: dbPort,
DbName: dbName,
DbUser: dbUser,
DbPassword: dbPassword,
JwtSecret: jwtSecret,
Env: env,
}
}
internal/cli/main.go
to receive env value form CLI, and send it to new configuration creation which will be used for starting server:func main() {
env := cli.Parse()
server.Start(conf.NewConfig(env))
}
package server
import (
"net/http"
"rgb/internal/conf"
"rgb/internal/store"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
)
func setRouter(cfg conf.Config) *gin.Engine {
// Creates default gin router with Logger and Recovery middleware already attached
router := gin.Default()
// Enables automatic redirection if the current route can't be matched but a
// handler for the path with (without) the trailing slash exists.
router.RedirectTrailingSlash = true
// Serve static files to frontend if server is started in production environment
if cfg.Env == "prod" {
router.Use(static.Serve("/", static.LocalFile("./assets/build", true)))
}
// Create API route group
api := router.Group("/api")
api.Use(customErrors)
{
api.POST("/signup", gin.Bind(store.User{}), signUp)
api.POST("/signin", gin.Bind(store.User{}), signIn)
}
authorized := api.Group("/")
authorized.Use(authorization)
{
authorized.GET("/posts", indexPosts)
authorized.POST("/posts", gin.Bind(store.Post{}), createPost)
authorized.PUT("/posts", gin.Bind(store.Post{}), updatePost)
authorized.DELETE("/posts/:id", deletePost)
}
router.NoRoute(func(ctx *gin.Context) { ctx.JSON(http.StatusNotFound, gin.H{}) })
return router
}
migrations/main.go
file. Just changestore.SetDBConnection(database.NewDBOptions(conf.NewConfig()))
store.SetDBConnection(database.NewDBOptions(conf.NewConfig("dev")))
Dockerfile, .dockerignore and docker-compose.yml
contents..dockerignore
file in project root directory:# This file
.dockerignore
# Git files
.git/
.gitignore
# VS Code config dir
.vscode/
# Docker configuration files
docker/
# Assets dependencies and built files
assets/build/
assets/node_modules/
# Log files
logs/
# Built binary
cmd/rgb/rgb
# ENV file
.env
# Readme file
README.md
docker/
with two files, Dockerfile and docker-compose.yml
. Content of Dockerfile
will be:FROM node:16 AS frontendBuilder
# set app work dir
WORKDIR /rgb
# copy assets files to the container
COPY assets/ .
# set assets/ as work dir to build frontend static files
WORKDIR /rgb/assets
RUN npm install
RUN npm run build
FROM golang:1.16.3 AS backendBuilder
# set app work dir
WORKDIR /go/src/rgb
# copy all files to the container
COPY . .
# build app executable
RUN CGO_ENABLED=0 GOOS=linux go build -o cmd/rgb/rgb cmd/rgb/main.go
# build migrations executable
RUN CGO_ENABLED=0 GOOS=linux go build -o migrations/migrations migrations/*.go
FROM alpine:3.14
# Create a group and user deploy
RUN addgroup -S deploy && adduser -S deploy -G deploy
ARG ROOT_DIR=/home/deploy/rgb
WORKDIR ${ROOT_DIR}
RUN chown deploy:deploy ${ROOT_DIR}
# copy static assets file from frontend build
COPY --from=frontendBuilder --chown=deploy:deploy /rgb/build ./assets/build
# copy app and migrations executables from backend builder
COPY --from=backendBuilder --chown=deploy:deploy /go/src/rgb/migrations/migrations ./migrations/
COPY --from=backendBuilder --chown=deploy:deploy /go/src/rgb/cmd/rgb/rgb .
# set user deploy as current user
USER deploy
# start app
CMD [ "./rgb", "-env", "prod" ]
docker-compose.yml
is:version: "3"
services:
rgb:
image: kramat/rgb
env_file:
- ../.env
environment:
RGB_DB_HOST: db
depends_on:
- db
ports:
- ${RGB_PORT}:${RGB_PORT}
db:
image: postgres
environment:
POSTGRES_USER: ${RGB_DB_USER}
POSTGRES_PASSWORD: ${RGB_DB_PASSWORD}
POSTGRES_DB: ${RGB_DB_NAME}
ports:
- ${RGB_DB_PORT}:${RGB_DB_PORT}
volumes:
- postgresql:/var/lib/postgresql/rgb
- postgresql_data:/var/lib/postgresql/rgb/data
volumes:
postgresql: {}
postgresql_data: {}
postgres
image from official Docker containers repository:docker pull postgres
rgb
image. Inside of project root directory run (change DOCKER_ID
with your own docker ID):docker build -t DOCKER_ID/rgb -f docker/Dockerfile .
rgb
and db
containers with resources, run:cd docker/
docker-compose up -d
docker ps
. Finally, we need to run migrations. Open shell in rgb
container by running:docker-compose run --rm rgb sh
cd migrations/
./migrations init
./migrations up
localhost:8080
in your browser to check that everything is working as it should, which means you should be able to create account and add new post: