working on auth.
mailer, basic setup with html template and a dev treansport
This commit is contained in:
		| @@ -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 { | ||||
| 		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,12 +48,11 @@ 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 | ||||
| 	} | ||||
| } | ||||
| @@ -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 | 
		Reference in New Issue
	
	Block a user