From 9d40c9d7ec783e9919c680cdb9d06170c51bf087 Mon Sep 17 00:00:00 2001 From: Ankit Patial Date: Sun, 17 Nov 2024 22:28:29 +0530 Subject: [PATCH] feature: verify tokens --- README.md | 39 +- cmd/server/handler/request.go | 63 ++ {graph => cmd}/server/main.go | 4 + config/urls.go | 7 + db/client.go | 18 +- db/ent/client.go | 182 ++++- db/ent/ent.go | 2 + db/ent/hook/hook.go | 12 + db/ent/migrate/schema.go | 29 +- db/ent/mutation.go | 674 ++++++++++++++++++- db/ent/predicate/predicate.go | 3 + db/ent/runtime.go | 15 + db/ent/schema/user.go | 5 +- db/ent/schema/verifyToken.go | 29 + db/ent/tx.go | 3 + db/ent/user.go | 18 +- db/ent/user/user.go | 30 + db/ent/user/where.go | 23 + db/ent/user_create.go | 32 + db/ent/user_query.go | 103 ++- db/ent/user_update.go | 163 +++++ db/ent/verifytoken.go | 178 +++++ db/ent/verifytoken/verifytoken.go | 117 ++++ db/ent/verifytoken/where.go | 334 +++++++++ db/ent/verifytoken_create.go | 305 +++++++++ db/ent/verifytoken_delete.go | 88 +++ db/ent/verifytoken_query.go | 614 +++++++++++++++++ db/ent/verifytoken_update.go | 406 +++++++++++ go.mod | 2 + go.sum | 12 +- gqlgen.yml | 9 +- graph/account.graphql | 20 +- graph/account.resolvers.go | 17 +- graph/generated/account.generated.go | 16 +- graph/generated/prelude.generated.go | 15 - graph/generated/root.generated.go | 265 +++++--- graph/generated/root_.generated.go | 78 ++- graph/model/models_gen.go | 8 +- graph/root.graphql | 5 + mailer/message/welcome.go | 15 +- mailer/message/welcome.html | 4 + pkg/auth/auth.go | 9 - pkg/auth/ctx.go | 28 - pkg/auth/session.go | 17 - pkg/user/create.go | 70 +- pkg/user/create_test.go | 10 +- pkg/{auth => user}/password.go | 2 +- pkg/user/session.go | 163 +++++ pkg/user/token.go | 50 ++ pkg/user/token_test.go | 43 ++ pkg/user/verify.go | 60 ++ taskfile.yml | 3 +- util/logger/logger.go | 7 + util/uid/sqid.go | 29 + web/lib/gql/{auth.gql.ts => account.gql.ts} | 0 web/routes/+layout.svelte | 4 +- web/routes/account/verify-email/+page.svelte | 7 + 57 files changed, 4188 insertions(+), 276 deletions(-) create mode 100644 cmd/server/handler/request.go rename {graph => cmd}/server/main.go (92%) create mode 100644 config/urls.go create mode 100644 db/ent/schema/verifyToken.go create mode 100644 db/ent/verifytoken.go create mode 100644 db/ent/verifytoken/verifytoken.go create mode 100644 db/ent/verifytoken/where.go create mode 100644 db/ent/verifytoken_create.go create mode 100644 db/ent/verifytoken_delete.go create mode 100644 db/ent/verifytoken_query.go create mode 100644 db/ent/verifytoken_update.go create mode 100644 mailer/message/welcome.html delete mode 100644 pkg/auth/auth.go delete mode 100644 pkg/auth/ctx.go delete mode 100644 pkg/auth/session.go rename pkg/{auth => user}/password.go (95%) create mode 100644 pkg/user/session.go create mode 100644 pkg/user/token.go create mode 100644 pkg/user/token_test.go create mode 100644 pkg/user/verify.go create mode 100644 util/uid/sqid.go rename web/lib/gql/{auth.gql.ts => account.gql.ts} (100%) create mode 100644 web/routes/account/verify-email/+page.svelte diff --git a/README.md b/README.md index bc5b327..58f4f22 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RANO -"Reliable And Nonstop Operations" for +Reliable And Nonstop Operations ## Prerequisites @@ -12,11 +12,45 @@ - [golang-migrate](https://github.com/golang-migrate/migrate) for manual migrations. To install [follow instructions](https://github.com/golang-migrate/migrate/tree/master/cmd/migrate) -## Go packages +## Use RANO as your new project template + +to use "rano" as your new project template first install `gonew` + +```bash +go install golang.org/x/tools/cmd/gonew@latest + +``` + +now run gonew in your new project’s parent directory with two arguments: first, the path to the template you wish to copy, and second, the module name of the project you are creating.\ +For example: + +```bash +gonew gitserver.in/patialtech/rano example.com/mynewapp +cd ./mynewapp +``` + +## Project Structure + +- `cmd` dir contains quick commands or scripts we need in app +- `config` app configurations depends .env.\ file\ + for development we have .env.development file +- `db` databse schema and migrations +- `graph` is [gqlgen](https://gqlgen.com/) graph server +- `mailer` to send emails and contains mail message templates +- `pkg` folder is home for most of the business logic for your go app +- `util` shared utilities +- `web`single page web app built using [SvelteKit](https://svelte.dev/docs/kit/introduction) + - `assets` keep images and icons used in app init + - `lib` client lib + - `public` files of frontend app + - `routes` svelte app pages + +## Go packages that are in use - [gqlgen](https://gqlgen.com/) - [mux](https://gitserver.in/patialtech/mux) - [ent. Go](https://entgo.io/docs/tutorial-setup) ORM +- ## Web modules @@ -25,3 +59,4 @@ - [daisyui](https://daisyui.com/docs/install/) - [urql](https://commerce.nearform.com/open-source/urql/docs/basics/svelte/) - [GraphQL Codegen](https://the-guild.dev/graphql/codegen) +- [short unique identifiers from numbers](https://sqids.org/go) diff --git a/cmd/server/handler/request.go b/cmd/server/handler/request.go new file mode 100644 index 0000000..c25687b --- /dev/null +++ b/cmd/server/handler/request.go @@ -0,0 +1,63 @@ +package handler + +import ( + "context" + "net" + "net/http" + "strings" + + "gitserver.in/patialtech/rano/util/uid" +) + +const RequestIDKey = "RequestID" +const RequestIPKey = "RequestIP" +const RequestUserAgentKey = "RequestUA" + +var defaultHeaders = []string{ + "True-Client-IP", // Cloudflare Enterprise plan + "X-Real-IP", + "X-Forwarded-For", +} + +// Request middleware that will do the following: +// - pull session user +// - set requestID +// - set ctx RealIP and client userAgent info +func Request() func(http.Handler) http.Handler { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + // ID + requestID := r.Header.Get("X-Request-Id") + if requestID == "" { + requestID = uid.ULID() + } + ctx = context.WithValue(ctx, RequestIDKey, requestID) + + // IP + if ip := getRealIP(r, defaultHeaders); ip != "" { + r.RemoteAddr = ip + } + ctx = context.WithValue(ctx, RequestIPKey, requestID) + + // User Agent + ctx = context.WithValue(ctx, RequestUserAgentKey, r.UserAgent()) + + h.ServeHTTP(w, r) + }) + } +} + +func getRealIP(r *http.Request, headers []string) string { + for _, header := range headers { + if ip := r.Header.Get(header); ip != "" { + ips := strings.Split(ip, ",") + if ips[0] == "" || net.ParseIP(ips[0]) == nil { + continue + } + return ips[0] + } + } + return "" +} diff --git a/graph/server/main.go b/cmd/server/main.go similarity index 92% rename from graph/server/main.go rename to cmd/server/main.go index ecbae71..320f7d9 100644 --- a/graph/server/main.go +++ b/cmd/server/main.go @@ -6,6 +6,7 @@ import ( "gitserver.in/patialtech/mux" "gitserver.in/patialtech/mux/middleware" + "gitserver.in/patialtech/rano/cmd/server/handler" "gitserver.in/patialtech/rano/config" "gitserver.in/patialtech/rano/graph" "gitserver.in/patialtech/rano/util/logger" @@ -13,6 +14,9 @@ import ( func main() { r := mux.NewRouter() + + r.Use(handler.Request()) + // CORS r.Use(middleware.CORS(middleware.CORSOption{ AllowedHeaders: []string{"Content-Type"}, diff --git a/config/urls.go b/config/urls.go new file mode 100644 index 0000000..cb35c66 --- /dev/null +++ b/config/urls.go @@ -0,0 +1,7 @@ +package config + +import "fmt" + +func VerifyEmailURL(token string) string { + return fmt.Sprintf("%s/account/verify-email#%s", Read().WebURL, token) +} diff --git a/db/client.go b/db/client.go index cef61c8..2a6ef95 100644 --- a/db/client.go +++ b/db/client.go @@ -23,9 +23,9 @@ type connector struct { func New() *sql.DB { databaseUrl := config.Read().DbURL db := sql.OpenDB(connector{dsn: databaseUrl}) - db.SetMaxIdleConns(5) db.SetMaxOpenConns(50) - db.SetConnMaxLifetime(time.Minute) + db.SetMaxIdleConns(5) + db.SetConnMaxLifetime(0) // Maximum amount of time a connection can be reused (0 means no limit) return db } @@ -56,20 +56,28 @@ func Client() *ent.Client { // A AuditHook is an example for audit-log hook. func AuditHook(next ent.Mutator) ent.Mutator { + type Entity interface { + GetID() (int64, error) + } return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (v ent.Value, err error) { + var id int64 start := time.Now() defer func() { - saveAudit(ctx, m.Type(), m.Op().String(), time.Since(start), err) + saveAudit(ctx, id, m.Type(), m.Op().String(), time.Since(start), err) }() v, err = next.Mutate(ctx, m) + + if en, ok := v.(Entity); ok { + id, _ = en.GetID() + } logger.Info("** %v", v) return }) } -func saveAudit(_ context.Context, entT, op string, d time.Duration, err error) { - logger.Info("audit: %s %s %s %v", entT, op, d, err) +func saveAudit(_ context.Context, entID int64, entT, op string, d time.Duration, err error) { + logger.Info("audit: %d, %s, %s, %s, %v", entID, entT, op, d, err) // ml.SetCreatedAt(time.Now()) // if usr := auth.CtxUser(ctx); usr != nil { // ml.SetByID(usr.ID) diff --git a/db/ent/client.go b/db/ent/client.go index b0c5812..beea21b 100644 --- a/db/ent/client.go +++ b/db/ent/client.go @@ -21,6 +21,7 @@ import ( "gitserver.in/patialtech/rano/db/ent/todo" "gitserver.in/patialtech/rano/db/ent/user" "gitserver.in/patialtech/rano/db/ent/usersession" + "gitserver.in/patialtech/rano/db/ent/verifytoken" ) // Client is the client that holds all ent builders. @@ -40,6 +41,8 @@ type Client struct { User *UserClient // UserSession is the client for interacting with the UserSession builders. UserSession *UserSessionClient + // VerifyToken is the client for interacting with the VerifyToken builders. + VerifyToken *VerifyTokenClient } // NewClient creates a new client configured with the given options. @@ -57,6 +60,7 @@ func (c *Client) init() { c.Todo = NewTodoClient(c.config) c.User = NewUserClient(c.config) c.UserSession = NewUserSessionClient(c.config) + c.VerifyToken = NewVerifyTokenClient(c.config) } type ( @@ -155,6 +159,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { Todo: NewTodoClient(cfg), User: NewUserClient(cfg), UserSession: NewUserSessionClient(cfg), + VerifyToken: NewVerifyTokenClient(cfg), }, nil } @@ -180,6 +185,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) Todo: NewTodoClient(cfg), User: NewUserClient(cfg), UserSession: NewUserSessionClient(cfg), + VerifyToken: NewVerifyTokenClient(cfg), }, nil } @@ -209,7 +215,7 @@ func (c *Client) Close() error { // In order to add hooks to a specific client, call: `client.Node.Use(...)`. func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ - c.AccessControl, c.Audit, c.Role, c.Todo, c.User, c.UserSession, + c.AccessControl, c.Audit, c.Role, c.Todo, c.User, c.UserSession, c.VerifyToken, } { n.Use(hooks...) } @@ -219,7 +225,7 @@ func (c *Client) Use(hooks ...Hook) { // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ - c.AccessControl, c.Audit, c.Role, c.Todo, c.User, c.UserSession, + c.AccessControl, c.Audit, c.Role, c.Todo, c.User, c.UserSession, c.VerifyToken, } { n.Intercept(interceptors...) } @@ -240,6 +246,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.User.mutate(ctx, m) case *UserSessionMutation: return c.UserSession.mutate(ctx, m) + case *VerifyTokenMutation: + return c.VerifyToken.mutate(ctx, m) default: return nil, fmt.Errorf("ent: unknown mutation type %T", m) } @@ -933,6 +941,22 @@ func (c *UserClient) QueryAuditLogs(u *User) *AuditQuery { return query } +// QueryVerifyTokens queries the verify_tokens edge of a User. +func (c *UserClient) QueryVerifyTokens(u *User) *VerifyTokenQuery { + query := (&VerifyTokenClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(verifytoken.Table, verifytoken.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.VerifyTokensTable, user.VerifyTokensColumn), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *UserClient) Hooks() []Hook { return c.hooks.User @@ -1107,12 +1131,162 @@ func (c *UserSessionClient) mutate(ctx context.Context, m *UserSessionMutation) } } +// VerifyTokenClient is a client for the VerifyToken schema. +type VerifyTokenClient struct { + config +} + +// NewVerifyTokenClient returns a client for the VerifyToken from the given config. +func NewVerifyTokenClient(c config) *VerifyTokenClient { + return &VerifyTokenClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `verifytoken.Hooks(f(g(h())))`. +func (c *VerifyTokenClient) Use(hooks ...Hook) { + c.hooks.VerifyToken = append(c.hooks.VerifyToken, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `verifytoken.Intercept(f(g(h())))`. +func (c *VerifyTokenClient) Intercept(interceptors ...Interceptor) { + c.inters.VerifyToken = append(c.inters.VerifyToken, interceptors...) +} + +// Create returns a builder for creating a VerifyToken entity. +func (c *VerifyTokenClient) Create() *VerifyTokenCreate { + mutation := newVerifyTokenMutation(c.config, OpCreate) + return &VerifyTokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of VerifyToken entities. +func (c *VerifyTokenClient) CreateBulk(builders ...*VerifyTokenCreate) *VerifyTokenCreateBulk { + return &VerifyTokenCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *VerifyTokenClient) MapCreateBulk(slice any, setFunc func(*VerifyTokenCreate, int)) *VerifyTokenCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &VerifyTokenCreateBulk{err: fmt.Errorf("calling to VerifyTokenClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*VerifyTokenCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &VerifyTokenCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for VerifyToken. +func (c *VerifyTokenClient) Update() *VerifyTokenUpdate { + mutation := newVerifyTokenMutation(c.config, OpUpdate) + return &VerifyTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *VerifyTokenClient) UpdateOne(vt *VerifyToken) *VerifyTokenUpdateOne { + mutation := newVerifyTokenMutation(c.config, OpUpdateOne, withVerifyToken(vt)) + return &VerifyTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *VerifyTokenClient) UpdateOneID(id int64) *VerifyTokenUpdateOne { + mutation := newVerifyTokenMutation(c.config, OpUpdateOne, withVerifyTokenID(id)) + return &VerifyTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for VerifyToken. +func (c *VerifyTokenClient) Delete() *VerifyTokenDelete { + mutation := newVerifyTokenMutation(c.config, OpDelete) + return &VerifyTokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *VerifyTokenClient) DeleteOne(vt *VerifyToken) *VerifyTokenDeleteOne { + return c.DeleteOneID(vt.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *VerifyTokenClient) DeleteOneID(id int64) *VerifyTokenDeleteOne { + builder := c.Delete().Where(verifytoken.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &VerifyTokenDeleteOne{builder} +} + +// Query returns a query builder for VerifyToken. +func (c *VerifyTokenClient) Query() *VerifyTokenQuery { + return &VerifyTokenQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeVerifyToken}, + inters: c.Interceptors(), + } +} + +// Get returns a VerifyToken entity by its id. +func (c *VerifyTokenClient) Get(ctx context.Context, id int64) (*VerifyToken, error) { + return c.Query().Where(verifytoken.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *VerifyTokenClient) GetX(ctx context.Context, id int64) *VerifyToken { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryUser queries the user edge of a VerifyToken. +func (c *VerifyTokenClient) QueryUser(vt *VerifyToken) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := vt.ID + step := sqlgraph.NewStep( + sqlgraph.From(verifytoken.Table, verifytoken.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, verifytoken.UserTable, verifytoken.UserColumn), + ) + fromV = sqlgraph.Neighbors(vt.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *VerifyTokenClient) Hooks() []Hook { + return c.hooks.VerifyToken +} + +// Interceptors returns the client interceptors. +func (c *VerifyTokenClient) Interceptors() []Interceptor { + return c.inters.VerifyToken +} + +func (c *VerifyTokenClient) mutate(ctx context.Context, m *VerifyTokenMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&VerifyTokenCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&VerifyTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&VerifyTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&VerifyTokenDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown VerifyToken mutation op: %q", m.Op()) + } +} + // hooks and interceptors per client, for fast access. type ( hooks struct { - AccessControl, Audit, Role, Todo, User, UserSession []ent.Hook + AccessControl, Audit, Role, Todo, User, UserSession, VerifyToken []ent.Hook } inters struct { - AccessControl, Audit, Role, Todo, User, UserSession []ent.Interceptor + AccessControl, Audit, Role, Todo, User, UserSession, + VerifyToken []ent.Interceptor } ) diff --git a/db/ent/ent.go b/db/ent/ent.go index ac50664..34ed91f 100644 --- a/db/ent/ent.go +++ b/db/ent/ent.go @@ -18,6 +18,7 @@ import ( "gitserver.in/patialtech/rano/db/ent/todo" "gitserver.in/patialtech/rano/db/ent/user" "gitserver.in/patialtech/rano/db/ent/usersession" + "gitserver.in/patialtech/rano/db/ent/verifytoken" ) // ent aliases to avoid import conflicts in user's code. @@ -84,6 +85,7 @@ func checkColumn(table, column string) error { todo.Table: todo.ValidColumn, user.Table: user.ValidColumn, usersession.Table: usersession.ValidColumn, + verifytoken.Table: verifytoken.ValidColumn, }) }) return columnCheck(table, column) diff --git a/db/ent/hook/hook.go b/db/ent/hook/hook.go index 7d5b340..5575e0b 100644 --- a/db/ent/hook/hook.go +++ b/db/ent/hook/hook.go @@ -81,6 +81,18 @@ func (f UserSessionFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserSessionMutation", m) } +// The VerifyTokenFunc type is an adapter to allow the use of ordinary +// function as VerifyToken mutator. +type VerifyTokenFunc func(context.Context, *ent.VerifyTokenMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f VerifyTokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.VerifyTokenMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.VerifyTokenMutation", m) +} + // Condition is a hook condition function. type Condition func(context.Context, ent.Mutation) bool diff --git a/db/ent/migrate/schema.go b/db/ent/migrate/schema.go index 0229076..337ba33 100644 --- a/db/ent/migrate/schema.go +++ b/db/ent/migrate/schema.go @@ -101,8 +101,8 @@ var ( {Name: "email_verified", Type: field.TypeBool, Default: false}, {Name: "phone", Type: field.TypeString, Nullable: true, Size: 20}, {Name: "phone_verified", Type: field.TypeBool, Default: false}, - {Name: "pwd_salt", Type: field.TypeString, Size: 250}, - {Name: "pwd_hash", Type: field.TypeString, Size: 250}, + {Name: "pwd_salt", Type: field.TypeString, Size: 100}, + {Name: "pwd_hash", Type: field.TypeString, Size: 100}, {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}, @@ -186,6 +186,29 @@ var ( }, }, } + // VerifyTokensColumns holds the columns for the "verify_tokens" table. + VerifyTokensColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt64, Increment: true}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "expires_at", Type: field.TypeTime}, + {Name: "token", Type: field.TypeString, Unique: true, Size: 50}, + {Name: "purpose", Type: field.TypeString, Nullable: true, Size: 50}, + {Name: "user_id", Type: field.TypeInt64, Nullable: true}, + } + // VerifyTokensTable holds the schema information for the "verify_tokens" table. + VerifyTokensTable = &schema.Table{ + Name: "verify_tokens", + Columns: VerifyTokensColumns, + PrimaryKey: []*schema.Column{VerifyTokensColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "verify_tokens_users_verify_tokens", + Columns: []*schema.Column{VerifyTokensColumns[5]}, + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.SetNull, + }, + }, + } // Tables holds all the tables in the schema. Tables = []*schema.Table{ AccessControlsTable, @@ -194,10 +217,12 @@ var ( TodosTable, UsersTable, UserSessionsTable, + VerifyTokensTable, } ) func init() { AuditsTable.ForeignKeys[0].RefTable = UsersTable UserSessionsTable.ForeignKeys[0].RefTable = UsersTable + VerifyTokensTable.ForeignKeys[0].RefTable = UsersTable } diff --git a/db/ent/mutation.go b/db/ent/mutation.go index c21d33b..41b1696 100644 --- a/db/ent/mutation.go +++ b/db/ent/mutation.go @@ -17,6 +17,7 @@ import ( "gitserver.in/patialtech/rano/db/ent/role" "gitserver.in/patialtech/rano/db/ent/user" "gitserver.in/patialtech/rano/db/ent/usersession" + "gitserver.in/patialtech/rano/db/ent/verifytoken" ) const ( @@ -34,6 +35,7 @@ const ( TypeTodo = "Todo" TypeUser = "User" TypeUserSession = "UserSession" + TypeVerifyToken = "VerifyToken" ) // AccessControlMutation represents an operation that mutates the AccessControl nodes in the graph. @@ -2225,6 +2227,9 @@ type UserMutation struct { audit_logs map[int64]struct{} removedaudit_logs map[int64]struct{} clearedaudit_logs bool + verify_tokens map[int64]struct{} + removedverify_tokens map[int64]struct{} + clearedverify_tokens bool done bool oldValue func(context.Context) (*User, error) predicates []predicate.User @@ -3055,6 +3060,60 @@ func (m *UserMutation) ResetAuditLogs() { m.removedaudit_logs = nil } +// AddVerifyTokenIDs adds the "verify_tokens" edge to the VerifyToken entity by ids. +func (m *UserMutation) AddVerifyTokenIDs(ids ...int64) { + if m.verify_tokens == nil { + m.verify_tokens = make(map[int64]struct{}) + } + for i := range ids { + m.verify_tokens[ids[i]] = struct{}{} + } +} + +// ClearVerifyTokens clears the "verify_tokens" edge to the VerifyToken entity. +func (m *UserMutation) ClearVerifyTokens() { + m.clearedverify_tokens = true +} + +// VerifyTokensCleared reports if the "verify_tokens" edge to the VerifyToken entity was cleared. +func (m *UserMutation) VerifyTokensCleared() bool { + return m.clearedverify_tokens +} + +// RemoveVerifyTokenIDs removes the "verify_tokens" edge to the VerifyToken entity by IDs. +func (m *UserMutation) RemoveVerifyTokenIDs(ids ...int64) { + if m.removedverify_tokens == nil { + m.removedverify_tokens = make(map[int64]struct{}) + } + for i := range ids { + delete(m.verify_tokens, ids[i]) + m.removedverify_tokens[ids[i]] = struct{}{} + } +} + +// RemovedVerifyTokens returns the removed IDs of the "verify_tokens" edge to the VerifyToken entity. +func (m *UserMutation) RemovedVerifyTokensIDs() (ids []int64) { + for id := range m.removedverify_tokens { + ids = append(ids, id) + } + return +} + +// VerifyTokensIDs returns the "verify_tokens" edge IDs in the mutation. +func (m *UserMutation) VerifyTokensIDs() (ids []int64) { + for id := range m.verify_tokens { + ids = append(ids, id) + } + return +} + +// ResetVerifyTokens resets all changes to the "verify_tokens" edge. +func (m *UserMutation) ResetVerifyTokens() { + m.verify_tokens = nil + m.clearedverify_tokens = false + m.removedverify_tokens = nil +} + // Where appends a list predicates to the UserMutation builder. func (m *UserMutation) Where(ps ...predicate.User) { m.predicates = append(m.predicates, ps...) @@ -3468,13 +3527,16 @@ func (m *UserMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *UserMutation) AddedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.sessions != nil { edges = append(edges, user.EdgeSessions) } if m.audit_logs != nil { edges = append(edges, user.EdgeAuditLogs) } + if m.verify_tokens != nil { + edges = append(edges, user.EdgeVerifyTokens) + } return edges } @@ -3494,19 +3556,28 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeVerifyTokens: + ids := make([]ent.Value, 0, len(m.verify_tokens)) + for id := range m.verify_tokens { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *UserMutation) RemovedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.removedsessions != nil { edges = append(edges, user.EdgeSessions) } if m.removedaudit_logs != nil { edges = append(edges, user.EdgeAuditLogs) } + if m.removedverify_tokens != nil { + edges = append(edges, user.EdgeVerifyTokens) + } return edges } @@ -3526,19 +3597,28 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeVerifyTokens: + ids := make([]ent.Value, 0, len(m.removedverify_tokens)) + for id := range m.removedverify_tokens { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.clearedsessions { edges = append(edges, user.EdgeSessions) } if m.clearedaudit_logs { edges = append(edges, user.EdgeAuditLogs) } + if m.clearedverify_tokens { + edges = append(edges, user.EdgeVerifyTokens) + } return edges } @@ -3550,6 +3630,8 @@ func (m *UserMutation) EdgeCleared(name string) bool { return m.clearedsessions case user.EdgeAuditLogs: return m.clearedaudit_logs + case user.EdgeVerifyTokens: + return m.clearedverify_tokens } return false } @@ -3572,6 +3654,9 @@ func (m *UserMutation) ResetEdge(name string) error { case user.EdgeAuditLogs: m.ResetAuditLogs() return nil + case user.EdgeVerifyTokens: + m.ResetVerifyTokens() + return nil } return fmt.Errorf("unknown User edge %s", name) } @@ -4212,3 +4297,586 @@ func (m *UserSessionMutation) ResetEdge(name string) error { } return fmt.Errorf("unknown UserSession edge %s", name) } + +// VerifyTokenMutation represents an operation that mutates the VerifyToken nodes in the graph. +type VerifyTokenMutation struct { + config + op Op + typ string + id *int64 + created_at *time.Time + expires_at *time.Time + token *string + purpose *string + clearedFields map[string]struct{} + user *int64 + cleareduser bool + done bool + oldValue func(context.Context) (*VerifyToken, error) + predicates []predicate.VerifyToken +} + +var _ ent.Mutation = (*VerifyTokenMutation)(nil) + +// verifytokenOption allows management of the mutation configuration using functional options. +type verifytokenOption func(*VerifyTokenMutation) + +// newVerifyTokenMutation creates new mutation for the VerifyToken entity. +func newVerifyTokenMutation(c config, op Op, opts ...verifytokenOption) *VerifyTokenMutation { + m := &VerifyTokenMutation{ + config: c, + op: op, + typ: TypeVerifyToken, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withVerifyTokenID sets the ID field of the mutation. +func withVerifyTokenID(id int64) verifytokenOption { + return func(m *VerifyTokenMutation) { + var ( + err error + once sync.Once + value *VerifyToken + ) + m.oldValue = func(ctx context.Context) (*VerifyToken, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().VerifyToken.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withVerifyToken sets the old VerifyToken of the mutation. +func withVerifyToken(node *VerifyToken) verifytokenOption { + return func(m *VerifyTokenMutation) { + m.oldValue = func(context.Context) (*VerifyToken, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m VerifyTokenMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m VerifyTokenMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of VerifyToken entities. +func (m *VerifyTokenMutation) SetID(id int64) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *VerifyTokenMutation) ID() (id int64, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *VerifyTokenMutation) IDs(ctx context.Context) ([]int64, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int64{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().VerifyToken.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCreatedAt sets the "created_at" field. +func (m *VerifyTokenMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *VerifyTokenMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the VerifyToken entity. +// If the VerifyToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *VerifyTokenMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *VerifyTokenMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetExpiresAt sets the "expires_at" field. +func (m *VerifyTokenMutation) SetExpiresAt(t time.Time) { + m.expires_at = &t +} + +// ExpiresAt returns the value of the "expires_at" field in the mutation. +func (m *VerifyTokenMutation) ExpiresAt() (r time.Time, exists bool) { + v := m.expires_at + if v == nil { + return + } + return *v, true +} + +// OldExpiresAt returns the old "expires_at" field's value of the VerifyToken entity. +// If the VerifyToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *VerifyTokenMutation) OldExpiresAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExpiresAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) + } + return oldValue.ExpiresAt, nil +} + +// ResetExpiresAt resets all changes to the "expires_at" field. +func (m *VerifyTokenMutation) ResetExpiresAt() { + m.expires_at = nil +} + +// SetToken sets the "token" field. +func (m *VerifyTokenMutation) SetToken(s string) { + m.token = &s +} + +// Token returns the value of the "token" field in the mutation. +func (m *VerifyTokenMutation) Token() (r string, exists bool) { + v := m.token + if v == nil { + return + } + return *v, true +} + +// OldToken returns the old "token" field's value of the VerifyToken entity. +// If the VerifyToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *VerifyTokenMutation) OldToken(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldToken is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldToken requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldToken: %w", err) + } + return oldValue.Token, nil +} + +// ResetToken resets all changes to the "token" field. +func (m *VerifyTokenMutation) ResetToken() { + m.token = nil +} + +// SetPurpose sets the "purpose" field. +func (m *VerifyTokenMutation) SetPurpose(s string) { + m.purpose = &s +} + +// Purpose returns the value of the "purpose" field in the mutation. +func (m *VerifyTokenMutation) Purpose() (r string, exists bool) { + v := m.purpose + if v == nil { + return + } + return *v, true +} + +// OldPurpose returns the old "purpose" field's value of the VerifyToken entity. +// If the VerifyToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *VerifyTokenMutation) OldPurpose(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPurpose is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPurpose requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPurpose: %w", err) + } + return oldValue.Purpose, nil +} + +// ClearPurpose clears the value of the "purpose" field. +func (m *VerifyTokenMutation) ClearPurpose() { + m.purpose = nil + m.clearedFields[verifytoken.FieldPurpose] = struct{}{} +} + +// PurposeCleared returns if the "purpose" field was cleared in this mutation. +func (m *VerifyTokenMutation) PurposeCleared() bool { + _, ok := m.clearedFields[verifytoken.FieldPurpose] + return ok +} + +// ResetPurpose resets all changes to the "purpose" field. +func (m *VerifyTokenMutation) ResetPurpose() { + m.purpose = nil + delete(m.clearedFields, verifytoken.FieldPurpose) +} + +// SetUserID sets the "user" edge to the User entity by id. +func (m *VerifyTokenMutation) SetUserID(id int64) { + m.user = &id +} + +// ClearUser clears the "user" edge to the User entity. +func (m *VerifyTokenMutation) ClearUser() { + m.cleareduser = true +} + +// UserCleared reports if the "user" edge to the User entity was cleared. +func (m *VerifyTokenMutation) UserCleared() bool { + return m.cleareduser +} + +// UserID returns the "user" edge ID in the mutation. +func (m *VerifyTokenMutation) UserID() (id int64, exists bool) { + if m.user != nil { + return *m.user, true + } + return +} + +// UserIDs returns the "user" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// UserID instead. It exists only for internal usage by the builders. +func (m *VerifyTokenMutation) UserIDs() (ids []int64) { + if id := m.user; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUser resets all changes to the "user" edge. +func (m *VerifyTokenMutation) ResetUser() { + m.user = nil + m.cleareduser = false +} + +// Where appends a list predicates to the VerifyTokenMutation builder. +func (m *VerifyTokenMutation) Where(ps ...predicate.VerifyToken) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the VerifyTokenMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *VerifyTokenMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.VerifyToken, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *VerifyTokenMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *VerifyTokenMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (VerifyToken). +func (m *VerifyTokenMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *VerifyTokenMutation) Fields() []string { + fields := make([]string, 0, 4) + if m.created_at != nil { + fields = append(fields, verifytoken.FieldCreatedAt) + } + if m.expires_at != nil { + fields = append(fields, verifytoken.FieldExpiresAt) + } + if m.token != nil { + fields = append(fields, verifytoken.FieldToken) + } + if m.purpose != nil { + fields = append(fields, verifytoken.FieldPurpose) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *VerifyTokenMutation) Field(name string) (ent.Value, bool) { + switch name { + case verifytoken.FieldCreatedAt: + return m.CreatedAt() + case verifytoken.FieldExpiresAt: + return m.ExpiresAt() + case verifytoken.FieldToken: + return m.Token() + case verifytoken.FieldPurpose: + return m.Purpose() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *VerifyTokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case verifytoken.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case verifytoken.FieldExpiresAt: + return m.OldExpiresAt(ctx) + case verifytoken.FieldToken: + return m.OldToken(ctx) + case verifytoken.FieldPurpose: + return m.OldPurpose(ctx) + } + return nil, fmt.Errorf("unknown VerifyToken field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *VerifyTokenMutation) SetField(name string, value ent.Value) error { + switch name { + case verifytoken.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case verifytoken.FieldExpiresAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExpiresAt(v) + return nil + case verifytoken.FieldToken: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetToken(v) + return nil + case verifytoken.FieldPurpose: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPurpose(v) + return nil + } + return fmt.Errorf("unknown VerifyToken field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *VerifyTokenMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *VerifyTokenMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *VerifyTokenMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown VerifyToken numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *VerifyTokenMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(verifytoken.FieldPurpose) { + fields = append(fields, verifytoken.FieldPurpose) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *VerifyTokenMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *VerifyTokenMutation) ClearField(name string) error { + switch name { + case verifytoken.FieldPurpose: + m.ClearPurpose() + return nil + } + return fmt.Errorf("unknown VerifyToken nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *VerifyTokenMutation) ResetField(name string) error { + switch name { + case verifytoken.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case verifytoken.FieldExpiresAt: + m.ResetExpiresAt() + return nil + case verifytoken.FieldToken: + m.ResetToken() + return nil + case verifytoken.FieldPurpose: + m.ResetPurpose() + return nil + } + return fmt.Errorf("unknown VerifyToken field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *VerifyTokenMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.user != nil { + edges = append(edges, verifytoken.EdgeUser) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *VerifyTokenMutation) AddedIDs(name string) []ent.Value { + switch name { + case verifytoken.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *VerifyTokenMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *VerifyTokenMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *VerifyTokenMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.cleareduser { + edges = append(edges, verifytoken.EdgeUser) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *VerifyTokenMutation) EdgeCleared(name string) bool { + switch name { + case verifytoken.EdgeUser: + return m.cleareduser + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *VerifyTokenMutation) ClearEdge(name string) error { + switch name { + case verifytoken.EdgeUser: + m.ClearUser() + return nil + } + return fmt.Errorf("unknown VerifyToken unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *VerifyTokenMutation) ResetEdge(name string) error { + switch name { + case verifytoken.EdgeUser: + m.ResetUser() + return nil + } + return fmt.Errorf("unknown VerifyToken edge %s", name) +} diff --git a/db/ent/predicate/predicate.go b/db/ent/predicate/predicate.go index 68e566a..5f1909e 100644 --- a/db/ent/predicate/predicate.go +++ b/db/ent/predicate/predicate.go @@ -23,3 +23,6 @@ type User func(*sql.Selector) // UserSession is the predicate function for usersession builders. type UserSession func(*sql.Selector) + +// VerifyToken is the predicate function for verifytoken builders. +type VerifyToken func(*sql.Selector) diff --git a/db/ent/runtime.go b/db/ent/runtime.go index f921994..4822909 100644 --- a/db/ent/runtime.go +++ b/db/ent/runtime.go @@ -10,6 +10,7 @@ import ( "gitserver.in/patialtech/rano/db/ent/schema" "gitserver.in/patialtech/rano/db/ent/user" "gitserver.in/patialtech/rano/db/ent/usersession" + "gitserver.in/patialtech/rano/db/ent/verifytoken" ) // The init function reads all schema descriptors with runtime code @@ -176,4 +177,18 @@ func init() { usersessionDescIP := usersessionFields[5].Descriptor() // usersession.IPValidator is a validator for the "ip" field. It is called by the builders before save. usersession.IPValidator = usersessionDescIP.Validators[0].(func(string) error) + verifytokenFields := schema.VerifyToken{}.Fields() + _ = verifytokenFields + // verifytokenDescCreatedAt is the schema descriptor for created_at field. + verifytokenDescCreatedAt := verifytokenFields[1].Descriptor() + // verifytoken.DefaultCreatedAt holds the default value on creation for the created_at field. + verifytoken.DefaultCreatedAt = verifytokenDescCreatedAt.Default.(func() time.Time) + // verifytokenDescToken is the schema descriptor for token field. + verifytokenDescToken := verifytokenFields[3].Descriptor() + // verifytoken.TokenValidator is a validator for the "token" field. It is called by the builders before save. + verifytoken.TokenValidator = verifytokenDescToken.Validators[0].(func(string) error) + // verifytokenDescPurpose is the schema descriptor for purpose field. + verifytokenDescPurpose := verifytokenFields[4].Descriptor() + // verifytoken.PurposeValidator is a validator for the "purpose" field. It is called by the builders before save. + verifytoken.PurposeValidator = verifytokenDescPurpose.Validators[0].(func(string) error) } diff --git a/db/ent/schema/user.go b/db/ent/schema/user.go index 7e219b1..d51d193 100644 --- a/db/ent/schema/user.go +++ b/db/ent/schema/user.go @@ -23,8 +23,8 @@ func (User) Fields() []ent.Field { field.Bool("email_verified").Default(false), field.String("phone").MaxLen(20).Optional(), field.Bool("phone_verified").Default(false), - field.String("pwd_salt").MaxLen(250).NotEmpty(), - field.String("pwd_hash").MaxLen(250).NotEmpty(), + field.String("pwd_salt").MaxLen(100).NotEmpty(), + field.String("pwd_hash").MaxLen(100).NotEmpty(), field.Uint8("login_failed_count").Optional().Default(0), field.Time("login_attempt_on").Optional().Nillable(), field.Time("login_locked_until").Optional().Nillable(), @@ -40,6 +40,7 @@ func (User) Edges() []ent.Edge { return []ent.Edge{ edge.To("sessions", UserSession.Type).StorageKey(edge.Column("user_id")), edge.To("audit_logs", Audit.Type).StorageKey(edge.Column("user_id")), + edge.To("verify_tokens", VerifyToken.Type).StorageKey(edge.Column("user_id")), } } diff --git a/db/ent/schema/verifyToken.go b/db/ent/schema/verifyToken.go new file mode 100644 index 0000000..dbc97ef --- /dev/null +++ b/db/ent/schema/verifyToken.go @@ -0,0 +1,29 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" +) + +// Verify holds the schema definition for the Verify entity. +type VerifyToken struct { + ent.Schema +} + +// Fields of the Verify. +func (VerifyToken) Fields() []ent.Field { + return []ent.Field{ + fieldID, + fieldCreated, + field.Time("expires_at").Immutable(), + field.String("token").MaxLen(50).Unique(), + field.String("purpose").MaxLen(50).Optional(), + } +} + +func (VerifyToken) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("user", User.Type).Ref("verify_tokens").Unique(), + } +} diff --git a/db/ent/tx.go b/db/ent/tx.go index e95c34d..01adcf0 100644 --- a/db/ent/tx.go +++ b/db/ent/tx.go @@ -24,6 +24,8 @@ type Tx struct { User *UserClient // UserSession is the client for interacting with the UserSession builders. UserSession *UserSessionClient + // VerifyToken is the client for interacting with the VerifyToken builders. + VerifyToken *VerifyTokenClient // lazily loaded. client *Client @@ -161,6 +163,7 @@ func (tx *Tx) init() { tx.Todo = NewTodoClient(tx.config) tx.User = NewUserClient(tx.config) tx.UserSession = NewUserSessionClient(tx.config) + tx.VerifyToken = NewVerifyTokenClient(tx.config) } // txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation. diff --git a/db/ent/user.go b/db/ent/user.go index 339c390..3cc9591 100644 --- a/db/ent/user.go +++ b/db/ent/user.go @@ -59,9 +59,11 @@ type UserEdges struct { Sessions []*UserSession `json:"sessions,omitempty"` // AuditLogs holds the value of the audit_logs edge. AuditLogs []*Audit `json:"audit_logs,omitempty"` + // VerifyTokens holds the value of the verify_tokens edge. + VerifyTokens []*VerifyToken `json:"verify_tokens,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [2]bool + loadedTypes [3]bool } // SessionsOrErr returns the Sessions value or an error if the edge @@ -82,6 +84,15 @@ func (e UserEdges) AuditLogsOrErr() ([]*Audit, error) { return nil, &NotLoadedError{edge: "audit_logs"} } +// VerifyTokensOrErr returns the VerifyTokens value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) VerifyTokensOrErr() ([]*VerifyToken, error) { + if e.loadedTypes[2] { + return e.VerifyTokens, nil + } + return nil, &NotLoadedError{edge: "verify_tokens"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -232,6 +243,11 @@ func (u *User) QueryAuditLogs() *AuditQuery { return NewUserClient(u.config).QueryAuditLogs(u) } +// QueryVerifyTokens queries the "verify_tokens" edge of the User entity. +func (u *User) QueryVerifyTokens() *VerifyTokenQuery { + return NewUserClient(u.config).QueryVerifyTokens(u) +} + // Update returns a builder for updating this User. // Note that you need to call User.Unwrap() before calling this method if this User // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/db/ent/user/user.go b/db/ent/user/user.go index 92b43aa..7387354 100644 --- a/db/ent/user/user.go +++ b/db/ent/user/user.go @@ -49,6 +49,8 @@ const ( EdgeSessions = "sessions" // EdgeAuditLogs holds the string denoting the audit_logs edge name in mutations. EdgeAuditLogs = "audit_logs" + // EdgeVerifyTokens holds the string denoting the verify_tokens edge name in mutations. + EdgeVerifyTokens = "verify_tokens" // Table holds the table name of the user in the database. Table = "users" // SessionsTable is the table that holds the sessions relation/edge. @@ -65,6 +67,13 @@ const ( AuditLogsInverseTable = "audits" // AuditLogsColumn is the table column denoting the audit_logs relation/edge. AuditLogsColumn = "user_id" + // VerifyTokensTable is the table that holds the verify_tokens relation/edge. + VerifyTokensTable = "verify_tokens" + // VerifyTokensInverseTable is the table name for the VerifyToken entity. + // It exists in this package in order to avoid circular dependency with the "verifytoken" package. + VerifyTokensInverseTable = "verify_tokens" + // VerifyTokensColumn is the table column denoting the verify_tokens relation/edge. + VerifyTokensColumn = "user_id" ) // Columns holds all SQL columns for user fields. @@ -263,6 +272,20 @@ func ByAuditLogs(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { sqlgraph.OrderByNeighborTerms(s, newAuditLogsStep(), append([]sql.OrderTerm{term}, terms...)...) } } + +// ByVerifyTokensCount orders the results by verify_tokens count. +func ByVerifyTokensCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newVerifyTokensStep(), opts...) + } +} + +// ByVerifyTokens orders the results by verify_tokens terms. +func ByVerifyTokens(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newVerifyTokensStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} func newSessionsStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -277,3 +300,10 @@ func newAuditLogsStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, AuditLogsTable, AuditLogsColumn), ) } +func newVerifyTokensStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(VerifyTokensInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, VerifyTokensTable, VerifyTokensColumn), + ) +} diff --git a/db/ent/user/where.go b/db/ent/user/where.go index 9659403..f9d5fb4 100644 --- a/db/ent/user/where.go +++ b/db/ent/user/where.go @@ -906,6 +906,29 @@ func HasAuditLogsWith(preds ...predicate.Audit) predicate.User { }) } +// HasVerifyTokens applies the HasEdge predicate on the "verify_tokens" edge. +func HasVerifyTokens() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, VerifyTokensTable, VerifyTokensColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasVerifyTokensWith applies the HasEdge predicate on the "verify_tokens" edge with a given conditions (other predicates). +func HasVerifyTokensWith(preds ...predicate.VerifyToken) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := newVerifyTokensStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User(sql.AndPredicates(predicates...)) diff --git a/db/ent/user_create.go b/db/ent/user_create.go index f3e7871..3475c8b 100644 --- a/db/ent/user_create.go +++ b/db/ent/user_create.go @@ -13,6 +13,7 @@ import ( "gitserver.in/patialtech/rano/db/ent/audit" "gitserver.in/patialtech/rano/db/ent/user" "gitserver.in/patialtech/rano/db/ent/usersession" + "gitserver.in/patialtech/rano/db/ent/verifytoken" ) // UserCreate is the builder for creating a User entity. @@ -220,6 +221,21 @@ func (uc *UserCreate) AddAuditLogs(a ...*Audit) *UserCreate { return uc.AddAuditLogIDs(ids...) } +// AddVerifyTokenIDs adds the "verify_tokens" edge to the VerifyToken entity by IDs. +func (uc *UserCreate) AddVerifyTokenIDs(ids ...int64) *UserCreate { + uc.mutation.AddVerifyTokenIDs(ids...) + return uc +} + +// AddVerifyTokens adds the "verify_tokens" edges to the VerifyToken entity. +func (uc *UserCreate) AddVerifyTokens(v ...*VerifyToken) *UserCreate { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return uc.AddVerifyTokenIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uc *UserCreate) Mutation() *UserMutation { return uc.mutation @@ -480,6 +496,22 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := uc.mutation.VerifyTokensIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.VerifyTokensTable, + Columns: []string{user.VerifyTokensColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/db/ent/user_query.go b/db/ent/user_query.go index f5b6f3e..836d25e 100644 --- a/db/ent/user_query.go +++ b/db/ent/user_query.go @@ -16,17 +16,19 @@ import ( "gitserver.in/patialtech/rano/db/ent/predicate" "gitserver.in/patialtech/rano/db/ent/user" "gitserver.in/patialtech/rano/db/ent/usersession" + "gitserver.in/patialtech/rano/db/ent/verifytoken" ) // UserQuery is the builder for querying User entities. type UserQuery struct { config - ctx *QueryContext - order []user.OrderOption - inters []Interceptor - predicates []predicate.User - withSessions *UserSessionQuery - withAuditLogs *AuditQuery + ctx *QueryContext + order []user.OrderOption + inters []Interceptor + predicates []predicate.User + withSessions *UserSessionQuery + withAuditLogs *AuditQuery + withVerifyTokens *VerifyTokenQuery // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -107,6 +109,28 @@ func (uq *UserQuery) QueryAuditLogs() *AuditQuery { return query } +// QueryVerifyTokens chains the current query on the "verify_tokens" edge. +func (uq *UserQuery) QueryVerifyTokens() *VerifyTokenQuery { + query := (&VerifyTokenClient{config: uq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := uq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := uq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, selector), + sqlgraph.To(verifytoken.Table, verifytoken.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.VerifyTokensTable, user.VerifyTokensColumn), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first User entity from the query. // Returns a *NotFoundError when no User was found. func (uq *UserQuery) First(ctx context.Context) (*User, error) { @@ -294,13 +318,14 @@ func (uq *UserQuery) Clone() *UserQuery { return nil } return &UserQuery{ - config: uq.config, - ctx: uq.ctx.Clone(), - order: append([]user.OrderOption{}, uq.order...), - inters: append([]Interceptor{}, uq.inters...), - predicates: append([]predicate.User{}, uq.predicates...), - withSessions: uq.withSessions.Clone(), - withAuditLogs: uq.withAuditLogs.Clone(), + config: uq.config, + ctx: uq.ctx.Clone(), + order: append([]user.OrderOption{}, uq.order...), + inters: append([]Interceptor{}, uq.inters...), + predicates: append([]predicate.User{}, uq.predicates...), + withSessions: uq.withSessions.Clone(), + withAuditLogs: uq.withAuditLogs.Clone(), + withVerifyTokens: uq.withVerifyTokens.Clone(), // clone intermediate query. sql: uq.sql.Clone(), path: uq.path, @@ -329,6 +354,17 @@ func (uq *UserQuery) WithAuditLogs(opts ...func(*AuditQuery)) *UserQuery { return uq } +// WithVerifyTokens tells the query-builder to eager-load the nodes that are connected to +// the "verify_tokens" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithVerifyTokens(opts ...func(*VerifyTokenQuery)) *UserQuery { + query := (&VerifyTokenClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + uq.withVerifyTokens = query + return uq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -407,9 +443,10 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e var ( nodes = []*User{} _spec = uq.querySpec() - loadedTypes = [2]bool{ + loadedTypes = [3]bool{ uq.withSessions != nil, uq.withAuditLogs != nil, + uq.withVerifyTokens != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -444,6 +481,13 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e return nil, err } } + if query := uq.withVerifyTokens; query != nil { + if err := uq.loadVerifyTokens(ctx, query, nodes, + func(n *User) { n.Edges.VerifyTokens = []*VerifyToken{} }, + func(n *User, e *VerifyToken) { n.Edges.VerifyTokens = append(n.Edges.VerifyTokens, e) }); err != nil { + return nil, err + } + } return nodes, nil } @@ -509,6 +553,37 @@ func (uq *UserQuery) loadAuditLogs(ctx context.Context, query *AuditQuery, nodes } return nil } +func (uq *UserQuery) loadVerifyTokens(ctx context.Context, query *VerifyTokenQuery, nodes []*User, init func(*User), assign func(*User, *VerifyToken)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int64]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + query.withFKs = true + query.Where(predicate.VerifyToken(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(user.VerifyTokensColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.user_id + if fk == nil { + return fmt.Errorf(`foreign-key "user_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "user_id" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) { _spec := uq.querySpec() diff --git a/db/ent/user_update.go b/db/ent/user_update.go index 4ae205f..cd685d7 100644 --- a/db/ent/user_update.go +++ b/db/ent/user_update.go @@ -15,6 +15,7 @@ import ( "gitserver.in/patialtech/rano/db/ent/predicate" "gitserver.in/patialtech/rano/db/ent/user" "gitserver.in/patialtech/rano/db/ent/usersession" + "gitserver.in/patialtech/rano/db/ent/verifytoken" ) // UserUpdate is the builder for updating User entities. @@ -279,6 +280,21 @@ func (uu *UserUpdate) AddAuditLogs(a ...*Audit) *UserUpdate { return uu.AddAuditLogIDs(ids...) } +// AddVerifyTokenIDs adds the "verify_tokens" edge to the VerifyToken entity by IDs. +func (uu *UserUpdate) AddVerifyTokenIDs(ids ...int64) *UserUpdate { + uu.mutation.AddVerifyTokenIDs(ids...) + return uu +} + +// AddVerifyTokens adds the "verify_tokens" edges to the VerifyToken entity. +func (uu *UserUpdate) AddVerifyTokens(v ...*VerifyToken) *UserUpdate { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return uu.AddVerifyTokenIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uu *UserUpdate) Mutation() *UserMutation { return uu.mutation @@ -326,6 +342,27 @@ func (uu *UserUpdate) RemoveAuditLogs(a ...*Audit) *UserUpdate { return uu.RemoveAuditLogIDs(ids...) } +// ClearVerifyTokens clears all "verify_tokens" edges to the VerifyToken entity. +func (uu *UserUpdate) ClearVerifyTokens() *UserUpdate { + uu.mutation.ClearVerifyTokens() + return uu +} + +// RemoveVerifyTokenIDs removes the "verify_tokens" edge to VerifyToken entities by IDs. +func (uu *UserUpdate) RemoveVerifyTokenIDs(ids ...int64) *UserUpdate { + uu.mutation.RemoveVerifyTokenIDs(ids...) + return uu +} + +// RemoveVerifyTokens removes "verify_tokens" edges to VerifyToken entities. +func (uu *UserUpdate) RemoveVerifyTokens(v ...*VerifyToken) *UserUpdate { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return uu.RemoveVerifyTokenIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { uu.defaults() @@ -566,6 +603,51 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uu.mutation.VerifyTokensCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.VerifyTokensTable, + Columns: []string{user.VerifyTokensColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedVerifyTokensIDs(); len(nodes) > 0 && !uu.mutation.VerifyTokensCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.VerifyTokensTable, + Columns: []string{user.VerifyTokensColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.VerifyTokensIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.VerifyTokensTable, + Columns: []string{user.VerifyTokensColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{user.Label} @@ -835,6 +917,21 @@ func (uuo *UserUpdateOne) AddAuditLogs(a ...*Audit) *UserUpdateOne { return uuo.AddAuditLogIDs(ids...) } +// AddVerifyTokenIDs adds the "verify_tokens" edge to the VerifyToken entity by IDs. +func (uuo *UserUpdateOne) AddVerifyTokenIDs(ids ...int64) *UserUpdateOne { + uuo.mutation.AddVerifyTokenIDs(ids...) + return uuo +} + +// AddVerifyTokens adds the "verify_tokens" edges to the VerifyToken entity. +func (uuo *UserUpdateOne) AddVerifyTokens(v ...*VerifyToken) *UserUpdateOne { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return uuo.AddVerifyTokenIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uuo *UserUpdateOne) Mutation() *UserMutation { return uuo.mutation @@ -882,6 +979,27 @@ func (uuo *UserUpdateOne) RemoveAuditLogs(a ...*Audit) *UserUpdateOne { return uuo.RemoveAuditLogIDs(ids...) } +// ClearVerifyTokens clears all "verify_tokens" edges to the VerifyToken entity. +func (uuo *UserUpdateOne) ClearVerifyTokens() *UserUpdateOne { + uuo.mutation.ClearVerifyTokens() + return uuo +} + +// RemoveVerifyTokenIDs removes the "verify_tokens" edge to VerifyToken entities by IDs. +func (uuo *UserUpdateOne) RemoveVerifyTokenIDs(ids ...int64) *UserUpdateOne { + uuo.mutation.RemoveVerifyTokenIDs(ids...) + return uuo +} + +// RemoveVerifyTokens removes "verify_tokens" edges to VerifyToken entities. +func (uuo *UserUpdateOne) RemoveVerifyTokens(v ...*VerifyToken) *UserUpdateOne { + ids := make([]int64, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return uuo.RemoveVerifyTokenIDs(ids...) +} + // Where appends a list predicates to the UserUpdate builder. func (uuo *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne { uuo.mutation.Where(ps...) @@ -1152,6 +1270,51 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uuo.mutation.VerifyTokensCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.VerifyTokensTable, + Columns: []string{user.VerifyTokensColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedVerifyTokensIDs(); len(nodes) > 0 && !uuo.mutation.VerifyTokensCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.VerifyTokensTable, + Columns: []string{user.VerifyTokensColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.VerifyTokensIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.VerifyTokensTable, + Columns: []string{user.VerifyTokensColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &User{config: uuo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/db/ent/verifytoken.go b/db/ent/verifytoken.go new file mode 100644 index 0000000..44ca532 --- /dev/null +++ b/db/ent/verifytoken.go @@ -0,0 +1,178 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "gitserver.in/patialtech/rano/db/ent/user" + "gitserver.in/patialtech/rano/db/ent/verifytoken" +) + +// VerifyToken is the model entity for the VerifyToken schema. +type VerifyToken struct { + config `json:"-"` + // ID of the ent. + ID int64 `json:"id,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"createdAt"` + // ExpiresAt holds the value of the "expires_at" field. + ExpiresAt time.Time `json:"expires_at,omitempty"` + // Token holds the value of the "token" field. + Token string `json:"token,omitempty"` + // Purpose holds the value of the "purpose" field. + Purpose string `json:"purpose,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the VerifyTokenQuery when eager-loading is set. + Edges VerifyTokenEdges `json:"edges"` + user_id *int64 + selectValues sql.SelectValues +} + +// VerifyTokenEdges holds the relations/edges for other nodes in the graph. +type VerifyTokenEdges struct { + // User holds the value of the user edge. + User *User `json:"user,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// UserOrErr returns the User value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e VerifyTokenEdges) UserOrErr() (*User, error) { + if e.User != nil { + return e.User, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: user.Label} + } + return nil, &NotLoadedError{edge: "user"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*VerifyToken) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case verifytoken.FieldID: + values[i] = new(sql.NullInt64) + case verifytoken.FieldToken, verifytoken.FieldPurpose: + values[i] = new(sql.NullString) + case verifytoken.FieldCreatedAt, verifytoken.FieldExpiresAt: + values[i] = new(sql.NullTime) + case verifytoken.ForeignKeys[0]: // user_id + values[i] = new(sql.NullInt64) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the VerifyToken fields. +func (vt *VerifyToken) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case verifytoken.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + vt.ID = int64(value.Int64) + case verifytoken.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + vt.CreatedAt = value.Time + } + case verifytoken.FieldExpiresAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field expires_at", values[i]) + } else if value.Valid { + vt.ExpiresAt = value.Time + } + case verifytoken.FieldToken: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field token", values[i]) + } else if value.Valid { + vt.Token = value.String + } + case verifytoken.FieldPurpose: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field purpose", values[i]) + } else if value.Valid { + vt.Purpose = value.String + } + case verifytoken.ForeignKeys[0]: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field user_id", value) + } else if value.Valid { + vt.user_id = new(int64) + *vt.user_id = int64(value.Int64) + } + default: + vt.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the VerifyToken. +// This includes values selected through modifiers, order, etc. +func (vt *VerifyToken) Value(name string) (ent.Value, error) { + return vt.selectValues.Get(name) +} + +// QueryUser queries the "user" edge of the VerifyToken entity. +func (vt *VerifyToken) QueryUser() *UserQuery { + return NewVerifyTokenClient(vt.config).QueryUser(vt) +} + +// Update returns a builder for updating this VerifyToken. +// Note that you need to call VerifyToken.Unwrap() before calling this method if this VerifyToken +// was returned from a transaction, and the transaction was committed or rolled back. +func (vt *VerifyToken) Update() *VerifyTokenUpdateOne { + return NewVerifyTokenClient(vt.config).UpdateOne(vt) +} + +// Unwrap unwraps the VerifyToken entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (vt *VerifyToken) Unwrap() *VerifyToken { + _tx, ok := vt.config.driver.(*txDriver) + if !ok { + panic("ent: VerifyToken is not a transactional entity") + } + vt.config.driver = _tx.drv + return vt +} + +// String implements the fmt.Stringer. +func (vt *VerifyToken) String() string { + var builder strings.Builder + builder.WriteString("VerifyToken(") + builder.WriteString(fmt.Sprintf("id=%v, ", vt.ID)) + builder.WriteString("created_at=") + builder.WriteString(vt.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("expires_at=") + builder.WriteString(vt.ExpiresAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("token=") + builder.WriteString(vt.Token) + builder.WriteString(", ") + builder.WriteString("purpose=") + builder.WriteString(vt.Purpose) + builder.WriteByte(')') + return builder.String() +} + +// VerifyTokens is a parsable slice of VerifyToken. +type VerifyTokens []*VerifyToken diff --git a/db/ent/verifytoken/verifytoken.go b/db/ent/verifytoken/verifytoken.go new file mode 100644 index 0000000..68c2022 --- /dev/null +++ b/db/ent/verifytoken/verifytoken.go @@ -0,0 +1,117 @@ +// Code generated by ent, DO NOT EDIT. + +package verifytoken + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the verifytoken type in the database. + Label = "verify_token" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldExpiresAt holds the string denoting the expires_at field in the database. + FieldExpiresAt = "expires_at" + // FieldToken holds the string denoting the token field in the database. + FieldToken = "token" + // FieldPurpose holds the string denoting the purpose field in the database. + FieldPurpose = "purpose" + // EdgeUser holds the string denoting the user edge name in mutations. + EdgeUser = "user" + // Table holds the table name of the verifytoken in the database. + Table = "verify_tokens" + // UserTable is the table that holds the user relation/edge. + UserTable = "verify_tokens" + // UserInverseTable is the table name for the User entity. + // It exists in this package in order to avoid circular dependency with the "user" package. + UserInverseTable = "users" + // UserColumn is the table column denoting the user relation/edge. + UserColumn = "user_id" +) + +// Columns holds all SQL columns for verifytoken fields. +var Columns = []string{ + FieldID, + FieldCreatedAt, + FieldExpiresAt, + FieldToken, + FieldPurpose, +} + +// ForeignKeys holds the SQL foreign-keys that are owned by the "verify_tokens" +// table and are not defined as standalone fields in the schema. +var ForeignKeys = []string{ + "user_id", +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + for i := range ForeignKeys { + if column == ForeignKeys[i] { + return true + } + } + return false +} + +var ( + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // TokenValidator is a validator for the "token" field. It is called by the builders before save. + TokenValidator func(string) error + // PurposeValidator is a validator for the "purpose" field. It is called by the builders before save. + PurposeValidator func(string) error +) + +// OrderOption defines the ordering options for the VerifyToken queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByExpiresAt orders the results by the expires_at field. +func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExpiresAt, opts...).ToFunc() +} + +// ByToken orders the results by the token field. +func ByToken(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldToken, opts...).ToFunc() +} + +// ByPurpose orders the results by the purpose field. +func ByPurpose(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPurpose, opts...).ToFunc() +} + +// ByUserField orders the results by user field. +func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...)) + } +} +func newUserStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn), + ) +} diff --git a/db/ent/verifytoken/where.go b/db/ent/verifytoken/where.go new file mode 100644 index 0000000..f620614 --- /dev/null +++ b/db/ent/verifytoken/where.go @@ -0,0 +1,334 @@ +// Code generated by ent, DO NOT EDIT. + +package verifytoken + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "gitserver.in/patialtech/rano/db/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int64) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLTE(FieldID, id)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldCreatedAt, v)) +} + +// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. +func ExpiresAt(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldExpiresAt, v)) +} + +// Token applies equality check predicate on the "token" field. It's identical to TokenEQ. +func Token(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldToken, v)) +} + +// Purpose applies equality check predicate on the "purpose" field. It's identical to PurposeEQ. +func Purpose(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldPurpose, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLTE(FieldCreatedAt, v)) +} + +// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. +func ExpiresAtEQ(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldExpiresAt, v)) +} + +// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. +func ExpiresAtNEQ(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNEQ(FieldExpiresAt, v)) +} + +// ExpiresAtIn applies the In predicate on the "expires_at" field. +func ExpiresAtIn(vs ...time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. +func ExpiresAtNotIn(vs ...time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNotIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtGT applies the GT predicate on the "expires_at" field. +func ExpiresAtGT(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGT(FieldExpiresAt, v)) +} + +// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. +func ExpiresAtGTE(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGTE(FieldExpiresAt, v)) +} + +// ExpiresAtLT applies the LT predicate on the "expires_at" field. +func ExpiresAtLT(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLT(FieldExpiresAt, v)) +} + +// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. +func ExpiresAtLTE(v time.Time) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLTE(FieldExpiresAt, v)) +} + +// TokenEQ applies the EQ predicate on the "token" field. +func TokenEQ(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldToken, v)) +} + +// TokenNEQ applies the NEQ predicate on the "token" field. +func TokenNEQ(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNEQ(FieldToken, v)) +} + +// TokenIn applies the In predicate on the "token" field. +func TokenIn(vs ...string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldIn(FieldToken, vs...)) +} + +// TokenNotIn applies the NotIn predicate on the "token" field. +func TokenNotIn(vs ...string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNotIn(FieldToken, vs...)) +} + +// TokenGT applies the GT predicate on the "token" field. +func TokenGT(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGT(FieldToken, v)) +} + +// TokenGTE applies the GTE predicate on the "token" field. +func TokenGTE(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGTE(FieldToken, v)) +} + +// TokenLT applies the LT predicate on the "token" field. +func TokenLT(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLT(FieldToken, v)) +} + +// TokenLTE applies the LTE predicate on the "token" field. +func TokenLTE(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLTE(FieldToken, v)) +} + +// TokenContains applies the Contains predicate on the "token" field. +func TokenContains(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldContains(FieldToken, v)) +} + +// TokenHasPrefix applies the HasPrefix predicate on the "token" field. +func TokenHasPrefix(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldHasPrefix(FieldToken, v)) +} + +// TokenHasSuffix applies the HasSuffix predicate on the "token" field. +func TokenHasSuffix(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldHasSuffix(FieldToken, v)) +} + +// TokenEqualFold applies the EqualFold predicate on the "token" field. +func TokenEqualFold(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEqualFold(FieldToken, v)) +} + +// TokenContainsFold applies the ContainsFold predicate on the "token" field. +func TokenContainsFold(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldContainsFold(FieldToken, v)) +} + +// PurposeEQ applies the EQ predicate on the "purpose" field. +func PurposeEQ(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEQ(FieldPurpose, v)) +} + +// PurposeNEQ applies the NEQ predicate on the "purpose" field. +func PurposeNEQ(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNEQ(FieldPurpose, v)) +} + +// PurposeIn applies the In predicate on the "purpose" field. +func PurposeIn(vs ...string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldIn(FieldPurpose, vs...)) +} + +// PurposeNotIn applies the NotIn predicate on the "purpose" field. +func PurposeNotIn(vs ...string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNotIn(FieldPurpose, vs...)) +} + +// PurposeGT applies the GT predicate on the "purpose" field. +func PurposeGT(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGT(FieldPurpose, v)) +} + +// PurposeGTE applies the GTE predicate on the "purpose" field. +func PurposeGTE(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldGTE(FieldPurpose, v)) +} + +// PurposeLT applies the LT predicate on the "purpose" field. +func PurposeLT(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLT(FieldPurpose, v)) +} + +// PurposeLTE applies the LTE predicate on the "purpose" field. +func PurposeLTE(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldLTE(FieldPurpose, v)) +} + +// PurposeContains applies the Contains predicate on the "purpose" field. +func PurposeContains(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldContains(FieldPurpose, v)) +} + +// PurposeHasPrefix applies the HasPrefix predicate on the "purpose" field. +func PurposeHasPrefix(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldHasPrefix(FieldPurpose, v)) +} + +// PurposeHasSuffix applies the HasSuffix predicate on the "purpose" field. +func PurposeHasSuffix(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldHasSuffix(FieldPurpose, v)) +} + +// PurposeIsNil applies the IsNil predicate on the "purpose" field. +func PurposeIsNil() predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldIsNull(FieldPurpose)) +} + +// PurposeNotNil applies the NotNil predicate on the "purpose" field. +func PurposeNotNil() predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldNotNull(FieldPurpose)) +} + +// PurposeEqualFold applies the EqualFold predicate on the "purpose" field. +func PurposeEqualFold(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldEqualFold(FieldPurpose, v)) +} + +// PurposeContainsFold applies the ContainsFold predicate on the "purpose" field. +func PurposeContainsFold(v string) predicate.VerifyToken { + return predicate.VerifyToken(sql.FieldContainsFold(FieldPurpose, v)) +} + +// HasUser applies the HasEdge predicate on the "user" edge. +func HasUser() predicate.VerifyToken { + return predicate.VerifyToken(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates). +func HasUserWith(preds ...predicate.User) predicate.VerifyToken { + return predicate.VerifyToken(func(s *sql.Selector) { + step := newUserStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.VerifyToken) predicate.VerifyToken { + return predicate.VerifyToken(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.VerifyToken) predicate.VerifyToken { + return predicate.VerifyToken(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.VerifyToken) predicate.VerifyToken { + return predicate.VerifyToken(sql.NotPredicates(p)) +} diff --git a/db/ent/verifytoken_create.go b/db/ent/verifytoken_create.go new file mode 100644 index 0000000..ca5a69b --- /dev/null +++ b/db/ent/verifytoken_create.go @@ -0,0 +1,305 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "gitserver.in/patialtech/rano/db/ent/user" + "gitserver.in/patialtech/rano/db/ent/verifytoken" +) + +// VerifyTokenCreate is the builder for creating a VerifyToken entity. +type VerifyTokenCreate struct { + config + mutation *VerifyTokenMutation + hooks []Hook +} + +// SetCreatedAt sets the "created_at" field. +func (vtc *VerifyTokenCreate) SetCreatedAt(t time.Time) *VerifyTokenCreate { + vtc.mutation.SetCreatedAt(t) + return vtc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (vtc *VerifyTokenCreate) SetNillableCreatedAt(t *time.Time) *VerifyTokenCreate { + if t != nil { + vtc.SetCreatedAt(*t) + } + return vtc +} + +// SetExpiresAt sets the "expires_at" field. +func (vtc *VerifyTokenCreate) SetExpiresAt(t time.Time) *VerifyTokenCreate { + vtc.mutation.SetExpiresAt(t) + return vtc +} + +// SetToken sets the "token" field. +func (vtc *VerifyTokenCreate) SetToken(s string) *VerifyTokenCreate { + vtc.mutation.SetToken(s) + return vtc +} + +// SetPurpose sets the "purpose" field. +func (vtc *VerifyTokenCreate) SetPurpose(s string) *VerifyTokenCreate { + vtc.mutation.SetPurpose(s) + return vtc +} + +// SetNillablePurpose sets the "purpose" field if the given value is not nil. +func (vtc *VerifyTokenCreate) SetNillablePurpose(s *string) *VerifyTokenCreate { + if s != nil { + vtc.SetPurpose(*s) + } + return vtc +} + +// SetID sets the "id" field. +func (vtc *VerifyTokenCreate) SetID(i int64) *VerifyTokenCreate { + vtc.mutation.SetID(i) + return vtc +} + +// SetUserID sets the "user" edge to the User entity by ID. +func (vtc *VerifyTokenCreate) SetUserID(id int64) *VerifyTokenCreate { + vtc.mutation.SetUserID(id) + return vtc +} + +// SetNillableUserID sets the "user" edge to the User entity by ID if the given value is not nil. +func (vtc *VerifyTokenCreate) SetNillableUserID(id *int64) *VerifyTokenCreate { + if id != nil { + vtc = vtc.SetUserID(*id) + } + return vtc +} + +// SetUser sets the "user" edge to the User entity. +func (vtc *VerifyTokenCreate) SetUser(u *User) *VerifyTokenCreate { + return vtc.SetUserID(u.ID) +} + +// Mutation returns the VerifyTokenMutation object of the builder. +func (vtc *VerifyTokenCreate) Mutation() *VerifyTokenMutation { + return vtc.mutation +} + +// Save creates the VerifyToken in the database. +func (vtc *VerifyTokenCreate) Save(ctx context.Context) (*VerifyToken, error) { + vtc.defaults() + return withHooks(ctx, vtc.sqlSave, vtc.mutation, vtc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (vtc *VerifyTokenCreate) SaveX(ctx context.Context) *VerifyToken { + v, err := vtc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (vtc *VerifyTokenCreate) Exec(ctx context.Context) error { + _, err := vtc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (vtc *VerifyTokenCreate) ExecX(ctx context.Context) { + if err := vtc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (vtc *VerifyTokenCreate) defaults() { + if _, ok := vtc.mutation.CreatedAt(); !ok { + v := verifytoken.DefaultCreatedAt() + vtc.mutation.SetCreatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (vtc *VerifyTokenCreate) check() error { + if _, ok := vtc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "VerifyToken.created_at"`)} + } + if _, ok := vtc.mutation.ExpiresAt(); !ok { + return &ValidationError{Name: "expires_at", err: errors.New(`ent: missing required field "VerifyToken.expires_at"`)} + } + if _, ok := vtc.mutation.Token(); !ok { + return &ValidationError{Name: "token", err: errors.New(`ent: missing required field "VerifyToken.token"`)} + } + if v, ok := vtc.mutation.Token(); ok { + if err := verifytoken.TokenValidator(v); err != nil { + return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "VerifyToken.token": %w`, err)} + } + } + if v, ok := vtc.mutation.Purpose(); ok { + if err := verifytoken.PurposeValidator(v); err != nil { + return &ValidationError{Name: "purpose", err: fmt.Errorf(`ent: validator failed for field "VerifyToken.purpose": %w`, err)} + } + } + return nil +} + +func (vtc *VerifyTokenCreate) sqlSave(ctx context.Context) (*VerifyToken, error) { + if err := vtc.check(); err != nil { + return nil, err + } + _node, _spec := vtc.createSpec() + if err := sqlgraph.CreateNode(ctx, vtc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != _node.ID { + id := _spec.ID.Value.(int64) + _node.ID = int64(id) + } + vtc.mutation.id = &_node.ID + vtc.mutation.done = true + return _node, nil +} + +func (vtc *VerifyTokenCreate) createSpec() (*VerifyToken, *sqlgraph.CreateSpec) { + var ( + _node = &VerifyToken{config: vtc.config} + _spec = sqlgraph.NewCreateSpec(verifytoken.Table, sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64)) + ) + if id, ok := vtc.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := vtc.mutation.CreatedAt(); ok { + _spec.SetField(verifytoken.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := vtc.mutation.ExpiresAt(); ok { + _spec.SetField(verifytoken.FieldExpiresAt, field.TypeTime, value) + _node.ExpiresAt = value + } + if value, ok := vtc.mutation.Token(); ok { + _spec.SetField(verifytoken.FieldToken, field.TypeString, value) + _node.Token = value + } + if value, ok := vtc.mutation.Purpose(); ok { + _spec.SetField(verifytoken.FieldPurpose, field.TypeString, value) + _node.Purpose = value + } + if nodes := vtc.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: verifytoken.UserTable, + Columns: []string{verifytoken.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.user_id = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// VerifyTokenCreateBulk is the builder for creating many VerifyToken entities in bulk. +type VerifyTokenCreateBulk struct { + config + err error + builders []*VerifyTokenCreate +} + +// Save creates the VerifyToken entities in the database. +func (vtcb *VerifyTokenCreateBulk) Save(ctx context.Context) ([]*VerifyToken, error) { + if vtcb.err != nil { + return nil, vtcb.err + } + specs := make([]*sqlgraph.CreateSpec, len(vtcb.builders)) + nodes := make([]*VerifyToken, len(vtcb.builders)) + mutators := make([]Mutator, len(vtcb.builders)) + for i := range vtcb.builders { + func(i int, root context.Context) { + builder := vtcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*VerifyTokenMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, vtcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, vtcb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + if specs[i].ID.Value != nil && nodes[i].ID == 0 { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int64(id) + } + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, vtcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (vtcb *VerifyTokenCreateBulk) SaveX(ctx context.Context) []*VerifyToken { + v, err := vtcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (vtcb *VerifyTokenCreateBulk) Exec(ctx context.Context) error { + _, err := vtcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (vtcb *VerifyTokenCreateBulk) ExecX(ctx context.Context) { + if err := vtcb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/db/ent/verifytoken_delete.go b/db/ent/verifytoken_delete.go new file mode 100644 index 0000000..46b3966 --- /dev/null +++ b/db/ent/verifytoken_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "gitserver.in/patialtech/rano/db/ent/predicate" + "gitserver.in/patialtech/rano/db/ent/verifytoken" +) + +// VerifyTokenDelete is the builder for deleting a VerifyToken entity. +type VerifyTokenDelete struct { + config + hooks []Hook + mutation *VerifyTokenMutation +} + +// Where appends a list predicates to the VerifyTokenDelete builder. +func (vtd *VerifyTokenDelete) Where(ps ...predicate.VerifyToken) *VerifyTokenDelete { + vtd.mutation.Where(ps...) + return vtd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (vtd *VerifyTokenDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, vtd.sqlExec, vtd.mutation, vtd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (vtd *VerifyTokenDelete) ExecX(ctx context.Context) int { + n, err := vtd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (vtd *VerifyTokenDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(verifytoken.Table, sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64)) + if ps := vtd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, vtd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + vtd.mutation.done = true + return affected, err +} + +// VerifyTokenDeleteOne is the builder for deleting a single VerifyToken entity. +type VerifyTokenDeleteOne struct { + vtd *VerifyTokenDelete +} + +// Where appends a list predicates to the VerifyTokenDelete builder. +func (vtdo *VerifyTokenDeleteOne) Where(ps ...predicate.VerifyToken) *VerifyTokenDeleteOne { + vtdo.vtd.mutation.Where(ps...) + return vtdo +} + +// Exec executes the deletion query. +func (vtdo *VerifyTokenDeleteOne) Exec(ctx context.Context) error { + n, err := vtdo.vtd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{verifytoken.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (vtdo *VerifyTokenDeleteOne) ExecX(ctx context.Context) { + if err := vtdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/db/ent/verifytoken_query.go b/db/ent/verifytoken_query.go new file mode 100644 index 0000000..6304a2c --- /dev/null +++ b/db/ent/verifytoken_query.go @@ -0,0 +1,614 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "gitserver.in/patialtech/rano/db/ent/predicate" + "gitserver.in/patialtech/rano/db/ent/user" + "gitserver.in/patialtech/rano/db/ent/verifytoken" +) + +// VerifyTokenQuery is the builder for querying VerifyToken entities. +type VerifyTokenQuery struct { + config + ctx *QueryContext + order []verifytoken.OrderOption + inters []Interceptor + predicates []predicate.VerifyToken + withUser *UserQuery + withFKs bool + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the VerifyTokenQuery builder. +func (vtq *VerifyTokenQuery) Where(ps ...predicate.VerifyToken) *VerifyTokenQuery { + vtq.predicates = append(vtq.predicates, ps...) + return vtq +} + +// Limit the number of records to be returned by this query. +func (vtq *VerifyTokenQuery) Limit(limit int) *VerifyTokenQuery { + vtq.ctx.Limit = &limit + return vtq +} + +// Offset to start from. +func (vtq *VerifyTokenQuery) Offset(offset int) *VerifyTokenQuery { + vtq.ctx.Offset = &offset + return vtq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (vtq *VerifyTokenQuery) Unique(unique bool) *VerifyTokenQuery { + vtq.ctx.Unique = &unique + return vtq +} + +// Order specifies how the records should be ordered. +func (vtq *VerifyTokenQuery) Order(o ...verifytoken.OrderOption) *VerifyTokenQuery { + vtq.order = append(vtq.order, o...) + return vtq +} + +// QueryUser chains the current query on the "user" edge. +func (vtq *VerifyTokenQuery) QueryUser() *UserQuery { + query := (&UserClient{config: vtq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := vtq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := vtq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(verifytoken.Table, verifytoken.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, verifytoken.UserTable, verifytoken.UserColumn), + ) + fromU = sqlgraph.SetNeighbors(vtq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first VerifyToken entity from the query. +// Returns a *NotFoundError when no VerifyToken was found. +func (vtq *VerifyTokenQuery) First(ctx context.Context) (*VerifyToken, error) { + nodes, err := vtq.Limit(1).All(setContextOp(ctx, vtq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{verifytoken.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (vtq *VerifyTokenQuery) FirstX(ctx context.Context) *VerifyToken { + node, err := vtq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first VerifyToken ID from the query. +// Returns a *NotFoundError when no VerifyToken ID was found. +func (vtq *VerifyTokenQuery) FirstID(ctx context.Context) (id int64, err error) { + var ids []int64 + if ids, err = vtq.Limit(1).IDs(setContextOp(ctx, vtq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{verifytoken.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (vtq *VerifyTokenQuery) FirstIDX(ctx context.Context) int64 { + id, err := vtq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single VerifyToken entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one VerifyToken entity is found. +// Returns a *NotFoundError when no VerifyToken entities are found. +func (vtq *VerifyTokenQuery) Only(ctx context.Context) (*VerifyToken, error) { + nodes, err := vtq.Limit(2).All(setContextOp(ctx, vtq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{verifytoken.Label} + default: + return nil, &NotSingularError{verifytoken.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (vtq *VerifyTokenQuery) OnlyX(ctx context.Context) *VerifyToken { + node, err := vtq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only VerifyToken ID in the query. +// Returns a *NotSingularError when more than one VerifyToken ID is found. +// Returns a *NotFoundError when no entities are found. +func (vtq *VerifyTokenQuery) OnlyID(ctx context.Context) (id int64, err error) { + var ids []int64 + if ids, err = vtq.Limit(2).IDs(setContextOp(ctx, vtq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{verifytoken.Label} + default: + err = &NotSingularError{verifytoken.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (vtq *VerifyTokenQuery) OnlyIDX(ctx context.Context) int64 { + id, err := vtq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of VerifyTokens. +func (vtq *VerifyTokenQuery) All(ctx context.Context) ([]*VerifyToken, error) { + ctx = setContextOp(ctx, vtq.ctx, ent.OpQueryAll) + if err := vtq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*VerifyToken, *VerifyTokenQuery]() + return withInterceptors[[]*VerifyToken](ctx, vtq, qr, vtq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (vtq *VerifyTokenQuery) AllX(ctx context.Context) []*VerifyToken { + nodes, err := vtq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of VerifyToken IDs. +func (vtq *VerifyTokenQuery) IDs(ctx context.Context) (ids []int64, err error) { + if vtq.ctx.Unique == nil && vtq.path != nil { + vtq.Unique(true) + } + ctx = setContextOp(ctx, vtq.ctx, ent.OpQueryIDs) + if err = vtq.Select(verifytoken.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (vtq *VerifyTokenQuery) IDsX(ctx context.Context) []int64 { + ids, err := vtq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (vtq *VerifyTokenQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, vtq.ctx, ent.OpQueryCount) + if err := vtq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, vtq, querierCount[*VerifyTokenQuery](), vtq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (vtq *VerifyTokenQuery) CountX(ctx context.Context) int { + count, err := vtq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (vtq *VerifyTokenQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, vtq.ctx, ent.OpQueryExist) + switch _, err := vtq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (vtq *VerifyTokenQuery) ExistX(ctx context.Context) bool { + exist, err := vtq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the VerifyTokenQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (vtq *VerifyTokenQuery) Clone() *VerifyTokenQuery { + if vtq == nil { + return nil + } + return &VerifyTokenQuery{ + config: vtq.config, + ctx: vtq.ctx.Clone(), + order: append([]verifytoken.OrderOption{}, vtq.order...), + inters: append([]Interceptor{}, vtq.inters...), + predicates: append([]predicate.VerifyToken{}, vtq.predicates...), + withUser: vtq.withUser.Clone(), + // clone intermediate query. + sql: vtq.sql.Clone(), + path: vtq.path, + } +} + +// WithUser tells the query-builder to eager-load the nodes that are connected to +// the "user" edge. The optional arguments are used to configure the query builder of the edge. +func (vtq *VerifyTokenQuery) WithUser(opts ...func(*UserQuery)) *VerifyTokenQuery { + query := (&UserClient{config: vtq.config}).Query() + for _, opt := range opts { + opt(query) + } + vtq.withUser = query + return vtq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CreatedAt time.Time `json:"createdAt"` +// Count int `json:"count,omitempty"` +// } +// +// client.VerifyToken.Query(). +// GroupBy(verifytoken.FieldCreatedAt). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (vtq *VerifyTokenQuery) GroupBy(field string, fields ...string) *VerifyTokenGroupBy { + vtq.ctx.Fields = append([]string{field}, fields...) + grbuild := &VerifyTokenGroupBy{build: vtq} + grbuild.flds = &vtq.ctx.Fields + grbuild.label = verifytoken.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// CreatedAt time.Time `json:"createdAt"` +// } +// +// client.VerifyToken.Query(). +// Select(verifytoken.FieldCreatedAt). +// Scan(ctx, &v) +func (vtq *VerifyTokenQuery) Select(fields ...string) *VerifyTokenSelect { + vtq.ctx.Fields = append(vtq.ctx.Fields, fields...) + sbuild := &VerifyTokenSelect{VerifyTokenQuery: vtq} + sbuild.label = verifytoken.Label + sbuild.flds, sbuild.scan = &vtq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a VerifyTokenSelect configured with the given aggregations. +func (vtq *VerifyTokenQuery) Aggregate(fns ...AggregateFunc) *VerifyTokenSelect { + return vtq.Select().Aggregate(fns...) +} + +func (vtq *VerifyTokenQuery) prepareQuery(ctx context.Context) error { + for _, inter := range vtq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, vtq); err != nil { + return err + } + } + } + for _, f := range vtq.ctx.Fields { + if !verifytoken.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if vtq.path != nil { + prev, err := vtq.path(ctx) + if err != nil { + return err + } + vtq.sql = prev + } + return nil +} + +func (vtq *VerifyTokenQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*VerifyToken, error) { + var ( + nodes = []*VerifyToken{} + withFKs = vtq.withFKs + _spec = vtq.querySpec() + loadedTypes = [1]bool{ + vtq.withUser != nil, + } + ) + if vtq.withUser != nil { + withFKs = true + } + if withFKs { + _spec.Node.Columns = append(_spec.Node.Columns, verifytoken.ForeignKeys...) + } + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*VerifyToken).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &VerifyToken{config: vtq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, vtq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := vtq.withUser; query != nil { + if err := vtq.loadUser(ctx, query, nodes, nil, + func(n *VerifyToken, e *User) { n.Edges.User = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (vtq *VerifyTokenQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*VerifyToken, init func(*VerifyToken), assign func(*VerifyToken, *User)) error { + ids := make([]int64, 0, len(nodes)) + nodeids := make(map[int64][]*VerifyToken) + for i := range nodes { + if nodes[i].user_id == nil { + continue + } + fk := *nodes[i].user_id + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "user_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (vtq *VerifyTokenQuery) sqlCount(ctx context.Context) (int, error) { + _spec := vtq.querySpec() + _spec.Node.Columns = vtq.ctx.Fields + if len(vtq.ctx.Fields) > 0 { + _spec.Unique = vtq.ctx.Unique != nil && *vtq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, vtq.driver, _spec) +} + +func (vtq *VerifyTokenQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(verifytoken.Table, verifytoken.Columns, sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64)) + _spec.From = vtq.sql + if unique := vtq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if vtq.path != nil { + _spec.Unique = true + } + if fields := vtq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, verifytoken.FieldID) + for i := range fields { + if fields[i] != verifytoken.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := vtq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := vtq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := vtq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := vtq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (vtq *VerifyTokenQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(vtq.driver.Dialect()) + t1 := builder.Table(verifytoken.Table) + columns := vtq.ctx.Fields + if len(columns) == 0 { + columns = verifytoken.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if vtq.sql != nil { + selector = vtq.sql + selector.Select(selector.Columns(columns...)...) + } + if vtq.ctx.Unique != nil && *vtq.ctx.Unique { + selector.Distinct() + } + for _, p := range vtq.predicates { + p(selector) + } + for _, p := range vtq.order { + p(selector) + } + if offset := vtq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := vtq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// VerifyTokenGroupBy is the group-by builder for VerifyToken entities. +type VerifyTokenGroupBy struct { + selector + build *VerifyTokenQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (vtgb *VerifyTokenGroupBy) Aggregate(fns ...AggregateFunc) *VerifyTokenGroupBy { + vtgb.fns = append(vtgb.fns, fns...) + return vtgb +} + +// Scan applies the selector query and scans the result into the given value. +func (vtgb *VerifyTokenGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, vtgb.build.ctx, ent.OpQueryGroupBy) + if err := vtgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*VerifyTokenQuery, *VerifyTokenGroupBy](ctx, vtgb.build, vtgb, vtgb.build.inters, v) +} + +func (vtgb *VerifyTokenGroupBy) sqlScan(ctx context.Context, root *VerifyTokenQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(vtgb.fns)) + for _, fn := range vtgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*vtgb.flds)+len(vtgb.fns)) + for _, f := range *vtgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*vtgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := vtgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// VerifyTokenSelect is the builder for selecting fields of VerifyToken entities. +type VerifyTokenSelect struct { + *VerifyTokenQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (vts *VerifyTokenSelect) Aggregate(fns ...AggregateFunc) *VerifyTokenSelect { + vts.fns = append(vts.fns, fns...) + return vts +} + +// Scan applies the selector query and scans the result into the given value. +func (vts *VerifyTokenSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, vts.ctx, ent.OpQuerySelect) + if err := vts.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*VerifyTokenQuery, *VerifyTokenSelect](ctx, vts.VerifyTokenQuery, vts, vts.inters, v) +} + +func (vts *VerifyTokenSelect) sqlScan(ctx context.Context, root *VerifyTokenQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(vts.fns)) + for _, fn := range vts.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*vts.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := vts.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/db/ent/verifytoken_update.go b/db/ent/verifytoken_update.go new file mode 100644 index 0000000..ec6051b --- /dev/null +++ b/db/ent/verifytoken_update.go @@ -0,0 +1,406 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "gitserver.in/patialtech/rano/db/ent/predicate" + "gitserver.in/patialtech/rano/db/ent/user" + "gitserver.in/patialtech/rano/db/ent/verifytoken" +) + +// VerifyTokenUpdate is the builder for updating VerifyToken entities. +type VerifyTokenUpdate struct { + config + hooks []Hook + mutation *VerifyTokenMutation +} + +// Where appends a list predicates to the VerifyTokenUpdate builder. +func (vtu *VerifyTokenUpdate) Where(ps ...predicate.VerifyToken) *VerifyTokenUpdate { + vtu.mutation.Where(ps...) + return vtu +} + +// SetToken sets the "token" field. +func (vtu *VerifyTokenUpdate) SetToken(s string) *VerifyTokenUpdate { + vtu.mutation.SetToken(s) + return vtu +} + +// SetNillableToken sets the "token" field if the given value is not nil. +func (vtu *VerifyTokenUpdate) SetNillableToken(s *string) *VerifyTokenUpdate { + if s != nil { + vtu.SetToken(*s) + } + return vtu +} + +// SetPurpose sets the "purpose" field. +func (vtu *VerifyTokenUpdate) SetPurpose(s string) *VerifyTokenUpdate { + vtu.mutation.SetPurpose(s) + return vtu +} + +// SetNillablePurpose sets the "purpose" field if the given value is not nil. +func (vtu *VerifyTokenUpdate) SetNillablePurpose(s *string) *VerifyTokenUpdate { + if s != nil { + vtu.SetPurpose(*s) + } + return vtu +} + +// ClearPurpose clears the value of the "purpose" field. +func (vtu *VerifyTokenUpdate) ClearPurpose() *VerifyTokenUpdate { + vtu.mutation.ClearPurpose() + return vtu +} + +// SetUserID sets the "user" edge to the User entity by ID. +func (vtu *VerifyTokenUpdate) SetUserID(id int64) *VerifyTokenUpdate { + vtu.mutation.SetUserID(id) + return vtu +} + +// SetNillableUserID sets the "user" edge to the User entity by ID if the given value is not nil. +func (vtu *VerifyTokenUpdate) SetNillableUserID(id *int64) *VerifyTokenUpdate { + if id != nil { + vtu = vtu.SetUserID(*id) + } + return vtu +} + +// SetUser sets the "user" edge to the User entity. +func (vtu *VerifyTokenUpdate) SetUser(u *User) *VerifyTokenUpdate { + return vtu.SetUserID(u.ID) +} + +// Mutation returns the VerifyTokenMutation object of the builder. +func (vtu *VerifyTokenUpdate) Mutation() *VerifyTokenMutation { + return vtu.mutation +} + +// ClearUser clears the "user" edge to the User entity. +func (vtu *VerifyTokenUpdate) ClearUser() *VerifyTokenUpdate { + vtu.mutation.ClearUser() + return vtu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (vtu *VerifyTokenUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, vtu.sqlSave, vtu.mutation, vtu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (vtu *VerifyTokenUpdate) SaveX(ctx context.Context) int { + affected, err := vtu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (vtu *VerifyTokenUpdate) Exec(ctx context.Context) error { + _, err := vtu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (vtu *VerifyTokenUpdate) ExecX(ctx context.Context) { + if err := vtu.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (vtu *VerifyTokenUpdate) check() error { + if v, ok := vtu.mutation.Token(); ok { + if err := verifytoken.TokenValidator(v); err != nil { + return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "VerifyToken.token": %w`, err)} + } + } + if v, ok := vtu.mutation.Purpose(); ok { + if err := verifytoken.PurposeValidator(v); err != nil { + return &ValidationError{Name: "purpose", err: fmt.Errorf(`ent: validator failed for field "VerifyToken.purpose": %w`, err)} + } + } + return nil +} + +func (vtu *VerifyTokenUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := vtu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(verifytoken.Table, verifytoken.Columns, sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64)) + if ps := vtu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := vtu.mutation.Token(); ok { + _spec.SetField(verifytoken.FieldToken, field.TypeString, value) + } + if value, ok := vtu.mutation.Purpose(); ok { + _spec.SetField(verifytoken.FieldPurpose, field.TypeString, value) + } + if vtu.mutation.PurposeCleared() { + _spec.ClearField(verifytoken.FieldPurpose, field.TypeString) + } + if vtu.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: verifytoken.UserTable, + Columns: []string{verifytoken.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := vtu.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: verifytoken.UserTable, + Columns: []string{verifytoken.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if n, err = sqlgraph.UpdateNodes(ctx, vtu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{verifytoken.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + vtu.mutation.done = true + return n, nil +} + +// VerifyTokenUpdateOne is the builder for updating a single VerifyToken entity. +type VerifyTokenUpdateOne struct { + config + fields []string + hooks []Hook + mutation *VerifyTokenMutation +} + +// SetToken sets the "token" field. +func (vtuo *VerifyTokenUpdateOne) SetToken(s string) *VerifyTokenUpdateOne { + vtuo.mutation.SetToken(s) + return vtuo +} + +// SetNillableToken sets the "token" field if the given value is not nil. +func (vtuo *VerifyTokenUpdateOne) SetNillableToken(s *string) *VerifyTokenUpdateOne { + if s != nil { + vtuo.SetToken(*s) + } + return vtuo +} + +// SetPurpose sets the "purpose" field. +func (vtuo *VerifyTokenUpdateOne) SetPurpose(s string) *VerifyTokenUpdateOne { + vtuo.mutation.SetPurpose(s) + return vtuo +} + +// SetNillablePurpose sets the "purpose" field if the given value is not nil. +func (vtuo *VerifyTokenUpdateOne) SetNillablePurpose(s *string) *VerifyTokenUpdateOne { + if s != nil { + vtuo.SetPurpose(*s) + } + return vtuo +} + +// ClearPurpose clears the value of the "purpose" field. +func (vtuo *VerifyTokenUpdateOne) ClearPurpose() *VerifyTokenUpdateOne { + vtuo.mutation.ClearPurpose() + return vtuo +} + +// SetUserID sets the "user" edge to the User entity by ID. +func (vtuo *VerifyTokenUpdateOne) SetUserID(id int64) *VerifyTokenUpdateOne { + vtuo.mutation.SetUserID(id) + return vtuo +} + +// SetNillableUserID sets the "user" edge to the User entity by ID if the given value is not nil. +func (vtuo *VerifyTokenUpdateOne) SetNillableUserID(id *int64) *VerifyTokenUpdateOne { + if id != nil { + vtuo = vtuo.SetUserID(*id) + } + return vtuo +} + +// SetUser sets the "user" edge to the User entity. +func (vtuo *VerifyTokenUpdateOne) SetUser(u *User) *VerifyTokenUpdateOne { + return vtuo.SetUserID(u.ID) +} + +// Mutation returns the VerifyTokenMutation object of the builder. +func (vtuo *VerifyTokenUpdateOne) Mutation() *VerifyTokenMutation { + return vtuo.mutation +} + +// ClearUser clears the "user" edge to the User entity. +func (vtuo *VerifyTokenUpdateOne) ClearUser() *VerifyTokenUpdateOne { + vtuo.mutation.ClearUser() + return vtuo +} + +// Where appends a list predicates to the VerifyTokenUpdate builder. +func (vtuo *VerifyTokenUpdateOne) Where(ps ...predicate.VerifyToken) *VerifyTokenUpdateOne { + vtuo.mutation.Where(ps...) + return vtuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (vtuo *VerifyTokenUpdateOne) Select(field string, fields ...string) *VerifyTokenUpdateOne { + vtuo.fields = append([]string{field}, fields...) + return vtuo +} + +// Save executes the query and returns the updated VerifyToken entity. +func (vtuo *VerifyTokenUpdateOne) Save(ctx context.Context) (*VerifyToken, error) { + return withHooks(ctx, vtuo.sqlSave, vtuo.mutation, vtuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (vtuo *VerifyTokenUpdateOne) SaveX(ctx context.Context) *VerifyToken { + node, err := vtuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (vtuo *VerifyTokenUpdateOne) Exec(ctx context.Context) error { + _, err := vtuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (vtuo *VerifyTokenUpdateOne) ExecX(ctx context.Context) { + if err := vtuo.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (vtuo *VerifyTokenUpdateOne) check() error { + if v, ok := vtuo.mutation.Token(); ok { + if err := verifytoken.TokenValidator(v); err != nil { + return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "VerifyToken.token": %w`, err)} + } + } + if v, ok := vtuo.mutation.Purpose(); ok { + if err := verifytoken.PurposeValidator(v); err != nil { + return &ValidationError{Name: "purpose", err: fmt.Errorf(`ent: validator failed for field "VerifyToken.purpose": %w`, err)} + } + } + return nil +} + +func (vtuo *VerifyTokenUpdateOne) sqlSave(ctx context.Context) (_node *VerifyToken, err error) { + if err := vtuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(verifytoken.Table, verifytoken.Columns, sqlgraph.NewFieldSpec(verifytoken.FieldID, field.TypeInt64)) + id, ok := vtuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "VerifyToken.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := vtuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, verifytoken.FieldID) + for _, f := range fields { + if !verifytoken.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != verifytoken.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := vtuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := vtuo.mutation.Token(); ok { + _spec.SetField(verifytoken.FieldToken, field.TypeString, value) + } + if value, ok := vtuo.mutation.Purpose(); ok { + _spec.SetField(verifytoken.FieldPurpose, field.TypeString, value) + } + if vtuo.mutation.PurposeCleared() { + _spec.ClearField(verifytoken.FieldPurpose, field.TypeString) + } + if vtuo.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: verifytoken.UserTable, + Columns: []string{verifytoken.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := vtuo.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: verifytoken.UserTable, + Columns: []string{verifytoken.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &VerifyToken{config: vtuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, vtuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{verifytoken.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + vtuo.mutation.done = true + return _node, nil +} diff --git a/go.mod b/go.mod index b1322af..8ac70e2 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,9 @@ go 1.23.2 require ( github.com/99designs/gqlgen v0.17.56 + github.com/brianvoe/gofakeit/v7 v7.1.2 github.com/jackc/pgx/v5 v5.7.1 + github.com/sqids/sqids-go v0.4.1 github.com/vektah/gqlparser/v2 v2.5.19 gitserver.in/patialtech/mux v0.3.1 golang.org/x/crypto v0.29.0 diff --git a/go.sum b/go.sum index 2ea02da..6b85742 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/brianvoe/gofakeit/v7 v7.1.2 h1:vSKaVScNhWVpf1rlyEKSvO8zKZfuDtGqoIHT//iNNb8= +github.com/brianvoe/gofakeit/v7 v7.1.2/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -134,8 +136,6 @@ 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 v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -146,8 +146,6 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -165,10 +163,8 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/sqids/sqids-go v0.4.1 h1:eQKYzmAZbLlRwHeHYPF35QhgxwZHLnlmVj9AkIj/rrw= +github.com/sqids/sqids-go v0.4.1/go.mod h1:EMwHuPQgSNFS0A49jESTfIQS+066XQTVhukrzEPScl8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/gqlgen.yml b/gqlgen.yml index b778b38..6f7492d 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -87,12 +87,13 @@ autobind: models: ID: model: - - github.com/99designs/gqlgen/graphql.ID - - github.com/99designs/gqlgen/graphql.Int - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 + SQID: + model: + - github.com/99designs/gqlgen/graphql.ID Int: model: - github.com/99designs/gqlgen/graphql.Int + Int64: + model: - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 diff --git a/graph/account.graphql b/graph/account.graphql index 40ca5f2..b65f11f 100644 --- a/graph/account.graphql +++ b/graph/account.graphql @@ -1,6 +1,18 @@ extend type Mutation { - login(email: String!, pwd: String!): AuthUser! - logout: Boolean! + """ + Verify email token + """ + accountVerifyEmail(token: String): Boolean! + + """ + User account login + """ + accountLogin(email: String!, pwd: String!): AuthUser! + + """ + User account logout + """ + accountLogout: Boolean! } extend type Query { @@ -8,8 +20,8 @@ extend type Query { } type AuthUser { - id: ID! + id: UID! email: String! - displayName: String! + name: String! roleID: Int! } diff --git a/graph/account.resolvers.go b/graph/account.resolvers.go index dcaf3b9..071208b 100644 --- a/graph/account.resolvers.go +++ b/graph/account.resolvers.go @@ -11,14 +11,19 @@ import ( "gitserver.in/patialtech/rano/graph/model" ) -// Login is the resolver for the login field. -func (r *mutationResolver) Login(ctx context.Context, email string, pwd string) (*model.AuthUser, error) { - panic(fmt.Errorf("not implemented: Login - login")) +// AccountVerifyEmail is the resolver for the accountVerifyEmail field. +func (r *mutationResolver) AccountVerifyEmail(ctx context.Context, token *string) (bool, error) { + panic(fmt.Errorf("not implemented: AccountVerifyEmail - accountVerifyEmail")) } -// Logout is the resolver for the logout field. -func (r *mutationResolver) Logout(ctx context.Context) (bool, error) { - panic(fmt.Errorf("not implemented: Logout - logout")) +// AccountLogin is the resolver for the accountLogin field. +func (r *mutationResolver) AccountLogin(ctx context.Context, email string, pwd string) (*model.AuthUser, error) { + panic(fmt.Errorf("not implemented: AccountLogin - accountLogin")) +} + +// AccountLogout is the resolver for the accountLogout field. +func (r *mutationResolver) AccountLogout(ctx context.Context) (bool, error) { + panic(fmt.Errorf("not implemented: AccountLogout - accountLogout")) } // Me is the resolver for the me field. diff --git a/graph/generated/account.generated.go b/graph/generated/account.generated.go index 04eed33..cb3d266 100644 --- a/graph/generated/account.generated.go +++ b/graph/generated/account.generated.go @@ -55,7 +55,7 @@ func (ec *executionContext) _AuthUser_id(ctx context.Context, field graphql.Coll } res := resTmp.(string) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalNUID2string(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_AuthUser_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -65,7 +65,7 @@ func (ec *executionContext) fieldContext_AuthUser_id(_ context.Context, field gr IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + return nil, errors.New("field of type UID does not have child fields") }, } return fc, nil @@ -115,8 +115,8 @@ func (ec *executionContext) fieldContext_AuthUser_email(_ context.Context, field return fc, nil } -func (ec *executionContext) _AuthUser_displayName(ctx context.Context, field graphql.CollectedField, obj *model.AuthUser) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_AuthUser_displayName(ctx, field) +func (ec *executionContext) _AuthUser_name(ctx context.Context, field graphql.CollectedField, obj *model.AuthUser) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_AuthUser_name(ctx, field) if err != nil { return graphql.Null } @@ -129,7 +129,7 @@ func (ec *executionContext) _AuthUser_displayName(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.DisplayName, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -146,7 +146,7 @@ func (ec *executionContext) _AuthUser_displayName(ctx context.Context, field gra return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_AuthUser_displayName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_AuthUser_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "AuthUser", Field: field, @@ -236,8 +236,8 @@ func (ec *executionContext) _AuthUser(ctx context.Context, sel ast.SelectionSet, if out.Values[i] == graphql.Null { out.Invalids++ } - case "displayName": - out.Values[i] = ec._AuthUser_displayName(ctx, field, obj) + case "name": + out.Values[i] = ec._AuthUser_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } diff --git a/graph/generated/prelude.generated.go b/graph/generated/prelude.generated.go index df738f0..fed2ba0 100644 --- a/graph/generated/prelude.generated.go +++ b/graph/generated/prelude.generated.go @@ -2256,21 +2256,6 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } -func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) { - res, err := graphql.UnmarshalID(v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { - res := graphql.MarshalID(v) - if res == graphql.Null { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - } - return res -} - func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { res, err := graphql.UnmarshalInt(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/graph/generated/root.generated.go b/graph/generated/root.generated.go index 77a8853..2414134 100644 --- a/graph/generated/root.generated.go +++ b/graph/generated/root.generated.go @@ -18,8 +18,9 @@ import ( // region ************************** generated!.gotpl ************************** type MutationResolver interface { - Login(ctx context.Context, email string, pwd string) (*model.AuthUser, error) - Logout(ctx context.Context) (bool, error) + AccountVerifyEmail(ctx context.Context, token *string) (bool, error) + AccountLogin(ctx context.Context, email string, pwd string) (*model.AuthUser, error) + AccountLogout(ctx context.Context) (bool, error) } type QueryResolver interface { Me(ctx context.Context) (*model.AuthUser, error) @@ -29,22 +30,22 @@ type QueryResolver interface { // region ***************************** args.gotpl ***************************** -func (ec *executionContext) field_Mutation_login_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Mutation_accountLogin_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_argsEmail(ctx, rawArgs) + arg0, err := ec.field_Mutation_accountLogin_argsEmail(ctx, rawArgs) if err != nil { return nil, err } args["email"] = arg0 - arg1, err := ec.field_Mutation_login_argsPwd(ctx, rawArgs) + arg1, err := ec.field_Mutation_accountLogin_argsPwd(ctx, rawArgs) if err != nil { return nil, err } args["pwd"] = arg1 return args, nil } -func (ec *executionContext) field_Mutation_login_argsEmail( +func (ec *executionContext) field_Mutation_accountLogin_argsEmail( ctx context.Context, rawArgs map[string]interface{}, ) (string, error) { @@ -57,7 +58,7 @@ func (ec *executionContext) field_Mutation_login_argsEmail( return zeroVal, nil } -func (ec *executionContext) field_Mutation_login_argsPwd( +func (ec *executionContext) field_Mutation_accountLogin_argsPwd( ctx context.Context, rawArgs map[string]interface{}, ) (string, error) { @@ -70,6 +71,29 @@ func (ec *executionContext) field_Mutation_login_argsPwd( return zeroVal, nil } +func (ec *executionContext) field_Mutation_accountVerifyEmail_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + arg0, err := ec.field_Mutation_accountVerifyEmail_argsToken(ctx, rawArgs) + if err != nil { + return nil, err + } + args["token"] = arg0 + return args, nil +} +func (ec *executionContext) field_Mutation_accountVerifyEmail_argsToken( + ctx context.Context, + rawArgs map[string]interface{}, +) (*string, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("token")) + if tmp, ok := rawArgs["token"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -101,8 +125,8 @@ func (ec *executionContext) field_Query___type_argsName( // region **************************** field.gotpl ***************************** -func (ec *executionContext) _Mutation_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mutation_login(ctx, field) +func (ec *executionContext) _Mutation_accountVerifyEmail(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_accountVerifyEmail(ctx, field) if err != nil { return graphql.Null } @@ -115,72 +139,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["email"].(string), fc.Args["pwd"].(string)) - }) - 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.(*model.AuthUser) - fc.Result = 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) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - 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() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_login_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - -func (ec *executionContext) _Mutation_logout(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mutation_logout(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.Mutation().Logout(rctx) + return ec.resolvers.Mutation().AccountVerifyEmail(rctx, fc.Args["token"].(*string)) }) if err != nil { ec.Error(ctx, err) @@ -197,7 +156,127 @@ func (ec *executionContext) _Mutation_logout(ctx context.Context, field graphql. return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Mutation_logout(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_accountVerifyEmail(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + 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") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_accountVerifyEmail_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_accountLogin(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_accountLogin(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.Mutation().AccountLogin(rctx, fc.Args["email"].(string), fc.Args["pwd"].(string)) + }) + 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.(*model.AuthUser) + fc.Result = res + return ec.marshalNAuthUser2ᚖgitserverᚗinᚋpatialtechᚋranoᚋgraphᚋmodelᚐAuthUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_accountLogin(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_AuthUser_id(ctx, field) + case "email": + return ec.fieldContext_AuthUser_email(ctx, field) + case "name": + return ec.fieldContext_AuthUser_name(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() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_accountLogin_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_accountLogout(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_accountLogout(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.Mutation().AccountLogout(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_Mutation_accountLogout(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -250,8 +329,8 @@ func (ec *executionContext) fieldContext_Query_me(_ context.Context, field graph 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 "name": + return ec.fieldContext_AuthUser_name(ctx, field) case "roleID": return ec.fieldContext_AuthUser_roleID(ctx, field) } @@ -421,16 +500,23 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Mutation") - case "login": + case "accountVerifyEmail": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_login(ctx, field) + return ec._Mutation_accountVerifyEmail(ctx, field) }) if out.Values[i] == graphql.Null { out.Invalids++ } - case "logout": + case "accountLogin": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_logout(ctx, field) + return ec._Mutation_accountLogin(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "accountLogout": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_accountLogout(ctx, field) }) if out.Values[i] == graphql.Null { out.Invalids++ @@ -531,4 +617,19 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr // region ***************************** type.gotpl ***************************** +func (ec *executionContext) unmarshalNUID2string(ctx context.Context, v interface{}) (string, error) { + res, err := graphql.UnmarshalString(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNUID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + // endregion ***************************** type.gotpl ***************************** diff --git a/graph/generated/root_.generated.go b/graph/generated/root_.generated.go index 850127c..385f863 100644 --- a/graph/generated/root_.generated.go +++ b/graph/generated/root_.generated.go @@ -41,15 +41,16 @@ type DirectiveRoot struct { type ComplexityRoot struct { AuthUser struct { - DisplayName func(childComplexity int) int - Email func(childComplexity int) int - ID func(childComplexity int) int - RoleID func(childComplexity int) int + Email func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int + RoleID func(childComplexity int) int } Mutation struct { - Login func(childComplexity int, email string, pwd string) int - Logout func(childComplexity int) int + AccountLogin func(childComplexity int, email string, pwd string) int + AccountLogout func(childComplexity int) int + AccountVerifyEmail func(childComplexity int, token *string) int } Query struct { @@ -76,13 +77,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { - case "AuthUser.displayName": - if e.complexity.AuthUser.DisplayName == nil { - break - } - - return e.complexity.AuthUser.DisplayName(childComplexity), true - case "AuthUser.email": if e.complexity.AuthUser.Email == nil { break @@ -97,6 +91,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.AuthUser.ID(childComplexity), true + case "AuthUser.name": + if e.complexity.AuthUser.Name == nil { + break + } + + return e.complexity.AuthUser.Name(childComplexity), true + case "AuthUser.roleID": if e.complexity.AuthUser.RoleID == nil { break @@ -104,24 +105,36 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.AuthUser.RoleID(childComplexity), true - case "Mutation.login": - if e.complexity.Mutation.Login == nil { + case "Mutation.accountLogin": + if e.complexity.Mutation.AccountLogin == nil { break } - args, err := ec.field_Mutation_login_args(context.TODO(), rawArgs) + args, err := ec.field_Mutation_accountLogin_args(context.TODO(), rawArgs) if err != nil { return 0, false } - return e.complexity.Mutation.Login(childComplexity, args["email"].(string), args["pwd"].(string)), true + return e.complexity.Mutation.AccountLogin(childComplexity, args["email"].(string), args["pwd"].(string)), true - case "Mutation.logout": - if e.complexity.Mutation.Logout == nil { + case "Mutation.accountLogout": + if e.complexity.Mutation.AccountLogout == nil { break } - return e.complexity.Mutation.Logout(childComplexity), true + return e.complexity.Mutation.AccountLogout(childComplexity), true + + case "Mutation.accountVerifyEmail": + if e.complexity.Mutation.AccountVerifyEmail == nil { + break + } + + args, err := ec.field_Mutation_accountVerifyEmail_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.AccountVerifyEmail(childComplexity, args["token"].(*string)), true case "Query.me": if e.complexity.Query.Me == nil { @@ -235,8 +248,20 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er var sources = []*ast.Source{ {Name: "../account.graphql", Input: `extend type Mutation { - login(email: String!, pwd: String!): AuthUser! - logout: Boolean! + """ + Verify email token + """ + accountVerifyEmail(token: String): Boolean! + + """ + User account login + """ + accountLogin(email: String!, pwd: String!): AuthUser! + + """ + User account logout + """ + accountLogout: Boolean! } extend type Query { @@ -244,9 +269,9 @@ extend type Query { } type AuthUser { - id: ID! + id: UID! email: String! - displayName: String! + name: String! roleID: Int! } `, BuiltIn: false}, @@ -258,6 +283,10 @@ type Mutation type Query +scalar UID + +scalar Int64 + """ Maps a Time GraphQL scalar to a Go time.Time struct. """ @@ -272,6 +301,7 @@ scalar Map Go type interface{} """ scalar Any + scalar Void """ diff --git a/graph/model/models_gen.go b/graph/model/models_gen.go index f07c08c..6941da8 100644 --- a/graph/model/models_gen.go +++ b/graph/model/models_gen.go @@ -3,10 +3,10 @@ package model type AuthUser struct { - ID string `json:"id"` - Email string `json:"email"` - DisplayName string `json:"displayName"` - RoleID int `json:"roleID"` + ID string `json:"id"` + Email string `json:"email"` + Name string `json:"name"` + RoleID int `json:"roleID"` } type Mutation struct { diff --git a/graph/root.graphql b/graph/root.graphql index ef350ea..f7ff282 100644 --- a/graph/root.graphql +++ b/graph/root.graphql @@ -6,6 +6,10 @@ type Mutation type Query +scalar UID + +scalar Int64 + """ Maps a Time GraphQL scalar to a Go time.Time struct. """ @@ -20,6 +24,7 @@ scalar Map Go type interface{} """ scalar Any + scalar Void """ diff --git a/mailer/message/welcome.go b/mailer/message/welcome.go index 974950d..9c44db7 100644 --- a/mailer/message/welcome.go +++ b/mailer/message/welcome.go @@ -1,16 +1,19 @@ package message +import _ "embed" + +//go:embed welcome.html +var welcomeHtml string + type Welcome struct { - Name string + Name string + VerifyURL string } func (e *Welcome) Subject() string { - return "Welcome" + return "Welcome, " + e.Name } func (e *Welcome) HtmlBody() (string, error) { - content := ` -

Welcome {{.Name}}

-` - return render(layout, content, e) + return render(layout, welcomeHtml, e) } diff --git a/mailer/message/welcome.html b/mailer/message/welcome.html new file mode 100644 index 0000000..c0edc9c --- /dev/null +++ b/mailer/message/welcome.html @@ -0,0 +1,4 @@ +

Welcome to My App

+ +

To complete your registration, please click on the link below to verify your email address:

+Verify Email Address diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go deleted file mode 100644 index a639d0a..0000000 --- a/pkg/auth/auth.go +++ /dev/null @@ -1,9 +0,0 @@ -package auth - -import "gitserver.in/patialtech/rano/graph/model" - -type AuthUser = model.AuthUser - -func authenticate(email, pwd string) (*AuthUser, error) { - panic("not implemented") -} diff --git a/pkg/auth/ctx.go b/pkg/auth/ctx.go deleted file mode 100644 index 6aceded..0000000 --- a/pkg/auth/ctx.go +++ /dev/null @@ -1,28 +0,0 @@ -package auth - -import ( - "context" - - "gitserver.in/patialtech/rano/config" -) - -type SessionUser struct { - ID int64 - Email string - DisplayName string - RoleID int -} - -func CtxWithUser(ctx context.Context, u *AuthUser) context.Context { - return context.WithValue(ctx, config.AuthUserCtxKey, &SessionUser{ - ID: u.ID, - Email: u.Email, - DisplayName: u.DisplayName, - RoleID: u.RoleID, - }) -} - -func CtxUser(ctx context.Context) *SessionUser { - u, _ := ctx.Value(config.AuthUserCtxKey).(*SessionUser) - return u -} diff --git a/pkg/auth/session.go b/pkg/auth/session.go deleted file mode 100644 index 23e6d48..0000000 --- a/pkg/auth/session.go +++ /dev/null @@ -1,17 +0,0 @@ -package auth - -// NewSession for user. -// -// Authenticated -func NewSession(email, pwd string) (*AuthUser, error) { - // authenticate. - - // create sesion entry in db - - panic("not implemented") -} - -// RemoveSession entry from DB -func RemoveSession(sID uint) { - panic("not implemented") -} diff --git a/pkg/user/create.go b/pkg/user/create.go index c2b6d06..a44aac2 100644 --- a/pkg/user/create.go +++ b/pkg/user/create.go @@ -4,15 +4,16 @@ import ( "context" "errors" "fmt" - "log/slog" "net/mail" "strings" + "time" + "gitserver.in/patialtech/rano/config" "gitserver.in/patialtech/rano/db" + "gitserver.in/patialtech/rano/db/ent/user" "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" ) @@ -36,17 +37,17 @@ var ( // // will return created userID on success func Create(ctx context.Context, inp *CreateInput) (int64, error) { - // check for nil inp + // Check for nil input. if inp == nil { return 0, ErrCreateInpNil } - // validate + // Validate struct. if err := validate.Struct(inp); err != nil { return 0, err } - // compare pwd and comparePwd + // Compare pwd and comparePwd. if inp.Pwd != inp.ConfirmPwd { return 0, ErrWrongConfirmPwd } @@ -56,42 +57,73 @@ func Create(ctx context.Context, inp *CreateInput) (int64, error) { return 0, err } - // save record to DB - client := db.Client() - u, err := client.User.Create(). + // Begin a transaction. + tx, err := db.Client().BeginTx(ctx, nil) + if err != nil { + return 0, err + } + + // Save User to DB. + u, err := tx.User.Create(). SetEmail(inp.Email). SetPwdHash(h). SetPwdSalt(salt). SetFirstName(inp.FirstName). SetMiddleName(inp.MiddleName). SetLastName(inp.LastName). + SetStatus(user.StatusActive). Save(ctx) if err != nil { - logger.Error(err, slog.String("ref", "user: create error")) return 0, errors.New("failed to create user") } - // email + // Get a new email-verification token + tokenDuration := time.Hour * 6 + token, err := newTokenToVerifyEmail(u.ID, tokenDuration) + if err != nil { + _ = tx.Rollback() + return 0, err + } + + // Save token to DB + err = tx.VerifyToken.Create(). + SetToken(token). + SetExpiresAt(time.Now().Add(tokenDuration).UTC()). + SetPurpose("VerifyEmail"). + SetUserID(u.ID).Exec(ctx) + if err != nil { + _ = tx.Rollback() + return 0, err + } + + name := fullName(inp.FirstName, inp.MiddleName, inp.LastName) + // Send a welcome email with a link to verigy email-address. err = mailer.Send( []mail.Address{ - {Name: inp.FullName(), Address: inp.Email}, + {Name: name, Address: inp.Email}, }, &message.Welcome{ - Name: inp.FullName(), + Name: name, + VerifyURL: config.VerifyEmailURL(token), }, ) if err != nil { - logger.Error(err, slog.String("ref", "user: send welcome email")) + _ = tx.Rollback() + return 0, err } + // Commit transaction + err = tx.Commit() + if err != nil { + return 0, err + } + + // ALL Done! + // Created a new user in system. 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) +func fullName(fName, mName, lName string) string { + name := fmt.Sprintf("%s %s %s", fName, mName, lName) return strings.Join(strings.Fields(name), " ") } diff --git a/pkg/user/create_test.go b/pkg/user/create_test.go index 533e112..3967aaf 100644 --- a/pkg/user/create_test.go +++ b/pkg/user/create_test.go @@ -3,6 +3,8 @@ package user import ( "context" "testing" + + "github.com/brianvoe/gofakeit/v7" ) func TestCreate(t *testing.T) { @@ -22,12 +24,12 @@ func TestCreate(t *testing.T) { t.Run("create", func(t *testing.T) { if _, err := Create(context.Background(), &CreateInput{ - Email: "aa@aa.com", + Email: gofakeit.Email(), Pwd: "pwd123", ConfirmPwd: "pwd123", - FirstName: "Ankit", - MiddleName: "Singh", - LastName: "Patial", + FirstName: gofakeit.FirstName(), + MiddleName: gofakeit.MiddleName(), + LastName: gofakeit.LastName(), RoleID: 1, }); err != nil { t.Error(err) diff --git a/pkg/auth/password.go b/pkg/user/password.go similarity index 95% rename from pkg/auth/password.go rename to pkg/user/password.go index 619ad84..93e5b08 100644 --- a/pkg/auth/password.go +++ b/pkg/user/password.go @@ -1,4 +1,4 @@ -package auth +package user // EmailResetPWD link to user to reset password func EmailResetPWD(email string) { diff --git a/pkg/user/session.go b/pkg/user/session.go new file mode 100644 index 0000000..e7b55ee --- /dev/null +++ b/pkg/user/session.go @@ -0,0 +1,163 @@ +package user + +import ( + "context" + "errors" + "log/slog" + "time" + + "gitserver.in/patialtech/rano/config" + "gitserver.in/patialtech/rano/db" + "gitserver.in/patialtech/rano/db/ent" + "gitserver.in/patialtech/rano/db/ent/user" + "gitserver.in/patialtech/rano/graph/model" + "gitserver.in/patialtech/rano/util/crypto" + "gitserver.in/patialtech/rano/util/logger" +) + +type ( + SessionUser struct { + ID string + Email string + Name string + RoleID int + } + AuthUser = model.AuthUser +) + +var ( + ErrInvalidCred = errors.New("invalid email or password") + ErrAccountNotActive = errors.New("account is not active") + ErrAccountLocked = errors.New("account is locked, please try after sometime") + ErrUnexpected = errors.New("unexpected error has happened") +) + +func CtxWithUser(ctx context.Context, u *AuthUser) context.Context { + return context.WithValue(ctx, config.AuthUserCtxKey, &SessionUser{ + ID: u.ID, + Email: u.Email, + Name: u.Name, + RoleID: u.RoleID, + }) +} + +func CtxUser(ctx context.Context) *SessionUser { + u, _ := ctx.Value(config.AuthUserCtxKey).(*SessionUser) + return u +} + +// NewSession for user. +// +// Authenticated +func NewSession(ctx context.Context, email, pwd string) (*AuthUser, error) { + // authenticate. + u, err := authenticate(ctx, email, pwd) + if err != nil { + return nil, err + } + + // 30 day token life + until := time.Now().Add(time.Hour * 24 * 30).UTC() + + // create sesion entry in db + db.Client().UserSession.Create(). + SetUserID(u.ID). + SetIssuedAt(time.Now().UTC()). + SetExpiresAt(until). + SetIP(""). + SetUserAgent("") + + return &AuthUser{ + Name: fullName(u.FirstName, *u.MiddleName, u.LastName), + }, nil +} + +// RemoveSession entry from DB +func RemoveSession(sID uint) { + panic("not implemented") +} + +// authenticate user against DB +func authenticate(ctx context.Context, email, pwd string) (*ent.User, error) { + client := db.Client() + + // incident email attr + attrEmail := slog.String("email", email) + + // get user by given email + u, err := client.User. + Query(). + Where(user.EmailEQ(email)). + Select( + user.FieldEmail, user.FieldPwdHash, user.FieldPwdSalt, + user.FieldLoginFailedCount, user.FieldLoginLockedUntil, user.FieldLoginAttemptOn, + user.FieldFirstName, user.FieldMiddleName, user.FieldLastName, + user.FieldStatus, + ). + Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + logger.Incident(ctx, "Authenticate", "wrong email", attrEmail) + return nil, ErrInvalidCred + } + + logger.Error(err) + return nil, ErrUnexpected + } + + // check account is ready for authentication + // ensure that user account is active or perform other needed checks + if u.Status != user.StatusActive { + logger.Incident(ctx, "Authenticate", "account issue", attrEmail) + return nil, ErrAccountNotActive + } + + // check account is locked + lck := u.LoginLockedUntil + now := time.Now().UTC() + if lck != nil && now.Before(lck.UTC()) { + logger.Incident(ctx, "Authenticate", "account locked", attrEmail) + return nil, ErrAccountLocked + } + + upQry := client.User.UpdateOneID(u.ID) + // compare password + // in-case password is wrong, lets increment failed attempt + if !crypto.ComparePasswordHash(pwd, u.PwdHash, u.PwdSalt) { + var locked bool + u.LoginFailedCount++ + upQry. + SetLoginAttemptOn(time.Now().UTC()). + SetLoginFailedCount(u.LoginFailedCount) + + // lock user if count is more that 4 + if u.LoginFailedCount > 4 { + locked = true + upQry.SetLoginLockedUntil(time.Now().Add(time.Hour * 6).UTC()) + } + + // update user login attempt status + if err = upQry.Exec(ctx); err != nil { + return nil, ErrUnexpected + } + + if locked { + return nil, ErrAccountLocked + } + + return nil, ErrInvalidCred + } + + if u.LoginFailedCount > 0 { + u.LoginFailedCount = 0 + upQry.ClearLoginFailedCount() + if err := upQry.Exec(ctx); err != nil { + logger.Error(err, attrEmail) + } + } + + // let's not get them out + u.PwdHash = "" + u.PwdSalt = "" + return u, nil +} diff --git a/pkg/user/token.go b/pkg/user/token.go new file mode 100644 index 0000000..6a83f75 --- /dev/null +++ b/pkg/user/token.go @@ -0,0 +1,50 @@ +package user + +import ( + "errors" + "time" + + "gitserver.in/patialtech/rano/util/uid" +) + +var ( + ErrExpiredToken = errors.New("expired token") + ErrInvalidToken = errors.New("invalid token") +) + +// newTokenToVerifyEmail for a user for given duration +func newTokenToVerifyEmail(userID int64, d time.Duration) (string, error) { + expiresAt := time.Now().Add(d).UTC().UnixMilli() + return uid.Encode([]uint64{ + uint64(userID), + 1, // identifies that its token to verify email + uint64(expiresAt), + }) +} + +// tokenToVerifyEmail will check for valid email token that is yet not expired +// +// returns userID on success +func tokenToVerifyEmail(token string) (int64, error) { + ids, err := uid.Decode(token) + if err != nil { + return 0, nil + } + + // slice must have 3 entries + if len(ids) != 3 { + return 0, ErrInvalidToken + } + + // must be an email verify token + if ids[1] != 1 { + return 0, ErrInvalidToken + } + + // check expiry + if int64(ids[2]) < time.Now().UTC().UnixMilli() { + return 0, ErrExpiredToken + } + + return int64(ids[0]), nil +} diff --git a/pkg/user/token_test.go b/pkg/user/token_test.go new file mode 100644 index 0000000..e887bbf --- /dev/null +++ b/pkg/user/token_test.go @@ -0,0 +1,43 @@ +package user + +import ( + "errors" + "testing" + "time" +) + +func FuzzNewVerifyEmailToken(f *testing.F) { + f.Add(int64(123)) + f.Fuzz(func(t *testing.T, userID int64) { + _, err := newTokenToVerifyEmail(userID, time.Millisecond*100) + if err != nil { + t.Errorf("failed for input %d, %v", userID, err) + } + }) +} + +func TestEmailToken(t *testing.T) { + uID := int64(1234) + // create a token + t1, err := newTokenToVerifyEmail(uID, time.Millisecond*100) + if err != nil { + t.Error(err) + return + } + + // let decode token + id, err := tokenToVerifyEmail(t1) + if err != nil { + t.Error(err) + return + } else if uID != id { + t.Error("uid and decoded id are not same, ", uID, "!=", id) + } + + // lets wait and try decode again + time.Sleep(time.Millisecond * 100) + _, err = tokenToVerifyEmail(t1) + if !errors.Is(err, ErrExpiredToken) { + t.Error("expected expired token error") + } +} diff --git a/pkg/user/verify.go b/pkg/user/verify.go new file mode 100644 index 0000000..cd116a2 --- /dev/null +++ b/pkg/user/verify.go @@ -0,0 +1,60 @@ +package user + +import ( + "context" + "errors" + "log/slog" + + "gitserver.in/patialtech/rano/db" + "gitserver.in/patialtech/rano/db/ent" + "gitserver.in/patialtech/rano/db/ent/verifytoken" + "gitserver.in/patialtech/rano/util/logger" +) + +// VerifyEmailAddress by a token +func VerifyEmailAddress(ctx context.Context, token string) error { + // decode token + uid, err := tokenToVerifyEmail(token) + if err != nil { + return err + } + + client := db.Client() + + // get token from DB + vt, err := client.VerifyToken.Query().Where(verifytoken.TokenEQ(token)).Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + return ErrInvalidToken + } + + logger.Error(err, slog.String("ref", "pkg/user/verify.VerifyEmail")) + return ErrInvalidToken + } + + // all good, lets do the following + // 1. Update user email verify status + // 2. Remvoe token from DB + // do it in a transaction + + tx, err := client.BeginTx(ctx, nil) + if err != nil { + logger.Error(err) + return errors.New("unexpected error") + } + + // update user email verify status + if err = tx.User.UpdateOneID(uid).SetEmailVerified(true).Exec(ctx); err != nil { + return err + } + + // remove token from DB + if err = tx.VerifyToken.DeleteOneID(vt.ID).Exec(ctx); err != nil { + _ = tx.Rollback() + return err + } + + // we are all done now, + // let's commit + return tx.Commit() +} diff --git a/taskfile.yml b/taskfile.yml index a5c7493..dfce2fc 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -14,7 +14,7 @@ tasks: start-graph: desc: run graph server cmds: - - cmd: go run ./graph/server + - cmd: go run ./cmd/server start-web: desc: run web in dev mode @@ -36,7 +36,6 @@ tasks: graph-gen: desc: graph gen cmds: - - go mod tidy - go generate ./graph graph-codegen: diff --git a/util/logger/logger.go b/util/logger/logger.go index 5e7f083..c6b3cda 100644 --- a/util/logger/logger.go +++ b/util/logger/logger.go @@ -1,6 +1,7 @@ package logger import ( + "context" "fmt" "log/slog" "os" @@ -16,6 +17,12 @@ func Warn(msg string, args ...any) { slog.Warn(fmt.Sprintf(msg, a...), b...) } +func Incident(ctx context.Context, name, msg string, args ...any) { + a, b := getArgs(args) + slog.Warn(fmt.Sprintf(msg, a...), b...) + // TODO: save incident for alert purpose +} + func Error(err error, args ...any) { a, b := getArgs(args) slog.Error(fmt.Sprintf(err.Error(), a...), b...) diff --git a/util/uid/sqid.go b/util/uid/sqid.go new file mode 100644 index 0000000..99ecf08 --- /dev/null +++ b/util/uid/sqid.go @@ -0,0 +1,29 @@ +package uid + +import "github.com/sqids/sqids-go" + +// use your own random version of alphabets +// there is online util at the bottomt of this page: https://sqids.org/go +var opts sqids.Options = sqids.Options{ + Alphabet: "fsvjrnGWiTk2Lt1l5MzEhFH73CIg60ByexQPYpX9Ro8ZKawAVUdNuDJbmqScO4", +} + +// Encode a slice of IDs into one unique ID +func Encode(ids []uint64) (string, error) { + s, err := sqids.New() + if err != nil { + return "", err + } + + return s.Encode(ids) // "86Rf07" +} + +// Decode an ID back to slice of IDs +func Decode(id string) ([]uint64, error) { + s, err := sqids.New() + if err != nil { + return nil, err + } + + return s.Decode(id), nil +} diff --git a/web/lib/gql/auth.gql.ts b/web/lib/gql/account.gql.ts similarity index 100% rename from web/lib/gql/auth.gql.ts rename to web/lib/gql/account.gql.ts diff --git a/web/routes/+layout.svelte b/web/routes/+layout.svelte index 6329cb5..48f4efa 100644 --- a/web/routes/+layout.svelte +++ b/web/routes/+layout.svelte @@ -3,11 +3,11 @@ import { updated } from '$app/stores'; import { setContextClient } from '@urql/svelte'; import { newClient } from '$lib/gql/client'; + import { Me } from '$lib/gql/account.gql'; import '../app.css'; - import { Me } from '$lib/gql/auth.gql'; const { children } = $props(); - const host = import.meta.env.VITE_GRAPH_URL ?? '' + const host = import.meta.env.VITE_GRAPH_URL ?? ''; const client = newClient(`${host}/query`); setContextClient(client); diff --git a/web/routes/account/verify-email/+page.svelte b/web/routes/account/verify-email/+page.svelte new file mode 100644 index 0000000..c9eab4e --- /dev/null +++ b/web/routes/account/verify-email/+page.svelte @@ -0,0 +1,7 @@ + + +

hello

+

{hash}