working on auth.
mailer, basic setup with html template and a dev treansport
This commit is contained in:
parent
b0db98452a
commit
26a00c9f7c
@ -4,3 +4,5 @@ GRAPH_PORT = 3009
|
||||
GRAPH_URL = http://localhost:3009
|
||||
VITE_GRAPH_URL = http://localhost:3009
|
||||
DB_URL = postgresql://root:root@127.0.0.1/rano_dev?search_path=public&sslmode=disable
|
||||
MAILER_TEMPLATES_DIR = mailer/templates
|
||||
MAILER_FROM_ADDRESS = NoReply<no-reply@my-app.com>
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
"gitserver.in/patialtech/rano/db"
|
||||
entMigrate "gitserver.in/patialtech/rano/db/ent/migrate"
|
||||
"gitserver.in/patialtech/rano/db/migrations"
|
||||
"gitserver.in/patialtech/rano/pkg/logger"
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"gitserver.in/patialtech/rano/config/dotenv"
|
||||
"gitserver.in/patialtech/rano/pkg/logger"
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -30,33 +30,35 @@ var (
|
||||
type (
|
||||
Env string
|
||||
Config struct {
|
||||
WebPort int `env:"WEB_PORT"`
|
||||
WebURL string `env:"WEB_URL"`
|
||||
GraphPort int `env:"GRAPH_PORT"`
|
||||
GrapURL string `env:"GRAPH_URL"`
|
||||
DbURL string `env:"DB_URL"`
|
||||
basePath string
|
||||
WebPort int `env:"WEB_PORT"`
|
||||
WebURL string `env:"WEB_URL"`
|
||||
GraphPort int `env:"GRAPH_PORT"`
|
||||
GrapURL string `env:"GRAPH_URL"`
|
||||
DbURL string `env:"DB_URL"`
|
||||
MailerTplDir string `env:"MAILER_TEMPLATES_DIR"`
|
||||
MailerFrom string `env:"MAILER_FROM_ADDRESS"`
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
// In dev env we run test and other program for diff dir locations under project root,
|
||||
// this makes reading env file harder.
|
||||
// Let's add a hack to make sure we fallback to root dir in dev env
|
||||
var base string
|
||||
if AppEnv == EnvDev {
|
||||
wd, _ := os.Getwd()
|
||||
idx := strings.Index(wd, projDir)
|
||||
if idx > -1 {
|
||||
wd = filepath.Join(wd[:idx], projDir)
|
||||
base = filepath.Join(wd[:idx], projDir)
|
||||
}
|
||||
} else {
|
||||
base, _ = os.Executable()
|
||||
}
|
||||
|
||||
envVar, err := dotenv.Read(wd, fmt.Sprintf(".env.%s", AppEnv))
|
||||
envVar, err := dotenv.Read(base, fmt.Sprintf(".env.%s", AppEnv))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conf = &Config{}
|
||||
conf = &Config{basePath: base}
|
||||
conf.loadEnv(envVar)
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitserver.in/patialtech/rano/pkg/logger"
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
)
|
||||
|
||||
//
|
||||
|
15
db/client.go
15
db/client.go
@ -12,7 +12,7 @@ import (
|
||||
pgx "github.com/jackc/pgx/v5/stdlib"
|
||||
"gitserver.in/patialtech/rano/config"
|
||||
"gitserver.in/patialtech/rano/db/ent"
|
||||
"gitserver.in/patialtech/rano/pkg/logger"
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
)
|
||||
|
||||
type connector struct {
|
||||
@ -56,17 +56,20 @@ func Client() *ent.Client {
|
||||
|
||||
// A AuditHook is an example for audit-log hook.
|
||||
func AuditHook(next ent.Mutator) ent.Mutator {
|
||||
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (v ent.Value, err error) {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
saveAudit(ctx, m.Type(), m.Op().String(), time.Since(start))
|
||||
saveAudit(ctx, m.Type(), m.Op().String(), time.Since(start), err)
|
||||
}()
|
||||
return next.Mutate(ctx, m)
|
||||
|
||||
v, err = next.Mutate(ctx, m)
|
||||
logger.Info("** %v", v)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func saveAudit(_ context.Context, entT, op string, d time.Duration) {
|
||||
logger.Info("audit %s %s %s", entT, op, d)
|
||||
func saveAudit(_ context.Context, entT, op string, d time.Duration, err error) {
|
||||
logger.Info("audit: %s %s %s %v", entT, op, d, err)
|
||||
// ml.SetCreatedAt(time.Now())
|
||||
// if usr := auth.CtxUser(ctx); usr != nil {
|
||||
// ml.SetByID(usr.ID)
|
||||
|
@ -99,10 +99,10 @@ var (
|
||||
{Name: "updated_at", Type: field.TypeTime},
|
||||
{Name: "email", Type: field.TypeString, Unique: true},
|
||||
{Name: "email_verified", Type: field.TypeBool, Default: false},
|
||||
{Name: "phone", Type: field.TypeString, Size: 20},
|
||||
{Name: "phone", Type: field.TypeString, Nullable: true, Size: 20},
|
||||
{Name: "phone_verified", Type: field.TypeBool, Default: false},
|
||||
{Name: "pwd_salt", Type: field.TypeString},
|
||||
{Name: "pwd_hash", Type: field.TypeString},
|
||||
{Name: "pwd_salt", Type: field.TypeString, Size: 250},
|
||||
{Name: "pwd_hash", Type: field.TypeString, Size: 250},
|
||||
{Name: "login_failed_count", Type: field.TypeUint8, Nullable: true, Default: 0},
|
||||
{Name: "login_attempt_on", Type: field.TypeTime, Nullable: true},
|
||||
{Name: "login_locked_until", Type: field.TypeTime, Nullable: true},
|
||||
|
@ -2509,9 +2509,22 @@ func (m *UserMutation) OldPhone(ctx context.Context) (v string, err error) {
|
||||
return oldValue.Phone, nil
|
||||
}
|
||||
|
||||
// ClearPhone clears the value of the "phone" field.
|
||||
func (m *UserMutation) ClearPhone() {
|
||||
m.phone = nil
|
||||
m.clearedFields[user.FieldPhone] = struct{}{}
|
||||
}
|
||||
|
||||
// PhoneCleared returns if the "phone" field was cleared in this mutation.
|
||||
func (m *UserMutation) PhoneCleared() bool {
|
||||
_, ok := m.clearedFields[user.FieldPhone]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ResetPhone resets all changes to the "phone" field.
|
||||
func (m *UserMutation) ResetPhone() {
|
||||
m.phone = nil
|
||||
delete(m.clearedFields, user.FieldPhone)
|
||||
}
|
||||
|
||||
// SetPhoneVerified sets the "phone_verified" field.
|
||||
@ -3358,6 +3371,9 @@ func (m *UserMutation) AddField(name string, value ent.Value) error {
|
||||
// mutation.
|
||||
func (m *UserMutation) ClearedFields() []string {
|
||||
var fields []string
|
||||
if m.FieldCleared(user.FieldPhone) {
|
||||
fields = append(fields, user.FieldPhone)
|
||||
}
|
||||
if m.FieldCleared(user.FieldLoginFailedCount) {
|
||||
fields = append(fields, user.FieldLoginFailedCount)
|
||||
}
|
||||
@ -3381,6 +3397,9 @@ func (m *UserMutation) FieldCleared(name string) bool {
|
||||
// error if the field is not defined in the schema.
|
||||
func (m *UserMutation) ClearField(name string) error {
|
||||
switch name {
|
||||
case user.FieldPhone:
|
||||
m.ClearPhone()
|
||||
return nil
|
||||
case user.FieldLoginFailedCount:
|
||||
m.ClearLoginFailedCount()
|
||||
return nil
|
||||
|
@ -113,11 +113,39 @@ func init() {
|
||||
// userDescPwdSalt is the schema descriptor for pwd_salt field.
|
||||
userDescPwdSalt := userFields[7].Descriptor()
|
||||
// user.PwdSaltValidator is a validator for the "pwd_salt" field. It is called by the builders before save.
|
||||
user.PwdSaltValidator = userDescPwdSalt.Validators[0].(func(string) error)
|
||||
user.PwdSaltValidator = func() func(string) error {
|
||||
validators := userDescPwdSalt.Validators
|
||||
fns := [...]func(string) error{
|
||||
validators[0].(func(string) error),
|
||||
validators[1].(func(string) error),
|
||||
}
|
||||
return func(pwd_salt string) error {
|
||||
for _, fn := range fns {
|
||||
if err := fn(pwd_salt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
// userDescPwdHash is the schema descriptor for pwd_hash field.
|
||||
userDescPwdHash := userFields[8].Descriptor()
|
||||
// user.PwdHashValidator is a validator for the "pwd_hash" field. It is called by the builders before save.
|
||||
user.PwdHashValidator = userDescPwdHash.Validators[0].(func(string) error)
|
||||
user.PwdHashValidator = func() func(string) error {
|
||||
validators := userDescPwdHash.Validators
|
||||
fns := [...]func(string) error{
|
||||
validators[0].(func(string) error),
|
||||
validators[1].(func(string) error),
|
||||
}
|
||||
return func(pwd_hash string) error {
|
||||
for _, fn := range fns {
|
||||
if err := fn(pwd_hash); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
// userDescLoginFailedCount is the schema descriptor for login_failed_count field.
|
||||
userDescLoginFailedCount := userFields[9].Descriptor()
|
||||
// user.DefaultLoginFailedCount holds the default value on creation for the login_failed_count field.
|
||||
|
@ -21,10 +21,10 @@ func (User) Fields() []ent.Field {
|
||||
fieldUpdated,
|
||||
field.String("email").Unique().NotEmpty(),
|
||||
field.Bool("email_verified").Default(false),
|
||||
field.String("phone").MaxLen(20),
|
||||
field.String("phone").MaxLen(20).Optional(),
|
||||
field.Bool("phone_verified").Default(false),
|
||||
field.String("pwd_salt").NotEmpty(),
|
||||
field.String("pwd_hash").NotEmpty(),
|
||||
field.String("pwd_salt").MaxLen(250).NotEmpty(),
|
||||
field.String("pwd_hash").MaxLen(250).NotEmpty(),
|
||||
field.Uint8("login_failed_count").Optional().Default(0),
|
||||
field.Time("login_attempt_on").Optional().Nillable(),
|
||||
field.Time("login_locked_until").Optional().Nillable(),
|
||||
|
@ -335,6 +335,16 @@ func PhoneHasSuffix(v string) predicate.User {
|
||||
return predicate.User(sql.FieldHasSuffix(FieldPhone, v))
|
||||
}
|
||||
|
||||
// PhoneIsNil applies the IsNil predicate on the "phone" field.
|
||||
func PhoneIsNil() predicate.User {
|
||||
return predicate.User(sql.FieldIsNull(FieldPhone))
|
||||
}
|
||||
|
||||
// PhoneNotNil applies the NotNil predicate on the "phone" field.
|
||||
func PhoneNotNil() predicate.User {
|
||||
return predicate.User(sql.FieldNotNull(FieldPhone))
|
||||
}
|
||||
|
||||
// PhoneEqualFold applies the EqualFold predicate on the "phone" field.
|
||||
func PhoneEqualFold(v string) predicate.User {
|
||||
return predicate.User(sql.FieldEqualFold(FieldPhone, v))
|
||||
|
@ -76,6 +76,14 @@ func (uc *UserCreate) SetPhone(s string) *UserCreate {
|
||||
return uc
|
||||
}
|
||||
|
||||
// SetNillablePhone sets the "phone" field if the given value is not nil.
|
||||
func (uc *UserCreate) SetNillablePhone(s *string) *UserCreate {
|
||||
if s != nil {
|
||||
uc.SetPhone(*s)
|
||||
}
|
||||
return uc
|
||||
}
|
||||
|
||||
// SetPhoneVerified sets the "phone_verified" field.
|
||||
func (uc *UserCreate) SetPhoneVerified(b bool) *UserCreate {
|
||||
uc.mutation.SetPhoneVerified(b)
|
||||
@ -292,9 +300,6 @@ func (uc *UserCreate) check() error {
|
||||
if _, ok := uc.mutation.EmailVerified(); !ok {
|
||||
return &ValidationError{Name: "email_verified", err: errors.New(`ent: missing required field "User.email_verified"`)}
|
||||
}
|
||||
if _, ok := uc.mutation.Phone(); !ok {
|
||||
return &ValidationError{Name: "phone", err: errors.New(`ent: missing required field "User.phone"`)}
|
||||
}
|
||||
if v, ok := uc.mutation.Phone(); ok {
|
||||
if err := user.PhoneValidator(v); err != nil {
|
||||
return &ValidationError{Name: "phone", err: fmt.Errorf(`ent: validator failed for field "User.phone": %w`, err)}
|
||||
|
@ -78,6 +78,12 @@ func (uu *UserUpdate) SetNillablePhone(s *string) *UserUpdate {
|
||||
return uu
|
||||
}
|
||||
|
||||
// ClearPhone clears the value of the "phone" field.
|
||||
func (uu *UserUpdate) ClearPhone() *UserUpdate {
|
||||
uu.mutation.ClearPhone()
|
||||
return uu
|
||||
}
|
||||
|
||||
// SetPhoneVerified sets the "phone_verified" field.
|
||||
func (uu *UserUpdate) SetPhoneVerified(b bool) *UserUpdate {
|
||||
uu.mutation.SetPhoneVerified(b)
|
||||
@ -425,6 +431,9 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||
if value, ok := uu.mutation.Phone(); ok {
|
||||
_spec.SetField(user.FieldPhone, field.TypeString, value)
|
||||
}
|
||||
if uu.mutation.PhoneCleared() {
|
||||
_spec.ClearField(user.FieldPhone, field.TypeString)
|
||||
}
|
||||
if value, ok := uu.mutation.PhoneVerified(); ok {
|
||||
_spec.SetField(user.FieldPhoneVerified, field.TypeBool, value)
|
||||
}
|
||||
@ -625,6 +634,12 @@ func (uuo *UserUpdateOne) SetNillablePhone(s *string) *UserUpdateOne {
|
||||
return uuo
|
||||
}
|
||||
|
||||
// ClearPhone clears the value of the "phone" field.
|
||||
func (uuo *UserUpdateOne) ClearPhone() *UserUpdateOne {
|
||||
uuo.mutation.ClearPhone()
|
||||
return uuo
|
||||
}
|
||||
|
||||
// SetPhoneVerified sets the "phone_verified" field.
|
||||
func (uuo *UserUpdateOne) SetPhoneVerified(b bool) *UserUpdateOne {
|
||||
uuo.mutation.SetPhoneVerified(b)
|
||||
@ -1002,6 +1017,9 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
|
||||
if value, ok := uuo.mutation.Phone(); ok {
|
||||
_spec.SetField(user.FieldPhone, field.TypeString, value)
|
||||
}
|
||||
if uuo.mutation.PhoneCleared() {
|
||||
_spec.ClearField(user.FieldPhone, field.TypeString)
|
||||
}
|
||||
if value, ok := uuo.mutation.PhoneVerified(); ok {
|
||||
_spec.SetField(user.FieldPhoneVerified, field.TypeBool, value)
|
||||
}
|
||||
|
11
go.mod
11
go.mod
@ -7,15 +7,18 @@ require (
|
||||
github.com/jackc/pgx/v5 v5.7.1
|
||||
github.com/vektah/gqlparser/v2 v2.5.19
|
||||
gitserver.in/patialtech/mux v0.3.1
|
||||
golang.org/x/crypto v0.29.0
|
||||
)
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.28.1 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-openapi/inflect v0.21.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
@ -25,6 +28,7 @@ require (
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
@ -32,8 +36,9 @@ require (
|
||||
github.com/zclconf/go-cty v1.15.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@ -42,12 +47,12 @@ require (
|
||||
entgo.io/ent v0.14.1
|
||||
github.com/agnivade/levenshtein v1.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sosodev/duration v1.3.1 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.5 // indirect
|
||||
|
61
go.sum
61
go.sum
@ -1,5 +1,3 @@
|
||||
ariga.io/atlas v0.25.1-0.20240717145915-af51d3945208 h1:ixs1c/fAXGS3mTdalyKQrtvfkFjgChih/unX66YTzYk=
|
||||
ariga.io/atlas v0.25.1-0.20240717145915-af51d3945208/go.mod h1:KPLc7Zj+nzoXfWshrcY1RwlOh94dsATQEy4UPrF2RkM=
|
||||
ariga.io/atlas v0.28.1 h1:cNE0FYmoYs1u4KF+FGnp2on1srhM6FDpjaCgL7Rd8/c=
|
||||
ariga.io/atlas v0.28.1/go.mod h1:LOOp18LCL9r+VifvVlJqgYJwYl271rrXD9/wIyzJ8sw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
@ -9,8 +7,6 @@ entgo.io/contrib v0.6.0 h1:xfo4TbJE7sJZWx7BV7YrpSz7IPFvS8MzL3fnfzZjKvQ=
|
||||
entgo.io/contrib v0.6.0/go.mod h1:3qWIseJ/9Wx2Hu5zVh15FDzv7d/UvKNcYKdViywWCQg=
|
||||
entgo.io/ent v0.14.1 h1:fUERL506Pqr92EPHJqr8EYxbPioflJo6PudkrEA8a/s=
|
||||
entgo.io/ent v0.14.1/go.mod h1:MH6XLG0KXpkcDQhKiHfANZSzR55TJyPL5IGNpI8wpco=
|
||||
github.com/99designs/gqlgen v0.17.55 h1:3vzrNWYyzSZjGDFo68e5j9sSauLxfKvLp+6ioRokVtM=
|
||||
github.com/99designs/gqlgen v0.17.55/go.mod h1:3Bq768f8hgVPGZxL8aY9MaYmbxa6llPM/qu1IGH1EJo=
|
||||
github.com/99designs/gqlgen v0.17.56 h1:+J42ARAHvnysH6klO9Wq+tCsGF32cpAgU3SyF0VRJtI=
|
||||
github.com/99designs/gqlgen v0.17.56/go.mod h1:rmB6vLvtL8uf9F9w0/irJ5alBkD8DJvj35ET31BKbtY=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
@ -22,8 +18,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0=
|
||||
github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U=
|
||||
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY=
|
||||
@ -32,8 +26,6 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||
@ -66,14 +58,22 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
|
||||
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
|
||||
github.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk=
|
||||
github.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
@ -116,8 +116,6 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc=
|
||||
github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0=
|
||||
github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M=
|
||||
github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
@ -132,20 +130,16 @@ 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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
@ -181,7 +175,6 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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=
|
||||
@ -190,22 +183,18 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
|
||||
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||
github.com/vektah/gqlparser/v2 v2.5.18 h1:zSND3GtutylAQ1JpWnTHcqtaRZjl+y3NROeW8vuNo6Y=
|
||||
github.com/vektah/gqlparser/v2 v2.5.18/go.mod h1:6HLzf7JKv9Fi3APymudztFQNmLXR5qJeEo6BOFcXVfc=
|
||||
github.com/vektah/gqlparser/v2 v2.5.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egplt7iSg=
|
||||
github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
|
||||
github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
|
||||
github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
gitserver.in/patialtech/mux v0.3.1 h1:lbhQVr2vBvTcUp64Qjd2+4/s2lQXiDtsl8c+PpZvnDE=
|
||||
gitserver.in/patialtech/mux v0.3.1/go.mod h1:/pYaLBNkRiMuxMKn9e2X0BIWt1bvHM19yQE/cJsm0q0=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
@ -218,26 +207,18 @@ go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2
|
||||
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo=
|
||||
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -246,28 +227,22 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -275,8 +250,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -302,8 +275,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
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.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
|
||||
schema:
|
||||
- graph/*.graphql
|
||||
- graph/**/*.graphql
|
||||
|
||||
# Where should the generated server code go?
|
||||
exec:
|
||||
|
@ -1,12 +1,9 @@
|
||||
extend type Mutation {
|
||||
login(username: String!, email: String!): Boolean!
|
||||
login(email: String!, pwd: String!): AuthUser!
|
||||
logout: Boolean!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
"""
|
||||
me, is current AuthUser info
|
||||
"""
|
||||
me: AuthUser
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package graph
|
||||
|
||||
// This file will be automatically regenerated based on the schema, any resolver implementations
|
||||
// will be copied through when generating and any unknown code will be moved to the end.
|
||||
// Code generated by github.com/99designs/gqlgen version v0.17.55
|
||||
// Code generated by github.com/99designs/gqlgen version v0.17.56
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// Login is the resolver for the login field.
|
||||
func (r *mutationResolver) Login(ctx context.Context, username string, email string) (bool, error) {
|
||||
func (r *mutationResolver) Login(ctx context.Context, email string, pwd string) (*model.AuthUser, error) {
|
||||
panic(fmt.Errorf("not implemented: Login - login"))
|
||||
}
|
||||
|
@ -273,6 +273,20 @@ func (ec *executionContext) _AuthUser(ctx context.Context, sel ast.SelectionSet,
|
||||
|
||||
// region ***************************** type.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) marshalNAuthUser2gitserverᚗinᚋpatialtechᚋranoᚋgraphᚋmodelᚐAuthUser(ctx context.Context, sel ast.SelectionSet, v model.AuthUser) graphql.Marshaler {
|
||||
return ec._AuthUser(ctx, sel, &v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNAuthUser2ᚖgitserverᚗinᚋpatialtechᚋranoᚋgraphᚋmodelᚐAuthUser(ctx context.Context, sel ast.SelectionSet, v *model.AuthUser) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._AuthUser(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOAuthUser2ᚖgitserverᚗinᚋpatialtechᚋranoᚋgraphᚋmodelᚐAuthUser(ctx context.Context, sel ast.SelectionSet, v *model.AuthUser) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
@ -18,11 +18,10 @@ import (
|
||||
// region ************************** generated!.gotpl **************************
|
||||
|
||||
type MutationResolver interface {
|
||||
Login(ctx context.Context, username string, email string) (bool, error)
|
||||
Login(ctx context.Context, email string, pwd string) (*model.AuthUser, error)
|
||||
Logout(ctx context.Context) (bool, error)
|
||||
}
|
||||
type QueryResolver interface {
|
||||
HeartBeat(ctx context.Context) (bool, error)
|
||||
Me(ctx context.Context) (*model.AuthUser, error)
|
||||
}
|
||||
|
||||
@ -33,24 +32,24 @@ type QueryResolver interface {
|
||||
func (ec *executionContext) field_Mutation_login_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
arg0, err := ec.field_Mutation_login_argsUsername(ctx, rawArgs)
|
||||
arg0, err := ec.field_Mutation_login_argsEmail(ctx, rawArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["username"] = arg0
|
||||
arg1, err := ec.field_Mutation_login_argsEmail(ctx, rawArgs)
|
||||
args["email"] = arg0
|
||||
arg1, err := ec.field_Mutation_login_argsPwd(ctx, rawArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["email"] = arg1
|
||||
args["pwd"] = arg1
|
||||
return args, nil
|
||||
}
|
||||
func (ec *executionContext) field_Mutation_login_argsUsername(
|
||||
func (ec *executionContext) field_Mutation_login_argsEmail(
|
||||
ctx context.Context,
|
||||
rawArgs map[string]interface{},
|
||||
) (string, error) {
|
||||
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("username"))
|
||||
if tmp, ok := rawArgs["username"]; ok {
|
||||
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
if tmp, ok := rawArgs["email"]; ok {
|
||||
return ec.unmarshalNString2string(ctx, tmp)
|
||||
}
|
||||
|
||||
@ -58,12 +57,12 @@ func (ec *executionContext) field_Mutation_login_argsUsername(
|
||||
return zeroVal, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_login_argsEmail(
|
||||
func (ec *executionContext) field_Mutation_login_argsPwd(
|
||||
ctx context.Context,
|
||||
rawArgs map[string]interface{},
|
||||
) (string, error) {
|
||||
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
if tmp, ok := rawArgs["email"]; ok {
|
||||
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("pwd"))
|
||||
if tmp, ok := rawArgs["pwd"]; ok {
|
||||
return ec.unmarshalNString2string(ctx, tmp)
|
||||
}
|
||||
|
||||
@ -116,7 +115,7 @@ func (ec *executionContext) _Mutation_login(ctx context.Context, field graphql.C
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Mutation().Login(rctx, fc.Args["username"].(string), fc.Args["email"].(string))
|
||||
return ec.resolvers.Mutation().Login(rctx, fc.Args["email"].(string), fc.Args["pwd"].(string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
@ -128,9 +127,9 @@ func (ec *executionContext) _Mutation_login(ctx context.Context, field graphql.C
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
res := resTmp.(*model.AuthUser)
|
||||
fc.Result = res
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
return ec.marshalNAuthUser2ᚖgitserverᚗinᚋpatialtechᚋranoᚋgraphᚋmodelᚐAuthUser(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Mutation_login(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
@ -140,7 +139,17 @@ func (ec *executionContext) fieldContext_Mutation_login(ctx context.Context, fie
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type Boolean does not have child fields")
|
||||
switch field.Name {
|
||||
case "id":
|
||||
return ec.fieldContext_AuthUser_id(ctx, field)
|
||||
case "email":
|
||||
return ec.fieldContext_AuthUser_email(ctx, field)
|
||||
case "displayName":
|
||||
return ec.fieldContext_AuthUser_displayName(ctx, field)
|
||||
case "roleID":
|
||||
return ec.fieldContext_AuthUser_roleID(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type AuthUser", field.Name)
|
||||
},
|
||||
}
|
||||
defer func() {
|
||||
@ -201,50 +210,6 @@ func (ec *executionContext) fieldContext_Mutation_logout(_ context.Context, fiel
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_heartBeat(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Query_heartBeat(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().HeartBeat(rctx)
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
fc.Result = res
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Query_heartBeat(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Query",
|
||||
Field: field,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type Boolean does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_me(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Query_me(ctx, field)
|
||||
if err != nil {
|
||||
@ -512,28 +477,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("Query")
|
||||
case "heartBeat":
|
||||
field := field
|
||||
|
||||
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Query_heartBeat(ctx, field)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&fs.Invalids, 1)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
rrm := func(ctx context.Context) graphql.Marshaler {
|
||||
return ec.OperationContext.RootResolverMiddleware(ctx,
|
||||
func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
|
||||
}
|
||||
|
||||
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
|
||||
case "me":
|
||||
field := field
|
||||
|
@ -48,13 +48,12 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
Mutation struct {
|
||||
Login func(childComplexity int, username string, email string) int
|
||||
Login func(childComplexity int, email string, pwd string) int
|
||||
Logout func(childComplexity int) int
|
||||
}
|
||||
|
||||
Query struct {
|
||||
HeartBeat func(childComplexity int) int
|
||||
Me func(childComplexity int) int
|
||||
Me func(childComplexity int) int
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +114,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.Login(childComplexity, args["username"].(string), args["email"].(string)), true
|
||||
return e.complexity.Mutation.Login(childComplexity, args["email"].(string), args["pwd"].(string)), true
|
||||
|
||||
case "Mutation.logout":
|
||||
if e.complexity.Mutation.Logout == nil {
|
||||
@ -124,13 +123,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.Mutation.Logout(childComplexity), true
|
||||
|
||||
case "Query.heartBeat":
|
||||
if e.complexity.Query.HeartBeat == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Query.HeartBeat(childComplexity), true
|
||||
|
||||
case "Query.me":
|
||||
if e.complexity.Query.Me == nil {
|
||||
break
|
||||
@ -143,12 +135,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
}
|
||||
|
||||
func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||
rc := graphql.GetOperationContext(ctx)
|
||||
ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)}
|
||||
opCtx := graphql.GetOperationContext(ctx)
|
||||
ec := executionContext{opCtx, e, 0, 0, make(chan graphql.DeferredResult)}
|
||||
inputUnmarshalMap := graphql.BuildUnmarshalerMap()
|
||||
first := true
|
||||
|
||||
switch rc.Operation.Operation {
|
||||
switch opCtx.Operation.Operation {
|
||||
case ast.Query:
|
||||
return func(ctx context.Context) *graphql.Response {
|
||||
var response graphql.Response
|
||||
@ -156,7 +148,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||
if first {
|
||||
first = false
|
||||
ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
|
||||
data = ec._Query(ctx, rc.Operation.SelectionSet)
|
||||
data = ec._Query(ctx, opCtx.Operation.SelectionSet)
|
||||
} else {
|
||||
if atomic.LoadInt32(&ec.pendingDeferred) > 0 {
|
||||
result := <-ec.deferredResults
|
||||
@ -186,7 +178,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||
}
|
||||
first = false
|
||||
ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
|
||||
data := ec._Mutation(ctx, rc.Operation.SelectionSet)
|
||||
data := ec._Mutation(ctx, opCtx.Operation.SelectionSet)
|
||||
var buf bytes.Buffer
|
||||
data.MarshalGQL(&buf)
|
||||
|
||||
@ -242,15 +234,12 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er
|
||||
}
|
||||
|
||||
var sources = []*ast.Source{
|
||||
{Name: "../auth.graphql", Input: `extend type Mutation {
|
||||
login(username: String!, email: String!): Boolean!
|
||||
{Name: "../account.graphql", Input: `extend type Mutation {
|
||||
login(email: String!, pwd: String!): AuthUser!
|
||||
logout: Boolean!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
"""
|
||||
me, is current AuthUser info
|
||||
"""
|
||||
me: AuthUser
|
||||
}
|
||||
|
||||
@ -261,15 +250,13 @@ type AuthUser {
|
||||
roleID: Int!
|
||||
}
|
||||
`, BuiltIn: false},
|
||||
{Name: "../index.graphql", Input: `# GraphQL schema example
|
||||
{Name: "../root.graphql", Input: `# GraphQL schema example
|
||||
#
|
||||
# https://gqlgen.com/getting-started/
|
||||
|
||||
type Mutation
|
||||
|
||||
type Query {
|
||||
heartBeat: Boolean!
|
||||
}
|
||||
type Query
|
||||
|
||||
"""
|
||||
Maps a Time GraphQL scalar to a Go time.Time struct.
|
||||
|
@ -1,28 +0,0 @@
|
||||
package graph
|
||||
|
||||
// This file will be automatically regenerated based on the schema, any resolver implementations
|
||||
// will be copied through when generating and any unknown code will be moved to the end.
|
||||
// Code generated by github.com/99designs/gqlgen version v0.17.55
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
graph "gitserver.in/patialtech/rano/graph/generated"
|
||||
)
|
||||
|
||||
// HeartBeat is the resolver for the heartBeat field.
|
||||
func (r *queryResolver) HeartBeat(ctx context.Context) (bool, error) {
|
||||
// do needful checkup
|
||||
//
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Mutation returns graph.MutationResolver implementation.
|
||||
func (r *Resolver) Mutation() graph.MutationResolver { return &mutationResolver{r} }
|
||||
|
||||
// Query returns graph.QueryResolver implementation.
|
||||
func (r *Resolver) Query() graph.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"gitserver.in/patialtech/rano/graph/generated"
|
||||
"gitserver.in/patialtech/rano/pkg/logger"
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
)
|
||||
|
||||
// This file will not be regenerated automatically.
|
||||
|
@ -4,9 +4,7 @@
|
||||
|
||||
type Mutation
|
||||
|
||||
type Query {
|
||||
heartBeat: Boolean!
|
||||
}
|
||||
type Query
|
||||
|
||||
"""
|
||||
Maps a Time GraphQL scalar to a Go time.Time struct.
|
18
graph/root.resolvers.go
Normal file
18
graph/root.resolvers.go
Normal file
@ -0,0 +1,18 @@
|
||||
package graph
|
||||
|
||||
// This file will be automatically regenerated based on the schema, any resolver implementations
|
||||
// will be copied through when generating and any unknown code will be moved to the end.
|
||||
// Code generated by github.com/99designs/gqlgen version v0.17.56
|
||||
|
||||
import (
|
||||
"gitserver.in/patialtech/rano/graph/generated"
|
||||
)
|
||||
|
||||
// Mutation returns generated.MutationResolver implementation.
|
||||
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
|
||||
|
||||
// Query returns generated.QueryResolver implementation.
|
||||
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
@ -8,7 +8,7 @@ import (
|
||||
"gitserver.in/patialtech/mux/middleware"
|
||||
"gitserver.in/patialtech/rano/config"
|
||||
"gitserver.in/patialtech/rano/graph"
|
||||
"gitserver.in/patialtech/rano/pkg/logger"
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
72
mailer/mailer.go
Normal file
72
mailer/mailer.go
Normal file
@ -0,0 +1,72 @@
|
||||
package mailer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/mail"
|
||||
|
||||
"gitserver.in/patialtech/rano/config"
|
||||
)
|
||||
|
||||
type (
|
||||
transporter interface {
|
||||
send(*message) error
|
||||
}
|
||||
|
||||
Recipients struct {
|
||||
To []mail.Address `json:"to"`
|
||||
Cc []mail.Address `json:"cc"`
|
||||
Bcc []mail.Address `json:"bcc"`
|
||||
}
|
||||
|
||||
message struct {
|
||||
From string `json:"from" validate:"required"`
|
||||
Recipients Recipients `json:"recipients" validate:"required"`
|
||||
Subject string `json:"subject" validate:"required"`
|
||||
HtmlBody string `json:"htmlBody" validate:"required"`
|
||||
ReplyTo *mail.Address `json:"replyTo"`
|
||||
}
|
||||
|
||||
Template interface {
|
||||
Subject() string
|
||||
HtmlBody() (string, error)
|
||||
}
|
||||
)
|
||||
|
||||
func Send(to []mail.Address, tpl Template) error {
|
||||
return send(Recipients{To: to}, tpl)
|
||||
}
|
||||
|
||||
func SendCC(subject string, to, cc []mail.Address, tpl Template) error {
|
||||
return send(Recipients{To: to, Cc: cc}, tpl)
|
||||
}
|
||||
|
||||
func send(recipients Recipients, tpl Template) error {
|
||||
if tpl == nil {
|
||||
return errors.New("mailer: email template is nil")
|
||||
}
|
||||
|
||||
if len(recipients.To) == 0 {
|
||||
return errors.New("mailer: no recipient found")
|
||||
}
|
||||
|
||||
// TODO remove recepient with bounce hiostory
|
||||
|
||||
body, err := tpl.HtmlBody()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// get ENV based transporter and send mail
|
||||
return getTransporter().send(&message{
|
||||
From: config.Read().MailerFrom,
|
||||
Recipients: recipients,
|
||||
Subject: tpl.Subject(),
|
||||
HtmlBody: body,
|
||||
})
|
||||
}
|
||||
|
||||
func getTransporter() transporter {
|
||||
switch config.AppEnv {
|
||||
default:
|
||||
return transportDev{}
|
||||
}
|
||||
}
|
120
mailer/message/_layout.html
Normal file
120
mailer/message/_layout.html
Normal file
@ -0,0 +1,120 @@
|
||||
{{define "layout"}}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>{{.Title}}</title>
|
||||
</head>
|
||||
<body
|
||||
style="
|
||||
background-color: #f2f2f2;
|
||||
margin: 0;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
"
|
||||
>
|
||||
<table cellpadding="0" cellspacing="0" border="0" bgcolor="#ffffff" align="center" style="width: 100%">
|
||||
<tbody>
|
||||
<tr style="border-collapse: collapse">
|
||||
<td align="center" bgcolor="#f2f2f2" style="border-collapse: collapse; padding: 15px">
|
||||
<table
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
border="0"
|
||||
bgcolor="#ffffff"
|
||||
style="
|
||||
width: 640px;
|
||||
border: 2px solid #77808a;
|
||||
background-color: #ffffff;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
"
|
||||
>
|
||||
<tbody>
|
||||
<tr style="border-collapse: collapse">
|
||||
<td style="border-collapse: collapse; padding: 15px">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr style="border-collapse: collapse">
|
||||
<td style="border-collapse: collapse; padding: 0; vertical-align: top; width: 200px">
|
||||
<img
|
||||
src="{{.WebAssetsURL}}/mailer-logo.png"
|
||||
alt="logo"
|
||||
title="{{.Title}}"
|
||||
style="outline: none; text-decoration: none; display: block; max-width: 175px"
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
style="
|
||||
border-collapse: collapse;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
"
|
||||
>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="m_-6736051552126560803borderRow" style="border-collapse: collapse">
|
||||
<td
|
||||
style="
|
||||
border-collapse: collapse;
|
||||
padding: 0;
|
||||
background-color: #e2e2e2;
|
||||
height: 1px;
|
||||
line-height: 0;
|
||||
"
|
||||
></td>
|
||||
</tr>
|
||||
|
||||
<tr style="border-collapse: collapse">
|
||||
<td style="border-collapse: collapse; padding: 15px; overflow-wrap: anywhere">
|
||||
<div style="font-size: 1em; line-height: 1.4em">
|
||||
{{ template "content" .}}
|
||||
<p>Thank You!</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="border-collapse: collapse">
|
||||
<td align="center" bgcolor="#f2f2f2" style="border-collapse: collapse; padding: 15px">
|
||||
<table
|
||||
width="630"
|
||||
cellpadding="5px"
|
||||
cellspacing="0"
|
||||
border="0"
|
||||
style="width: 640px; font-size: 0.75em; color: #aaaaaa; text-align: left"
|
||||
>
|
||||
<tbody>
|
||||
<tr style="border-collapse: collapse">
|
||||
<td style="border-collapse: collapse; padding: 15px; padding-top: 0">
|
||||
<p style="color: #333; font-size: 0.875em; line-height: 1.4em; margin: 0 0 0.75em">
|
||||
<a href="{{.websiteURL}}" target="_blank" rel="noopener noreferrer">{{.websiteDomain}}</a>
|
||||
</p>
|
||||
{{if not .AllowReply}}
|
||||
<p>
|
||||
<small>
|
||||
<strong>** This is a system generated email, please do not reply to it **</strong>
|
||||
</small>
|
||||
</p>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
48
mailer/message/render.go
Normal file
48
mailer/message/render.go
Normal file
@ -0,0 +1,48 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"html/template"
|
||||
|
||||
"gitserver.in/patialtech/rano/config"
|
||||
"gitserver.in/patialtech/rano/mailer"
|
||||
"gitserver.in/patialtech/rano/util/structs"
|
||||
)
|
||||
|
||||
//go:embed _layout.html
|
||||
var layout string
|
||||
|
||||
type TplData struct {
|
||||
WebAssetsURL string
|
||||
mailer.Template
|
||||
}
|
||||
|
||||
// render data in give HTML layout and content templates
|
||||
func render(layout string, content string, data mailer.Template) (string, error) {
|
||||
// layout
|
||||
tpl, err := template.New("layout").Parse(layout)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// content
|
||||
_, err = tpl.New("content").Parse(content)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// excute layout + content temaplte and render data
|
||||
buf := new(bytes.Buffer)
|
||||
d := structs.Map(data)
|
||||
d["Title"] = "My App"
|
||||
d["WebAssetsURL"] = config.Read().WebURL
|
||||
d["AllowReply"] = false
|
||||
|
||||
err = tpl.ExecuteTemplate(buf, "layout", d)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
31
mailer/message/render_test.go
Normal file
31
mailer/message/render_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testmail struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (t testmail) Subject() string {
|
||||
return "Test Test"
|
||||
}
|
||||
|
||||
func (t testmail) HtmlBody() (string, error) {
|
||||
content := `<p>{{.Message}}</p>`
|
||||
return render(layout, content, t)
|
||||
}
|
||||
|
||||
func TestRender(t *testing.T) {
|
||||
tpl := testmail{
|
||||
Message: "some mesage",
|
||||
}
|
||||
|
||||
if b, err := tpl.HtmlBody(); err != nil {
|
||||
t.Error(err)
|
||||
} else if !strings.Contains(b, tpl.Message) {
|
||||
t.Error("supposed to contain:", tpl.Message)
|
||||
}
|
||||
}
|
16
mailer/message/welcome.go
Normal file
16
mailer/message/welcome.go
Normal file
@ -0,0 +1,16 @@
|
||||
package message
|
||||
|
||||
type Welcome struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (e *Welcome) Subject() string {
|
||||
return "Welcome"
|
||||
}
|
||||
|
||||
func (e *Welcome) HtmlBody() (string, error) {
|
||||
content := `
|
||||
<p>Welcome {{.Name}}</p>
|
||||
`
|
||||
return render(layout, content, e)
|
||||
}
|
23
mailer/transport_dev.go
Normal file
23
mailer/transport_dev.go
Normal file
@ -0,0 +1,23 @@
|
||||
package mailer
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"gitserver.in/patialtech/rano/util/open"
|
||||
)
|
||||
|
||||
type transportDev struct{}
|
||||
|
||||
func (transportDev) send(msg *message) error {
|
||||
dir := os.TempDir()
|
||||
id := time.Now().Format("20060102T150405999")
|
||||
file := filepath.Join(dir, id+".html")
|
||||
|
||||
if err := os.WriteFile(file, []byte(msg.HtmlBody), 0440); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return open.WithDefaultApp(file)
|
||||
}
|
97
pkg/user/create.go
Normal file
97
pkg/user/create.go
Normal file
@ -0,0 +1,97 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/mail"
|
||||
"strings"
|
||||
|
||||
"gitserver.in/patialtech/rano/db"
|
||||
"gitserver.in/patialtech/rano/mailer"
|
||||
"gitserver.in/patialtech/rano/mailer/message"
|
||||
"gitserver.in/patialtech/rano/util/crypto"
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
"gitserver.in/patialtech/rano/util/validate"
|
||||
)
|
||||
|
||||
type CreateInput struct {
|
||||
Email string `validate:"email"`
|
||||
Phone string
|
||||
Pwd string `validate:"required"`
|
||||
ConfirmPwd string `validate:"required"`
|
||||
FirstName string `validate:"required"`
|
||||
MiddleName string
|
||||
LastName string
|
||||
RoleID uint8 `validate:"required"`
|
||||
}
|
||||
|
||||
var (
|
||||
ErrCreateInpNil = errors.New("user: create input is nil")
|
||||
ErrWrongConfirmPwd = errors.New("user: confirm password does not match")
|
||||
)
|
||||
|
||||
// Create user record in DB
|
||||
//
|
||||
// will return created userID on success
|
||||
func Create(ctx context.Context, inp *CreateInput) (int64, error) {
|
||||
// check for nil inp
|
||||
if inp == nil {
|
||||
return 0, ErrCreateInpNil
|
||||
}
|
||||
|
||||
// validate
|
||||
if err := validate.Struct(inp); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// compare pwd and comparePwd
|
||||
if inp.Pwd != inp.ConfirmPwd {
|
||||
return 0, ErrWrongConfirmPwd
|
||||
}
|
||||
|
||||
h, salt, err := crypto.PasswordHash(inp.Pwd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// save record to DB
|
||||
client := db.Client()
|
||||
u, err := client.User.Create().
|
||||
SetEmail(inp.Email).
|
||||
SetPwdHash(h).
|
||||
SetPwdSalt(salt).
|
||||
SetFirstName(inp.FirstName).
|
||||
SetMiddleName(inp.MiddleName).
|
||||
SetLastName(inp.LastName).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
logger.Error(err, slog.String("ref", "user: create error"))
|
||||
return 0, errors.New("failed to create user")
|
||||
}
|
||||
|
||||
// email
|
||||
err = mailer.Send(
|
||||
[]mail.Address{
|
||||
{Name: inp.FullName(), Address: inp.Email},
|
||||
},
|
||||
&message.Welcome{
|
||||
Name: inp.FullName(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(err, slog.String("ref", "user: send welcome email"))
|
||||
}
|
||||
|
||||
return u.ID, nil
|
||||
}
|
||||
|
||||
func (inp *CreateInput) FullName() string {
|
||||
if inp == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("%s %s %s", inp.FirstName, inp.MiddleName, inp.LastName)
|
||||
return strings.Join(strings.Fields(name), " ")
|
||||
}
|
36
pkg/user/create_test.go
Normal file
36
pkg/user/create_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
t.Run("check nil", func(t *testing.T) {
|
||||
if _, err := Create(context.Background(), nil); err == nil {
|
||||
t.Error("nil check error expected")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("trigger validation errors", func(t *testing.T) {
|
||||
if _, err := Create(context.Background(), &CreateInput{}); err == nil {
|
||||
t.Error("validation errors are expected")
|
||||
} else {
|
||||
t.Log(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create", func(t *testing.T) {
|
||||
if _, err := Create(context.Background(), &CreateInput{
|
||||
Email: "aa@aa.com",
|
||||
Pwd: "pwd123",
|
||||
ConfirmPwd: "pwd123",
|
||||
FirstName: "Ankit",
|
||||
MiddleName: "Singh",
|
||||
LastName: "Patial",
|
||||
RoleID: 1,
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
45
taskfile.yml
45
taskfile.yml
@ -6,39 +6,44 @@ env:
|
||||
dotenv: ['.env.{{.ENV}}']
|
||||
|
||||
tasks:
|
||||
gen:
|
||||
desc: use go generate, for graph files
|
||||
preconditions:
|
||||
- go mod tidy
|
||||
cmds:
|
||||
- go mod tidy
|
||||
- go generate ./graph
|
||||
- task: ent-gen
|
||||
|
||||
check:
|
||||
desc: perform go vuln check
|
||||
cmds:
|
||||
- govulncheck -show verbose ./...
|
||||
|
||||
install:
|
||||
desc: install packages
|
||||
cmds:
|
||||
- deno install --allow-scripts=npm:@sveltejs/kit
|
||||
|
||||
graph:
|
||||
start-graph:
|
||||
desc: run graph server
|
||||
cmds:
|
||||
- cmd: go run ./graph/server
|
||||
|
||||
codegen:
|
||||
start-web:
|
||||
desc: run web in dev mode
|
||||
cmd: deno task dev
|
||||
|
||||
gen:
|
||||
desc: use go generate, for graph files
|
||||
preconditions:
|
||||
- go mod tidy
|
||||
cmds:
|
||||
- task: graph-gen
|
||||
- task: ent-gen
|
||||
|
||||
vuln-check:
|
||||
desc: perform go vuln check
|
||||
cmds:
|
||||
- govulncheck -show verbose ./...
|
||||
|
||||
graph-gen:
|
||||
desc: graph gen
|
||||
cmds:
|
||||
- go mod tidy
|
||||
- go generate ./graph
|
||||
|
||||
graph-codegen:
|
||||
desc: generate graph types
|
||||
cmds:
|
||||
- cmd: deno task codegen
|
||||
|
||||
web:
|
||||
desc: run web in dev mode
|
||||
cmd: deno task dev
|
||||
|
||||
ent-new:
|
||||
desc: create new db Emtity
|
||||
cmd: cd ./db && go run -mod=mod entgo.io/ent/cmd/ent new {{.name}}
|
||||
|
75
util/crypto/hash.go
Normal file
75
util/crypto/hash.go
Normal file
@ -0,0 +1,75 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
func MD5(b []byte) string {
|
||||
hash := md5.Sum(b)
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func MD5Int(b []byte) uint64 {
|
||||
n := new(big.Int)
|
||||
n.SetString(MD5(b), 16)
|
||||
return n.Uint64()
|
||||
}
|
||||
|
||||
// Password using Argon2id
|
||||
//
|
||||
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
|
||||
func PasswordHash(pwd string) (hash, salt string, err error) {
|
||||
var sl []byte
|
||||
sl, err = randomSecret(32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate hash
|
||||
h := argon2.IDKey([]byte(pwd), sl, 3, 12288, 1, 32)
|
||||
|
||||
hash = base64.StdEncoding.EncodeToString(h)
|
||||
salt = base64.StdEncoding.EncodeToString(sl)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ComparePassword
|
||||
func ComparePasswordHash(pwd, hash, salt string) bool {
|
||||
var h, s []byte
|
||||
var err error
|
||||
|
||||
if h, err = base64.StdEncoding.DecodeString(hash); err != nil {
|
||||
logger.Error(err, slog.String("ref", "util/crypto.ComparePasswordHash decode hash"))
|
||||
}
|
||||
|
||||
if s, err = base64.StdEncoding.DecodeString(salt); err != nil {
|
||||
logger.Error(err, slog.String("ref", "util/crypto.ComparePasswordHash decode salt"))
|
||||
}
|
||||
|
||||
// Generate hash for comparison.
|
||||
ph := argon2.IDKey([]byte(pwd), s, 3, 12288, 1, 32)
|
||||
|
||||
// Compare the generated hash with the stored hash.
|
||||
// If they don't match return error.
|
||||
return bytes.Equal(h, ph)
|
||||
}
|
||||
|
||||
func randomSecret(length uint32) ([]byte, error) {
|
||||
secret := make([]byte, length)
|
||||
_, err := rand.Read(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return secret, nil
|
||||
}
|
22
util/crypto/hash_test.go
Normal file
22
util/crypto/hash_test.go
Normal file
@ -0,0 +1,22 @@
|
||||
package crypto
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPasswordHash(t *testing.T) {
|
||||
pwd := "MY Bingo pwd"
|
||||
hash, salt, err := PasswordHash(pwd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if hash == "" || salt == "" {
|
||||
t.Error("either hash or password is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if !ComparePasswordHash(pwd, string(hash), string(salt)) {
|
||||
t.Error("supposed to match")
|
||||
}
|
||||
|
||||
}
|
@ -40,5 +40,6 @@ func getArgs(args []any) ([]any, []any) {
|
||||
a = append(a, arg)
|
||||
}
|
||||
}
|
||||
|
||||
return a, b
|
||||
}
|
15
util/open/darwin.go
Normal file
15
util/open/darwin.go
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build darwin
|
||||
|
||||
package open
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func open(input string) *exec.Cmd {
|
||||
return exec.Command("open", input)
|
||||
}
|
||||
|
||||
func openWith(input string, appName string) *exec.Cmd {
|
||||
return exec.Command("open", "-a", appName, input)
|
||||
}
|
17
util/open/linux.go
Normal file
17
util/open/linux.go
Normal file
@ -0,0 +1,17 @@
|
||||
//go:build linux
|
||||
|
||||
package open
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// http://sources.debian.net/src/xdg-utils
|
||||
|
||||
func open(input string) *exec.Cmd {
|
||||
return exec.Command("xdg-open", input)
|
||||
}
|
||||
|
||||
func openWith(input string, appName string) *exec.Cmd {
|
||||
return exec.Command(appName, input)
|
||||
}
|
12
util/open/open.go
Normal file
12
util/open/open.go
Normal file
@ -0,0 +1,12 @@
|
||||
package open
|
||||
|
||||
// WithDefaultApp open a file, directory, or URI using the OS's default application for that object type.
|
||||
func WithDefaultApp(input string) error {
|
||||
cmd := open(input)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// WithApp will open a file directory, or URI using the specified application.
|
||||
func App(input string, appName string) error {
|
||||
return openWith(input, appName).Run()
|
||||
}
|
32
util/open/windows.go
Normal file
32
util/open/windows.go
Normal file
@ -0,0 +1,32 @@
|
||||
//go:build windows
|
||||
|
||||
package open
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
cmd = "url.dll,FileProtocolHandler"
|
||||
runDll32 = filepath.Join(os.Getenv("SYSTEMROOT"), "System32", "rundll32.exe")
|
||||
)
|
||||
|
||||
func cleaninput(input string) string {
|
||||
r := strings.NewReplacer("&", "^&")
|
||||
return r.Replace(input)
|
||||
}
|
||||
|
||||
func open(input string) *exec.Cmd {
|
||||
cmd := exec.Command(runDll32, cmd, input)
|
||||
// cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func openWith(input string, appName string) *exec.Cmd {
|
||||
cmd := exec.Command("cmd", "/C", "start", "", appName, cleaninput(input))
|
||||
// cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
return cmd
|
||||
}
|
33
util/structs/structs.go
Normal file
33
util/structs/structs.go
Normal file
@ -0,0 +1,33 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func Map(obj interface{}) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
|
||||
val := reflect.ValueOf(obj)
|
||||
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
typ := val.Type()
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
fieldName := typ.Field(i).Name
|
||||
fieldValueKind := val.Field(i).Kind()
|
||||
var fieldValue interface{}
|
||||
|
||||
if fieldValueKind == reflect.Struct {
|
||||
fieldValue = Map(val.Field(i).Interface())
|
||||
} else {
|
||||
fieldValue = val.Field(i).Interface()
|
||||
}
|
||||
|
||||
result[fieldName] = fieldValue
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
26
util/validate/validate.go
Normal file
26
util/validate/validate.go
Normal file
@ -0,0 +1,26 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
var validate *validator.Validate
|
||||
|
||||
func init() {
|
||||
validate = validator.New()
|
||||
// register function to get tag name from json tags.
|
||||
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
|
||||
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
|
||||
if name == "-" {
|
||||
return ""
|
||||
}
|
||||
return name
|
||||
})
|
||||
}
|
||||
|
||||
func Struct(s any) error {
|
||||
return validate.Struct(s)
|
||||
}
|
6
web/lib/gql/graph.d.ts
vendored
6
web/lib/gql/graph.d.ts
vendored
@ -28,20 +28,18 @@ export type AuthUser = {
|
||||
|
||||
export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
login: Scalars['Boolean']['output'];
|
||||
login: AuthUser;
|
||||
logout: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationLoginArgs = {
|
||||
email: Scalars['String']['input'];
|
||||
username: Scalars['String']['input'];
|
||||
pwd: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
heartBeat: Scalars['Boolean']['output'];
|
||||
/** me, is current AuthUser info */
|
||||
me?: Maybe<AuthUser>;
|
||||
};
|
||||
|
||||
|
BIN
web/public/mailer-logo.png
Normal file
BIN
web/public/mailer-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
Loading…
Reference in New Issue
Block a user