{"items":[{"children":[{"children":[{"properties":{"category":["caddy","elixir","phoenix","waffle"],"content":[{"html":"<p>A short tutorial how to configure uploads with Phoenix Framework.</p>\n<h2>The problem</h2>\n<p>It&#39;s confusing because there are different system which work with different paths:</p>\n<ul>\n<li><strong>Browser</strong>: https://inhji.de/uploads/files/images/cat.jpg\n<ul>\n<li>This is the public path</li>\n</ul>\n</li>\n<li><strong>Caddy</strong>: /var/www/hajur/uploads/files/images/cat.png\n<ul>\n<li>This is the physical path of the image on disk</li>\n</ul>\n</li>\n<li><strong>Waffle Config</strong>: /var/www/hajur\n<ul>\n<li>This is the <em>base</em> path that all image files should be placed in</li>\n</ul>\n</li>\n<li><strong>Image Uploader</strong>: uploads/files/images\n<ul>\n<li>This is the specific path for this kind of image</li>\n</ul>\n</li>\n</ul>\n<h2>Configuration</h2>\n<h3>Waffle</h3>\n<p>In our <code>config.exs</code>, we define the default path for uploads. This is only used for <em>dev</em> and <em>test</em> environments.</p>\n<pre class=\"lumis\" style=\"color: #abb2bf; background-color: #282c34;\"><code class=\"language-elixir\" translate=\"no\" tabindex=\"0\"><div class=\"line\" data-line=\"1\"><span style=\"color: #61afef;\">config</span> <span style=\"color: #e06c75;\">:waffle</span><span style=\"color: #abb2bf;\">,</span>\n</div><div class=\"line\" data-line=\"2\">  <span style=\"color: #e06c75;\">storage: </span><span style=\"color: #e5c07b;\">Waffle.Storage.Local</span><span style=\"color: #abb2bf;\">,</span>\n</div><div class=\"line\" style=\"background-color: #3b4252;\" data-line=\"3\">  <span style=\"color: #e06c75;\">storage_dir_prefix: </span><span style=\"color: #98c379;\">&quot;priv/waffle/public&quot;</span>\n</div></code></pre>\n<p>For production, we place a <code>UPLOAD_DIR</code> variable in our <code>.env</code> file which will then replace our <code>storage_dir_prefix</code> option:</p>\n<pre class=\"lumis\" style=\"color: #abb2bf; background-color: #282c34;\"><code class=\"language-elixir\" translate=\"no\" tabindex=\"0\"><div class=\"line\" data-line=\"1\"><span style=\"color: #e06c75;\">upload_dir</span> <span style=\"color: #56b6c2;\">=</span>\n</div><div class=\"line\" data-line=\"2\">  <span style=\"color: #e5c07b;\">System</span><span style=\"color: #56b6c2;\">.</span><span style=\"color: #61afef;\">get_env</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #98c379;\">&quot;UPLOAD_DIR&quot;</span><span style=\"color: #c678dd;\">)</span> <span style=\"color: #56b6c2;\">||</span>\n</div><div class=\"line\" data-line=\"3\">    <span style=\"color: #c678dd;\">raise</span> <span style=\"color: #98c379;\">&quot;&quot;&quot;\n</span></div><div class=\"line\" data-line=\"4\">    environment variable UPLOAD_DIR is missing.\n</div><div class=\"line\" data-line=\"5\">    It should look like: /var/www/hajur/files\n</div><div class=\"line\" data-line=\"6\">    &quot;&quot;&quot;\n</div><div class=\"line\" data-line=\"7\">\n</div><div class=\"line\" data-line=\"8\"><span style=\"color: #61afef;\">config</span> <span style=\"color: #e06c75;\">:waffle</span><span style=\"color: #abb2bf;\">,</span>\n</div><div class=\"line\" style=\"background-color: #3b4252;\" data-line=\"9\">  <span style=\"color: #e06c75;\">storage_dir_prefix: </span><span style=\"color: #e06c75;\">upload_dir</span>\n</div></code></pre>\n<h3>Image Uploader</h3>\n<p>I&#39;m dithering all images and also create a smaller version as well as a very small thumbnail. The final filename is created by hashing the filename of the uploaded file. The <code>storage_dir</code> is the thing that needs to match the caddy configuration.</p>\n<pre class=\"lumis\" style=\"color: #abb2bf; background-color: #282c34;\"><code class=\"language-elixir\" translate=\"no\" tabindex=\"0\"><div class=\"line\" data-line=\"1\"><span style=\"color: #c678dd;\">defmodule</span> <span style=\"color: #e5c07b;\">Hajur.Content.ImageUploader</span> <span style=\"color: #c678dd;\">do</span>\n</div><div class=\"line\" data-line=\"2\">  <span style=\"color: #c678dd;\">use</span> <span style=\"color: #e5c07b;\">Waffle.Definition</span>\n</div><div class=\"line\" data-line=\"3\">  <span style=\"color: #c678dd;\">use</span> <span style=\"color: #e5c07b;\">Waffle.Ecto.Definition</span>\n</div><div class=\"line\" data-line=\"4\">\n</div><div class=\"line\" data-line=\"5\">  <span style=\"color: #d19a66;\">@</span><span style=\"color: #d19a66;\">versions </span><span style=\"color: #c678dd;\">[</span><span style=\"color: #e06c75;\">:original</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #e06c75;\">:full</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #e06c75;\">:thumb</span><span style=\"color: #c678dd;\">]</span>\n</div><div class=\"line\" data-line=\"6\">\n</div><div class=\"line\" data-line=\"7\">  <span style=\"color: #7f848e;\"># Whitelist file extensions:</span>\n</div><div class=\"line\" data-line=\"8\">  <span style=\"color: #c678dd;\">def</span> <span style=\"color: #61afef;\">validate</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #c678dd;\">{</span><span style=\"color: #7f848e;\">_file</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #7f848e;\">_post</span><span style=\"color: #c678dd;\">}</span><span style=\"color: #c678dd;\">)</span> <span style=\"color: #c678dd;\">do</span>\n</div><div class=\"line\" data-line=\"9\">    <span style=\"color: #d19a66;\">true</span>\n</div><div class=\"line\" data-line=\"10\">  <span style=\"color: #c678dd;\">end</span>\n</div><div class=\"line\" data-line=\"11\">\n</div><div class=\"line\" data-line=\"12\">  <span style=\"color: #c678dd;\">def</span> <span style=\"color: #61afef;\">transform</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #e06c75;\">:original</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #7f848e;\">_</span><span style=\"color: #c678dd;\">)</span> <span style=\"color: #c678dd;\">do</span>\n</div><div class=\"line\" data-line=\"13\">    <span style=\"color: #c678dd;\">{</span><span style=\"color: #e06c75;\">:magick</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #98c379;\">&quot;-auto-orient -colors 24 -dither FloydSteinberg -format png&quot;</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #e06c75;\">:png</span><span style=\"color: #c678dd;\">}</span>\n</div><div class=\"line\" data-line=\"14\">  <span style=\"color: #c678dd;\">end</span>\n</div><div class=\"line\" data-line=\"15\">\n</div><div class=\"line\" data-line=\"16\">  <span style=\"color: #c678dd;\">def</span> <span style=\"color: #61afef;\">transform</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #e06c75;\">:full</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #7f848e;\">_</span><span style=\"color: #c678dd;\">)</span> <span style=\"color: #c678dd;\">do</span>\n</div><div class=\"line\" data-line=\"17\">    <span style=\"color: #c678dd;\">{</span><span style=\"color: #e06c75;\">:magick</span><span style=\"color: #abb2bf;\">,</span>\n</div><div class=\"line\" data-line=\"18\">     <span style=\"color: #98c379;\">&quot;-auto-orient -resize 1280x1024&gt; -background #ffffff00 -gravity center -extent 1280x1024 -colors 24 -dither FloydSteinberg -format png&quot;</span><span style=\"color: #abb2bf;\">,</span>\n</div><div class=\"line\" data-line=\"19\">     <span style=\"color: #e06c75;\">:png</span><span style=\"color: #c678dd;\">}</span>\n</div><div class=\"line\" data-line=\"20\">  <span style=\"color: #c678dd;\">end</span>\n</div><div class=\"line\" data-line=\"21\">\n</div><div class=\"line\" data-line=\"22\">  <span style=\"color: #c678dd;\">def</span> <span style=\"color: #61afef;\">transform</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #e06c75;\">:thumb</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #7f848e;\">_</span><span style=\"color: #c678dd;\">)</span> <span style=\"color: #c678dd;\">do</span>\n</div><div class=\"line\" data-line=\"23\">    <span style=\"color: #c678dd;\">{</span><span style=\"color: #e06c75;\">:magick</span><span style=\"color: #abb2bf;\">,</span>\n</div><div class=\"line\" data-line=\"24\">     <span style=\"color: #98c379;\">&quot;-auto-orient -resize 250x250&gt; -background #ffffff00 -gravity center -extent 250x250 -colors 24 -dither FloydSteinberg -format png&quot;</span><span style=\"color: #abb2bf;\">,</span>\n</div><div class=\"line\" data-line=\"25\">     <span style=\"color: #e06c75;\">:png</span><span style=\"color: #c678dd;\">}</span>\n</div><div class=\"line\" data-line=\"26\">  <span style=\"color: #c678dd;\">end</span>\n</div><div class=\"line\" data-line=\"27\">\n</div><div class=\"line\" data-line=\"28\">  <span style=\"color: #7f848e;\"># Override the persisted filenames:</span>\n</div><div class=\"line\" data-line=\"29\">  <span style=\"color: #c678dd;\">def</span> <span style=\"color: #61afef;\">filename</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #e06c75;\">version</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #c678dd;\">{</span><span style=\"color: #e06c75;\">file</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #7f848e;\">_post_image</span><span style=\"color: #c678dd;\">}</span> <span style=\"color: #56b6c2;\">=</span> <span style=\"color: #7f848e;\">_scope</span><span style=\"color: #c678dd;\">)</span> <span style=\"color: #c678dd;\">do</span>\n</div><div class=\"line\" data-line=\"30\">    <span style=\"color: #e06c75;\">hash</span> <span style=\"color: #56b6c2;\">=</span> <span style=\"color: #e5c07b;\">:crypto</span><span style=\"color: #56b6c2;\">.</span><span style=\"color: #61afef;\">hash</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #e06c75;\">:sha</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #e06c75;\">file</span><span style=\"color: #56b6c2;\">.</span><span style=\"color: #e06c75;\">file_name</span><span style=\"color: #c678dd;\">)</span> <span style=\"color: #56b6c2;\">|&gt;</span> <span style=\"color: #e5c07b;\">Base</span><span style=\"color: #56b6c2;\">.</span><span style=\"color: #61afef;\">encode16</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #c678dd;\">)</span>\n</div><div class=\"line\" data-line=\"31\">    <span style=\"color: #98c379;\">&quot;</span><span style=\"color: #61afef;\">#{</span><span style=\"color: #e06c75;\">hash</span><span style=\"color: #61afef;\">}</span><span style=\"color: #98c379;\">-</span><span style=\"color: #61afef;\">#{</span><span style=\"color: #e06c75;\">version</span><span style=\"color: #61afef;\">}</span><span style=\"color: #98c379;\">&quot;</span>\n</div><div class=\"line\" data-line=\"32\">  <span style=\"color: #c678dd;\">end</span>\n</div><div class=\"line\" data-line=\"33\">\n</div><div class=\"line\" data-line=\"34\">  <span style=\"color: #7f848e;\"># Override the storage directory:</span>\n</div><div class=\"line\" data-line=\"35\">  <span style=\"color: #c678dd;\">def</span> <span style=\"color: #61afef;\">storage_dir</span><span style=\"color: #c678dd;\">(</span><span style=\"color: #7f848e;\">_version</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #c678dd;\">{</span><span style=\"color: #7f848e;\">_file</span><span style=\"color: #abb2bf;\">,</span> <span style=\"color: #7f848e;\">_post</span><span style=\"color: #c678dd;\">}</span><span style=\"color: #c678dd;\">)</span> <span style=\"color: #c678dd;\">do</span>\n</div><div class=\"line\" style=\"background-color: #3b4252;\" data-line=\"36\">    <span style=\"color: #98c379;\">&quot;uploads/files/images&quot;</span>\n</div><div class=\"line\" data-line=\"37\">  <span style=\"color: #c678dd;\">end</span>\n</div><div class=\"line\" data-line=\"38\"><span style=\"color: #c678dd;\">end</span>\n</div></code></pre>\n<h3>Caddy</h3>\n<p>I tried using rewrite without the wrapping <code>handle</code> block which did not work. In the end, I added the <code>file_server</code> directive. I have a feeling that that might have been the problem all along.</p>\n<p>We are matching for requests which begin with <code>/uploads</code> and rewrite them to the actual directory where uploads are placed by the server. This will rewrite a url like:</p>\n<pre class=\"lumis\" style=\"color: #abb2bf; background-color: #282c34;\"><code class=\"language-elixir\" translate=\"no\" tabindex=\"0\"><div class=\"line\" data-line=\"1\"><span style=\"color: #61afef;\">https</span><span style=\"color: #e06c75;\">://</span><span style=\"color: #c678dd;\">in</span><span style=\"color: #e06c75;\">hji</span><span style=\"color: #56b6c2;\">.</span><span style=\"color: #e06c75;\">de</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">uploads</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">files</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">images</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">somehash</span><span style=\"color: #56b6c2;\">-</span><span style=\"color: #e06c75;\">full</span><span style=\"color: #56b6c2;\">.</span><span style=\"color: #e06c75;\">png</span>\n</div></code></pre>\n<p>to</p>\n<pre class=\"lumis\" style=\"color: #abb2bf; background-color: #282c34;\"><code class=\"language-elixir\" translate=\"no\" tabindex=\"0\"><div class=\"line\" data-line=\"1\"><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">var</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">www</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">hajur</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">uploads</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">files</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">images</span><span style=\"color: #56b6c2;\">/</span><span style=\"color: #e06c75;\">somehash</span><span style=\"color: #56b6c2;\">-</span><span style=\"color: #e06c75;\">full</span><span style=\"color: #56b6c2;\">.</span><span style=\"color: #e06c75;\">png</span>\n</div></code></pre>\n<pre class=\"lumis\" style=\"color: #abb2bf; background-color: #282c34;\"><code class=\"language-caddy\" translate=\"no\" tabindex=\"0\"><div class=\"line\" data-line=\"1\"><span style=\"color: #98c379;\">inhji.de</span> <span style=\"color: #c678dd;\">{</span>\n</div><div class=\"line\" style=\"background-color: #3b4252;\" data-line=\"2\">  <span style=\"color: #61afef;\">@uploads</span> <span style=\"color: #e06c75;\">path</span> <span style=\"color: #e06c75;\">/uploads/*</span>\n</div><div class=\"line\" style=\"background-color: #3b4252;\" data-line=\"3\">  <span style=\"color: #c678dd;\">handle</span> <span style=\"color: #61afef;\">@uploads</span> <span style=\"color: #c678dd;\">{</span>\n</div><div class=\"line\" style=\"background-color: #3b4252;\" data-line=\"4\">    <span style=\"color: #c678dd;\">file_server</span>\n</div><div class=\"line\" style=\"background-color: #3b4252;\" data-line=\"5\">    <span style=\"color: #c678dd;\">rewrite</span> <span style=\"color: #61afef;\">*</span> <span style=\"color: #e06c75;\">/var/www/hajur/{path}</span>\n</div><div class=\"line\" style=\"background-color: #3b4252;\" data-line=\"6\">  <span style=\"color: #c678dd;\">}</span>\n</div><div class=\"line\" data-line=\"7\">\n</div><div class=\"line\" data-line=\"8\">  <span style=\"color: #c678dd;\">reverse_proxy</span> <span style=\"color: #61afef;\">*</span> <span style=\"color: #e06c75;\">localhost:9000</span>\n</div><div class=\"line\" data-line=\"9\"><span style=\"color: #c678dd;\">}</span>\n</div></code></pre>","value":"A short tutorial how to configure uploads with Phoenix Framework.\nThe problem\nIt's confusing because there are different system which work with different paths:\n\nBrowser: https://inhji.de/uploads/files/images/cat.jpg\n\nThis is the public path\n\n\nCaddy: /var/www/hajur/uploads/files/images/cat.png\n\nThis is the physical path of the image on disk\n\n\nWaffle Config: /var/www/hajur\n\nThis is the base path that all image files should be placed in\n\n\nImage Uploader: uploads/files/images\n\nThis is the specific path for this kind of image\n\n\n\nConfiguration\nWaffle\nIn our config.exs, we define the default path for uploads. This is only used for dev and test environments.\nconfig :waffle,\n  storage: Waffle.Storage.Local,\n  storage_dir_prefix: \"priv/waffle/public\"\n\nFor production, we place a UPLOAD_DIR variable in our .env file which will then replace our storage_dir_prefix option:\nupload_dir =\n  System.get_env(\"UPLOAD_DIR\") ||\n    raise \"\"\"\n    environment variable UPLOAD_DIR is missing.\n    It should look like: /var/www/hajur/files\n    \"\"\"\n\nconfig :waffle,\n  storage_dir_prefix: upload_dir\n\nImage Uploader\nI'm dithering all images and also create a smaller version as well as a very small thumbnail. The final filename is created by hashing the filename of the uploaded file. The storage_dir is the thing that needs to match the caddy configuration.\ndefmodule Hajur.Content.ImageUploader do\n  use Waffle.Definition\n  use Waffle.Ecto.Definition\n\n  @versions [:original, :full, :thumb]\n\n  # Whitelist file extensions:\n  def validate({_file, _post}) do\n    true\n  end\n\n  def transform(:original, _) do\n    {:magick, \"-auto-orient -colors 24 -dither FloydSteinberg -format png\", :png}\n  end\n\n  def transform(:full, _) do\n    {:magick,\n     \"-auto-orient -resize 1280x1024> -background #ffffff00 -gravity center -extent 1280x1024 -colors 24 -dither FloydSteinberg -format png\",\n     :png}\n  end\n\n  def transform(:thumb, _) do\n    {:magick,\n     \"-auto-orient -resize 250x250> -background #ffffff00 -gravity center -extent 250x250 -colors 24 -dither FloydSteinberg -format png\",\n     :png}\n  end\n\n  # Override the persisted filenames:\n  def filename(version, {file, _post_image} = _scope) do\n    hash = :crypto.hash(:sha, file.file_name) |> Base.encode16()\n    \"#{hash}-#{version}\"\n  end\n\n  # Override the storage directory:\n  def storage_dir(_version, {_file, _post}) do\n    \"uploads/files/images\"\n  end\nend\n\nCaddy\nI tried using rewrite without the wrapping handle block which did not work. In the end, I added the file_server directive. I have a feeling that that might have been the problem all along.\nWe are matching for requests which begin with /uploads and rewrite them to the actual directory where uploads are placed by the server. This will rewrite a url like:\nhttps://inhji.de/uploads/files/images/somehash-full.png\n\nto\n/var/www/hajur/uploads/files/images/somehash-full.png\n\ninhji.de {\n  @uploads path /uploads/*\n  handle @uploads {\n    file_server\n    rewrite * /var/www/hajur/{path}\n  }\n\n  reverse_proxy * localhost:9000\n}"}],"name":["File Uploads with Phoenix Live View, Waffle and Caddy"],"published":["2026-04-26T23:14:12+02:00"],"summary":["A short tutorial how to configure uploads with Phoenix Framework.\nThe problem\nIt's confusing because there are different system which work with different paths:\n\nBrowser: https://inhji.de/uploads/files/images/cat.jpg\n\nThis is the public path\n\n\nCaddy: /var/www/hajur/uploads/files/images/cat.png\n\nThis is the physical path of the image on disk\n\n\nWaffle Config: /var/www/hajur\n\nThis is the base path that all image files should be placed in\n\n\nImage Uploader: uploads/files/images\n\nThis is the specific path for this kind of image\n\n\n\nConfiguration\nWaffle\nIn our config.exs, we define the default path for uploads. This is only used for dev and test environments.\nconfig :waffle,\n  storage: Waffle.Storage.Local,\n  storage_dir_prefix: \"priv/waffle/public\"\n\nFor production, we place a UPLOAD_DIR variable in our .env file which will then replace our storage_dir_prefix option:\nupload_dir =\n  System.get_env(\"UPLOAD_DIR\") ||\n    raise \"\"\"\n    environment variable UPLOAD_DIR is missing.\n    It should look like: /var/www/hajur/files\n    \"\"\"\n\nconfig :waffle,\n  storage_dir_prefix: upload_dir\n\nImage Uploader\nI'm dithering all images and also create a smaller version as well as a very small thumbnail. The final filename is created by hashing the filename of the uploaded file. The storage_dir is the thing that needs to match the caddy configuration.\ndefmodule Hajur.Content.ImageUploader do\n  use Waffle.Definition\n  use Waffle.Ecto.Definition\n\n  @versions [:original, :full, :thumb]\n\n  # Whitelist file extensions:\n  def validate({_file, _post}) do\n    true\n  end\n\n  def transform(:original, _) do\n    {:magick, \"-auto-orient -colors 24 -dither FloydSteinberg -format png\", :png}\n  end\n\n  def transform(:full, _) do\n    {:magick,\n     \"-auto-orient -resize 1280x1024> -background #ffffff00 -gravity center -extent 1280x1024 -colors 24 -dither FloydSteinberg -format png\",\n     :png}\n  end\n\n  def transform(:thumb, _) do\n    {:magick,\n     \"-auto-orient -resize 250x250> -background #ffffff00 -gravity center -extent 250x250 -colors 24 -dither FloydSteinberg -format png\",\n     :png}\n  end\n\n  # Override the persisted filenames:\n  def filename(version, {file, _post_image} = _scope) do\n    hash = :crypto.hash(:sha, file.file_name) |> Base.encode16()\n    \"#{hash}-#{version}\"\n  end\n\n  # Override the storage directory:\n  def storage_dir(_version, {_file, _post}) do\n    \"uploads/files/images\"\n  end\nend\n\nCaddy\nI tried using rewrite without the wrapping handle block which did not work. In the end, I added the file_server directive. I have a feeling that that might have been the problem all along.\nWe are matching for requests which begin with /uploads and rewrite them to the actual directory where uploads are placed by the server. This will rewrite a url like:\nhttps://inhji.de/uploads/files/images/somehash-full.png\n\nto\n/var/www/hajur/uploads/files/images/somehash-full.png\n\ninhji.de {\n  @uploads path /uploads/*\n  handle @uploads {\n    file_server\n    rewrite * /var/www/hajur/{path}\n  }\n\n  reverse_proxy * localhost:9000\n}"],"updated":["2026-04-27T10:49:41+02:00"],"url":["https://inhji.de/posts/file-uploads-with-phoenix-live-view-waffle-and-caddy/mf2"]},"type":["h-entry"]},{"id":"main-sidebar","properties":{"name":["Jonathan"],"nickname":["inhji"],"note":["Jacked in since 1996."],"url":["https://inhji.de/"]},"type":["h-card"]}],"properties":{},"type":["h-screen"]}],"properties":{},"type":["h-screen"]}],"rel-urls":{"https://aperture.p3k.io/microsub/171":{"rels":["microsub"]},"https://bandcamp.com/inhji":{"rels":["me"],"text":"\n            Bandcamp\n          "},"https://chaos.social/@inhji":{"rels":["me"],"text":"\n            Fediverse\n          "},"https://codeberg.org/inhji":{"rels":["me"],"text":"\n            Codeberg\n          "},"https://git.inhji.de/inhji":{"rels":["me"],"text":"\n            Git Server\n          "},"https://github.com/inhji":{"rels":["me"]},"https://indieauth.com/auth":{"rels":["authorization_endpoint"]},"https://inhji.de/":{"rels":["me"],"text":"\n        Jonathan\n      "},"https://inhji.de/assets/css/app-1f12478d5a9377a7a35f1a48326d8f64.css?vsn=d":{"rels":["stylesheet"]},"https://listenbrainz.org/user/inhji":{"rels":["me"],"text":"\n            ListenBrainz\n          "},"https://musicbrainz.org/user/inhji":{"rels":["me"],"text":"\n            MusicBrainz\n          "},"https://tokens.indieauth.com/token":{"rels":["token_endpoint"]}},"rels":{"authorization_endpoint":["https://indieauth.com/auth"],"me":["https://github.com/inhji","https://inhji.de/","https://bandcamp.com/inhji","https://listenbrainz.org/user/inhji","https://codeberg.org/inhji","https://chaos.social/@inhji","https://musicbrainz.org/user/inhji","https://git.inhji.de/inhji"],"microsub":["https://aperture.p3k.io/microsub/171"],"stylesheet":["https://inhji.de/assets/css/app-1f12478d5a9377a7a35f1a48326d8f64.css?vsn=d"],"token_endpoint":["https://tokens.indieauth.com/token"]}}