tina4ruby 3.10.50 → 3.10.54

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: abf3c5a919a2ef3d8045a321ff7199560cc9d984afbbfce65517728b2f12ae3c
4
- data.tar.gz: c7976150a47140a2585fd5adc19ccc66b6dbeaeeb38bdcdbf405bb239006acfe
3
+ metadata.gz: 5f97899d98f7c34c914f158fc57c2b0253b79e544ad9feacdca10d6a6479209f
4
+ data.tar.gz: 8370cfeefc28525f4cd918543e51e5f444c7da5845c80e58c2dc548d513f26de
5
5
  SHA512:
6
- metadata.gz: 8d2167a92355ee4d21810bd3999cd67993f48ab3be21ca7d2e85f83c5b51cfcc225a0d5db40213feecad93cb9548640fa3f80b06d43ede6bc0270fe06af98e07
7
- data.tar.gz: 3afbe6fa4642493a511949cbf0f324224e1fccbedaaa1edd18790bbbb8fcfaf2c29f1d8bb0a13d4ec16f94b8c4234b1e4d41603b9194763a9c3dd9dce60b0d04
6
+ metadata.gz: dde7f333c0474ff7add07c7693ebbb27b8a11d565f6f031ee0aeee106cd46e3e696a70bed388ceeebd40640b7022eb68e7e12aee55668a8914c3d5ac205cee0b
7
+ data.tar.gz: 0da6d2ab9ea1bb916f305f5f1dc2426dd92e1a54b3ac601c5b9cc607587ca64e7b40e0141e9cee6d7889bf001647c35b52ba2d19441027fe14bba2727d53f235
data/lib/tina4/cli.rb CHANGED
@@ -155,7 +155,7 @@ module Tina4
155
155
  # ── start ─────────────────────────────────────────────────────────────
156
156
 
157
157
  def cmd_start(argv)
158
- options = { port: nil, host: nil, dev: false, no_browser: false, production: false }
158
+ options = { port: nil, host: nil, dev: false, no_browser: false, no_reload: false, production: false }
159
159
  parser = OptionParser.new do |opts|
160
160
  opts.banner = "Usage: tina4ruby start [options]"
161
161
  opts.on("-p", "--port PORT", Integer, "Port (default: 7147)") { |v| options[:port] = v }
@@ -163,6 +163,7 @@ module Tina4
163
163
  opts.on("-d", "--dev", "Enable dev mode with auto-reload") { options[:dev] = true }
164
164
  opts.on("--production", "Use production server (Puma)") { options[:production] = true }
165
165
  opts.on("--no-browser", "Do not open browser on start") { options[:no_browser] = true }
166
+ opts.on("--no-reload", "Disable file watcher / live-reload") { options[:no_reload] = true }
166
167
  end
167
168
  parser.parse!(argv)
168
169
 
@@ -172,6 +173,11 @@ module Tina4
172
173
  options[:no_browser] = true
173
174
  end
174
175
 
176
+ # --no-reload flag sets TINA4_NO_RELOAD so the existing env check picks it up
177
+ if options[:no_reload]
178
+ ENV["TINA4_NO_RELOAD"] = "true"
179
+ end
180
+
175
181
  # Priority: CLI flag > ENV var > default
176
182
  options[:port] = resolve_config(:port, options[:port])
177
183
  options[:host] = resolve_config(:host, options[:host])
@@ -191,7 +197,8 @@ module Tina4
191
197
  load_routes(root_dir)
192
198
 
193
199
  if options[:dev]
194
- Tina4::DevReload.start(root_dir: root_dir)
200
+ no_reload = %w[true 1 yes].include?(ENV.fetch("TINA4_NO_RELOAD", "").downcase)
201
+ Tina4::DevReload.start(root_dir: root_dir) unless no_reload
195
202
  Tina4::ScssCompiler.compile_all(root_dir)
196
203
  end
197
204
 
data/lib/tina4/cors.rb CHANGED
@@ -66,7 +66,7 @@ module Tina4
66
66
  elsif request_origin && origin_allowed?(request_origin.chomp("/"))
67
67
  request_origin.chomp("/")
68
68
  else
69
- config[:origins].split(",").first&.strip || "*"
69
+ ""
70
70
  end
71
71
  end
72
72
  end
data/lib/tina4/orm.rb CHANGED
@@ -18,7 +18,7 @@ module Tina4
18
18
 
19
19
  class << self
20
20
  def db
21
- @db || Tina4.database
21
+ @db || Tina4.database || auto_discover_db
22
22
  end
23
23
 
24
24
  # Per-model database binding
@@ -345,6 +345,13 @@ module Tina4
345
345
 
346
346
  private
347
347
 
348
+ def auto_discover_db
349
+ url = ENV["DATABASE_URL"]
350
+ return nil unless url
351
+ Tina4.database = Tina4::Database.new(url, username: ENV.fetch("DATABASE_USERNAME", ""), password: ENV.fetch("DATABASE_PASSWORD", ""))
352
+ Tina4.database
353
+ end
354
+
348
355
  def find_by_id(id)
349
356
  pk = primary_key_field || :id
350
357
  sql = "SELECT * FROM #{table_name} WHERE #{pk} = ?"
@@ -3,6 +3,19 @@ require "json"
3
3
  require "securerandom"
4
4
 
5
5
  module Tina4
6
+ # Middleware wrapper that tags requests arriving on the AI dev port.
7
+ # Suppresses live-reload behaviour so AI tools get stable responses.
8
+ class AiPortRackApp
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ env["tina4.ai_port"] = true
15
+ @app.call(env)
16
+ end
17
+ end
18
+
6
19
  class RackApp
7
20
  STATIC_DIRS = %w[public src/public src/assets assets].freeze
8
21
 
@@ -43,6 +56,10 @@ module Tina4
43
56
 
44
57
  # Dev dashboard routes (handled before anything else)
45
58
  if path.start_with?("/__dev")
59
+ # Block live-reload endpoint on the AI port — AI tools must get stable responses
60
+ if path == "/__dev_reload" && env["tina4.ai_port"]
61
+ return [404, { "content-type" => "text/plain" }, ["Not available on AI port"]]
62
+ end
46
63
  dev_response = Tina4::DevAdmin.handle_request(env)
47
64
  return dev_response if dev_response
48
65
  end
@@ -95,7 +112,7 @@ module Tina4
95
112
  matched_pattern: matched_pattern || "(no match)",
96
113
  }
97
114
  joined = body_parts.join
98
- overlay = inject_dev_overlay(joined, request_info)
115
+ overlay = inject_dev_overlay(joined, request_info, ai_port: env["tina4.ai_port"])
99
116
  rack_response = [status, headers, [overlay]]
100
117
  end
101
118
  end
@@ -630,7 +647,7 @@ module Tina4
630
647
  [-1, {}, []]
631
648
  end
632
649
 
633
- def inject_dev_overlay(body, request_info)
650
+ def inject_dev_overlay(body, request_info, ai_port: false)
634
651
  version = Tina4::VERSION
635
652
  method = request_info[:method]
636
653
  path = request_info[:path]
@@ -638,9 +655,11 @@ module Tina4
638
655
  request_id = Tina4::Log.request_id || "-"
639
656
  route_count = Tina4::Router.routes.length
640
657
 
658
+ ai_badge = ai_port ? '<span style="background:#7c3aed;color:#fff;font-size:10px;padding:1px 6px;border-radius:3px;font-weight:bold;">AI PORT</span>' : ""
659
+
641
660
  toolbar = <<~HTML.strip
642
661
  <div id="tina4-dev-toolbar" style="position:fixed;bottom:0;left:0;right:0;background:#333;color:#fff;font-family:monospace;font-size:12px;padding:6px 16px;z-index:99999;display:flex;align-items:center;gap:16px;">
643
- <span id="tina4-ver-btn" style="color:#d32f2f;font-weight:bold;cursor:pointer;text-decoration:underline dotted;" onclick="tina4VersionModal()" title="Click to check for updates">Tina4 v#{version}</span>
662
+ #{ai_badge}<span id="tina4-ver-btn" style="color:#d32f2f;font-weight:bold;cursor:pointer;text-decoration:underline dotted;" onclick="tina4VersionModal()" title="Click to check for updates">Tina4 v#{version}</span>
644
663
  <div id="tina4-ver-modal" style="display:none;position:fixed;bottom:3rem;left:1rem;background:#1e1e2e;border:1px solid #d32f2f;border-radius:8px;padding:16px 20px;z-index:100000;min-width:320px;box-shadow:0 8px 32px rgba(0,0,0,0.5);font-family:monospace;font-size:13px;color:#cdd6f4;">
645
664
  <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
646
665
  <strong style="color:#89b4fa;">Version Info</strong>
@@ -709,6 +728,7 @@ module Tina4
709
728
  el.style.color='#f38ba8';
710
729
  });
711
730
  }
731
+ #{ai_port ? "" : "/* tina4:reload-js */"}
712
732
  </script>
713
733
  HTML
714
734
 
data/lib/tina4/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.10.50"
4
+ VERSION = "3.10.54"
5
5
  end
@@ -11,6 +11,7 @@ module Tina4
11
11
  def start
12
12
  require "webrick"
13
13
  require "stringio"
14
+ require "socket"
14
15
  Tina4.print_banner(host: @host, port: @port)
15
16
  Tina4::Log.info("Starting Tina4 WEBrick server on http://#{@host}:#{@port}")
16
17
  @server = WEBrick::HTTPServer.new(
@@ -101,10 +102,117 @@ module Tina4
101
102
  servlet.define_method(:webrick_req_port) { port }
102
103
 
103
104
  @server.mount("/", servlet, rack_app)
105
+
106
+ # AI dev port (port + 1) — no-reload, no-browser
107
+ @ai_server = nil
108
+ @ai_thread = nil
109
+ no_ai_port = %w[true 1 yes].include?(ENV.fetch("TINA4_NO_AI_PORT", "").downcase)
110
+ is_debug = %w[true 1 yes].include?(ENV.fetch("TINA4_DEBUG", "").downcase)
111
+
112
+ if is_debug && !no_ai_port
113
+ ai_port = @port + 1
114
+ begin
115
+ test = TCPServer.new("0.0.0.0", ai_port)
116
+ test.close
117
+
118
+ @ai_server = WEBrick::HTTPServer.new(
119
+ BindAddress: @host,
120
+ Port: ai_port,
121
+ Logger: WEBrick::Log.new(File::NULL),
122
+ AccessLog: []
123
+ )
124
+
125
+ # Wrap the rack app so AI-port requests are tagged
126
+ ai_rack_app = Tina4::AiPortRackApp.new(@app)
127
+
128
+ # Build a servlet identical to the main one but bound to the AI port host/port
129
+ ai_host = @host
130
+ ai_port_str = ai_port.to_s
131
+ ai_servlet = Class.new(WEBrick::HTTPServlet::AbstractServlet) do
132
+ define_method(:initialize) do |server, app|
133
+ super(server)
134
+ @app = app
135
+ end
136
+
137
+ %w[GET POST PUT DELETE PATCH HEAD OPTIONS].each do |http_method|
138
+ define_method("do_#{http_method}") do |webrick_req, webrick_res|
139
+ handle_request(webrick_req, webrick_res)
140
+ end
141
+ end
142
+
143
+ define_method(:handle_request) do |webrick_req, webrick_res|
144
+ if Tina4::Shutdown.shutting_down?
145
+ webrick_res.status = 503
146
+ webrick_res.body = '{"error":"Service shutting down"}'
147
+ webrick_res["content-type"] = "application/json"
148
+ return
149
+ end
150
+
151
+ Tina4::Shutdown.track_request do
152
+ env = build_rack_env(webrick_req)
153
+ status, headers, body = @app.call(env)
154
+
155
+ webrick_res.status = status
156
+ headers.each do |key, value|
157
+ if key.downcase == "set-cookie"
158
+ Array(value.split("\n")).each { |c| webrick_res.cookies << WEBrick::Cookie.parse_set_cookie(c) }
159
+ else
160
+ webrick_res[key] = value
161
+ end
162
+ end
163
+
164
+ response_body = ""
165
+ body.each { |chunk| response_body += chunk }
166
+ webrick_res.body = response_body
167
+ end
168
+ end
169
+
170
+ define_method(:build_rack_env) do |req|
171
+ input = StringIO.new(req.body || "")
172
+ env = {
173
+ "REQUEST_METHOD" => req.request_method,
174
+ "PATH_INFO" => req.path,
175
+ "QUERY_STRING" => req.query_string || "",
176
+ "SERVER_NAME" => webrick_req_host,
177
+ "SERVER_PORT" => webrick_req_port,
178
+ "CONTENT_TYPE" => req.content_type || "",
179
+ "CONTENT_LENGTH" => (req.content_length rescue 0).to_s,
180
+ "REMOTE_ADDR" => req.peeraddr&.last || "127.0.0.1",
181
+ "rack.input" => input,
182
+ "rack.errors" => $stderr,
183
+ "rack.url_scheme" => "http",
184
+ "rack.version" => [1, 3],
185
+ "rack.multithread" => true,
186
+ "rack.multiprocess" => false,
187
+ "rack.run_once" => false
188
+ }
189
+
190
+ req.header.each do |key, values|
191
+ env_key = "HTTP_#{key.upcase.gsub('-', '_')}"
192
+ env[env_key] = values.join(", ")
193
+ end
194
+
195
+ env
196
+ end
197
+ end
198
+
199
+ ai_servlet.define_method(:webrick_req_host) { ai_host }
200
+ ai_servlet.define_method(:webrick_req_port) { ai_port_str }
201
+
202
+ @ai_server.mount("/", ai_servlet, ai_rack_app)
203
+ @ai_thread = Thread.new { @ai_server.start }
204
+ puts " AI Port: http://localhost:#{ai_port} (no-reload)"
205
+ rescue Errno::EADDRINUSE
206
+ puts " AI Port: SKIPPED (port #{ai_port} in use)"
207
+ end
208
+ end
209
+
104
210
  @server.start
105
211
  end
106
212
 
107
213
  def stop
214
+ @ai_server&.shutdown
215
+ @ai_thread&.join(5)
108
216
  @server&.shutdown
109
217
  end
110
218
  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: 3.10.50
4
+ version: 3.10.54
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team