tina4ruby 3.11.13 → 3.11.15
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 +4 -4
- data/CHANGELOG.md +80 -80
- data/LICENSE.txt +21 -21
- data/README.md +137 -137
- data/exe/tina4ruby +5 -5
- data/lib/tina4/ai.rb +696 -696
- data/lib/tina4/api.rb +189 -189
- data/lib/tina4/auth.rb +305 -305
- data/lib/tina4/auto_crud.rb +244 -244
- data/lib/tina4/cache.rb +154 -154
- data/lib/tina4/cli.rb +1449 -1449
- data/lib/tina4/constants.rb +46 -46
- data/lib/tina4/container.rb +74 -74
- data/lib/tina4/cors.rb +74 -74
- data/lib/tina4/crud.rb +692 -692
- data/lib/tina4/database/sqlite3_adapter.rb +165 -165
- data/lib/tina4/database.rb +625 -625
- data/lib/tina4/database_result.rb +208 -208
- data/lib/tina4/debug.rb +8 -8
- data/lib/tina4/dev.rb +14 -14
- data/lib/tina4/dev_admin.rb +935 -935
- data/lib/tina4/dev_mailbox.rb +191 -191
- data/lib/tina4/drivers/firebird_driver.rb +124 -110
- data/lib/tina4/drivers/mongodb_driver.rb +561 -561
- data/lib/tina4/drivers/mssql_driver.rb +112 -112
- data/lib/tina4/drivers/mysql_driver.rb +90 -90
- data/lib/tina4/drivers/odbc_driver.rb +191 -191
- data/lib/tina4/drivers/postgres_driver.rb +116 -106
- data/lib/tina4/drivers/sqlite_driver.rb +122 -122
- data/lib/tina4/env.rb +95 -95
- data/lib/tina4/error_overlay.rb +252 -252
- data/lib/tina4/events.rb +109 -109
- data/lib/tina4/field_types.rb +154 -154
- data/lib/tina4/frond.rb +2025 -2025
- data/lib/tina4/gallery/auth/meta.json +1 -1
- data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -114
- data/lib/tina4/gallery/database/meta.json +1 -1
- data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -43
- data/lib/tina4/gallery/error-overlay/meta.json +1 -1
- data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -17
- data/lib/tina4/gallery/orm/meta.json +1 -1
- data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -16
- data/lib/tina4/gallery/queue/meta.json +1 -1
- data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +325 -325
- data/lib/tina4/gallery/rest-api/meta.json +1 -1
- data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -14
- data/lib/tina4/gallery/templates/meta.json +1 -1
- data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -12
- data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -257
- data/lib/tina4/graphql.rb +966 -966
- data/lib/tina4/health.rb +39 -39
- data/lib/tina4/html_element.rb +170 -170
- data/lib/tina4/job.rb +80 -80
- data/lib/tina4/localization.rb +168 -168
- data/lib/tina4/log.rb +203 -203
- data/lib/tina4/mcp.rb +696 -696
- data/lib/tina4/messenger.rb +587 -587
- data/lib/tina4/metrics.rb +793 -793
- data/lib/tina4/middleware.rb +445 -445
- data/lib/tina4/migration.rb +451 -451
- data/lib/tina4/orm.rb +790 -790
- data/lib/tina4/public/css/tina4.css +2463 -2463
- data/lib/tina4/public/css/tina4.min.css +1 -1
- data/lib/tina4/public/images/logo.svg +5 -5
- data/lib/tina4/public/js/frond.min.js +2 -2
- data/lib/tina4/public/js/tina4-dev-admin.js +565 -565
- data/lib/tina4/public/js/tina4-dev-admin.min.js +480 -480
- data/lib/tina4/public/js/tina4.min.js +92 -92
- data/lib/tina4/public/js/tina4js.min.js +48 -48
- data/lib/tina4/public/swagger/index.html +90 -90
- data/lib/tina4/public/swagger/oauth2-redirect.html +63 -63
- data/lib/tina4/query_builder.rb +380 -380
- data/lib/tina4/queue.rb +366 -366
- data/lib/tina4/queue_backends/kafka_backend.rb +80 -80
- data/lib/tina4/queue_backends/lite_backend.rb +298 -298
- data/lib/tina4/queue_backends/mongo_backend.rb +126 -126
- data/lib/tina4/queue_backends/rabbitmq_backend.rb +73 -73
- data/lib/tina4/rack_app.rb +817 -817
- data/lib/tina4/rate_limiter.rb +130 -130
- data/lib/tina4/request.rb +268 -255
- data/lib/tina4/response.rb +346 -346
- data/lib/tina4/response_cache.rb +551 -551
- data/lib/tina4/router.rb +406 -406
- data/lib/tina4/scss/tina4css/_alerts.scss +34 -34
- data/lib/tina4/scss/tina4css/_badges.scss +22 -22
- data/lib/tina4/scss/tina4css/_buttons.scss +69 -69
- data/lib/tina4/scss/tina4css/_cards.scss +49 -49
- data/lib/tina4/scss/tina4css/_forms.scss +156 -156
- data/lib/tina4/scss/tina4css/_grid.scss +81 -81
- data/lib/tina4/scss/tina4css/_modals.scss +84 -84
- data/lib/tina4/scss/tina4css/_nav.scss +149 -149
- data/lib/tina4/scss/tina4css/_reset.scss +94 -94
- data/lib/tina4/scss/tina4css/_tables.scss +54 -54
- data/lib/tina4/scss/tina4css/_typography.scss +55 -55
- data/lib/tina4/scss/tina4css/_utilities.scss +197 -197
- data/lib/tina4/scss/tina4css/_variables.scss +117 -117
- data/lib/tina4/scss/tina4css/base.scss +1 -1
- data/lib/tina4/scss/tina4css/colors.scss +48 -48
- data/lib/tina4/scss/tina4css/tina4.scss +17 -17
- data/lib/tina4/scss_compiler.rb +178 -178
- data/lib/tina4/seeder.rb +567 -567
- data/lib/tina4/service_runner.rb +303 -303
- data/lib/tina4/session.rb +297 -297
- data/lib/tina4/session_handlers/database_handler.rb +72 -72
- data/lib/tina4/session_handlers/file_handler.rb +67 -67
- data/lib/tina4/session_handlers/mongo_handler.rb +49 -49
- data/lib/tina4/session_handlers/redis_handler.rb +43 -43
- data/lib/tina4/session_handlers/valkey_handler.rb +43 -43
- data/lib/tina4/shutdown.rb +84 -84
- data/lib/tina4/sql_translation.rb +158 -158
- data/lib/tina4/swagger.rb +124 -124
- data/lib/tina4/template.rb +894 -894
- data/lib/tina4/templates/base.twig +26 -26
- data/lib/tina4/templates/errors/302.twig +14 -14
- data/lib/tina4/templates/errors/401.twig +9 -9
- data/lib/tina4/templates/errors/403.twig +29 -29
- data/lib/tina4/templates/errors/404.twig +29 -29
- data/lib/tina4/templates/errors/500.twig +38 -38
- data/lib/tina4/templates/errors/502.twig +9 -9
- data/lib/tina4/templates/errors/503.twig +12 -12
- data/lib/tina4/templates/errors/base.twig +37 -37
- data/lib/tina4/test_client.rb +159 -159
- data/lib/tina4/testing.rb +340 -340
- data/lib/tina4/validator.rb +174 -174
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4/webserver.rb +312 -312
- data/lib/tina4/websocket.rb +343 -343
- data/lib/tina4/websocket_backplane.rb +190 -190
- data/lib/tina4/wsdl.rb +564 -564
- data/lib/tina4.rb +458 -458
- data/lib/tina4ruby.rb +4 -4
- metadata +3 -3
data/lib/tina4/session.rb
CHANGED
|
@@ -1,297 +1,297 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "securerandom"
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
|
-
module Tina4
|
|
6
|
-
class Session
|
|
7
|
-
DEFAULT_OPTIONS = {
|
|
8
|
-
cookie_name: "tina4_session",
|
|
9
|
-
secret: nil,
|
|
10
|
-
max_age: 86400,
|
|
11
|
-
handler: :file,
|
|
12
|
-
handler_options: {}
|
|
13
|
-
}.freeze
|
|
14
|
-
|
|
15
|
-
attr_reader :id, :data
|
|
16
|
-
|
|
17
|
-
def initialize(env, options = {})
|
|
18
|
-
@options = DEFAULT_OPTIONS.merge(options)
|
|
19
|
-
@options[:secret] ||= ENV["SECRET"] || "tina4-default-secret"
|
|
20
|
-
@handler = create_handler
|
|
21
|
-
@id = extract_session_id(env) || SecureRandom.hex(32)
|
|
22
|
-
@data = load_session
|
|
23
|
-
@modified = false
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def [](key)
|
|
27
|
-
@data[key.to_s]
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def []=(key, value)
|
|
31
|
-
@data[key.to_s] = value
|
|
32
|
-
@modified = true
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def delete(key)
|
|
36
|
-
@data.delete(key.to_s)
|
|
37
|
-
@modified = true
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def clear
|
|
41
|
-
@data = {}
|
|
42
|
-
@modified = true
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def to_hash
|
|
46
|
-
@data.dup
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def save
|
|
50
|
-
return unless @modified
|
|
51
|
-
@handler.write(@id, @data)
|
|
52
|
-
@modified = false
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def destroy
|
|
56
|
-
@handler.destroy(@id)
|
|
57
|
-
@data = {}
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Get a session value with optional default
|
|
61
|
-
def get(key, default = nil)
|
|
62
|
-
@data[key.to_s] || default
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Set a session value
|
|
66
|
-
def set(key, value)
|
|
67
|
-
@data[key.to_s] = value
|
|
68
|
-
@modified = true
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Check if a key exists in the session
|
|
72
|
-
def has?(key)
|
|
73
|
-
@data.key?(key.to_s)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Return all session data
|
|
77
|
-
def all
|
|
78
|
-
@data.dup
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Flash data: set a value that is removed after next read.
|
|
82
|
-
# Call with value to set, call without value to get (and remove).
|
|
83
|
-
def flash(key, value = nil)
|
|
84
|
-
flash_key = "_flash_#{key}"
|
|
85
|
-
if value.nil?
|
|
86
|
-
val = @data.delete(flash_key.to_s)
|
|
87
|
-
@modified = true if val
|
|
88
|
-
val
|
|
89
|
-
else
|
|
90
|
-
@data[flash_key.to_s] = value
|
|
91
|
-
@modified = true
|
|
92
|
-
value
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Get flash data by key (alias for flash(key) without value)
|
|
97
|
-
def get_flash(key, default = nil)
|
|
98
|
-
result = flash(key)
|
|
99
|
-
result.nil? ? default : result
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Regenerate the session ID while preserving data — returns new ID
|
|
103
|
-
def regenerate
|
|
104
|
-
old_id = @id
|
|
105
|
-
@id = SecureRandom.hex(32)
|
|
106
|
-
@handler.destroy(old_id)
|
|
107
|
-
@modified = true
|
|
108
|
-
@id
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Start or resume a session. If session_id is given, load that session;
|
|
112
|
-
# otherwise generate a new ID. Returns the session ID string.
|
|
113
|
-
def start(session_id = nil)
|
|
114
|
-
if session_id
|
|
115
|
-
@id = session_id
|
|
116
|
-
@data = load_session
|
|
117
|
-
else
|
|
118
|
-
@id = SecureRandom.hex(32)
|
|
119
|
-
@data = {}
|
|
120
|
-
end
|
|
121
|
-
@modified = false
|
|
122
|
-
@id
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Returns the current session ID string.
|
|
126
|
-
def get_session_id
|
|
127
|
-
@id
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Reads raw session data for a given session ID from backend storage.
|
|
131
|
-
# Returns the data hash or nil.
|
|
132
|
-
def read(session_id)
|
|
133
|
-
@handler.read(session_id)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# Writes raw session data for a given session ID to backend storage.
|
|
137
|
-
def write(session_id, data, ttl = nil)
|
|
138
|
-
if ttl
|
|
139
|
-
@handler.write(session_id, data, ttl)
|
|
140
|
-
else
|
|
141
|
-
@handler.write(session_id, data)
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Garbage collection: remove expired sessions from the handler
|
|
146
|
-
def gc(max_lifetime = nil)
|
|
147
|
-
max_lifetime ||= @options[:max_age]
|
|
148
|
-
@handler.gc(max_lifetime) if @handler.respond_to?(:gc)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def cookie_header(cookie_name = nil)
|
|
152
|
-
name = cookie_name || @options[:cookie_name]
|
|
153
|
-
samesite = ENV["TINA4_SESSION_SAMESITE"] || "Lax"
|
|
154
|
-
"#{name}=#{@id}; Path=/; HttpOnly; SameSite=#{samesite}; Max-Age=#{@options[:max_age]}"
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
private
|
|
158
|
-
|
|
159
|
-
def extract_session_id(env)
|
|
160
|
-
cookie_str = env["HTTP_COOKIE"] || ""
|
|
161
|
-
cookie_str.split(";").each do |pair|
|
|
162
|
-
key, value = pair.strip.split("=", 2)
|
|
163
|
-
return value if key == @options[:cookie_name]
|
|
164
|
-
end
|
|
165
|
-
nil
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def load_session
|
|
169
|
-
existing = @handler.read(@id)
|
|
170
|
-
existing || {}
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def create_handler
|
|
174
|
-
case @options[:handler].to_sym
|
|
175
|
-
when :file
|
|
176
|
-
Tina4::SessionHandlers::FileHandler.new(@options[:handler_options])
|
|
177
|
-
when :redis
|
|
178
|
-
Tina4::SessionHandlers::RedisHandler.new(@options[:handler_options])
|
|
179
|
-
when :mongo, :mongodb
|
|
180
|
-
Tina4::SessionHandlers::MongoHandler.new(@options[:handler_options])
|
|
181
|
-
when :valkey
|
|
182
|
-
Tina4::SessionHandlers::ValkeyHandler.new(@options[:handler_options])
|
|
183
|
-
when :database, :db
|
|
184
|
-
Tina4::SessionHandlers::DatabaseHandler.new(@options[:handler_options])
|
|
185
|
-
else
|
|
186
|
-
Tina4::SessionHandlers::FileHandler.new(@options[:handler_options])
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
class LazySession
|
|
192
|
-
def initialize(env, options = {})
|
|
193
|
-
@env = env
|
|
194
|
-
@options = options
|
|
195
|
-
@session = nil
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def [](key)
|
|
199
|
-
ensure_loaded
|
|
200
|
-
@session[key]
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
def []=(key, value)
|
|
204
|
-
ensure_loaded
|
|
205
|
-
@session[key] = value
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def delete(key)
|
|
209
|
-
ensure_loaded
|
|
210
|
-
@session.delete(key)
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
def clear
|
|
214
|
-
ensure_loaded
|
|
215
|
-
@session.clear
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
def save
|
|
219
|
-
@session&.save
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
def destroy
|
|
223
|
-
@session&.destroy
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def get(key, default = nil)
|
|
227
|
-
ensure_loaded
|
|
228
|
-
@session.get(key, default)
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
def set(key, value)
|
|
232
|
-
ensure_loaded
|
|
233
|
-
@session.set(key, value)
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
def has?(key)
|
|
237
|
-
ensure_loaded
|
|
238
|
-
@session.has?(key)
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
def all
|
|
242
|
-
ensure_loaded
|
|
243
|
-
@session.all
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
def flash(key, value = nil)
|
|
247
|
-
ensure_loaded
|
|
248
|
-
@session.flash(key, value)
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
def regenerate
|
|
252
|
-
ensure_loaded
|
|
253
|
-
@session.regenerate
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
def gc(max_lifetime = nil)
|
|
257
|
-
ensure_loaded
|
|
258
|
-
@session.gc(max_lifetime)
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
def start(session_id = nil)
|
|
262
|
-
ensure_loaded
|
|
263
|
-
@session.start(session_id)
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def get_session_id
|
|
267
|
-
ensure_loaded
|
|
268
|
-
@session.get_session_id
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
def read(session_id)
|
|
272
|
-
ensure_loaded
|
|
273
|
-
@session.read(session_id)
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
def write(session_id, data, ttl = nil)
|
|
277
|
-
ensure_loaded
|
|
278
|
-
@session.write(session_id, data, ttl)
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def cookie_header(cookie_name = nil)
|
|
282
|
-
ensure_loaded
|
|
283
|
-
@session.cookie_header(cookie_name)
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def to_hash
|
|
287
|
-
ensure_loaded
|
|
288
|
-
@session.to_hash
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
private
|
|
292
|
-
|
|
293
|
-
def ensure_loaded
|
|
294
|
-
@session ||= Session.new(@env, @options)
|
|
295
|
-
end
|
|
296
|
-
end
|
|
297
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "securerandom"
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Tina4
|
|
6
|
+
class Session
|
|
7
|
+
DEFAULT_OPTIONS = {
|
|
8
|
+
cookie_name: "tina4_session",
|
|
9
|
+
secret: nil,
|
|
10
|
+
max_age: 86400,
|
|
11
|
+
handler: :file,
|
|
12
|
+
handler_options: {}
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
attr_reader :id, :data
|
|
16
|
+
|
|
17
|
+
def initialize(env, options = {})
|
|
18
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
|
19
|
+
@options[:secret] ||= ENV["SECRET"] || "tina4-default-secret"
|
|
20
|
+
@handler = create_handler
|
|
21
|
+
@id = extract_session_id(env) || SecureRandom.hex(32)
|
|
22
|
+
@data = load_session
|
|
23
|
+
@modified = false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def [](key)
|
|
27
|
+
@data[key.to_s]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def []=(key, value)
|
|
31
|
+
@data[key.to_s] = value
|
|
32
|
+
@modified = true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def delete(key)
|
|
36
|
+
@data.delete(key.to_s)
|
|
37
|
+
@modified = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def clear
|
|
41
|
+
@data = {}
|
|
42
|
+
@modified = true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_hash
|
|
46
|
+
@data.dup
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def save
|
|
50
|
+
return unless @modified
|
|
51
|
+
@handler.write(@id, @data)
|
|
52
|
+
@modified = false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def destroy
|
|
56
|
+
@handler.destroy(@id)
|
|
57
|
+
@data = {}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Get a session value with optional default
|
|
61
|
+
def get(key, default = nil)
|
|
62
|
+
@data[key.to_s] || default
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Set a session value
|
|
66
|
+
def set(key, value)
|
|
67
|
+
@data[key.to_s] = value
|
|
68
|
+
@modified = true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Check if a key exists in the session
|
|
72
|
+
def has?(key)
|
|
73
|
+
@data.key?(key.to_s)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Return all session data
|
|
77
|
+
def all
|
|
78
|
+
@data.dup
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Flash data: set a value that is removed after next read.
|
|
82
|
+
# Call with value to set, call without value to get (and remove).
|
|
83
|
+
def flash(key, value = nil)
|
|
84
|
+
flash_key = "_flash_#{key}"
|
|
85
|
+
if value.nil?
|
|
86
|
+
val = @data.delete(flash_key.to_s)
|
|
87
|
+
@modified = true if val
|
|
88
|
+
val
|
|
89
|
+
else
|
|
90
|
+
@data[flash_key.to_s] = value
|
|
91
|
+
@modified = true
|
|
92
|
+
value
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Get flash data by key (alias for flash(key) without value)
|
|
97
|
+
def get_flash(key, default = nil)
|
|
98
|
+
result = flash(key)
|
|
99
|
+
result.nil? ? default : result
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Regenerate the session ID while preserving data — returns new ID
|
|
103
|
+
def regenerate
|
|
104
|
+
old_id = @id
|
|
105
|
+
@id = SecureRandom.hex(32)
|
|
106
|
+
@handler.destroy(old_id)
|
|
107
|
+
@modified = true
|
|
108
|
+
@id
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Start or resume a session. If session_id is given, load that session;
|
|
112
|
+
# otherwise generate a new ID. Returns the session ID string.
|
|
113
|
+
def start(session_id = nil)
|
|
114
|
+
if session_id
|
|
115
|
+
@id = session_id
|
|
116
|
+
@data = load_session
|
|
117
|
+
else
|
|
118
|
+
@id = SecureRandom.hex(32)
|
|
119
|
+
@data = {}
|
|
120
|
+
end
|
|
121
|
+
@modified = false
|
|
122
|
+
@id
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Returns the current session ID string.
|
|
126
|
+
def get_session_id
|
|
127
|
+
@id
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Reads raw session data for a given session ID from backend storage.
|
|
131
|
+
# Returns the data hash or nil.
|
|
132
|
+
def read(session_id)
|
|
133
|
+
@handler.read(session_id)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Writes raw session data for a given session ID to backend storage.
|
|
137
|
+
def write(session_id, data, ttl = nil)
|
|
138
|
+
if ttl
|
|
139
|
+
@handler.write(session_id, data, ttl)
|
|
140
|
+
else
|
|
141
|
+
@handler.write(session_id, data)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Garbage collection: remove expired sessions from the handler
|
|
146
|
+
def gc(max_lifetime = nil)
|
|
147
|
+
max_lifetime ||= @options[:max_age]
|
|
148
|
+
@handler.gc(max_lifetime) if @handler.respond_to?(:gc)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def cookie_header(cookie_name = nil)
|
|
152
|
+
name = cookie_name || @options[:cookie_name]
|
|
153
|
+
samesite = ENV["TINA4_SESSION_SAMESITE"] || "Lax"
|
|
154
|
+
"#{name}=#{@id}; Path=/; HttpOnly; SameSite=#{samesite}; Max-Age=#{@options[:max_age]}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
def extract_session_id(env)
|
|
160
|
+
cookie_str = env["HTTP_COOKIE"] || ""
|
|
161
|
+
cookie_str.split(";").each do |pair|
|
|
162
|
+
key, value = pair.strip.split("=", 2)
|
|
163
|
+
return value if key == @options[:cookie_name]
|
|
164
|
+
end
|
|
165
|
+
nil
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def load_session
|
|
169
|
+
existing = @handler.read(@id)
|
|
170
|
+
existing || {}
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def create_handler
|
|
174
|
+
case @options[:handler].to_sym
|
|
175
|
+
when :file
|
|
176
|
+
Tina4::SessionHandlers::FileHandler.new(@options[:handler_options])
|
|
177
|
+
when :redis
|
|
178
|
+
Tina4::SessionHandlers::RedisHandler.new(@options[:handler_options])
|
|
179
|
+
when :mongo, :mongodb
|
|
180
|
+
Tina4::SessionHandlers::MongoHandler.new(@options[:handler_options])
|
|
181
|
+
when :valkey
|
|
182
|
+
Tina4::SessionHandlers::ValkeyHandler.new(@options[:handler_options])
|
|
183
|
+
when :database, :db
|
|
184
|
+
Tina4::SessionHandlers::DatabaseHandler.new(@options[:handler_options])
|
|
185
|
+
else
|
|
186
|
+
Tina4::SessionHandlers::FileHandler.new(@options[:handler_options])
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
class LazySession
|
|
192
|
+
def initialize(env, options = {})
|
|
193
|
+
@env = env
|
|
194
|
+
@options = options
|
|
195
|
+
@session = nil
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def [](key)
|
|
199
|
+
ensure_loaded
|
|
200
|
+
@session[key]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def []=(key, value)
|
|
204
|
+
ensure_loaded
|
|
205
|
+
@session[key] = value
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def delete(key)
|
|
209
|
+
ensure_loaded
|
|
210
|
+
@session.delete(key)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def clear
|
|
214
|
+
ensure_loaded
|
|
215
|
+
@session.clear
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def save
|
|
219
|
+
@session&.save
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def destroy
|
|
223
|
+
@session&.destroy
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def get(key, default = nil)
|
|
227
|
+
ensure_loaded
|
|
228
|
+
@session.get(key, default)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def set(key, value)
|
|
232
|
+
ensure_loaded
|
|
233
|
+
@session.set(key, value)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def has?(key)
|
|
237
|
+
ensure_loaded
|
|
238
|
+
@session.has?(key)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def all
|
|
242
|
+
ensure_loaded
|
|
243
|
+
@session.all
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def flash(key, value = nil)
|
|
247
|
+
ensure_loaded
|
|
248
|
+
@session.flash(key, value)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def regenerate
|
|
252
|
+
ensure_loaded
|
|
253
|
+
@session.regenerate
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def gc(max_lifetime = nil)
|
|
257
|
+
ensure_loaded
|
|
258
|
+
@session.gc(max_lifetime)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def start(session_id = nil)
|
|
262
|
+
ensure_loaded
|
|
263
|
+
@session.start(session_id)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def get_session_id
|
|
267
|
+
ensure_loaded
|
|
268
|
+
@session.get_session_id
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def read(session_id)
|
|
272
|
+
ensure_loaded
|
|
273
|
+
@session.read(session_id)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def write(session_id, data, ttl = nil)
|
|
277
|
+
ensure_loaded
|
|
278
|
+
@session.write(session_id, data, ttl)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def cookie_header(cookie_name = nil)
|
|
282
|
+
ensure_loaded
|
|
283
|
+
@session.cookie_header(cookie_name)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def to_hash
|
|
287
|
+
ensure_loaded
|
|
288
|
+
@session.to_hash
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
private
|
|
292
|
+
|
|
293
|
+
def ensure_loaded
|
|
294
|
+
@session ||= Session.new(@env, @options)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|