From daa39320a636453a1cab6c87fcecd806c8e66ed7 Mon Sep 17 00:00:00 2001 From: Roman Priesol Date: Sat, 12 Sep 2026 00:31:36 +0200 Subject: [PATCH] Add Brother printer switcher proxied through one port, move sinatra to port 80 --- app.rb | 62 +++++++++++++++++++++++++++++++++++++++- views/brother_picker.erb | 18 ++++++++++++ views/layout.erb | 4 ++- 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 views/brother_picker.erb diff --git a/app.rb b/app.rb index 685db40..9fbdb1a 100644 --- a/app.rb +++ b/app.rb @@ -2,11 +2,12 @@ require 'sinatra' require "sinatra/reloader" require 'tmpdir' +require 'net/http' set :protection, :except => :frame_options set :environment, :production set :bind, "::" -set :port, 4567 +set :port, 80 if !File.exist?('config.json') || !File.readable?('config.json') raise RuntimeException, 'Could not read config.json' @@ -17,6 +18,12 @@ PRINTER_DPI = 203 # DatamaxONeil E4204B Mark III native head resolution PRINTER_DARKNESS = 8 # lower than factory default to stop thermal dot-gain from fusing barcode bars TEST_LABEL_PDF = File.join(File.dirname(__FILE__), 'test-label.pdf') +BROTHER_PRINTERS = { + 'ql560' => { port: 8560, label: 'Brother QL-560' }, + 'ql700' => { port: 8700, label: 'Brother QL-700' }, + 'ql1100' => { port: 9100, label: 'Brother QL-1100' } +}.freeze + configure do enable :static set :session_secret, File.read(File.join(File.dirname(__FILE__), '.session_secret')).strip @@ -73,6 +80,36 @@ helpers do "^XA\n^MD#{PRINTER_DARKNESS}\n^PW#{width}\n^LL#{height}\n^FO0,0^GFA,#{total_bytes},#{total_bytes},#{bytes_per_row},#{hexdata}\n^XZ\n" end + + # Transparently reverse-proxies the current request to one of the + # brother_ql_web instances, so all three printers are reachable through + # this single sinatra port instead of their own dedicated ports. + def proxy_to_brother(port, path) + uri = URI("http://127.0.0.1:#{port}#{path}") + uri.query = request.query_string unless request.query_string.empty? + + proxied = Net::HTTP.const_get(request.request_method.capitalize).new(uri) + request.env.each do |key, value| + next unless key.start_with?('HTTP_') && key != 'HTTP_HOST' + + header = key.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-') + proxied[header] = value + end + proxied['Content-Type'] = request.content_type if request.content_type + proxied['X-Forwarded-Prefix'] = '/brother' + proxied['X-Forwarded-Host'] = request.host + proxied.body = request.body.read unless request.request_method == 'GET' + + res = Net::HTTP.start(uri.host, uri.port) { |http| http.request(proxied) } + + headers = {} + res.each_header do |key, value| + next if %w[transfer-encoding connection content-length].include?(key.downcase) + + headers[key] = value + end + [res.code.to_i, headers, [res.body || '']] + end end before do @@ -136,3 +173,26 @@ post '/test-print' do end redirect '/upload' end + +get '/brother' do + @printers = BROTHER_PRINTERS + @selected = session[:brother_printer] + erb :brother_picker +end + +post '/brother/select' do + key = params[:printer] + halt 400, 'unknown printer' unless BROTHER_PRINTERS.key?(key) + + session[:brother_printer] = key + redirect '/brother/' +end + +%w[get post put delete].each do |verb| + send(verb, '/brother/*') do + key = session[:brother_printer] + redirect '/brother' unless key + + proxy_to_brother(BROTHER_PRINTERS[key][:port], '/' + params[:splat].first) + end +end diff --git a/views/brother_picker.erb b/views/brother_picker.erb new file mode 100644 index 0000000..928917e --- /dev/null +++ b/views/brother_picker.erb @@ -0,0 +1,18 @@ +

Brother printers

+

Choose a printer to open its label designer through this server.

+ +
+ <% @printers.each do |key, info| %> +
+ + +
+ <% end %> +
+ +<% if @selected %> +

Open <%= @printers[@selected][:label] %> »

+<% end %> diff --git a/views/layout.erb b/views/layout.erb index 55d886e..4fe379e 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -24,6 +24,9 @@ + @@ -53,4 +56,3 @@ -