Skip to content

Commit

Permalink
feat: add go-cache and cron basic configuration (#8)
Browse files Browse the repository at this point in the history
* Add logging and colorize output

* Update config file name and instructions

* Add server start output and refactor print function

* Add local server URL to server startup message

* Add go-cache and cron basic configuration
  • Loading branch information
funnyzak authored Feb 16, 2024
1 parent dd3b11c commit 2208656
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 34 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Go Gin

Gin gonic starter with zerolog, viper, gorm, jwt basic setup.
Gin gonic starter with zerolog, viper, gorm, jwt basic, go-cache, cron basic configuration.

[![Build Status][build-status-image]][build-status]
[![license][license-image]][repository-url]
Expand All @@ -19,10 +19,10 @@ If you want to develop with this project, you can follow the steps below.
git clone git@github.com:funnyzak/go-gin.git && cd go-gin
```

2. Copy the `config.example.json` file to `config.json` and update the values.
2. Copy the `config.yaml.example` file to `config.yaml` and update the values.

```bash
cp config.example.json config.json
cp config.yaml.example config.yaml
```

3. Run the application.
Expand All @@ -38,7 +38,7 @@ If you want to develop with this project, you can follow the steps below.

### CI/CD

You can click `Use this template` to create a new repository based on this project. and add Secrets Keys: `DOCKER_USERNAME` and `DOCKER_PASSWORD` in the repository settings. And when you push the code, it will automatically build binary and docker image and push to the Docker Hub.
You can fork this repository and add Secrets Keys: `DOCKER_USERNAME` and `DOCKER_PASSWORD` in the repository settings. And when you push the code, it will automatically build binary and docker image and push to the Docker Hub.

## Structure

Expand Down
49 changes: 43 additions & 6 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ package main

import (
"context"
"fmt"
"go-gin/cmd/srv/controller"
"go-gin/pkg/utils"
"go-gin/service/singleton"

"github.com/ory/graceful"
flag "github.com/spf13/pflag"
)

type CliParam struct {
Version bool // Show version
ConfigName string // Config file name
Port uint // Server port
}
Expand All @@ -18,17 +21,25 @@ var (
cliParam CliParam
)

func main() {
func init() {
flag.CommandLine.ParseErrorsWhitelist.UnknownFlags = true
flag.BoolVarP(&cliParam.Version, "version", "v", false, "show version")
flag.StringVarP(&cliParam.ConfigName, "config", "c", "config", "config file name")
flag.UintVarP(&cliParam.Port, "port", "p", 0, "server port")
flag.Parse()
flag.Lookup("config").NoOptDefVal = "config"

singleton.InitConfig(cliParam.ConfigName)
singleton.InitLog(singleton.Conf)
singleton.InitTimezoneAndCache()
singleton.InitDBFromPath(singleton.Conf.DBPath)
initService()
}

func main() {
if cliParam.Version {
fmt.Println(singleton.Version)
return
}

port := singleton.Conf.Server.Port
if cliParam.Port != 0 {
Expand All @@ -37,18 +48,44 @@ func main() {

srv := controller.ServerWeb(port)

startOutput := func() {
fmt.Println()
fmt.Println("Server is running with config:")
utils.PrintStructFieldsAndValues(singleton.Conf, "")

fmt.Println()
fmt.Println("Server is running at:")
fmt.Printf(" - %-7s: %s\n", "Local", utils.Colorize(utils.ColorGreen, fmt.Sprintf("http://127.0.0.1:%d", port)))
ipv4s, err := utils.GetIPv4NetworkIPs()
if ipv4s != nil && err == nil {
for _, ip := range ipv4s {
fmt.Printf(" - %-7s: %s\n", "Network", utils.Colorize(utils.ColorGreen, fmt.Sprintf("http://%s:%d", ip, port)))
}
}
fmt.Println()
}

if err := graceful.Graceful(func() error {
startOutput()
return srv.ListenAndServe()
}, func(c context.Context) error {
singleton.Log.Info().Msg("Graceful::START")
fmt.Print(utils.Colorize("Server is shutting down", utils.ColorRed))
srv.Shutdown(c)
return nil
}); err != nil {
singleton.Log.Err(err).Msg("Graceful::Error")
fmt.Println(utils.Colorize("Server is shutting down with error: %s", utils.ColorRed), err)
}

}

func initService() {
singleton.InitSingleton()
// Load all services in the singleton package
singleton.LoadSingleton()

if _, err := singleton.Cron.AddFunc("0 * * * * *", writeHello); err != nil {
panic(err)
}
}

func writeHello() {
singleton.Log.Info().Msg("Hello world, I am a cron task")
}
2 changes: 1 addition & 1 deletion cmd/srv/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func serveStatic(r *gin.Engine) {

// Load templates
func loadTemplates(r *gin.Engine) {
new_tmpl := template.New("").Funcs(mygin.FuncMap)
new_tmpl := template.New("").Funcs(gogin.FuncMap)
var err error
new_tmpl, err = new_tmpl.ParseFS(resource.TemplateFS, "template/**/*.html", "template/*.html")
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ site:
description: A simple web application using Go and Gin
base_url: http://localhost:8080

debug: true
debug: false

log:
level: debug
Expand All @@ -28,3 +28,4 @@ jwt:
refresh_token_expiration: 720 # minutes
access_token_cookie_name: go-gin-access
refresh_token_cookie_name: go-gin-refresh
location: Asia/Chongqing # Timezone
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/myesui/uuid v1.0.0 // indirect
github.com/ory/graceful v0.1.3 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/robfig/cron/v3 v3.0.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
github.com/ory/graceful v0.1.3 h1:FaeXcHZh168WzS+bqruqWEw/HgXWLdNv2nJ+fbhxbhc=
github.com/ory/graceful v0.1.3/go.mod h1:4zFz687IAF7oNHHiB586U4iL+/4aV09o/PYLE34t2bA=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
Expand All @@ -111,6 +113,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
Expand Down
1 change: 1 addition & 0 deletions internal/gconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ type Config struct {
AccessTokenCookieName string `mapstructure:"access_token_cookie_name"`
RefreshTokenCookieName string `mapstructure:"refresh_token_cookie_name"`
} `mapstructure:"jwt"`
Location string `mapstructure:"location"`
}
11 changes: 6 additions & 5 deletions pkg/mygin/template.go → internal/gogin/template.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package mygin
package gogin

import (
"fmt"
"go-gin/service/singleton"
"html/template"
"strconv"
"strings"
Expand All @@ -17,10 +18,10 @@ var FuncMap = template.FuncMap{
}
},
"tf": func(t time.Time) string {
return t.Format("01/02/2006 15:04:05")
return t.In(singleton.Loc).Format("01/02/2006 15:04:05")
},
"tsf": func(ts int64) string {
return time.Unix(int64(ts/1000), 0).Format("01/02/2006 15:04:05")
return time.Unix(int64(ts/1000), 0).In(singleton.Loc).Format("01/02/2006 15:04:05")
},
"text2html": func(text string) template.HTML {
text = strings.Replace(text, "\n", "</p><p>", -1)
Expand All @@ -36,7 +37,7 @@ var FuncMap = template.FuncMap{
return template.HTML(`<` + s + `>`) // #nosec
},
"stf": func(s uint64) string {
return time.Unix(int64(s), 0).Format("01/02/2006 15:04")
return time.Unix(int64(s), 0).In(singleton.Loc).Format("01/02/2006 15:04")
},
"sf": func(duration uint64) string {
return time.Duration(time.Duration(duration) * time.Second).String()
Expand Down Expand Up @@ -119,7 +120,7 @@ var FuncMap = template.FuncMap{
},
"dayBefore": func(i int) string {
year, month, day := time.Now().Date()
today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
today := time.Date(year, month, day, 0, 0, 0, 0, singleton.Loc)
return today.AddDate(0, 0, i-29).Format("01/02")
},
"className": func(percent float32) string {
Expand Down
27 changes: 17 additions & 10 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,31 +133,38 @@ func GetNetworkIPs() ([]string, error) {
return ips, nil
}

func PrintStructFieldsAndValues(s interface{}, title string) error {
func PrintStructFieldsAndValues(s interface{}, indent string) {
v := reflect.ValueOf(s)

if v.Kind() == reflect.Ptr {
v = v.Elem()
}

if v.Kind() != reflect.Struct {
return fmt.Errorf("PrintStructFieldsAndValues: %v is not a struct", v.Type())
fmt.Printf("%s%v is not a struct\n", indent, v.Type())
return
}

typeOfS := v.Type()

fmt.Println()
if title != "" {
fmt.Printf("%s\n", title)
}
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if field.CanInterface() {
fmt.Printf(" - %-20s: %v\n", typeOfS.Field(i).Name, Colorize(ColorGreen, fmt.Sprint(field.Interface())))
fmt.Printf("%s - %s: ", indent, typeOfS.Field(i).Name)

if field.Kind() == reflect.Struct {
fmt.Println()
PrintStructFieldsAndValues(field.Interface(), indent+" ")
} else if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
fmt.Println()
PrintStructFieldsAndValues(field.Interface(), indent+" ")
} else {
if field.CanInterface() {
fmt.Println(Colorize(ColorGreen, fmt.Sprint(field.Interface())))
} else {
fmt.Println()
}
}
}
fmt.Println()
return nil
}

func ParseBool(val string, defVal bool) bool {
Expand Down
22 changes: 22 additions & 0 deletions service/singleton/crontask.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package singleton

import (
"sync"

"github.com/robfig/cron/v3"
)

var (
Cron *cron.Cron
CronLock sync.RWMutex
)

func InitCronTask() {
Cron = cron.New(cron.WithSeconds(), cron.WithLocation(Loc))
}

func LoadCronTasks() {
InitCronTask()

Cron.Start()
}
31 changes: 24 additions & 7 deletions service/singleton/singleton.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package singleton
import (
"fmt"
"os"
"time"

"github.com/patrickmn/go-cache"
"github.com/rs/zerolog"
"github.com/spf13/viper"
"gorm.io/driver/sqlite"
"gorm.io/gorm"

Expand All @@ -17,22 +20,36 @@ import (
var Version = "0.0.1"

var (
Conf *gconfig.Config
Log *zerolog.Logger
DB *gorm.DB
ViperConf *viper.Viper // Viper config for the application
Conf *gconfig.Config // Global config for the application
Log *zerolog.Logger // Global logger for the application
DB *gorm.DB // Global db for the application
Cache *cache.Cache // Global cache for the application
Loc *time.Location // Global location for the application
)

func InitSingleton() {
// TOO: init db
func LoadSingleton() {
LoadCronTasks()
// TODO: Add your initialization code here, eg Service, Task, etc.
}

func InitTimezoneAndCache() {
var err error
Loc, err = time.LoadLocation(Conf.Location)
if err != nil {
panic(err)
}

Cache = cache.New(5*time.Minute, 10*time.Minute)
}

func InitConfig(name string) {
_config, err := utils.ReadViperConfig(name, "yaml", []string{".", "./config", "../"})
ViperConf, err := utils.ReadViperConfig(name, "yaml", []string{".", "./config", "../"})
if err != nil {
panic(fmt.Errorf("unable to read config: %s", err))
}

if err := _config.Unmarshal(&Conf); err != nil {
if err := ViperConf.Unmarshal(&Conf); err != nil {
panic(fmt.Errorf("unable to unmarshal config: %s", err))
}
}
Expand Down

0 comments on commit 2208656

Please sign in to comment.