From 13538191c9abf4ec19f59f161552c9348a840d97 Mon Sep 17 00:00:00 2001 From: maddalax Date: Sun, 29 Sep 2024 01:36:39 -0500 Subject: [PATCH] update tasks so they are by ip due to spammers --- examples/todo-list/ent/migrate/schema.go | 1 + examples/todo-list/ent/mutation.go | 85 ++++++++++++++++++-- examples/todo-list/ent/runtime.go | 4 + examples/todo-list/ent/schema/task.go | 4 +- examples/todo-list/ent/task.go | 15 +++- examples/todo-list/ent/task/task.go | 10 +++ examples/todo-list/ent/task/where.go | 80 ++++++++++++++++++ examples/todo-list/ent/task_create.go | 22 +++++ examples/todo-list/ent/task_update.go | 52 ++++++++++++ examples/todo-list/go.sum | 12 +++ examples/todo-list/internal/tasks/service.go | 18 +++-- examples/todo-list/internal/util/ip.go | 27 +++++++ examples/todo-list/partials/task/task.go | 19 +++-- 13 files changed, 324 insertions(+), 25 deletions(-) create mode 100644 examples/todo-list/internal/util/ip.go diff --git a/examples/todo-list/ent/migrate/schema.go b/examples/todo-list/ent/migrate/schema.go index da44192..73dbee0 100644 --- a/examples/todo-list/ent/migrate/schema.go +++ b/examples/todo-list/ent/migrate/schema.go @@ -16,6 +16,7 @@ var ( {Name: "updated_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, {Name: "completed_at", Type: field.TypeTime, Nullable: true}, {Name: "tags", Type: field.TypeJSON, Nullable: true}, + {Name: "ip_address", Type: field.TypeString, Nullable: true, Default: ""}, } // TasksTable holds the schema information for the "tasks" table. TasksTable = &schema.Table{ diff --git a/examples/todo-list/ent/mutation.go b/examples/todo-list/ent/mutation.go index bc87648..e3f3e48 100644 --- a/examples/todo-list/ent/mutation.go +++ b/examples/todo-list/ent/mutation.go @@ -31,8 +31,8 @@ const ( // TaskMutation represents an operation that mutates the Task nodes in the graph. type TaskMutation struct { config - op Op - typ string + op Op + typ string id *uuid.UUID name *string created_at *time.Time @@ -40,10 +40,11 @@ type TaskMutation struct { completed_at *time.Time tags *[]string appendtags []string + ip_address *string clearedFields map[string]struct{} - done bool - oldValue func(context.Context) (*Task, error) - predicates []predicate.Task + done bool + oldValue func(context.Context) (*Task, error) + predicates []predicate.Task } var _ ent.Mutation = (*TaskMutation)(nil) @@ -372,6 +373,55 @@ func (m *TaskMutation) ResetTags() { delete(m.clearedFields, task.FieldTags) } +// SetIPAddress sets the "ip_address" field. +func (m *TaskMutation) SetIPAddress(s string) { + m.ip_address = &s +} + +// IPAddress returns the value of the "ip_address" field in the mutation. +func (m *TaskMutation) IPAddress() (r string, exists bool) { + v := m.ip_address + if v == nil { + return + } + return *v, true +} + +// OldIPAddress returns the old "ip_address" field's value of the Task entity. +// If the Task 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 *TaskMutation) OldIPAddress(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldIPAddress is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldIPAddress requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldIPAddress: %w", err) + } + return oldValue.IPAddress, nil +} + +// ClearIPAddress clears the value of the "ip_address" field. +func (m *TaskMutation) ClearIPAddress() { + m.ip_address = nil + m.clearedFields[task.FieldIPAddress] = struct{}{} +} + +// IPAddressCleared returns if the "ip_address" field was cleared in this mutation. +func (m *TaskMutation) IPAddressCleared() bool { + _, ok := m.clearedFields[task.FieldIPAddress] + return ok +} + +// ResetIPAddress resets all changes to the "ip_address" field. +func (m *TaskMutation) ResetIPAddress() { + m.ip_address = nil + delete(m.clearedFields, task.FieldIPAddress) +} + // Where appends a list predicates to the TaskMutation builder. func (m *TaskMutation) Where(ps ...predicate.Task) { m.predicates = append(m.predicates, ps...) @@ -406,7 +456,7 @@ func (m *TaskMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *TaskMutation) Fields() []string { - fields := make([]string, 0, 5) + fields := make([]string, 0, 6) if m.name != nil { fields = append(fields, task.FieldName) } @@ -422,6 +472,9 @@ func (m *TaskMutation) Fields() []string { if m.tags != nil { fields = append(fields, task.FieldTags) } + if m.ip_address != nil { + fields = append(fields, task.FieldIPAddress) + } return fields } @@ -440,6 +493,8 @@ func (m *TaskMutation) Field(name string) (ent.Value, bool) { return m.CompletedAt() case task.FieldTags: return m.Tags() + case task.FieldIPAddress: + return m.IPAddress() } return nil, false } @@ -459,6 +514,8 @@ func (m *TaskMutation) OldField(ctx context.Context, name string) (ent.Value, er return m.OldCompletedAt(ctx) case task.FieldTags: return m.OldTags(ctx) + case task.FieldIPAddress: + return m.OldIPAddress(ctx) } return nil, fmt.Errorf("unknown Task field %s", name) } @@ -503,6 +560,13 @@ func (m *TaskMutation) SetField(name string, value ent.Value) error { } m.SetTags(v) return nil + case task.FieldIPAddress: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetIPAddress(v) + return nil } return fmt.Errorf("unknown Task field %s", name) } @@ -539,6 +603,9 @@ func (m *TaskMutation) ClearedFields() []string { if m.FieldCleared(task.FieldTags) { fields = append(fields, task.FieldTags) } + if m.FieldCleared(task.FieldIPAddress) { + fields = append(fields, task.FieldIPAddress) + } return fields } @@ -559,6 +626,9 @@ func (m *TaskMutation) ClearField(name string) error { case task.FieldTags: m.ClearTags() return nil + case task.FieldIPAddress: + m.ClearIPAddress() + return nil } return fmt.Errorf("unknown Task nullable field %s", name) } @@ -582,6 +652,9 @@ func (m *TaskMutation) ResetField(name string) error { case task.FieldTags: m.ResetTags() return nil + case task.FieldIPAddress: + m.ResetIPAddress() + return nil } return fmt.Errorf("unknown Task field %s", name) } diff --git a/examples/todo-list/ent/runtime.go b/examples/todo-list/ent/runtime.go index 09af9f2..04e14d7 100644 --- a/examples/todo-list/ent/runtime.go +++ b/examples/todo-list/ent/runtime.go @@ -28,6 +28,10 @@ func init() { taskDescUpdatedAt := taskFields[3].Descriptor() // task.DefaultUpdatedAt holds the default value on creation for the updated_at field. task.DefaultUpdatedAt = taskDescUpdatedAt.Default.(func() time.Time) + // taskDescIPAddress is the schema descriptor for ip_address field. + taskDescIPAddress := taskFields[6].Descriptor() + // task.DefaultIPAddress holds the default value on creation for the ip_address field. + task.DefaultIPAddress = taskDescIPAddress.Default.(string) // taskDescID is the schema descriptor for id field. taskDescID := taskFields[0].Descriptor() // task.DefaultID holds the default value on creation for the id field. diff --git a/examples/todo-list/ent/schema/task.go b/examples/todo-list/ent/schema/task.go index 594a7d5..f6e858c 100644 --- a/examples/todo-list/ent/schema/task.go +++ b/examples/todo-list/ent/schema/task.go @@ -17,8 +17,7 @@ func (Task) Fields() []ent.Field { return []ent.Field{ field.UUID("id", uuid.UUID{}). Default(uuid.New), - field.String("name"). - Default("unknown"), + field.String("name").Default("unknown"), field.Time("created_at").Default(time.Now).Annotations( entsql.Default("CURRENT_TIMESTAMP"), ), @@ -27,6 +26,7 @@ func (Task) Fields() []ent.Field { ), field.Time("completed_at").Optional().Nillable(), field.Strings("tags").Optional(), + field.String("ip_address").Optional().Default(""), } } diff --git a/examples/todo-list/ent/task.go b/examples/todo-list/ent/task.go index d8fa6f7..6a5e54d 100644 --- a/examples/todo-list/ent/task.go +++ b/examples/todo-list/ent/task.go @@ -28,7 +28,9 @@ type Task struct { // CompletedAt holds the value of the "completed_at" field. CompletedAt *time.Time `json:"completed_at,omitempty"` // Tags holds the value of the "tags" field. - Tags []string `json:"tags,omitempty"` + Tags []string `json:"tags,omitempty"` + // IPAddress holds the value of the "ip_address" field. + IPAddress string `json:"ip_address,omitempty"` selectValues sql.SelectValues } @@ -39,7 +41,7 @@ func (*Task) scanValues(columns []string) ([]any, error) { switch columns[i] { case task.FieldTags: values[i] = new([]byte) - case task.FieldName: + case task.FieldName, task.FieldIPAddress: values[i] = new(sql.NullString) case task.FieldCreatedAt, task.FieldUpdatedAt, task.FieldCompletedAt: values[i] = new(sql.NullTime) @@ -99,6 +101,12 @@ func (t *Task) assignValues(columns []string, values []any) error { return fmt.Errorf("unmarshal field tags: %w", err) } } + case task.FieldIPAddress: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field ip_address", values[i]) + } else if value.Valid { + t.IPAddress = value.String + } default: t.selectValues.Set(columns[i], values[i]) } @@ -151,6 +159,9 @@ func (t *Task) String() string { builder.WriteString(", ") builder.WriteString("tags=") builder.WriteString(fmt.Sprintf("%v", t.Tags)) + builder.WriteString(", ") + builder.WriteString("ip_address=") + builder.WriteString(t.IPAddress) builder.WriteByte(')') return builder.String() } diff --git a/examples/todo-list/ent/task/task.go b/examples/todo-list/ent/task/task.go index c1ded40..fbf0f27 100644 --- a/examples/todo-list/ent/task/task.go +++ b/examples/todo-list/ent/task/task.go @@ -24,6 +24,8 @@ const ( FieldCompletedAt = "completed_at" // FieldTags holds the string denoting the tags field in the database. FieldTags = "tags" + // FieldIPAddress holds the string denoting the ip_address field in the database. + FieldIPAddress = "ip_address" // Table holds the table name of the task in the database. Table = "tasks" ) @@ -36,6 +38,7 @@ var Columns = []string{ FieldUpdatedAt, FieldCompletedAt, FieldTags, + FieldIPAddress, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -55,6 +58,8 @@ var ( DefaultCreatedAt func() time.Time // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. DefaultUpdatedAt func() time.Time + // DefaultIPAddress holds the default value on creation for the "ip_address" field. + DefaultIPAddress string // DefaultID holds the default value on creation for the "id" field. DefaultID func() uuid.UUID ) @@ -86,3 +91,8 @@ func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { func ByCompletedAt(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCompletedAt, opts...).ToFunc() } + +// ByIPAddress orders the results by the ip_address field. +func ByIPAddress(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldIPAddress, opts...).ToFunc() +} diff --git a/examples/todo-list/ent/task/where.go b/examples/todo-list/ent/task/where.go index a7ea7cb..37b306e 100644 --- a/examples/todo-list/ent/task/where.go +++ b/examples/todo-list/ent/task/where.go @@ -75,6 +75,11 @@ func CompletedAt(v time.Time) predicate.Task { return predicate.Task(sql.FieldEQ(FieldCompletedAt, v)) } +// IPAddress applies equality check predicate on the "ip_address" field. It's identical to IPAddressEQ. +func IPAddress(v string) predicate.Task { + return predicate.Task(sql.FieldEQ(FieldIPAddress, v)) +} + // NameEQ applies the EQ predicate on the "name" field. func NameEQ(v string) predicate.Task { return predicate.Task(sql.FieldEQ(FieldName, v)) @@ -280,6 +285,81 @@ func TagsNotNil() predicate.Task { return predicate.Task(sql.FieldNotNull(FieldTags)) } +// IPAddressEQ applies the EQ predicate on the "ip_address" field. +func IPAddressEQ(v string) predicate.Task { + return predicate.Task(sql.FieldEQ(FieldIPAddress, v)) +} + +// IPAddressNEQ applies the NEQ predicate on the "ip_address" field. +func IPAddressNEQ(v string) predicate.Task { + return predicate.Task(sql.FieldNEQ(FieldIPAddress, v)) +} + +// IPAddressIn applies the In predicate on the "ip_address" field. +func IPAddressIn(vs ...string) predicate.Task { + return predicate.Task(sql.FieldIn(FieldIPAddress, vs...)) +} + +// IPAddressNotIn applies the NotIn predicate on the "ip_address" field. +func IPAddressNotIn(vs ...string) predicate.Task { + return predicate.Task(sql.FieldNotIn(FieldIPAddress, vs...)) +} + +// IPAddressGT applies the GT predicate on the "ip_address" field. +func IPAddressGT(v string) predicate.Task { + return predicate.Task(sql.FieldGT(FieldIPAddress, v)) +} + +// IPAddressGTE applies the GTE predicate on the "ip_address" field. +func IPAddressGTE(v string) predicate.Task { + return predicate.Task(sql.FieldGTE(FieldIPAddress, v)) +} + +// IPAddressLT applies the LT predicate on the "ip_address" field. +func IPAddressLT(v string) predicate.Task { + return predicate.Task(sql.FieldLT(FieldIPAddress, v)) +} + +// IPAddressLTE applies the LTE predicate on the "ip_address" field. +func IPAddressLTE(v string) predicate.Task { + return predicate.Task(sql.FieldLTE(FieldIPAddress, v)) +} + +// IPAddressContains applies the Contains predicate on the "ip_address" field. +func IPAddressContains(v string) predicate.Task { + return predicate.Task(sql.FieldContains(FieldIPAddress, v)) +} + +// IPAddressHasPrefix applies the HasPrefix predicate on the "ip_address" field. +func IPAddressHasPrefix(v string) predicate.Task { + return predicate.Task(sql.FieldHasPrefix(FieldIPAddress, v)) +} + +// IPAddressHasSuffix applies the HasSuffix predicate on the "ip_address" field. +func IPAddressHasSuffix(v string) predicate.Task { + return predicate.Task(sql.FieldHasSuffix(FieldIPAddress, v)) +} + +// IPAddressIsNil applies the IsNil predicate on the "ip_address" field. +func IPAddressIsNil() predicate.Task { + return predicate.Task(sql.FieldIsNull(FieldIPAddress)) +} + +// IPAddressNotNil applies the NotNil predicate on the "ip_address" field. +func IPAddressNotNil() predicate.Task { + return predicate.Task(sql.FieldNotNull(FieldIPAddress)) +} + +// IPAddressEqualFold applies the EqualFold predicate on the "ip_address" field. +func IPAddressEqualFold(v string) predicate.Task { + return predicate.Task(sql.FieldEqualFold(FieldIPAddress, v)) +} + +// IPAddressContainsFold applies the ContainsFold predicate on the "ip_address" field. +func IPAddressContainsFold(v string) predicate.Task { + return predicate.Task(sql.FieldContainsFold(FieldIPAddress, v)) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.Task) predicate.Task { return predicate.Task(sql.AndPredicates(predicates...)) diff --git a/examples/todo-list/ent/task_create.go b/examples/todo-list/ent/task_create.go index a5e657f..3205eb3 100644 --- a/examples/todo-list/ent/task_create.go +++ b/examples/todo-list/ent/task_create.go @@ -83,6 +83,20 @@ func (tc *TaskCreate) SetTags(s []string) *TaskCreate { return tc } +// SetIPAddress sets the "ip_address" field. +func (tc *TaskCreate) SetIPAddress(s string) *TaskCreate { + tc.mutation.SetIPAddress(s) + return tc +} + +// SetNillableIPAddress sets the "ip_address" field if the given value is not nil. +func (tc *TaskCreate) SetNillableIPAddress(s *string) *TaskCreate { + if s != nil { + tc.SetIPAddress(*s) + } + return tc +} + // SetID sets the "id" field. func (tc *TaskCreate) SetID(u uuid.UUID) *TaskCreate { tc.mutation.SetID(u) @@ -144,6 +158,10 @@ func (tc *TaskCreate) defaults() { v := task.DefaultUpdatedAt() tc.mutation.SetUpdatedAt(v) } + if _, ok := tc.mutation.IPAddress(); !ok { + v := task.DefaultIPAddress + tc.mutation.SetIPAddress(v) + } if _, ok := tc.mutation.ID(); !ok { v := task.DefaultID() tc.mutation.SetID(v) @@ -216,6 +234,10 @@ func (tc *TaskCreate) createSpec() (*Task, *sqlgraph.CreateSpec) { _spec.SetField(task.FieldTags, field.TypeJSON, value) _node.Tags = value } + if value, ok := tc.mutation.IPAddress(); ok { + _spec.SetField(task.FieldIPAddress, field.TypeString, value) + _node.IPAddress = value + } return _node, _spec } diff --git a/examples/todo-list/ent/task_update.go b/examples/todo-list/ent/task_update.go index ba2839d..063222a 100644 --- a/examples/todo-list/ent/task_update.go +++ b/examples/todo-list/ent/task_update.go @@ -109,6 +109,26 @@ func (tu *TaskUpdate) ClearTags() *TaskUpdate { return tu } +// SetIPAddress sets the "ip_address" field. +func (tu *TaskUpdate) SetIPAddress(s string) *TaskUpdate { + tu.mutation.SetIPAddress(s) + return tu +} + +// SetNillableIPAddress sets the "ip_address" field if the given value is not nil. +func (tu *TaskUpdate) SetNillableIPAddress(s *string) *TaskUpdate { + if s != nil { + tu.SetIPAddress(*s) + } + return tu +} + +// ClearIPAddress clears the value of the "ip_address" field. +func (tu *TaskUpdate) ClearIPAddress() *TaskUpdate { + tu.mutation.ClearIPAddress() + return tu +} + // Mutation returns the TaskMutation object of the builder. func (tu *TaskUpdate) Mutation() *TaskMutation { return tu.mutation @@ -176,6 +196,12 @@ func (tu *TaskUpdate) sqlSave(ctx context.Context) (n int, err error) { if tu.mutation.TagsCleared() { _spec.ClearField(task.FieldTags, field.TypeJSON) } + if value, ok := tu.mutation.IPAddress(); ok { + _spec.SetField(task.FieldIPAddress, field.TypeString, value) + } + if tu.mutation.IPAddressCleared() { + _spec.ClearField(task.FieldIPAddress, field.TypeString) + } if n, err = sqlgraph.UpdateNodes(ctx, tu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{task.Label} @@ -276,6 +302,26 @@ func (tuo *TaskUpdateOne) ClearTags() *TaskUpdateOne { return tuo } +// SetIPAddress sets the "ip_address" field. +func (tuo *TaskUpdateOne) SetIPAddress(s string) *TaskUpdateOne { + tuo.mutation.SetIPAddress(s) + return tuo +} + +// SetNillableIPAddress sets the "ip_address" field if the given value is not nil. +func (tuo *TaskUpdateOne) SetNillableIPAddress(s *string) *TaskUpdateOne { + if s != nil { + tuo.SetIPAddress(*s) + } + return tuo +} + +// ClearIPAddress clears the value of the "ip_address" field. +func (tuo *TaskUpdateOne) ClearIPAddress() *TaskUpdateOne { + tuo.mutation.ClearIPAddress() + return tuo +} + // Mutation returns the TaskMutation object of the builder. func (tuo *TaskUpdateOne) Mutation() *TaskMutation { return tuo.mutation @@ -373,6 +419,12 @@ func (tuo *TaskUpdateOne) sqlSave(ctx context.Context) (_node *Task, err error) if tuo.mutation.TagsCleared() { _spec.ClearField(task.FieldTags, field.TypeJSON) } + if value, ok := tuo.mutation.IPAddress(); ok { + _spec.SetField(task.FieldIPAddress, field.TypeString, value) + } + if tuo.mutation.IPAddressCleared() { + _spec.ClearField(task.FieldIPAddress, field.TypeString) + } _node = &Task{config: tuo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/examples/todo-list/go.sum b/examples/todo-list/go.sum index 018238e..3abd3bf 100644 --- a/examples/todo-list/go.sum +++ b/examples/todo-list/go.sum @@ -35,14 +35,22 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/maddalax/htmgo/framework v0.0.0-20240929054559-c045e880a7c7 h1:aORZEDTAjTaF2FWIpiFOFnNpAUMl02wIUMvhs8du/AM= github.com/maddalax/htmgo/framework v0.0.0-20240929054559-c045e880a7c7/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= +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.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= @@ -56,6 +64,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -63,6 +73,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/examples/todo-list/internal/tasks/service.go b/examples/todo-list/internal/tasks/service.go index 5b47d8c..974dcf5 100644 --- a/examples/todo-list/internal/tasks/service.go +++ b/examples/todo-list/internal/tasks/service.go @@ -3,15 +3,18 @@ package tasks import ( "context" "github.com/google/uuid" + "github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/service" "time" "todolist/ent" "todolist/ent/predicate" "todolist/ent/task" + "todolist/internal/util" ) type Service struct { - db *ent.Client + db *ent.Client + ipAddress string } type CreateRequest struct { @@ -19,9 +22,10 @@ type CreateRequest struct { Tags []string } -func NewService(locator *service.Locator) Service { +func NewService(ctx *h.RequestContext) Service { return Service{ - db: service.Get[ent.Client](locator), + ipAddress: util.GetClientIp(ctx.Request), + db: service.Get[ent.Client](ctx.ServiceLocator()), } } @@ -29,6 +33,7 @@ func (s *Service) Create(request CreateRequest) (*ent.Task, error) { return s.db.Task.Create(). SetName(request.Name). SetTags(request.Tags). + SetIPAddress(s.ipAddress). Save(context.Background()) } @@ -37,7 +42,7 @@ func (s *Service) Get(id uuid.UUID) (*ent.Task, error) { } func (s *Service) SetName(id uuid.UUID, name string) (*ent.Task, error) { - return s.db.Task.UpdateOneID(id).SetName(name).Save(context.Background()) + return s.db.Task.UpdateOneID(id).Where(task.IPAddress(s.ipAddress)).SetName(name).Save(context.Background()) } func (s *Service) SetAllCompleted(value bool) error { @@ -51,6 +56,7 @@ func (s *Service) SetAllCompleted(value bool) error { } _, err := updater. + Where(task.IPAddress(s.ipAddress)). SetUpdatedAt(time.Now()). Save(ctx) @@ -59,7 +65,7 @@ func (s *Service) SetAllCompleted(value bool) error { func (s *Service) ClearCompleted() error { ctx := context.Background() - _, err := s.db.Task.Delete().Where(task.CompletedAtNotNil()).Exec(ctx) + _, err := s.db.Task.Delete().Where(task.CompletedAtNotNil(), task.IPAddress(s.ipAddress)).Exec(ctx) return err } @@ -75,9 +81,11 @@ func (s *Service) SetCompleted(id uuid.UUID, value bool) (*ent.Task, error) { return updater. SetUpdatedAt(time.Now()). + Where(task.IPAddress(s.ipAddress)). Save(ctx) } func (s *Service) List(ps ...predicate.Task) ([]*ent.Task, error) { + ps = append(ps, task.IPAddress(s.ipAddress)) return s.db.Task.Query().Where(ps...).All(context.Background()) } diff --git a/examples/todo-list/internal/util/ip.go b/examples/todo-list/internal/util/ip.go new file mode 100644 index 0000000..4fcbe91 --- /dev/null +++ b/examples/todo-list/internal/util/ip.go @@ -0,0 +1,27 @@ +package util + +import ( + "net/http" + "strings" +) + +func GetClientIp(r *http.Request) string { + // Try to get the real client IP from the 'CF-Connecting-IP' header + if ip := r.Header.Get("CF-Connecting-IP"); ip != "" { + return ip + } + + // If not available, fall back to 'X-Forwarded-For' + if ip := r.Header.Get("X-Forwarded-For"); ip != "" { + return ip + } + + // Otherwise, use the default remote address (this will be Cloudflare's IP) + remote := r.RemoteAddr + + if strings.HasPrefix(remote, "[::1]") { + return "localhost" + } + + return remote +} diff --git a/examples/todo-list/partials/task/task.go b/examples/todo-list/partials/task/task.go index 2fa37ab..29498ab 100644 --- a/examples/todo-list/partials/task/task.go +++ b/examples/todo-list/partials/task/task.go @@ -26,7 +26,7 @@ func getActiveTab(ctx *h.RequestContext) Tab { } func Card(ctx *h.RequestContext) *h.Element { - service := tasks.NewService(ctx.ServiceLocator()) + service := tasks.NewService(ctx) list, _ := service.List() return h.Div( @@ -52,7 +52,6 @@ func Input(list []*ent.Task) *h.Element { h.Input( "text", h.Required(), - h.Disabled(), h.MaxLength(150), h.AutoComplete("off"), h.AutoFocus(), @@ -212,7 +211,7 @@ func UpdateName(ctx *h.RequestContext) *h.Partial { return h.NewPartial(h.Div(h.Text("task must be less than 150 characters"))) } - service := tasks.NewService(ctx.ServiceLocator()) + service := tasks.NewService(ctx) task, err := service.Get(id) if task == nil { @@ -235,7 +234,7 @@ func EditNameForm(ctx *h.RequestContext) *h.Partial { return h.NewPartial(h.Div(h.Text("invalid id"))) } - service := tasks.NewService(ctx.ServiceLocator()) + service := tasks.NewService(ctx) task, err := service.Get(id) if task == nil { @@ -253,7 +252,7 @@ func ToggleCompleted(ctx *h.RequestContext) *h.Partial { return h.NewPartial(h.Div(h.Text("invalid id"))) } - service := tasks.NewService(ctx.ServiceLocator()) + service := tasks.NewService(ctx) task, err := service.Get(id) if task == nil { @@ -277,7 +276,7 @@ func ToggleCompleted(ctx *h.RequestContext) *h.Partial { } func CompleteAll(ctx *h.RequestContext) *h.Partial { - service := tasks.NewService(ctx.ServiceLocator()) + service := tasks.NewService(ctx) service.SetAllCompleted(ctx.QueryParam("complete") == "true") @@ -287,7 +286,7 @@ func CompleteAll(ctx *h.RequestContext) *h.Partial { } func ClearCompleted(ctx *h.RequestContext) *h.Partial { - service := tasks.NewService(ctx.ServiceLocator()) + service := tasks.NewService(ctx) _ = service.ClearCompleted() list, _ := service.List() @@ -306,11 +305,11 @@ func Create(ctx *h.RequestContext) *h.Partial { ) } - service := tasks.NewService(ctx.ServiceLocator()) + service := tasks.NewService(ctx) list, _ := service.List() - if list != nil && len(list) >= 200 { + if list != nil && len(list) >= 100 { return h.NewPartial( h.Div( h.HxOnLoad(js.Alert("There are too many tasks, please complete and clear some.")), @@ -334,7 +333,7 @@ func Create(ctx *h.RequestContext) *h.Partial { } func ChangeTab(ctx *h.RequestContext) *h.Partial { - service := tasks.NewService(ctx.ServiceLocator()) + service := tasks.NewService(ctx) list, _ := service.List() tab := ctx.QueryParam("tab")