Browse Source

Upgrade bootstrap, optimization, mysql pool/logger correct shutdown, todo

Vova Tkach 5 years ago
parent
commit
ee1d2f4ded

+ 12 - 2
engine/mysqlpool/mysqlpool.go

@@ -1,6 +1,8 @@
 package mysqlpool
 package mysqlpool
 
 
 import (
 import (
+	"errors"
+	"strings"
 	"sync"
 	"sync"
 
 
 	"golang-fave/engine/sqlw"
 	"golang-fave/engine/sqlw"
@@ -18,6 +20,7 @@ func New() *MySqlPool {
 }
 }
 
 
 func (this *MySqlPool) Get(key string) *sqlw.DB {
 func (this *MySqlPool) Get(key string) *sqlw.DB {
+	// TODO: return nil if context canceled!
 	this.Lock()
 	this.Lock()
 	defer this.Unlock()
 	defer this.Unlock()
 	if value, ok := this.connections[key]; ok == true {
 	if value, ok := this.connections[key]; ok == true {
@@ -32,12 +35,19 @@ func (this *MySqlPool) Set(key string, value *sqlw.DB) {
 	this.connections[key] = value
 	this.connections[key] = value
 }
 }
 
 
-func (this *MySqlPool) CloseAll() {
+func (this *MySqlPool) Close() error {
 	this.Lock()
 	this.Lock()
 	defer this.Unlock()
 	defer this.Unlock()
+	var errs []string
 	for _, c := range this.connections {
 	for _, c := range this.connections {
 		if c != nil {
 		if c != nil {
-			c.Close()
+			if err := c.Close(); err != nil {
+				errs = append(errs, err.Error())
+			}
 		}
 		}
 	}
 	}
+	if len(errs) > 0 {
+		return errors.New(strings.Join(errs, ", "))
+	}
+	return nil
 }
 }

+ 3 - 0
engine/sqlw/sqlw.go

@@ -1,5 +1,8 @@
 package sqlw
 package sqlw
 
 
+// TODO: rework all with context from request
+// https://golang.org/pkg/database/sql/
+
 import (
 import (
 	"database/sql"
 	"database/sql"
 	_ "github.com/go-sql-driver/mysql"
 	_ "github.com/go-sql-driver/mysql"

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module golang-fave
 require (
 require (
 	github.com/disintegration/imaging v1.6.1
 	github.com/disintegration/imaging v1.6.1
 	github.com/go-sql-driver/mysql v1.4.1
 	github.com/go-sql-driver/mysql v1.4.1
-	github.com/vladimirok5959/golang-server-bootstrap v1.0.8
+	github.com/vladimirok5959/golang-server-bootstrap v1.1.1
 	github.com/vladimirok5959/golang-server-resources v1.0.4
 	github.com/vladimirok5959/golang-server-resources v1.0.4
 	github.com/vladimirok5959/golang-server-sessions v1.0.6
 	github.com/vladimirok5959/golang-server-sessions v1.0.6
 	github.com/vladimirok5959/golang-server-static v1.0.2
 	github.com/vladimirok5959/golang-server-static v1.0.2

+ 4 - 28
go.sum

@@ -1,40 +1,16 @@
-github.com/disintegration/imaging v1.6.0 h1:nVPXRUUQ36Z7MNf0O77UzgnOb1mkMMor7lmJMJXc/mA=
-github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
 github.com/disintegration/imaging v1.6.1 h1:JnBbK6ECIZb1NsWIikP9pd8gIlTIRx7fuDNpU9fsxOE=
 github.com/disintegration/imaging v1.6.1 h1:JnBbK6ECIZb1NsWIikP9pd8gIlTIRx7fuDNpU9fsxOE=
 github.com/disintegration/imaging v1.6.1/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
 github.com/disintegration/imaging v1.6.1/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
 github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
 github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/vladimirok5959/golang-ctrlc v1.0.1 h1:ahZFNSw01qxKeIyO8PscOsE1Z10aJomprONkxwvZTn4=
-github.com/vladimirok5959/golang-ctrlc v1.0.1/go.mod h1:fIz52p0IMNedUfGBoT82xKAuFGfFMeQhYp+ne0FIKwU=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.4 h1:+Su6EAc5ZXntjEOkYKVZUJ8pRVrh7GIvqrkmeMS48Vo=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.4/go.mod h1:R5PGBuqlupYd0evIXoi81plWH/HpNQO2V/jHxZzg2y0=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.5 h1:+7MfVJCh/c5LcDV9xCWeXSE3KVDSP5EiQPddkccznV8=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.5/go.mod h1:R5PGBuqlupYd0evIXoi81plWH/HpNQO2V/jHxZzg2y0=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.6 h1:KN8hK5ej9W0037Dq4+kWNaB5PRcOcuNX5Hf2HEuSAeQ=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.6/go.mod h1:TxfWNSKqZetbZMaActi8hGcMrxLwjMNNQzCjIxzr9ZY=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.7 h1:k8Rfcl7AufIQGzrqsEmmnirI6bddArC8iO+wP07y7eA=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.7/go.mod h1:IDc3Spk1slgsj1bMYDpx/W6rtci60YDDA4y0uRNSWds=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.8 h1:jLvoOKPawnwjMITiLMJ0+2ihIhx1ChS9GCbt4Fm3t+M=
-github.com/vladimirok5959/golang-server-bootstrap v1.0.8/go.mod h1:IDc3Spk1slgsj1bMYDpx/W6rtci60YDDA4y0uRNSWds=
-github.com/vladimirok5959/golang-server-resources v1.0.2 h1:XwxFXyaOtfDGRmYp8P9q4P4gx4YK8NiYacpHe9V8Lck=
-github.com/vladimirok5959/golang-server-resources v1.0.2/go.mod h1:tsf2oAEf3E3ukiQSCO7dstl0IXbEXec68UUIiMWysBc=
-github.com/vladimirok5959/golang-server-resources v1.0.3 h1:kQLQZe2BGucs95J1A4J5pNYwscR9N65oitZib4uEw34=
-github.com/vladimirok5959/golang-server-resources v1.0.3/go.mod h1:g45afp447i47A9sdX15TMIWQzEOna8pNHwu1w0NWDwI=
+github.com/vladimirok5959/golang-ctrlc v1.0.2 h1:TVMWJv0WSdJqIzfnQ/Lhr9HmQ+i6H6kD6kIn8X+BNFs=
+github.com/vladimirok5959/golang-ctrlc v1.0.2/go.mod h1:fIz52p0IMNedUfGBoT82xKAuFGfFMeQhYp+ne0FIKwU=
+github.com/vladimirok5959/golang-server-bootstrap v1.1.1 h1:wELSsO0Jr7g/31qXCtLK2EM9pAa4FiKOgDBpUWL5d/Y=
+github.com/vladimirok5959/golang-server-bootstrap v1.1.1/go.mod h1:xDrCyDXa5+xdzptoPSfn7JOhLO1+Q95xTPlrdovyBSg=
 github.com/vladimirok5959/golang-server-resources v1.0.4 h1:myqowOUgF7DPdXWbNGBqxVsn9MfAi+FTHr8DvWkJFgM=
 github.com/vladimirok5959/golang-server-resources v1.0.4 h1:myqowOUgF7DPdXWbNGBqxVsn9MfAi+FTHr8DvWkJFgM=
 github.com/vladimirok5959/golang-server-resources v1.0.4/go.mod h1:TQ9jCCEpRE2ThB/JgxVE8k4yu3PY43qq339vsWLohSQ=
 github.com/vladimirok5959/golang-server-resources v1.0.4/go.mod h1:TQ9jCCEpRE2ThB/JgxVE8k4yu3PY43qq339vsWLohSQ=
-github.com/vladimirok5959/golang-server-sessions v1.0.2 h1:VG14aTnH8+vb81quhekkrJ1vTi57EU4USDX1UzDYIzw=
-github.com/vladimirok5959/golang-server-sessions v1.0.2/go.mod h1:W6eCEIltyTs5IUvN1DfzLk59z+M1031kr8bMFUiq8vU=
-github.com/vladimirok5959/golang-server-sessions v1.0.3 h1:P2HKDP1zY6H6XYrB8LzsW7boFLdWT+DtAIZE8kmtq9k=
-github.com/vladimirok5959/golang-server-sessions v1.0.3/go.mod h1:W6eCEIltyTs5IUvN1DfzLk59z+M1031kr8bMFUiq8vU=
-github.com/vladimirok5959/golang-server-sessions v1.0.4 h1:LsqgTDAjeSnPblzpof4GBTNV04Kv8T5FI1UvIX7WUTQ=
-github.com/vladimirok5959/golang-server-sessions v1.0.4/go.mod h1:W6eCEIltyTs5IUvN1DfzLk59z+M1031kr8bMFUiq8vU=
-github.com/vladimirok5959/golang-server-sessions v1.0.5 h1:/DgfYZ0cEfy4P9v2BEgScC89Mur25mSUQ2D5f/87Tb8=
-github.com/vladimirok5959/golang-server-sessions v1.0.5/go.mod h1:W6eCEIltyTs5IUvN1DfzLk59z+M1031kr8bMFUiq8vU=
 github.com/vladimirok5959/golang-server-sessions v1.0.6 h1:i5W1hnWZ63B6x3mTvPK8gsy74E5maQ81142ZlmpKbE4=
 github.com/vladimirok5959/golang-server-sessions v1.0.6 h1:i5W1hnWZ63B6x3mTvPK8gsy74E5maQ81142ZlmpKbE4=
 github.com/vladimirok5959/golang-server-sessions v1.0.6/go.mod h1:H99PB5Va7fw+Iljgne/f4ISsjg+V18Gq+9cNp+R6gaQ=
 github.com/vladimirok5959/golang-server-sessions v1.0.6/go.mod h1:H99PB5Va7fw+Iljgne/f4ISsjg+V18Gq+9cNp+R6gaQ=
-github.com/vladimirok5959/golang-server-static v1.0.0 h1:jmVNUCVF44+Am0euUMYFN4L3gdJYwm7aS8LFUmUAHJk=
-github.com/vladimirok5959/golang-server-static v1.0.0/go.mod h1:dxZsjCCpT65Z9dLP6p7RmR2rbgtYj6E4FSSSUqkNrsw=
 github.com/vladimirok5959/golang-server-static v1.0.2 h1:pNR+vy+z2g73ETyx+50WxM4x2vqku3J1QCcRp7DnORo=
 github.com/vladimirok5959/golang-server-static v1.0.2 h1:pNR+vy+z2g73ETyx+50WxM4x2vqku3J1QCcRp7DnORo=
 github.com/vladimirok5959/golang-server-static v1.0.2/go.mod h1:NN4wQ3UjLdeQA14S8REpAjtXZxMS1o8Fs7XxomDP1Q4=
 github.com/vladimirok5959/golang-server-static v1.0.2/go.mod h1:NN4wQ3UjLdeQA14S8REpAjtXZxMS1o8Fs7XxomDP1Q4=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI=

+ 201 - 142
main.go

@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
+	"strings"
 	"time"
 	"time"
 
 
 	"golang-fave/assets"
 	"golang-fave/assets"
@@ -37,32 +38,8 @@ func init() {
 }
 }
 
 
 func main() {
 func main() {
-	// Universal, params by env vars
-	if consts.ParamHost == "0.0.0.0" {
-		if os.Getenv("FAVE_HOST") != "" {
-			consts.ParamHost = os.Getenv("FAVE_HOST")
-		}
-	}
-	if consts.ParamPort == 8080 {
-		if os.Getenv("FAVE_PORT") != "" {
-			consts.ParamPort = utils.StrToInt(os.Getenv("FAVE_PORT"))
-		}
-	}
-	if consts.ParamWwwDir == "" {
-		if os.Getenv("FAVE_DIR") != "" {
-			consts.ParamWwwDir = os.Getenv("FAVE_DIR")
-		}
-	}
-	if consts.ParamDebug == false {
-		if os.Getenv("FAVE_DEBUG") == "true" {
-			consts.ParamDebug = true
-		}
-	}
-	if consts.ParamKeepAlive == false {
-		if os.Getenv("FAVE_KEEPALIVE") == "true" {
-			consts.ParamKeepAlive = true
-		}
-	}
+	// Params from env vars
+	read_env_params()
 
 
 	// Check www dir
 	// Check www dir
 	consts.ParamWwwDir = utils.FixPath(consts.ParamWwwDir)
 	consts.ParamWwwDir = utils.FixPath(consts.ParamWwwDir)
@@ -79,7 +56,6 @@ func main() {
 
 
 	// Init logger
 	// Init logger
 	lg := logger.New()
 	lg := logger.New()
-	defer lg.Close()
 
 
 	// Attach www dir to logger
 	// Attach www dir to logger
 	lg.SetWwwDir(consts.ParamWwwDir)
 	lg.SetWwwDir(consts.ParamWwwDir)
@@ -124,137 +100,220 @@ func main() {
 	// Init and start web server
 	// Init and start web server
 	server_address := fmt.Sprintf("%s:%d", consts.ParamHost, consts.ParamPort)
 	server_address := fmt.Sprintf("%s:%d", consts.ParamHost, consts.ParamPort)
 
 
-	bootstrap.Start(
-		&bootstrap.Opts{
-			Timeout: 8 * time.Second,
-			Handle:  lg.Handler,
-			Host:    server_address,
-			Path:    consts.AssetsPath,
-			Object:  mpool,
-			Cbserv: func(s *http.Server) {
-				s.SetKeepAlivesEnabled(consts.ParamKeepAlive)
-			},
-			Before: func(ctx context.Context, w http.ResponseWriter, r *http.Request, o interface{}) {
-				w.Header().Set("Server", "fave.pro/"+consts.ServerVersion)
-			},
-			After: func(ctx context.Context, w http.ResponseWriter, r *http.Request, o interface{}) {
-				// Schema
-				r.URL.Scheme = "http"
-
-				// Mounted assets
-				if res.Response(w, r, func(w http.ResponseWriter, r *http.Request, i *resource.OneResource) {
-					w.Header().Set("Cache-Control", "public, max-age=31536000")
-					if consts.ParamDebug && i.Path == "assets/cp/scripts.js" {
-						w.Write([]byte("window.fave_debug=true;"))
-					}
-				}, nil) {
-					return
-				}
+	// Server params
+	server_params := func(s *http.Server) {
+		s.SetKeepAlivesEnabled(consts.ParamKeepAlive)
+	}
 
 
-				// Host and port
-				host, port := utils.ExtractHostPort(r.Host, false)
-				curr_host := host
+	// Before callback
+	before := func(
+		ctx context.Context,
+		w http.ResponseWriter,
+		r *http.Request,
+		o *[]bootstrap.Iface,
+	) {
+		w.Header().Set("Server", "fave.pro/"+consts.ServerVersion)
+	}
 
 
-				// Domain bindings
-				doms := domains.New(consts.ParamWwwDir)
-				if mhost := doms.GetHost(host); mhost != "" {
-					curr_host = mhost
+	// After callback
+	after := func(
+		ctx context.Context,
+		w http.ResponseWriter,
+		r *http.Request,
+		o *[]bootstrap.Iface,
+	) {
+		// Schema
+		r.URL.Scheme = "http"
+
+		// Mounted assets
+		if res.Response(
+			w,
+			r,
+			func(
+				w http.ResponseWriter,
+				r *http.Request,
+				i *resource.OneResource,
+			) {
+				w.Header().Set("Cache-Control", "public, max-age=31536000")
+				if consts.ParamDebug && i.Path == "assets/cp/scripts.js" {
+					w.Write([]byte("window.fave_debug=true;"))
 				}
 				}
+			},
+			nil,
+		) {
+			return
+		}
 
 
-				vhost_dir := consts.ParamWwwDir + string(os.PathSeparator) + curr_host
-				if !utils.IsDirExists(vhost_dir) {
-					curr_host = "localhost"
-					vhost_dir = consts.ParamWwwDir + string(os.PathSeparator) + "localhost"
-				}
+		// Host and port
+		host, port := utils.ExtractHostPort(r.Host, false)
+		curr_host := host
 
 
-				// Check for minimal dir structure
-				vhost_dir_config := vhost_dir + string(os.PathSeparator) + "config"
-				vhost_dir_htdocs := vhost_dir + string(os.PathSeparator) + "htdocs"
-				vhost_dir_logs := vhost_dir + string(os.PathSeparator) + "logs"
-				vhost_dir_template := vhost_dir + string(os.PathSeparator) + "template"
-				vhost_dir_tmp := vhost_dir + string(os.PathSeparator) + "tmp"
-				if !utils.IsDirExists(vhost_dir_config) {
-					utils.SystemErrorPageEngine(w, errors.New("Folder "+vhost_dir_config+" is not found"))
-					return
-				}
-				if !utils.IsDirExists(vhost_dir_htdocs) {
-					utils.SystemErrorPageEngine(w, errors.New("Folder "+vhost_dir_htdocs+" is not found"))
-					return
-				}
-				if !utils.IsDirExists(vhost_dir_logs) {
-					utils.SystemErrorPageEngine(w, errors.New("Folder "+vhost_dir_logs+" is not found"))
-					return
-				}
-				if !utils.IsDirExists(vhost_dir_template) {
-					utils.SystemErrorPageEngine(w, errors.New("Folder "+vhost_dir_template+" is not found"))
-					return
-				}
-				if !utils.IsDirExists(vhost_dir_tmp) {
-					utils.SystemErrorPageEngine(w, errors.New("Folder "+vhost_dir_tmp+" is not found"))
-					return
-				}
+		// Domain bindings
+		doms := domains.New(consts.ParamWwwDir)
+		if mhost := doms.GetHost(host); mhost != "" {
+			curr_host = mhost
+		}
 
 
-				// Static files
-				if stat.Response(vhost_dir_htdocs, w, r, nil, nil) {
-					return
-				}
+		vhost_dir := consts.ParamWwwDir + string(os.PathSeparator) + curr_host
+		if !utils.IsDirExists(vhost_dir) {
+			curr_host = "localhost"
+			vhost_dir = consts.ParamWwwDir + string(os.PathSeparator) + "localhost"
+		}
 
 
-				// Robots.txt and styles.css from template dir
-				if ServeTemplateFile(w, r, "robots.txt", "", vhost_dir_template) {
-					return
-				}
-				if ServeTemplateFile(w, r, "styles.css", "assets/theme/", vhost_dir_template) {
-					return
-				}
-				if ServeTemplateFile(w, r, "scripts.js", "assets/theme/", vhost_dir_template) {
-					return
-				}
+		// Check for minimal dirs structure
+		vhost_dir_config := vhost_dir + string(os.PathSeparator) + "config"
+		vhost_dir_htdocs := vhost_dir + string(os.PathSeparator) + "htdocs"
+		vhost_dir_logs := vhost_dir + string(os.PathSeparator) + "logs"
+		vhost_dir_template := vhost_dir + string(os.PathSeparator) + "template"
+		vhost_dir_tmp := vhost_dir + string(os.PathSeparator) + "tmp"
+		if !utils.IsDirExists(vhost_dir_config) {
+			utils.SystemErrorPageEngine(
+				w,
+				errors.New("Folder "+vhost_dir_config+" is not found"),
+			)
+			return
+		}
+		if !utils.IsDirExists(vhost_dir_htdocs) {
+			utils.SystemErrorPageEngine(
+				w,
+				errors.New("Folder "+vhost_dir_htdocs+" is not found"),
+			)
+			return
+		}
+		if !utils.IsDirExists(vhost_dir_logs) {
+			utils.SystemErrorPageEngine(
+				w,
+				errors.New("Folder "+vhost_dir_logs+" is not found"),
+			)
+			return
+		}
+		if !utils.IsDirExists(vhost_dir_template) {
+			utils.SystemErrorPageEngine(
+				w,
+				errors.New("Folder "+vhost_dir_template+" is not found"),
+			)
+			return
+		}
+		if !utils.IsDirExists(vhost_dir_tmp) {
+			utils.SystemErrorPageEngine(
+				w,
+				errors.New("Folder "+vhost_dir_tmp+" is not found"),
+			)
+			return
+		}
 
 
-				// Session
-				sess := session.New(w, r, vhost_dir_tmp)
-				defer sess.Close()
+		// Static files
+		if stat.Response(vhost_dir_htdocs, w, r, nil, nil) {
+			return
+		}
 
 
-				// Convert
-				var mp *mysqlpool.MySqlPool
-				if mpool, ok := o.(*mysqlpool.MySqlPool); ok {
-					mp = mpool
-				}
+		// Robots.txt and styles.css from template dir
+		if ServeTemplateFile(w, r, "robots.txt", "", vhost_dir_template) {
+			return
+		}
+		if ServeTemplateFile(w, r, "styles.css", "assets/theme/", vhost_dir_template) {
+			return
+		}
+		if ServeTemplateFile(w, r, "scripts.js", "assets/theme/", vhost_dir_template) {
+			return
+		}
 
 
-				// Logic
-				if mp != nil {
-					if engine.Response(
-						mp,
-						sb,
-						lg,
-						mods,
-						w,
-						r,
-						sess,
-						cbs,
-						host,
-						port,
-						curr_host,
-						vhost_dir_config,
-						vhost_dir_htdocs,
-						vhost_dir_logs,
-						vhost_dir_template,
-						vhost_dir_tmp,
-					) {
-						return
-					}
-				}
+		// Session
+		sess := session.New(w, r, vhost_dir_tmp)
+		defer sess.Close()
+
+		// Convert
+		var lg *logger.Logger
+		if v, ok := (*o)[0].(*logger.Logger); ok {
+			lg = v
+		}
 
 
-				// Error 404
-				utils.SystemErrorPage404(w)
+		var mpool *mysqlpool.MySqlPool
+		if v, ok := (*o)[1].(*mysqlpool.MySqlPool); ok {
+			mpool = v
+		}
+		// ---
+
+		// Logic
+		if mpool != nil {
+			if engine.Response(
+				mpool,
+				sb,
+				lg,
+				mods,
+				w,
+				r,
+				sess,
+				cbs,
+				host,
+				port,
+				curr_host,
+				vhost_dir_config,
+				vhost_dir_htdocs,
+				vhost_dir_logs,
+				vhost_dir_template,
+				vhost_dir_tmp,
+			) {
+				return
+			}
+		}
+
+		// Error 404
+		utils.SystemErrorPage404(w)
+	}
+
+	// Shutdown callback
+	shutdown := func(
+		ctx context.Context,
+		o *[]bootstrap.Iface,
+	) error {
+		var errs []string
+
+		// ---
+		if mpool, ok := (*o)[1].(*mysqlpool.MySqlPool); ok {
+			if err := mpool.Close(); err != nil {
+				errs = append(errs, err.Error())
+			}
+		}
+
+		if lg, ok := (*o)[0].(*logger.Logger); ok {
+			lg.Close()
+		}
+		// ---
+
+		if len(errs) > 0 {
+			return errors.New("Shutdown callback: " + strings.Join(errs, ", "))
+		}
+
+		return nil
+	}
+
+	// Start server
+	bootstrap.Start(
+		&bootstrap.Opts{
+			Handle:   lg.Handler,
+			Host:     server_address,
+			Path:     consts.AssetsPath,
+			Cbserv:   server_params,
+			Before:   before,
+			After:    after,
+			Timeout:  8 * time.Second,
+			Shutdown: shutdown,
+			Objects: &[]bootstrap.Iface{
+				lg,
+				mpool,
 			},
 			},
 		},
 		},
 	)
 	)
-
-	// Close MySQL
-	mpool.CloseAll()
 }
 }
 
 
-func ServeTemplateFile(w http.ResponseWriter, r *http.Request, file string, path string, dir string) bool {
+func ServeTemplateFile(
+	w http.ResponseWriter,
+	r *http.Request,
+	file string,
+	path string,
+	dir string,
+) bool {
 	if r.URL.Path == "/"+path+file {
 	if r.URL.Path == "/"+path+file {
 		f, err := os.Open(dir + string(os.PathSeparator) + file)
 		f, err := os.Open(dir + string(os.PathSeparator) + file)
 		if err == nil {
 		if err == nil {

+ 36 - 0
params.go

@@ -0,0 +1,36 @@
+package main
+
+import (
+	"os"
+
+	"golang-fave/consts"
+	"golang-fave/utils"
+)
+
+func read_env_params() {
+	if consts.ParamHost == "0.0.0.0" {
+		if os.Getenv("FAVE_HOST") != "" {
+			consts.ParamHost = os.Getenv("FAVE_HOST")
+		}
+	}
+	if consts.ParamPort == 8080 {
+		if os.Getenv("FAVE_PORT") != "" {
+			consts.ParamPort = utils.StrToInt(os.Getenv("FAVE_PORT"))
+		}
+	}
+	if consts.ParamWwwDir == "" {
+		if os.Getenv("FAVE_DIR") != "" {
+			consts.ParamWwwDir = os.Getenv("FAVE_DIR")
+		}
+	}
+	if consts.ParamDebug == false {
+		if os.Getenv("FAVE_DEBUG") == "true" {
+			consts.ParamDebug = true
+		}
+	}
+	if consts.ParamKeepAlive == false {
+		if os.Getenv("FAVE_KEEPALIVE") == "true" {
+			consts.ParamKeepAlive = true
+		}
+	}
+}

+ 55 - 23
vendor/github.com/vladimirok5959/golang-ctrlc/ctrlc/ctrlc.go

@@ -2,10 +2,10 @@ package ctrlc
 
 
 import (
 import (
 	"context"
 	"context"
-	"flag"
 	"fmt"
 	"fmt"
 	"os"
 	"os"
 	"os/signal"
 	"os/signal"
+	"strings"
 	"syscall"
 	"syscall"
 	"time"
 	"time"
 )
 )
@@ -23,20 +23,14 @@ type Iface interface {
 type CallbackFunc func(ctx context.Context, shutdown context.CancelFunc) *[]Iface
 type CallbackFunc func(ctx context.Context, shutdown context.CancelFunc) *[]Iface
 
 
 func App(t time.Duration, f CallbackFunc) {
 func App(t time.Duration, f CallbackFunc) {
-	var ParamColor string
-	flag.StringVar(&ParamColor, "color", "auto", "color output (auto/always/never)")
-	flag.Parse()
-
-	useColors := !IS_WIN_PLATFORM && ParamColor == "always"
-
 	stop := make(chan os.Signal)
 	stop := make(chan os.Signal)
 	signal.Notify(stop, syscall.SIGTERM)
 	signal.Notify(stop, syscall.SIGTERM)
 	signal.Notify(stop, syscall.SIGINT)
 	signal.Notify(stop, syscall.SIGINT)
 
 
 	fmt.Printf(
 	fmt.Printf(
-		icon_start(useColors)+"%s\n",
+		icon_start(UseColors())+"%s\n",
 		cly(
 		cly(
-			useColors,
+			UseColors(),
 			fmt.Sprintf(
 			fmt.Sprintf(
 				"Application started (%d sec)",
 				"Application started (%d sec)",
 				t/time.Second,
 				t/time.Second,
@@ -50,9 +44,9 @@ func App(t time.Duration, f CallbackFunc) {
 	select {
 	select {
 	case <-sctx.Done():
 	case <-sctx.Done():
 		fmt.Printf(
 		fmt.Printf(
-			"\r"+icon_warn(useColors)+"%s\n",
+			"\r"+icon_warn(UseColors())+"%s\n",
 			cly(
 			cly(
-				useColors,
+				UseColors(),
 				fmt.Sprintf(
 				fmt.Sprintf(
 					"Shutting down (application) (%d sec)",
 					"Shutting down (application) (%d sec)",
 					t/time.Second,
 					t/time.Second,
@@ -63,9 +57,9 @@ func App(t time.Duration, f CallbackFunc) {
 		switch val {
 		switch val {
 		case syscall.SIGINT:
 		case syscall.SIGINT:
 			fmt.Printf(
 			fmt.Printf(
-				"\r"+icon_warn(useColors)+"%s\n",
+				"\r"+icon_warn(UseColors())+"%s\n",
 				cly(
 				cly(
-					useColors,
+					UseColors(),
 					fmt.Sprintf(
 					fmt.Sprintf(
 						"Shutting down (interrupt) (%d sec)",
 						"Shutting down (interrupt) (%d sec)",
 						t/time.Second,
 						t/time.Second,
@@ -74,9 +68,9 @@ func App(t time.Duration, f CallbackFunc) {
 			)
 			)
 		case syscall.SIGTERM:
 		case syscall.SIGTERM:
 			fmt.Printf(
 			fmt.Printf(
-				icon_warn(useColors)+"%s\n",
+				icon_warn(UseColors())+"%s\n",
 				cly(
 				cly(
-					useColors,
+					UseColors(),
 					fmt.Sprintf(
 					fmt.Sprintf(
 						"Shutting down (terminate) (%d sec)",
 						"Shutting down (terminate) (%d sec)",
 						t/time.Second,
 						t/time.Second,
@@ -85,9 +79,9 @@ func App(t time.Duration, f CallbackFunc) {
 			)
 			)
 		default:
 		default:
 			fmt.Printf(
 			fmt.Printf(
-				icon_warn(useColors)+"%s\n",
+				icon_warn(UseColors())+"%s\n",
 				cly(
 				cly(
-					useColors,
+					UseColors(),
 					fmt.Sprintf(
 					fmt.Sprintf(
 						"Shutting down (%d sec)",
 						"Shutting down (%d sec)",
 						t/time.Second,
 						t/time.Second,
@@ -105,9 +99,9 @@ func App(t time.Duration, f CallbackFunc) {
 		if err := iface.Shutdown(ctx); err != nil {
 		if err := iface.Shutdown(ctx); err != nil {
 			errors = true
 			errors = true
 			fmt.Printf(
 			fmt.Printf(
-				icon_hot(useColors)+"%s\n",
+				icon_hot(UseColors())+"%s\n",
 				clr(
 				clr(
-					useColors,
+					UseColors(),
 					fmt.Sprintf(
 					fmt.Sprintf(
 						"Shutdown error (%T): %s",
 						"Shutdown error (%T): %s",
 						iface,
 						iface,
@@ -121,9 +115,9 @@ func App(t time.Duration, f CallbackFunc) {
 
 
 	if errors {
 	if errors {
 		fmt.Printf(
 		fmt.Printf(
-			icon_mag(useColors)+"%s\n",
+			icon_mag(UseColors())+"%s\n",
 			cly(
 			cly(
-				useColors,
+				UseColors(),
 				fmt.Sprintf(
 				fmt.Sprintf(
 					"Application exited with errors (%d sec)",
 					"Application exited with errors (%d sec)",
 					t/time.Second,
 					t/time.Second,
@@ -133,9 +127,9 @@ func App(t time.Duration, f CallbackFunc) {
 		os.Exit(1)
 		os.Exit(1)
 	} else {
 	} else {
 		fmt.Printf(
 		fmt.Printf(
-			icon_sc(useColors)+"%s\n",
+			icon_sc(UseColors())+"%s\n",
 			clg(
 			clg(
-				useColors,
+				UseColors(),
 				fmt.Sprintf(
 				fmt.Sprintf(
 					"Application exited successfully (%d sec)",
 					"Application exited successfully (%d sec)",
 					t/time.Second,
 					t/time.Second,
@@ -144,3 +138,41 @@ func App(t time.Duration, f CallbackFunc) {
 		)
 		)
 	}
 	}
 }
 }
+
+func UseColors() bool {
+	useColors := strings.Contains(
+		fmt.Sprintf("%s", os.Args),
+		"--color=always",
+	)
+	if !useColors {
+		useColors = strings.Contains(
+			fmt.Sprintf("%s", os.Args),
+			"-color=always",
+		)
+	}
+	if !useColors {
+		useColors = strings.Contains(
+			fmt.Sprintf("%s", os.Args),
+			"color=always",
+		)
+	}
+	if !useColors {
+		useColors = strings.Contains(
+			fmt.Sprintf("%s", os.Args),
+			"--color always",
+		)
+	}
+	if !useColors {
+		useColors = strings.Contains(
+			fmt.Sprintf("%s", os.Args),
+			"-color always",
+		)
+	}
+	if !useColors {
+		useColors = strings.Contains(
+			fmt.Sprintf("%s", os.Args),
+			"color always",
+		)
+	}
+	return !IS_WIN_PLATFORM && useColors
+}

File diff suppressed because it is too large
+ 0 - 0
vendor/github.com/vladimirok5959/golang-server-bootstrap/bootstrap/bootstrap.css.go


+ 97 - 23
vendor/github.com/vladimirok5959/golang-server-bootstrap/bootstrap/bootstrap.go

@@ -2,6 +2,8 @@ package bootstrap
 
 
 import (
 import (
 	"context"
 	"context"
+	"crypto/md5"
+	"encoding/hex"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
@@ -10,19 +12,31 @@ import (
 	"github.com/vladimirok5959/golang-ctrlc/ctrlc"
 	"github.com/vladimirok5959/golang-ctrlc/ctrlc"
 )
 )
 
 
-type customHandler func(h http.Handler) http.Handler
-type callbackBeforeAfter func(ctx context.Context, w http.ResponseWriter, r *http.Request, o interface{})
-type callbackServer func(s *http.Server)
+type Handler func(h http.Handler) http.Handler
+
+type CBServer func(s *http.Server)
+
+type Iface interface{}
+
+type BeforeAfter func(
+	ctx context.Context,
+	w http.ResponseWriter,
+	r *http.Request,
+	o *[]Iface,
+)
+
+type ShutdownFunc func(ctx context.Context, o *[]Iface) error
 
 
 type Opts struct {
 type Opts struct {
-	Handle  customHandler
-	Host    string
-	Timeout time.Duration
-	Path    string
-	Before  callbackBeforeAfter
-	After   callbackBeforeAfter
-	Cbserv  callbackServer
-	Object  interface{}
+	Handle   Handler
+	Host     string
+	Path     string
+	Cbserv   CBServer
+	Before   BeforeAfter
+	After    BeforeAfter
+	Objects  *[]Iface
+	Timeout  time.Duration
+	Shutdown ShutdownFunc
 }
 }
 
 
 type bootstrap struct {
 type bootstrap struct {
@@ -34,36 +48,94 @@ func new(ctx context.Context, opts *Opts) *bootstrap {
 	return &bootstrap{ctx: ctx, opts: opts}
 	return &bootstrap{ctx: ctx, opts: opts}
 }
 }
 
 
+func etag(str string) string {
+	hasher := md5.New()
+	hasher.Write([]byte(str))
+	return hex.EncodeToString(hasher.Sum(nil))
+}
+
+func modified(p string, s int, v int64, w *http.ResponseWriter, r *http.Request) bool {
+	(*w).Header().Set("Content-Length", fmt.Sprintf("%d", s))
+	(*w).Header().Set("Cache-Control", "no-cache")
+
+	// Set: ETag
+	ehash := etag(fmt.Sprintf("%s-%d-%d", p, s, v))
+	(*w).Header().Set("ETag", fmt.Sprintf("%s", ehash))
+
+	// Set: Last-Modified
+	(*w).Header().Set(
+		"Last-Modified",
+		time.Unix(v, 0).In(time.FixedZone("GMT", 0)).Format("Wed, 01 Oct 2006 15:04:05 GMT"),
+	)
+
+	// Check: ETag
+	if cc := r.Header.Get("Cache-Control"); cc != "no-cache" {
+		if inm := r.Header.Get("If-None-Match"); inm == ehash {
+			(*w).WriteHeader(http.StatusNotModified)
+			return false
+		}
+	}
+
+	// Check: Last-Modified
+	if cc := r.Header.Get("Cache-Control"); cc != "no-cache" {
+		if ims := r.Header.Get("If-Modified-Since"); ims != "" {
+			if t, err := time.Parse("Wed, 01 Oct 2006 15:04:05 GMT", ims); err == nil {
+				if time.Unix(v, 0).In(time.FixedZone("GMT", 0)).Unix() <= t.In(time.FixedZone("GMT", 0)).Unix() {
+					(*w).WriteHeader(http.StatusNotModified)
+					return false
+				}
+			}
+		}
+	}
+
+	return true
+}
+
 func (this *bootstrap) handler(w http.ResponseWriter, r *http.Request) {
 func (this *bootstrap) handler(w http.ResponseWriter, r *http.Request) {
 	if this.opts.Before != nil {
 	if this.opts.Before != nil {
-		this.opts.Before(this.ctx, w, r, this.opts.Object)
+		this.opts.Before(this.ctx, w, r, this.opts.Objects)
 	}
 	}
 	if r.URL.Path == "/"+this.opts.Path+"/bootstrap.css" {
 	if r.URL.Path == "/"+this.opts.Path+"/bootstrap.css" {
-		w.Header().Set("Cache-Control", "public, max-age=31536000")
 		w.Header().Set("Content-Type", "text/css")
 		w.Header().Set("Content-Type", "text/css")
-		w.Write(resource_bootstrap_css)
+		if !modified(r.URL.Path, len(rbc), rbcm, &w, r) {
+			return
+		}
+		w.Write(rbc)
 		return
 		return
 	} else if r.URL.Path == "/"+this.opts.Path+"/bootstrap.js" {
 	} else if r.URL.Path == "/"+this.opts.Path+"/bootstrap.js" {
-		w.Header().Set("Cache-Control", "public, max-age=31536000")
 		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
 		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
-		w.Write(resource_bootstrap_js)
+		if !modified(r.URL.Path, len(rbj), rbjm, &w, r) {
+			return
+		}
+		w.Write(rbj)
 		return
 		return
 	} else if r.URL.Path == "/"+this.opts.Path+"/jquery.js" {
 	} else if r.URL.Path == "/"+this.opts.Path+"/jquery.js" {
-		w.Header().Set("Cache-Control", "public, max-age=31536000")
 		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
 		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
-		w.Write(resource_jquery_js)
+		if !modified(r.URL.Path, len(rjj), rjjm, &w, r) {
+			return
+		}
+		w.Write(rjj)
 		return
 		return
 	} else if r.URL.Path == "/"+this.opts.Path+"/popper.js" {
 	} else if r.URL.Path == "/"+this.opts.Path+"/popper.js" {
-		w.Header().Set("Cache-Control", "public, max-age=31536000")
 		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
 		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
-		w.Write(resource_popper_js)
+		if !modified(r.URL.Path, len(rpj), rpjm, &w, r) {
+			return
+		}
+		w.Write(rpj)
 		return
 		return
 	}
 	}
 	if this.opts.After != nil {
 	if this.opts.After != nil {
-		this.opts.After(this.ctx, w, r, this.opts.Object)
+		this.opts.After(this.ctx, w, r, this.opts.Objects)
 	}
 	}
 }
 }
 
 
+func (this *bootstrap) Shutdown(ctx context.Context) error {
+	if this.opts.Shutdown != nil {
+		return this.opts.Shutdown(ctx, this.opts.Objects)
+	}
+	return nil
+}
+
 func Start(opts *Opts) {
 func Start(opts *Opts) {
 	if opts == nil {
 	if opts == nil {
 		fmt.Println("Start: options is not defined")
 		fmt.Println("Start: options is not defined")
@@ -73,8 +145,10 @@ func Start(opts *Opts) {
 	ctrlc.App(
 	ctrlc.App(
 		opts.Timeout,
 		opts.Timeout,
 		func(ctx context.Context, shutdown context.CancelFunc) *[]ctrlc.Iface {
 		func(ctx context.Context, shutdown context.CancelFunc) *[]ctrlc.Iface {
+			bt := new(ctx, opts)
+
 			mux := http.NewServeMux()
 			mux := http.NewServeMux()
-			mux.HandleFunc("/", new(ctx, opts).handler)
+			mux.HandleFunc("/", bt.handler)
 
 
 			var srv *http.Server
 			var srv *http.Server
 			if opts.Handle == nil {
 			if opts.Handle == nil {
@@ -104,7 +178,7 @@ func Start(opts *Opts) {
 				}
 				}
 			}()
 			}()
 
 
-			return &[]ctrlc.Iface{srv}
+			return &[]ctrlc.Iface{bt, srv}
 		},
 		},
 	)
 	)
 }
 }

File diff suppressed because it is too large
+ 0 - 0
vendor/github.com/vladimirok5959/golang-server-bootstrap/bootstrap/bootstrap.js.go


File diff suppressed because it is too large
+ 0 - 0
vendor/github.com/vladimirok5959/golang-server-bootstrap/bootstrap/jquery.js.go


File diff suppressed because it is too large
+ 0 - 0
vendor/github.com/vladimirok5959/golang-server-bootstrap/bootstrap/popper.js.go


+ 2 - 2
vendor/modules.txt

@@ -2,9 +2,9 @@
 github.com/disintegration/imaging
 github.com/disintegration/imaging
 # github.com/go-sql-driver/mysql v1.4.1
 # github.com/go-sql-driver/mysql v1.4.1
 github.com/go-sql-driver/mysql
 github.com/go-sql-driver/mysql
-# github.com/vladimirok5959/golang-ctrlc v1.0.1
+# github.com/vladimirok5959/golang-ctrlc v1.0.2
 github.com/vladimirok5959/golang-ctrlc/ctrlc
 github.com/vladimirok5959/golang-ctrlc/ctrlc
-# github.com/vladimirok5959/golang-server-bootstrap v1.0.8
+# github.com/vladimirok5959/golang-server-bootstrap v1.1.1
 github.com/vladimirok5959/golang-server-bootstrap/bootstrap
 github.com/vladimirok5959/golang-server-bootstrap/bootstrap
 # github.com/vladimirok5959/golang-server-resources v1.0.4
 # github.com/vladimirok5959/golang-server-resources v1.0.4
 github.com/vladimirok5959/golang-server-resources/resource
 github.com/vladimirok5959/golang-server-resources/resource

Some files were not shown because too many files changed in this diff