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
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
|
-
module Tina4
|
|
6
|
-
module SessionHandlers
|
|
7
|
-
class DatabaseHandler
|
|
8
|
-
TABLE_NAME = "tina4_session"
|
|
9
|
-
|
|
10
|
-
CREATE_TABLE_SQL = <<~SQL
|
|
11
|
-
CREATE TABLE IF NOT EXISTS #{TABLE_NAME} (
|
|
12
|
-
session_id VARCHAR(255) PRIMARY KEY,
|
|
13
|
-
data TEXT NOT NULL,
|
|
14
|
-
expires_at REAL NOT NULL
|
|
15
|
-
)
|
|
16
|
-
SQL
|
|
17
|
-
|
|
18
|
-
def initialize(options = {})
|
|
19
|
-
@ttl = options[:ttl] || 86400
|
|
20
|
-
@db = options[:db] || Tina4::Database.new(ENV["DATABASE_URL"])
|
|
21
|
-
ensure_table
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def read(session_id)
|
|
25
|
-
row = @db.fetch_one("SELECT data, expires_at FROM #{TABLE_NAME} WHERE session_id = ?", [session_id])
|
|
26
|
-
return nil unless row
|
|
27
|
-
|
|
28
|
-
expires_at = (row[:expires_at] || row["expires_at"]).to_f
|
|
29
|
-
if expires_at > 0 && expires_at < Time.now.to_f
|
|
30
|
-
destroy(session_id)
|
|
31
|
-
return nil
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
JSON.parse(row[:data] || row["data"])
|
|
35
|
-
rescue JSON::ParserError
|
|
36
|
-
nil
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def write(session_id, data)
|
|
40
|
-
expires_at = @ttl > 0 ? Time.now.to_f + @ttl : 0.0
|
|
41
|
-
json_data = JSON.generate(data)
|
|
42
|
-
|
|
43
|
-
existing = @db.fetch_one("SELECT session_id FROM #{TABLE_NAME} WHERE session_id = ?", [session_id])
|
|
44
|
-
if existing
|
|
45
|
-
@db.execute("UPDATE #{TABLE_NAME} SET data = ?, expires_at = ? WHERE session_id = ?", [json_data, expires_at, session_id])
|
|
46
|
-
else
|
|
47
|
-
@db.execute("INSERT INTO #{TABLE_NAME} (session_id, data, expires_at) VALUES (?, ?, ?)", [session_id, json_data, expires_at])
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def destroy(session_id)
|
|
52
|
-
@db.execute("DELETE FROM #{TABLE_NAME} WHERE session_id = ?", [session_id])
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def cleanup
|
|
56
|
-
@db.execute("DELETE FROM #{TABLE_NAME} WHERE expires_at > 0 AND expires_at < ?", [Time.now.to_f])
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Garbage-collect expired sessions. Matches the Python interface.
|
|
60
|
-
# @param max_age [Integer] maximum session age in seconds (unused — expiry is absolute)
|
|
61
|
-
def gc(max_age)
|
|
62
|
-
@db.execute("DELETE FROM #{TABLE_NAME} WHERE expires_at > 0 AND expires_at < ?", [Time.now.to_f])
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
private
|
|
66
|
-
|
|
67
|
-
def ensure_table
|
|
68
|
-
@db.execute(CREATE_TABLE_SQL)
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Tina4
|
|
6
|
+
module SessionHandlers
|
|
7
|
+
class DatabaseHandler
|
|
8
|
+
TABLE_NAME = "tina4_session"
|
|
9
|
+
|
|
10
|
+
CREATE_TABLE_SQL = <<~SQL
|
|
11
|
+
CREATE TABLE IF NOT EXISTS #{TABLE_NAME} (
|
|
12
|
+
session_id VARCHAR(255) PRIMARY KEY,
|
|
13
|
+
data TEXT NOT NULL,
|
|
14
|
+
expires_at REAL NOT NULL
|
|
15
|
+
)
|
|
16
|
+
SQL
|
|
17
|
+
|
|
18
|
+
def initialize(options = {})
|
|
19
|
+
@ttl = options[:ttl] || 86400
|
|
20
|
+
@db = options[:db] || Tina4::Database.new(ENV["DATABASE_URL"])
|
|
21
|
+
ensure_table
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def read(session_id)
|
|
25
|
+
row = @db.fetch_one("SELECT data, expires_at FROM #{TABLE_NAME} WHERE session_id = ?", [session_id])
|
|
26
|
+
return nil unless row
|
|
27
|
+
|
|
28
|
+
expires_at = (row[:expires_at] || row["expires_at"]).to_f
|
|
29
|
+
if expires_at > 0 && expires_at < Time.now.to_f
|
|
30
|
+
destroy(session_id)
|
|
31
|
+
return nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
JSON.parse(row[:data] || row["data"])
|
|
35
|
+
rescue JSON::ParserError
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def write(session_id, data)
|
|
40
|
+
expires_at = @ttl > 0 ? Time.now.to_f + @ttl : 0.0
|
|
41
|
+
json_data = JSON.generate(data)
|
|
42
|
+
|
|
43
|
+
existing = @db.fetch_one("SELECT session_id FROM #{TABLE_NAME} WHERE session_id = ?", [session_id])
|
|
44
|
+
if existing
|
|
45
|
+
@db.execute("UPDATE #{TABLE_NAME} SET data = ?, expires_at = ? WHERE session_id = ?", [json_data, expires_at, session_id])
|
|
46
|
+
else
|
|
47
|
+
@db.execute("INSERT INTO #{TABLE_NAME} (session_id, data, expires_at) VALUES (?, ?, ?)", [session_id, json_data, expires_at])
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def destroy(session_id)
|
|
52
|
+
@db.execute("DELETE FROM #{TABLE_NAME} WHERE session_id = ?", [session_id])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def cleanup
|
|
56
|
+
@db.execute("DELETE FROM #{TABLE_NAME} WHERE expires_at > 0 AND expires_at < ?", [Time.now.to_f])
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Garbage-collect expired sessions. Matches the Python interface.
|
|
60
|
+
# @param max_age [Integer] maximum session age in seconds (unused — expiry is absolute)
|
|
61
|
+
def gc(max_age)
|
|
62
|
+
@db.execute("DELETE FROM #{TABLE_NAME} WHERE expires_at > 0 AND expires_at < ?", [Time.now.to_f])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def ensure_table
|
|
68
|
+
@db.execute(CREATE_TABLE_SQL)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
require "fileutils"
|
|
4
|
-
|
|
5
|
-
module Tina4
|
|
6
|
-
module SessionHandlers
|
|
7
|
-
class FileHandler
|
|
8
|
-
def initialize(options = {})
|
|
9
|
-
@dir = options[:dir] || File.join(Dir.pwd, "sessions")
|
|
10
|
-
@ttl = options[:ttl] || 86400
|
|
11
|
-
FileUtils.mkdir_p(@dir)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def read(session_id)
|
|
15
|
-
path = session_path(session_id)
|
|
16
|
-
return nil unless File.exist?(path)
|
|
17
|
-
|
|
18
|
-
# Check expiry
|
|
19
|
-
if File.mtime(path) + @ttl < Time.now
|
|
20
|
-
File.delete(path)
|
|
21
|
-
return nil
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
data = File.read(path)
|
|
25
|
-
JSON.parse(data)
|
|
26
|
-
rescue JSON::ParserError
|
|
27
|
-
nil
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def write(session_id, data)
|
|
31
|
-
path = session_path(session_id)
|
|
32
|
-
File.write(path, JSON.generate(data))
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def destroy(session_id)
|
|
36
|
-
path = session_path(session_id)
|
|
37
|
-
File.delete(path) if File.exist?(path)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def cleanup
|
|
41
|
-
return unless Dir.exist?(@dir)
|
|
42
|
-
Dir.glob(File.join(@dir, "sess_*")).each do |file|
|
|
43
|
-
File.delete(file) if File.mtime(file) + @ttl < Time.now
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Garbage-collect expired sessions. Matches the Python interface.
|
|
48
|
-
# @param max_age [Integer] maximum session age in seconds
|
|
49
|
-
def gc(max_age)
|
|
50
|
-
return unless Dir.exist?(@dir)
|
|
51
|
-
now = Time.now
|
|
52
|
-
Dir.glob(File.join(@dir, "sess_*")).each do |file|
|
|
53
|
-
File.delete(file) if File.mtime(file) + max_age < now
|
|
54
|
-
rescue StandardError
|
|
55
|
-
# Corrupt or locked file — skip
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
61
|
-
def session_path(session_id)
|
|
62
|
-
safe_id = session_id.gsub(/[^a-zA-Z0-9_-]/, "")
|
|
63
|
-
File.join(@dir, "sess_#{safe_id}.json")
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "json"
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Tina4
|
|
6
|
+
module SessionHandlers
|
|
7
|
+
class FileHandler
|
|
8
|
+
def initialize(options = {})
|
|
9
|
+
@dir = options[:dir] || File.join(Dir.pwd, "sessions")
|
|
10
|
+
@ttl = options[:ttl] || 86400
|
|
11
|
+
FileUtils.mkdir_p(@dir)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read(session_id)
|
|
15
|
+
path = session_path(session_id)
|
|
16
|
+
return nil unless File.exist?(path)
|
|
17
|
+
|
|
18
|
+
# Check expiry
|
|
19
|
+
if File.mtime(path) + @ttl < Time.now
|
|
20
|
+
File.delete(path)
|
|
21
|
+
return nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
data = File.read(path)
|
|
25
|
+
JSON.parse(data)
|
|
26
|
+
rescue JSON::ParserError
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def write(session_id, data)
|
|
31
|
+
path = session_path(session_id)
|
|
32
|
+
File.write(path, JSON.generate(data))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def destroy(session_id)
|
|
36
|
+
path = session_path(session_id)
|
|
37
|
+
File.delete(path) if File.exist?(path)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def cleanup
|
|
41
|
+
return unless Dir.exist?(@dir)
|
|
42
|
+
Dir.glob(File.join(@dir, "sess_*")).each do |file|
|
|
43
|
+
File.delete(file) if File.mtime(file) + @ttl < Time.now
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Garbage-collect expired sessions. Matches the Python interface.
|
|
48
|
+
# @param max_age [Integer] maximum session age in seconds
|
|
49
|
+
def gc(max_age)
|
|
50
|
+
return unless Dir.exist?(@dir)
|
|
51
|
+
now = Time.now
|
|
52
|
+
Dir.glob(File.join(@dir, "sess_*")).each do |file|
|
|
53
|
+
File.delete(file) if File.mtime(file) + max_age < now
|
|
54
|
+
rescue StandardError
|
|
55
|
+
# Corrupt or locked file — skip
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def session_path(session_id)
|
|
62
|
+
safe_id = session_id.gsub(/[^a-zA-Z0-9_-]/, "")
|
|
63
|
+
File.join(@dir, "sess_#{safe_id}.json")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
|
|
4
|
-
module Tina4
|
|
5
|
-
module SessionHandlers
|
|
6
|
-
class MongoHandler
|
|
7
|
-
def initialize(options = {})
|
|
8
|
-
require "mongo"
|
|
9
|
-
@ttl = options[:ttl] || 86400
|
|
10
|
-
client = Mongo::Client.new(
|
|
11
|
-
options[:uri] || "mongodb://localhost:27017",
|
|
12
|
-
database: options[:database] || "tina4_sessions"
|
|
13
|
-
)
|
|
14
|
-
@collection = client[options[:collection] || "sessions"]
|
|
15
|
-
# Ensure TTL index
|
|
16
|
-
@collection.indexes.create_one(
|
|
17
|
-
{ updated_at: 1 },
|
|
18
|
-
expire_after_seconds: @ttl
|
|
19
|
-
)
|
|
20
|
-
rescue LoadError
|
|
21
|
-
raise "MongoDB session handler requires the 'mongo' gem. Install with: gem install mongo"
|
|
22
|
-
rescue Mongo::Error => e
|
|
23
|
-
Tina4::Log.error("MongoDB session setup failed: #{e.message}")
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def read(session_id)
|
|
27
|
-
doc = @collection.find(_id: session_id).first
|
|
28
|
-
return nil unless doc
|
|
29
|
-
doc["data"]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def write(session_id, data)
|
|
33
|
-
@collection.update_one(
|
|
34
|
-
{ _id: session_id },
|
|
35
|
-
{ "$set" => { data: data, updated_at: Time.now } },
|
|
36
|
-
upsert: true
|
|
37
|
-
)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def destroy(session_id)
|
|
41
|
-
@collection.delete_one(_id: session_id)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def cleanup
|
|
45
|
-
# MongoDB TTL index handles cleanup
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module Tina4
|
|
5
|
+
module SessionHandlers
|
|
6
|
+
class MongoHandler
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
require "mongo"
|
|
9
|
+
@ttl = options[:ttl] || 86400
|
|
10
|
+
client = Mongo::Client.new(
|
|
11
|
+
options[:uri] || "mongodb://localhost:27017",
|
|
12
|
+
database: options[:database] || "tina4_sessions"
|
|
13
|
+
)
|
|
14
|
+
@collection = client[options[:collection] || "sessions"]
|
|
15
|
+
# Ensure TTL index
|
|
16
|
+
@collection.indexes.create_one(
|
|
17
|
+
{ updated_at: 1 },
|
|
18
|
+
expire_after_seconds: @ttl
|
|
19
|
+
)
|
|
20
|
+
rescue LoadError
|
|
21
|
+
raise "MongoDB session handler requires the 'mongo' gem. Install with: gem install mongo"
|
|
22
|
+
rescue Mongo::Error => e
|
|
23
|
+
Tina4::Log.error("MongoDB session setup failed: #{e.message}")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def read(session_id)
|
|
27
|
+
doc = @collection.find(_id: session_id).first
|
|
28
|
+
return nil unless doc
|
|
29
|
+
doc["data"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def write(session_id, data)
|
|
33
|
+
@collection.update_one(
|
|
34
|
+
{ _id: session_id },
|
|
35
|
+
{ "$set" => { data: data, updated_at: Time.now } },
|
|
36
|
+
upsert: true
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def destroy(session_id)
|
|
41
|
+
@collection.delete_one(_id: session_id)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def cleanup
|
|
45
|
+
# MongoDB TTL index handles cleanup
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
|
|
4
|
-
module Tina4
|
|
5
|
-
module SessionHandlers
|
|
6
|
-
class RedisHandler
|
|
7
|
-
def initialize(options = {})
|
|
8
|
-
require "redis"
|
|
9
|
-
@prefix = options[:prefix] || "tina4:session:"
|
|
10
|
-
@ttl = options[:ttl] || 86400
|
|
11
|
-
@redis = Redis.new(
|
|
12
|
-
host: options[:host] || "localhost",
|
|
13
|
-
port: options[:port] || 6379,
|
|
14
|
-
db: options[:db] || 0,
|
|
15
|
-
password: options[:password]
|
|
16
|
-
)
|
|
17
|
-
rescue LoadError
|
|
18
|
-
raise "Redis session handler requires the 'redis' gem. Install with: gem install redis"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def read(session_id)
|
|
22
|
-
data = @redis.get("#{@prefix}#{session_id}")
|
|
23
|
-
return nil unless data
|
|
24
|
-
JSON.parse(data)
|
|
25
|
-
rescue JSON::ParserError
|
|
26
|
-
nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def write(session_id, data)
|
|
30
|
-
key = "#{@prefix}#{session_id}"
|
|
31
|
-
@redis.setex(key, @ttl, JSON.generate(data))
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def destroy(session_id)
|
|
35
|
-
@redis.del("#{@prefix}#{session_id}")
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def cleanup
|
|
39
|
-
# Redis handles TTL automatically
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module Tina4
|
|
5
|
+
module SessionHandlers
|
|
6
|
+
class RedisHandler
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
require "redis"
|
|
9
|
+
@prefix = options[:prefix] || "tina4:session:"
|
|
10
|
+
@ttl = options[:ttl] || 86400
|
|
11
|
+
@redis = Redis.new(
|
|
12
|
+
host: options[:host] || "localhost",
|
|
13
|
+
port: options[:port] || 6379,
|
|
14
|
+
db: options[:db] || 0,
|
|
15
|
+
password: options[:password]
|
|
16
|
+
)
|
|
17
|
+
rescue LoadError
|
|
18
|
+
raise "Redis session handler requires the 'redis' gem. Install with: gem install redis"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def read(session_id)
|
|
22
|
+
data = @redis.get("#{@prefix}#{session_id}")
|
|
23
|
+
return nil unless data
|
|
24
|
+
JSON.parse(data)
|
|
25
|
+
rescue JSON::ParserError
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def write(session_id, data)
|
|
30
|
+
key = "#{@prefix}#{session_id}"
|
|
31
|
+
@redis.setex(key, @ttl, JSON.generate(data))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def destroy(session_id)
|
|
35
|
+
@redis.del("#{@prefix}#{session_id}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cleanup
|
|
39
|
+
# Redis handles TTL automatically
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
|
|
4
|
-
module Tina4
|
|
5
|
-
module SessionHandlers
|
|
6
|
-
class ValkeyHandler
|
|
7
|
-
def initialize(options = {})
|
|
8
|
-
require "redis"
|
|
9
|
-
@prefix = options[:prefix] || ENV["TINA4_SESSION_VALKEY_PREFIX"] || "tina4:session:"
|
|
10
|
-
@ttl = options[:ttl] || (ENV["TINA4_SESSION_VALKEY_TTL"] ? ENV["TINA4_SESSION_VALKEY_TTL"].to_i : 86400)
|
|
11
|
-
@redis = Redis.new(
|
|
12
|
-
host: options[:host] || ENV["TINA4_SESSION_VALKEY_HOST"] || "localhost",
|
|
13
|
-
port: options[:port] || (ENV["TINA4_SESSION_VALKEY_PORT"] ? ENV["TINA4_SESSION_VALKEY_PORT"].to_i : 6379),
|
|
14
|
-
db: options[:db] || (ENV["TINA4_SESSION_VALKEY_DB"] ? ENV["TINA4_SESSION_VALKEY_DB"].to_i : 0),
|
|
15
|
-
password: options[:password] || ENV["TINA4_SESSION_VALKEY_PASSWORD"]
|
|
16
|
-
)
|
|
17
|
-
rescue LoadError
|
|
18
|
-
raise "Valkey session handler requires the 'redis' gem (Valkey uses the RESP protocol). Install with: gem install redis"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def read(session_id)
|
|
22
|
-
data = @redis.get("#{@prefix}#{session_id}")
|
|
23
|
-
return nil unless data
|
|
24
|
-
JSON.parse(data)
|
|
25
|
-
rescue JSON::ParserError
|
|
26
|
-
nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def write(session_id, data)
|
|
30
|
-
key = "#{@prefix}#{session_id}"
|
|
31
|
-
@redis.setex(key, @ttl, JSON.generate(data))
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def destroy(session_id)
|
|
35
|
-
@redis.del("#{@prefix}#{session_id}")
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def cleanup
|
|
39
|
-
# Valkey handles TTL automatically (same as Redis)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module Tina4
|
|
5
|
+
module SessionHandlers
|
|
6
|
+
class ValkeyHandler
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
require "redis"
|
|
9
|
+
@prefix = options[:prefix] || ENV["TINA4_SESSION_VALKEY_PREFIX"] || "tina4:session:"
|
|
10
|
+
@ttl = options[:ttl] || (ENV["TINA4_SESSION_VALKEY_TTL"] ? ENV["TINA4_SESSION_VALKEY_TTL"].to_i : 86400)
|
|
11
|
+
@redis = Redis.new(
|
|
12
|
+
host: options[:host] || ENV["TINA4_SESSION_VALKEY_HOST"] || "localhost",
|
|
13
|
+
port: options[:port] || (ENV["TINA4_SESSION_VALKEY_PORT"] ? ENV["TINA4_SESSION_VALKEY_PORT"].to_i : 6379),
|
|
14
|
+
db: options[:db] || (ENV["TINA4_SESSION_VALKEY_DB"] ? ENV["TINA4_SESSION_VALKEY_DB"].to_i : 0),
|
|
15
|
+
password: options[:password] || ENV["TINA4_SESSION_VALKEY_PASSWORD"]
|
|
16
|
+
)
|
|
17
|
+
rescue LoadError
|
|
18
|
+
raise "Valkey session handler requires the 'redis' gem (Valkey uses the RESP protocol). Install with: gem install redis"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def read(session_id)
|
|
22
|
+
data = @redis.get("#{@prefix}#{session_id}")
|
|
23
|
+
return nil unless data
|
|
24
|
+
JSON.parse(data)
|
|
25
|
+
rescue JSON::ParserError
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def write(session_id, data)
|
|
30
|
+
key = "#{@prefix}#{session_id}"
|
|
31
|
+
@redis.setex(key, @ttl, JSON.generate(data))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def destroy(session_id)
|
|
35
|
+
@redis.del("#{@prefix}#{session_id}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cleanup
|
|
39
|
+
# Valkey handles TTL automatically (same as Redis)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|