Initial
This commit is contained in:
47
.air.toml
Normal file
47
.air.toml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "./tmp/main"
|
||||||
|
cmd = "go build -o ./tmp/main ./app/."
|
||||||
|
delay = 500
|
||||||
|
exclude_dir = [
|
||||||
|
"assets",
|
||||||
|
"tmp",
|
||||||
|
"vendor",
|
||||||
|
"testdata",
|
||||||
|
"build",
|
||||||
|
"migrations",
|
||||||
|
"cert",
|
||||||
|
"http-requests",
|
||||||
|
"app/docs",
|
||||||
|
]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = true
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/.vscode
|
||||||
|
/.idea
|
||||||
|
/__debug*
|
||||||
|
/build
|
||||||
|
/tmp
|
||||||
|
/.log
|
||||||
|
/config.yaml
|
||||||
|
/cert/test
|
||||||
|
/.env
|
||||||
|
/tern.prod.conf
|
||||||
64
Makefile
Normal file
64
Makefile
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
EXEC=fiskalator
|
||||||
|
CONTAINER_NAME=fiskalator
|
||||||
|
IMAGE_NAME=fiskalator
|
||||||
|
REMOTE_IMAGE=edkirin/$(IMAGE_NAME)
|
||||||
|
VERSION_TAG=$$(date +%y%m%d.%H%M)
|
||||||
|
|
||||||
|
|
||||||
|
run:
|
||||||
|
@air
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
@go test ./...
|
||||||
|
|
||||||
|
|
||||||
|
test-verbose:
|
||||||
|
@go test -v ./...
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
@go build -ldflags "-s -w" -o ./build/${EXEC} ./app/main.go
|
||||||
|
|
||||||
|
|
||||||
|
upgrade-packages:
|
||||||
|
@go get -u ./...
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
- @docker rm $(CONTAINER_NAME)
|
||||||
|
- docker images | grep '$(IMAGE_NAME) ' | awk '{print $$1 ":" $$2}' | xargs docker rmi
|
||||||
|
|
||||||
|
|
||||||
|
docker-build: clean
|
||||||
|
@docker \
|
||||||
|
build \
|
||||||
|
--no-cache \
|
||||||
|
--progress plain \
|
||||||
|
--tag $(IMAGE_NAME):$(VERSION_TAG) \
|
||||||
|
--tag $(IMAGE_NAME):latest \
|
||||||
|
--tag $(REMOTE_IMAGE):$(VERSION_TAG) \
|
||||||
|
--tag $(REMOTE_IMAGE):latest \
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
|
docker-run:
|
||||||
|
- @docker rm $(CONTAINER_NAME)
|
||||||
|
@docker run \
|
||||||
|
--env-file .env \
|
||||||
|
--add-host=sunce:192.168.1.169 \
|
||||||
|
--publish=8080:8080 \
|
||||||
|
--name $(CONTAINER_NAME) \
|
||||||
|
$(IMAGE_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
docker-push:
|
||||||
|
@git tag -a "$(VERSION_TAG)" -m "$(VERSION_TAG)"
|
||||||
|
@git push origin "$(VERSION_TAG)"
|
||||||
|
@docker push $(REMOTE_IMAGE) --all-tags
|
||||||
|
|
||||||
|
|
||||||
|
migrate:
|
||||||
|
@tern migrate -m migrations
|
||||||
106
app/lib/cfg/cfg.go
Normal file
106
app/lib/cfg/cfg.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package cfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/kelseyhightower/envconfig"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServiceConfig struct {
|
||||||
|
Address string `yaml:"address"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DatabaseConfig struct {
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port string `yaml:"port"`
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
Password string `yaml:"password" json:"-"`
|
||||||
|
DebugSQL bool `yaml:"debugSQL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplicationConfig struct {
|
||||||
|
LogLevel string `yaml:"logLevel"`
|
||||||
|
LogFile string `yaml:"logFile"`
|
||||||
|
Debug bool `yaml:"debug"`
|
||||||
|
DebugPrint bool `yaml:"debugPrint"`
|
||||||
|
DisableSendMail bool `yaml:"disableSendMail"`
|
||||||
|
IsProduction bool `yaml:"isProduction"`
|
||||||
|
MetricsPrefix string `yaml:"metricsPrefix"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type S3StorageConfig struct {
|
||||||
|
AccessKey string `yaml:"accessKey"`
|
||||||
|
SecretKey string `yaml:"secretKey"`
|
||||||
|
RegionName string `yaml:"regionName"`
|
||||||
|
EndpointUrl string `yaml:"endpointUrl"`
|
||||||
|
BucketName string `yaml:"bucketName"`
|
||||||
|
CertPath string `yaml:"certPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertConfig struct {
|
||||||
|
FinaDemoCaCert string `yaml:"finaDemoCaCert"`
|
||||||
|
FinaProdCaCert string `yaml:"finaProdCaCert"`
|
||||||
|
DemoCertOib string `yaml:"demoCertOib"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type configStruct struct {
|
||||||
|
Service ServiceConfig `yaml:"service"`
|
||||||
|
Database DatabaseConfig `yaml:"database"`
|
||||||
|
Application ApplicationConfig `yaml:"application"`
|
||||||
|
S3Storage S3StorageConfig `yaml:"s3storage"`
|
||||||
|
Cert CertConfig `yaml:"cert"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const ENV_PREFIX = "FISKALATOR"
|
||||||
|
const ENV_CONFIG = ENV_PREFIX + "_CONFIG"
|
||||||
|
const DEFAULT_CONFIG_FILE = "config.yaml"
|
||||||
|
|
||||||
|
var Config configStruct
|
||||||
|
|
||||||
|
func panicWithError(err error) {
|
||||||
|
panic("Config file error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFile(cfgFile string, cfg *configStruct) {
|
||||||
|
f, err := os.Open(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
panicWithError(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
decoder := yaml.NewDecoder(f)
|
||||||
|
err = decoder.Decode(cfg)
|
||||||
|
if err != nil {
|
||||||
|
panicWithError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readEnv(cfg *configStruct) {
|
||||||
|
err := envconfig.Process(ENV_PREFIX, cfg)
|
||||||
|
if err != nil {
|
||||||
|
panicWithError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
cfgFile := os.Getenv(ENV_CONFIG)
|
||||||
|
if cfgFile == "" {
|
||||||
|
cfgFile = DEFAULT_CONFIG_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
readFile(cfgFile, &Config)
|
||||||
|
readEnv(&Config)
|
||||||
|
|
||||||
|
maskedCfg := Config
|
||||||
|
maskedCfg.Database.Password = "**password hidden**"
|
||||||
|
maskedCfg.S3Storage.AccessKey = "**access key hidden**"
|
||||||
|
maskedCfg.S3Storage.SecretKey = "**secret key hidden**"
|
||||||
|
|
||||||
|
fmt.Println("--- CONFIG -------------------------------")
|
||||||
|
fmt.Printf("%+v\n", maskedCfg)
|
||||||
|
fmt.Println("------------------------------------------")
|
||||||
|
}
|
||||||
65
app/lib/db/db.go
Normal file
65
app/lib/db/db.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"repo-pattern/app/lib/cfg"
|
||||||
|
"repo-pattern/app/lib/logging"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
gormLogger "gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CONNECTION_MAX_IDLE_TIME = time.Minute * 1
|
||||||
|
const DB_CONNECTION_TIMEOUT = 5
|
||||||
|
|
||||||
|
var DBConn *gorm.DB
|
||||||
|
|
||||||
|
func ConnectToDb(config *cfg.DatabaseConfig) (*gorm.DB, error) {
|
||||||
|
var connectionString = strings.Join([]string{
|
||||||
|
"postgres://",
|
||||||
|
config.Username, ":",
|
||||||
|
config.Password, "@",
|
||||||
|
config.Host, ":",
|
||||||
|
config.Port, "/",
|
||||||
|
config.Name,
|
||||||
|
"?sslmode=disable",
|
||||||
|
"&TimeZone=UTC",
|
||||||
|
"&connect_timeout=", strconv.Itoa(DB_CONNECTION_TIMEOUT),
|
||||||
|
}, "")
|
||||||
|
|
||||||
|
var logLevel gormLogger.LogLevel
|
||||||
|
if config.DebugSQL {
|
||||||
|
logLevel = gormLogger.Info
|
||||||
|
} else {
|
||||||
|
logLevel = gormLogger.Silent
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := gorm.Open(postgres.Open(connectionString), &gorm.Config{
|
||||||
|
Logger: gormLogger.Default.LogMode(logLevel),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDB() *gorm.DB {
|
||||||
|
var err error
|
||||||
|
DBConn, err = ConnectToDb(&cfg.Config.Database)
|
||||||
|
if err != nil {
|
||||||
|
msg := fmt.Sprintf("Error connecting to database: %s. Terminating!", err)
|
||||||
|
logging.Log.Error(msg)
|
||||||
|
panic(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set connection autodisconnect after idle time
|
||||||
|
db, _ := DBConn.DB()
|
||||||
|
db.SetConnMaxIdleTime(CONNECTION_MAX_IDLE_TIME)
|
||||||
|
|
||||||
|
return DBConn
|
||||||
|
}
|
||||||
39
app/lib/helpers/factory.go
Normal file
39
app/lib/helpers/factory.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Factory struct {
|
||||||
|
dbConn *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactory(dbConn *gorm.DB) *Factory {
|
||||||
|
return &Factory{
|
||||||
|
dbConn: dbConn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Factory) CreateModel(model interface{}) {
|
||||||
|
if f.dbConn == nil {
|
||||||
|
panic("Factory created without db connection. Unable to create model.")
|
||||||
|
}
|
||||||
|
f.dbConn.Create(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeValuesToModel(model interface{}, values map[string]any) {
|
||||||
|
st := reflect.ValueOf(model).Elem()
|
||||||
|
|
||||||
|
for key, value := range values {
|
||||||
|
field := st.FieldByName(key)
|
||||||
|
var v reflect.Value
|
||||||
|
if value != nil {
|
||||||
|
v = reflect.ValueOf(value)
|
||||||
|
} else {
|
||||||
|
v = reflect.Zero(field.Type())
|
||||||
|
}
|
||||||
|
field.Set(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/lib/helpers/util.go
Normal file
27
app/lib/helpers/util.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UTCNow() time.Time {
|
||||||
|
utc, _ := time.LoadLocation("UTC")
|
||||||
|
return time.Now().In(utc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UNUSED allows unused variables to be included in Go programs
|
||||||
|
func UNUSED(x ...interface{}) {}
|
||||||
|
|
||||||
|
func StructToMap(data interface{}) (map[string]interface{}, error) {
|
||||||
|
dataBytes, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mapData := make(map[string]interface{})
|
||||||
|
err = json.Unmarshal(dataBytes, &mapData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mapData, nil
|
||||||
|
}
|
||||||
70
app/lib/helpers/util_test.go
Normal file
70
app/lib/helpers/util_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_StructToMap(t *testing.T) {
|
||||||
|
t.Run("Convert simple struct to map", func(t *testing.T) {
|
||||||
|
type User struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
Height float32 `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
user := &User{
|
||||||
|
Name: "Pero",
|
||||||
|
Age: 66,
|
||||||
|
Height: 187.6,
|
||||||
|
}
|
||||||
|
result, _ := StructToMap(user)
|
||||||
|
|
||||||
|
expected := make(map[string]interface{})
|
||||||
|
expected["name"] = "Pero"
|
||||||
|
expected["age"] = 66
|
||||||
|
expected["height"] = 187.6
|
||||||
|
|
||||||
|
assert.Equal(t, fmt.Sprintf("%v", result), fmt.Sprintf("%v", expected))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Convert nested struct to map", func(t *testing.T) {
|
||||||
|
type Address struct {
|
||||||
|
City string `json:"city"`
|
||||||
|
PostalCode int `json:"postalCode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
Height float32 `json:"height"`
|
||||||
|
Address Address `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
user := &User{
|
||||||
|
Name: "Pero",
|
||||||
|
Age: 66,
|
||||||
|
Height: 187.6,
|
||||||
|
Address: Address{
|
||||||
|
City: "Zagreb",
|
||||||
|
PostalCode: 10020,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result, _ := StructToMap(user)
|
||||||
|
|
||||||
|
address := make(map[string]interface{})
|
||||||
|
address["city"] = "Zagreb"
|
||||||
|
address["postalCode"] = 10020
|
||||||
|
|
||||||
|
expected := make(map[string]interface{})
|
||||||
|
expected["name"] = "Pero"
|
||||||
|
expected["age"] = 66
|
||||||
|
expected["height"] = 187.6
|
||||||
|
expected["address"] = make(map[string]interface{})
|
||||||
|
expected["address"] = address
|
||||||
|
|
||||||
|
assert.Equal(t, fmt.Sprintf("%v", result), fmt.Sprintf("%v", expected))
|
||||||
|
})
|
||||||
|
}
|
||||||
65
app/lib/logging/logging.go
Normal file
65
app/lib/logging/logging.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"repo-pattern/app/lib/cfg"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Log *zap.Logger = nil
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
logConfig := zap.NewProductionConfig()
|
||||||
|
logConfig.Level, err = zap.ParseAtomicLevel(cfg.Config.Application.LogLevel)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logConfig.OutputPaths = []string{
|
||||||
|
cfg.Config.Application.LogFile,
|
||||||
|
"stderr",
|
||||||
|
}
|
||||||
|
|
||||||
|
Log, err = logConfig.Build()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
logLevel, err := logrus.ParseLevel(cfg.Config.Application.LogLevel)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Invalid configured logLevel: %s\n", cfg.Config.Application.LogLevel))
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.SetLevel(logLevel)
|
||||||
|
|
||||||
|
Log.SetFormatter(&logrus.TextFormatter{
|
||||||
|
FullTimestamp: true,
|
||||||
|
TimestampFormat: "2006-01-02 15:04:05",
|
||||||
|
PadLevelText: true,
|
||||||
|
DisableQuote: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
LogFile := cfg.Config.Application.LogFile
|
||||||
|
file, err := os.OpenFile(
|
||||||
|
LogFile,
|
||||||
|
os.O_CREATE|os.O_WRONLY|os.O_APPEND,
|
||||||
|
0655,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
msg := fmt.Sprintf("Failed to log to file %s: %s", cfg.Config.Application.LogFile, err)
|
||||||
|
Log.Warning(msg)
|
||||||
|
panic(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
mw := io.MultiWriter(os.Stdout, file)
|
||||||
|
Log.SetOutput(mw)
|
||||||
|
|
||||||
|
configJson, err := json.Marshal(cfg.Config)
|
||||||
|
if err == nil {
|
||||||
|
Info(fmt.Sprintf("Using config: %s", configJson))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
20
app/main.go
Normal file
20
app/main.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"repo-pattern/app/lib/cfg"
|
||||||
|
"repo-pattern/app/lib/db"
|
||||||
|
"repo-pattern/app/lib/logging"
|
||||||
|
"repo-pattern/app/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg.Init()
|
||||||
|
logging.Init()
|
||||||
|
defer logging.Log.Sync()
|
||||||
|
|
||||||
|
db := db.InitDB()
|
||||||
|
repository.Dao = repository.CreateDAO(db)
|
||||||
|
|
||||||
|
fmt.Println("Running...")
|
||||||
|
}
|
||||||
23
app/models/api_key.go
Normal file
23
app/models/api_key.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ApiKey struct {
|
||||||
|
Key uuid.UUID `gorm:"type(uuid);unique;default:uuid_generate_v4()"`
|
||||||
|
CompanyId uuid.UUID `gorm:"type(uuid)" faker:"-"`
|
||||||
|
ActiveFrom *time.Time `faker:"-"`
|
||||||
|
ActiveTo *time.Time `faker:"-"`
|
||||||
|
LastUsed *time.Time
|
||||||
|
CreatedAt time.Time `faker:"-"`
|
||||||
|
UpdatedAt time.Time `faker:"-"`
|
||||||
|
|
||||||
|
Company Company `faker:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ApiKey) TableName() string {
|
||||||
|
return "api_keys"
|
||||||
|
}
|
||||||
28
app/models/cert.go
Normal file
28
app/models/cert.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cert struct {
|
||||||
|
Id uuid.UUID `gorm:"type(uuid);unique;default:uuid_generate_v4()"`
|
||||||
|
CompanyId uuid.UUID `gorm:"type(uuid)" faker:"-"`
|
||||||
|
Company Company `faker:"-"`
|
||||||
|
Alive bool `faker:"-"`
|
||||||
|
SerialNumber string
|
||||||
|
Issuer string
|
||||||
|
Subject string
|
||||||
|
ValidAfter time.Time `faker:"-"`
|
||||||
|
ValidBefore time.Time `faker:"-"`
|
||||||
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
|
KeyPassword string
|
||||||
|
CreatedAt time.Time `faker:"-"`
|
||||||
|
UpdatedAt time.Time `faker:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Cert) TableName() string {
|
||||||
|
return "certificates"
|
||||||
|
}
|
||||||
30
app/models/company.go
Normal file
30
app/models/company.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/mozillazg/go-slugify"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Company struct {
|
||||||
|
Id uuid.UUID `gorm:"type(uuid);unique;default:uuid_generate_v4()"`
|
||||||
|
CertificateId *uuid.UUID `gorm:"type(uuid)" faker:"-"`
|
||||||
|
Name string `faker:"name"`
|
||||||
|
Address string
|
||||||
|
City string
|
||||||
|
Email string `faker:"email"`
|
||||||
|
Oib string `faker:"-"`
|
||||||
|
TaxSystem bool
|
||||||
|
IsActive bool `faker:"-"`
|
||||||
|
CreatedAt time.Time `faker:"-"`
|
||||||
|
UpdatedAt time.Time `faker:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Company) TableName() string {
|
||||||
|
return "companies"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Company) SlugifyName() string {
|
||||||
|
return slugify.Slugify(m.Name)
|
||||||
|
}
|
||||||
27
app/models/fisk_log_item.go
Normal file
27
app/models/fisk_log_item.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FiskLogItem struct {
|
||||||
|
Id uuid.UUID `gorm:"type(uuid);unique"`
|
||||||
|
ApiKeyId uuid.UUID `gorm:"type(uuid)"`
|
||||||
|
CompanyId uuid.UUID `gorm:"type(uuid)"`
|
||||||
|
IsDemo bool
|
||||||
|
RequestData datatypes.JSONMap `gorm:"type:jsonb;not null"`
|
||||||
|
ResponseData *datatypes.JSONMap `gorm:"type:jsonb"`
|
||||||
|
IznosUkupno int
|
||||||
|
Zki *string
|
||||||
|
Jir *uuid.UUID `gorm:"type(uuid)"`
|
||||||
|
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *FiskLogItem) TableName() string {
|
||||||
|
return "fisk_log"
|
||||||
|
}
|
||||||
89
app/repository/api_keys.go
Normal file
89
app/repository/api_keys.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"repo-pattern/app/lib/helpers"
|
||||||
|
"repo-pattern/app/models"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ApiKeysRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiKeysFilter struct {
|
||||||
|
Key *uuid.UUID
|
||||||
|
CompanyId *uuid.UUID
|
||||||
|
IsActive *bool
|
||||||
|
CompanyIsActive *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateApiKeysRepository(db *gorm.DB) *ApiKeysRepository {
|
||||||
|
return &ApiKeysRepository{db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyApiKeyFilter(db *gorm.DB, query *gorm.DB, filter *ApiKeysFilter) *gorm.DB {
|
||||||
|
if filter.Key != nil {
|
||||||
|
query.Where("key = ?", *filter.Key)
|
||||||
|
}
|
||||||
|
if filter.IsActive != nil {
|
||||||
|
now := helpers.UTCNow()
|
||||||
|
if *filter.IsActive {
|
||||||
|
query.Where("active_from IS NULL OR active_from <= ?", now)
|
||||||
|
query.Where("active_to IS NULL OR active_to >= ?", now)
|
||||||
|
} else {
|
||||||
|
query.Where(
|
||||||
|
"active_from IS NOT NULL AND active_from >= ?", now,
|
||||||
|
).Or(
|
||||||
|
"active_to IS NOT NULL AND active_to =< ?", now,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if filter.CompanyId != nil {
|
||||||
|
query.Where("company_id = ?", *filter.CompanyId)
|
||||||
|
}
|
||||||
|
if filter.CompanyIsActive != nil {
|
||||||
|
query.Joins("Company").Where(
|
||||||
|
map[string]interface{}{"Company.is_active": *filter.CompanyIsActive},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ApiKeysRepository) New(companyId uuid.UUID) *models.ApiKey {
|
||||||
|
now := helpers.UTCNow()
|
||||||
|
return &models.ApiKey{
|
||||||
|
CompanyId: companyId,
|
||||||
|
ActiveFrom: &now,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ApiKeysRepository) Get(filter *ApiKeysFilter) *models.ApiKey {
|
||||||
|
var api_key models.ApiKey
|
||||||
|
|
||||||
|
query := r.db.Model(&models.ApiKey{})
|
||||||
|
applyApiKeyFilter(r.db, query, filter)
|
||||||
|
|
||||||
|
result := query.First(&api_key).Joins("Company")
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api_key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ApiKeysRepository) Save(model *models.ApiKey) {
|
||||||
|
model.UpdatedAt = helpers.UTCNow()
|
||||||
|
|
||||||
|
// we don't have standard model Id, but Key as primary key.
|
||||||
|
// this could be possible reason for save not assigning generated key uuid
|
||||||
|
// to model.Key. but this works.
|
||||||
|
if model.Key == uuid.Nil {
|
||||||
|
r.db.Create(model)
|
||||||
|
} else {
|
||||||
|
r.db.Save(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
101
app/repository/certs.go
Normal file
101
app/repository/certs.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"repo-pattern/app/lib/helpers"
|
||||||
|
"repo-pattern/app/models"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CertRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertFilter struct {
|
||||||
|
Alive *bool
|
||||||
|
Id *uuid.UUID
|
||||||
|
CompanyId *uuid.UUID
|
||||||
|
CompanyIsActive *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateCertRepository(db *gorm.DB) *CertRepository {
|
||||||
|
return &CertRepository{db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyCertFilter(db *gorm.DB, query *gorm.DB, filter *CertFilter) *gorm.DB {
|
||||||
|
if filter.Id != nil {
|
||||||
|
query.Where("certificates.id = ?", *filter.Id)
|
||||||
|
}
|
||||||
|
if filter.Alive == nil {
|
||||||
|
query.Where("certificates.alive = TRUE")
|
||||||
|
} else {
|
||||||
|
query.Where("certificates.alive = ?", *filter.Alive)
|
||||||
|
}
|
||||||
|
if filter.CompanyId != nil {
|
||||||
|
query.Where("certificates.company_id = ?", *filter.CompanyId)
|
||||||
|
}
|
||||||
|
if filter.CompanyIsActive != nil {
|
||||||
|
query.Joins("LEFT JOIN companies on companies.is_active = TRUE")
|
||||||
|
// query.Joins("Company").Where(
|
||||||
|
// map[string]interface{}{"Company.is_active": *filter.CompanyIsActive},
|
||||||
|
// )
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CertRepository) New(companyId uuid.UUID) *models.Cert {
|
||||||
|
now := helpers.UTCNow()
|
||||||
|
return &models.Cert{
|
||||||
|
Alive: true,
|
||||||
|
CompanyId: companyId,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CertRepository) Get(filter *CertFilter) *models.Cert {
|
||||||
|
var cert models.Cert
|
||||||
|
|
||||||
|
query := r.db.Model(&models.Cert{})
|
||||||
|
applyCertFilter(r.db, query, filter)
|
||||||
|
|
||||||
|
result := query.First(&cert).Joins("Company")
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CertRepository) GetForCompany(companyId uuid.UUID) *models.Cert {
|
||||||
|
isAlive := true
|
||||||
|
filter := CertFilter{
|
||||||
|
Alive: &isAlive,
|
||||||
|
CompanyId: &companyId,
|
||||||
|
}
|
||||||
|
return r.Get(&filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CertRepository) Save(model *models.Cert) {
|
||||||
|
model.UpdatedAt = helpers.UTCNow()
|
||||||
|
r.db.Save(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CertRepository) Exists(filter *CertFilter) bool {
|
||||||
|
var row models.Cert
|
||||||
|
|
||||||
|
query := r.db.Model(&models.Cert{})
|
||||||
|
query = applyCertFilter(r.db, query, filter)
|
||||||
|
result := query.Select("certificates.id").First(&row)
|
||||||
|
|
||||||
|
return !errors.Is(result.Error, gorm.ErrRecordNotFound) && result.Error == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CertRepository) Delete(filter *CertFilter) {
|
||||||
|
query := r.db.Model(&models.Cert{})
|
||||||
|
applyCertFilter(r.db, query, filter)
|
||||||
|
|
||||||
|
query.Update("alive", false).Joins("Company")
|
||||||
|
}
|
||||||
57
app/repository/companies.go
Normal file
57
app/repository/companies.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"repo-pattern/app/lib/helpers"
|
||||||
|
"repo-pattern/app/models"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CompanyRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompanyFilter struct {
|
||||||
|
Id *uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateCompanyRepository(db *gorm.DB) *CompanyRepository {
|
||||||
|
return &CompanyRepository{db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyCompanyFilter(db *gorm.DB, query *gorm.DB, filter *CompanyFilter) *gorm.DB {
|
||||||
|
if filter.Id != nil {
|
||||||
|
query.Where("id = ?", *filter.Id)
|
||||||
|
}
|
||||||
|
query.Where("is_active = ?", true)
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CompanyRepository) New() *models.Company {
|
||||||
|
now := helpers.UTCNow()
|
||||||
|
return &models.Company{
|
||||||
|
IsActive: true,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CompanyRepository) Get(filter *CompanyFilter) *models.Company {
|
||||||
|
var company models.Company
|
||||||
|
|
||||||
|
query := r.db.Model(&models.Company{})
|
||||||
|
applyCompanyFilter(r.db, query, filter)
|
||||||
|
|
||||||
|
result := query.First(&company)
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &company
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CompanyRepository) Save(model *models.Company) {
|
||||||
|
model.UpdatedAt = helpers.UTCNow()
|
||||||
|
r.db.Save(model)
|
||||||
|
}
|
||||||
23
app/repository/dao.go
Normal file
23
app/repository/dao.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
type DAO struct {
|
||||||
|
db *gorm.DB
|
||||||
|
ApiKeysRepository *ApiKeysRepository
|
||||||
|
CertRepository *CertRepository
|
||||||
|
CompanyRepository *CompanyRepository
|
||||||
|
FiskLogRepository *FiskLogRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
var Dao DAO
|
||||||
|
|
||||||
|
func CreateDAO(db *gorm.DB) DAO {
|
||||||
|
return DAO{
|
||||||
|
db: db,
|
||||||
|
ApiKeysRepository: CreateApiKeysRepository(db),
|
||||||
|
CertRepository: CreateCertRepository(db),
|
||||||
|
CompanyRepository: CreateCompanyRepository(db),
|
||||||
|
FiskLogRepository: CreateFiskLogRepository(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/repository/fisk_log.go
Normal file
29
app/repository/fisk_log.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"repo-pattern/app/lib/helpers"
|
||||||
|
"repo-pattern/app/models"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FiskLogRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateFiskLogRepository(db *gorm.DB) *FiskLogRepository {
|
||||||
|
return &FiskLogRepository{db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FiskLogRepository) New(model models.FiskLogItem) *models.FiskLogItem {
|
||||||
|
now := helpers.UTCNow()
|
||||||
|
model.CreatedAt = now
|
||||||
|
model.UpdatedAt = now
|
||||||
|
return &model
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FiskLogRepository) Save(model *models.FiskLogItem) error {
|
||||||
|
model.UpdatedAt = helpers.UTCNow()
|
||||||
|
result := r.db.Save(model)
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
35
go.mod
Normal file
35
go.mod
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
module repo-pattern
|
||||||
|
|
||||||
|
go 1.22.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
|
github.com/mozillazg/go-slugify v0.2.0
|
||||||
|
github.com/stretchr/testify v1.8.1
|
||||||
|
go.uber.org/zap v1.27.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
gorm.io/datatypes v1.2.1
|
||||||
|
gorm.io/driver/postgres v1.5.9
|
||||||
|
gorm.io/gorm v1.25.10
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/mozillazg/go-unidecode v0.2.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
|
golang.org/x/crypto v0.22.0 // indirect
|
||||||
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
gorm.io/driver/mysql v1.5.6 // indirect
|
||||||
|
)
|
||||||
84
go.sum
Normal file
84
go.sum
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
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/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
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/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
|
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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||||
|
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||||
|
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
|
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
||||||
|
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
||||||
|
github.com/mozillazg/go-slugify v0.2.0 h1:SIhqDlnJWZH8OdiTmQgeXR28AOnypmAXPeOTcG7b9lk=
|
||||||
|
github.com/mozillazg/go-slugify v0.2.0/go.mod h1:z7dPH74PZf2ZPFkyxx+zjPD8CNzRJNa1CGacv0gg8Ns=
|
||||||
|
github.com/mozillazg/go-unidecode v0.2.0 h1:vFGEzAH9KSwyWmXCOblazEWDh7fOkpmy/Z4ArmamSUc=
|
||||||
|
github.com/mozillazg/go-unidecode v0.2.0/go.mod h1:zB48+/Z5toiRolOZy9ksLryJ976VIwmDmpQ2quyt1aA=
|
||||||
|
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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
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=
|
||||||
|
gorm.io/datatypes v1.2.1 h1:r+g0bk4LPCW2v4+Ls7aeNgGme7JYdNDQ2VtvlNUfBh0=
|
||||||
|
gorm.io/datatypes v1.2.1/go.mod h1:hYK6OTb/1x+m96PgoZZq10UXJ6RvEBb9kRDQ2yyhzGs=
|
||||||
|
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
||||||
|
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||||
|
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
||||||
|
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||||
|
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
|
||||||
|
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||||
|
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
||||||
|
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
||||||
|
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
|
||||||
|
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
Reference in New Issue
Block a user