From 4523c645de6674cbe04a0f634ea31c60acf47051 Mon Sep 17 00:00:00 2001 From: hiifong Date: Mon, 31 Jul 2023 17:22:37 +0800 Subject: [PATCH] Update (#6) --- .air.toml | 44 +++++++++++++++++++++++ .gitignore | 3 ++ Makefile | 29 +++++++++++++++ cmd/cmd.go | 6 ++-- cmd/gen.go | 10 ++++-- cmd/web.go | 9 +++-- conf/tiktok.yml | 25 +++++++------ models/db/db.go | 54 ---------------------------- modules/config/config.go | 77 +++++++++++++--------------------------- modules/redis/redis.go | 19 +++++----- modules/web/route.go | 11 ------ routers/init.go | 58 +++++++++++++++++++++++------- 12 files changed, 182 insertions(+), 163 deletions(-) create mode 100644 .air.toml delete mode 100644 models/db/db.go diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..77f318c --- /dev/null +++ b/.air.toml @@ -0,0 +1,44 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = [] + bin = "./tmp/tiktok" + cmd = "go build -o ./tmp/tiktok ." + delay = 0 + exclude_dir = ["assets", "tmp", "vendor", "testdata", "apk", "docs"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + time = false + +[misc] + clean_on_exit = false + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/.gitignore b/.gitignore index f7ac156..13b055c 100644 --- a/.gitignore +++ b/.gitignore @@ -181,3 +181,6 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk + +# Air +tmp diff --git a/Makefile b/Makefile index e69de29..fc01afc 100644 --- a/Makefile +++ b/Makefile @@ -0,0 +1,29 @@ +GO ?= go +AIR ?= air + +.DEFAULT_GOAL := default + +TAGS ?= + +default: help + +.PHONY: build +build: ## build tiktok binary file + ${GO} build -o tiktok . + +.PHONY: watch +watch: ## live reload + ${AIR} server + +.PHONY: test +test: ## go test + ${GO} test -v $$(${GO} list ./... | grep -v /models/gen) + +.PHONY: clear +clear: ## clear project + -rm -rf ./tmp tiktok + + +.PHONY: help +help: Makefile ## print Makefile help information. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 } /^\$$\([0-9A-Za-z_-]+\):.*?##/ { gsub("_","-", $$1); printf " \033[36m%-45s\033[0m %s\n", tolower(substr($$1, 3, length($$1)-7)), $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' Makefile #$(MAKEFILE_LIST) diff --git a/cmd/cmd.go b/cmd/cmd.go index 1f4881c..19eec27 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -2,12 +2,12 @@ package cmd import "github.com/urfave/cli/v2" -func NewApp() *cli.App { - app := cli.NewApp() +func NewApp() *cli.App { //nolint:typecheck + app := cli.NewApp() //nolint:typecheck app.EnableBashCompletion = true // 子命令集 - subCmdWithConfig := []*cli.Command{ + subCmdWithConfig := []*cli.Command{ //nolint:typecheck CmdWeb, CmdGen, } diff --git a/cmd/gen.go b/cmd/gen.go index 8a98a43..59bceb5 100644 --- a/cmd/gen.go +++ b/cmd/gen.go @@ -2,6 +2,8 @@ package cmd import ( "biu-x.org/TikTok/models/user" + "biu-x.org/TikTok/modules/config" + "fmt" "github.com/urfave/cli/v2" "gorm.io/driver/mysql" "gorm.io/gen" @@ -15,20 +17,22 @@ type Querier interface { } // CmdGen 子命令 -var CmdGen = &cli.Command{ +var CmdGen = &cli.Command{ //nolint:typecheck Name: "gen", Usage: "gen gorm code", Description: `GEN: Friendly & Safer GORM powered by Code Generation.`, Action: runGen, } -func runGen(ctx *cli.Context) error { +func runGen(ctx *cli.Context) error { //nolint:typecheck g := gen.NewGenerator(gen.Config{ OutPath: "./models/gen", Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode }) - db, err := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local")) + url := fmt.Sprintf("%v:%v@(%v:%v)/%v", config.Get("mysql.username"), config.Get("mysql.password"), config.Get("mysql.host"), config.Get("mysql.port"), config.Get("mysql.database")) + + db, err := gorm.Open(mysql.Open(url + "?charset=utf8mb4&parseTime=True&loc=Local")) if err != nil { return err } diff --git a/cmd/web.go b/cmd/web.go index 422eadf..0539d06 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -4,18 +4,17 @@ import ( "biu-x.org/TikTok/modules/config" "biu-x.org/TikTok/routers" "fmt" - "github.com/spf13/viper" "github.com/urfave/cli/v2" ) // CmdWeb api 子命令 -var CmdWeb = &cli.Command{ +var CmdWeb = &cli.Command{ //nolint:typecheck Name: "server", Usage: "Start TikTok api server", Description: `Star TikTok api server`, Action: runWeb, Flags: []cli.Flag{ - &cli.StringFlag{ + &cli.StringFlag{ //nolint:typecheck Name: "port", Aliases: []string{"p"}, Value: "3000", @@ -24,9 +23,9 @@ var CmdWeb = &cli.Command{ }, } -func runWeb(ctx *cli.Context) error { +func runWeb(ctx *cli.Context) error { //nolint:typecheck config.InitConfig() - fmt.Println(viper.Get("server.port")) + fmt.Println(config.Get("redis")) routers.Init() return nil } diff --git a/conf/tiktok.yml b/conf/tiktok.yml index 720c7ae..90f7423 100644 --- a/conf/tiktok.yml +++ b/conf/tiktok.yml @@ -1,6 +1,7 @@ server: port: 8080 mode: prod + log: level: debug mode: @@ -8,17 +9,15 @@ log: - file path: ./log -database: - mysql: - host: 127.0.0.1 - port: 3306 - username: root - password: 123456 - database: tiktok +mysql: + host: 127.0.0.1 + port: 3306 + username: root + password: 123456 + database: tiktok -cache: - redis: - host: 127.0.0.1 - port: 6379 - password: 123456 - db: 0 +redis: + host: 127.0.0.1 + port: 6379 + password: 123456 + db: 0 diff --git a/models/db/db.go b/models/db/db.go deleted file mode 100644 index 7698037..0000000 --- a/models/db/db.go +++ /dev/null @@ -1,54 +0,0 @@ -package db - -type Connection struct { - Host string - Port int - UserName string - Password string - Database string -} - -type ConnectionOption func(*Connection) - -func NewConnection(options ...ConnectionOption) *Connection { - conn := &Connection{ - Host: "127.0.0.1", - Port: 3306, - UserName: "root", - } - - for _, option := range options { - option(conn) - } - return conn -} - -func WithHost(h string) ConnectionOption { - return func(c *Connection) { - c.Host = h - } -} - -func WithPort(p int) ConnectionOption { - return func(c *Connection) { - c.Port = p - } -} - -func WithUsername(n string) ConnectionOption { - return func(c *Connection) { - c.UserName = n - } -} - -func WithPassword(p string) ConnectionOption { - return func(c *Connection) { - c.Password = p - } -} - -func WithDatabase(d string) ConnectionOption { - return func(c *Connection) { - c.Database = d - } -} diff --git a/modules/config/config.go b/modules/config/config.go index b9657c0..bb7a9b1 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -1,58 +1,28 @@ package config import ( + "errors" "fmt" "github.com/fsnotify/fsnotify" "github.com/spf13/viper" ) -type Server struct { - Port int - Mode string -} - -type Log struct { - Level string - Mode []string - Path string -} - -type MySQL struct { - Host string - Port int - Username string - Password string - Database string -} - -type DataBase struct { - *MySQL -} - -type Redis struct { - Host string - Port int - Password string - DB int -} - -type Cache struct { - *Redis -} +var config *viper.Viper func InitConfig() { - viper.SetDefault("server", map[string]interface{}{ + config = viper.New() + config.SetDefault("server", map[string]interface{}{ "port": 8080, "mode": "prod", }) - viper.SetDefault("log", map[string]interface{}{ + config.SetDefault("log", map[string]interface{}{ "level": "debug", "mode": []string{"console", "file"}, "path": "./log", }) - viper.SetDefault("mysql", map[string]interface{}{ + config.SetDefault("mysql", map[string]interface{}{ "host": "127.0.0.1", "port": 3306, "username": "root", @@ -60,37 +30,38 @@ func InitConfig() { "database": "demo", }) - viper.SetDefault("redis", map[string]interface{}{ + config.SetDefault("redis", map[string]interface{}{ "host": "127.0.0.1", "port": 6379, "password": "123456", "database": 0, }) - //viper.SetConfigFile("./conf/tiktok.yml") - viper.SetConfigName("tiktok") - viper.AddConfigPath("./conf/") - viper.AddConfigPath("/etc/tiktok/") - viper.AddConfigPath("$HOME/.tiktok/") - viper.AddConfigPath("./") - viper.SetConfigType("yml") + config.SetConfigName("tiktok") + config.AddConfigPath("./conf/") + config.AddConfigPath("./") + config.AddConfigPath("$HOME/.tiktok/") + config.AddConfigPath("/etc/tiktok/") + config.SetConfigType("yml") - if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { + if err := config.ReadInConfig(); err != nil { + var configFileNotFoundError viper.ConfigFileNotFoundError + if errors.As(err, &configFileNotFoundError) { // 配置文件未找到错误 fmt.Println("Config file not found!") - } else { - // 配置文件被找到,但产生了另外的错误 - fmt.Printf("some error: %v\n", err) } } - viper.WatchConfig() - viper.WatchConfig() - viper.OnConfigChange(func(e fsnotify.Event) { + config.WatchConfig() + config.WatchConfig() + config.OnConfigChange(func(e fsnotify.Event) { // 配置文件发生变更之后会调用的回调函数 fmt.Println("Config file changed:", e.Name) }) - fmt.Println(viper.Get("database.mysql.host")) + fmt.Println(config.Get("mysql")) +} + +func Get(key string) interface{} { + return config.Get(key) } diff --git a/modules/redis/redis.go b/modules/redis/redis.go index 5f2c069..9303769 100644 --- a/modules/redis/redis.go +++ b/modules/redis/redis.go @@ -1,32 +1,35 @@ package redis import ( + "biu-x.org/TikTok/modules/config" "context" "fmt" "github.com/redis/go-redis/v9" + "strconv" "time" ) type Client struct { - *redis.Client - ctx context.Context + *redis.Client //nolint:typecheck + ctx context.Context } func NewRedisClient() *Client { - r := redis.NewClient(&redis.Options{ - Addr: "127.0.0.1:6379", - Password: "123456", - DB: 0, + db, _ := strconv.Atoi(fmt.Sprintf("%v", config.Get("redis.db"))) + r := redis.NewClient(&redis.Options{ //nolint:typecheck + Addr: fmt.Sprintf("%v:%v", config.Get("redis.host"), config.Get("redis.port")), + Password: fmt.Sprintf("%v", config.Get("redis.password")), + DB: db, }) ctx := context.Background() return &Client{Client: r, ctx: ctx} } -func (c Client) Set(key string, value interface{}) *redis.StatusCmd { +func (c Client) Set(key string, value interface{}) *redis.StatusCmd { //nolint:typecheck fmt.Printf("%#v\n", key) return c.Client.Set(c.ctx, key, value, 10*time.Second) } -func (c Client) Get(key interface{}) *redis.StringCmd { +func (c Client) Get(key interface{}) *redis.StringCmd { //nolint:typecheck return c.Client.Get(c.ctx, key.(string)) } diff --git a/modules/web/route.go b/modules/web/route.go index be38825..efb3895 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -1,12 +1 @@ package web - -import "github.com/go-chi/chi/v5" - -type Route struct { - chi.Router -} - -func NewRoute() *Route { - r := chi.NewRouter() - return &Route{r} -} diff --git a/routers/init.go b/routers/init.go index 9eebfc6..ec8d3eb 100644 --- a/routers/init.go +++ b/routers/init.go @@ -1,26 +1,58 @@ package routers import ( + "biu-x.org/TikTok/modules/config" v1 "biu-x.org/TikTok/routers/api/v1" - "errors" + "context" + "fmt" + "github.com/gin-gonic/gin" "log" + "net/http" + "os/signal" + "syscall" + "time" ) -var StartGinServeErr = errors.New("start Gin filed") - func Init() { - err := NewWeb() - if err != nil { - return + // Create context that listens for the interrupt signal from the OS. + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + var mode string + if config.Get("server.mode") != "prod" { + mode = gin.DebugMode + } else { + mode = gin.ReleaseMode } -} + gin.SetMode(mode) -func NewWeb() error { e := v1.NewAPI() - err := e.Run() - if err != nil { - log.Fatalf("%v: %v\n", StartGinServeErr, err) - return err + addr := ":" + fmt.Sprint(config.Get("server.port")) + srv := &http.Server{ + Addr: addr, + Handler: e, + } + + // Initializing the server in a goroutine so that + // it won't block the graceful shutdown handling below + go func() { + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("listen: %s\n", err) + } + }() + + // Listen for the interrupt signal. + <-ctx.Done() + + // Restore default behavior on the interrupt signal and notify user of shutdown. + stop() + log.Println("shutting down gracefully, press Ctrl+C again to force") + + // The context is used to inform the server it has 5 seconds to finish + // the request it is currently handling + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err := srv.Shutdown(ctx); err != nil { + log.Fatal("Server forced to shutdown: ", err) } - return nil }