From f07ab5263246d940fbc1d646bcdb5663398378d0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 11 Dec 2019 15:24:39 +0100 Subject: [PATCH] Add docs --- plugin.go | 7 +++++++ server.go | 8 ++++++-- session.go | 11 ++++++++++- template.go | 4 +++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/plugin.go b/plugin.go index 73f3962..aa964ec 100644 --- a/plugin.go +++ b/plugin.go @@ -8,10 +8,17 @@ import ( const pluginDir = "plugins" +// Plugin extends koushin with additional functionality. type Plugin interface { + // Name should return the plugin name. Name() string + // LoadTemplate populates t with the plugin's functions and templates. LoadTemplate(t *template.Template) error + // SetRoutes populates group with the plugin's routes. SetRoutes(group *echo.Group) + // Inject is called prior to rendering a template. It can extend the + // template data by setting new items in the Extra map. Inject(name string, data interface{}) error + // Close is called when the plugin is unloaded. Close() error } diff --git a/server.go b/server.go index 44392a0..1c9ca73 100644 --- a/server.go +++ b/server.go @@ -14,9 +14,10 @@ const cookieName = "koushin_session" const messagesPerPage = 50 +// Server holds all the koushin server state. type Server struct { Sessions *SessionManager - Plugins []Plugin + Plugins []Plugin imap struct { host string @@ -98,11 +99,13 @@ func newServer(imapURL, smtpURL string) (*Server, error) { type Context struct { echo.Context Server *Server - Session *Session + Session *Session // nil if user isn't logged in } var aLongTimeAgo = time.Unix(233431200, 0) +// SetSession sets a cookie for the provided session. Passing a nil session +// unsets the cookie. func (ctx *Context) SetSession(s *Session) { cookie := http.Cookie{ Name: cookieName, @@ -127,6 +130,7 @@ type Options struct { Theme string } +// New creates a new server. func New(e *echo.Echo, options *Options) error { s, err := newServer(options.IMAPURL, options.SMTPURL) if err != nil { diff --git a/session.go b/session.go index e4e2eb0..1f536eb 100644 --- a/session.go +++ b/session.go @@ -23,8 +23,9 @@ func generateToken() (string, error) { return base64.URLEncoding.EncodeToString(b), nil } -var ErrSessionExpired = errors.New("session expired") +var errSessionExpired = errors.New("session expired") +// AuthError wraps an authentication error. type AuthError struct { cause error } @@ -33,6 +34,7 @@ func (err AuthError) Error() string { return fmt.Sprintf("authentication failed: %v", err.cause) } +// Session is an active user session. It may also hold an IMAP connection. type Session struct { manager *SessionManager username, password string @@ -49,6 +51,8 @@ func (s *Session) ping() { s.pings <- struct{}{} } +// Do executes an IMAP operation on this session. The IMAP client can only be +// used from inside f. func (s *Session) Do(f func(*imapclient.Client) error) error { s.locker.Lock() defer s.locker.Unlock() @@ -65,6 +69,7 @@ func (s *Session) Do(f func(*imapclient.Client) error) error { return f(s.imapConn) } +// Close destroys the session. This can be used to log the user out. func (s *Session) Close() { select { case <-s.closed: @@ -74,6 +79,8 @@ func (s *Session) Close() { } } +// SessionManager keeps track of active sessions. It connects and re-connects +// to the upstream IMAP server as necessary. It prunes expired sessions. type SessionManager struct { newIMAPClient func() (*imapclient.Client, error) @@ -113,6 +120,8 @@ func (sm *SessionManager) get(token string) (*Session, error) { return session, nil } +// Put connects to the IMAP server and creates a new session. If authentication +// fails, the error will be of type AuthError. func (sm *SessionManager) Put(username, password string) (*Session, error) { c, err := sm.connect(username, password) if err != nil { diff --git a/template.go b/template.go index 3bdcc9d..cdcbf66 100644 --- a/template.go +++ b/template.go @@ -21,6 +21,7 @@ type GlobalRenderData struct { Username string // TODO: list of mailboxes + // Additional plugin-specific data Extra map[string]interface{} } @@ -28,7 +29,8 @@ type GlobalRenderData struct { // template-specific fields. type RenderData struct { Global GlobalRenderData - Extra map[string]interface{} + // Additional plugin-specific data + Extra map[string]interface{} } func NewRenderData(ctx *Context) *RenderData {