neiam
/oh-my-ex-oh-ircd
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
Credo: helpers for upload checks, token listings and the vhost form
18212a4 · Gabe Morell · 2026-11-04 20:39
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/<slug></code>. | |
| 485 | + No short links yet. Create one above — it'll redirect from <code class="font-mono">/s/<slug></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