| Age | Commit message (Collapse) | Author |
|
Wire an os|memory selector mirroring the existing Redis-vs-file backend
selection. This marks the seam where a future object-store backend (e.g.
Cloudflare R2 / S3) slots in via hotline.WithFileStore.
|
|
Replace the leaky *redis.Client field on Server with a PresenceTracker
interface that receives connect/rename/disconnect lifecycle events. This
removes Redis-specific set encoding from hotline/server.go and the session
handlers, and drops the redis dependency from the hotline package.
The Redis implementation moves to mobius.RedisPresenceTracker, which owns
the legacy "login::ip"/"login:nickname:ip" set encoding so existing
deployments keep working. The API server reads online users through a new
OnlineLister interface and falls back to the in-memory ClientMgr when no
presence tracker is configured. Startup clearing of stale online state now
happens unconditionally when Redis is configured, not only when the API
server is enabled.
|
|
main.go's reloadFunc reached through the server's interface fields with
concrete type assertions (srv.MessageBoard.(*mobius.FlatNews), etc.) to
trigger SIGHUP/API reloads, leaking storage implementation details past
the manager interfaces.
Storage backends now implement a one-method Reloader interface, with
compile-time assertions for FlatNews, BanFile, ThreadedNewsYAML, and
Agreement. BanFile.Load and ThreadedNewsYAML.Load are renamed Reload
for a uniform method set, matching FlatNews's existing convention of
using Reload for the initial load as well.
main.go registers each backend in a named reloader list as it is
constructed, and reloadFunc iterates the list. The banner reload is a
ReloaderFunc that also performs the initial load, and the Redis-backed
ban list simply registers no reloader, replacing the old type-switch
special case.
|
|
ClientConn's mutable session state (Flags, UserName, Icon, IdleTime,
AutoReply) was guarded inconsistently: two mutexes (FlagsMU and mu)
covered some paths while others mutated or read the fields with no
locking at all, including HandleSetClientUserInfo, HandleUpdateUser
(which writes other clients' admin flag), the login flow, the HTTP API
handlers, and the keepalive loop. Consolidate on a single mutex with
accessor methods (SetFlag/IsFlagSet/FlagBytes, SetUserName/GetUserName,
and so on) used by all production code; direct field access remains for
test construction. The idle/away logic moves into incrementIdleTime
and clearIdleAndAway helpers that report whether a notification is
needed, so SendAll is no longer called while holding the lock.
HandleRejectChatInvite also no longer appends to the username slice,
which could write past its length into the backing buffer.
The server banner is now behind Banner/SetBanner with an RWMutex: the
SIGHUP reload previously reassigned the field while banner download
goroutines read it, and nilled it when the file read failed. Reload
now keeps the previous banner on failure.
Per-IP rate limiter entries now record a last-seen time, and the
keepalive ticker evicts entries idle for over seven days, so the map
no longer grows unboundedly with each unique client IP.
|
|
ListenAndServe previously started each listener in a goroutine that
called log.Fatal on any error, which skipped deferred cleanup and made
errors unobservable to callers, and Server.Shutdown terminated the
process with os.Exit. Context cancellation was also ineffective:
Serve only checked ctx between Accept calls, which block indefinitely.
ListenAndServe now binds its listeners up front and returns bind
errors, closes every listener when the context is canceled so accept
loops unblock and return, and reports the first serve loop error to
the caller. Shutdown closes a lazily-initialized channel that cancels
ListenAndServe's context, so the shutdown API works race-free even
though it starts before ListenAndServe. "Server shutting down" is
logged once by ListenAndServe rather than per accept loop, which
produced duplicate or missing lines depending on scheduling.
main.go now treats context.Canceled as a clean exit, logs other server
errors and exits nonzero, and runs deferred cleanup (e.g. Bonjour
shutdown) on the way out.
|
|
Startup log:
- Add interface, port, and fileTransferPort fields to the "Hotline
server started" line so operators can see what the server bound to.
- Resolve an empty -interface flag to 0.0.0.0 for display.
Levels:
- Demote the two Redis startup messages (ban management, cleared online
users) from Info to Debug.
Consistency:
- Standardize the error field key to "err" (was "Err" in a few account
handlers) and lowercase "Account" -> "account".
- Standardize the remote-address key to "remoteAddr" (was "RemoteAddr").
- Replace fmt.Sprintf in the tracker-registration message and string
concatenation in the config-dir-init and ban-disconnect messages with
structured fields; use the standard "err" key.
- Give the two bare rLogger.Error(err.Error()) file-transfer calls a
descriptive message and an "err" field.
logger.go:
- Only attach the rotating lumberjack file writer when --log-file is set.
An empty Filename made lumberjack write to a temp file by default.
|
|
Extract ban logic into a BanMgr interface with two implementations:
- BanFile: file-based YAML storage with support for IP, username, and
nickname bans (backwards-compatible with legacy format)
- RedisBanMgr: Redis-backed implementation with permanent and temporary
ban support, using fail-safe deny-on-error behavior
This replaces scattered Redis calls in API handlers, transaction
handlers, and server connection logic with unified interface calls,
removing the Redis dependency from the API server constructor and
enabling ban functionality for both file-only and Redis deployments.
|
|
|
|
- Add TLSConfig and TLSPort fields to Server struct
- Add WithTLS option function for configuration
- Add ServeWithTLS and ServeFileTransfersWithTLS methods
- Update ListenAndServe to start TLS listeners when configured
- Add -tls-cert, -tls-key, -tls-port command-line flags
- Fix data race in rateLimiters map access with mutex
- Add TLS documentation with certificate generation instructions
|
|
Updated all file close operations to use anonymous functions that ignore
return values to satisfy golangci-lint errcheck warnings.
|
|
- Fix incorrect error message for banner loading
- Convert all error logging to structured format
- Remove server shutdown during config reload failures
|
|
Replace all instances of filepath.Join with path.Join across the codebase
to improve Windows compatibility following the guidance from
https://github.com/golang/go/issues/44305.
Key changes:
- Replaced filepath.Join with path.Join in 14 files
- Updated import statements appropriately
- Resolved variable shadowing issues where function parameters
named 'path' were conflicting with the path package
- Maintained filepath imports where needed for OS-specific functions
like filepath.Walk, filepath.IsAbs, filepath.Dir, and filepath.Base
All tests pass, confirming the changes maintain functionality while
improving cross-platform compatibility.
|
|
- Refactor copyDir: Add proper error handling, resource cleanup with defer,
true recursion, better permissions (0755), and separation of concerns
- Refactor configSearchPaths -> findConfigPath: Add directory validation,
better naming, and clearer documentation
- Add comprehensive test suite for all functions with 100% test coverage
- Remove panic in copyDir, replace with proper error propagation
- Fix resource leaks by using defer for file cleanup
|
|
|
|
|
|
Bonjour doesn't seem happy inside Docker, so I'm making it optional and off by default.
|
|
|
|
|
|
|
|
* Added ability to reload config, agreement, news, and user accounts without restarting the server by sending SIGHUP to the running process
* Added ability to use modern unix or windows line breaks in Agreement.txt and MessageBoard.txt instead of classic MacOS `\r` breaks.
* Extensive refactor towards swappable backends for the active server state
* Extensive refactored towards making the hotline package generic and re-usable for alternate server implemenations
* Fix bug where users whose accounts have been deleted would not be disconnected
|
|
|
|
|
|
|
|
|
|
|
|
* Split CLI client into separate project
* Convert more functions to follow common Golang idioms e.g io.Reader, io.Writer
* Use ldflags for versioning
* Misc cleanup and simplification
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Old behavior:
if config dir found, exit with message
New behavior:
if config dir found, log a message and continue
This simplifies the process for new users.
|
|
|
|
re: #29
TODO: add more stat counters
Usage:
```
./mobius-hotline-server -stats-port 5503
```
```
❯ curl -s localhost:5503 | jq .
{
"LoginCount": 0,
"StartTime": "2022-06-08T20:49:10.183921-07:00",
"DownloadCounter": 0,
"UploadCounter": 0
}
```
|
|
|
|
|
|
|
|
* Removed some unnecessary user of pointers
* Removed dead cruft
* Reorganized code
|
|
|
|
|