OmarGoubail
Hello, I am working on an app and I want to separate admin and user routes to different subdomains, I found an article how-to-serve-multiple-domains-in-a-single-phoenix-app It got me part of the way there, but for some reason I can’t get it working the same way for example in the article:
scope "/", MyAppWeb, host: "music." do
live "/", MusicLive
end
# partial host match - match subdomain `video.`, i.e., matches `video.myapp.com`
scope "/", MyAppWeb, host: "video." do
live "/", VideoLive
end
# partial host match - match subdomain `admin.`, i.e., matches `admin.myapp.com`
scope "/", MyAppWeb, host: "admin." do
live "/", AdminLive, :home
live "/settings", AdminSettingsLive # admin.myapp.com/settings
end
They used “/” in different subdomains, however when I tried to do the same thing, I get a compiler warning this clause cannot match because a previous clause at line 30 always matchesElixir.
# Public routes (no authentication required)
scope "/", AvocatoxWeb do
pipe_through :browser
# Add other public routes here
live "/", Home
end
# Admin routes (authentication required)
scope "/", AvocatoxWeb, host: "admin." do
pipe_through [:browser, :admin]
live "/", Admin.Home
end
It works when I go to http://localhost:4000 and http://admin.localhost:4000, but I get the same home function which is the first one.
I mean instead of this markup in the admin.
def render(assigns) do
~H"""
<div>
<h1>Admin Home</h1>
</div>
"""
end
I get this
def render(assigns) do
~H"""
<div>
<h1>Home</h1>
</div>
"""
end
I am new to elixir and phoenix and I am enjoying them a lot! Hopefully this is just a gap in my understanding. Thanks for your help in Advance.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
dpreston
Try moving your default scope (without a
:host) to the end of your list of scopes. Or, at least, after any scopes that have matching paths.OmarGoubail
It worked! But I am not sure I understand why.
arcyfelix
Generally speaking, you don’t want to rely only on the path, because then a user can type admin and be an admin, but I assume you know that (just checking).
I think it would be the easiest to write a Plug and pipe through it.
BartOtten
Because the route configuration you see is being rewritten to functions, subject to pattern matching. When no ‘:host’ is given, the argument will be a wildcard match.
Example
Now when you visit “admin.domain.com/“ it will match the first function head. That is not what you want.
Changing the order makes the wildcard match come last, which is what you want.
OmarGoubail
I am not sure I understand completely what you mean, but I did write an
ensure_adminplug, which doesn’t allow normal users to log in only admins that have an account with the role admin.I am handling different cases as I go but I think this is secure, no?
arcyfelix
You are on the right track (atleast in my head
).
Here is something that might explain it better than me:
https://thoughtbot.com/blog/redirecting-routes-in-a-phoenix-application-using-plugs
kip
@OmarGoubail, Maybe this will help clarify a little bit:
foo/1orfoo/3where/1and/3is the arity meaning a function with one param, and a function with three params. In Elixir these are different functions.scopemacro resolves to a set of functions with the same name and arity - but different pattern matches.If that clear so far (and by all means comment if it is not clear) then we can now pay our attention to the runtime environment. Lets say we have the following (and this is very unlikely to be what
scopeactually compiles to, but ultimately it does compile to something like this):How does the runtime know which of these function bodies to invoke? It checks each function in turn - in lexical order as written in your code.
In our example if will first check
def plug(:my_plug, conn, scheme, host, path, query). Since the only pattern match here is on the plug name, it will match. And this is the function body that will run.This is basically the source of the
this clause cannot matcherror. The first function body matches everything so it will always be invoked.By moving the “default” function body to the end of the list of function bodies, it will only be invoked if all other function bodies don’t match. Using the example above, we can see that the
plug(:my_plug, conn, scheme, "admin.", path, query)will match if the host isadmin. And therefore that function body will be invoked, not the default one.OmarGoubail
Thank you so much! That helped a lot.
OmarGoubail
Thank you for the resource! Plugs are kind of confusing to me, I understand the basics but am having trouble grasping it fully. But they seem to simplify a lot of work and open a lot of possibilities.
muelthe
Whilst not directly related to your original question, and assuming I’ve understood what you’re aiming to do, I would recommend taking a look at the following with regards to Phoenix and in particular LiveView as far as authentication and authorization checks: Security considerations — Phoenix LiveView v1.2.5