Static HTML page in Phoenix - html

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.

Related

flask_restplus: unable to get flask_swagger_ui to work in conjunction with namespaces

We have a large flask_restplus application that's been in service for a year or so. We now would like to use flask_swagger_ui to work within this application to serve some statically generated swagger docs for part of the application.
Our flask_restplus-based application is already using namespaces, but the flask_swagger_ui docs that I have found seem to suggest that we have to use blueprints via its get_swaggerui_blueprint method in order to serve this swagger. However, when we do this, the flask_restplus application doesn't recognize the URL associated with this blueprint. We're wondering if perhaps this might be due to some sort of unwanted interaction between namespaces and blueprints within the same application ... or perhaps could it just be due to the fact that we're doing something incorrectly ... ???
Here is what I believe is the pertinent fragment of our code ...
app = flask.Flask(__name__)
api = flask_restplus.Api(
app,
version='1.0',
title='Test Application',
description='Test Application',
doc='/docs/'
)
ns = api.namespace('server', description='Main server', path='/server')
utilns = api.namespace('util', description='Util', path='/util')
SWAGGER_URL = '/swagger'
API_URL = 'http://petstore.swagger.io/v2/swagger.json' # just for testing
swaggerui_blueprint = get_swaggerui_blueprint(
SWAGGER_URL,
API_URL,
config={
'app_name': "Test application"
},
)
app.register_blueprint(swaggerui_blueprint)
# Set up endpoints under the namespaces defined above,
# and do some more initialization.
app.run()
However, if we try to access our app like this, http://our.domain.name:PORT/swagger/dist, it fails as follows ...
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 FINAL//EN">
<title>405 Method Not Available</title>
<h1>Method Not Allowed</h1>
<p>This method is not allowed for the requested URL.</p>
We get the same error if we leave off "dist", as follows:
http://our.domain.name:PORT/swagger.
This also fails if we refer to a static swagger.json file instead of the petshop URL.
All the other namespace-defined endpoints work fine.
Can anyone see what we are doing incorrectly?
Thank you very much in advance.
I stumbled upon the solution. In flask_restplus, any blueprint and #app.route definitions must appear before the flask_restplus.Api instantiation. I couldn't find this in any documentation, and I got it from a discussion that involved someone who had encountered and finally fixed this issue. So, this is indeed a weird and seemingly undocumented interaction between blueprints and namespaces in flask_restplus.
Also, I discovered that url_prefix=SWAGGER_URL needs to be passed to app.register_blueprint along with the blueprint variable, itself.
Once I started doing the following, my swagger queries began to work:
app = flask.Flask(__name__)
# Any #app.route and blueprint definitions must
# appear before the flask_restplus.Api
# instantiation.
SWAGGER_URL = '/swagger'
# Use the following API_URL just for testing ...
API_URL = 'http://petstore.swagger.io/v2/swagger.json'
swaggerui_blueprint = get_swaggerui_blueprint(
SWAGGER_URL,
API_URL,
config={
'app_name': "Test application"
},
)
app.register_blueprint(
swaggerui_blueprint,
url_prefix=SWAGGER_URL
)
# OK. Now we can instantiate flask_restplus.API
api = flask_restplus.Api(
app,
version='1.0',
title='Test Application',
description='Test Application',
doc='/docs/'
)
ns = api.namespace(
'server',
description='Main server',
path='/server'
)
utilns = api.namespace(
'util',
description='Util',
path='/util'
)
# Set up endpoints under the namespaces defined above,
# and do some more initialization. And then ...
app.run()

Change swagger host and port serving it statically

Inspired by this sample repository, I'm generating a swagger output in json with protoc and serving it. However, for certain reasons I'm hosting the swagger content on a different port(:10000) than my REST api service(:8000).
I'm using the Go library statik to bundle up the swagger assets and serve them. It works, and a webpage is served when going to localhost:10000.
However, every cURL request swagger makes seems to be confined to just that - localhost:10000. The REST API lives on localhost:8081.
Serving swagger-ui with static content, how do I change the host/port for the REST api server?
I've tried going into the index.html of the swagger-ui content to add basePath as here, but with no luck. Every request is still made to :10000
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "./service.swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
// I added this, but it did not change anything.
basePath: "localhost:8081"
})
// End Swagger UI call region
window.ui = ui}
</script>
Add host with value localhost:8081
Remove basePath, basePath is used to change the relative path after host i.e if your web server is hosted at /v1/ etc, then you can use basepath to change that
i am still trying to find out how to pass host value dynamically for production, stage, dev env

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

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

Fatfree routing with PHP built-in web server

I'm learning fatfree's route and found it behaves unexpected.
Here is my code in index.php:
$f3 = require_once(dirname(dirname(__FILE__)). '/lib/base.php');
$f3 = \Base::instance();
echo 'received uri: '.$_SERVER['REQUEST_URI'].'<br>';
$f3->route('GET /brew/#count',
function($f3,$params) {
echo $params['count'].' bottles of beer on the wall.';
}
);
$f3->run();
and here is the URL which I access: http://xx.xx.xx.xx:8090/brew/12
I get a 404 error:
received uri: /brew/12
Not Found
HTTP 404 (GET /12)
the strange thing is that the URI in F3 is now "/12" instead of "/brew/12" and I guess this is the issue.
When I check the base.php (3.6.5), $this->hive['BASE'] = "/brew" and $this->hive['PATH'] = "/12".
But if F3 only uses $this->hive['PATH'] to match the predefined route, it won't be able to match them.
If I change the route to:
$f3->route('GET /brew',
and use the URL: http://xx.xx.xx.xx:8090/brew, then the route matches without issue.
In this case, $this->hive['BASE'] = "" and $this->hive['PATH'] = "/brew". If F3 compares the $this->hive['PATH'] with predefined route, they match each other.
BTW, I'm using PHP's built-in web server and since $_SERVER['REQUEST_URI'] (which is used by base.php) returns the correct URI, I don't think there is anything wrong with the URL rewrite in my .htrouter.php.
Any idea? What did I miss here?
add the content of .htrouter.php here
<?php
#get the relative URL
$uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
#if request to a real file (such as a html, image, js, css) then leave it as it is
if ($uri !== '/' && file_exists(__DIR__ . $uri)) {
return false;
}
#if request virtual URL then pass it to the bootstrap file - index.php
$_GET['_url'] = $_SERVER['REQUEST_URI'];
require_once __DIR__ . './public/index.php';
Your issue is directly related to the way you're using the PHP built-in web server.
As stated in the PHP docs, here's how the server handles requests:
URI requests are served from the current working directory where PHP was started, unless the -t option is used to specify an explicit document root. If a URI request does not specify a file, then either index.php or index.html in the given directory are returned. If neither file exists, the lookup for index.php and index.html will be continued in the parent directory and so on until one is found or the document root has been reached. If an index.php or index.html is found, it is returned and $_SERVER['PATH_INFO'] is set to the trailing part of the URI. Otherwise a 404 response code is returned.
If a PHP file is given on the command line when the web server is started it is treated as a "router" script. The script is run at the start of each HTTP request. If this script returns FALSE, then the requested resource is returned as-is. Otherwise the script's output is returned to the browser.
That means that, by default (without a router script), the web server is doing a pretty good job for routing unexisting URIs to your document root index.php file.
In other words, provided your file structure is like:
lib/
base.php
template.php
etc.
public/
index.php
The following command is enough to start your server and dispatch the requests properly to the framework:
php -S 0.0.0.0:8090 -t public/
Or if you're running the command directly from the public/ folder:
cd public
php -S 0.0.0.0:8090
Beware that the working directory of your application depends on the folder from which you call the command. In order to leverage this value, I strongly advise you to add chdir(__DIR__); at the top of your public/index.php file. This way, all subsequent require calls will be relative to your public/ folder. For ex: $f3 = require('../lib/base.php');
Routing file-style URIs
The built-in server, by default, won't pass unexisting file URIs to your index.php, as stated in:
If a URI request does not specify a file, then either index.php or index.html in the given directory are returned
So if you plan to define some routes with dots, such as:
$f3->route('GET /brew.json','Brew->json');
$f3->route('GET /brew.html','Brew->html');
Then it won't work because PHP won't pass the request to index.php.
In that case, you need to call a custom router, such as the .htrouter.php you were trying to use. The only thing is that your .htrouter.php has obviously been designed for a different framework (F3 doesn't care about $_GET['url'] but cares about $_SERVER['SCRIPT_NAME'].
Here's an exemple of .htrouter.php that should work with F3:
// public directory definition
$public_dir=__DIR__.'/public';
// serve existing files as-is
if (file_exists($public_dir.$_SERVER['REQUEST_URI']))
return FALSE;
// patch SCRIPT_NAME and pass the request to index.php
$_SERVER['SCRIPT_NAME']='index.php';
require($public_dir.'/index.php');
NB: the $public_dir variable should be set accordingly to the location of the .htrouter.php file.
For example if you call:
php -S 0.0.0.0:8090 -t public/ .htrouter.php
it should be $public_dir=__DIR__.'/public'.
But if you call:
cd public
php -S 0.0.0.0:8090 .htrouter.php
it should be $public_dir=__DIR__.
OK, I checked the base.php and found out when f3 calculates the base URI, it uses $_SERVER['SCRIPT_NAME'].
$base='';
if (!$cli)
$base=rtrim($this->fixslashes(
dirname($_SERVER['SCRIPT_NAME'])),'/');
if we have web server directly forward all requests to index.php, then
_SERVER['SCRIPT_NAME'] = /index.php, and in this this case, base is ''.
if we use URL rewriting via .htrouter.php to index.php, then
_SERVER['SCRIPT_NAME'] = /brew/12, and in this this case, base is '/brew' which causes the issue.
Since I'm going to use the URL rewrite, I have to comment out the if statement and make sure base =''.
Thanks xfra35 for providing the clue.
Apache like php router here:
It can url rewrite.
https://github.com/kyesil/QPHP/blob/master/router.php
Usage:
php -S localhost:8081 router.php

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!