I am using ROR, nginx, passenger...
If there is no picture in the DB then my app will serve the 'default_avatar.png'. I noticed I was unable to save new pictures. So, I updated my dragonfly initializer to point at my db server:
...
# I have obscured the host IP for this example.
config.url_host = Rails.env.production? ? 'http://IP_OF_HOST' : 'http://localhost:3000'
...
Now I can save pictures and view them through my app, but the 'default_avatar.png' does not resolve. Oddly enough, other image assets do seem to come through. Why do I get 403? At first guess I thought it was a permissions error. But then why would it serve the other images?
UPDATE:
I have just noticed a very important clue. When the assets do not work, they have the url:
/media/jakbfAGasgAgADSGANJGFDgbnadglnalgbakljbgkjabg/default_avatar.png
And when they do work:
/assets/avatar.png
I should mention that I have 2 app servers and 1 db server. I do not believe it to be a permissions error.
I've encountered the same problem.
You need to specify the extension of the file when using the html helper provided by AssetTagHelper lib.
This will work:
<%= image_tag('avatar.png') %>
This won't work:
<%= image_tag('avatar') %>
Not easy to debug.
Related
I'm trying to create an "asset controller" shim which will filter static asset requests so only authorized users can get retrieve certain assets. I wanted to continue to use the asset pipeline so I setup a route like this
get 'assets/*assetfile' => 'assets#sendfile'
Then I created an AssetsController with one method "sendfile". Stripping it down to only the stuff that matters, it looks like this:
class AssetsController < ApplicationController
def sendfile
# Basically the following function forces the file
# path to be Rails.root/public/assets/basename
assetfilename=sanitize_filename(params[:assetfile] + '.' + params[:format])
send_file(assetfilename)
end
end
It looks like I have to run this in production mode as rails by-passes my route for assets in development. So I precompile my assets and I can verify in the controller that the files exist where they are expected to be.
However, now the problem is that I'm getting a "ActionController::InvalidCrossOriginRequest" when the Javascript asset is requested (just using the default application.* assets for now). I've read about this error and I understand that as of Rails 4.1 there are special cross-origin protections for Javascript assets. Sounds good to me, but I don't understand where the "cross-origin" part is coming from. Using firebug, I can see that the asset requests are being requested from the same domain as the original page.
I am certain that this is the problem because I can solve it by putting "skip_before_action :verify_authenticity_token" in the beginning of my controller. However, I really don't want to do this (I don't fully understand why this check is necessary, but I'm sure there are very good reasons).
The application.html.erb file is unchanged from the default generated file so I assume it's sending the CSRF token when the request is made, just as it would if I didn't have my own controller for assets.
So what am I missing?
Ok, I think I answered my own question (unsatisfactorily). Again, long post, so bear with me. I mistakenly forgot to add this to my original questions, but I'm using Ruby 2.2.0 and Rails 4.2.4.
From looking at the code in "actionpack-4.2.4/lib/action_controller/metal/request_forgery_protection.rb", it looks like Rails is doing two checks. The first check is the "verify_authenticity_token" method which does the expected validation of the authenticity token for POST requests. For GET requests, it ALSO sets a flag which causes a second check on the formed computed response to the request.
The check on the response simply says that if the request was NOT an XHR (AJAX) request AND the MIME Type of the response is "text/javascript", then raise an "ActionController::InvalidCrossOriginRequest", which was the error I was getting.
I verified this by setting the type to "application/javascript" for ".js" files in "send_file". Here's the code:
if request.format.js?
send_file(assetfilename, type: 'application/javascript')
else
send_file(assetfilename)
end
I can skip the response check all together by just adding the following line to the top of my controller class:
skip_after_action :verify_same_origin_request
The check on the response seems pretty weak to me and it's not clear how this really provides further protection against CSRF. But I'll post that in another question.
I just deployed one of my apps to heroku. This app uses :
A default "myapp.herokuapp.com" address,
And I got a domain configured so that the app can be reached through "www.myapp.com".
And I noticed today the following issue : my application links are based on "http://myapp.herokuapp.com" domain (hence I get "http://myapp.herokuapp.com/page" URLs) even when I access the app using my domain name (I would then expect to get "www.myapp.com/page" URLs).
I tried to edit my production.rb and set the default_url_options :
# Base domain for url generation
config.action_controller.default_url_options = { :host => "www.myapp.com" }
But it doesn't change a thing. Also tried to change this in application.rb, just in case, but nothing happens either.
Any clue ?
Thanks a lot for your help guys !
Edit : This used to work as expected before today when I did the database migration to the new Heroku postgres thing. Don't know if this can have any impact.
If you're using _path methods for your urls, this is generating a relative path which is always based on the url you visit. If you're using controller/fragment caching, you should probably use _url instead in your views. You might also want to consider setting config.action_controller.perform_caching to false in your production.rb if all your pages have some controller logic.
See this page for more info on how caching works in Rails.
I had a similar problem. It was caused by the following line of code which was pointing to heroku.com and getting redirected to herokuapp.com
config.action_mailer.default_url_options = { :host => 'my-staging-domain.heroku.com' }
I mention it because it's the action_mailer.default_url_options yet clearly it affects the default url options outside of the scope of the mailer if you haven't explicitly set up the action_controller.default_url_options
how to display the image, which stored outside from the project directory?
Is there a simple way to do it?
I see two ways :
On top of your rails server, in production and sometimes in dev, you have to use a web server like apache or nginx. With these, you can serve files from other directories for specific URL. E.G. you can make http://yourapp.com/images/ serving files from a specific dir. In rails, display the image with a traditionnal image_tag
Example with Nginx :
# Find the right `server` section which you currently use to serve your rails app
server {
listen 80;
# Add this
location /images {
root /var/www/my/specific/folder;
}
location / {
#...
#... Here you should already some code to proxy to your rails server
}
}
With that, when you access to `yourserver.com/images`, nginx serve your specific folder and not your rails app. Then in your app view :
<%= image_tag 'http://yourserver.com/images/my_pic.jpg' %>
If you can't access your server settings, you can serve an image file from a controller action with send_file
In a controller :
class ImagesController < ApplicationController
def show
send_file File.join('/var/www/my/specific/folder',params[:name]), :disposition => 'inline'
end
end
In config/routes.rb
match '/images/:name' => 'images#show', :as => :custom_image
Then when you access this action (via the route you defined in config/routes.rb), you have the image. So in your view you do a traditionnal image_tag with this URL :
<%= image_tag custom_image_path( 'my_pic.jpg' ) %>
OR
<%= image_tag custom_image_url( 'my_pic.jpg' ) %>
If it's stored outside of the Rails app directory, then it does not belong to asset pipeline and you can simply link to it:
<%= image_tag("http://example.com/some_file.jpg") %>
Obviously it must be accessible through HTTP (you need nginx or Apache server installed).
This is probably bad idea and will lead to a bunch of issues. Some security, some functionality, but most of the effects I actually don't know.
I do know, from experience, that whenever you go against the rails conventions for what and where stuff is, it's a slippery slope of things breaking, best avoided.
Create a solution using the framework provided.
Note this functionality can also be affected a bit if you are using rails 3.1+ as opposed to <= 3.0 due to asset compilation which copies js, css and images into public.
This might be (read: probably is) a dumb question, but here goes...
Is there a simple, preferably non third-party, way to display Rails logs in the browser? It gets kind of old opening a log file, then closing it and re-opening it on the next error. It would be superfantastic to just to go domain.tld/logs, which would require user authentication, of course.
For reference, there is a very simple way to do this. However, you would want to protect this page with some sort of authentication/authorization.
Controller:
lines = params[:lines]
if Rails.env == "production"
#logs = `tail -n #{lines} log/production.log`
else
#logs = `tail -n #{lines} log/development.log`
end
Log File View:
<pre><%= #logs %></pre>
View to display link:
<%= link_to "log file", log_path(lines: 100) %>
Routes:
get 'logs/:lines' => "pages#log", as: "log"
All you need is to open log file and put its content into browser.
Realted topic: ruby: all ways to read from file.
Also you should know that your logs grow very fast, and it's not a good idea to show whole log in browser. You can just open last 100-200 rows. Related topic: Reading the last n lines of a file in Ruby?
Also you can try this solution: http://newrelic.com/. It is more complex and little oftopic, but quite useful.
I created a for this purpose called browserlog.
Installing is just a matter of adding the gem to the Gemfile:
gem 'browserlog'
Afterwards all you need is to mount the engine on the route you want:
MyApp::Application.routes.draw do
mount Browserlog::Engine => '/logs'
end
Once that's set up, accessing /logs/development (or production, test, etc) will show a window like this:
(source: andredieb.com)
Pimp my Log can be used to visualization many types of logs including Ruby on Rails.
It also includes the management of users and notification.
[Edited]
By default, the PimpMyLog does not support Ruby on Rails logs. It's necessary to implement the regex to works with it.
[/Edited]
I’m having some problems with John Guenin's x_sendfile (http://john.guen.in/past/2007/4/17/send_files_faster_with_xsendfile/).
When coding the download of a PDF file, I’m using the following code:
def send_the_file(filename)
xsendfile (“#{Rails.root}/doc/” + filename, :type => ‘application/pdf’)
end
but I only get 1 byte downloaded. This usually happens if the filename is not absolute (hence the #{Rails.root} being added. I’ve also checked that the file has the necessary permissions. This is failing both on localhost and my prod site.
Any ideas what I'm doing wrong?
TIA,
Urf
What version of Rails are you using? If you're on 2.1 or later, the X-Sendfile option is built into Rails' send_file method.
send_file 'filename', :x_sendfile => true
Otherwise, are you sure that mod_xsendfile has been installed and configured correctly?
You may want to make sure that your are actually using a web server that supports xsendfile. If you are dev mode you probably aren't and it may fail.
Try to set in apche httpd.conf file
XSendFileAllowAbove on