tina4ruby 0.5.2 → 3.0.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 +4 -4
- data/CHANGELOG.md +1 -1
- data/README.md +360 -559
- data/exe/{tina4 → tina4ruby} +1 -0
- data/lib/tina4/ai.rb +312 -0
- data/lib/tina4/auth.rb +44 -3
- data/lib/tina4/auto_crud.rb +163 -0
- data/lib/tina4/cli.rb +242 -77
- data/lib/tina4/constants.rb +46 -0
- data/lib/tina4/cors.rb +74 -0
- data/lib/tina4/database/sqlite3_adapter.rb +139 -0
- data/lib/tina4/database.rb +43 -7
- data/lib/tina4/debug.rb +4 -79
- data/lib/tina4/dev_admin.rb +1162 -0
- data/lib/tina4/dev_mailbox.rb +191 -0
- data/lib/tina4/dev_reload.rb +9 -9
- data/lib/tina4/drivers/firebird_driver.rb +19 -3
- data/lib/tina4/drivers/mssql_driver.rb +3 -3
- data/lib/tina4/drivers/mysql_driver.rb +4 -4
- data/lib/tina4/drivers/postgres_driver.rb +9 -2
- data/lib/tina4/drivers/sqlite_driver.rb +1 -1
- data/lib/tina4/env.rb +42 -2
- data/lib/tina4/error_overlay.rb +252 -0
- data/lib/tina4/events.rb +90 -0
- data/lib/tina4/field_types.rb +4 -0
- data/lib/tina4/frond.rb +1336 -0
- data/lib/tina4/gallery/auth/meta.json +1 -0
- data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -0
- data/lib/tina4/gallery/database/meta.json +1 -0
- data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -0
- data/lib/tina4/gallery/error-overlay/meta.json +1 -0
- data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -0
- data/lib/tina4/gallery/orm/meta.json +1 -0
- data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -0
- data/lib/tina4/gallery/queue/meta.json +1 -0
- data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +27 -0
- data/lib/tina4/gallery/rest-api/meta.json +1 -0
- data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -0
- data/lib/tina4/gallery/templates/meta.json +1 -0
- data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -0
- data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -0
- data/lib/tina4/health.rb +39 -0
- data/lib/tina4/html_element.rb +148 -0
- data/lib/tina4/localization.rb +2 -2
- data/lib/tina4/log.rb +203 -0
- data/lib/tina4/messenger.rb +484 -0
- data/lib/tina4/migration.rb +132 -29
- data/lib/tina4/orm.rb +337 -31
- data/lib/tina4/public/css/tina4.css +178 -1
- data/lib/tina4/public/css/tina4.min.css +1 -2
- data/lib/tina4/public/favicon.ico +0 -0
- data/lib/tina4/public/images/logo.svg +5 -0
- data/lib/tina4/public/images/tina4-logo-icon.webp +0 -0
- data/lib/tina4/public/js/frond.min.js +420 -0
- data/lib/tina4/public/js/tina4-dev-admin.min.js +367 -0
- data/lib/tina4/public/js/tina4.min.js +93 -0
- data/lib/tina4/public/swagger/index.html +90 -0
- data/lib/tina4/public/swagger/oauth2-redirect.html +63 -0
- data/lib/tina4/queue.rb +40 -4
- data/lib/tina4/queue_backends/lite_backend.rb +88 -0
- data/lib/tina4/rack_app.rb +314 -23
- data/lib/tina4/rate_limiter.rb +123 -0
- data/lib/tina4/request.rb +61 -15
- data/lib/tina4/response.rb +54 -24
- data/lib/tina4/response_cache.rb +134 -0
- data/lib/tina4/router.rb +90 -15
- data/lib/tina4/scss_compiler.rb +2 -2
- data/lib/tina4/seeder.rb +56 -61
- data/lib/tina4/service_runner.rb +303 -0
- data/lib/tina4/session.rb +85 -0
- data/lib/tina4/session_handlers/mongo_handler.rb +1 -1
- data/lib/tina4/session_handlers/valkey_handler.rb +43 -0
- data/lib/tina4/shutdown.rb +84 -0
- data/lib/tina4/sql_translation.rb +295 -0
- data/lib/tina4/template.rb +36 -6
- data/lib/tina4/templates/base.twig +2 -2
- data/lib/tina4/templates/errors/302.twig +14 -0
- data/lib/tina4/templates/errors/401.twig +9 -0
- data/lib/tina4/templates/errors/403.twig +22 -15
- data/lib/tina4/templates/errors/404.twig +22 -15
- data/lib/tina4/templates/errors/500.twig +31 -15
- data/lib/tina4/templates/errors/502.twig +9 -0
- data/lib/tina4/templates/errors/503.twig +12 -0
- data/lib/tina4/templates/errors/base.twig +37 -0
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4/webserver.rb +28 -18
- data/lib/tina4.rb +57 -21
- metadata +51 -19
- data/lib/tina4/public/js/tina4.js +0 -134
- data/lib/tina4/public/js/tina4helper.js +0 -387
data/lib/tina4/webserver.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Tina4
|
|
4
4
|
class WebServer
|
|
5
|
-
def initialize(app, host: "0.0.0.0", port:
|
|
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::
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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/
|
|
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,
|
|
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,38 @@ 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
|
-
|
|
93
|
-
|
|
109
|
+
def print_banner(host: "0.0.0.0", port: 7147)
|
|
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
|
+
puts "#{color}#{BANNER}#{reset}"
|
|
119
|
+
puts " Tina4 Ruby v#{VERSION} — This is not a framework"
|
|
120
|
+
puts ""
|
|
121
|
+
puts " Server: http://#{display}:#{port}"
|
|
122
|
+
puts " Swagger: http://localhost:#{port}/swagger"
|
|
123
|
+
puts " Dashboard: http://localhost:#{port}/__dev"
|
|
124
|
+
puts " Debug: #{is_debug ? 'ON' : 'OFF'} (Log level: #{log_level})"
|
|
94
125
|
puts ""
|
|
95
126
|
rescue
|
|
96
|
-
puts "
|
|
127
|
+
puts "#{color}TINA4 Ruby v#{VERSION}#{reset}"
|
|
97
128
|
end
|
|
98
129
|
|
|
99
130
|
def initialize!(root_dir = Dir.pwd)
|
|
@@ -103,11 +134,11 @@ module Tina4
|
|
|
103
134
|
print_banner
|
|
104
135
|
|
|
105
136
|
# Load environment
|
|
106
|
-
Tina4::Env.
|
|
137
|
+
Tina4::Env.load_env(root_dir)
|
|
107
138
|
|
|
108
139
|
# Setup debug logging
|
|
109
|
-
Tina4::
|
|
110
|
-
Tina4::
|
|
140
|
+
Tina4::Log.setup(root_dir)
|
|
141
|
+
Tina4::Log.info("Tina4 Ruby v#{VERSION} initializing...")
|
|
111
142
|
|
|
112
143
|
# Setup auth keys
|
|
113
144
|
Tina4::Auth.setup(root_dir)
|
|
@@ -121,7 +152,7 @@ module Tina4
|
|
|
121
152
|
# Auto-discover routes
|
|
122
153
|
auto_discover(root_dir)
|
|
123
154
|
|
|
124
|
-
Tina4::
|
|
155
|
+
Tina4::Log.info("Tina4 initialized successfully")
|
|
125
156
|
end
|
|
126
157
|
|
|
127
158
|
# DSL methods for route registration
|
|
@@ -218,6 +249,11 @@ module Tina4
|
|
|
218
249
|
Tina4::Localization.t(key, **options)
|
|
219
250
|
end
|
|
220
251
|
|
|
252
|
+
# Service runner DSL
|
|
253
|
+
def service(name, options = {}, &block)
|
|
254
|
+
Tina4::ServiceRunner.register(name, nil, options, &block)
|
|
255
|
+
end
|
|
256
|
+
|
|
221
257
|
# DI container shortcuts
|
|
222
258
|
def register(name, instance = nil, &block)
|
|
223
259
|
Tina4::Container.register(name, instance, &block)
|
|
@@ -250,9 +286,9 @@ module Tina4
|
|
|
250
286
|
if db_url && !db_url.empty?
|
|
251
287
|
begin
|
|
252
288
|
@database = Tina4::Database.new(db_url)
|
|
253
|
-
Tina4::
|
|
289
|
+
Tina4::Log.info("Database connected: #{db_url.sub(/:[^:@]+@/, ':***@')}")
|
|
254
290
|
rescue => e
|
|
255
|
-
Tina4::
|
|
291
|
+
Tina4::Log.error("Database connection failed: #{e.message}")
|
|
256
292
|
end
|
|
257
293
|
end
|
|
258
294
|
end
|
|
@@ -266,9 +302,9 @@ module Tina4
|
|
|
266
302
|
Dir.glob(File.join(full_dir, "**/*.rb")).sort.each do |file|
|
|
267
303
|
begin
|
|
268
304
|
load file
|
|
269
|
-
Tina4::
|
|
305
|
+
Tina4::Log.debug("Auto-loaded: #{file}")
|
|
270
306
|
rescue => e
|
|
271
|
-
Tina4::
|
|
307
|
+
Tina4::Log.error("Failed to load #{file}: #{e.message}")
|
|
272
308
|
end
|
|
273
309
|
end
|
|
274
310
|
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.
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tina4 Team
|
|
@@ -93,20 +93,6 @@ dependencies:
|
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '3.1'
|
|
96
|
-
- !ruby/object:Gem::Dependency
|
|
97
|
-
name: thor
|
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
|
99
|
-
requirements:
|
|
100
|
-
- - "~>"
|
|
101
|
-
- !ruby/object:Gem::Version
|
|
102
|
-
version: '1.3'
|
|
103
|
-
type: :runtime
|
|
104
|
-
prerelease: false
|
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
-
requirements:
|
|
107
|
-
- - "~>"
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
version: '1.3'
|
|
110
96
|
- !ruby/object:Gem::Dependency
|
|
111
97
|
name: json
|
|
112
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -237,24 +223,31 @@ description: A lightweight, zero-configuration, Windows-friendly Ruby web framew
|
|
|
237
223
|
email:
|
|
238
224
|
- info@tina4.com
|
|
239
225
|
executables:
|
|
240
|
-
-
|
|
226
|
+
- tina4ruby
|
|
241
227
|
extensions: []
|
|
242
228
|
extra_rdoc_files: []
|
|
243
229
|
files:
|
|
244
230
|
- CHANGELOG.md
|
|
245
231
|
- LICENSE.txt
|
|
246
232
|
- README.md
|
|
247
|
-
- exe/
|
|
233
|
+
- exe/tina4ruby
|
|
248
234
|
- lib/tina4.rb
|
|
235
|
+
- lib/tina4/ai.rb
|
|
249
236
|
- lib/tina4/api.rb
|
|
250
237
|
- lib/tina4/auth.rb
|
|
238
|
+
- lib/tina4/auto_crud.rb
|
|
251
239
|
- lib/tina4/cli.rb
|
|
240
|
+
- lib/tina4/constants.rb
|
|
252
241
|
- lib/tina4/container.rb
|
|
242
|
+
- lib/tina4/cors.rb
|
|
253
243
|
- lib/tina4/crud.rb
|
|
254
244
|
- lib/tina4/database.rb
|
|
245
|
+
- lib/tina4/database/sqlite3_adapter.rb
|
|
255
246
|
- lib/tina4/database_result.rb
|
|
256
247
|
- lib/tina4/debug.rb
|
|
257
248
|
- lib/tina4/dev.rb
|
|
249
|
+
- lib/tina4/dev_admin.rb
|
|
250
|
+
- lib/tina4/dev_mailbox.rb
|
|
258
251
|
- lib/tina4/dev_reload.rb
|
|
259
252
|
- lib/tina4/drivers/firebird_driver.rb
|
|
260
253
|
- lib/tina4/drivers/mssql_driver.rb
|
|
@@ -262,23 +255,53 @@ files:
|
|
|
262
255
|
- lib/tina4/drivers/postgres_driver.rb
|
|
263
256
|
- lib/tina4/drivers/sqlite_driver.rb
|
|
264
257
|
- lib/tina4/env.rb
|
|
258
|
+
- lib/tina4/error_overlay.rb
|
|
259
|
+
- lib/tina4/events.rb
|
|
265
260
|
- lib/tina4/field_types.rb
|
|
261
|
+
- lib/tina4/frond.rb
|
|
262
|
+
- lib/tina4/gallery/auth/meta.json
|
|
263
|
+
- lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb
|
|
264
|
+
- lib/tina4/gallery/database/meta.json
|
|
265
|
+
- lib/tina4/gallery/database/src/routes/api/gallery_db.rb
|
|
266
|
+
- lib/tina4/gallery/error-overlay/meta.json
|
|
267
|
+
- lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb
|
|
268
|
+
- lib/tina4/gallery/orm/meta.json
|
|
269
|
+
- lib/tina4/gallery/orm/src/routes/api/gallery_products.rb
|
|
270
|
+
- lib/tina4/gallery/queue/meta.json
|
|
271
|
+
- lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb
|
|
272
|
+
- lib/tina4/gallery/rest-api/meta.json
|
|
273
|
+
- lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb
|
|
274
|
+
- lib/tina4/gallery/templates/meta.json
|
|
275
|
+
- lib/tina4/gallery/templates/src/routes/gallery_page.rb
|
|
276
|
+
- lib/tina4/gallery/templates/src/templates/gallery_page.twig
|
|
266
277
|
- lib/tina4/graphql.rb
|
|
278
|
+
- lib/tina4/health.rb
|
|
279
|
+
- lib/tina4/html_element.rb
|
|
267
280
|
- lib/tina4/localization.rb
|
|
281
|
+
- lib/tina4/log.rb
|
|
282
|
+
- lib/tina4/messenger.rb
|
|
268
283
|
- lib/tina4/middleware.rb
|
|
269
284
|
- lib/tina4/migration.rb
|
|
270
285
|
- lib/tina4/orm.rb
|
|
271
286
|
- lib/tina4/public/css/tina4.css
|
|
272
287
|
- lib/tina4/public/css/tina4.min.css
|
|
273
|
-
- lib/tina4/public/
|
|
274
|
-
- lib/tina4/public/
|
|
288
|
+
- lib/tina4/public/favicon.ico
|
|
289
|
+
- lib/tina4/public/images/logo.svg
|
|
290
|
+
- lib/tina4/public/images/tina4-logo-icon.webp
|
|
291
|
+
- lib/tina4/public/js/frond.min.js
|
|
292
|
+
- lib/tina4/public/js/tina4-dev-admin.min.js
|
|
293
|
+
- lib/tina4/public/js/tina4.min.js
|
|
294
|
+
- lib/tina4/public/swagger/index.html
|
|
295
|
+
- lib/tina4/public/swagger/oauth2-redirect.html
|
|
275
296
|
- lib/tina4/queue.rb
|
|
276
297
|
- lib/tina4/queue_backends/kafka_backend.rb
|
|
277
298
|
- lib/tina4/queue_backends/lite_backend.rb
|
|
278
299
|
- lib/tina4/queue_backends/rabbitmq_backend.rb
|
|
279
300
|
- lib/tina4/rack_app.rb
|
|
301
|
+
- lib/tina4/rate_limiter.rb
|
|
280
302
|
- lib/tina4/request.rb
|
|
281
303
|
- lib/tina4/response.rb
|
|
304
|
+
- lib/tina4/response_cache.rb
|
|
282
305
|
- lib/tina4/router.rb
|
|
283
306
|
- lib/tina4/scss/tina4css/_alerts.scss
|
|
284
307
|
- lib/tina4/scss/tina4css/_badges.scss
|
|
@@ -298,16 +321,25 @@ files:
|
|
|
298
321
|
- lib/tina4/scss/tina4css/tina4.scss
|
|
299
322
|
- lib/tina4/scss_compiler.rb
|
|
300
323
|
- lib/tina4/seeder.rb
|
|
324
|
+
- lib/tina4/service_runner.rb
|
|
301
325
|
- lib/tina4/session.rb
|
|
302
326
|
- lib/tina4/session_handlers/file_handler.rb
|
|
303
327
|
- lib/tina4/session_handlers/mongo_handler.rb
|
|
304
328
|
- lib/tina4/session_handlers/redis_handler.rb
|
|
329
|
+
- lib/tina4/session_handlers/valkey_handler.rb
|
|
330
|
+
- lib/tina4/shutdown.rb
|
|
331
|
+
- lib/tina4/sql_translation.rb
|
|
305
332
|
- lib/tina4/swagger.rb
|
|
306
333
|
- lib/tina4/template.rb
|
|
307
334
|
- lib/tina4/templates/base.twig
|
|
335
|
+
- lib/tina4/templates/errors/302.twig
|
|
336
|
+
- lib/tina4/templates/errors/401.twig
|
|
308
337
|
- lib/tina4/templates/errors/403.twig
|
|
309
338
|
- lib/tina4/templates/errors/404.twig
|
|
310
339
|
- lib/tina4/templates/errors/500.twig
|
|
340
|
+
- lib/tina4/templates/errors/502.twig
|
|
341
|
+
- lib/tina4/templates/errors/503.twig
|
|
342
|
+
- lib/tina4/templates/errors/base.twig
|
|
311
343
|
- lib/tina4/testing.rb
|
|
312
344
|
- lib/tina4/version.rb
|
|
313
345
|
- 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">×</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
|
-
// ☰
|
|
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
|
-
})();
|