commit 07b9f14059ba33b55b45ff61b78b05470a963068 from: mtmn date: Mon Jun 22 15:58:03 2026 UTC drop custom ansi history, fix config parser commit - d41859a46afd1a54ec1b5c5dac5d8f88104b1b33 commit + 07b9f14059ba33b55b45ff61b78b05470a963068 blob - 38d357f7c440babbac320f31b5cab51bc8194f0b blob + 1b7aebe30f53e1899976c373ef9a2ed6474097ec --- config/runtime.exs +++ config/runtime.exs @@ -11,9 +11,7 @@ if config_env() == :prod do case String.split(line, "=", parts: 2) do [k, v] when byte_size(k) > 0 -> - if String.contains?(v, "$"), - do: acc, - else: Map.put(acc, k, v |> String.trim("\"") |> String.trim("'")) + Map.put(acc, k, v |> String.trim("\"") |> String.trim("'")) _ -> acc blob - 2a618c27dce7829a505dde17a087acf12fd0164a blob + ffc6b643e6de43cd73fda7e826f7066d716193eb --- lib/shortener/db.ex +++ lib/shortener/db.ex @@ -47,43 +47,27 @@ defmodule Shirts.DB do GenServer.call(__MODULE__, :list) end - # --- server callbacks --- - @impl true def handle_call({:insert, code, url}, _from, conn) do result = - case Exqlite.Sqlite3.prepare(conn, "INSERT INTO shirts (code, url) VALUES (?1, ?2)") do - {:ok, stmt} -> - r = - with :ok <- Exqlite.Sqlite3.bind(stmt, [code, url]), - :done <- Exqlite.Sqlite3.step(conn, stmt) do - :ok - else - {:error, reason} -> {:error, reason} - end + with_stmt(conn, "INSERT INTO shirts (code, url) VALUES (?1, ?2)", fn stmt -> + with :ok <- Exqlite.Sqlite3.bind(stmt, [code, url]), + :done <- Exqlite.Sqlite3.step(conn, stmt) do + :ok + end + end) - _ = Exqlite.Sqlite3.release(conn, stmt) - r - - {:error, reason} -> - {:error, reason} - end - {:reply, result, conn} end def handle_call({:lookup, code}, _from, conn) do result = - case Exqlite.Sqlite3.prepare(conn, "SELECT url FROM shirts WHERE code = ?1") do - {:ok, stmt} -> - r = do_lookup(conn, stmt, code) - _ = Exqlite.Sqlite3.release(conn, stmt) - r + with_stmt(conn, "SELECT url FROM shirts WHERE code = ?1", fn stmt -> + with :ok <- Exqlite.Sqlite3.bind(stmt, [code]) do + lookup_step(conn, stmt) + end + end) - {:error, reason} -> - {:error, reason} - end - {:reply, result, conn} end @@ -91,40 +75,40 @@ defmodule Shirts.DB do sql = "SELECT code, url, created_at FROM shirts ORDER BY created_at ASC" result = - case Exqlite.Sqlite3.prepare(conn, sql) do - {:ok, stmt} -> - r = - case Exqlite.Sqlite3.fetch_all(conn, stmt) do - {:ok, rows} -> - {:ok, - Enum.map(rows, fn [code, url, created_at] -> - %{code: code, url: url, created_at: created_at} - end)} + with_stmt(conn, sql, fn stmt -> + with {:ok, rows} <- Exqlite.Sqlite3.fetch_all(conn, stmt) do + {:ok, + Enum.map(rows, fn [code, url, created_at] -> + %{code: code, url: url, created_at: created_at} + end)} + end + end) - {:error, reason} -> - {:error, reason} - end - - _ = Exqlite.Sqlite3.release(conn, stmt) - r - - {:error, reason} -> - {:error, reason} - end - {:reply, result, conn} end - defp do_lookup(conn, stmt, code) do - with :ok <- Exqlite.Sqlite3.bind(stmt, [code]) do - case Exqlite.Sqlite3.step(conn, stmt) do - {:row, [url]} -> {:ok, url} - :done -> :not_found - {:error, reason} -> {:error, reason} - end + defp lookup_step(conn, stmt) do + case Exqlite.Sqlite3.step(conn, stmt) do + {:row, [url]} -> {:ok, url} + :done -> :not_found + {:error, reason} -> {:error, reason} end end + defp with_stmt(conn, sql, fun) do + case Exqlite.Sqlite3.prepare(conn, sql) do + {:ok, stmt} -> + try do + fun.(stmt) + after + Exqlite.Sqlite3.release(conn, stmt) + end + + {:error, reason} -> + {:error, reason} + end + end + @impl true def terminate(_reason, conn) do Logger.info("Closing database connection") blob - 1c8962d6361212cacd06b49b659ce6b362902368 blob + 092700072553550e2204e3d3e869b0a773467f86 --- lib/shortener/router.ex +++ lib/shortener/router.ex @@ -27,9 +27,7 @@ defmodule Shirts.Router do base_url = Application.fetch_env!(:shirts, :base_url) short_url = "#{base_url}/s/#{code}" - conn - |> put_resp_content_type("application/json") - |> send_resp(201, Jason.encode!(%{short_url: short_url, code: code, url: url})) + json(conn, 201, %{short_url: short_url, code: code, url: url}) else {:error, :unauthorized} -> json(conn, 401, %{error: "unauthorized"}) @@ -51,13 +49,7 @@ defmodule Shirts.Router do get "/history" do with :ok <- check_auth(conn), {:ok, shirts} <- Shirts.DB.list() do - if wants_json?(conn) do - json(conn, 200, %{shirts: shirts, count: length(shirts)}) - else - conn - |> put_resp_content_type("text/plain") - |> send_resp(200, history_table(shirts)) - end + json(conn, 200, %{shirts: shirts, count: length(shirts)}) else {:error, :unauthorized} -> json(conn, 401, %{error: "unauthorized"}) @@ -121,54 +113,6 @@ defmodule Shirts.Router do end end - defp wants_json?(conn) do - get_req_header(conn, "accept") - |> Enum.any?(&String.contains?(&1, "application/json")) - end - - # Renders the link history as a colored ANSI table matching the `shist` script. - @history_columns ["code", "created_at", "url"] - - defp history_table([]), do: "shirts (0)\n" - - defp history_table(shirts) do - base_url = Application.fetch_env!(:shirts, :base_url) - - rows = - Enum.map(shirts, fn s -> - %{"code" => "#{base_url}/s/#{s.code}", "created_at" => s.created_at, "url" => s.url} - end) - - table = - Owl.Table.new(rows, - border_style: :solid_rounded, - padding_x: 1, - sort_columns: fn a, b -> - Enum.find_index(@history_columns, &(&1 == a)) <= - Enum.find_index(@history_columns, &(&1 == b)) - end, - render_cell: [ - header: &(&1 |> Owl.Data.tag(:cyan)), - body: fn - "code", v -> Owl.Data.tag(v, :cyan) - "url", v -> Owl.Data.tag(v, :blue) - "created_at", v -> Owl.Data.tag(v, :light_black) - end - ] - ) - - [ - Owl.Data.tag("shirts", :bright), - " ", - Owl.Data.tag("(#{length(shirts)})", :light_black), - "\n\n", - table, - "\n" - ] - |> Owl.Data.to_chardata() - |> IO.chardata_to_string() - end - defp random_code do :crypto.strong_rand_bytes(6) |> Base.url_encode64(padding: false) blob - 502180943c37ebb3d98511f069129366510f07e9 blob + d5dbeba9d23237432eb43bc1c7fd8ed36722dce5 --- mix.exs +++ mix.exs @@ -25,8 +25,7 @@ defmodule Shirts.MixProject do {:bandit, "~> 1.10"}, {:plug, "~> 1.19"}, {:exqlite, "~> 0.37"}, - {:jason, "~> 1.4"}, - {:owl, "~> 0.13"} + {:jason, "~> 1.4"} ] end end blob - e37e5b91f425fec0a997030fd08e172cde639f91 blob + 57d13f80abb43864c11d780bb759beb1ef72641b --- mix.lock +++ mix.lock @@ -17,7 +17,6 @@ "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, - "owl": {:hex, :owl, "0.13.1", "1ec4a5dea170465f0e90c502c203079224516bc0cbd599281c8667b3c6ef8848", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "351e768af8f2edc575cdaab1a5a2f6d6381be591758a026c701c703145508a0c"}, "plug": {:hex, :plug, "1.19.2", "e4950525b22c6789dfb38a3f95d47171ba159da3fc5a33be9643b43d5e8adb98", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b6fce20a56af5e60fa5dfecf3f907bb98ec981be43c79a3809a499bc3d133de0"}, "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"},