restructure
This commit is contained in:
@@ -1,20 +1,16 @@
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/providers/config"
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mojocn/base64Captcha"
|
||||
"github.com/rogeecn/atom/container"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/dig"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewCaptcha); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type CaptchaResponse struct {
|
||||
CaptchaId string `json:"captcha_id,omitempty"`
|
||||
PicPath string `json:"pic_path,omitempty"`
|
||||
@@ -23,16 +19,32 @@ type CaptchaResponse struct {
|
||||
}
|
||||
|
||||
type Captcha struct {
|
||||
conf *config.Config
|
||||
captcha *base64Captcha.Captcha
|
||||
}
|
||||
|
||||
func NewCaptcha(conf *config.Config, driver base64Captcha.Driver) (*Captcha, error) {
|
||||
var store = base64Captcha.DefaultMemStore
|
||||
return &Captcha{
|
||||
conf: conf,
|
||||
captcha: base64Captcha.NewCaptcha(driver, store),
|
||||
}, nil
|
||||
func Provide(conf *Config, opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*Captcha, error) {
|
||||
driver := base64Captcha.NewDriverDigit(
|
||||
int(conf.Width),
|
||||
int(conf.Height),
|
||||
int(conf.Long),
|
||||
conf.MaxScrew,
|
||||
conf.DotCount,
|
||||
)
|
||||
|
||||
store := base64Captcha.DefaultMemStore
|
||||
return &Captcha{
|
||||
captcha: base64Captcha.NewCaptcha(driver, store),
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Captcha) OpenCaptchaTimeOutDuration() time.Duration {
|
||||
d, err := time.ParseDuration(viper.GetString("CAPTCHA_IMG_OPEN_TIMEOUT"))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func (c *Captcha) Generate() (*CaptchaResponse, error) {
|
||||
@@ -44,8 +56,8 @@ func (c *Captcha) Generate() (*CaptchaResponse, error) {
|
||||
return &CaptchaResponse{
|
||||
CaptchaId: id,
|
||||
PicPath: b64s,
|
||||
CaptchaLength: c.conf.Captcha.KeyLong,
|
||||
OpenCaptcha: c.conf.Captcha.OpenCaptcha,
|
||||
CaptchaLength: viper.GetUint("CAPTCHA_IMG_KEY_LONG"),
|
||||
OpenCaptcha: viper.GetUint("CAPTCHA_IMG_OPEN"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
25
providers/captcha/config.go
Normal file
25
providers/captcha/config.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Long uint // 验证码长度
|
||||
Width uint // 验证码宽度
|
||||
Height uint // 验证码高度
|
||||
Open uint // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码
|
||||
OpenTimeOut string // 防爆破验证码超时时间,单位:s(秒)
|
||||
MaxScrew float64 // MaxSkew max absolute skew factor of a single digit.
|
||||
DotCount int // Number of background circles.
|
||||
|
||||
}
|
||||
|
||||
func (c *Config) OpenCaptchaTimeOutDuration() time.Duration {
|
||||
d, err := time.ParseDuration(c.OpenTimeOut)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/providers/config"
|
||||
"log"
|
||||
|
||||
"github.com/mojocn/base64Captcha"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewCaptchaDriverDigit); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewCaptchaDriverDigit(conf *config.Config) (base64Captcha.Driver, error) {
|
||||
// 字符,公式,验证码配置
|
||||
// 生成默认数字的driver
|
||||
return base64Captcha.NewDriverDigit(
|
||||
int(conf.Captcha.ImgHeight),
|
||||
int(conf.Captcha.ImgWidth),
|
||||
int(conf.Captcha.KeyLong),
|
||||
0.7,
|
||||
80), nil
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/utils"
|
||||
"atom/utils/fs"
|
||||
"log"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rogeecn/fabfile"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
App App
|
||||
Captcha Captcha
|
||||
Http Http
|
||||
Log Log
|
||||
Database Database
|
||||
Storage Storage
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(Load); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
var err error
|
||||
confFile := utils.ShareConfigFile
|
||||
if confFile == "" {
|
||||
confFile, err = fabfile.Find("config.toml")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
path, name, _ := fs.FilePathInfo(confFile)
|
||||
|
||||
viper.SetConfigName(name) // name of config file (without extension)
|
||||
viper.SetConfigType("toml") // REQUIRED if the config file does not have the extension in the name
|
||||
viper.AddConfigPath("$HOME/") // call multiple times to add many search paths
|
||||
viper.AddConfigPath(path) // optionally look for config in the working directory
|
||||
viper.AddConfigPath(".") // optionally look for config in the working directory
|
||||
// Find and read the config file
|
||||
if err := viper.ReadInConfig(); err != nil { // Handle errors reading the config file
|
||||
return nil, errors.Wrapf(err, "read config failed, %s", confFile)
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
return nil, errors.Wrapf(err, "unmarshal data failed, %s", confFile)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package config
|
||||
|
||||
import "atom/utils"
|
||||
|
||||
type App struct {
|
||||
Mode string
|
||||
}
|
||||
|
||||
func (a App) IsDebug() bool {
|
||||
return a.Mode == "debug"
|
||||
}
|
||||
|
||||
func (a App) IsRelease() bool {
|
||||
return a.Mode == "release"
|
||||
}
|
||||
func (a App) IsTesting() bool {
|
||||
return a.Mode == "testing" || utils.IsInTesting()
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Captcha struct {
|
||||
KeyLong uint // 验证码长度
|
||||
ImgWidth uint // 验证码宽度
|
||||
ImgHeight uint // 验证码高度
|
||||
OpenCaptcha uint // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码
|
||||
OpenCaptchaTimeOut string // 防爆破验证码超时时间,单位:s(秒)
|
||||
}
|
||||
|
||||
func (c *Captcha) OpenCaptchaTimeOutDuration() time.Duration {
|
||||
d, err := time.ParseDuration(c.OpenCaptchaTimeOut)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Database database config
|
||||
type Database struct {
|
||||
Driver string
|
||||
MySQL *MySQL
|
||||
SQLite *SQLite
|
||||
Redis *Redis
|
||||
PostgreSQL *PostgreSQL
|
||||
}
|
||||
|
||||
// MySQL database config
|
||||
type MySQL struct {
|
||||
Host string
|
||||
Port uint
|
||||
Database string
|
||||
Username string
|
||||
Password string
|
||||
Prefix string // 表前缀
|
||||
Singular bool // 是否开启全局禁用复数,true表示开启
|
||||
MaxIdleConns int // 空闲中的最大连接数
|
||||
MaxOpenConns int // 打开到数据库的最大连接数
|
||||
Engine string // 数据库引擎,默认InnoDB
|
||||
}
|
||||
|
||||
func (m *MySQL) CreateDatabaseSql() string {
|
||||
return fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", m.Database)
|
||||
}
|
||||
func (m *MySQL) EmptyDsn() string {
|
||||
dsnTpl := "%s@tcp(%s:%d)/"
|
||||
|
||||
authString := func() string {
|
||||
if len(m.Password) > 0 {
|
||||
return m.Username + ":" + m.Password
|
||||
}
|
||||
return m.Username
|
||||
}
|
||||
|
||||
return fmt.Sprintf(dsnTpl, authString(), m.Host, m.Port)
|
||||
}
|
||||
|
||||
// DSN connection dsn
|
||||
func (m *MySQL) DSN() string {
|
||||
dsnTpl := "%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
|
||||
authString := func() string {
|
||||
if len(m.Password) > 0 {
|
||||
return m.Username + ":" + m.Password
|
||||
}
|
||||
return m.Username
|
||||
}
|
||||
|
||||
return fmt.Sprintf(dsnTpl, authString(), m.Host, m.Port, m.Database)
|
||||
}
|
||||
|
||||
type PostgreSQL struct {
|
||||
Username string
|
||||
Password string
|
||||
Database string
|
||||
Host string
|
||||
Port uint
|
||||
SslMode string
|
||||
TimeZone string
|
||||
Prefix string // 表前缀
|
||||
Singular bool // 是否开启全局禁用复数,true表示开启
|
||||
MaxIdleConns int // 空闲中的最大连接数
|
||||
MaxOpenConns int // 打开到数据库的最大连接数
|
||||
}
|
||||
|
||||
func (m *PostgreSQL) EmptyDsn() string {
|
||||
dsnTpl := "host=%s user=%s password=%s port=%d dbname=postgres sslmode=disable TimeZone=Asia/Shanghai"
|
||||
|
||||
return fmt.Sprintf(dsnTpl, m.Host, m.Username, m.Password, m.Port)
|
||||
}
|
||||
|
||||
// DSN connection dsn
|
||||
func (m *PostgreSQL) DSN() string {
|
||||
dsnTpl := "host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=%s"
|
||||
return fmt.Sprintf(dsnTpl, m.Host, m.Username, m.Password, m.Database, m.Port, m.SslMode, m.TimeZone)
|
||||
}
|
||||
|
||||
type Redis struct {
|
||||
Host string
|
||||
Port uint
|
||||
Database uint
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// DSN connection dsn
|
||||
func (m *Redis) DSN() string {
|
||||
dsnTpl := "%s:%d"
|
||||
return fmt.Sprintf(dsnTpl, m.Host, m.Port)
|
||||
}
|
||||
|
||||
type SQLite struct {
|
||||
File string
|
||||
}
|
||||
|
||||
func (m *SQLite) CreateDatabaseSql() string {
|
||||
return ""
|
||||
}
|
||||
func (m *SQLite) EmptyDsn() string {
|
||||
return m.File
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package config
|
||||
|
||||
type Log struct {
|
||||
Level string
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package config
|
||||
|
||||
type Storage struct {
|
||||
Driver string
|
||||
AliYunOSS AliYunOSS
|
||||
AwsS3 AwsS3
|
||||
}
|
||||
|
||||
type AliYunOSS struct {
|
||||
Bucket string
|
||||
Region string
|
||||
Endpoint string
|
||||
AccessKeyID string
|
||||
AccessKeySecret string
|
||||
BaseURL string
|
||||
Path string
|
||||
}
|
||||
|
||||
type AwsS3 struct {
|
||||
Bucket string
|
||||
Region string
|
||||
Endpoint string
|
||||
DisableSSL bool
|
||||
SecretID string
|
||||
SecretKey string
|
||||
BaseURL string
|
||||
Path string
|
||||
S3ForcePathStyle bool
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/providers/config"
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
DriverMySQL = "mysql"
|
||||
DriverSQLite = "sqlite"
|
||||
DriverPostgres = "postgres"
|
||||
DriverSQLServer = "sqlserver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewDatabase); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewDatabase(config *config.Config) (*gorm.DB, error) {
|
||||
switch config.Database.Driver {
|
||||
case DriverMySQL:
|
||||
return NewMySQL(config.Database.MySQL)
|
||||
case DriverSQLite:
|
||||
return NewSQLite(config.Database.SQLite)
|
||||
case DriverPostgres:
|
||||
return NewPostgres(config.Database.PostgreSQL)
|
||||
}
|
||||
return nil, errors.New("failed to connect to db")
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"atom/providers/config"
|
||||
"atom/providers/log"
|
||||
"database/sql"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
func NewMySQL(conf *config.MySQL) (*gorm.DB, error) {
|
||||
if err := createMySQLDatabase(conf.EmptyDsn(), "mysql", conf.CreateDatabaseSql()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mysqlConfig := mysql.Config{
|
||||
DSN: conf.DSN(), // DSN data source name
|
||||
DefaultStringSize: 191, // string 类型字段的默认长度
|
||||
SkipInitializeWithVersion: false, // 根据版本自动配置
|
||||
}
|
||||
|
||||
gormConfig := gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: conf.Prefix,
|
||||
SingularTable: conf.Singular,
|
||||
},
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
}
|
||||
|
||||
// TODO: config logger
|
||||
// _default := logger.New(NewWriter(log.New(os.Stdout, "\r\n", log.LstdFlags)), logger.Config{
|
||||
// SlowThreshold: 200 * time.Millisecond,
|
||||
// LogLevel: logger.Warn,
|
||||
// Colorful: true,
|
||||
// })
|
||||
// conf.Logger = _default.LogMode(logger.Warn)
|
||||
|
||||
db, err := gorm.Open(mysql.New(mysqlConfig), &gormConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// config instance
|
||||
db.InstanceSet("gorm:table_options", "ENGINE="+conf.Engine)
|
||||
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.SetMaxIdleConns(conf.MaxIdleConns)
|
||||
sqlDB.SetMaxOpenConns(conf.MaxOpenConns)
|
||||
|
||||
return db, err
|
||||
}
|
||||
|
||||
// createDatabase 创建数据库
|
||||
func createMySQLDatabase(dsn, driver, createSql string) error {
|
||||
db, err := sql.Open(driver, dsn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(db *sql.DB) {
|
||||
err = db.Close()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}(db)
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec(createSql)
|
||||
return err
|
||||
}
|
||||
50
providers/database/mysql/config.go
Executable file
50
providers/database/mysql/config.go
Executable file
@@ -0,0 +1,50 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MySQL database config
|
||||
type Config struct {
|
||||
Host string
|
||||
Port uint
|
||||
Database string
|
||||
Username string
|
||||
Password string
|
||||
Prefix string // 表前缀
|
||||
Singular bool // 是否开启全局禁用复数,true表示开启
|
||||
MaxIdleConns int // 空闲中的最大连接数
|
||||
MaxOpenConns int // 打开到数据库的最大连接数
|
||||
Engine string // 数据库引擎,默认InnoDB
|
||||
}
|
||||
|
||||
func (m *Config) CreateDatabaseSql() string {
|
||||
return fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", m.Database)
|
||||
}
|
||||
|
||||
func (m *Config) EmptyDsn() string {
|
||||
dsnTpl := "%s@tcp(%s:%d)/"
|
||||
|
||||
authString := func() string {
|
||||
if len(m.Password) > 0 {
|
||||
return m.Username + ":" + m.Password
|
||||
}
|
||||
return m.Username
|
||||
}
|
||||
|
||||
return fmt.Sprintf(dsnTpl, authString(), m.Host, m.Port)
|
||||
}
|
||||
|
||||
// DSN connection dsn
|
||||
func (m *Config) DSN() string {
|
||||
dsnTpl := "%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
|
||||
authString := func() string {
|
||||
if len(m.Password) > 0 {
|
||||
return m.Username + ":" + m.Password
|
||||
}
|
||||
return m.Username
|
||||
}
|
||||
|
||||
return fmt.Sprintf(dsnTpl, authString(), m.Host, m.Port, m.Database)
|
||||
}
|
||||
77
providers/database/mysql/mysql.go
Normal file
77
providers/database/mysql/mysql.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/rogeecn/atom/container"
|
||||
"github.com/rogeecn/atom/providers/log"
|
||||
"go.uber.org/dig"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
func Provide(conf *Config, opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*gorm.DB, error) {
|
||||
if err := createMySQLDatabase(conf.EmptyDsn(), "mysql", conf.CreateDatabaseSql()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mysqlConfig := mysql.Config{
|
||||
DSN: conf.DSN(), // DSN data source name
|
||||
DefaultStringSize: 191, // string 类型字段的默认长度
|
||||
SkipInitializeWithVersion: false, // 根据版本自动配置
|
||||
}
|
||||
|
||||
gormConfig := gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: conf.Prefix,
|
||||
SingularTable: conf.Singular,
|
||||
},
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
}
|
||||
|
||||
// TODO: config logger
|
||||
// _default := logger.New(NewWriter(log.New(os.Stdout, "\r\n", log.LstdFlags)), logger.Config{
|
||||
// SlowThreshold: 200 * time.Millisecond,
|
||||
// LogLevel: logger.Warn,
|
||||
// Colorful: true,
|
||||
// })
|
||||
// conf.Logger = _default.LogMode(logger.Warn)
|
||||
|
||||
db, err := gorm.Open(mysql.New(mysqlConfig), &gormConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// config instance
|
||||
db.InstanceSet("gorm:table_options", "ENGINE="+conf.Engine)
|
||||
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.SetMaxIdleConns(conf.MaxIdleConns)
|
||||
sqlDB.SetMaxOpenConns(conf.MaxOpenConns)
|
||||
|
||||
return db, err
|
||||
}, opts...)
|
||||
}
|
||||
|
||||
// createDatabase 创建数据库
|
||||
func createMySQLDatabase(dsn, driver, createSql string) error {
|
||||
db, err := sql.Open(driver, dsn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(db *sql.DB) {
|
||||
err = db.Close()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}(db)
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec(createSql)
|
||||
return err
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"atom/providers/config"
|
||||
"log"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
func NewPostgres(conf *config.PostgreSQL) (*gorm.DB, error) {
|
||||
dbConfig := postgres.Config{
|
||||
DSN: conf.DSN(), // DSN data source name
|
||||
}
|
||||
log.Println("PostgreSQL DSN: ", dbConfig.DSN)
|
||||
|
||||
gormConfig := gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: conf.Prefix,
|
||||
SingularTable: conf.Singular,
|
||||
},
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
}
|
||||
|
||||
db, err := gorm.Open(postgres.New(dbConfig), &gormConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.SetMaxIdleConns(conf.MaxIdleConns)
|
||||
sqlDB.SetMaxOpenConns(conf.MaxOpenConns)
|
||||
|
||||
return db, err
|
||||
}
|
||||
31
providers/database/postgres/config.go
Executable file
31
providers/database/postgres/config.go
Executable file
@@ -0,0 +1,31 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Username string
|
||||
Password string
|
||||
Database string
|
||||
Host string
|
||||
Port uint
|
||||
SslMode string
|
||||
TimeZone string
|
||||
Prefix string // 表前缀
|
||||
Singular bool // 是否开启全局禁用复数,true表示开启
|
||||
MaxIdleConns int // 空闲中的最大连接数
|
||||
MaxOpenConns int // 打开到数据库的最大连接数
|
||||
}
|
||||
|
||||
func (m *Config) EmptyDsn() string {
|
||||
dsnTpl := "host=%s user=%s password=%s port=%d dbname=postgres sslmode=disable TimeZone=Asia/Shanghai"
|
||||
|
||||
return fmt.Sprintf(dsnTpl, m.Host, m.Username, m.Password, m.Port)
|
||||
}
|
||||
|
||||
// DSN connection dsn
|
||||
func (m *Config) DSN() string {
|
||||
dsnTpl := "host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=%s"
|
||||
return fmt.Sprintf(dsnTpl, m.Host, m.Username, m.Password, m.Database, m.Port, m.SslMode, m.TimeZone)
|
||||
}
|
||||
40
providers/database/postgres/postgres.go
Normal file
40
providers/database/postgres/postgres.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/rogeecn/atom/container"
|
||||
"go.uber.org/dig"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
func Provide(conf *Config, opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*gorm.DB, error) {
|
||||
dbConfig := postgres.Config{
|
||||
DSN: conf.DSN(), // DSN data source name
|
||||
}
|
||||
log.Println("PostgreSQL DSN: ", dbConfig.DSN)
|
||||
|
||||
gormConfig := gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: conf.Prefix,
|
||||
SingularTable: conf.Singular,
|
||||
},
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
}
|
||||
|
||||
db, err := gorm.Open(postgres.New(dbConfig), &gormConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.SetMaxIdleConns(conf.MaxIdleConns)
|
||||
sqlDB.SetMaxOpenConns(conf.MaxOpenConns)
|
||||
|
||||
return db, err
|
||||
}, opts...)
|
||||
}
|
||||
1
providers/database/redis/todo.go
Normal file
1
providers/database/redis/todo.go
Normal file
@@ -0,0 +1 @@
|
||||
package redis
|
||||
@@ -1,18 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"atom/providers/config"
|
||||
|
||||
// "gorm.io/driver/sqlite"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func NewSQLite(conf *config.SQLite) (*gorm.DB, error) {
|
||||
db, err := gorm.Open(sqlite.Open(conf.File), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, err
|
||||
}
|
||||
13
providers/database/sqlite/config.go
Executable file
13
providers/database/sqlite/config.go
Executable file
@@ -0,0 +1,13 @@
|
||||
package sqlite
|
||||
|
||||
type Config struct {
|
||||
File string
|
||||
}
|
||||
|
||||
func (m *Config) CreateDatabaseSql() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Config) EmptyDsn() string {
|
||||
return m.File
|
||||
}
|
||||
21
providers/database/sqlite/sqlite.go
Normal file
21
providers/database/sqlite/sqlite.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/rogeecn/atom/container"
|
||||
"go.uber.org/dig"
|
||||
|
||||
// "gorm.io/driver/sqlite"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Provide(conf *Config, opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*gorm.DB, error) {
|
||||
db, err := gorm.Open(sqlite.Open(conf.File), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, err
|
||||
}, opts...)
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package dictionary
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/database/query"
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Dict struct {
|
||||
dict map[uint64]*DictInfo
|
||||
dictItems []*DictInfo
|
||||
mapAlias map[string]uint64
|
||||
query *query.Query
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewDictionary); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewDictionary(query *query.Query) (*Dict, error) {
|
||||
dict := &Dict{
|
||||
query: query,
|
||||
dict: make(map[uint64]*DictInfo),
|
||||
dictItems: []*DictInfo{},
|
||||
mapAlias: make(map[string]uint64),
|
||||
}
|
||||
|
||||
if err := dict.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
func (dict *Dict) Load() error {
|
||||
dictTable := dict.query.SysDictionary
|
||||
items, err := dictTable.WithContext(context.Background()).Where(dictTable.Status.Is(true)).Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ids := []uint64{}
|
||||
for _, item := range items {
|
||||
ids = append(ids, item.ID)
|
||||
dict.mapAlias[item.Alias_] = item.ID
|
||||
}
|
||||
|
||||
dictDetailTable := dict.query.SysDictionaryDetail
|
||||
dictItems, err := dictDetailTable.WithContext(context.Background()).
|
||||
Where(dictDetailTable.Status.Is(true)).
|
||||
Where(dictDetailTable.ID.In(ids...)).
|
||||
Order(dictDetailTable.Weight.Desc()).
|
||||
Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idItems := make(map[uint64][]*DictItem)
|
||||
for _, dictItem := range dictItems {
|
||||
id := uint64(dictItem.SysDictionaryID)
|
||||
idItems[id] = append(idItems[id], &DictItem{
|
||||
Label: dictItem.Label,
|
||||
Value: dictItem.Value,
|
||||
})
|
||||
}
|
||||
for _, item := range items {
|
||||
info := &DictInfo{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
Alias: item.Alias_,
|
||||
Description: item.Description,
|
||||
Items: idItems[item.ID],
|
||||
}
|
||||
dict.dictItems = append(dict.dictItems, info)
|
||||
dict.dict[item.ID] = info
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLabelByValue
|
||||
func (dict *Dict) GetLabelByValue(alias, value string) (string, error) {
|
||||
items, err := dict.GetItems(alias)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
if item.Value == value {
|
||||
return item.Label, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("dict item not exists")
|
||||
}
|
||||
|
||||
// GetLabelByValue
|
||||
func (dict *Dict) GetItems(alias string) ([]*DictItem, error) {
|
||||
dictID, ok := dict.mapAlias[alias]
|
||||
if !ok {
|
||||
return nil, errors.New("dict not exists")
|
||||
}
|
||||
|
||||
dictItem, ok := dict.dict[dictID]
|
||||
if !ok {
|
||||
return nil, errors.New("dict not exists")
|
||||
}
|
||||
return dictItem.Items, nil
|
||||
}
|
||||
|
||||
func (dict *Dict) All(alias string) []*DictInfo {
|
||||
return dict.dictItems
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package dictionary
|
||||
|
||||
type DictInfo struct {
|
||||
ID uint64
|
||||
Name string
|
||||
Alias string
|
||||
Description string
|
||||
Items []*DictItem
|
||||
}
|
||||
|
||||
type DictItem struct {
|
||||
Label string
|
||||
Value string
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
package faker
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/rogeecn/atom/container"
|
||||
"go.uber.org/dig"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewFaker); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
func Provide(opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*gofakeit.Faker, error) {
|
||||
faker := gofakeit.New(time.Now().UnixNano())
|
||||
gofakeit.SetGlobalFaker(faker)
|
||||
|
||||
func NewFaker() *gofakeit.Faker {
|
||||
faker := gofakeit.New(time.Now().UnixNano())
|
||||
gofakeit.SetGlobalFaker(faker)
|
||||
|
||||
return faker
|
||||
return faker, nil
|
||||
}, opts...)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package config
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Http struct {
|
||||
type Config struct {
|
||||
Static string
|
||||
Host string
|
||||
Port uint
|
||||
@@ -17,21 +15,6 @@ type Http struct {
|
||||
Mode string
|
||||
Whitelist []Whitelist
|
||||
}
|
||||
JWT JWT
|
||||
}
|
||||
|
||||
type JWT struct {
|
||||
SigningKey string // jwt签名
|
||||
ExpiresTime string // 过期时间
|
||||
Issuer string // 签发者
|
||||
}
|
||||
|
||||
func (j JWT) ExpiresTimeDuration() time.Duration {
|
||||
d, err := time.ParseDuration(j.ExpiresTime)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
type Whitelist struct {
|
||||
@@ -42,10 +25,10 @@ type Whitelist struct {
|
||||
AllowCredentials bool
|
||||
}
|
||||
|
||||
func (h *Http) Address() string {
|
||||
func (h *Config) Address() string {
|
||||
return fmt.Sprintf("%s:%d", h.Host, h.Port)
|
||||
}
|
||||
|
||||
func (h *Http) PortString() string {
|
||||
func (h *Config) PortString() string {
|
||||
return fmt.Sprintf(":%d", h.Port)
|
||||
}
|
||||
9
providers/http/contracts.go
Normal file
9
providers/http/contracts.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package http
|
||||
|
||||
type Route interface {
|
||||
Register()
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
Serve() error
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/providers/config"
|
||||
"atom/providers/log"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewService); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
Engine *gin.Engine
|
||||
conf *config.Config
|
||||
}
|
||||
|
||||
func (e *Service) Serve() error {
|
||||
if e.conf.Http.Https {
|
||||
return e.Engine.RunTLS(e.conf.Http.PortString(), e.conf.Http.HttpsCert, e.conf.Http.HttpKey)
|
||||
}
|
||||
return e.Engine.Run(e.conf.Http.PortString())
|
||||
}
|
||||
|
||||
func NewService(cfg *config.Config, logger *log.Logger) *Service {
|
||||
gin.DefaultWriter = logger.LevelWriter(zap.InfoLevel)
|
||||
gin.DefaultErrorWriter = logger.LevelWriter(zap.ErrorLevel)
|
||||
|
||||
if cfg.App.IsDebug() {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
} else {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
engine := gin.New()
|
||||
|
||||
engine.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
||||
return fmt.Sprintf(`%s - [%s] "%s %s %s %d %s '%q' %s"\n`,
|
||||
param.ClientIP,
|
||||
param.TimeStamp.Format(time.RFC1123),
|
||||
param.Method,
|
||||
param.Path,
|
||||
param.Request.Proto,
|
||||
param.StatusCode,
|
||||
param.Latency,
|
||||
param.Request.UserAgent(),
|
||||
param.ErrorMessage,
|
||||
)
|
||||
}))
|
||||
|
||||
engine.Use(gin.Recovery())
|
||||
|
||||
return &Service{Engine: engine, conf: cfg}
|
||||
}
|
||||
54
providers/http/gin/engine.go
Normal file
54
providers/http/gin/engine.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/rogeecn/atom/container"
|
||||
"github.com/rogeecn/atom/providers/http"
|
||||
"github.com/rogeecn/atom/providers/log"
|
||||
"go.uber.org/dig"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
conf *http.Config
|
||||
Engine *gin.Engine
|
||||
}
|
||||
|
||||
func (e *Service) Use(middleware ...gin.HandlerFunc) gin.IRoutes {
|
||||
return e.Engine.Use(middleware...)
|
||||
}
|
||||
|
||||
func (e *Service) Serve() error {
|
||||
if e.conf.Https {
|
||||
return e.Engine.RunTLS(e.conf.PortString(), e.conf.HttpsCert, e.conf.HttpKey)
|
||||
}
|
||||
return e.Engine.Run(e.conf.PortString())
|
||||
}
|
||||
|
||||
func Provide(cfg *http.Config, opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (http.Service, error) {
|
||||
gin.DefaultWriter = log.LevelWriter{Level: log.InfoLevel}
|
||||
gin.DefaultErrorWriter = log.LevelWriter{Level: log.ErrorLevel}
|
||||
|
||||
engine := gin.New()
|
||||
engine.Use(gin.Recovery())
|
||||
engine.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
||||
return fmt.Sprintf(`%s - [%s] "%s %s %s %d %s '%q' %s"\n`,
|
||||
param.ClientIP,
|
||||
param.TimeStamp.Format(time.RFC1123),
|
||||
param.Method,
|
||||
param.Path,
|
||||
param.Request.Proto,
|
||||
param.StatusCode,
|
||||
param.Latency,
|
||||
param.Request.UserAgent(),
|
||||
param.ErrorMessage,
|
||||
)
|
||||
}))
|
||||
|
||||
return &Service{Engine: engine, conf: cfg}, nil
|
||||
}, opts...)
|
||||
}
|
||||
21
providers/jwt/config.go
Normal file
21
providers/jwt/config.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/rogeecn/atom/providers/log"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
SigningKey string // jwt签名
|
||||
ExpiresTime string // 过期时间
|
||||
Issuer string // 签发者
|
||||
}
|
||||
|
||||
func (c *Config) ExpiresTimeDuration() time.Duration {
|
||||
d, err := time.ParseDuration(c.ExpiresTime)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/providers/config"
|
||||
"atom/providers/log"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rogeecn/atom/container"
|
||||
"go.uber.org/dig"
|
||||
|
||||
jwt "github.com/golang-jwt/jwt/v4"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
@@ -17,12 +17,6 @@ const (
|
||||
HttpHeader = "Authorization"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewJWT); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type BaseClaims struct {
|
||||
UID uint64 `json:"uid,omitempty"`
|
||||
Role uint64 `json:"role,omitempty"`
|
||||
@@ -37,8 +31,8 @@ type Claims struct {
|
||||
const TOKEN_PREFIX = "Bearer "
|
||||
|
||||
type JWT struct {
|
||||
config *config.Config
|
||||
singleflight *singleflight.Group
|
||||
config *Config
|
||||
SigningKey []byte
|
||||
}
|
||||
|
||||
@@ -49,21 +43,23 @@ var (
|
||||
TokenInvalid = errors.New("Couldn't handle this token:")
|
||||
)
|
||||
|
||||
func NewJWT(config *config.Config) (*JWT, error) {
|
||||
return &JWT{
|
||||
config: config,
|
||||
SigningKey: []byte(config.Http.JWT.SigningKey),
|
||||
}, nil
|
||||
func Provide(config *Config, opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*JWT, error) {
|
||||
return &JWT{
|
||||
config: config,
|
||||
SigningKey: []byte(config.SigningKey),
|
||||
}, nil
|
||||
}, opts...)
|
||||
}
|
||||
|
||||
func (j *JWT) CreateClaims(baseClaims BaseClaims) *Claims {
|
||||
ep, _ := time.ParseDuration(j.config.Http.JWT.ExpiresTime)
|
||||
ep, _ := time.ParseDuration(j.config.ExpiresTime)
|
||||
claims := Claims{
|
||||
BaseClaims: baseClaims,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
NotBefore: jwt.NewNumericDate(time.Now().Add(-time.Second * 10)), // 签名生效时间
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(ep)), // 过期时间 7天 配置文件
|
||||
Issuer: j.config.Http.JWT.Issuer, // 签名的发行者
|
||||
Issuer: j.config.Issuer, // 签名的发行者
|
||||
},
|
||||
}
|
||||
return &claims
|
||||
|
||||
28
providers/log/config.go
Normal file
28
providers/log/config.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package log
|
||||
|
||||
type Config struct {
|
||||
Level Level
|
||||
}
|
||||
|
||||
type Level int8
|
||||
|
||||
const (
|
||||
// DebugLevel logs are typically voluminous, and are usually disabled in
|
||||
// production.
|
||||
DebugLevel Level = iota - 1
|
||||
// InfoLevel is the default logging priority.
|
||||
InfoLevel
|
||||
// WarnLevel logs are more important than Info, but don't need individual
|
||||
// human review.
|
||||
WarnLevel
|
||||
// ErrorLevel logs are high-priority. If an application is running smoothly,
|
||||
// it shouldn't generate any error-level logs.
|
||||
ErrorLevel
|
||||
// DPanicLevel logs are particularly important errors. In development the
|
||||
// logger panics after writing the message.
|
||||
DPanicLevel
|
||||
// PanicLevel logs a message, then panics.
|
||||
PanicLevel
|
||||
// FatalLevel logs a message, then calls os.Exit(1).
|
||||
FatalLevel
|
||||
)
|
||||
@@ -2,20 +2,18 @@ package log
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type LevelWriter struct {
|
||||
Level zapcore.Level
|
||||
Level Level
|
||||
}
|
||||
|
||||
func (w LevelWriter) Write(p []byte) (n int, err error) {
|
||||
str := strings.TrimSpace(string(p))
|
||||
switch w.Level {
|
||||
case zapcore.InfoLevel:
|
||||
case InfoLevel:
|
||||
Info(str)
|
||||
case zapcore.ErrorLevel:
|
||||
case ErrorLevel:
|
||||
Error(str)
|
||||
}
|
||||
return len(p), nil
|
||||
|
||||
@@ -1,99 +1,103 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"log"
|
||||
"github.com/rogeecn/atom/container"
|
||||
|
||||
"go.uber.org/dig"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewZapLogger); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
func Provide(config *Config, opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*Logger, error) {
|
||||
logger, err := newZapLogger(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultLogger = logger
|
||||
return logger, nil
|
||||
}, opts...)
|
||||
}
|
||||
|
||||
var DefaultLogger *Logger
|
||||
var defaultLogger *Logger
|
||||
|
||||
type Logger struct {
|
||||
logger *zap.SugaredLogger
|
||||
}
|
||||
|
||||
func (l *Logger) LevelWriter(level zapcore.Level) *LevelWriter {
|
||||
func (l *Logger) LevelWriter(level Level) *LevelWriter {
|
||||
return &LevelWriter{Level: level}
|
||||
}
|
||||
|
||||
// Debug uses fmt.Sprint to construct and log a message.
|
||||
func Debug(args ...interface{}) {
|
||||
DefaultLogger.logger.Debug(args...)
|
||||
defaultLogger.logger.Debug(args...)
|
||||
}
|
||||
|
||||
// Info uses fmt.Sprint to construct and log a message.
|
||||
func Info(args ...interface{}) {
|
||||
DefaultLogger.logger.Info(args...)
|
||||
defaultLogger.logger.Info(args...)
|
||||
}
|
||||
|
||||
// Warn uses fmt.Sprint to construct and log a message.
|
||||
func Warn(args ...interface{}) {
|
||||
DefaultLogger.logger.Warn(args...)
|
||||
defaultLogger.logger.Warn(args...)
|
||||
}
|
||||
|
||||
// Error uses fmt.Sprint to construct and log a message.
|
||||
func Error(args ...interface{}) {
|
||||
DefaultLogger.logger.Error(args...)
|
||||
defaultLogger.logger.Error(args...)
|
||||
}
|
||||
|
||||
// DPanic uses fmt.Sprint to construct and log a message. In development, the
|
||||
// logger then panics. (See DPanicLevel for details.)
|
||||
func DPanic(args ...interface{}) {
|
||||
DefaultLogger.logger.DPanic(args...)
|
||||
defaultLogger.logger.DPanic(args...)
|
||||
}
|
||||
|
||||
// Panic uses fmt.Sprint to construct and log a message, then panics.
|
||||
func Panic(args ...interface{}) {
|
||||
DefaultLogger.logger.Panic(args...)
|
||||
defaultLogger.logger.Panic(args...)
|
||||
}
|
||||
|
||||
// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit.
|
||||
func Fatal(args ...interface{}) {
|
||||
DefaultLogger.logger.Fatal(args...)
|
||||
defaultLogger.logger.Fatal(args...)
|
||||
}
|
||||
|
||||
// Debugf uses fmt.Sprintf to log a templated message.
|
||||
func Debugf(template string, args ...interface{}) {
|
||||
DefaultLogger.logger.Debugf(template, args...)
|
||||
defaultLogger.logger.Debugf(template, args...)
|
||||
}
|
||||
|
||||
// Infof uses fmt.Sprintf to log a templated message.
|
||||
func Infof(template string, args ...interface{}) {
|
||||
DefaultLogger.logger.Infof(template, args...)
|
||||
defaultLogger.logger.Infof(template, args...)
|
||||
}
|
||||
|
||||
// Warnf uses fmt.Sprintf to log a templated message.
|
||||
func Warnf(template string, args ...interface{}) {
|
||||
DefaultLogger.logger.Warnf(template, args...)
|
||||
defaultLogger.logger.Warnf(template, args...)
|
||||
}
|
||||
|
||||
// Errorf uses fmt.Sprintf to log a templated message.
|
||||
func Errorf(template string, args ...interface{}) {
|
||||
DefaultLogger.logger.Errorf(template, args...)
|
||||
defaultLogger.logger.Errorf(template, args...)
|
||||
}
|
||||
|
||||
// DPanicf uses fmt.Sprintf to log a templated message. In development, the
|
||||
// logger then panics. (See DPanicLevel for details.)
|
||||
func DPanicf(template string, args ...interface{}) {
|
||||
DefaultLogger.logger.DPanicf(template, args...)
|
||||
defaultLogger.logger.DPanicf(template, args...)
|
||||
}
|
||||
|
||||
// Panicf uses fmt.Sprintf to log a templated message, then panics.
|
||||
func Panicf(template string, args ...interface{}) {
|
||||
DefaultLogger.logger.Panicf(template, args...)
|
||||
defaultLogger.logger.Panicf(template, args...)
|
||||
}
|
||||
|
||||
// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit.
|
||||
func Fatalf(template string, args ...interface{}) {
|
||||
DefaultLogger.logger.Fatalf(template, args...)
|
||||
defaultLogger.logger.Fatalf(template, args...)
|
||||
}
|
||||
|
||||
// Debugw logs a message with some additional context. The variadic key-value
|
||||
@@ -103,47 +107,47 @@ func Fatalf(template string, args ...interface{}) {
|
||||
//
|
||||
// s.With(keysAndValues).Debug(msg)
|
||||
func Debugw(msg string, keysAndValues ...interface{}) {
|
||||
DefaultLogger.logger.Debugw(msg, keysAndValues...)
|
||||
defaultLogger.logger.Debugw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// Infow logs a message with some additional context. The variadic key-value
|
||||
// pairs are treated as they are in With.
|
||||
func Infow(msg string, keysAndValues ...interface{}) {
|
||||
DefaultLogger.logger.Infow(msg, keysAndValues...)
|
||||
defaultLogger.logger.Infow(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// Warnw logs a message with some additional context. The variadic key-value
|
||||
// pairs are treated as they are in With.
|
||||
func Warnw(msg string, keysAndValues ...interface{}) {
|
||||
DefaultLogger.logger.Warnw(msg, keysAndValues...)
|
||||
defaultLogger.logger.Warnw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// Errorw logs a message with some additional context. The variadic key-value
|
||||
// pairs are treated as they are in With.
|
||||
func Errorw(msg string, keysAndValues ...interface{}) {
|
||||
DefaultLogger.logger.Errorw(msg, keysAndValues...)
|
||||
defaultLogger.logger.Errorw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// DPanicw logs a message with some additional context. In development, the
|
||||
// logger then panics. (See DPanicLevel for details.) The variadic key-value
|
||||
// pairs are treated as they are in With.
|
||||
func DPanicw(msg string, keysAndValues ...interface{}) {
|
||||
DefaultLogger.logger.DPanicw(msg, keysAndValues...)
|
||||
defaultLogger.logger.DPanicw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// Panicw logs a message with some additional context, then panics. The
|
||||
// variadic key-value pairs are treated as they are in With.
|
||||
func Panicw(msg string, keysAndValues ...interface{}) {
|
||||
DefaultLogger.logger.Panicw(msg, keysAndValues...)
|
||||
defaultLogger.logger.Panicw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// Fatalw logs a message with some additional context, then calls os.Exit. The
|
||||
// variadic key-value pairs are treated as they are in With.
|
||||
func Fatalw(msg string, keysAndValues ...interface{}) {
|
||||
DefaultLogger.logger.Fatalw(msg, keysAndValues...)
|
||||
defaultLogger.logger.Fatalw(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// Sync flushes any buffered log entries.
|
||||
func Sync() error {
|
||||
return DefaultLogger.logger.Sync()
|
||||
return defaultLogger.logger.Sync()
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"atom/providers/config"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func NewZapLogger(conf *config.Config) (*Logger, error) {
|
||||
func newZapLogger(conf *Config) (*Logger, error) {
|
||||
logger, err := zap.NewDevelopment()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
DefaultLogger = &Logger{logger: logger.Sugar()}
|
||||
return DefaultLogger, nil
|
||||
return &Logger{logger: logger.Sugar()}, nil
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
_ "atom/providers/captcha"
|
||||
_ "atom/providers/captcha/driver"
|
||||
_ "atom/providers/config"
|
||||
_ "atom/providers/database"
|
||||
_ "atom/providers/dictionary"
|
||||
_ "atom/providers/faker"
|
||||
_ "atom/providers/http"
|
||||
_ "atom/providers/jwt"
|
||||
_ "atom/providers/log"
|
||||
_ "atom/providers/query"
|
||||
_ "atom/providers/rbac"
|
||||
_ "atom/providers/single_flight"
|
||||
_ "atom/providers/uuid"
|
||||
)
|
||||
@@ -1,19 +0,0 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/database/query"
|
||||
"log"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewQuery); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewQuery(db *gorm.DB) *query.Query {
|
||||
return query.Use(db)
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"atom/database/query"
|
||||
"atom/providers/log"
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
gormadapter "github.com/casbin/gorm-adapter/v3"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewCasbin); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Casbin struct {
|
||||
query *query.Query
|
||||
enforcer *casbin.CachedEnforcer
|
||||
}
|
||||
|
||||
type CasbinInfo struct {
|
||||
Path string `json:"path"` // 路径
|
||||
Method string `json:"method"` // 方法
|
||||
}
|
||||
|
||||
func NewCasbin(query *query.Query, db *gorm.DB) (IRbac, error) {
|
||||
cb := &Casbin{query: query}
|
||||
|
||||
a, _ := gormadapter.NewAdapterByDB(db)
|
||||
text := `
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
|
||||
`
|
||||
m, err := model.NewModelFromString(text)
|
||||
if err != nil {
|
||||
log.Error(err, "字符串加载模型失败!")
|
||||
return nil, err
|
||||
}
|
||||
cb.enforcer, _ = casbin.NewCachedEnforcer(m, a)
|
||||
|
||||
cb.enforcer.SetExpireTime(60 * 60)
|
||||
_ = cb.enforcer.LoadPolicy()
|
||||
return cb, nil
|
||||
}
|
||||
|
||||
func (cb *Casbin) Can(role, method, path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (cb *Casbin) Reload() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cb *Casbin) JsonPermissionsForUser(username string) (string, error) {
|
||||
return casbin.CasbinJsGetPermissionForUser(cb.enforcer, username)
|
||||
}
|
||||
|
||||
func (cb *Casbin) Update(roleID uint, infos []CasbinInfo) error {
|
||||
roleIdStr := strconv.Itoa(int(roleID))
|
||||
cb.Clear(0, roleIdStr)
|
||||
|
||||
rules := [][]string{}
|
||||
for _, v := range infos {
|
||||
rules = append(rules, []string{roleIdStr, v.Path, v.Method})
|
||||
}
|
||||
|
||||
success, _ := cb.enforcer.AddPolicies(rules)
|
||||
if !success {
|
||||
return errors.New("存在相同api,添加失败,请联系管理员")
|
||||
}
|
||||
|
||||
err := cb.enforcer.InvalidateCache()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cb *Casbin) UpdateApi(before, after CasbinInfo) error {
|
||||
rule := cb.query.CasbinRule
|
||||
|
||||
_, err := rule.WithContext(context.Background()).
|
||||
Where(rule.V1.Eq(before.Path)).
|
||||
Where(rule.V1.Eq(before.Method)).
|
||||
UpdateSimple(
|
||||
rule.V1.Value(after.Path),
|
||||
rule.V2.Value(after.Method),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cb.enforcer.InvalidateCache()
|
||||
}
|
||||
|
||||
// 获取权限列表
|
||||
func (cb *Casbin) GetPolicyPathByRoleID(roleID uint) (pathMaps []CasbinInfo) {
|
||||
roleIdStr := strconv.Itoa(int(roleID))
|
||||
list := cb.enforcer.GetFilteredPolicy(0, roleIdStr)
|
||||
for _, v := range list {
|
||||
pathMaps = append(pathMaps, CasbinInfo{
|
||||
Path: v[1],
|
||||
Method: v[2],
|
||||
})
|
||||
}
|
||||
return pathMaps
|
||||
}
|
||||
|
||||
// 清除匹配的权限
|
||||
func (cb *Casbin) Clear(v int, p ...string) bool {
|
||||
success, _ := cb.enforcer.RemoveFilteredPolicy(v, p...)
|
||||
return success
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package rbac
|
||||
|
||||
type IRbac interface {
|
||||
Can(role, method, path string) bool
|
||||
JsonPermissionsForUser(string) (string, error)
|
||||
Reload() error
|
||||
}
|
||||
@@ -1,18 +1,13 @@
|
||||
package single_flight
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"log"
|
||||
|
||||
"github.com/rogeecn/atom/container"
|
||||
"go.uber.org/dig"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewSingleFlight); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewSingleFlight() (*singleflight.Group, error) {
|
||||
return &singleflight.Group{}, nil
|
||||
func Provide(opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*singleflight.Group, error) {
|
||||
return &singleflight.Group{}, nil
|
||||
}, opts...)
|
||||
}
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"atom/container"
|
||||
"log"
|
||||
"github.com/rogeecn/atom/container"
|
||||
"go.uber.org/dig"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := container.Container.Provide(NewUUID); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Generator struct {
|
||||
generator uuid.Generator
|
||||
}
|
||||
|
||||
func NewUUID() (*Generator, error) {
|
||||
return &Generator{
|
||||
generator: uuid.DefaultGenerator,
|
||||
}, nil
|
||||
func Provide(opts ...dig.ProvideOption) error {
|
||||
return container.Container.Provide(func() (*Generator, error) {
|
||||
return &Generator{
|
||||
generator: uuid.DefaultGenerator,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (u *Generator) MustGenerate() string {
|
||||
|
||||
Reference in New Issue
Block a user