tina4ruby 0.5.2 → 3.2.1

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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/README.md +434 -544
  4. data/exe/{tina4 → tina4ruby} +1 -0
  5. data/lib/tina4/ai.rb +312 -0
  6. data/lib/tina4/auth.rb +44 -3
  7. data/lib/tina4/auto_crud.rb +163 -0
  8. data/lib/tina4/cli.rb +389 -97
  9. data/lib/tina4/constants.rb +46 -0
  10. data/lib/tina4/cors.rb +74 -0
  11. data/lib/tina4/database/sqlite3_adapter.rb +139 -0
  12. data/lib/tina4/database.rb +144 -7
  13. data/lib/tina4/debug.rb +4 -79
  14. data/lib/tina4/dev_admin.rb +1162 -0
  15. data/lib/tina4/dev_mailbox.rb +191 -0
  16. data/lib/tina4/dev_reload.rb +9 -9
  17. data/lib/tina4/drivers/firebird_driver.rb +19 -3
  18. data/lib/tina4/drivers/mssql_driver.rb +3 -3
  19. data/lib/tina4/drivers/mysql_driver.rb +4 -4
  20. data/lib/tina4/drivers/postgres_driver.rb +9 -2
  21. data/lib/tina4/drivers/sqlite_driver.rb +1 -1
  22. data/lib/tina4/env.rb +42 -2
  23. data/lib/tina4/error_overlay.rb +252 -0
  24. data/lib/tina4/events.rb +90 -0
  25. data/lib/tina4/field_types.rb +4 -0
  26. data/lib/tina4/frond.rb +1497 -0
  27. data/lib/tina4/gallery/auth/meta.json +1 -0
  28. data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -0
  29. data/lib/tina4/gallery/database/meta.json +1 -0
  30. data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -0
  31. data/lib/tina4/gallery/error-overlay/meta.json +1 -0
  32. data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -0
  33. data/lib/tina4/gallery/orm/meta.json +1 -0
  34. data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -0
  35. data/lib/tina4/gallery/queue/meta.json +1 -0
  36. data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +325 -0
  37. data/lib/tina4/gallery/rest-api/meta.json +1 -0
  38. data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -0
  39. data/lib/tina4/gallery/templates/meta.json +1 -0
  40. data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -0
  41. data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -0
  42. data/lib/tina4/health.rb +39 -0
  43. data/lib/tina4/html_element.rb +148 -0
  44. data/lib/tina4/localization.rb +2 -2
  45. data/lib/tina4/log.rb +203 -0
  46. data/lib/tina4/messenger.rb +562 -0
  47. data/lib/tina4/migration.rb +132 -29
  48. data/lib/tina4/orm.rb +463 -35
  49. data/lib/tina4/public/css/tina4.css +178 -1
  50. data/lib/tina4/public/css/tina4.min.css +1 -2
  51. data/lib/tina4/public/favicon.ico +0 -0
  52. data/lib/tina4/public/images/logo.svg +5 -0
  53. data/lib/tina4/public/images/tina4-logo-icon.webp +0 -0
  54. data/lib/tina4/public/js/frond.min.js +420 -0
  55. data/lib/tina4/public/js/tina4-dev-admin.min.js +367 -0
  56. data/lib/tina4/public/js/tina4.min.js +93 -0
  57. data/lib/tina4/public/swagger/index.html +90 -0
  58. data/lib/tina4/public/swagger/oauth2-redirect.html +63 -0
  59. data/lib/tina4/queue.rb +162 -6
  60. data/lib/tina4/queue_backends/lite_backend.rb +88 -0
  61. data/lib/tina4/rack_app.rb +331 -27
  62. data/lib/tina4/rate_limiter.rb +123 -0
  63. data/lib/tina4/request.rb +61 -15
  64. data/lib/tina4/response.rb +54 -24
  65. data/lib/tina4/response_cache.rb +551 -0
  66. data/lib/tina4/router.rb +90 -15
  67. data/lib/tina4/scss_compiler.rb +2 -2
  68. data/lib/tina4/seeder.rb +56 -61
  69. data/lib/tina4/service_runner.rb +303 -0
  70. data/lib/tina4/session.rb +85 -0
  71. data/lib/tina4/session_handlers/mongo_handler.rb +1 -1
  72. data/lib/tina4/session_handlers/valkey_handler.rb +43 -0
  73. data/lib/tina4/shutdown.rb +84 -0
  74. data/lib/tina4/sql_translation.rb +295 -0
  75. data/lib/tina4/template.rb +36 -6
  76. data/lib/tina4/templates/base.twig +2 -2
  77. data/lib/tina4/templates/errors/302.twig +14 -0
  78. data/lib/tina4/templates/errors/401.twig +9 -0
  79. data/lib/tina4/templates/errors/403.twig +22 -15
  80. data/lib/tina4/templates/errors/404.twig +22 -15
  81. data/lib/tina4/templates/errors/500.twig +31 -15
  82. data/lib/tina4/templates/errors/502.twig +9 -0
  83. data/lib/tina4/templates/errors/503.twig +12 -0
  84. data/lib/tina4/templates/errors/base.twig +37 -0
  85. data/lib/tina4/version.rb +1 -1
  86. data/lib/tina4/webserver.rb +28 -18
  87. data/lib/tina4.rb +118 -21
  88. metadata +68 -8
  89. data/lib/tina4/public/js/tina4.js +0 -134
  90. data/lib/tina4/public/js/tina4helper.js +0 -387
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Tina4
4
4
  class WebServer
5
- def initialize(app, host: "0.0.0.0", port: 7145)
5
+ def initialize(app, host: "0.0.0.0", port: 7147)
6
6
  @app = app
7
7
  @host = host
8
8
  @port = port
@@ -11,8 +11,8 @@ module Tina4
11
11
  def start
12
12
  require "webrick"
13
13
  require "stringio"
14
- Tina4.print_banner
15
- Tina4::Debug.info("Starting Tina4 WEBrick server on http://#{@host}:#{@port}")
14
+ Tina4.print_banner(host: @host, port: @port)
15
+ Tina4::Log.info("Starting Tina4 WEBrick server on http://#{@host}:#{@port}")
16
16
  @server = WEBrick::HTTPServer.new(
17
17
  BindAddress: @host,
18
18
  Port: @port,
@@ -20,6 +20,9 @@ module Tina4
20
20
  AccessLog: []
21
21
  )
22
22
 
23
+ # Setup graceful shutdown with WEBrick server reference
24
+ Tina4::Shutdown.setup(server: @server)
25
+
23
26
  # Use a custom servlet that passes ALL methods (including OPTIONS) to Rack
24
27
  rack_app = @app
25
28
  servlet = Class.new(WEBrick::HTTPServlet::AbstractServlet) do
@@ -35,21 +38,31 @@ module Tina4
35
38
  end
36
39
 
37
40
  define_method(:handle_request) do |webrick_req, webrick_res|
38
- env = build_rack_env(webrick_req)
39
- status, headers, body = @app.call(env)
41
+ # Reject new requests during shutdown
42
+ if Tina4::Shutdown.shutting_down?
43
+ webrick_res.status = 503
44
+ webrick_res.body = '{"error":"Service shutting down"}'
45
+ webrick_res["content-type"] = "application/json"
46
+ return
47
+ end
48
+
49
+ Tina4::Shutdown.track_request do
50
+ env = build_rack_env(webrick_req)
51
+ status, headers, body = @app.call(env)
40
52
 
41
- webrick_res.status = status
42
- headers.each do |key, value|
43
- if key.downcase == "set-cookie"
44
- Array(value.split("\n")).each { |c| webrick_res.cookies << WEBrick::Cookie.parse_set_cookie(c) }
45
- else
46
- webrick_res[key] = value
53
+ webrick_res.status = status
54
+ headers.each do |key, value|
55
+ if key.downcase == "set-cookie"
56
+ Array(value.split("\n")).each { |c| webrick_res.cookies << WEBrick::Cookie.parse_set_cookie(c) }
57
+ else
58
+ webrick_res[key] = value
59
+ end
47
60
  end
48
- end
49
61
 
50
- response_body = ""
51
- body.each { |chunk| response_body += chunk }
52
- webrick_res.body = response_body
62
+ response_body = ""
63
+ body.each { |chunk| response_body += chunk }
64
+ webrick_res.body = response_body
65
+ end
53
66
  end
54
67
 
55
68
  define_method(:build_rack_env) do |req|
@@ -88,9 +101,6 @@ module Tina4
88
101
  servlet.define_method(:webrick_req_port) { port }
89
102
 
90
103
  @server.mount("/", servlet, rack_app)
91
-
92
- trap("INT") { @server.shutdown }
93
- trap("TERM") { @server.shutdown }
94
104
  @server.start
95
105
  end
96
106
 
data/lib/tina4.rb CHANGED
@@ -10,7 +10,9 @@ end
10
10
 
11
11
  # ── Core (always loaded) ──────────────────────────────────────────────
12
12
  require_relative "tina4/version"
13
- require_relative "tina4/debug"
13
+ require_relative "tina4/constants"
14
+ require_relative "tina4/log"
15
+ require_relative "tina4/debug" # backward compat alias
14
16
  require_relative "tina4/env"
15
17
  require_relative "tina4/router"
16
18
  require_relative "tina4/request"
@@ -21,13 +23,30 @@ require_relative "tina4/database_result"
21
23
  require_relative "tina4/field_types"
22
24
  require_relative "tina4/orm"
23
25
  require_relative "tina4/migration"
26
+ require_relative "tina4/auto_crud"
27
+ require_relative "tina4/database/sqlite3_adapter"
24
28
  require_relative "tina4/template"
29
+ require_relative "tina4/frond"
25
30
  require_relative "tina4/auth"
26
31
  require_relative "tina4/session"
27
32
  require_relative "tina4/middleware"
33
+ require_relative "tina4/cors"
34
+ require_relative "tina4/rate_limiter"
35
+ require_relative "tina4/health"
36
+ require_relative "tina4/shutdown"
28
37
  require_relative "tina4/localization"
29
38
  require_relative "tina4/container"
30
39
  require_relative "tina4/queue"
40
+ require_relative "tina4/service_runner"
41
+ require_relative "tina4/events"
42
+ require_relative "tina4/dev_admin"
43
+ require_relative "tina4/messenger"
44
+ require_relative "tina4/dev_mailbox"
45
+ require_relative "tina4/ai"
46
+ require_relative "tina4/sql_translation"
47
+ require_relative "tina4/response_cache"
48
+ require_relative "tina4/html_element"
49
+ require_relative "tina4/error_overlay"
31
50
 
32
51
  module Tina4
33
52
  # ── Lazy-loaded: database drivers ─────────────────────────────────────
@@ -43,7 +62,8 @@ module Tina4
43
62
  module SessionHandlers
44
63
  autoload :FileHandler, File.expand_path("tina4/session_handlers/file_handler", __dir__)
45
64
  autoload :RedisHandler, File.expand_path("tina4/session_handlers/redis_handler", __dir__)
46
- autoload :MongoHandler, File.expand_path("tina4/session_handlers/mongo_handler", __dir__)
65
+ autoload :MongoHandler, File.expand_path("tina4/session_handlers/mongo_handler", __dir__)
66
+ autoload :ValkeyHandler, File.expand_path("tina4/session_handlers/valkey_handler", __dir__)
47
67
  end
48
68
 
49
69
  # ── Lazy-loaded: queue backends ───────────────────────────────────────
@@ -73,27 +93,52 @@ module Tina4
73
93
  autoload :ScssCompiler, File.expand_path("tina4/scss_compiler", __dir__)
74
94
  autoload :DevReload, File.expand_path("tina4/dev_reload", __dir__)
75
95
  autoload :FakeData, File.expand_path("tina4/seeder", __dir__)
76
- autoload :Seeder, File.expand_path("tina4/seeder", __dir__)
77
96
  autoload :WSDL, File.expand_path("tina4/wsdl", __dir__)
78
97
  BANNER = <<~'BANNER'
79
98
 
80
- ████████╗██╗███╗ ██╗ █████╗ ██╗ ██╗
81
- ╚══██╔══╝██║████╗ ██║██╔══██╗██║ ██║
82
- ██║ ██║██╔██╗ ██║███████║███████║
83
- ██║ ██║██║╚██╗██║██╔══██║╚════██║
84
- ██║ ██║██║ ╚████║██║ ██║ ██║
85
- ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝
99
+ ______ _ __ __
100
+ /_ __/(_)___ ____ _/ // /
101
+ / / / / __ \/ __ `/ // /_
102
+ / / / / / / / /_/ /__ __/
103
+ /_/ /_/_/ /_/\__,_/ /_/
86
104
  BANNER
87
105
 
88
106
  class << self
89
107
  attr_accessor :root_dir, :database
90
108
 
91
- def print_banner
92
- puts "\e[35m#{BANNER}\e[0m"
93
- puts " \e[35mTina4 Ruby v#{VERSION} - This is not a framework\e[0m"
109
+ def print_banner(host: "0.0.0.0", port: 7147, server_name: nil)
110
+ is_tty = $stdout.respond_to?(:isatty) && $stdout.isatty
111
+ color = is_tty ? "\e[31m" : ""
112
+ reset = is_tty ? "\e[0m" : ""
113
+
114
+ is_debug = Tina4::Env.truthy?(ENV["TINA4_DEBUG"])
115
+ log_level = (ENV["TINA4_LOG_LEVEL"] || "[TINA4_LOG_ALL]").upcase
116
+ display = (host == "0.0.0.0" || host == "::") ? "localhost" : host
117
+
118
+ # Auto-detect server name if not provided
119
+ if server_name.nil?
120
+ if is_debug
121
+ server_name = "WEBrick"
122
+ else
123
+ begin
124
+ require "puma"
125
+ server_name = "puma"
126
+ rescue LoadError
127
+ server_name = "WEBrick"
128
+ end
129
+ end
130
+ end
131
+
132
+ puts "#{color}#{BANNER}#{reset}"
133
+ puts " Tina4 Ruby v#{VERSION} — This is not a framework"
134
+ puts ""
135
+ puts " Server: http://#{display}:#{port} (#{server_name})"
136
+ puts " Swagger: http://localhost:#{port}/swagger"
137
+ puts " Dashboard: http://localhost:#{port}/__dev"
138
+ puts " Debug: #{is_debug ? 'ON' : 'OFF'} (Log level: #{log_level})"
94
139
  puts ""
95
140
  rescue
96
- puts "\e[35mTINA4 Ruby v#{VERSION}\e[0m"
141
+ puts "#{color}TINA4 Ruby v#{VERSION}#{reset}"
97
142
  end
98
143
 
99
144
  def initialize!(root_dir = Dir.pwd)
@@ -103,11 +148,11 @@ module Tina4
103
148
  print_banner
104
149
 
105
150
  # Load environment
106
- Tina4::Env.load(root_dir)
151
+ Tina4::Env.load_env(root_dir)
107
152
 
108
153
  # Setup debug logging
109
- Tina4::Debug.setup(root_dir)
110
- Tina4::Debug.info("Tina4 Ruby v#{VERSION} initializing...")
154
+ Tina4::Log.setup(root_dir)
155
+ Tina4::Log.info("Tina4 Ruby v#{VERSION} initializing...")
111
156
 
112
157
  # Setup auth keys
113
158
  Tina4::Auth.setup(root_dir)
@@ -121,7 +166,54 @@ module Tina4
121
166
  # Auto-discover routes
122
167
  auto_discover(root_dir)
123
168
 
124
- Tina4::Debug.info("Tina4 initialized successfully")
169
+ Tina4::Log.info("Tina4 initialized successfully")
170
+ end
171
+
172
+ # Initialize and start the web server.
173
+ # This is the primary entry point for app.rb files:
174
+ # Tina4.initialize!(__dir__)
175
+ # Tina4.run!
176
+ # Or combined: Tina4.run!(__dir__)
177
+ def run!(root_dir = Dir.pwd)
178
+ initialize!(root_dir) unless @root_dir
179
+
180
+ host = ENV.fetch("HOST", ENV.fetch("TINA4_HOST", "0.0.0.0"))
181
+ port = ENV.fetch("PORT", ENV.fetch("TINA4_PORT", "7147")).to_i
182
+
183
+ app = Tina4::RackApp.new(root_dir: root_dir)
184
+ is_debug = Tina4::Env.truthy?(ENV["TINA4_DEBUG"])
185
+
186
+ # Try Puma first (production-grade), fall back to WEBrick
187
+ if !is_debug
188
+ begin
189
+ require "puma"
190
+ require "puma/configuration"
191
+ require "puma/launcher"
192
+
193
+ config = Puma::Configuration.new do |user_config|
194
+ user_config.bind "tcp://#{host}:#{port}"
195
+ user_config.app app
196
+ user_config.threads 0, 16
197
+ user_config.workers 0
198
+ user_config.environment "production"
199
+ user_config.log_requests false
200
+ user_config.quiet
201
+ end
202
+
203
+ Tina4::Log.info("Production server: puma")
204
+ Tina4::Shutdown.setup
205
+
206
+ launcher = Puma::Launcher.new(config)
207
+ launcher.run
208
+ return
209
+ rescue LoadError
210
+ # Puma not installed, fall through to WEBrick
211
+ end
212
+ end
213
+
214
+ Tina4::Log.info("Development server: WEBrick")
215
+ server = Tina4::WebServer.new(app, host: host, port: port)
216
+ server.start
125
217
  end
126
218
 
127
219
  # DSL methods for route registration
@@ -218,6 +310,11 @@ module Tina4
218
310
  Tina4::Localization.t(key, **options)
219
311
  end
220
312
 
313
+ # Service runner DSL
314
+ def service(name, options = {}, &block)
315
+ Tina4::ServiceRunner.register(name, nil, options, &block)
316
+ end
317
+
221
318
  # DI container shortcuts
222
319
  def register(name, instance = nil, &block)
223
320
  Tina4::Container.register(name, instance, &block)
@@ -250,9 +347,9 @@ module Tina4
250
347
  if db_url && !db_url.empty?
251
348
  begin
252
349
  @database = Tina4::Database.new(db_url)
253
- Tina4::Debug.info("Database connected: #{db_url.sub(/:[^:@]+@/, ':***@')}")
350
+ Tina4::Log.info("Database connected: #{db_url.sub(/:[^:@]+@/, ':***@')}")
254
351
  rescue => e
255
- Tina4::Debug.error("Database connection failed: #{e.message}")
352
+ Tina4::Log.error("Database connection failed: #{e.message}")
256
353
  end
257
354
  end
258
355
  end
@@ -266,9 +363,9 @@ module Tina4
266
363
  Dir.glob(File.join(full_dir, "**/*.rb")).sort.each do |file|
267
364
  begin
268
365
  load file
269
- Tina4::Debug.debug("Auto-loaded: #{file}")
366
+ Tina4::Log.debug("Auto-loaded: #{file}")
270
367
  rescue => e
271
- Tina4::Debug.error("Failed to load #{file}: #{e.message}")
368
+ Tina4::Log.error("Failed to load #{file}: #{e.message}")
272
369
  end
273
370
  end
274
371
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team
@@ -94,19 +94,33 @@ dependencies:
94
94
  - !ruby/object:Gem::Version
95
95
  version: '3.1'
96
96
  - !ruby/object:Gem::Dependency
97
- name: thor
97
+ name: net-smtp
98
98
  requirement: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '1.3'
102
+ version: '0.5'
103
103
  type: :runtime
104
104
  prerelease: false
105
105
  version_requirements: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '1.3'
109
+ version: '0.5'
110
+ - !ruby/object:Gem::Dependency
111
+ name: net-imap
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.5'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.5'
110
124
  - !ruby/object:Gem::Dependency
111
125
  name: json
112
126
  requirement: !ruby/object:Gem::Requirement
@@ -237,24 +251,31 @@ description: A lightweight, zero-configuration, Windows-friendly Ruby web framew
237
251
  email:
238
252
  - info@tina4.com
239
253
  executables:
240
- - tina4
254
+ - tina4ruby
241
255
  extensions: []
242
256
  extra_rdoc_files: []
243
257
  files:
244
258
  - CHANGELOG.md
245
259
  - LICENSE.txt
246
260
  - README.md
247
- - exe/tina4
261
+ - exe/tina4ruby
248
262
  - lib/tina4.rb
263
+ - lib/tina4/ai.rb
249
264
  - lib/tina4/api.rb
250
265
  - lib/tina4/auth.rb
266
+ - lib/tina4/auto_crud.rb
251
267
  - lib/tina4/cli.rb
268
+ - lib/tina4/constants.rb
252
269
  - lib/tina4/container.rb
270
+ - lib/tina4/cors.rb
253
271
  - lib/tina4/crud.rb
254
272
  - lib/tina4/database.rb
273
+ - lib/tina4/database/sqlite3_adapter.rb
255
274
  - lib/tina4/database_result.rb
256
275
  - lib/tina4/debug.rb
257
276
  - lib/tina4/dev.rb
277
+ - lib/tina4/dev_admin.rb
278
+ - lib/tina4/dev_mailbox.rb
258
279
  - lib/tina4/dev_reload.rb
259
280
  - lib/tina4/drivers/firebird_driver.rb
260
281
  - lib/tina4/drivers/mssql_driver.rb
@@ -262,23 +283,53 @@ files:
262
283
  - lib/tina4/drivers/postgres_driver.rb
263
284
  - lib/tina4/drivers/sqlite_driver.rb
264
285
  - lib/tina4/env.rb
286
+ - lib/tina4/error_overlay.rb
287
+ - lib/tina4/events.rb
265
288
  - lib/tina4/field_types.rb
289
+ - lib/tina4/frond.rb
290
+ - lib/tina4/gallery/auth/meta.json
291
+ - lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb
292
+ - lib/tina4/gallery/database/meta.json
293
+ - lib/tina4/gallery/database/src/routes/api/gallery_db.rb
294
+ - lib/tina4/gallery/error-overlay/meta.json
295
+ - lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb
296
+ - lib/tina4/gallery/orm/meta.json
297
+ - lib/tina4/gallery/orm/src/routes/api/gallery_products.rb
298
+ - lib/tina4/gallery/queue/meta.json
299
+ - lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb
300
+ - lib/tina4/gallery/rest-api/meta.json
301
+ - lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb
302
+ - lib/tina4/gallery/templates/meta.json
303
+ - lib/tina4/gallery/templates/src/routes/gallery_page.rb
304
+ - lib/tina4/gallery/templates/src/templates/gallery_page.twig
266
305
  - lib/tina4/graphql.rb
306
+ - lib/tina4/health.rb
307
+ - lib/tina4/html_element.rb
267
308
  - lib/tina4/localization.rb
309
+ - lib/tina4/log.rb
310
+ - lib/tina4/messenger.rb
268
311
  - lib/tina4/middleware.rb
269
312
  - lib/tina4/migration.rb
270
313
  - lib/tina4/orm.rb
271
314
  - lib/tina4/public/css/tina4.css
272
315
  - lib/tina4/public/css/tina4.min.css
273
- - lib/tina4/public/js/tina4.js
274
- - lib/tina4/public/js/tina4helper.js
316
+ - lib/tina4/public/favicon.ico
317
+ - lib/tina4/public/images/logo.svg
318
+ - lib/tina4/public/images/tina4-logo-icon.webp
319
+ - lib/tina4/public/js/frond.min.js
320
+ - lib/tina4/public/js/tina4-dev-admin.min.js
321
+ - lib/tina4/public/js/tina4.min.js
322
+ - lib/tina4/public/swagger/index.html
323
+ - lib/tina4/public/swagger/oauth2-redirect.html
275
324
  - lib/tina4/queue.rb
276
325
  - lib/tina4/queue_backends/kafka_backend.rb
277
326
  - lib/tina4/queue_backends/lite_backend.rb
278
327
  - lib/tina4/queue_backends/rabbitmq_backend.rb
279
328
  - lib/tina4/rack_app.rb
329
+ - lib/tina4/rate_limiter.rb
280
330
  - lib/tina4/request.rb
281
331
  - lib/tina4/response.rb
332
+ - lib/tina4/response_cache.rb
282
333
  - lib/tina4/router.rb
283
334
  - lib/tina4/scss/tina4css/_alerts.scss
284
335
  - lib/tina4/scss/tina4css/_badges.scss
@@ -298,16 +349,25 @@ files:
298
349
  - lib/tina4/scss/tina4css/tina4.scss
299
350
  - lib/tina4/scss_compiler.rb
300
351
  - lib/tina4/seeder.rb
352
+ - lib/tina4/service_runner.rb
301
353
  - lib/tina4/session.rb
302
354
  - lib/tina4/session_handlers/file_handler.rb
303
355
  - lib/tina4/session_handlers/mongo_handler.rb
304
356
  - lib/tina4/session_handlers/redis_handler.rb
357
+ - lib/tina4/session_handlers/valkey_handler.rb
358
+ - lib/tina4/shutdown.rb
359
+ - lib/tina4/sql_translation.rb
305
360
  - lib/tina4/swagger.rb
306
361
  - lib/tina4/template.rb
307
362
  - lib/tina4/templates/base.twig
363
+ - lib/tina4/templates/errors/302.twig
364
+ - lib/tina4/templates/errors/401.twig
308
365
  - lib/tina4/templates/errors/403.twig
309
366
  - lib/tina4/templates/errors/404.twig
310
367
  - lib/tina4/templates/errors/500.twig
368
+ - lib/tina4/templates/errors/502.twig
369
+ - lib/tina4/templates/errors/503.twig
370
+ - lib/tina4/templates/errors/base.twig
311
371
  - lib/tina4/testing.rb
312
372
  - lib/tina4/version.rb
313
373
  - lib/tina4/webserver.rb
@@ -1,134 +0,0 @@
1
- /**
2
- * Tina4 CSS — Lightweight JS components
3
- * Replaces Bootstrap's JavaScript for: modals, alerts, navbar toggler
4
- * ~3KB unminified, zero dependencies
5
- */
6
- (function () {
7
- "use strict";
8
-
9
- // ── Modals ──────────────────────────────────────────────────
10
- // Usage: <button data-t4-toggle="modal" data-t4-target="#myModal">Open</button>
11
- // <button data-t4-dismiss="modal">Close</button>
12
- // Also supports Bootstrap syntax: data-bs-toggle, data-bs-target, data-bs-dismiss
13
-
14
- function getModalEl(selector) {
15
- if (!selector) return null;
16
- return document.querySelector(selector);
17
- }
18
-
19
- function openModal(modal) {
20
- if (!modal) return;
21
- // Create backdrop if not exists
22
- var backdrop = modal._t4Backdrop;
23
- if (!backdrop) {
24
- backdrop = document.createElement("div");
25
- backdrop.className = "modal-backdrop";
26
- document.body.appendChild(backdrop);
27
- modal._t4Backdrop = backdrop;
28
- backdrop.addEventListener("click", function () {
29
- closeModal(modal);
30
- });
31
- }
32
- modal.style.display = "block";
33
- backdrop.style.display = "block";
34
- // Force reflow then add .show for transition
35
- void modal.offsetHeight;
36
- modal.classList.add("show");
37
- backdrop.classList.add("show");
38
- document.body.style.overflow = "hidden";
39
- // Focus trap — focus first focusable element
40
- var focusable = modal.querySelector("input, select, textarea, button, [tabindex]");
41
- if (focusable) focusable.focus();
42
- }
43
-
44
- function closeModal(modal) {
45
- if (!modal) return;
46
- modal.classList.remove("show");
47
- var backdrop = modal._t4Backdrop;
48
- if (backdrop) backdrop.classList.remove("show");
49
- // Wait for transition
50
- setTimeout(function () {
51
- modal.style.display = "none";
52
- if (backdrop) backdrop.style.display = "none";
53
- document.body.style.overflow = "";
54
- }, 150);
55
- }
56
-
57
- // Delegated click handler for modal triggers
58
- document.addEventListener("click", function (e) {
59
- var trigger = e.target.closest("[data-t4-toggle='modal'], [data-bs-toggle='modal']");
60
- if (trigger) {
61
- e.preventDefault();
62
- var target = trigger.getAttribute("data-t4-target") || trigger.getAttribute("data-bs-target") || trigger.getAttribute("href");
63
- var modal = getModalEl(target);
64
- if (modal) openModal(modal);
65
- return;
66
- }
67
-
68
- // Dismiss button
69
- var dismiss = e.target.closest("[data-t4-dismiss='modal'], [data-bs-dismiss='modal'], .btn-close");
70
- if (dismiss) {
71
- var modal = dismiss.closest(".modal");
72
- if (modal) closeModal(modal);
73
- return;
74
- }
75
- });
76
-
77
- // ESC key closes top modal
78
- document.addEventListener("keydown", function (e) {
79
- if (e.key === "Escape") {
80
- var modals = document.querySelectorAll(".modal.show");
81
- if (modals.length > 0) closeModal(modals[modals.length - 1]);
82
- }
83
- });
84
-
85
- // ── Alerts (dismissible) ────────────────────────────────────
86
- // Usage: <div class="alert alert-danger alert-dismissible">
87
- // Message <button class="btn-close" data-t4-dismiss="alert">&times;</button>
88
- // </div>
89
-
90
- document.addEventListener("click", function (e) {
91
- var dismiss = e.target.closest("[data-t4-dismiss='alert'], [data-bs-dismiss='alert']");
92
- if (dismiss) {
93
- var alert = dismiss.closest(".alert");
94
- if (alert) {
95
- alert.style.opacity = "0";
96
- setTimeout(function () { alert.remove(); }, 150);
97
- }
98
- }
99
- });
100
-
101
- // ── Navbar toggler ──────────────────────────────────────────
102
- // Usage: <button class="navbar-toggler" data-t4-toggle="collapse" data-t4-target="#navContent">
103
- // &#9776;
104
- // </button>
105
- // <div class="navbar-collapse collapse" id="navContent">...</div>
106
-
107
- document.addEventListener("click", function (e) {
108
- var toggler = e.target.closest("[data-t4-toggle='collapse'], [data-bs-toggle='collapse']");
109
- if (toggler) {
110
- e.preventDefault();
111
- var target = toggler.getAttribute("data-t4-target") || toggler.getAttribute("data-bs-target") || toggler.getAttribute("href");
112
- var el = document.querySelector(target);
113
- if (el) {
114
- el.classList.toggle("show");
115
- }
116
- }
117
- });
118
-
119
- // ── Programmatic API ────────────────────────────────────────
120
- // window.tina4.modal.open("#myModal")
121
- // window.tina4.modal.close("#myModal")
122
-
123
- window.tina4 = window.tina4 || {};
124
- window.tina4.modal = {
125
- open: function (selector) {
126
- var modal = typeof selector === "string" ? document.querySelector(selector) : selector;
127
- openModal(modal);
128
- },
129
- close: function (selector) {
130
- var modal = typeof selector === "string" ? document.querySelector(selector) : selector;
131
- closeModal(modal);
132
- }
133
- };
134
- })();