Files
lemma/server/documentation.md
2024-12-02 21:23:47 +01:00

27 KiB

NovaMD Package Documentation

Generated documentation for all packages in the NovaMD project.

Table of Contents

cmd/server

Package main provides the entry point for the application. It loads the
configuration, initializes the server, and starts the server.

internal/app

package app // import "novamd/internal/app"

Package app provides application-level functionality for initializing and
running the server

FUNCTIONS

func SetupRoutes(r chi.Router, db db.Database, s storage.Manager, authMiddleware *auth.Middleware, sessionService *auth.SessionService)
    SetupRoutes configures the API routes


TYPES

type Server struct {
	// Has unexported fields.
}
    Server represents the HTTP server and its dependencies

func NewServer(cfg *config.Config) (*Server, error)
    NewServer initializes a new server instance with all dependencies

func (s *Server) Close() error
    Close handles graceful shutdown of server dependencies

func (s *Server) Start() error
    Start configures and starts the HTTP server

internal/auth

package auth // import "novamd/internal/auth"

Package auth provides JWT token generation and validation

TYPES

type Claims struct {
	jwt.RegisteredClaims           // Embedded standard JWT claims
	UserID               int       `json:"uid"`  // User identifier
	Role                 string    `json:"role"` // User role (admin, editor, viewer)
	Type                 TokenType `json:"type"` // Token type (access or refresh)
}
    Claims represents the custom claims we store in JWT tokens

type JWTConfig struct {
	SigningKey         string        // Secret key used to sign tokens
	AccessTokenExpiry  time.Duration // How long access tokens are valid
	RefreshTokenExpiry time.Duration // How long refresh tokens are valid
}
    JWTConfig holds the configuration for the JWT service

type JWTManager interface {
	GenerateAccessToken(userID int, role string) (string, error)
	GenerateRefreshToken(userID int, role string) (string, error)
	ValidateToken(tokenString string) (*Claims, error)
	RefreshAccessToken(refreshToken string) (string, error)
}
    JWTManager defines the interface for managing JWT tokens

func NewJWTService(config JWTConfig) (JWTManager, error)
    NewJWTService creates a new JWT service with the provided configuration
    Returns an error if the signing key is missing

type Middleware struct {
	// Has unexported fields.
}
    Middleware handles JWT authentication for protected routes

func NewMiddleware(jwtManager JWTManager) *Middleware
    NewMiddleware creates a new authentication middleware

func (m *Middleware) Authenticate(next http.Handler) http.Handler
    Authenticate middleware validates JWT tokens and sets user information in
    context

func (m *Middleware) RequireRole(role string) func(http.Handler) http.Handler
    RequireRole returns a middleware that ensures the user has the required role

func (m *Middleware) RequireWorkspaceAccess(next http.Handler) http.Handler
    RequireWorkspaceAccess returns a middleware that ensures the user has access
    to the workspace

type SessionService struct {
	// Has unexported fields.
}
    SessionService manages user sessions in the database

func NewSessionService(db db.SessionStore, jwtManager JWTManager) *SessionService
    NewSessionService creates a new session service with the given database and
    JWT manager

func (s *SessionService) CleanExpiredSessions() error
    CleanExpiredSessions removes all expired sessions from the database

func (s *SessionService) CreateSession(userID int, role string) (*models.Session, string, error)
    CreateSession creates a new user session for a user with the given userID
    and role

func (s *SessionService) InvalidateSession(sessionID string) error
    InvalidateSession removes a session with the given sessionID from the
    database

func (s *SessionService) RefreshSession(refreshToken string) (string, error)
    RefreshSession creates a new access token using a refreshToken

type TokenType string
    TokenType represents the type of JWT token (access or refresh)

const (
	AccessToken  TokenType = "access"  // AccessToken - Short-lived token for API access
	RefreshToken TokenType = "refresh" // RefreshToken - Long-lived token for obtaining new access tokens
)

internal/config

package config // import "novamd/internal/config"

Package config provides the configuration for the application

TYPES

type Config struct {
	DBPath            string
	WorkDir           string
	StaticPath        string
	Port              string
	AppURL            string
	CORSOrigins       []string
	AdminEmail        string
	AdminPassword     string
	EncryptionKey     string
	JWTSigningKey     string
	RateLimitRequests int
	RateLimitWindow   time.Duration
	IsDevelopment     bool
}
    Config holds the configuration for the application

func DefaultConfig() *Config
    DefaultConfig returns a new Config instance with default values

func Load() (*Config, error)
    Load creates a new Config instance with values from environment variables

func (c *Config) Validate() error
    Validate checks if the configuration is valid

internal/context

package context // import "novamd/internal/context"

Package context provides functions for managing request context

CONSTANTS

const (
	// HandlerContextKey is the key used to store handler context in the request context
	HandlerContextKey contextKey = "handlerContext"
)

FUNCTIONS

func WithHandlerContext(r *http.Request, hctx *HandlerContext) *http.Request
    WithHandlerContext adds handler context to the request

func WithUserContextMiddleware(next http.Handler) http.Handler
    WithUserContextMiddleware extracts user information from JWT claims and adds
    it to the request context

func WithWorkspaceContextMiddleware(db db.WorkspaceReader) func(http.Handler) http.Handler
    WithWorkspaceContextMiddleware adds workspace information to the request
    context


TYPES

type HandlerContext struct {
	UserID    int
	UserRole  string
	Workspace *models.Workspace // Optional, only set for workspace routes
}
    HandlerContext holds the request-specific data available to all handlers

func GetRequestContext(w http.ResponseWriter, r *http.Request) (*HandlerContext, bool)
    GetRequestContext retrieves the handler context from the request

type UserClaims struct {
	UserID int
	Role   string
}
    UserClaims represents user information from authentication

func GetUserFromContext(ctx context.Context) (*UserClaims, error)
    GetUserFromContext retrieves user claims from the context

internal/db

package db // import "novamd/internal/db"

Package db provides the database access layer for the application. It contains
methods for interacting with the database, such as creating, updating, and
deleting records.

CONSTANTS

const (
	// JWTSecretKey is the key for the JWT secret in the system settings
	JWTSecretKey = "jwt_secret"
)

TYPES

type Database interface {
	UserStore
	WorkspaceStore
	SessionStore
	SystemStore
	Begin() (*sql.Tx, error)
	Close() error
	Migrate() error
}
    Database defines the methods for interacting with the database

func Init(dbPath string, secretsService secrets.Service) (Database, error)
    Init initializes the database connection

type Migration struct {
	Version int
	SQL     string
}
    Migration represents a database migration

type SessionStore interface {
	CreateSession(session *models.Session) error
	GetSessionByRefreshToken(refreshToken string) (*models.Session, error)
	DeleteSession(sessionID string) error
	CleanExpiredSessions() error
}
    SessionStore defines the methods for interacting with jwt sessions in the
    database

type SystemStore interface {
	GetSystemStats() (*UserStats, error)
	EnsureJWTSecret() (string, error)
	GetSystemSetting(key string) (string, error)
	SetSystemSetting(key, value string) error
}
    SystemStore defines the methods for interacting with system settings and
    stats in the database

type UserStats struct {
	TotalUsers      int `json:"totalUsers"`
	TotalWorkspaces int `json:"totalWorkspaces"`
	ActiveUsers     int `json:"activeUsers"` // Users with activity in last 30 days
}
    UserStats represents system-wide statistics

type UserStore interface {
	CreateUser(user *models.User) (*models.User, error)
	GetUserByEmail(email string) (*models.User, error)
	GetUserByID(userID int) (*models.User, error)
	GetAllUsers() ([]*models.User, error)
	UpdateUser(user *models.User) error
	DeleteUser(userID int) error
	UpdateLastWorkspace(userID int, workspaceName string) error
	GetLastWorkspaceName(userID int) (string, error)
	CountAdminUsers() (int, error)
}
    UserStore defines the methods for interacting with user data in the database

type WorkspaceReader interface {
	GetWorkspaceByID(workspaceID int) (*models.Workspace, error)
	GetWorkspaceByName(userID int, workspaceName string) (*models.Workspace, error)
	GetWorkspacesByUserID(userID int) ([]*models.Workspace, error)
	GetAllWorkspaces() ([]*models.Workspace, error)
}
    WorkspaceReader defines the methods for reading workspace data from the
    database

type WorkspaceStore interface {
	WorkspaceReader
	WorkspaceWriter
}
    WorkspaceStore defines the methods for interacting with workspace data in
    the database

type WorkspaceWriter interface {
	CreateWorkspace(workspace *models.Workspace) error
	UpdateWorkspace(workspace *models.Workspace) error
	DeleteWorkspace(workspaceID int) error
	UpdateWorkspaceSettings(workspace *models.Workspace) error
	DeleteWorkspaceTx(tx *sql.Tx, workspaceID int) error
	UpdateLastWorkspaceTx(tx *sql.Tx, userID, workspaceID int) error
	UpdateLastOpenedFile(workspaceID int, filePath string) error
	GetLastOpenedFile(workspaceID int) (string, error)
}
    WorkspaceWriter defines the methods for writing workspace data to the
    database

internal/git

package git // import "novamd/internal/git"

Package git provides functionalities to interact with Git repositories,
including cloning, pulling, committing, and pushing changes.

TYPES

type Client interface {
	Clone() error
	Pull() error
	Commit(message string) error
	Push() error
	EnsureRepo() error
}
    Client defines the interface for Git operations

func New(url, username, token, workDir, commitName, commitEmail string) Client
    New creates a new git Client instance

type Config struct {
	URL         string
	Username    string
	Token       string
	WorkDir     string
	CommitName  string
	CommitEmail string
}
    Config holds the configuration for a Git client

internal/handlers

package handlers // import "novamd/internal/handlers"

Package handlers contains the request handlers for the api routes.

TYPES

type CreateUserRequest struct {
	Email       string          `json:"email"`
	DisplayName string          `json:"displayName"`
	Password    string          `json:"password"`
	Role        models.UserRole `json:"role"`
}
    CreateUserRequest holds the request fields for creating a new user

type DeleteAccountRequest struct {
	Password string `json:"password"`
}
    DeleteAccountRequest represents a user account deletion request

type Handler struct {
	DB      db.Database
	Storage storage.Manager
}
    Handler provides common functionality for all handlers

func NewHandler(db db.Database, s storage.Manager) *Handler
    NewHandler creates a new handler with the given dependencies

func (h *Handler) AdminCreateUser() http.HandlerFunc
    AdminCreateUser creates a new user

func (h *Handler) AdminDeleteUser() http.HandlerFunc
    AdminDeleteUser deletes a specific user

func (h *Handler) AdminGetSystemStats() http.HandlerFunc
    AdminGetSystemStats returns system-wide statistics for admins

func (h *Handler) AdminGetUser() http.HandlerFunc
    AdminGetUser gets a specific user by ID

func (h *Handler) AdminListUsers() http.HandlerFunc
    AdminListUsers returns a list of all users

func (h *Handler) AdminListWorkspaces() http.HandlerFunc
    AdminListWorkspaces returns a list of all workspaces and their stats

func (h *Handler) AdminUpdateUser() http.HandlerFunc
    AdminUpdateUser updates a specific user

func (h *Handler) CreateWorkspace() http.HandlerFunc
    CreateWorkspace creates a new workspace

func (h *Handler) DeleteAccount() http.HandlerFunc
    DeleteAccount handles user account deletion

func (h *Handler) DeleteFile() http.HandlerFunc
    DeleteFile deletes a file

func (h *Handler) DeleteWorkspace() http.HandlerFunc
    DeleteWorkspace deletes the current workspace

func (h *Handler) GetCurrentUser() http.HandlerFunc
    GetCurrentUser returns the currently authenticated user

func (h *Handler) GetFileContent() http.HandlerFunc
    GetFileContent returns the content of a file

func (h *Handler) GetLastOpenedFile() http.HandlerFunc
    GetLastOpenedFile returns the last opened file in the workspace

func (h *Handler) GetLastWorkspaceName() http.HandlerFunc
    GetLastWorkspaceName returns the name of the last opened workspace

func (h *Handler) GetWorkspace() http.HandlerFunc
    GetWorkspace returns the current workspace

func (h *Handler) ListFiles() http.HandlerFunc
    ListFiles returns a list of all files in the workspace

func (h *Handler) ListWorkspaces() http.HandlerFunc
    ListWorkspaces returns a list of all workspaces for the current user

func (h *Handler) Login(authService *auth.SessionService) http.HandlerFunc
    Login handles user authentication and returns JWT tokens

func (h *Handler) Logout(authService *auth.SessionService) http.HandlerFunc
    Logout invalidates the user's session

func (h *Handler) LookupFileByName() http.HandlerFunc
    LookupFileByName returns the paths of files with the given name

func (h *Handler) PullChanges() http.HandlerFunc
    PullChanges pulls changes from the remote repository

func (h *Handler) RefreshToken(authService *auth.SessionService) http.HandlerFunc
    RefreshToken generates a new access token using a refresh token

func (h *Handler) SaveFile() http.HandlerFunc
    SaveFile saves the content of a file

func (h *Handler) StageCommitAndPush() http.HandlerFunc
    StageCommitAndPush stages, commits, and pushes changes to the remote
    repository

func (h *Handler) UpdateLastOpenedFile() http.HandlerFunc
    UpdateLastOpenedFile updates the last opened file in the workspace

func (h *Handler) UpdateLastWorkspaceName() http.HandlerFunc
    UpdateLastWorkspaceName updates the name of the last opened workspace

func (h *Handler) UpdateProfile() http.HandlerFunc
    UpdateProfile updates the current user's profile

func (h *Handler) UpdateWorkspace() http.HandlerFunc
    UpdateWorkspace updates the current workspace

type LoginRequest struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}
    LoginRequest represents a user login request

type LoginResponse struct {
	AccessToken  string          `json:"accessToken"`
	RefreshToken string          `json:"refreshToken"`
	User         *models.User    `json:"user"`
	Session      *models.Session `json:"session"`
}
    LoginResponse represents a user login response

type RefreshRequest struct {
	RefreshToken string `json:"refreshToken"`
}
    RefreshRequest represents a refresh token request

type RefreshResponse struct {
	AccessToken string `json:"accessToken"`
}
    RefreshResponse represents a refresh token response

type StaticHandler struct {
	// Has unexported fields.
}
    StaticHandler serves static files with support for SPA routing and
    pre-compressed files

func NewStaticHandler(staticPath string) *StaticHandler
    NewStaticHandler creates a new StaticHandler with the given static path

func (h *StaticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
    ServeHTTP serves the static files

type SystemStats struct {
	*db.UserStats
	*storage.FileCountStats
}
    SystemStats holds system-wide statistics

type UpdateProfileRequest struct {
	DisplayName     string `json:"displayName"`
	Email           string `json:"email"`
	CurrentPassword string `json:"currentPassword"`
	NewPassword     string `json:"newPassword"`
}
    UpdateProfileRequest represents a user profile update request

type UpdateUserRequest struct {
	Email       string          `json:"email,omitempty"`
	DisplayName string          `json:"displayName,omitempty"`
	Password    string          `json:"password,omitempty"`
	Role        models.UserRole `json:"role,omitempty"`
}
    UpdateUserRequest holds the request fields for updating a user

type WorkspaceStats struct {
	UserID             int       `json:"userID"`
	UserEmail          string    `json:"userEmail"`
	WorkspaceID        int       `json:"workspaceID"`
	WorkspaceName      string    `json:"workspaceName"`
	WorkspaceCreatedAt time.Time `json:"workspaceCreatedAt"`
	*storage.FileCountStats
}
    WorkspaceStats holds workspace statistics

internal/models

package models // import "novamd/internal/models"

Package models contains the data models used throughout the application.
These models are used to represent data in the database, as well as to validate
and serialize data in the application.

TYPES

type Session struct {
	ID           string    // Unique session identifier
	UserID       int       // ID of the user this session belongs to
	RefreshToken string    // The refresh token associated with this session
	ExpiresAt    time.Time // When this session expires
	CreatedAt    time.Time // When this session was created
}
    Session represents a user session in the database

type User struct {
	ID              int       `json:"id" validate:"required,min=1"`
	Email           string    `json:"email" validate:"required,email"`
	DisplayName     string    `json:"displayName"`
	PasswordHash    string    `json:"-"`
	Role            UserRole  `json:"role" validate:"required,oneof=admin editor viewer"`
	CreatedAt       time.Time `json:"createdAt"`
	LastWorkspaceID int       `json:"lastWorkspaceId"`
}
    User represents a user in the system

func (u *User) Validate() error
    Validate validates the user struct

type UserRole string
    UserRole represents the role of a user in the system

const (
	RoleAdmin  UserRole = "admin"
	RoleEditor UserRole = "editor"
	RoleViewer UserRole = "viewer"
)
    User roles

type Workspace struct {
	ID                 int       `json:"id" validate:"required,min=1"`
	UserID             int       `json:"userId" validate:"required,min=1"`
	Name               string    `json:"name" validate:"required"`
	CreatedAt          time.Time `json:"createdAt"`
	LastOpenedFilePath string    `json:"lastOpenedFilePath"`

	// Integrated settings
	Theme                string `json:"theme" validate:"oneof=light dark"`
	AutoSave             bool   `json:"autoSave"`
	ShowHiddenFiles      bool   `json:"showHiddenFiles"`
	GitEnabled           bool   `json:"gitEnabled"`
	GitURL               string `json:"gitUrl" validate:"required_if=GitEnabled true"`
	GitUser              string `json:"gitUser" validate:"required_if=GitEnabled true"`
	GitToken             string `json:"gitToken" validate:"required_if=GitEnabled true"`
	GitAutoCommit        bool   `json:"gitAutoCommit"`
	GitCommitMsgTemplate string `json:"gitCommitMsgTemplate"`
	GitCommitName        string `json:"gitCommitName"`
	GitCommitEmail       string `json:"gitCommitEmail" validate:"omitempty,required_if=GitEnabled true,email"`
}
    Workspace represents a user's workspace in the system

func (w *Workspace) SetDefaultSettings()
    SetDefaultSettings sets the default settings for the workspace

func (w *Workspace) Validate() error
    Validate validates the workspace struct

func (w *Workspace) ValidateGitSettings() error
    ValidateGitSettings validates the git settings if git is enabled

internal/secrets

package secrets // import "novamd/internal/secrets"

Package secrets provides an Encryptor interface for encrypting and decrypting
strings using AES-256-GCM.

FUNCTIONS

func ValidateKey(key string) error
    ValidateKey checks if the provided base64-encoded key is suitable for
    AES-256


TYPES

type Service interface {
	Encrypt(plaintext string) (string, error)
	Decrypt(ciphertext string) (string, error)
}
    Service is an interface for encrypting and decrypting strings

func NewService(key string) (Service, error)
    NewService creates a new Encryptor instance with the provided base64-encoded
    key

internal/storage

package storage // import "novamd/internal/storage"

Package storage provides functionalities to interact with the file system,
including listing files, finding files by name, getting file content, saving
files, and deleting files.

FUNCTIONS

func IsPathValidationError(err error) bool
    IsPathValidationError checks if the error is a PathValidationError


TYPES

type FileCountStats struct {
	TotalFiles int   `json:"totalFiles"`
	TotalSize  int64 `json:"totalSize"`
}
    FileCountStats holds statistics about files in a workspace

type FileManager interface {
	ListFilesRecursively(userID, workspaceID int) ([]FileNode, error)
	FindFileByName(userID, workspaceID int, filename string) ([]string, error)
	GetFileContent(userID, workspaceID int, filePath string) ([]byte, error)
	SaveFile(userID, workspaceID int, filePath string, content []byte) error
	DeleteFile(userID, workspaceID int, filePath string) error
	GetFileStats(userID, workspaceID int) (*FileCountStats, error)
	GetTotalFileStats() (*FileCountStats, error)
}
    FileManager provides functionalities to interact with files in the storage.

type FileNode struct {
	ID       string     `json:"id"`
	Name     string     `json:"name"`
	Path     string     `json:"path"`
	Children []FileNode `json:"children,omitempty"`
}
    FileNode represents a file or directory in the storage.

type Manager interface {
	FileManager
	WorkspaceManager
	RepositoryManager
}
    Manager interface combines all storage interfaces.

type Options struct {
	Fs           fileSystem
	NewGitClient func(url, user, token, path, commitName, commitEmail string) git.Client
}
    Options represents the options for the storage service.

type PathValidationError struct {
	Path    string
	Message string
}
    PathValidationError represents a path validation error (e.g., path traversal
    attempt)

func (e *PathValidationError) Error() string

type RepositoryManager interface {
	SetupGitRepo(userID, workspaceID int, gitURL, gitUser, gitToken, commitName, commitEmail string) error
	DisableGitRepo(userID, workspaceID int)
	StageCommitAndPush(userID, workspaceID int, message string) error
	Pull(userID, workspaceID int) error
}
    RepositoryManager defines the interface for managing Git repositories.

type Service struct {
	RootDir  string
	GitRepos map[int]map[int]git.Client // map[userID]map[workspaceID]*git.Client
	// Has unexported fields.
}
    Service represents the file system structure.

func NewService(rootDir string) *Service
    NewService creates a new Storage instance with the default options and the
    given rootDir root directory.

func NewServiceWithOptions(rootDir string, options Options) *Service
    NewServiceWithOptions creates a new Storage instance with the given options
    and the given rootDir root directory.

func (s *Service) DeleteFile(userID, workspaceID int, filePath string) error
    DeleteFile deletes the file at the given filePath. Path must be a relative
    path within the workspace directory given by userID and workspaceID.

func (s *Service) DeleteUserWorkspace(userID, workspaceID int) error
    DeleteUserWorkspace deletes the workspace directory for the given userID and
    workspaceID.

func (s *Service) DisableGitRepo(userID, workspaceID int)
    DisableGitRepo disables the Git repository for the given userID and
    workspaceID.

func (s *Service) FindFileByName(userID, workspaceID int, filename string) ([]string, error)
    FindFileByName returns a list of file paths that match the given filename.
    Files are searched recursively in the workspace directory and its
    subdirectories. Workspace is identified by the given userID and workspaceID.

func (s *Service) GetFileContent(userID, workspaceID int, filePath string) ([]byte, error)
    GetFileContent returns the content of the file at the given filePath.
    Path must be a relative path within the workspace directory given by userID
    and workspaceID.

func (s *Service) GetFileStats(userID, workspaceID int) (*FileCountStats, error)
    GetFileStats returns the total number of files and related statistics in a
    workspace Workspace is identified by the given userID and workspaceID

func (s *Service) GetTotalFileStats() (*FileCountStats, error)
    GetTotalFileStats returns the total file statistics for the storage.

func (s *Service) GetWorkspacePath(userID, workspaceID int) string
    GetWorkspacePath returns the path to the workspace directory for the given
    userID and workspaceID.

func (s *Service) InitializeUserWorkspace(userID, workspaceID int) error
    InitializeUserWorkspace creates the workspace directory for the given userID
    and workspaceID.

func (s *Service) ListFilesRecursively(userID, workspaceID int) ([]FileNode, error)
    ListFilesRecursively returns a list of all files in the workspace directory
    and its subdirectories. Workspace is identified by the given userID and
    workspaceID.

func (s *Service) Pull(userID, workspaceID int) error
    Pull pulls the changes from the remote Git repository. The git repository
    belongs to the given userID and is associated with the given workspaceID.

func (s *Service) SaveFile(userID, workspaceID int, filePath string, content []byte) error
    SaveFile writes the content to the file at the given filePath. Path must
    be a relative path within the workspace directory given by userID and
    workspaceID.

func (s *Service) SetupGitRepo(userID, workspaceID int, gitURL, gitUser, gitToken, commitName, commitEmail string) error
    SetupGitRepo sets up a Git repository for the given userID and workspaceID.
    The repository is cloned from the given gitURL using the given gitUser and
    gitToken.

func (s *Service) StageCommitAndPush(userID, workspaceID int, message string) error
    StageCommitAndPush stages, commit with the message, and pushes the changes
    to the Git repository. The git repository belongs to the given userID and is
    associated with the given workspaceID.

func (s *Service) ValidatePath(userID, workspaceID int, path string) (string, error)
    ValidatePath validates the if the given path is valid within the workspace
    directory. Workspace directory is defined as the directory for the given
    userID and workspaceID.

type WorkspaceManager interface {
	ValidatePath(userID, workspaceID int, path string) (string, error)
	GetWorkspacePath(userID, workspaceID int) string
	InitializeUserWorkspace(userID, workspaceID int) error
	DeleteUserWorkspace(userID, workspaceID int) error
}
    WorkspaceManager provides functionalities to interact with workspaces in the
    storage.