server

package
v0.10.1 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package server provides a gRPC service implementation for the mailbox library. Users wire it into their own gRPC server:

svc, _ := mailbox.New(cfg, mailbox.WithStore(store))
server, _ := server.New(svc, server.WithSecurityGuard(myGuard))

grpcServer := grpc.NewServer(
    grpc.ChainUnaryInterceptor(
        server.AuthInterceptor(myGuard),
        server.LoggingInterceptor(logger),
        server.RecoveryInterceptor(logger),
    ),
    grpc.ChainStreamInterceptor(
        server.StreamAuthInterceptor(myGuard),
        server.StreamLoggingInterceptor(logger),
        server.StreamRecoveryInterceptor(logger),
    ),
)
mailboxpb.RegisterMailboxServiceServer(grpcServer, server)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AuthInterceptor

func AuthInterceptor(guard SecurityGuard) grpc.UnaryServerInterceptor

AuthInterceptor returns a unary interceptor that authenticates every request using the provided SecurityGuard. On success the Identity is stored in the context so RPC methods can call Authorize without a redundant Authenticate. On failure the request is rejected with codes.Unauthenticated.

func ContextWithIdentity

func ContextWithIdentity(ctx context.Context, id Identity) context.Context

ContextWithIdentity returns a new context carrying id.

func LoggingInterceptor

func LoggingInterceptor(logger *slog.Logger) grpc.UnaryServerInterceptor

LoggingInterceptor returns a unary interceptor that logs each request at Debug level on success and Error level on failure.

func RecoveryInterceptor

func RecoveryInterceptor(logger *slog.Logger) grpc.UnaryServerInterceptor

RecoveryInterceptor returns a unary interceptor that catches panics, logs the stack trace, and returns codes.Internal to the caller.

func StreamAuthInterceptor

func StreamAuthInterceptor(guard SecurityGuard) grpc.StreamServerInterceptor

StreamAuthInterceptor returns a stream interceptor that authenticates every stream using the provided SecurityGuard.

func StreamLoggingInterceptor

func StreamLoggingInterceptor(logger *slog.Logger) grpc.StreamServerInterceptor

StreamLoggingInterceptor returns a stream interceptor that logs each stream.

func StreamRecoveryInterceptor

func StreamRecoveryInterceptor(logger *slog.Logger) grpc.StreamServerInterceptor

StreamRecoveryInterceptor returns a stream interceptor that catches panics.

Types

type Action

type Action string

Action identifies the operation being authorized.

const (
	ActionRead   Action = "read"
	ActionWrite  Action = "write"
	ActionDelete Action = "delete"
	ActionAdmin  Action = "admin"
)

Action constants passed to SecurityGuard.Authorize.

type Decision

type Decision struct {
	Allowed bool
	Reason  string
}

Decision is the outcome of an authorization check.

type Identity

type Identity interface {
	UserID() string
	Claims() map[string]any
}

Identity represents an authenticated caller extracted from incoming RPC metadata.

func IdentityFromContext

func IdentityFromContext(ctx context.Context) (Identity, bool)

IdentityFromContext retrieves the Identity stored by AuthInterceptor.

type Option

type Option func(*serverOptions)

Option configures a Server.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the structured logger used by the server. If l is nil the option is ignored.

func WithMaxBulkSize

func WithMaxBulkSize(n int) Option

WithMaxBulkSize sets the maximum number of message IDs accepted in a single bulk request (BulkDelete, BulkMove, etc.). Requests exceeding this limit are rejected with codes.InvalidArgument. Default: 500.

func WithSecurityGuard

func WithSecurityGuard(g SecurityGuard) Option

WithSecurityGuard sets the SecurityGuard used for authentication and authorization. If g is nil the option is ignored and the default DenyAll guard is kept.

func WithStreamMaxDuration

func WithStreamMaxDuration(d time.Duration) Option

WithStreamMaxDuration sets a hard upper bound on how long a StreamNotifications stream may remain open. After the duration elapses the stream is closed with a normal EOF, forcing clients to reconnect. This prevents half-open connections from holding goroutines indefinitely. A zero or negative duration disables the limit (default: no limit).

type Resource

type Resource struct {
	OwnerID string
}

Resource identifies the mailbox resource being accessed. OwnerID is the user whose mailbox is the target of the operation. An empty OwnerID means the check is method-level only (no specific user).

type SecurityGuard

type SecurityGuard interface {
	// Authenticate extracts and validates the caller's identity from the context.
	// Return a gRPC status error (e.g. codes.Unauthenticated) on failure.
	Authenticate(ctx context.Context) (Identity, error)

	// Authorize checks whether id may perform action on resource.
	// resource.OwnerID is the target mailbox user.
	Authorize(ctx context.Context, id Identity, action Action, resource Resource) (Decision, error)
}

SecurityGuard handles authentication and authorization for every RPC.

Authentication (Authenticate) runs once per RPC inside AuthInterceptor and stores the Identity in the context. Authorization (Authorize) runs inside each RPC method with the specific OwnerID and action being requested.

Typical custom guard:

func (g *myGuard) Authenticate(ctx context.Context) (server.Identity, error) {
    md, _ := metadata.FromIncomingContext(ctx)
    token := md.Get("authorization")[0]
    claims, err := validateJWT(token)
    if err != nil {
        return nil, status.Errorf(codes.Unauthenticated, "invalid token: %v", err)
    }
    return &myIdentity{userID: claims.Sub, claims: claims.Extra}, nil
}

func (g *myGuard) Authorize(_ context.Context, id server.Identity, action server.Action, res server.Resource) (server.Decision, error) {
    // allow users to access only their own mailbox
    if id.UserID() == res.OwnerID {
        return server.Decision{Allowed: true}, nil
    }
    return server.Decision{Allowed: false, Reason: "access denied"}, nil
}

func AllowAll

func AllowAll() SecurityGuard

AllowAll returns a SecurityGuard that authenticates every caller as an anonymous identity and permits all actions. Use only in development or tests.

func DenyAll

func DenyAll() SecurityGuard

DenyAll returns a SecurityGuard whose Authenticate always fails with Unauthenticated. Use as a safe placeholder when no guard is configured.

type Server

type Server struct {
	mailboxpb.UnimplementedMailboxServiceServer
	// contains filtered or unexported fields
}

Server implements the MailboxServiceServer gRPC interface. Use New to create an instance and register it with a grpc.Server:

grpcServer := grpc.NewServer(...)
mailboxpb.RegisterMailboxServiceServer(grpcServer, server)

func New

func New(svc mailbox.Service, opts ...Option) (*Server, error)

New creates a Server backed by svc. The server is ready to be registered with a grpc.Server immediately; no additional lifecycle management is needed. Returns an error if svc is nil.

func (*Server) AddTag

func (s *Server) AddTag(ctx context.Context, req *mailboxpb.TagRequest) (*emptypb.Empty, error)

AddTag adds a tag to a message.

func (*Server) BulkAddTag

BulkAddTag adds a tag to multiple messages.

func (*Server) BulkDelete

BulkDelete moves multiple messages to trash.

func (*Server) BulkMove

BulkMove moves multiple messages to a folder.

func (*Server) BulkPermanentlyDelete

BulkPermanentlyDelete hard-deletes multiple messages from trash.

func (*Server) BulkRemoveTag

BulkRemoveTag removes a tag from multiple messages.

func (*Server) BulkUpdateFlags

BulkUpdateFlags changes the read/archived status of multiple messages.

func (*Server) ComposeDraft

ComposeDraft creates and saves a new draft.

func (*Server) DeleteDraft

func (s *Server) DeleteDraft(ctx context.Context, req *mailboxpb.DeleteDraftRequest) (*emptypb.Empty, error)

DeleteDraft permanently deletes a draft.

func (*Server) DeleteMessage

func (s *Server) DeleteMessage(ctx context.Context, req *mailboxpb.DeleteMessageRequest) (*emptypb.Empty, error)

DeleteMessage moves a message to trash.

func (*Server) GetMessage

GetMessage retrieves a single message by ID.

func (*Server) GetReplies

GetReplies returns direct replies to a message.

func (*Server) GetStats

GetStats returns aggregate statistics for a user's mailbox.

func (*Server) GetThread

GetThread returns all messages in a thread.

func (*Server) ListDrafts

ListDrafts lists all drafts for a user.

func (*Server) ListFolders

ListFolders lists all folders (system and custom) for a user.

func (*Server) ListMessages

ListMessages lists messages in a folder with pagination.

func (*Server) MarkAllRead

MarkAllRead marks all unread messages in a folder as read.

func (*Server) MoveMessage

func (s *Server) MoveMessage(ctx context.Context, req *mailboxpb.MoveMessageRequest) (*emptypb.Empty, error)

MoveMessage moves a message to a different folder.

func (*Server) PermanentlyDeleteMessage

func (s *Server) PermanentlyDeleteMessage(ctx context.Context, req *mailboxpb.PermanentlyDeleteMessageRequest) (*emptypb.Empty, error)

PermanentlyDeleteMessage hard-deletes a message from trash.

func (*Server) RemoveTag

func (s *Server) RemoveTag(ctx context.Context, req *mailboxpb.TagRequest) (*emptypb.Empty, error)

RemoveTag removes a tag from a message.

func (*Server) RestoreMessage

func (s *Server) RestoreMessage(ctx context.Context, req *mailboxpb.RestoreMessageRequest) (*emptypb.Empty, error)

RestoreMessage restores a message from trash.

func (*Server) SearchMessages

SearchMessages performs full-text search across a user's messages.

func (*Server) SendDraft

SendDraft sends an existing draft.

func (*Server) SendMessage

SendMessage sends a message without the draft workflow.

func (*Server) StreamNotifications

StreamNotifications streams real-time notification events for a user. The stream stays open until the client disconnects, the server context is cancelled, or WithStreamMaxDuration elapses (forcing a reconnect cycle). Use last_event_id to replay events missed since the last connection ("" for new events only).

func (*Server) UnreadCount

UnreadCount returns the total number of unread messages for a user.

func (*Server) UpdateDraft

UpdateDraft patches an existing draft. Non-empty string fields replace the current value; empty strings are ignored. Non-empty repeated fields replace the current list; empty slices are ignored. Header and metadata entries are merged into the existing maps.

func (*Server) UpdateFlags

func (s *Server) UpdateFlags(ctx context.Context, req *mailboxpb.UpdateFlagsRequest) (*emptypb.Empty, error)

UpdateFlags changes the read or archived status of a message.

Jump to

Keyboard shortcuts

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