feat: mv internal/pkg to root [skip ci]

This commit is contained in:
lab
2021-12-31 03:37:11 +08:00
parent ab7b33e589
commit 0989b9ebdc
14 changed files with 0 additions and 0 deletions

43
pkg/config/config.go Normal file
View File

@@ -0,0 +1,43 @@
package config
import (
"github.com/spf13/viper"
)
const (
DEFAULT_CONFIG_FILE_NAME = "weapp"
DEFAULT_CONFIG_FILE_TYPE = "yaml"
)
var defaultPaths = []string{
".",
"./conf",
"./config",
"../conf",
"../config",
"/conf",
"/config",
"${HOME}/config",
"/",
}
func AutoLoad() error {
return LoadConfig(DEFAULT_CONFIG_FILE_NAME, DEFAULT_CONFIG_FILE_TYPE)
}
func LoadConfig(fileName, filetype string, paths ...string) error {
for _, path := range defaultPaths {
viper.AddConfigPath(path)
}
for _, path := range paths {
viper.AddConfigPath(path)
}
viper.SetConfigName(fileName)
viper.SetConfigType(filetype)
if err := viper.ReadInConfig(); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,70 @@
package pgsqldb
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"git.esin.io/kimbon/weapp/internal/pkg/logging/gormlogrus"
)
func DefaultDBWithGORM() *gorm.DB {
url := NewConnectionString()
logger := logrus.StandardLogger()
db, err := NewGORM(url, logger)
if err != nil {
panic(err)
}
return db
}
func NewGORM(url string, logger logrus.FieldLogger) (*gorm.DB, error) {
db, err := gorm.Open(
postgres.Open(url),
&gorm.Config{
FullSaveAssociations: true,
Logger: gormlogrus.New(logger),
})
if err != nil {
return nil, err
}
return db, nil
}
type connectionString struct {
host string
port int32
user string
password string
database string
applicationName string
timezone string
sslModel string
}
func NewConnectionString() string {
c := connectionString{
host: viper.GetString("db.postgres.host"),
port: viper.GetInt32("db.postgres.port"),
database: viper.GetString("db.postgres.database"),
user: viper.GetString("db.postgres.user"),
password: viper.GetString("db.postgres.password"),
applicationName: viper.GetString("db.postgres.application_name"),
timezone: viper.GetString("db.postgres.tz"),
sslModel: viper.GetString("db.postgres.ssl.mode"),
}
return c.URLString()
}
func (c connectionString) URLString() string {
return fmt.Sprintf(c.Format(),
c.user, c.password, c.host, c.port, c.database, c.sslModel, c.applicationName, c.timezone)
}
func (c connectionString) Format() string {
return "postgres://%s:%s@%s:%d/%s?sslmode=%s&application_name=%s&timezone=%s"
}

22
pkg/form/conform.go Normal file
View File

@@ -0,0 +1,22 @@
package form
import (
"context"
"github.com/go-playground/mold/v4"
"github.com/go-playground/mold/v4/modifiers"
)
var conform = NewConform()
func NewConform() *mold.Transformer {
return modifiers.New()
}
func ConformStruct(v interface{}) error {
return ConformStructWithContext(context.TODO(), v)
}
func ConformStructWithContext(ctx context.Context, v interface{}) error {
return conform.Struct(ctx, v)
}

28
pkg/form/encoding.go Normal file
View File

@@ -0,0 +1,28 @@
package form
import (
"net/url"
"github.com/go-playground/form/v4"
)
var (
decoder = NewDecoder()
encoder = NewEncoder()
)
func NewDecoder() *form.Decoder {
return form.NewDecoder()
}
func Decode(values url.Values, v interface{}) error {
return decoder.Decode(v, values)
}
func NewEncoder() *form.Encoder {
return form.NewEncoder()
}
func Encode(v interface{}) (values url.Values, err error) {
return encoder.Encode(v)
}

29
pkg/form/form.go Normal file
View File

@@ -0,0 +1,29 @@
package form
import "net/url"
func SafeQuery(values url.Values, v interface{}) error {
if err := Decode(values, v); err != nil {
return err
}
if err := ConformStruct(v); err != nil {
return err
}
if err := ValidateStruct(v); err != nil {
return err
}
return nil
}
func SafeStruct(v interface{}) error {
if err := ConformStruct(v); err != nil {
return err
}
if err := ValidateStruct(v); err != nil {
return err
}
return nil
}

24
pkg/form/scrub.go Normal file
View File

@@ -0,0 +1,24 @@
package form
import (
"context"
"github.com/go-playground/mold/v4"
"github.com/go-playground/mold/v4/scrubbers"
)
var (
scrub = NewScrubbers()
)
func NewScrubbers() *mold.Transformer {
return scrubbers.New()
}
func ScrubStruct(v interface{}) error {
return ScrubStructWithContext(context.TODO(), v)
}
func ScrubStructWithContext(ctx context.Context, v interface{}) error {
return scrub.Struct(ctx, v)
}

121
pkg/form/validate.go Normal file
View File

@@ -0,0 +1,121 @@
package form
import (
"fmt"
"reflect"
"strings"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zhtrans "github.com/go-playground/validator/v10/translations/zh"
// entrans "github.com/go-playground/validator/v10/translations/en"
)
var (
translator = NewValidateTranslator()
validate = NewValidate()
)
type FieldError struct {
Field string `json:"field"`
Error string `json:"error"`
Value string `json:"value"`
Raw validator.FieldError `json:"-"`
}
func (err FieldError) String() string {
return fmt.Sprintf("%s:%s", err.Field, err.Error)
}
type FieldsError []*FieldError
func (errs FieldsError) Error() string {
var msg []string
for _, err := range errs {
msg = append(msg, err.String())
}
return strings.Join(msg, ";")
}
func init() {
// en := en.New() //英文翻译器
// zh := zh.New() //中文翻译器
// // 第一个参数是必填,如果没有其他的语言设置,就用这第一个
// // 后面的参数是支持多语言环境(
// // uni := ut.New(en, en) 也是可以的
// // uni := ut.New(en, zh, tw)
// uni := ut.New(en, zh)
// translator, ok := uni.GetTranslator("zh") //获取需要的语言
// if !ok {
// panic("translator not found")
// }
// validate := validator.New()
// validate.RegisterTagNameFunc(func(field reflect.StructField) string {
// label := field.Tag.Get("label")
// if label == "" {
// return field.Name
// }
// return label
// })
zhtrans.RegisterDefaultTranslations(validate, translator)
}
func NewValidateTranslator() ut.Translator {
en := en.New() //英文翻译器
zh := zh.New() //中文翻译器
// 第一个参数是必填,如果没有其他的语言设置,就用这第一个
// 后面的参数是支持多语言环境(
// uni := ut.New(en, en) 也是可以的
// uni := ut.New(en, zh, tw)
uni := ut.New(en, zh)
var ok bool
trans, ok := uni.GetTranslator("zh") //获取需要的语言
if !ok {
panic("form validation translator not found")
}
return trans
}
func NewValidate() *validator.Validate {
v := validator.New()
v.RegisterTagNameFunc(func(field reflect.StructField) string {
label := field.Tag.Get("label")
if label == "" {
return field.Name
}
return label
})
return v
}
func ValidateStruct(v interface{}) FieldsError {
if err := validate.Struct(v); err != nil {
validationErrs, ok := err.(validator.ValidationErrors)
if ok {
var fieldsErr FieldsError
for _, ve := range validationErrs {
fieldsErr = append(fieldsErr, NewFieldError(ve, translator))
}
return fieldsErr
}
}
return nil
}
func NewFieldError(err validator.FieldError, unt ut.Translator) *FieldError {
return &FieldError{
Field: err.StructField(),
Error: err.Translate(unt),
Raw: err,
Value: fmt.Sprintf("%v", err.Value()),
}
}

View File

@@ -0,0 +1,71 @@
package gormlogrus
import (
"context"
"errors"
"fmt"
"time"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
"gorm.io/gorm/utils"
)
type GormLogrusAdapter struct {
SlowThreshold time.Duration
SourceField string
SkipErrRecordNotFound bool
Logger logrus.FieldLogger
}
func New(logger logrus.FieldLogger) *GormLogrusAdapter {
return &GormLogrusAdapter{
Logger: logger,
SkipErrRecordNotFound: false,
SourceField: "gorm",
SlowThreshold: 1 * time.Second,
}
}
func (l *GormLogrusAdapter) LogMode(gormlogger.LogLevel) gormlogger.Interface {
return l
}
func (l *GormLogrusAdapter) Info(ctx context.Context, s string, args ...interface{}) {
l.Logger.Infof(s, args)
}
func (l *GormLogrusAdapter) Warn(ctx context.Context, s string, args ...interface{}) {
logrus.Warnf(s, args)
}
func (l *GormLogrusAdapter) Error(ctx context.Context, s string, args ...interface{}) {
logrus.Errorf(s, args)
}
func (l *GormLogrusAdapter) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
elapsed := time.Since(begin)
sql, rowsAffectedNum := fc()
fields := logrus.Fields{}
if l.SourceField != "" {
fields[l.SourceField] = utils.FileWithLineNum()
}
// format := "%s [cost: %s] [rows: %d]"
// args := []interface{}{sql, elapsed, rowsAffectedNum}
msg := fmt.Sprintf("%s [cost: %s] [rows: %d]", sql, elapsed, rowsAffectedNum)
if err != nil && !(errors.Is(err, gorm.ErrRecordNotFound) && l.SkipErrRecordNotFound) {
fields[logrus.ErrorKey] = err
l.Logger.WithFields(fields).Error(msg)
return
}
if l.SlowThreshold != 0 && elapsed > l.SlowThreshold {
l.Logger.WithFields(fields).Warn(msg)
return
}
l.Logger.WithFields(fields).Debug(msg)
}

View File

@@ -0,0 +1,68 @@
package logrusmgo
import (
"github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/mongo"
)
type LogrusMongoHook struct {
col *mongo.Collection
levels []logrus.Level
}
func NewLogrusMongoHook(col *mongo.Collection,
levels ...logrus.Level) *LogrusMongoHook {
return &LogrusMongoHook{
col: col,
levels: levels,
}
}
var _ logrus.Hook = (*LogrusMongoHook)(nil)
func (hook *LogrusMongoHook) Fire(entry *logrus.Entry) error {
doc := make(logrus.Fields)
doc["Level"] = entry.Level.String()
doc["Time"] = entry.Time
doc["Message"] = entry.Message
if caller := entry.Caller; caller != nil {
doc["File"] = caller.File
doc["Line"] = caller.Line
doc["Func"] = caller.Function
}
if data := hook.FieldsData(entry.Data); len(data) > 0 {
doc["Data"] = data
}
_, err := hook.col.InsertOne(entry.Context, doc)
if err != nil {
return err
}
return nil
}
func (hook *LogrusMongoHook) SetLevels(levels ...logrus.Level) {
hook.levels = append(hook.levels, levels...)
}
func (hook *LogrusMongoHook) Levels() []logrus.Level {
if len(hook.levels) == 0 {
return logrus.AllLevels
}
return hook.levels
}
func (hook *LogrusMongoHook) FieldsData(fields logrus.Fields) map[string]interface{} {
data := make(map[string]interface{})
for k, v := range fields {
if errData, isError := v.(error); logrus.ErrorKey == k && v != nil && isError {
data[k] = errData.Error()
} else {
data[k] = v
}
}
return data
}

75
pkg/logging/logrus.go Normal file
View File

@@ -0,0 +1,75 @@
package logging
import (
"github.com/sirupsen/logrus"
)
const (
defaultTimestampFormat = "2006-01-02 15:04:05.000000"
defaultLoggerLevel = logrus.DebugLevel
)
var DefaultConfig = defaultConfig()
func defaultConfig() *Config {
return &Config{
Level: defaultLoggerLevel.String(),
TimestampFormat: defaultTimestampFormat,
ReportCaller: true,
}
}
type Config struct {
Level string
TimestampFormat string `mapstructure:"timestamp_format"`
ReportCaller bool `mapstructure:"report_caller"`
}
func (cfg Config) GetLevel() logrus.Level {
l, err := logrus.ParseLevel(cfg.Level)
if err != nil {
logrus.
WithError(err).
Errorf("parse logger level failed with: %s", cfg.Level)
return defaultLoggerLevel
}
return l
}
func (cfg Config) GetTimestampFormat() string {
if cfg.TimestampFormat != "" {
return cfg.TimestampFormat
}
return defaultTimestampFormat
}
func Init() {
InitLogger(DefaultConfig)
}
func InitLogger(cfg *Config, hooks ...logrus.Hook) {
logrus.SetLevel(cfg.GetLevel())
logrus.SetReportCaller(cfg.ReportCaller)
formatter := logrus.JSONFormatter{
PrettyPrint: true,
TimestampFormat: cfg.GetTimestampFormat(),
}
logrus.SetFormatter(&formatter)
for _, hook := range hooks {
logrus.AddHook(hook)
}
}
func New() logrus.FieldLogger {
return logrus.StandardLogger()
}
func WithScope(scope string) logrus.FieldLogger {
return logrus.WithField("scope", scope)
}
func AddHook(hook logrus.Hook) {
logrus.AddHook(hook)
}

View File

@@ -0,0 +1,50 @@
package pgxlogrus
import (
"context"
"fmt"
"time"
"github.com/jackc/pgx/v4"
"github.com/sirupsen/logrus"
)
type pgxLogger struct {
logger logrus.FieldLogger
}
func New(logger logrus.FieldLogger) *pgxLogger {
return &pgxLogger{
logger: logger,
}
}
func (lg pgxLogger) Log(ctx context.Context, level pgx.LogLevel, msg string, data map[string]interface{}) {
lg.logger.WithFields(lg.fieldsData(data)).Info(msg)
}
func (lg pgxLogger) fieldsData(data map[string]interface{}) logrus.Fields {
fields := make(logrus.Fields)
if v, ok := data["time"]; ok && v != nil {
fields["cost"] = v
fields["costString"] = fmt.Sprintf("%v", time.Duration(v.(time.Duration)))
}
if v, ok := data["sql"]; ok {
fields["SQL"] = v
}
if v, ok := data["pid"]; ok {
fields["PID"] = v
}
if v, ok := data["args"]; ok {
fields["args"] = v
}
if v, ok := data["rowCount"]; ok {
fields["rowCount"] = v
}
return fields
}

27
pkg/pubsub/pubsub.go Normal file
View File

@@ -0,0 +1,27 @@
package pubsub
import "context"
type Subject string
func (subject Subject) String() string {
return string(subject)
}
type Message interface{}
type Publishing struct{}
type PublishOption func(*Publishing)
type Publisher interface {
Publish(context.Context, Subject, Message, ...PublishOption) error
}
type MessageHandler interface {
HandleMessage(context.Context, *Message) error
}
type Subscriber interface {
Subscribe(context.Context, Subject, MessageHandler) error
}

View File

@@ -0,0 +1,16 @@
package middleware
import "net/http"
func CORSHeaderMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Del("Content-Length")
w.Header().Set("Transfer-Encoding", "chunked")
w.Header().Set("Content-Type", "application/json;charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,OPTIONS")
w.Header().Set("Access-Control-Allow-Credentials", "true")
next.ServeHTTP(w, r)
})
}

View File

@@ -0,0 +1,23 @@
package middleware
import (
"net/http"
"time"
"github.com/sirupsen/logrus"
)
func LogRequestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
at := time.Now()
defer func() {
logrus.WithFields(logrus.Fields{
"RemoteAddr": r.RemoteAddr,
"Method": r.Method,
"URI": r.RequestURI,
"Cost": time.Since(at).String(),
}).Infof("[%s] %s", r.Method, r.RequestURI)
}()
next.ServeHTTP(w, r)
})
}