How to have Rails routes.rb redirect several paths to a single controller#action? - react-router

I'm wondering if there is a way to use "resourceful routing" in Rails to have all the routes point to one controller#action. I want React Router to handle the links.
I've reviewed https://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use and found this How to use React Router on top of Rails router (not using react-rails gem)?
My current routes.rb file (is this the best way to achieve to do this?):
Rails.application.routes.draw do
root to: "home#index"
get 'games', to: 'home#index'
get 'games/new', to: 'home#index'
get 'games/:id', to: 'home#index'
get 'games/:id/edit', to: 'home#index'
namespace :api do
resources :games
end
end

resources :games, controller: :home
This will provide you with the crud paths + index that you want. The paths will be
/games
/games/new
/games/:id
/games/:id/edit
all pointing to HomeController
And then you can still have you're namespaced routes
UPDATE: Updated answer based on comment from op

Related

Call a controller method via button in Rails

I want to create a button in views/companies/edit.html.haml for destroy_referrals method, but I can't get the button to work. To call the action on a button I tried to create a new route, but I am still receiving the error The action 'destroy_referrals' could not be found for CompaniesController
I created a destroy_referrals method in Admin::CompaniesController,
def destroy_referrals
load_company
#company.destroy_referrals
flash[:notice] = 'Referrals deleted'
redirect_to :action => :index
end
it manipulates the company.rb model to find the referrals from users with the same company_id and destroys them,
def destroy_referrals
Referral.joins(:user).where("users.company_id = ?", self.id).destroy_all
end
Some things I've tried below...
routes.rb
post '/destroy_referrals' => 'admin/companies#destroy_referrals', :as => 'destroy_referrals'
views/companies/edit.html.haml
= button_to 'Destroy Referrals', destroy_referral_path
= button_to 'Destroy Referrals', admin_company_destroy_referral_path(#company)
HTML links (not “buttons”) aren't wired up to call “methods” directly.
The button_to method creates an HTML button, which behaves similarly to a link, but with different visual styles.
Ruby has methods, which are a common programing construct.
HTML links have an href attribute that a browser can follow by making a subsequent HTTP request to.
When a browser sends a request to your Rails application, the application uses your routes to determine how to handle that request.
The conventional way to handle a request in Rails is to route it to a controller action.
A RESTful request to /products/1 might, by convention, route to the show action on the ProductsController.
In Rails, an “action” is created by defining a method on the controller.
RESTful routes reflect the representational state transfer paradigm, involving reading, listing, creating, updating, and deleting records/objects.
To delete (or destroy) a record, RESTful convention would be to send a DELETE request to the Rails application, which would be routed to the relevant controller's destroy action, defined by the destroy method.
You can create an HTML link that results in a browser sending such a request using the link_to helper and specifying method: :delete in the parameters.
You can define a route instructing Rails to handle such a request using the built in “resourceful” routing: e.g. resources :products. Alternately, an explicit route can be defined: delete '/products/:id' => 'products#destroy'.
This all said, the error Rails is giving you is that it can't find the destroy_referrals method in CompaniesController. Defining methods in Admin::CompaniesController won't do anything to resolve this.
#Swards the route needed to be delete
'companies/:id/destroy_referrals' => 'companies#destroy_referrals',
:as => 'destroy_referrals'
I thought it might be a simple fix. Do you still need the admin namespace?
Consider this, instead of creating a named route
namespace 'admin' do
resources :companies do
delete 'destroy_referrals', :on => :member, :as => 'destroy_referrals'
end
end
And refer to it as admin_destroy_referrals_company_path
= button_to 'Destroy Referrals', admin_destroy_referrals_company_path(#company), :method => :delete

Static HTML page in Phoenix

I'm trying to build a JSON backend for Elm. I only want to serve one page - elm.html, one js file - Main.js - and one css file.
I tried following these instructions but there is not enough there to help a complete newbie like me.
So now I have router.ex
scope "/", JwtExample do
pipe_through :browser # Use the default browser stack
get "/elm", RootController, :index
get "/", PageController, :index
end
# Other scopes may use custom stacks.
scope "/api", JwtExample do
pipe_through :api
resources "/users", UserController, except: [:new, :edit]
end
This controller
defmodule JwtExample.RootController do
use JwtExample.Web, :controller
plug :action
def index(conn, _params) do
redirect conn, to: "/elm.html"
end
end
And my files in web/static and priv/static
But when I surf to /elm I get
no route found for GET /elm.html (JwtExample.Router)
OK, so based on psantos answer, I needed to change lib/endpoint.ex to read
plug Plug.Static,
at: "/", from: :jwt_example, gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt elm.html)
Here's a solution that also servers the static page for requests to the root url, i. e. https://myapp.test/:
here's a solution that maps a request to the root path to index.html with a short function plug that can be added to your endpoint.ex without involving controllers. It works by defining a short Plug function to change the requested path. My hope is that it's a little faster to to it in the endpoint compared to doing it in a controller.
def redirect_index(conn = %Plug.Conn{path_info: []}, _opts) do
%Plug.Conn{conn | path_info: ["elm.html"]}
end
def redirect_index(conn, _opts) do
conn
end
plug :redirect_index
# This is Phoenix's standard configuration of Plug.Static with
# index.html added.
plug Plug.Static,
at: "/", from: :phoenix_elm_starter_template, gzip: false,
only: ~w(css fonts elm.html images js favicon.ico robots.txt)
Note that in production, you would usually deal with static assets at application server layer, possibly not hitting Phoenix at all.

Rails API: make application controller serve JSON

This tutorial, React + Flux Backed by Rails API, by Fancy Pixel, says:
Now we need to change the application controller so that it inherits
from ActionController::API, and kiss the protect_from_forgery goodbye.
Since we are serving only JSON, it makes sense to add
respond_to :json
to the applciation controller, helps DRYing all out. While we are at
it, we might as well delete the assets and views folders, we won’t
need them.
I am not sure about what I am supposed to do.
This is what my application controller looks like:
class ApplicationController < ActionController::API
respond_to :json
end
Is it correct?
Also, should I keep or remove protect_from_forgery with: :null_session :
class ApplicationController < ActionController::API
protect_from_forgery with: :null_session
respond_to :json
end
To communicate from any Rails Backend (not even necessarily just an API) all you have to do is write the following in your API's controller(s):
class Api::V1::SearchController < ApplicationController
#Run an Authentication Method on their API Key
before_action :authenticate
def search
#Performs your backend logic
content_array = get_content_method
render :json => content_array
#Renders up a JSON that you can retrieve with an HTTP get request
end
end
Also on the topic of serving data, you can send your params in an obscured JSON, allowing you to hide your API Key, thus protecting you from unwanted access. My favorite way of doing this is by using the .to_query method to send over the necessary params. I have done it like so:
BASE_URL = "http://yourwebsite.com/search?"
def generate_search_url(params)
BASE_URL + params.to_query + "&Api-Key=#{ENV['API-KEY']}"
end
This way you can work with the data you're given just like any other params, but it would be more difficult to abuse the API without a granted key.

Redirecting front-end routes to Dashboard in Bolt CMS

I'm trying to redirect front end routes to the admin dashboard, as I'm using the Bolt installation as a REST API back end. Here's how I'm routing the content:
contentlink:
path: /{contenttypeslug}/{slug}
defaults: { _controller: 'Bolt\Controllers\Backend::dashboard' }
requirements:
contenttypeslug: 'Bolt\Controllers\Routing::getAnyContentTypeRequirement'
So, all I've done is use the dashboard controller. When I try to visit one of those routes, I get the following whoops error:
Twig_Error_Loader
Template "dashboard/dashboard.twig" is not defined ()
So for some reason it's not looking in the correct place for the template. Is there a way to correct this?
This looks like it's to do with the Twig path which is setup differently depending on whether there is a frontend or backend request.
you can always add a path to the Twig environment that Bolt uses with the following call:
$app['twig.loader.filesystem']->prependPath("/path/to/twig");
The path to the backend twig templates may vary but usually this will work.
$path = $app['resources']->getPath('app/view/twig');
$app['twig.loader.filesystem']->prependPath($path);

Masking an image's real URL in Rails

I'm creating an incredibly basic photo sharing app in Rails that displays albums from the local filesystem.
For example -
/path/to/pictures
|
|-> 2003_college_graduation
|-> 2002_miami_spring_break
However, anyone can take a look at the HTML source and get the absolute path to the image -
my.server.com/path/to/pictures/2003_college_graduation/IMG_0001.JPG
And with a little guesswork, anyone could navigate to other images on the server, even ones they don't have permission to.
Is there any way to "mask" the URL here?
One potential solution is to hash each filepath into a UUID and store the mappings in mysql table. Then when they request the URL with that hash I can look it up in the table and pull the correct image. But that makes the URL looks messy and creates a problem if the URL ever changes (because the hash will change).
Are there any libraries or other workarounds to mask the real path to a file?
Thanks!
You could use a url minifier (take your pick) and use that link. They'd still be able to see the original source if they followed it, but it would get it out of the html file.
What you're trying to achieve here is a security through obscurity, which isn't going to work in the end. One can get aware of the scrambled URLs from any other source and still have access to the pics he should not be seeing.
The real solution is to actually control access to the files. It is a pretty common problem with a pretty common solution. In order to force access control you have to invoke a Rails controller action before serving the file and verify the credentials, and then, if the credentials are valid, serve the actual file.
It could be like this in the controller:
class PhotoController < ApplicationController
def photo
if user_has_access?(params[:album], params[:photo])
# be *very* careful here to ensure that user_has_access? really validates
# album and photo access, otherwise, there's a chance of you letting a malicious
# user to get any file from your system by feeding in certain params[:album]
# and params[:photo]
send_file(File.join('/path/to/albums', params[:album], "#{params[:photo]}.jpg"), type: 'image/jpeg', disposition: 'inline')
else
render(file: File.join(Rails.root, 'public/403.html'), status: 403, layout: false)
end
end
private
def user_has_access?(album, photo)
# validate that the current user has access and return true, if he does,
# and false if not
end
end
And then in your routes file:
get '/photos/:album/:photo.jpg' => 'photo#photo', as: album_photo
And then in your views:
<%= image_tag album_photo_path('album', 'photo') %>
What's good about send_file is that it simply serves the file out of Rails in development mode, but in production it can be configured to offload it to the actual webserver to keep the performance of your Rails code optimal.
Hope that gives a basic idea of what it might be and helps a bit!