diff --git a/README.md b/README.md index e7b9ffe..e075411 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ unnecessary features, or they didn't have all the features I wanted. - Provides a simple API for adding new short links - Counts number of hits for each short link in a privacy respecting way i.e. only the hit is recorded, and nothing else +- Allows setting the URL of your website, in case you want to conveniently generate + short links locally - Links are stored in an SQLite database - Available as a Docker container - Backend written in Java using [Spark Java](http://sparkjava.com/), frontend @@ -80,6 +82,9 @@ export username= export password= # Sets where the database exists. Can be local or remote (optional) export db_url= # Default: './urls.sqlite' +# Sets the url of website, so that it displays that even when accessed +# locally (optional, defaults to hostname you're accessing it on) +export site_url= ``` ### 3. Run it @@ -113,6 +118,17 @@ docker run -p 4567:4567 \ -e db_url=/urls.sqlite \ -d simply-shorten:latest ``` +1.b Further, set the URL of your website (optional) +``` +touch ./urls.sqlite +docker run -p 4567:4567 \ + -e username="username" \ + -e password="password" \ + -v ./urls.sqlite:/urls.sqlite \ + -e db_url=/urls.sqlite \ + -e site_url="https://www.example.com" \ + -d simply-shorten:latest +``` ## Disable authentication As requested in #5, it is possible to completely disable the authentication. diff --git a/docker-compose.yml b/docker-compose.yml index 7b8b3d9..d627a3f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,10 @@ services: # Change if you want to mount the database somewhere else # In this case, you can get rid of the db volume below # - db_url=/urls.sqlite + # Change it in case you want to set the website name + # displayed in front of the shorturls, defaults to + # the hostname you're accessing it from + # - site_url=https://www.example.com - username=admin - password=$3CuReP4S$W0rD volumes: diff --git a/src/main/java/tk/draganczuk/url/App.java b/src/main/java/tk/draganczuk/url/App.java index 6685f89..15f6a1f 100644 --- a/src/main/java/tk/draganczuk/url/App.java +++ b/src/main/java/tk/draganczuk/url/App.java @@ -36,6 +36,7 @@ public class App { get("/all", Routes::getAll); post("/new", Routes::addUrl); delete("/:shortUrl", Routes::delete); + get("/siteUrl", Routes::siteUrl); }); get("/:shortUrl", Routes::goToLongUrl); diff --git a/src/main/java/tk/draganczuk/url/Routes.java b/src/main/java/tk/draganczuk/url/Routes.java index f67de52..1860b2e 100644 --- a/src/main/java/tk/draganczuk/url/Routes.java +++ b/src/main/java/tk/draganczuk/url/Routes.java @@ -42,6 +42,10 @@ public class Routes { } } + public static String siteUrl(Request req, Response res) { + return System.getenv().getOrDefault("site_url", "unset"); + } + public static String goToLongUrl(Request req, Response res) { String shortUrl = req.params("shortUrl"); diff --git a/src/main/resources/public/js/main.js b/src/main/resources/public/js/main.js index 5b4a137..4871067 100644 --- a/src/main/resources/public/js/main.js +++ b/src/main/resources/public/js/main.js @@ -1,3 +1,12 @@ +const siteName = async () => await fetch("/api/siteUrl").then(res => res.text()).then(text => { + if (text == "unset") { + return window.location.host; + } + else { + return text; + } +}); + const refreshData = async () => { let data = await fetch("/api/all").then(res => res.text()); data = data @@ -13,7 +22,8 @@ const refreshData = async () => { displayData(data); }; -const displayData = (data) => { +const displayData = async (data) => { + site = await siteName(); table_box = document.querySelector(".pure-table"); if (data.length == 0) { table_box.style.visibility = "hidden"; @@ -22,8 +32,7 @@ const displayData = (data) => { const table = document.querySelector("#url-table"); table_box.style.visibility = "visible"; table.innerHTML = ''; // Clear - data.map(TR) - .forEach(tr => table.appendChild(tr)); + data.forEach(tr => table.appendChild(TR(tr, site))); } }; @@ -37,10 +46,10 @@ const addAlertBox = async (s, t) => { controls.appendChild(alertBox); }; -const TR = (row) => { +const TR = (row, site) => { const tr = document.createElement("tr"); const longTD = TD(A(row.long)); - const shortTD = TD(A_INT(row.short)); + const shortTD = TD(A_INT(row.short, site)); const hitsTD = TD(row.hits); const btn = deleteButton(row.short); @@ -52,13 +61,14 @@ const TR = (row) => { return tr; }; -const copyShortUrl = (s) => { - navigator.clipboard.writeText(`${window.location.host}/${s}`); +const copyShortUrl = async (s) => { + site = await siteName(); + navigator.clipboard.writeText(`${site}/${s}`); addAlertBox(`Short URL ${s} copied to clipboard!`, "green"); }; const A = (s) => `${s}`; -const A_INT = (s) => `${window.location.host}/${s}`; +const A_INT = (s, t) => `${t}/${s}`; const deleteButton = (shortUrl) => { const td = document.createElement("td"); @@ -99,7 +109,7 @@ const submitForm = () => { method: "POST", body: `${longUrl.value};${shortUrl.value}` }) - .then((res) => { + .then(res => { if (!res.ok) { addAlertBox("Short URL not valid or already in use!", "red"); return "error"; @@ -107,7 +117,7 @@ const submitForm = () => { else { return res.text(); } - }).then((text) => { + }).then(text => { if (text != "error") { copyShortUrl(text); longUrl.value = "";