better_auth-hanami 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE.md +22 -0
- data/README.md +153 -0
- data/lib/better_auth/hanami/action_helpers.rb +74 -0
- data/lib/better_auth/hanami/configuration.rb +53 -0
- data/lib/better_auth/hanami/generators/install_generator.rb +130 -0
- data/lib/better_auth/hanami/generators/migration_generator.rb +50 -0
- data/lib/better_auth/hanami/generators/relation_generator.rb +130 -0
- data/lib/better_auth/hanami/migration.rb +99 -0
- data/lib/better_auth/hanami/mounted_app.rb +36 -0
- data/lib/better_auth/hanami/routing.rb +34 -0
- data/lib/better_auth/hanami/sequel_adapter.rb +288 -0
- data/lib/better_auth/hanami/version.rb +7 -0
- data/lib/better_auth/hanami.rb +35 -0
- data/lib/better_auth_hanami.rb +3 -0
- data/lib/tasks/better_auth.rake +22 -0
- metadata +233 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module Hanami
|
|
5
|
+
module Migration
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def render(options)
|
|
9
|
+
tables = BetterAuth::Schema.auth_tables(options)
|
|
10
|
+
lines = [
|
|
11
|
+
"# frozen_string_literal: true",
|
|
12
|
+
"",
|
|
13
|
+
"require \"date\"",
|
|
14
|
+
"require \"rom-sql\"",
|
|
15
|
+
"",
|
|
16
|
+
"ROM::SQL.migration do",
|
|
17
|
+
" change do"
|
|
18
|
+
]
|
|
19
|
+
tables.each_value { |table| lines.concat(create_table_lines(table, options)) }
|
|
20
|
+
lines.concat([" end", "end", ""])
|
|
21
|
+
lines.join("\n")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create_table_lines(table, options)
|
|
25
|
+
table_name = table.fetch(:model_name)
|
|
26
|
+
lines = ["", " create_table :#{table_name} do"]
|
|
27
|
+
table.fetch(:fields).each do |logical_field, attributes|
|
|
28
|
+
lines << column_line(logical_field, attributes, options)
|
|
29
|
+
end
|
|
30
|
+
lines << " primary_key [:id]" if table.fetch(:fields).key?("id")
|
|
31
|
+
table.fetch(:fields).each do |logical_field, attributes|
|
|
32
|
+
index = index_line(logical_field, attributes)
|
|
33
|
+
lines << index if index
|
|
34
|
+
end
|
|
35
|
+
lines << " end"
|
|
36
|
+
lines
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def column_line(logical_field, attributes, options)
|
|
40
|
+
column = attributes[:field_name] || physical_name(logical_field)
|
|
41
|
+
reference = attributes[:references]
|
|
42
|
+
if reference
|
|
43
|
+
target = foreign_key_target(reference.fetch(:model), options)
|
|
44
|
+
parts = ["foreign_key :#{column}, :#{target}", "type: #{hanami_type(attributes)}"]
|
|
45
|
+
parts << "null: false" if attributes[:required]
|
|
46
|
+
parts << "on_delete: :#{reference[:on_delete]}" if reference[:on_delete]
|
|
47
|
+
return " #{parts.join(", ")}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
parts = ["column :#{column}", hanami_type(attributes)]
|
|
51
|
+
parts << "null: false" if attributes[:required]
|
|
52
|
+
default = default_value(attributes)
|
|
53
|
+
parts << "default: #{default}" unless default.nil?
|
|
54
|
+
" #{parts.join(", ")}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def index_line(logical_field, attributes)
|
|
58
|
+
return unless attributes[:unique] || attributes[:index]
|
|
59
|
+
|
|
60
|
+
column = attributes[:field_name] || physical_name(logical_field)
|
|
61
|
+
unique = attributes[:unique] ? ", unique: true" : ""
|
|
62
|
+
" index :#{column}#{unique}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def hanami_type(attributes)
|
|
66
|
+
case attributes[:type]
|
|
67
|
+
when "boolean" then "TrueClass"
|
|
68
|
+
when "date" then "DateTime"
|
|
69
|
+
when "number" then "Integer"
|
|
70
|
+
else "String"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def default_value(attributes)
|
|
75
|
+
default = attributes[:default_value]
|
|
76
|
+
return if default.respond_to?(:call)
|
|
77
|
+
|
|
78
|
+
case default
|
|
79
|
+
when true then "true"
|
|
80
|
+
when false then "false"
|
|
81
|
+
when Numeric then default.to_s
|
|
82
|
+
when String then default.inspect
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def foreign_key_target(model, options)
|
|
87
|
+
tables = BetterAuth::Schema.auth_tables(options)
|
|
88
|
+
tables.fetch(model.to_s, nil)&.fetch(:model_name) || model
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def physical_name(value)
|
|
92
|
+
value.to_s
|
|
93
|
+
.gsub(/([a-z\d])([A-Z])/, "\\1_\\2")
|
|
94
|
+
.tr("-", "_")
|
|
95
|
+
.downcase
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module Hanami
|
|
5
|
+
class MountedApp
|
|
6
|
+
def initialize(auth, mount_path:)
|
|
7
|
+
@auth = auth
|
|
8
|
+
@mount_path = normalize_path(mount_path)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(env)
|
|
12
|
+
@auth.call(env.merge("PATH_INFO" => mounted_path_info(env)))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def mounted_path_info(env)
|
|
18
|
+
path_info = normalize_path(env["PATH_INFO"])
|
|
19
|
+
script_name = normalize_path(env["SCRIPT_NAME"])
|
|
20
|
+
prefix = (script_name == "/") ? @mount_path : script_name
|
|
21
|
+
|
|
22
|
+
return path_info if path_info == prefix || path_info.start_with?("#{prefix}/")
|
|
23
|
+
|
|
24
|
+
normalize_path("#{prefix}/#{path_info.delete_prefix("/")}")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def normalize_path(path)
|
|
28
|
+
normalized = path.to_s
|
|
29
|
+
normalized = "/#{normalized}" unless normalized.start_with?("/")
|
|
30
|
+
normalized = normalized.squeeze("/")
|
|
31
|
+
normalized = normalized.delete_suffix("/") unless normalized == "/"
|
|
32
|
+
normalized.empty? ? "/" : normalized
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module Hanami
|
|
5
|
+
module Routing
|
|
6
|
+
HTTP_METHODS = %i[get post put patch delete options].freeze
|
|
7
|
+
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.extend(self)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def better_auth(auth: nil, at: BetterAuth::Configuration::DEFAULT_BASE_PATH)
|
|
13
|
+
mount_path = normalize_better_auth_mount_path(at)
|
|
14
|
+
auth ||= BetterAuth::Hanami.auth(base_path: mount_path)
|
|
15
|
+
app = BetterAuth::Hanami::MountedApp.new(auth, mount_path: mount_path)
|
|
16
|
+
|
|
17
|
+
HTTP_METHODS.each do |method_name|
|
|
18
|
+
public_send(method_name, mount_path, to: app)
|
|
19
|
+
public_send(method_name, "#{mount_path}/*path", to: app)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def normalize_better_auth_mount_path(path)
|
|
26
|
+
normalized = path.to_s
|
|
27
|
+
normalized = "/#{normalized}" unless normalized.start_with?("/")
|
|
28
|
+
normalized = normalized.squeeze("/")
|
|
29
|
+
normalized = normalized.delete_suffix("/") unless normalized == "/"
|
|
30
|
+
normalized.empty? ? "/" : normalized
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "time"
|
|
5
|
+
require "sequel"
|
|
6
|
+
|
|
7
|
+
module BetterAuth
|
|
8
|
+
module Hanami
|
|
9
|
+
class SequelAdapter < BetterAuth::Adapters::Base
|
|
10
|
+
attr_reader :connection
|
|
11
|
+
|
|
12
|
+
def self.from_hanami(options, container: nil)
|
|
13
|
+
if container.nil? && defined?(::Hanami) && ::Hanami.respond_to?(:app)
|
|
14
|
+
container = ::Hanami.app
|
|
15
|
+
end
|
|
16
|
+
return BetterAuth::Adapters::Memory.new(options) unless container
|
|
17
|
+
|
|
18
|
+
from_container(container, options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.from_container(container, options)
|
|
22
|
+
gateway = if container.respond_to?(:key?) && container.key?("db.gateway")
|
|
23
|
+
container["db.gateway"]
|
|
24
|
+
elsif container.respond_to?(:[]) && safe_fetch(container, "db.gateway")
|
|
25
|
+
container["db.gateway"]
|
|
26
|
+
end
|
|
27
|
+
return BetterAuth::Adapters::Memory.new(options) unless gateway
|
|
28
|
+
|
|
29
|
+
connection = gateway.respond_to?(:connection) ? gateway.connection : gateway
|
|
30
|
+
new(options, connection: connection)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.safe_fetch(container, key)
|
|
34
|
+
container[key]
|
|
35
|
+
rescue KeyError
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def initialize(options, connection:)
|
|
40
|
+
super(options)
|
|
41
|
+
@connection = connection
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def create(model:, data:, force_allow_id: false)
|
|
45
|
+
model = model.to_s
|
|
46
|
+
input = transform_input(model, data, "create", force_allow_id)
|
|
47
|
+
table_dataset(model).insert(physical_attributes(model, input))
|
|
48
|
+
find_one(model: model, where: [{field: "id", value: input.fetch("id")}])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def find_one(model:, where: [], select: nil, join: nil)
|
|
52
|
+
find_many(model: model, where: where, select: select, join: join, limit: 1).first
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def find_many(model:, where: [], sort_by: nil, limit: nil, offset: nil, select: nil, join: nil)
|
|
56
|
+
model = model.to_s
|
|
57
|
+
dataset = table_dataset(model)
|
|
58
|
+
dataset = apply_where(model, dataset, where || [])
|
|
59
|
+
dataset = apply_select(model, dataset, select) if select
|
|
60
|
+
dataset = apply_order(model, dataset, sort_by) if sort_by
|
|
61
|
+
dataset = dataset.limit(Integer(limit)) if limit
|
|
62
|
+
dataset = dataset.offset(Integer(offset)) if offset
|
|
63
|
+
|
|
64
|
+
records = dataset.all.map { |row| normalize_record(model, row) }
|
|
65
|
+
attach_joins(model, records, join)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def update(model:, where:, update:)
|
|
69
|
+
model = model.to_s
|
|
70
|
+
existing = find_one(model: model, where: where, select: ["id"])
|
|
71
|
+
return nil unless existing
|
|
72
|
+
|
|
73
|
+
update_many(model: model, where: where, update: update)
|
|
74
|
+
find_one(model: model, where: [{field: "id", value: existing.fetch("id")}])
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update_many(model:, where:, update:, returning: false)
|
|
78
|
+
model = model.to_s
|
|
79
|
+
existing = returning ? find_many(model: model, where: where, select: ["id"]) : []
|
|
80
|
+
attributes = physical_attributes(model, transform_input(model, update, "update", true))
|
|
81
|
+
apply_where(model, table_dataset(model), where || []).update(attributes)
|
|
82
|
+
return unless returning
|
|
83
|
+
|
|
84
|
+
existing.map { |record| find_one(model: model, where: [{field: "id", value: record.fetch("id")}]) }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def delete(model:, where:)
|
|
88
|
+
delete_many(model: model, where: where)
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def delete_many(model:, where:)
|
|
93
|
+
model = model.to_s
|
|
94
|
+
apply_where(model, table_dataset(model), where || []).delete
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def count(model:, where: nil)
|
|
98
|
+
model = model.to_s
|
|
99
|
+
apply_where(model, table_dataset(model), where || []).count
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def transaction
|
|
103
|
+
connection.transaction { yield self }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def table_dataset(model)
|
|
109
|
+
connection[table_for(model).to_sym]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def apply_where(model, dataset, where)
|
|
113
|
+
expression = Array(where).each_with_index.reduce(nil) do |combined, (clause, index)|
|
|
114
|
+
current = where_expression(model, clause)
|
|
115
|
+
next current if index.zero?
|
|
116
|
+
|
|
117
|
+
connector = fetch_key(clause, :connector).to_s.upcase
|
|
118
|
+
(connector == "OR") ? Sequel.|(combined, current) : Sequel.&(combined, current)
|
|
119
|
+
end
|
|
120
|
+
expression ? dataset.where(expression) : dataset
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def where_expression(model, clause)
|
|
124
|
+
field = storage_key(fetch_key(clause, :field))
|
|
125
|
+
column = storage_field(model, field)
|
|
126
|
+
identifier = Sequel[column.to_sym]
|
|
127
|
+
operator = (fetch_key(clause, :operator) || "eq").to_s
|
|
128
|
+
value = fetch_key(clause, :value)
|
|
129
|
+
|
|
130
|
+
case operator
|
|
131
|
+
when "in" then {column.to_sym => Array(value)}
|
|
132
|
+
when "not_in" then Sequel.~(column.to_sym => Array(value))
|
|
133
|
+
when "ne" then Sequel.~(column.to_sym => value)
|
|
134
|
+
when "gt" then identifier > value
|
|
135
|
+
when "gte" then identifier >= value
|
|
136
|
+
when "lt" then identifier < value
|
|
137
|
+
when "lte" then identifier <= value
|
|
138
|
+
when "contains" then Sequel.like(identifier, "%#{value}%")
|
|
139
|
+
when "starts_with" then Sequel.like(identifier, "#{value}%")
|
|
140
|
+
when "ends_with" then Sequel.like(identifier, "%#{value}")
|
|
141
|
+
else {column.to_sym => value}
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def apply_select(model, dataset, select)
|
|
146
|
+
dataset.select(*Array(select).map { |field| storage_field(model, storage_key(field)).to_sym })
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def apply_order(model, dataset, sort_by)
|
|
150
|
+
column = storage_field(model, storage_key(fetch_key(sort_by, :field))).to_sym
|
|
151
|
+
direction = (fetch_key(sort_by, :direction).to_s.downcase == "desc") ? Sequel.desc(column) : column
|
|
152
|
+
dataset.order(direction)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def attach_joins(model, records, join)
|
|
156
|
+
return records unless join
|
|
157
|
+
|
|
158
|
+
records.each do |record|
|
|
159
|
+
join.each_key do |join_model|
|
|
160
|
+
join_model = join_model.to_s
|
|
161
|
+
case [model.to_s, join_model]
|
|
162
|
+
when ["session", "user"], ["account", "user"]
|
|
163
|
+
record[join_model] = find_one(model: join_model, where: [{field: "id", value: record["userId"]}])
|
|
164
|
+
when ["user", "account"]
|
|
165
|
+
record[join_model] = find_many(model: "account", where: [{field: "userId", value: record["id"]}])
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
records
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def transform_input(model, data, action, force_allow_id)
|
|
173
|
+
fields = schema_for(model).fetch(:fields)
|
|
174
|
+
input = stringify_keys(data)
|
|
175
|
+
output = {}
|
|
176
|
+
|
|
177
|
+
fields.each do |field, attributes|
|
|
178
|
+
next if field == "id" && input.key?(field) && !force_allow_id
|
|
179
|
+
|
|
180
|
+
value_provided = input.key?(field)
|
|
181
|
+
value = input[field]
|
|
182
|
+
if value_provided && attributes[:input] == false && value && !force_allow_id
|
|
183
|
+
raise APIError.new("BAD_REQUEST", message: "#{field} is not allowed to be set")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
if !value_provided && action == "create" && attributes.key?(:default_value)
|
|
187
|
+
value = resolve_default(attributes[:default_value])
|
|
188
|
+
value_provided = true
|
|
189
|
+
elsif !value_provided && action == "update" && attributes[:on_update]
|
|
190
|
+
value = resolve_default(attributes[:on_update])
|
|
191
|
+
value_provided = true
|
|
192
|
+
end
|
|
193
|
+
if !value_provided && action == "create" && attributes[:required]
|
|
194
|
+
raise APIError.new("BAD_REQUEST", message: "#{field} is required") unless field == "id"
|
|
195
|
+
end
|
|
196
|
+
output[field] = coerce_value(value, attributes) if value_provided
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
output["id"] = generated_id if action == "create" && !output.key?("id")
|
|
200
|
+
output
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def physical_attributes(model, logical)
|
|
204
|
+
logical.each_with_object({}) do |(field, value), attributes|
|
|
205
|
+
attributes[storage_field(model, field).to_sym] = value
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def normalize_record(model, row)
|
|
210
|
+
return nil unless row
|
|
211
|
+
|
|
212
|
+
schema_for(model).fetch(:fields).each_with_object({}) do |(field, attributes), output|
|
|
213
|
+
column = (attributes[:field_name] || physical_name(field)).to_sym
|
|
214
|
+
output[field] = coerce_output_value(row[column], attributes) if row.key?(column)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def table_for(model)
|
|
219
|
+
schema_for(model).fetch(:model_name)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def schema_for(model)
|
|
223
|
+
BetterAuth::Schema.auth_tables(options).fetch(model.to_s)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def storage_field(model, field)
|
|
227
|
+
schema_for(model).fetch(:fields).fetch(field.to_s).fetch(:field_name, physical_name(field))
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def generated_id
|
|
231
|
+
generator = options.advanced.dig(:database, :generate_id)
|
|
232
|
+
return generator.call.to_s if generator.respond_to?(:call)
|
|
233
|
+
return SecureRandom.uuid if generator == "uuid"
|
|
234
|
+
|
|
235
|
+
SecureRandom.hex(16)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def resolve_default(default)
|
|
239
|
+
default.respond_to?(:call) ? default.call : default
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def coerce_value(value, attributes)
|
|
243
|
+
return value if value.nil?
|
|
244
|
+
return Time.parse(value) if attributes[:type] == "date" && value.is_a?(String)
|
|
245
|
+
|
|
246
|
+
value
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def coerce_output_value(value, attributes)
|
|
250
|
+
return value if value.nil?
|
|
251
|
+
return coerce_boolean(value) if attributes[:type] == "boolean"
|
|
252
|
+
return Time.parse(value.to_s) if attributes[:type] == "date" && !value.is_a?(Time)
|
|
253
|
+
|
|
254
|
+
value
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def coerce_boolean(value)
|
|
258
|
+
return value if value == true || value == false
|
|
259
|
+
return false if value == 0 || value.to_s == "0" || value.to_s.downcase == "f" || value.to_s.downcase == "false"
|
|
260
|
+
return true if value == 1 || value.to_s == "1" || value.to_s.downcase == "t" || value.to_s.downcase == "true"
|
|
261
|
+
|
|
262
|
+
value
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def stringify_keys(data)
|
|
266
|
+
data.each_with_object({}) do |(key, value), result|
|
|
267
|
+
result[storage_key(key)] = value
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def fetch_key(hash, key)
|
|
272
|
+
hash[key] || hash[key.to_s] || hash[storage_key(key)] || hash[storage_key(key).to_sym]
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def storage_key(value)
|
|
276
|
+
parts = physical_name(value).split("_")
|
|
277
|
+
([parts.first] + parts.drop(1).map(&:capitalize)).join
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def physical_name(value)
|
|
281
|
+
value.to_s
|
|
282
|
+
.gsub(/([a-z\d])([A-Z])/, "\\1_\\2")
|
|
283
|
+
.tr("-", "_")
|
|
284
|
+
.downcase
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "better_auth"
|
|
4
|
+
require_relative "hanami/version"
|
|
5
|
+
require_relative "hanami/configuration"
|
|
6
|
+
require_relative "hanami/mounted_app"
|
|
7
|
+
require_relative "hanami/routing"
|
|
8
|
+
require_relative "hanami/migration"
|
|
9
|
+
require_relative "hanami/sequel_adapter"
|
|
10
|
+
require_relative "hanami/action_helpers"
|
|
11
|
+
require_relative "hanami/generators/install_generator"
|
|
12
|
+
require_relative "hanami/generators/migration_generator"
|
|
13
|
+
require_relative "hanami/generators/relation_generator"
|
|
14
|
+
|
|
15
|
+
module BetterAuth
|
|
16
|
+
module Hanami
|
|
17
|
+
class << self
|
|
18
|
+
def configuration
|
|
19
|
+
@configuration ||= Configuration.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def configure
|
|
23
|
+
yield configuration
|
|
24
|
+
@auth = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def auth(overrides = nil)
|
|
28
|
+
options = configuration.to_auth_options
|
|
29
|
+
return @auth ||= BetterAuth.auth(options) if overrides.nil? || overrides.empty?
|
|
30
|
+
|
|
31
|
+
BetterAuth.auth(options.merge(overrides))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "better_auth/hanami"
|
|
4
|
+
|
|
5
|
+
namespace :better_auth do
|
|
6
|
+
desc "Create Better Auth Hanami provider, routes, settings, tasks, and base migration"
|
|
7
|
+
task :init do
|
|
8
|
+
BetterAuth::Hanami::Generators::InstallGenerator.new.run
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
namespace :generate do
|
|
12
|
+
desc "Create the Better Auth Hanami base migration"
|
|
13
|
+
task :migration do
|
|
14
|
+
BetterAuth::Hanami::Generators::MigrationGenerator.new.run
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Create Hanami relations and repos for Better Auth tables"
|
|
18
|
+
task :relations do
|
|
19
|
+
BetterAuth::Hanami::Generators::RelationGenerator.new.run
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|