Compare commits

..

No commits in common. "dev" and "master" have entirely different histories.
dev ... master

40 changed files with 1097 additions and 697 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ xtest
*.db
*.sqlite
.DS_Store
etc

41
api.go Normal file
View File

@ -0,0 +1,41 @@
package uzone
import (
"net/http"
"github.com/loveuer/uzone/pkg/api"
)
func (u *uzone) API() *api.App { return u.api.engine }
func (u *uzone) GET(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodGet, path, handlers...)
}
func (u *uzone) POST(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodPost, path, handlers...)
}
func (u *uzone) PUT(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodPut, path, handlers...)
}
func (u *uzone) DELETE(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodDelete, path, handlers...)
}
func (u *uzone) PATCH(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodPatch, path, handlers...)
}
func (u *uzone) HEAD(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodHead, path, handlers...)
}
func (u *uzone) OPTIONS(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodOptions, path, handlers...)
}
func (u *uzone) HandleAPI(method, path string, handlers ...api.HandlerFunc) {
u.api.engine.Handle(method, path, handlers...)
}

8
config.go Normal file
View File

@ -0,0 +1,8 @@
package uzone
import "context"
type Config struct {
Ctx context.Context
Debug bool
}

View File

@ -0,0 +1,35 @@
package main
import (
"github.com/loveuer/uzone"
"github.com/loveuer/uzone/pkg/api"
"github.com/loveuer/uzone/pkg/interfaces"
)
type Record struct {
Id uint64 `json:"id" gorm:"primaryKey;column:id"`
CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"`
Name string `json:"name" gorm:"column:name"`
}
func main() {
app := uzone.New(uzone.Config{Debug: true})
app.With(uzone.InitDB("sqlite://data.db", &Record{}))
app.With(uzone.InitApi(api.New()))
app.With(uzone.InitFn(func(u interfaces.Uzone) {
u.UseLogger().Debug("[init] create init record")
u.UseDB().Create(&Record{Name: "init"})
}))
app.GET("/hello/:name", func(c *api.Ctx) error {
name := c.Param("name")
c.UseLogger().Debug("[hello] got name = %s", name)
record := &Record{Name: name}
err := c.UseDB().Create(record).Error
return c.JSON(map[string]any{"record": record, "err": err})
})
app.RunSignal()
}

7
go.mod
View File

@ -1,6 +1,6 @@
module github.com/loveuer/upp
module github.com/loveuer/uzone
go 1.23.4
go 1.20
require (
gitea.com/loveuer/gredis v1.0.0
@ -14,7 +14,9 @@ require (
github.com/loveuer/nf v0.3.1
github.com/samber/lo v1.47.0
github.com/spf13/cast v1.7.1
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.25.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.7
gorm.io/driver/postgres v1.5.11
gorm.io/gorm v1.25.12
@ -38,6 +40,7 @@ require (
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.2.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.21.0 // indirect

21
go.sum
View File

@ -6,7 +6,6 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@ -16,9 +15,7 @@ github.com/elastic/go-elasticsearch/v7 v7.17.10/go.mod h1:OJ4wdbtDNk5g503kvlHLyE
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
@ -29,9 +26,7 @@ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
@ -51,9 +46,7 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/loveuer/nf v0.3.1 h1:FTmyAC9LQF06BVGeGwrwaYfbC6MIQMqr+GoZUQQPvXU=
github.com/loveuer/nf v0.3.1/go.mod h1:aApO+2cSP0ULczkfS4OVw8zfWM3rY8gQrzc5PnVV7lY=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -64,11 +57,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@ -77,7 +67,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
@ -86,11 +75,14 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -100,10 +92,9 @@ golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

84
log.go Normal file
View File

@ -0,0 +1,84 @@
package uzone
import (
"context"
"sync"
"github.com/google/uuid"
"github.com/loveuer/uzone/pkg/api"
"github.com/loveuer/uzone/pkg/log"
"go.uber.org/zap"
)
type uzone_logger struct {
ctx context.Context
caller string
sugar *zap.SugaredLogger
}
func (ul *uzone_logger) WithContext(ctx context.Context) *uzone_logger {
ul.ctx = ctx
return ul
}
// func (ul *uzone_logger) WithCaller(caller string) *uzone_logger {
// ul.caller = caller
// return ul
// }
func (ul *uzone_logger) GC() {
ul.ctx = nil
ul.caller = ""
ul.sugar = nil
log.Logger.Sync()
uzone_logger_pool.Put(ul)
}
var uzone_logger_pool = &sync.Pool{
New: func() any {
s := log.Logger.Sugar()
return &uzone_logger{sugar: s}
},
}
func (ul *uzone_logger) traceId() string {
if ul.ctx == nil {
return uuid.Must(uuid.NewV7()).String()
}
if tid, ok := ul.ctx.Value(api.TraceKey).(string); ok && tid != "" {
return tid
}
return uuid.Must(uuid.NewV7()).String()
}
func (ul *uzone_logger) Debug(msg string, data ...any) {
ul.sugar.With("trace", ul.traceId()).Debugf(msg, data...)
ul.GC()
}
func (ul *uzone_logger) Info(msg string, data ...any) {
ul.sugar.With("trace", ul.traceId()).Infof(msg, data...)
ul.GC()
}
func (ul *uzone_logger) Warn(msg string, data ...any) {
ul.sugar.With("trace", ul.traceId()).Warnf(msg, data...)
ul.GC()
}
func (ul *uzone_logger) Error(msg string, data ...any) {
ul.sugar.With("trace", ul.traceId()).Errorf(msg, data...)
ul.GC()
}
func (ul *uzone_logger) Panic(msg string, data ...any) {
ul.sugar.With("trace", ul.traceId()).Panicf(msg, data...)
ul.GC()
}
func (ul *uzone_logger) Fatal(msg string, data ...any) {
ul.sugar.With("trace", ul.traceId()).Fatalf(msg, data...)
ul.GC()
}

102
module.go Normal file
View File

@ -0,0 +1,102 @@
package uzone
import (
"context"
"crypto/tls"
"log"
"github.com/loveuer/uzone/pkg/api"
"github.com/loveuer/uzone/pkg/cache"
"github.com/loveuer/uzone/pkg/db"
"github.com/loveuer/uzone/pkg/es"
"github.com/loveuer/uzone/pkg/interfaces"
)
type module func(u *uzone)
func InitDB(uri string, models ...any) module {
db, err := db.New(uri)
if err != nil {
log.Panic(err.Error())
}
if err = db.AutoMigrate(models...); err != nil {
log.Panic(err.Error())
}
return func(u *uzone) {
u.db = db
}
}
func InitCache(uri string) module {
cache, err := cache.New(uri)
if err != nil {
log.Panic(err.Error())
}
return func(u *uzone) {
u.cache = cache
}
}
func InitES(uri string) module {
client, err := es.New(context.TODO(), uri)
if err != nil {
log.Panic(err.Error())
}
return func(u *uzone) {
u.es = client
}
}
type ApiConfig struct {
Address string
TLSConfig *tls.Config
}
func InitApi(api *api.App, cfgs ...ApiConfig) module {
cfg := ApiConfig{}
if len(cfgs) > 0 {
cfg = cfgs[0]
}
if cfg.Address == "" {
cfg.Address = "localhost:8080"
}
return func(u *uzone) {
api.Uzone = u
u.api = &uzoneApi{
engine: api,
config: cfg,
}
}
}
func InitTaskChan(ch <-chan func(uzone interfaces.Uzone) error) module {
return func(u *uzone) {
if u.taskCh == nil {
u.taskCh = make([]<-chan func(u interfaces.Uzone) error, 0)
}
u.taskCh = append(u.taskCh, ch)
}
}
// sync functions
// 添加 同步执行函数
func InitFn(fns ...func(interfaces.Uzone)) module {
return func(u *uzone) {
u.initFns._sync = append(u.initFns._sync, fns...)
}
}
// async functions
// 添加 异步执行函数
func InitAsyncFn(fns ...func(interfaces.Uzone)) module {
return func(u *uzone) {
u.initFns._async = append(u.initFns._async, fns...)
}
}

View File

@ -12,8 +12,8 @@ import (
"regexp"
"sync"
"github.com/loveuer/upp/internal/bytesconv"
"github.com/loveuer/upp/pkg/interfaces"
"github.com/loveuer/uzone/internal/bytesconv"
"github.com/loveuer/uzone/pkg/interfaces"
)
var (
@ -25,7 +25,7 @@ var (
type App struct {
RouterGroup
Upp interfaces.Upp
Uzone interfaces.Uzone
config *Config
groups []*RouterGroup
server *http.Server

View File

@ -14,8 +14,11 @@ import (
"strings"
"sync"
"github.com/elastic/go-elasticsearch/v7"
"github.com/google/uuid"
"github.com/loveuer/upp/internal/sse"
"github.com/loveuer/uzone/internal/sse"
"github.com/loveuer/uzone/pkg/cache"
"github.com/loveuer/uzone/pkg/interfaces"
"gorm.io/gorm"
)
@ -39,8 +42,20 @@ type Ctx struct {
fullPath string
}
func (c *Ctx) UseDB() *gorm.DB {
return c.app.Upp.UseDB()
func (c *Ctx) UseLogger() interfaces.Logger {
return c.app.Uzone.UseLogger(c.Context())
}
func (c *Ctx) UseDB(ctx ...context.Context) *gorm.DB {
return c.app.Uzone.UseDB(ctx...)
}
func (c *Ctx) UseCache() cache.Cache {
return c.app.Uzone.UseCache()
}
func (c *Ctx) UseES() elasticsearch.Client {
return *c.app.Uzone.UseES()
}
func (c *Ctx) reset(w http.ResponseWriter, r *http.Request) {

View File

@ -10,7 +10,7 @@ import (
"github.com/loveuer/nf"
"github.com/loveuer/nf/nft/log"
"github.com/loveuer/nf/nft/resp"
"github.com/loveuer/upp/pkg/tool"
"github.com/loveuer/uzone/pkg/tool"
)
func NewRecover(enableStackTrace bool) HandlerFunc {

View File

@ -7,7 +7,7 @@ import (
"unicode"
"unicode/utf8"
"github.com/loveuer/upp/internal/bytesconv"
"github.com/loveuer/uzone/internal/bytesconv"
)
var (

View File

@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/loveuer/upp/internal/schema"
"github.com/loveuer/uzone/internal/schema"
)
const (

3
pkg/cache/cache.go vendored
View File

@ -19,6 +19,7 @@ type Cache interface {
// SetEx value 会被序列化, 优先使用 MarshalBinary 方法, 没有则执行 json.Marshal
SetEx(ctx context.Context, key string, value any, duration time.Duration) error
Del(ctx context.Context, keys ...string) error
Close() error
}
type Scanner interface {
@ -34,7 +35,7 @@ type decoded_value interface {
}
const (
Prefix = "upp:"
Prefix = "zone:"
)
var ErrorKeyNotFound = errors.New("key not found")

View File

@ -134,6 +134,11 @@ func (l *_lru) Del(ctx context.Context, keys ...string) error {
return nil
}
func (l *_lru) Close() error {
l.client = nil
return nil
}
func newLRUCache() (Cache, error) {
client := expirable.NewLRU[string, *_lru_value](1024*1024, nil, 0)

View File

@ -103,3 +103,9 @@ func (m *_mem) Del(ctx context.Context, keys ...string) error {
m.client.Delete(keys...)
return nil
}
func (m *_mem) Close() error {
m.client = nil
return nil
}

View File

@ -104,3 +104,7 @@ func (r *_redis) SetEx(ctx context.Context, key string, value any, duration time
func (r *_redis) Del(ctx context.Context, keys ...string) error {
return r.client.Del(ctx, keys...).Err()
}
func (r *_redis) Close() error {
return r.client.Close()
}

2
pkg/cache/new.go vendored
View File

@ -7,7 +7,7 @@ import (
"gitea.com/loveuer/gredis"
"github.com/go-redis/redis/v8"
"github.com/loveuer/upp/pkg/tool"
"github.com/loveuer/uzone/pkg/tool"
)
func New(uri string) (Cache, error) {

View File

@ -6,7 +6,7 @@ import (
"strings"
"github.com/glebarez/sqlite"
"github.com/loveuer/upp/pkg/log"
"github.com/loveuer/uzone/pkg/log"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
@ -32,11 +32,11 @@ func New(uri string) (*gorm.DB, error) {
switch ins.Scheme {
case "sqlite":
path := strings.TrimPrefix(uri, ins.Scheme+"://")
log.Debug("db.New: type = %s, path = %s", ins.Scheme, path)
log.Logger.Sugar().Debugf("db.New: type = %s, path = %s", ins.Scheme, path)
tx, err = gorm.Open(sqlite.Open(path))
case "mysql", "mariadb":
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?%s", username, password, ins.Host, ins.Path, ins.RawQuery)
log.Debug("db.New: type = %s, dsn = %s", ins.Scheme, dsn)
log.Logger.Sugar().Debugf("db.New: type = %s, dsn = %s", ins.Scheme, dsn)
tx, err = gorm.Open(mysql.Open(dsn))
case "pg", "postgres", "postgresql":
opts := make([]string, 0)
@ -44,7 +44,7 @@ func New(uri string) (*gorm.DB, error) {
opts = append(opts, fmt.Sprintf("%s=%s", key, val))
}
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s %s", ins.Hostname(), username, password, ins.Path, ins.Port(), strings.Join(opts, " "))
log.Debug("db.New: type = %s, dsn = %s", ins.Scheme, dsn)
log.Logger.Sugar().Debugf("db.New: type = %s, dsn = %s", ins.Scheme, dsn)
tx, err = gorm.Open(postgres.Open(dsn))
default:
return nil, fmt.Errorf("invalid database type(uri_scheme): %s", ins.Scheme)

77
pkg/es/client.go Normal file
View File

@ -0,0 +1,77 @@
package es
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"strings"
"time"
elastic "github.com/elastic/go-elasticsearch/v7"
"github.com/elastic/go-elasticsearch/v7/esapi"
"github.com/loveuer/uzone/pkg/tool"
"github.com/samber/lo"
)
// New elasticsearch client v7
// example:
// - uri: http://127.0.0.1:9200
// - uri: https://<username>:<password>@node1:9200,node2:9200,node3:9200
func New(ctx context.Context, uri string) (*elastic.Client, error) {
var (
err error
username string
password string
client *elastic.Client
ins *url.URL
)
if ins, err = url.Parse(uri); err != nil {
return nil, err
}
endpoints := lo.Map(
strings.Split(ins.Host, ","),
func(item string, index int) string {
return fmt.Sprintf("%s://%s", ins.Scheme, item)
},
)
if ins.User != nil {
username = ins.User.Username()
password, _ = ins.User.Password()
}
if client, err = elastic.NewClient(
elastic.Config{
Addresses: endpoints,
Username: username,
Password: password,
CACert: nil,
RetryOnStatus: []int{429},
MaxRetries: 3,
RetryBackoff: nil,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
DialContext: (&net.Dialer{Timeout: 10 * time.Second}).DialContext,
},
},
); err != nil {
return nil, err
}
var res *esapi.Response
if res, err = client.Ping(client.Ping.WithContext(tool.TimeoutCtx(ctx, 5))); err != nil {
return nil, err
}
if res.StatusCode != 200 {
err = fmt.Errorf("ping client response: %s", res.String())
return nil, err
}
return client, nil
}

View File

@ -26,7 +26,7 @@ type ApiEngine interface {
}
type ApiContext interface {
App() Upp
App() Uzone
// parse body, form, json
BodyParser(out any) error
Context() context.Context

View File

@ -1,13 +0,0 @@
package interfaces
import (
"github.com/elastic/go-elasticsearch/v7"
"github.com/loveuer/upp/pkg/cache"
"gorm.io/gorm"
)
type Upp interface {
UseDB() *gorm.DB
UseCache() cache.Cache
UseES() *elasticsearch.Client
}

18
pkg/interfaces/uzone.go Normal file
View File

@ -0,0 +1,18 @@
package interfaces
import (
"context"
"github.com/elastic/go-elasticsearch/v7"
"github.com/loveuer/uzone/pkg/cache"
"gorm.io/gorm"
)
type Uzone interface {
Debug() bool
UseCtx() context.Context
UseDB(ctx ...context.Context) *gorm.DB
UseCache() cache.Cache
UseES() *elasticsearch.Client
UseLogger(ctxs ...context.Context) Logger
}

View File

@ -1,67 +0,0 @@
package log
import (
"fmt"
"os"
"sync"
)
var (
nilLogger = func(prefix, timestamp, msg string, data ...any) {}
normalLogger = func(prefix, timestamp, msg string, data ...any) {
fmt.Printf(prefix+"| "+timestamp+" | "+msg+"\n", data...)
}
panicLogger = func(prefix, timestamp, msg string, data ...any) {
panic(fmt.Sprintf(prefix+"| "+timestamp+" | "+msg+"\n", data...))
}
fatalLogger = func(prefix, timestamp, msg string, data ...any) {
fmt.Printf(prefix+"| "+timestamp+" | "+msg+"\n", data...)
os.Exit(1)
}
DefaultLogger = &logger{
Mutex: sync.Mutex{},
timeFormat: "2006-01-02T15:04:05",
writer: os.Stdout,
level: LogLevelInfo,
debug: nilLogger,
info: normalLogger,
warn: normalLogger,
error: normalLogger,
panic: panicLogger,
fatal: fatalLogger,
}
)
func SetTimeFormat(format string) {
DefaultLogger.SetTimeFormat(format)
}
func SetLogLevel(level LogLevel) {
DefaultLogger.SetLogLevel(level)
}
func Debug(msg string, data ...any) {
DefaultLogger.Debug(msg, data...)
}
func Info(msg string, data ...any) {
DefaultLogger.Info(msg, data...)
}
func Warn(msg string, data ...any) {
DefaultLogger.Warn(msg, data...)
}
func Error(msg string, data ...any) {
DefaultLogger.Error(msg, data...)
}
func Panic(msg string, data ...any) {
DefaultLogger.Panic(msg, data...)
}
func Fatal(msg string, data ...any) {
DefaultLogger.Fatal(msg, data...)
}

View File

@ -1,115 +1,36 @@
package log
import (
"github.com/fatih/color"
"io"
"sync"
"time"
"go.uber.org/zap"
)
type LogLevel uint32
const (
LogLevelDebug = iota
LogLevelInfo
LogLevelWarn
LogLevelError
LogLevelPanic
LogLevelFatal
)
type logger struct {
sync.Mutex
timeFormat string
writer io.Writer
level LogLevel
debug func(prefix, timestamp, msg string, data ...any)
info func(prefix, timestamp, msg string, data ...any)
warn func(prefix, timestamp, msg string, data ...any)
error func(prefix, timestamp, msg string, data ...any)
panic func(prefix, timestamp, msg string, data ...any)
fatal func(prefix, timestamp, msg string, data ...any)
}
var (
red = color.New(color.FgRed)
hired = color.New(color.FgHiRed)
green = color.New(color.FgGreen)
yellow = color.New(color.FgYellow)
white = color.New(color.FgWhite)
Logger *zap.Logger
locker = &sync.Mutex{}
)
func (l *logger) SetTimeFormat(format string) {
l.Lock()
defer l.Unlock()
l.timeFormat = format
}
func (l *logger) SetLogLevel(level LogLevel) {
l.Lock()
defer l.Unlock()
if level > LogLevelDebug {
l.debug = nilLogger
} else {
l.debug = normalLogger
}
if level > LogLevelInfo {
l.info = nilLogger
} else {
l.info = normalLogger
}
if level > LogLevelWarn {
l.warn = nilLogger
} else {
l.warn = normalLogger
}
if level > LogLevelError {
l.error = nilLogger
} else {
l.error = normalLogger
}
if level > LogLevelPanic {
l.panic = nilLogger
} else {
l.panic = panicLogger
}
if level > LogLevelFatal {
l.fatal = nilLogger
} else {
l.fatal = fatalLogger
func init() {
var err error
config := zap.NewProductionConfig()
Logger, err = config.Build()
if err != nil {
panic(err)
}
}
func (l *logger) Debug(msg string, data ...any) {
l.debug(white.Sprint("Debug "), time.Now().Format(l.timeFormat), msg, data...)
}
func Debug() {
var err error
func (l *logger) Info(msg string, data ...any) {
l.info(green.Sprint("Info "), time.Now().Format(l.timeFormat), msg, data...)
}
config := zap.NewProductionConfig()
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
func (l *logger) Warn(msg string, data ...any) {
l.warn(yellow.Sprint("Warn "), time.Now().Format(l.timeFormat), msg, data...)
}
locker.Lock()
defer locker.Unlock()
func (l *logger) Error(msg string, data ...any) {
l.error(red.Sprint("Error "), time.Now().Format(l.timeFormat), msg, data...)
Logger, err = config.Build()
if err != nil {
panic(err)
}
func (l *logger) Panic(msg string, data ...any) {
l.panic(hired.Sprint("Panic "), time.Now().Format(l.timeFormat), msg, data...)
}
func (l *logger) Fatal(msg string, data ...any) {
l.fatal(hired.Sprint("Fatal "), time.Now().Format(l.timeFormat), msg, data...)
}
type WroteLogger interface {
Info(msg string, data ...any)
}

View File

@ -1,21 +0,0 @@
package log
import (
"os"
"sync"
)
func New() *logger {
return &logger{
Mutex: sync.Mutex{},
timeFormat: "2006-01-02T15:04:05",
writer: os.Stdout,
level: LogLevelInfo,
debug: nilLogger,
info: normalLogger,
warn: normalLogger,
error: normalLogger,
panic: panicLogger,
fatal: fatalLogger,
}
}

View File

@ -1,11 +1,11 @@
package tool
import "github.com/loveuer/upp/pkg/log"
import "github.com/loveuer/uzone/pkg/log"
func Must(errs ...error) {
for _, err := range errs {
if err != nil {
log.Panic(err.Error())
log.Logger.Panic(err.Error())
}
}
}

View File

@ -9,7 +9,6 @@ import (
"strconv"
"strings"
"github.com/loveuer/upp/pkg/log"
"golang.org/x/crypto/pbkdf2"
)
@ -21,26 +20,29 @@ func NewPassword(password string) string {
return EncryptPassword(password, RandomString(8), int(RandomInt(50000)+100000))
}
func ComparePassword(in, db string) bool {
// ComparePassword
// if password in and db are same, return nil
func ComparePassword(in, db string) error {
strs := strings.Split(db, "$")
if len(strs) != 3 {
log.Error("password in db invalid: %s", db)
return false
return fmt.Errorf("password in db invalid: %s", db)
}
encs := strings.Split(strs[0], ":")
if len(encs) != 3 {
log.Error("password in db invalid: %s", db)
return false
return fmt.Errorf("password in db invalid: %s", db)
}
encIteration, err := strconv.Atoi(encs[2])
if err != nil {
log.Error("password in db invalid: %s, convert iter err: %s", db, err)
return false
return fmt.Errorf("password in db invalid: %s, convert iter err: %s", db, err)
}
return EncryptPassword(in, strs[1], encIteration) == db
if EncryptPassword(in, strs[1], encIteration) != db {
return fmt.Errorf("password input and db not same")
}
return nil
}
func EncryptPassword(password, salt string, iter int) string {
@ -66,7 +68,6 @@ func CheckPassword(password string) error {
for idx, pattern := range patternList {
match, err = regexp.MatchString(pattern, password)
if err != nil {
log.Warn("regex match string err, reg_str: %s, err: %v", pattern, err)
return errors.New("密码强度不够")
}

View File

@ -8,8 +8,9 @@ import (
"reflect"
"strings"
"github.com/loveuer/uzone/pkg/log"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/loveuer/upp/pkg/log"
)
func TablePrinter(data any, writers ...io.Writer) {
@ -81,8 +82,6 @@ Start:
p := fmt.Sprintf("%s.%s", prefix, rv.Type().Field(i).Name)
field := rv.Field(i)
// log.Debug("TablePrinter: prefix: %s, field: %v", p, rv.Field(i))
if !field.CanInterface() {
return
}
@ -95,7 +94,7 @@ Start:
func TableMapPrinter(data []byte) {
m := make(map[string]any)
if err := json.Unmarshal(data, &m); err != nil {
log.Warn(err.Error())
log.Logger.Warn(err.Error())
return
}

View File

@ -1,19 +0,0 @@
package tool
import "cmp"
func Min[T cmp.Ordered](a, b T) T {
if a <= b {
return a
}
return b
}
func Max[T cmp.Ordered](a, b T) T {
if a >= b {
return a
}
return b
}

129
property.go Normal file
View File

@ -0,0 +1,129 @@
package uzone
import (
"fmt"
"github.com/spf13/cast"
"gopkg.in/yaml.v3"
"os"
"reflect"
"time"
"github.com/loveuer/uzone/pkg/tool"
)
type _property struct {
Debug bool `yaml:"debug" json:"debug" env:"UZONE.DEBUG"`
Listen struct {
Http string `yaml:"http" json:"http" env:"UZONE.LISTEN.HTTP"`
} `yaml:"listen" json:"listen"`
DB struct {
URI string `json:"uri" env:"UZONE.DB.URI"`
} `yaml:"db" json:"db"`
Cache struct {
URI string `json:"uri" env:"UZONE.CACHE.URI"`
} `yaml:"cache" json:"cache"`
Elasticsearch struct {
URI string `yaml:"uri" json:"uri" env:"UZONE.ELASTICSEARCH.URI"`
} `yaml:"elasticsearch" json:"elasticsearch"`
}
var property = &_property{}
func init() {
time.Local = time.FixedZone("CST", 8*3600)
var (
err error
bs []byte
configFn = func(path string) error {
if bs, err = os.ReadFile(path); err != nil {
uzone_logger_pool.Get().(*uzone_logger).Debug("[%30s] read %s err, err = %s", "init", path, err.Error())
return err
}
if err = yaml.Unmarshal(bs, property); err != nil {
uzone_logger_pool.Get().(*uzone_logger).Debug("[%30s] unmarshal %s err, err = %s", "init", path, err.Error())
return err
}
return nil
}
)
if err = configFn("etc/config.yaml"); err == nil {
goto BindEnv
}
if err = configFn("etc/config.yml"); err == nil {
goto BindEnv
}
_ = configFn("etc/config.json")
BindEnv:
_ = bindEnv(property)
if property.Debug {
tool.TablePrinter(property)
}
}
func bindEnv(data any) error {
rv := reflect.ValueOf(data)
if rv.Type().Kind() != reflect.Pointer {
return fmt.Errorf("can only bind ptr")
}
rv = rv.Elem()
return bindStruct(rv)
}
func bindStruct(rv reflect.Value) error {
if rv.Type().Kind() != reflect.Struct {
return fmt.Errorf("can only bind struct ptr")
}
for i := 0; i < rv.NumField(); i++ {
f := rv.Field(i)
if f.Type().Kind() == reflect.Pointer {
f = f.Elem()
}
if f.Type().Kind() == reflect.Struct {
return bindStruct(f)
}
if !f.CanSet() {
continue
}
tag := rv.Type().Field(i).Tag.Get("env")
if tag == "" || tag == "-" {
continue
}
bv := os.Getenv(tag)
if bv == "" {
continue
}
switch f.Type().Kind() {
case reflect.String:
f.SetString(bv)
case reflect.Bool:
f.SetBool(cast.ToBool(bv))
case reflect.Int64, reflect.Int, reflect.Uint64, reflect.Uint, reflect.Int32, reflect.Uint32, reflect.Int16, reflect.Uint16, reflect.Int8, reflect.Uint8:
f.SetInt(cast.ToInt64(bv))
case reflect.Float64, reflect.Float32:
f.SetFloat(cast.ToFloat64(bv))
default:
uzone_logger_pool.Get().(*uzone_logger).Warn("[%30s] unsupported env binding, type = %s, value = %s", "init", f.Type().Kind().String(), bv)
}
}
return nil
}

16
property_test.go Normal file
View File

@ -0,0 +1,16 @@
package uzone
import (
"os"
"testing"
)
func Test_bindEnv(t *testing.T) {
os.Setenv("UZONE.LISTEN.HTTP", "0.0.0.0:99")
os.Setenv("UZONE.DEBUG", "true")
p := &_property{}
bindEnv(p)
t.Logf("listen.http = %s", p.Listen.Http)
t.Logf("debug = %v", p.Debug)
}

View File

@ -1,11 +1,58 @@
# UPP - your app
# UZone
### Usage
- 1. usage present
```go
app := upp.New()
app := uzone.New()
app.With(db, es, api)
app.Run(ctx)
```
- 2. simple example
```go
package main
import (
"github.com/loveuer/uzone"
"github.com/loveuer/uzone/pkg/api"
"github.com/loveuer/uzone/pkg/interfaces"
)
type Record struct {
Id uint64 `json:"id" gorm:"primaryKey;column:id"`
CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"`
Name string `json:"name" gorm:"column:name"`
}
func main() {
app := uzone.New(uzone.Config{Debug: true})
app.With(uzone.InitDB("sqlite://data.db", &Record{}))
app.With(uzone.InitApi(api.New()))
app.With(uzone.InitFn(func(u interfaces.Uzone) {
u.UseLogger().Debug("[init] create init record")
u.UseDB().Create(&Record{Name: "init"})
}))
app.GET("/hello/:name", func(c *api.Ctx) error {
name := c.Param("name")
c.UseLogger().Debug("[hello] got name = %s", name)
record := &Record{Name: name}
err := c.UseDB().Create(record).Error
return c.JSON(map[string]any{"record": record, "err": err})
})
app.RunSignal()
}
```
### Config
> The program will load configuration files in the following order:
- `etc/config.yaml`
- `etc/config.yml`
- `config.json`
> Environment variables will take precedence and override any matching configurations found in the files above.

102
run.go Normal file
View File

@ -0,0 +1,102 @@
package uzone
import (
"context"
"fmt"
"os/signal"
"syscall"
"github.com/loveuer/uzone/pkg/interfaces"
"github.com/loveuer/uzone/pkg/tool"
)
func (u *uzone) startAPI(ctx context.Context) {
address := property.Listen.Http
if address == "" {
address = u.api.config.Address
}
fmt.Printf("Uzone | api listen at %s\n", address)
go u.api.engine.Run(address)
go func() {
<-ctx.Done()
u.api.engine.Shutdown(tool.Timeout(2))
}()
}
func (u *uzone) startTask(ctx context.Context) {
fmt.Printf("Uzone | start task channel[%02d]", len(u.taskCh))
for _, _ch := range u.taskCh {
go func(ch <-chan func(interfaces.Uzone) error) {
var err error
for {
select {
case <-ctx.Done():
case task, ok := <-ch:
if !ok {
return
}
if err = task(u); err != nil {
u.UseLogger(ctx).Error(err.Error())
}
}
}
}(_ch)
}
}
func (u *uzone) Run(ctx context.Context) {
u.RunSignal(ctx)
}
func (u *uzone) runInitFns(ctx context.Context) {
for _, fn := range u.initFns._sync {
fn(u)
}
}
func (u *uzone) startInitFns(ctx context.Context) {
for _, fn := range u.initFns._async {
go fn(u)
}
}
func (u *uzone) RunSignal(ctxs ...context.Context) {
c := context.Background()
if len(ctxs) > 0 {
c = ctxs[0]
}
ctx, cancel := signal.NotifyContext(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
defer cancel()
u.ctx = ctx
print(Banner)
if len(u.initFns._sync) > 0 {
u.runInitFns(ctx)
}
if len(u.initFns._async) > 0 {
u.startInitFns(ctx)
}
if u.api != nil {
u.startAPI(ctx)
}
if len(u.taskCh) > 0 {
u.startTask(ctx)
}
<-ctx.Done()
u.UseLogger().Warn(" Upp | quit by signal...")
if u.cache != nil {
u.cache.Close()
}
<-tool.Timeout(2).Done()
}

View File

@ -1,41 +0,0 @@
package upp
import (
"net/http"
"github.com/loveuer/upp/pkg/api"
)
func (u *upp) API() *api.App { return u.api.engine }
func (u *upp) GET(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodGet, path, handlers...)
}
func (u *upp) POST(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodPost, path, handlers...)
}
func (u *upp) PUT(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodPut, path, handlers...)
}
func (u *upp) DELETE(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodDelete, path, handlers...)
}
func (u *upp) PATCH(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodPatch, path, handlers...)
}
func (u *upp) HEAD(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodHead, path, handlers...)
}
func (u *upp) OPTIONS(path string, handlers ...api.HandlerFunc) {
u.HandleAPI(http.MethodOptions, path, handlers...)
}
func (u *upp) HandleAPI(method, path string, handlers ...api.HandlerFunc) {
u.api.engine.Handle(method, path, handlers...)
}

View File

@ -1,25 +0,0 @@
package upp
func (u *upp) Debug(msg string, data ...any) {
u.logger.Debug(msg, data...)
}
func (u *upp) Info(msg string, data ...any) {
u.logger.Info(msg, data...)
}
func (u *upp) Warn(msg string, data ...any) {
u.logger.Warn(msg, data...)
}
func (u *upp) Error(msg string, data ...any) {
u.logger.Error(msg, data...)
}
func (u *upp) Panic(msg string, data ...any) {
u.logger.Panic(msg, data...)
}
func (u *upp) Fatal(msg string, data ...any) {
u.logger.Fatal(msg, data...)
}

View File

@ -1,80 +0,0 @@
package upp
import (
"crypto/tls"
"log"
"github.com/elastic/go-elasticsearch/v7"
"github.com/loveuer/upp/pkg/api"
"github.com/loveuer/upp/pkg/cache"
"github.com/loveuer/upp/pkg/db"
"gorm.io/gorm"
)
type module func(u *upp)
func InitDB(uri string, models ...any) module {
db, err := db.New(uri)
if err != nil {
log.Panic(err.Error())
}
if err = db.AutoMigrate(models...); err != nil {
log.Panic(err.Error())
}
return func(u *upp) {
u.db = db
}
}
func (u *upp) UseDB() *gorm.DB {
tx := u.db.Session(&gorm.Session{})
if u.debug {
tx = tx.Debug()
}
return tx
}
func InitCache(uri string) module {
cache, err := cache.New(uri)
if err != nil {
log.Panic(err.Error())
}
return func(u *upp) {
u.cache = cache
}
}
func (u *upp) UseCache() cache.Cache {
return u.cache
}
func (u *upp) UseES() *elasticsearch.Client {
return nil
}
type ApiConfig struct {
Address string
TLSConfig *tls.Config
}
func InitApi(api *api.App, cfgs ...ApiConfig) module {
cfg := ApiConfig{}
if len(cfgs) > 0 {
cfg = cfgs[0]
}
if cfg.Address == "" {
cfg.Address = "localhost:8080"
}
return func(u *upp) {
api.Upp = u
u.api = &uppApi{
engine: api,
config: cfg,
}
}
}

View File

@ -1,71 +0,0 @@
package upp
import (
"context"
"os/signal"
"syscall"
"github.com/loveuer/upp/pkg/api"
"github.com/loveuer/upp/pkg/cache"
"github.com/loveuer/upp/pkg/interfaces"
"github.com/loveuer/upp/pkg/log"
"github.com/loveuer/upp/pkg/tool"
"gorm.io/gorm"
)
type uppApi struct {
engine *api.App
config ApiConfig
}
type upp struct {
debug bool
logger interfaces.Logger
db *gorm.DB
cache cache.Cache
api *uppApi
}
func (u *upp) With(modules ...module) {
for _, m := range modules {
m(u)
}
}
func (u *upp) Run(ctx context.Context) {
u.StartAPI(ctx)
<-ctx.Done()
}
func (u *upp) RunSignal() {
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
defer cancel()
if u.api != nil {
u.StartAPI(ctx)
}
<-ctx.Done()
u.Warn(" UPP | quit by signal...")
<-tool.Timeout(2).Done()
}
func (u *upp) StartAPI(ctx context.Context) {
u.Info("UPP | run api at %s", u.api.config.Address)
go u.api.engine.Run(u.api.config.Address)
go func() {
<-ctx.Done()
u.api.engine.Shutdown(tool.Timeout(2))
}()
}
func New() *upp {
app := &upp{
logger: log.New(),
}
return app
}

58
use.go Normal file
View File

@ -0,0 +1,58 @@
package uzone
import (
"context"
"github.com/elastic/go-elasticsearch/v7"
"github.com/loveuer/uzone/pkg/cache"
"github.com/loveuer/uzone/pkg/interfaces"
"github.com/loveuer/uzone/pkg/tool"
"gorm.io/gorm"
)
func (u *uzone) Debug() bool {
return u.debug
}
func (u *uzone) UseCtx() context.Context {
return u.ctx
}
func (u *uzone) UseDB(ctx ...context.Context) *gorm.DB {
var c context.Context
if len(ctx) > 0 {
c = ctx[0]
} else {
c = tool.Timeout(30)
}
tx := u.db.Session(&gorm.Session{
Context: c,
})
if u.Debug() {
tx = tx.Debug()
}
return tx
}
func (u *uzone) UseCache() cache.Cache {
return u.cache
}
func (u *uzone) UseES() *elasticsearch.Client {
return u.es
}
func (u *uzone) UseLogger(ctxs ...context.Context) interfaces.Logger {
logger := u.logger.Get().(*uzone_logger)
logger.ctx = u.UseCtx()
if len(ctxs) > 0 {
logger.ctx = ctxs[0]
}
return logger
}

73
uzone.go Normal file
View File

@ -0,0 +1,73 @@
package uzone
import (
"context"
"sync"
"github.com/elastic/go-elasticsearch/v7"
"github.com/loveuer/uzone/pkg/api"
"github.com/loveuer/uzone/pkg/cache"
"github.com/loveuer/uzone/pkg/interfaces"
"github.com/loveuer/uzone/pkg/log"
"gorm.io/gorm"
)
const Banner = `
__ ______
/ / / /_ / ___ ___ ___
/ /_/ / / /_/ _ \/ _ \/ -_)
\____/ /___/\___/_//_/\__/
`
type uzoneApi struct {
engine *api.App
config ApiConfig
}
type uzone struct {
debug bool
ctx context.Context
logger *sync.Pool
db *gorm.DB
cache cache.Cache
es *elasticsearch.Client
api *uzoneApi
initFns struct {
_sync []func(interfaces.Uzone)
_async []func(interfaces.Uzone)
}
taskCh []<-chan func(interfaces.Uzone) error
}
func (u *uzone) With(modules ...module) {
for _, m := range modules {
m(u)
}
}
func New(configs ...Config) *uzone {
config := Config{}
if len(configs) > 0 {
config = configs[0]
}
app := &uzone{
logger: uzone_logger_pool,
initFns: struct {
_sync []func(interfaces.Uzone)
_async []func(interfaces.Uzone)
}{
_sync: make([]func(interfaces.Uzone), 0),
_async: make([]func(interfaces.Uzone), 0),
},
}
if config.Debug || property.Debug {
log.Debug()
app.debug = true
}
return app
}