Sinatra `map` not working - ruby

I'm trying to run sinatra behind a proxy, at the url http://server/project/, so that GET http://server/project/foo goes to /foo in the app. My config.ru looks like this:
map('/project') { run App }
I see in the log "GET /project/ HTTP/1.1" 404; even though i have a GET '/' method defined. It works when run normally (just run App without the map call). I can't figure out why.

Related

Does my Sinatra API application need a class declaration?

Someone developed an API application for me that works well. Unfortunately, it doesn't log anywhere and there are no logs at all. The app runs with the "rackup" command and sits behind an nginx web server. The Sinatra errors are not logged to the nginx logs.
The app.rb file looks something like this:
require './libs'
require 'sinatra'
require 'sinatra/namespace'
set :bind, '::1'
before do
content_type :json
headers 'Access-Control-Allow-Origin' => '*', 'Access-Control-Allow-Methods' => ['OPTIONS', 'GET', 'POST']
end
namespace '/api/v1' do
namespace '/getit/:thingtoget' do
helpers do
def myhelper1
<stuff>
end
def myhelper1
<stuff>
end
end
before do
myhelper1
myhelper2
end
get '/info' do
WidgetDomain::get_info(#va1, #var2).to_json
end
<more API paths here>
end
Now this is working fine. But now I want to introduce logging. So I looked at the Sinatra README and it said I could enable logging like so:
class MyApp < Sinatra::Base
configure :production, :development do
enable :logging
end
end
Do I put that above the namespace stuff? If I declare an app like that, does my namespace stuff need to be inside that app code somehow? I don't have a grasp of how this works at all.
It almost looks like it's trying to log in the nginx log, but here's what an error line looks like there:
2018/12/30 19:53:15 [error] 6615#0: *21522 connect() failed (111: Connection refused) while connecting to upstream, client: <someip>, server: api.example.com, request: "GET /api/v1/getit/thingtoget1/stuff/var1/var2/var3 HTTP/1.1", upstream: "http://[::1]:9292/api/v1/getit/thingtoget/stuff/var1/var2/var3", host: "api.example.com", referrer: "an HTML page from the nginx server"
It's almost like it's trying to connect back to the server to retrieve a message or something. But in case the logging has something to do with the way I've declared the server in the nginx configuration, here it is:
server {
listen 443;
listen [::]:443;
server_name api.myapp.com;
ssl on;
ssl_certificate /etc/nginx/ssl/myapp_com.pem;
ssl_certificate_key /etc/nginx/ssl/star_myapp_com.key;
location / {
proxy_pass http://localhost:9292;
}
}
Have a look at Sinatra's README in the Logging section but I tend to set up a logger outside of the Sinatra app in case other bits get bolted on or several apps are used together and I can use the same logger for all of them. Simplest way is a global variable (one of the few places they're acceptable, but it's not the only way) :
require 'mono_logger' # because it's thread safe
require 'pathname' # because paths aren't strings :)
log_path = Pathname(__dir__).join("logs/app.log")
$logger = MonoLogger.new(log_path)
$logger.level = MonoLogger::INFO
Then in a route or wherever:
get '/' do
$logger.info "here"
In my terminal:
$ cat logs/app.log
I, [2019-01-07T13:03:52.989415 #64378] INFO -- : here
As to configuration blocks, you don't need to worry about putting them within a class declaration unless you're using a modular app (see Modular vs. Classic Style in the README) and yours is in the classic style.
Configuration blocks aren't namespaced (Sinatra::Namespace handles things that take a route as an argument, like get and before) so follow convention and stick it near the top of the file.
Hope that helps.

Sinatra show view /example/:id returns 404 error for assets, when using sprockets

When I am accessing:
get "/example/:id" do
...
slim :'example/show'
end
I am getting this error:
"GET /example/assets/app.css HTTP/1.1" 404
"GET /example/assets/app.js HTTP/1.1" 404
I am suspecting that :id can be a problem here, because
my assets setup works when I am accessing:
get "/example" do
...
slim :'example/index'
end
works:
"GET /assets/app.css HTTP/1.1" 304
"GET /assets/app.js HTTP/1.1" 200
My setup for sprockets:
class App < Sinatra::Base
set :environment, Sprockets::Environment.new
environment.append_path "assets/stylesheets"
environment.append_path "assets/javascripts"
environment.js_compressor = :uglify
environment.css_compressor = :scss
get "/assets/*" do
env["PATH_INFO"].sub!("/assets", "")
settings.environment.call(env)
end
...
end
My full repo but without last changes: https://github.com/aneta-bielska/home-for-paws-app
In your layout you have the following lines which define the links to your assets:
link rel="stylesheet" href="assets/app.css"
script src="assets/app.js"
Since the urls in these elements don’t start with a slash, the browser treats them as relative to the page where they appear. This means that when you visit /example the links go to /assets/app.cs and /assets/app.js. However when you go to e.g example/1 the links are treated as relative to the 1, so the browser tries to fetch /example/assets/app.cs (and similarly for app.js).
You need to make sure theses links are always treated as absolute. The simplest way would be to just add a / at the start:
link rel="stylesheet" href="/assets/app.css"
script src="/assets/app.js"
A more robust solution might be to use Sinatra’s url helper to ensure you always create the correct links, as it will take it account mounting the app at different paths on the server:
link rel="stylesheet" href=url("/assets/app.css")
script src=url("/assets/app.js")

URL Map an entire namespace using rack mount?

I have two modular Sinatra rack based applications: core.rb & project.rb:
# core.rb
class Core < Sinatra::Base
get "/" do
"Hello, world!"
end
end
# project.rb
class Project < Sinatra::Base
get "/" do
"A snazzy little Sinatra project I wish to showcase."
end
get "/foo" do
"If you see this, congratulations."
end
end
My goal is simply to map the entire /projects namespace to the Project class, wheras everything else is handled by the Core class. I found that you can do this to a limited extent in 2 ways:
# config.ru
require "./core.rb"
require "./projects.rb"
map "/projects" do
# Method #1: Using Sinatra's built-in Middleware
use Project
# Method #2: Using Rack::Cascade
run Rack::Cascade.new( [Project, Core] )
end
run Core
Both of the methods I tried above have the same effect. The routes / and /projects show up correctly, however when going to /projects/foo it throws an error which states it can't find the /foo route in my main core.rb file - which is NOT what I want. In other words it's looking for my /foo route in the wrong file :(
So, is it possible to map across the entire /projects namespace using rack-mount? And no, adding "/projects/" to all my routes in project.rb is not an option here I'm afraid.
Your config.ru file seems to work okay when I test it, but it looks a little confused. Here’s a simpler example that achieves the same thing:
map "/projects" do
run Project # note run, not use
end
run Core
Now any request where the path starts with /projects will be routed to the Project app, and all other will go to Core, which is associated with the root path automatically.

getting rack mapped directory inside app

Say I have a config.ru like:
map '/foo' do
run MyApp
end
and a Sinatra app like:
class MyApp < Sinatra::Base
use Rack::Session::File, key: 'rack.session', domain: 'my.domain.com', path: '/foo', expire_after: 86400 * 14, secret: 'mysecret'
end
How can I make MyApp agnostic to which request directory (/foo in this case) is used to access it? I have found that request.script_name contains this directory, but I cannot use it for the path: parameter of the use Rack::Session::File statement since it is not defined yet when starting the app from passenger, but only when requests are sent to the application later.
Unfortunately it's impossible even with dirty hacks.
So I suppose it's possible to do via two different ways:
External configuration file e.g. routes.yml (config.ru uses it for
map statement, application to discover such prefix in url);
Environment variable (I've chosen this because it's easy to configure on Heroku.

Having trouble debugging Sinatra app in production

I'm deploying a Sinatra app using passenger. The deployed app is working, but not entirely: some paths work fine, others simply render a blank page. I can't seem to find any major differences between the routes that work and the routes that don't, and I can't seem to track down any errors..
Handlers
I have defined the not_found and error handlers as follows:
not_found do
'404. Bummer!'
end
error do
'Nasty error: ' + env['sinatra.error'].name
end
These work fine on my local machine, both in development and production, but I never see these come up on the server.
Apache Logs
When I tail Apache's access.log and hit one of the broken paths, I see a 500:
helpers [27/Oct/2009:15:54:59 -0400] "GET /admin/member_photos/photos HTTP/1.1" 500 20 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"
rack_hoptoad
I've also installed and configured rack_hoptoad middleware in my config.ru, but no exceptions are making it to hoptoad.
# Send exceptions to hoptoad
require 'rack_hoptoad'
use Rack::HoptoadNotifier, 'MY_API_KEY'
logging
I've set up logging like so..
set :raise_errors => true
set :logging, true
log = File.new("log/sinatra.log", "a+")
STDOUT.reopen(log)
STDERR.reopen(log)
require 'logger'
configure do
LOGGER = Logger.new("log/sinatra.log")
end
helpers do
def logger
LOGGER
end
end
This setup lets me call logger.info within my routes, which works locally and on the server for the working routes, but the broken paths don't get far enough to call logger.info.
What to do?
Any ideas as to how I can see what's causing the 500 errors? Thanks for any help!
I would try using the Rack::ShowExceptions middleware to try and trace out the problem. In your config.ru add these two lines before the run call:
require 'rubygems'
require 'your-app'
use Rack::ShowExceptions
run YourApp
That should catch and display the backtrace for any exceptions occurring in Rack or in your app. That should give you more details to work with, at least that would be the hope.
Maybe there's something wrong with your log setup?
Redirect STDERR when running the Sinatra server so you can read it. Like:
ruby myapp.rb -p 1234 > log/app.log 2>&1
Thanks for the responses, but I didn't end up needing to use them. I was originally deploying the app in a sub-URI configuration. When I deployed the app to it's own subdomain instead, the problems went away.
So.. I'm not really sure what the problem was, but getting rid of this line is my Apache configuration for the site is what resolved things:
Redirect permanent / https://www.example.org/admin/member_photos/

Resources