Documentation
¶
Overview ¶
Package handlers implements hermes's HTTP handlers: authentication, the chat UI (SSR + HTMX), realtime SSE streams, search, files and settings.
Index ¶
- func AuthRateLimiter() func(http.Handler) http.Handler
- type ChannelVM
- type Deps
- type Handlers
- func (h *Handlers) Avatar(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) ChangeNick(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) ChannelStream(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) ChannelView(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) ChannelsBrowser(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) CloseDM(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) CreateGroup(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) CreateOrJoinChannel(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) DeopUser(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Emojis(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GlobalStream(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Home(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) InviteUser(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) KickUser(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) LeaveChannel(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) LoadMessages(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Login(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) LoginPage(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Logout(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) MagicVerify(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) MarkAllRead(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) MarkRead(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Mentions(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) OpUser(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) OpenDM(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Profile(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) PushSubscribe(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) PushUnsubscribe(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) RemoveAvatar(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) RequireAuth(next http.Handler) http.Handler
- func (h *Handlers) Search(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SendMessage(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) ServeFile(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetAvatars(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetAway(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetChannelMode(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetDND(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetDensity(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetPresence(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetSendTyping(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetSound(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetStatusText(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetTheme(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SetTopic(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Settings(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Signup(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SignupPage(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SyncMessages(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) ToggleMute(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Typing(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) UpdateSettings(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Upload(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) UploadAvatar(w http.ResponseWriter, r *http.Request)
- type MemberVM
- type MessageVM
- type PaneVM
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
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 (*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 ¶
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 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).