From 58d0345f802fdcb4754a8fa2dc42f998cb713c4c Mon Sep 17 00:00:00 2001 From: jomaOliver Date: Fri, 28 Nov 2025 14:23:29 -0300 Subject: [PATCH] New feature: Default destination; Allow us to forward requests based only on the path and default destination. --- README.md | 14 ++++++++++ spec/server_spec.cr | 62 +++++++++++++++++++++++++++++++++++++-------- src/server.cr | 25 +++++++++++++++--- 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index be16225..d19c81d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,20 @@ https://udl.fdo.cr/?r=https://reservation.com/restaurants/silvestre This will bypass the limitation of Safari (embedded webview) that doesn't allow your Universal Links to trigger. You should now have a working "open in app" UX. +### Default destination support +If the `DEFAULT_DESTINATION` environment variable is set and no `r` query parameter is provided: + +- A request to the root path: + + - `https://udl.fdo.cr/` will redirect to `DEFAULT_DESTINATION` + +- A request with a path: + + - `https://udl.fdo.cr/restaurants/silvestre` will redirect to `DEFAULT_DESTINATION + "/restaurants/silvestre"` + +The original `?r=` format continues to work as before. If `DEFAULT_DESTINATION` is not set, behavior remains unchanged. + + `https://udl.fdo.cr` is a **public (free to use) UDL Server** instance for anyone to try out and use on your own. It has usage limits (throttling), which should be more than enough for most low-medium traffic websites. If this service adds value to you or your company please consider sponsoring me right here on GitHub. I offer different sponsor tiers too where I will host a private instance without usage limits for ensured reliability. [Read more about this on my profile](https://github.com/sponsors/fdocr) to support the OSS work I do on my free time. diff --git a/spec/server_spec.cr b/spec/server_spec.cr index bd0531c..d28a393 100644 --- a/spec/server_spec.cr +++ b/spec/server_spec.cr @@ -1,5 +1,25 @@ require "./spec_helper" +def with_default_destination(value) + original = ENV["DEFAULT_DESTINATION"]? + ENV["DEFAULT_DESTINATION"] = value + yield +ensure + if original + ENV["DEFAULT_DESTINATION"] = original + else + ENV.delete("DEFAULT_DESTINATION") + end +end + +def without_default_destination + original = ENV["DEFAULT_DESTINATION"]? + ENV.delete("DEFAULT_DESTINATION") + yield +ensure + ENV["DEFAULT_DESTINATION"] = original if original +end + describe "UDL Server" do context "success" do it "redirects to the Target URI parameter if valid" do @@ -16,6 +36,24 @@ describe "UDL Server" do response.headers["Location"].should eq(target_url) end + it "redirects to DEFAULT_DESTINATION when root path has no r parameter and DEFAULT_DESTINATION is set" do + with_default_destination("https://example.com") do + get "/" + + response.status_code.should eq(302) + response.headers["Location"].should eq("https://example.com") + end + end + + it "redirects to DEFAULT_DESTINATION + path when path has no r parameter and DEFAULT_DESTINATION is set" do + with_default_destination("https://example.com") do + get "/about" + + response.status_code.should eq(302) + response.headers["Location"].should eq("https://example.com/about") + end + end + it "populates apple-app-site-association file" do get "/.well-known/apple-app-site-association" @@ -26,20 +64,24 @@ describe "UDL Server" do end context "failure" do - it "renders fallback page if target redirect not provided" do - get "/" + it "renders fallback page if target redirect not provided and DEFAULT_DESTINATION is not set" do + without_default_destination do + get "/" - response.status_code.should eq(200) - response.body.should contain("Something went wrong") - response.body.should contain("Check out the README for more details") + response.status_code.should eq(200) + response.body.should contain("Something went wrong") + response.body.should contain("Check out the README for more details") + end end - it "renders fallback page if requesting any other path" do - get "/about-us" + it "renders fallback page if requesting any other path and DEFAULT_DESTINATION is not set" do + without_default_destination do + get "/about-us" - response.status_code.should eq(200) - response.body.should contain("Something went wrong") - response.body.should contain("Check out the README for more details") + response.status_code.should eq(200) + response.body.should contain("Something went wrong") + response.body.should contain("Check out the README for more details") + end end it "renders fallback page if r parameter is an invalid URL" do diff --git a/src/server.cr b/src/server.cr index f4cae00..f8e53e3 100644 --- a/src/server.cr +++ b/src/server.cr @@ -13,7 +13,19 @@ error_context = "Use the root path instead, i.e. `/?r=TARGET_URL_HERE`" get "/" do |env| begin - target_uri = URI.parse(env.params.query["r"]) + redirect_param = env.params.query["r"]? + + # If no r parameter, redirect to DEFAULT_DESTINATION (only if set) + unless redirect_param + if default_destination = ENV["DEFAULT_DESTINATION"]? + env.redirect default_destination + next + else + raise "Missing redirect parameter" + end + end + + target_uri = URI.parse(redirect_param) # Check that it's a valid URL valid_uri = /https?/ =~ target_uri.scheme && target_uri.host @@ -48,8 +60,15 @@ get "/.well-known/apple-app-site-association" do |env| end get "/*" do |env| - udl_error = "Invalid path `#{env.request.path}` - #{error_context}" - render "src/views/fallback.ecr" + # If DEFAULT_DESTINATION is set, redirect to it + path; otherwise show fallback. + if default_target = ENV["DEFAULT_DESTINATION"]? + path = env.request.path + final_url = default_target.rstrip("/") + "/" + path.lstrip("/") + env.redirect final_url + else + udl_error = "Invalid path `#{env.request.path}` - #{error_context}" + render "src/views/fallback.ecr" + end end error 404 do