handlers

package
v0.0.0-...-7e2b890 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 19, 2026 License: MIT Imports: 39 Imported by: 0

Documentation

Overview

Package handlers implements hermes's HTTP handlers: authentication, the chat UI (SSR + HTMX), realtime SSE streams, search, files and settings.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AuthRateLimiter

func AuthRateLimiter() func(http.Handler) http.Handler

AuthRateLimiter is the limiter applied to login/signup POSTs.

Types

type ChannelVM

type ChannelVM struct {
	ID        string
	Name      string // raw IRC name or dm key
	Label     string // display label (channel name without #, or DM partner)
	Kind      string
	IsDM      bool
	Topic     string        // raw text (for the edit field)
	TopicHTML template.HTML // linkified, for display
	Unread    int
	Muted     bool
	Active    bool
	// Channel mode flags (operator settings), for non-DM channels.
	InviteOnly bool
	Moderated  bool
	Secret     bool
	OpTopic    bool
}

ChannelVM is a sidebar/list entry.

type Deps

type Deps struct {
	Cfg     config.Config
	Store   *data.Store
	Views   *views.Renderer
	Session *scs.SessionManager
	Manager *irc.Manager
	Hub     *sse.Hub
	Mailer  auth.Mailer
	Box     *crypto.Box
	Log     *slog.Logger
}

Deps are the dependencies shared by all handlers.

type Handlers

type Handlers struct {
	Deps
}

func New

func New(d Deps) *Handlers

func (*Handlers) Avatar

func (h *Handlers) Avatar(w http.ResponseWriter, r *http.Request)

Avatar serves a user's avatar by nick/username: an uploaded image if they have one, otherwise a deterministic identicon. Non-hermes IRC nicks (no account) always get an identicon.

func (*Handlers) ChangeNick

func (h *Handlers) ChangeNick(w http.ResponseWriter, r *http.Request)

ChangeNick changes the user's IRC nick (and stored username), then reconnects.

func (*Handlers) ChannelStream

func (h *Handlers) ChannelStream(w http.ResponseWriter, r *http.Request)

ChannelStream is the per-channel SSE stream: live messages, presence and topic for the channel currently open in the browser.

func (*Handlers) ChannelView

func (h *Handlers) ChannelView(w http.ResponseWriter, r *http.Request)

ChannelView renders a channel (or DM) pane. Returns just the pane for HTMX navigation, or the full app shell for a direct page load.

func (*Handlers) ChannelsBrowser

func (h *Handlers) ChannelsBrowser(w http.ResponseWriter, r *http.Request)

ChannelsBrowser lists public channels to join.

func (*Handlers) CloseDM

func (h *Handlers) CloseDM(w http.ResponseWriter, r *http.Request)

CloseDM removes the viewer's membership in a direct-message conversation so it drops out of their sidebar. It's non-destructive: the message history stays, and DMing the person again re-opens it.

func (*Handlers) CreateGroup

func (h *Handlers) CreateGroup(w http.ResponseWriter, r *http.Request)

CreateGroup creates an invite-only private channel (a "group DM"), invites the listed nicks, and redirects to it.

func (*Handlers) CreateOrJoinChannel

func (h *Handlers) CreateOrJoinChannel(w http.ResponseWriter, r *http.Request)

CreateOrJoinChannel joins (creating if needed) a channel and redirects to it.

func (*Handlers) DeopUser

func (h *Handlers) DeopUser(w http.ResponseWriter, r *http.Request)

DeopUser removes channel operator from a member.

func (*Handlers) Emojis

func (h *Handlers) Emojis(w http.ResponseWriter, r *http.Request)

Emojis serves the available :shortcode: emoji set as JSON for the composer's client-side autocomplete. Sorted by shortcode for stable ordering.

func (*Handlers) GlobalStream

func (h *Handlers) GlobalStream(w http.ResponseWriter, r *http.Request)

GlobalStream is the per-user SSE stream backing the sidebar: it re-renders the channel/DM lists (with unread badges) whenever anything changes.

func (*Handlers) Home

func (h *Handlers) Home(w http.ResponseWriter, r *http.Request)

Home is the public root. Logged-in users go to their first channel; everyone else sees the marketing landing page.

func (*Handlers) InviteUser

func (h *Handlers) InviteUser(w http.ResponseWriter, r *http.Request)

InviteUser invites a nick to the channel.

func (*Handlers) KickUser

func (h *Handlers) KickUser(w http.ResponseWriter, r *http.Request)

KickUser removes a member from the channel.

func (*Handlers) LeaveChannel

func (h *Handlers) LeaveChannel(w http.ResponseWriter, r *http.Request)

LeaveChannel parts a channel and redirects to the index.

func (*Handlers) LoadMessages

func (h *Handlers) LoadMessages(w http.ResponseWriter, r *http.Request)

LoadMessages returns a page of older messages (scrollback) for prepending.

func (*Handlers) Login

func (h *Handlers) Login(w http.ResponseWriter, r *http.Request)

Login issues a magic link for an existing account.

func (*Handlers) LoginPage

func (h *Handlers) LoginPage(w http.ResponseWriter, r *http.Request)

LoginPage renders the email-entry login form.

func (*Handlers) Logout

func (h *Handlers) Logout(w http.ResponseWriter, r *http.Request)

Logout clears the session.

func (*Handlers) MagicVerify

func (h *Handlers) MagicVerify(w http.ResponseWriter, r *http.Request)

MagicVerify consumes a magic-link token and establishes a session.

func (*Handlers) MarkAllRead

func (h *Handlers) MarkAllRead(w http.ResponseWriter, r *http.Request)

MarkAllRead clears unread badges across all the viewer's channels.

func (*Handlers) MarkRead

func (h *Handlers) MarkRead(w http.ResponseWriter, r *http.Request)

MarkRead advances the viewer's last-read marker to the newest message in a channel, clearing its unread badge.

func (*Handlers) Mentions

func (h *Handlers) Mentions(w http.ResponseWriter, r *http.Request)

Mentions renders a cross-channel list of recent messages that @mention the viewer — the first "Quick View". The store does a coarse LIKE pre-filter; here we refine with the exact mention parser (so "@nickname" doesn't match nick "nick") and render the same compact row as search results.

func (*Handlers) OpUser

func (h *Handlers) OpUser(w http.ResponseWriter, r *http.Request)

OpUser grants channel operator to a member.

func (*Handlers) OpenDM

func (h *Handlers) OpenDM(w http.ResponseWriter, r *http.Request)

OpenDM ensures a DM channel with a nick exists and redirects to it.

func (*Handlers) Profile

func (h *Handlers) Profile(w http.ResponseWriter, r *http.Request)

Profile renders a user's profile card, opened by clicking a nick or avatar in the member list or chat. It works for any IRC nick: registered hermes users show their display name, avatar, presence and custom status; bare IRC nicks get an identicon. An optional ?c=<channelID> adds the user's role (op/voice) in that channel.

func (*Handlers) PushSubscribe

func (h *Handlers) PushSubscribe(w http.ResponseWriter, r *http.Request)

PushSubscribe stores a browser Web Push subscription for this device, so the user can be notified of @mentions / DMs while Hermes is closed.

func (*Handlers) PushUnsubscribe

func (h *Handlers) PushUnsubscribe(w http.ResponseWriter, r *http.Request)

PushUnsubscribe removes a device's push subscription.

func (*Handlers) RemoveAvatar

func (h *Handlers) RemoveAvatar(w http.ResponseWriter, r *http.Request)

RemoveAvatar clears the user's uploaded avatar, reverting to the identicon.

func (*Handlers) RequireAuth

func (h *Handlers) RequireAuth(next http.Handler) http.Handler

RequireAuth loads the session user and rejects unauthenticated requests.

func (*Handlers) Search

func (h *Handlers) Search(w http.ResponseWriter, r *http.Request)

Search runs a full-text search over the user's messages and renders results.

func (*Handlers) SendMessage

func (h *Handlers) SendMessage(w http.ResponseWriter, r *http.Request)

SendMessage sends a chat line; the message is delivered back to the browser via SSE, so this returns 204 and the composer resets client-side.

func (*Handlers) ServeFile

func (h *Handlers) ServeFile(w http.ResponseWriter, r *http.Request)

ServeFile serves a hosted file by id.

func (*Handlers) SetAvatars

func (h *Handlers) SetAvatars(w http.ResponseWriter, r *http.Request)

SetAvatars persists whether avatars are shown in the message and member lists. Like density, the client toggles the data-avatars attribute instantly.

func (*Handlers) SetAway

func (h *Handlers) SetAway(w http.ResponseWriter, r *http.Request)

SetAway toggles the user's AWAY status on IRC.

func (*Handlers) SetChannelMode

func (h *Handlers) SetChannelMode(w http.ResponseWriter, r *http.Request)

SetChannelMode toggles a channel mode (operator only).

func (*Handlers) SetDND

func (h *Handlers) SetDND(w http.ResponseWriter, r *http.Request)

SetDND toggles the user's Do Not Disturb flag. Unlike availability (away), DND is about notifications, not IRC presence: it silences sound, in-page desktop notifications and Web Push across all the user's devices, without touching their IRC away state. Account-wide, so it's synced via selfstatus.

func (*Handlers) SetDensity

func (h *Handlers) SetDensity(w http.ResponseWriter, r *http.Request)

SetDensity persists the message density preference ("cozy" or "compact") in the session. Unlike theme, density is pure CSS over the existing DOM, so the client flips the data-density attribute instantly and we just persist here — no page refresh needed.

func (*Handlers) SetPresence

func (h *Handlers) SetPresence(w http.ResponseWriter, r *http.Request)

SetPresence records whether the user's browser tab is focused/visible. A blurred/hidden tab is treated as "not viewing": the open channel keeps accruing an unread badge and @mentions there still notify. Best-effort, 204.

func (*Handlers) SetSendTyping

func (h *Handlers) SetSendTyping(w http.ResponseWriter, r *http.Request)

SetSendTyping persists whether the user broadcasts typing notifications.

func (*Handlers) SetSound

func (h *Handlers) SetSound(w http.ResponseWriter, r *http.Request)

SetSound persists whether this device plays a notification sound for DMs and @mentions. Device-local (session-scoped, like theme/density): notification sound output is a per-device trait, so it deliberately does not sync. Stored only when explicitly turned on, so absence == off (sounds are opt-in).

func (*Handlers) SetStatusText

func (h *Handlers) SetStatusText(w http.ResponseWriter, r *http.Request)

SetStatusText saves the user's freeform custom status.

func (*Handlers) SetTheme

func (h *Handlers) SetTheme(w http.ResponseWriter, r *http.Request)

SetTheme persists the user's appearance preference in the session: "light", "dark", or "system" (follow the OS preference). The page reloads so the new theme applies; for "system" no data-theme is rendered and DaisyUI's prefers-color-scheme media query takes over.

func (*Handlers) SetTopic

func (h *Handlers) SetTopic(w http.ResponseWriter, r *http.Request)

SetTopic updates a channel topic on IRC.

func (*Handlers) Settings

func (h *Handlers) Settings(w http.ResponseWriter, r *http.Request)

Settings renders the profile/preferences page.

func (*Handlers) Signup

func (h *Handlers) Signup(w http.ResponseWriter, r *http.Request)

Signup creates an app account, provisions an Eris IRC account, stores the encrypted SASL credentials, starts the bouncer connection, and emails a magic link.

func (*Handlers) SignupPage

func (h *Handlers) SignupPage(w http.ResponseWriter, r *http.Request)

SignupPage renders the signup form.

func (*Handlers) SyncMessages

func (h *Handlers) SyncMessages(w http.ResponseWriter, r *http.Request)

SyncMessages returns message rows newer than ?after=<id> as standalone live rows for the client to append. This backfills a stream that was asleep (e.g. a backgrounded mobile tab, or a second device), keeping multi-device use in sync without a full reload. Catching up also marks the channel read.

func (*Handlers) ToggleMute

func (h *Handlers) ToggleMute(w http.ResponseWriter, r *http.Request)

ToggleMute mutes/unmutes a channel for the viewer (suppresses its unread badge and notifications).

func (*Handlers) Typing

func (h *Handlers) Typing(w http.ResponseWriter, r *http.Request)

Typing relays the composer's typing state to IRC as an IRCv3 +typing TAGMSG. It's best-effort and fire-and-forget: a 204 regardless, no body. Honours the per-user "send typing" privacy preference.

func (*Handlers) UpdateSettings

func (h *Handlers) UpdateSettings(w http.ResponseWriter, r *http.Request)

UpdateSettings saves the display name.

func (*Handlers) Upload

func (h *Handlers) Upload(w http.ResponseWriter, r *http.Request)

Upload accepts a multipart file, hosts it under the data dir, and posts its URL as a message into the channel (IRC carries the link, not the bytes).

func (*Handlers) UploadAvatar

func (h *Handlers) UploadAvatar(w http.ResponseWriter, r *http.Request)

UploadAvatar stores an uploaded avatar image for the user, replacing their identicon. Only image content types are accepted.

type MemberVM

type MemberVM struct {
	Nick      string
	Op        bool
	Voice     bool
	Away      bool
	AvatarVer int64
}

MemberVM is a roster entry.

type MessageVM

type MessageVM struct {
	ID          string
	Nick        string
	Label       string
	AvatarSeed  string
	AvatarVer   int64
	Body        template.HTML
	Time        int64
	IsAction    bool
	IsEvent     bool
	Mine        bool
	Mentioned   bool   // mentions the viewing user
	Grouped     bool   // continuation of the previous author's run (hide header)
	Divider     string // non-empty => render a date divider before this message
	FirstUnread bool   // first message after the viewer's last-read marker
	Preview     *data.LinkPreview
}

MessageVM is a rendered message row.

type PaneVM

type PaneVM struct {
	Me          *data.User
	Theme       string
	Sound       bool // per-device notification-sound preference (status popup)
	Channel     *ChannelVM
	Messages    []MessageVM
	Members     []MemberVM
	Channels    []ChannelVM // sidebar
	DMs         []ChannelVM // sidebar DMs
	HasMore     bool
	OldestID    string
	HasNewer    bool   // loaded window stops short of the newest message (search jump)
	NewestID    string // newest loaded message (for the "jump to recent" affordance)
	ScrollToID  string // message to centre + highlight on load (search/mention jump)
	CanSetTopic bool   // viewer is a channel operator
}

PaneVM is the data for a channel pane (and the app shell).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL