grpc

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Feb 3, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package grpcserver provides the gRPC server implementation for Clonr.

The server exposes repository management operations via gRPC, allowing remote clients to interact with the repository database. It implements the ClonrService defined in the protobuf definitions.

Server Lifecycle

The server is started via NewServer which creates a gRPC server with health checking, logging, recovery, and timeout interceptors:

srv := grpcserver.NewServer(db)
srv.GRPCServer.Serve(listener)

Server Discovery

When the server starts, it writes a server.json file containing connection information (address, port, PID) to the user's cache directory. This allows clients to automatically discover running servers without configuration.

The server info file locations are platform-specific:

  • Windows: %LOCALAPPDATA%\clonr\server.json
  • Linux: ~/.cache/clonr/server.json
  • macOS: ~/Library/Caches/clonr/server.json

Duplicate Prevention

Before starting, the server checks if another instance is already running using IsServerRunning. This function reads the server.json file and verifies the PID is a running clonr process using goprocess.

Interceptors

The server includes three interceptors:

  • Logging: Logs all RPC calls with method name, status, and duration
  • Recovery: Catches panics and converts them to gRPC errors
  • Timeout: Enforces a 30-second timeout on all requests

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoServerInfo indicates no server info file exists
	ErrNoServerInfo = errors.New("no server info file")
)

Functions

func CanceledError

func CanceledError() error

CanceledError returns a standard request canceled error.

func InternalError

func InternalError(msg string) error

InternalError returns an internal error with the given message.

func InternalErrorf

func InternalErrorf(format string, args ...any) error

InternalErrorf returns an internal error with formatted message.

func IsClonrProcessRunning

func IsClonrProcessRunning(pid int) bool

IsClonrProcessRunning checks if a clonr server with the given PID is running. Uses goprocess to verify it's actually a Go process with clonr executable.

func ModelToProtoConfig

func ModelToProtoConfig(cfg *model.Config) *v1.Config

ModelToProtoConfig converts a model.Config to a proto Config

func ModelToProtoProfile

func ModelToProtoProfile(profile *model.Profile) *v1.Profile

ModelToProtoProfile converts a model.Profile to a proto Profile

func ModelToProtoRepository

func ModelToProtoRepository(repo *model.Repository) *v1.Repository

ModelToProtoRepository converts a model.Repository to a proto Repository

func ModelToProtoWorkspace

func ModelToProtoWorkspace(workspace *model.Workspace) *v1.Workspace

ModelToProtoWorkspace converts a model.Workspace to a proto Workspace

func NotFoundError

func NotFoundError(resourceType string) error

NotFoundError returns a standard not found error with the resource type.

func NotNil

func NotNil(value any, fieldName string) error

NotNil validates that a pointer/interface is not nil. Returns nil if valid, or an InvalidArgument error with the field name.

func ProtoToModelConfig

func ProtoToModelConfig(protoCfg *v1.Config) *model.Config

ProtoToModelConfig converts a proto Config to a model.Config

func ProtoToModelProfile

func ProtoToModelProfile(protoProfile *v1.Profile) *model.Profile

ProtoToModelProfile converts a proto Profile to a model.Profile

func ProtoToModelRepository

func ProtoToModelRepository(protoRepo *v1.Repository) model.Repository

ProtoToModelRepository converts a proto Repository to a model.Repository

func ProtoToModelWorkspace

func ProtoToModelWorkspace(protoWorkspace *v1.Workspace) *model.Workspace

ProtoToModelWorkspace converts a proto Workspace to a model.Workspace

func RemoveServerInfo

func RemoveServerInfo()

RemoveServerInfo removes the server info file (called when the server stops)

func RequiredName

func RequiredName(value string) error

RequiredName validates that a name string is not empty. Returns nil if valid, or an InvalidArgument error.

func RequiredOneOf

func RequiredOneOf(fields map[string]string) error

RequiredOneOf validates that at least one of the provided values is not empty. Returns nil if at least one is valid, or an InvalidArgument error listing the field names.

func RequiredPath

func RequiredPath(value string) error

RequiredPath validates that a path string is not empty. Returns nil if valid, or an InvalidArgument error.

func RequiredString

func RequiredString(value, fieldName string) error

RequiredString validates that a string field is not empty. Returns nil if valid, or an InvalidArgument error with the field name.

func RequiredURL

func RequiredURL(value string) error

RequiredURL validates that a URL string is not empty and is a valid URL. Returns nil if valid, or an InvalidArgument error.

func WriteServerInfo

func WriteServerInfo(port int) error

WriteServerInfo writes server information to the local data directory

Types

type IdleTracker

type IdleTracker struct {
	// contains filtered or unexported fields
}

IdleTracker tracks server activity and determines when the server is idle.

func NewIdleTracker

func NewIdleTracker(timeout time.Duration) *IdleTracker

NewIdleTracker creates a new idle tracker with the specified timeout. If timeout is 0, the tracker is disabled (never triggers shutdown).

func (*IdleTracker) IdleTimeout

func (t *IdleTracker) IdleTimeout() time.Duration

IdleTimeout returns the configured idle timeout.

func (*IdleTracker) IsEnabled

func (t *IdleTracker) IsEnabled() bool

IsEnabled returns true if idle timeout is enabled.

func (*IdleTracker) ShutdownChan

func (t *IdleTracker) ShutdownChan() <-chan struct{}

ShutdownChan returns a channel that will be closed when idle timeout is reached.

func (*IdleTracker) Start

func (t *IdleTracker) Start()

Start begins monitoring for idle timeout. This should be called in a goroutine.

func (*IdleTracker) Stop

func (t *IdleTracker) Stop()

Stop stops the idle tracker.

func (*IdleTracker) Touch

func (t *IdleTracker) Touch()

Touch updates the last activity time.

type RotationScheduler

type RotationScheduler struct {
	// contains filtered or unexported fields
}

RotationScheduler periodically checks and auto-rotates expired encryption keys.

func NewRotationScheduler

func NewRotationScheduler(db store.Store, checkInterval, maxAge time.Duration) *RotationScheduler

NewRotationScheduler creates a new rotation scheduler. checkInterval is how often to check for expired keys. maxAge is the maximum age before a key is rotated (0 to disable).

func (*RotationScheduler) Start

func (rs *RotationScheduler) Start()

Start begins the rotation scheduler background task.

func (*RotationScheduler) Stop

func (rs *RotationScheduler) Stop()

Stop gracefully stops the rotation scheduler.

type ServerInfo

type ServerInfo struct {
	Address   string    `json:"address"`
	Port      int       `json:"port"`
	PID       int       `json:"pid"`
	StartedAt time.Time `json:"started_at"`
}

ServerInfo contains information about a running server

func IsServerRunning

func IsServerRunning() *ServerInfo

IsServerRunning checks if a clonr server is already running. Returns the server info if running, nil otherwise. Uses goprocess to verify it's actually a running clonr process.

func ReadServerInfo

func ReadServerInfo() (*ServerInfo, error)

ReadServerInfo reads the server info file if it exists

type ServerWithHealth

type ServerWithHealth struct {
	GRPCServer   *grpc.Server
	HealthServer *health.Server
	IdleTracker  *IdleTracker
}

ServerWithHealth wraps gRPC server and health service for lifecycle management

func NewServer

func NewServer(db store.Store, idleTimeout time.Duration) *ServerWithHealth

NewServer creates a new gRPC server with all interceptors, health service, and registered services. If idleTimeout is > 0, the server will track activity and signal shutdown after being idle.

type Service

type Service struct {
	v1.UnimplementedClonrServiceServer
	// contains filtered or unexported fields
}

Service implements the ClonrServiceServer interface

func NewService

func NewService(db store.Store) *Service

NewService creates a new gRPC service instance

func (*Service) DeleteProfile

DeleteProfile removes a profile by name

func (*Service) DeleteWorkspace

DeleteWorkspace removes a workspace by name

func (*Service) GetActiveProfile

GetActiveProfile retrieves the currently active profile

func (*Service) GetActiveWorkspace

GetActiveWorkspace retrieves the currently active workspace

func (*Service) GetAllRepos

GetAllRepos retrieves all repositories

func (*Service) GetConfig

GetConfig retrieves the application configuration

func (*Service) GetProfile

GetProfile retrieves a profile by name

func (*Service) GetRepos

func (s *Service) GetRepos(_ context.Context, req *v1.GetReposRequest) (*v1.GetReposResponse, error)

GetRepos retrieves repositories with optional filtering

func (*Service) GetReposByWorkspace

GetReposByWorkspace retrieves all repository URLs in a workspace

func (*Service) GetWorkspace

GetWorkspace retrieves a workspace by name

func (*Service) InsertRepoIfNotExists

InsertRepoIfNotExists inserts a repository if it doesn't already exist

func (*Service) ListProfiles

ListProfiles retrieves all profiles

func (*Service) ListWorkspaces

ListWorkspaces retrieves all workspaces

func (*Service) Ping

func (s *Service) Ping(ctx context.Context, req *v1.Empty) (*v1.Empty, error)

Ping verifies database connectivity

func (*Service) ProfileExists

ProfileExists checks if a profile exists by name

func (*Service) RemoveRepoByURL

RemoveRepoByURL removes a repository by URL

func (*Service) RepoExistsByPath

RepoExistsByPath checks if a repository exists by path

func (*Service) RepoExistsByURL

RepoExistsByURL checks if a repository exists by URL

func (*Service) SaveConfig

SaveConfig saves the application configuration

func (*Service) SaveProfile

SaveProfile saves or updates a profile

func (*Service) SaveRepo

func (s *Service) SaveRepo(_ context.Context, req *v1.SaveRepoRequest) (*v1.SaveRepoResponse, error)

SaveRepo saves a repository to the database

func (*Service) SaveWorkspace

SaveWorkspace saves or updates a workspace

func (*Service) SetActiveProfile

SetActiveProfile sets the active profile by name

func (*Service) SetActiveWorkspace

SetActiveWorkspace sets the active workspace by name

func (*Service) SetFavoriteByURL

func (s *Service) SetFavoriteByURL(_ context.Context, req *v1.SetFavoriteRequest) (*v1.SetFavoriteResponse, error)

SetFavoriteByURL marks or unmarks a repository as favorite

func (*Service) UpdateRepoTimestamp

UpdateRepoTimestamp updates the timestamp for a repository

func (*Service) UpdateRepoWorkspace

UpdateRepoWorkspace updates the workspace for a repository

func (*Service) WorkspaceExists

WorkspaceExists checks if a workspace exists by name

Jump to

Keyboard shortcuts

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