Credo: helpers for upload checks, token listings and the vhost form

18212a4 · Gabe Morell · 2026-11-04 20:39

9 files +72 -52

Files changed

modified lib/oh_my_ex_oh_ircd/images.ex
+13 −14
@@ -241,21 +241,20 @@ defmodule OhMyExOhIrcd.Images do
241 241 content_type = attrs[:content_type] || attrs["content_type"]
242 242 byte_size = attrs[:byte_size] || attrs["byte_size"]
243 243
244 − cond do
245 − content_type not in allowed_types() ->
246 − {:error, :bad_type}
247 −
248 − not is_integer(byte_size) or byte_size <= 0 ->
249 − {:error, :empty}
250 −
251 − byte_size > max_bytes() ->
252 − {:error, :too_large}
244 + with :ok <- validate_upload(content_type, byte_size) do
245 + case Channels.get(channel_name) do
246 + nil -> {:error, :not_registered}
247 + %ChannelRecord{id: id} -> do_store(id, attrs, content_type, source)
248 + end
249 + end
250 + end
253 251
254 − true ->
255 − case Channels.get(channel_name) do
256 − nil -> {:error, :not_registered}
257 − %ChannelRecord{id: id} -> do_store(id, attrs, content_type, source)
258 − end
252 + defp validate_upload(content_type, byte_size) do
253 + cond do
254 + content_type not in allowed_types() -> {:error, :bad_type}
255 + not is_integer(byte_size) or byte_size <= 0 -> {:error, :empty}
256 + byte_size > max_bytes() -> {:error, :too_large}
257 + true -> :ok
259 258 end
260 259 end
261 260
modified lib/oh_my_ex_oh_ircd/irc/img_serv.ex
+7 −5
@@ -423,11 +423,7 @@ defmodule OhMyExOhIrcd.IRC.ImgServ do
423 423 tokens ->
424 424 reply(who, "Active tokens for #{channel}:")
425 425
426 − Enum.each(tokens, fn t ->
427 − used = if t.last_used_at, do: DateTime.to_iso8601(t.last_used_at), else: "never"
428 − label = if t.label, do: " (#{t.label})", else: ""
429 − reply(who, " ##{t.id} as #{t.who}#{label} — last used: #{used}")
430 − end)
426 + Enum.each(tokens, &reply_token_line(who, &1))
431 427 end
432 428 end)
433 429 end
@@ -468,6 +464,12 @@ defmodule OhMyExOhIrcd.IRC.ImgServ do
468 464 state
469 465 end
470 466
467 + defp reply_token_line(who, t) do
468 + used = if t.last_used_at, do: DateTime.to_iso8601(t.last_used_at), else: "never"
469 + label = if t.label, do: " (#{t.label})", else: ""
470 + reply(who, " ##{t.id} as #{t.who}#{label} — last used: #{used}")
471 + end
472 +
471 473 ## Authorization
472 474
473 475 defp with_op(channel, who, state, fun) do
modified lib/oh_my_ex_oh_ircd/irc/scrobble_serv.ex
+7 −5
@@ -425,11 +425,7 @@ defmodule OhMyExOhIrcd.IRC.ScrobbleServ do
425 425 tokens ->
426 426 reply(who, "Active tokens for #{channel}:")
427 427
428 − Enum.each(tokens, fn t ->
429 − used = if t.last_used_at, do: DateTime.to_iso8601(t.last_used_at), else: "never"
430 − label = if t.label, do: " (#{t.label})", else: ""
431 − reply(who, " ##{t.id} as #{t.who}#{label} — last used: #{used}")
432 − end)
428 + Enum.each(tokens, &reply_token_line(who, &1))
433 429 end
434 430 end)
435 431 end
@@ -470,6 +466,12 @@ defmodule OhMyExOhIrcd.IRC.ScrobbleServ do
470 466 state
471 467 end
472 468
469 + defp reply_token_line(who, t) do
470 + used = if t.last_used_at, do: DateTime.to_iso8601(t.last_used_at), else: "never"
471 + label = if t.label, do: " (#{t.label})", else: ""
472 + reply(who, " ##{t.id} as #{t.who}#{label} — last used: #{used}")
473 + end
474 +
473 475 ## Authorization
474 476
475 477 # Run `fun` only if `who` is founder/op of the registered `channel`.
modified lib/oh_my_ex_oh_ircd_web/live/admin_live.ex
+45 −28
@@ -79,7 +79,8 @@ defmodule OhMyExOhIrcdWeb.AdminLive do
79 79
80 80 cond do
81 81 host == "" ->
82 − {:noreply, put_flash(socket, :error, "Enter a host, or use Clear to revert to the cloak.")}
82 + {:noreply,
83 + put_flash(socket, :error, "Enter a host, or use Clear to revert to the cloak.")}
83 84
84 85 not Vhost.valid_host?(host) ->
85 86 {:noreply, put_flash(socket, :error, "Invalid host: #{host}")}
@@ -87,7 +88,8 @@ defmodule OhMyExOhIrcdWeb.AdminLive do
87 88 true ->
88 89 case Vhost.set_live(nick, host, by) do
89 90 :ok ->
90 − {:noreply, socket |> put_flash(:info, "Set host for #{nick} to #{host}.") |> load_users()}
91 + {:noreply,
92 + socket |> put_flash(:info, "Set host for #{nick} to #{host}.") |> load_users()}
91 93
92 94 {:error, :no_such_nick} ->
93 95 {:noreply, put_flash(socket, :error, "#{nick} is not connected.")}
@@ -123,24 +125,7 @@ defmodule OhMyExOhIrcdWeb.AdminLive do
123 125 {:noreply, put_flash(socket, :error, "Invalid host: #{host}")}
124 126
125 127 true ->
126 − case Accounts.set_vhost_by_email(email, new_host) do
127 − {:ok, _user} ->
128 − msg =
129 − if new_host,
130 − do: "Saved vhost for #{email} (applies on next connect).",
131 − else: "Cleared vhost for #{email}."
132 −
133 − {:noreply,
134 − socket
135 − |> put_flash(:info, msg)
136 − |> assign(:vhost_form, vhost_email_form())}
137 −
138 − {:error, :not_found} ->
139 − {:noreply, put_flash(socket, :error, "No account with email #{email}.")}
140 −
141 − {:error, _changeset} ->
142 − {:noreply, put_flash(socket, :error, "Could not save vhost.")}
143 − end
128 + {:noreply, save_vhost_by_email(socket, email, new_host)}
144 129 end
145 130 end
146 131
@@ -275,6 +260,26 @@ defmodule OhMyExOhIrcdWeb.AdminLive do
275 260
276 261 defp vhost_email_form, do: to_form(%{"email" => "", "host" => ""}, as: :vhost)
277 262
263 + defp save_vhost_by_email(socket, email, new_host) do
264 + case Accounts.set_vhost_by_email(email, new_host) do
265 + {:ok, _user} ->
266 + msg =
267 + if new_host,
268 + do: "Saved vhost for #{email} (applies on next connect).",
269 + else: "Cleared vhost for #{email}."
270 +
271 + socket
272 + |> put_flash(:info, msg)
273 + |> assign(:vhost_form, vhost_email_form())
274 +
275 + {:error, :not_found} ->
276 + put_flash(socket, :error, "No account with email #{email}.")
277 +
278 + {:error, _changeset} ->
279 + put_flash(socket, :error, "Could not save vhost.")
280 + end
281 + end
282 +
278 283 defp link_form, do: to_form(%{"slug" => "", "url" => "", "title" => ""}, as: :link)
279 284
280 285 defp load_links(socket), do: assign(socket, :links, Links.list_links())
@@ -375,7 +380,9 @@ defmodule OhMyExOhIrcdWeb.AdminLive do
375 380 defp render_mode_string(flags, key, limit) do
376 381 flag_letters =
377 382 ["i", "m", "n", "f"]
378 − |> Enum.filter(fn l -> MapSet.member?(flags, Channel.flag_from_letter(:binary.first(l))) end)
383 + |> Enum.filter(fn l ->
384 + MapSet.member?(flags, Channel.flag_from_letter(:binary.first(l)))
385 + end)
379 386 |> Enum.join("")
380 387
381 388 "+nt" <> flag_letters <> if(key, do: "k", else: "") <> if(limit, do: "l", else: "")
@@ -475,8 +482,7 @@ defmodule OhMyExOhIrcdWeb.AdminLive do
475 482
476 483 <%= if @links == [] do %>
477 484 <div class="rounded-box border border-base-300 bg-base-200 p-6 text-sm text-base-content/70">
478 − No short links yet. Create one above — it'll redirect from
479 − <code class="font-mono">/s/&lt;slug&gt;</code>.
485 + No short links yet. Create one above — it'll redirect from <code class="font-mono">/s/&lt;slug&gt;</code>.
480 486 </div>
481 487 <% else %>
482 488 <div class="overflow-x-auto rounded-box border border-base-300">
@@ -635,9 +641,10 @@ defmodule OhMyExOhIrcdWeb.AdminLive do
635 641 <div class="flex items-baseline gap-3 flex-wrap">
636 642 <h2 class="text-lg font-mono font-semibold">Users on the network</h2>
637 643 <span class="text-xs text-base-content/60 tabular-nums">
638 − {kind_count(@users, :registered)} registered ·
639 − {kind_count(@users, :anonymous)} anonymous ·
640 − {kind_count(@users, :service)} service
644 + {kind_count(@users, :registered)} registered · {kind_count(@users, :anonymous)} anonymous · {kind_count(
645 + @users,
646 + :service
647 + )} service
641 648 </span>
642 649 </div>
643 650
@@ -793,8 +800,18 @@ defmodule OhMyExOhIrcdWeb.AdminLive do
793 800 </p>
794 801
795 802 <.form for={@vhost_form} phx-submit="set_vhost_email" class="flex flex-wrap items-end gap-3">
796 − <.input field={@vhost_form[:email]} type="text" label="Account email" placeholder="alice@example.com" />
797 − <.input field={@vhost_form[:host]} type="text" label="Host (blank clears)" placeholder="staff/admin" />
803 + <.input
804 + field={@vhost_form[:email]}
805 + type="text"
806 + label="Account email"
807 + placeholder="alice@example.com"
808 + />
809 + <.input
810 + field={@vhost_form[:host]}
811 + type="text"
812 + label="Host (blank clears)"
813 + placeholder="staff/admin"
814 + />
798 815 <button type="submit" class="btn btn-primary">Save vhost</button>
799 816 </.form>
800 817 </section>

Parents: 4bbe618