From 071e99f4dadd4bcba360abffad22a17b496df1e3 Mon Sep 17 00:00:00 2001 From: LordMathis Date: Thu, 23 Oct 2025 19:17:51 +0200 Subject: [PATCH] Remove documentation.md --- server/documentation.md | 1289 --------------------------------------- 1 file changed, 1289 deletions(-) delete mode 100644 server/documentation.md diff --git a/server/documentation.md b/server/documentation.md deleted file mode 100644 index c6451df..0000000 --- a/server/documentation.md +++ /dev/null @@ -1,1289 +0,0 @@ -# Lemma Package Documentation - -Generated documentation for all packages in the Lemma project. - -## Table of Contents -- [cmd/server](#cmd-server) -- [docs](#docs) -- [internal/app](#internal-app) -- [internal/auth](#internal-auth) -- [internal/context](#internal-context) -- [internal/db](#internal-db) -- [internal/git](#internal-git) -- [internal/handlers](#internal-handlers) -- [internal/logging](#internal-logging) -- [internal/models](#internal-models) -- [internal/secrets](#internal-secrets) -- [internal/storage](#internal-storage) -- [internal/testenv](#internal-testenv) - -## cmd/server - -```go -Package main provides the entry point for the application. It loads the -configuration, initializes the server, and starts the server. -``` - -## docs - -```go -package docs // import "lemma/docs" - -Package docs Code generated by swaggo/swag. DO NOT EDIT - -VARIABLES - -var SwaggerInfo = &swag.Spec{ - Version: "1.0", - Host: "", - BasePath: "/api/v1", - Schemes: []string{}, - Title: "Lemma API", - Description: "This is the API for Lemma markdown note taking app.", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", -} - SwaggerInfo holds exported Swagger Info so clients can modify it - -``` - -## internal/app - -```go -package app // import "lemma/internal/app" - -Package app provides application-level functionality for initializing and -running the server - -FUNCTIONS - -func ParseDBURL(dbURL string) (db.DBType, string, error) - ParseDBURL parses a database URL and returns the driver name and data source - - -TYPES - -type Config struct { - DBURL string - DBType db.DBType - WorkDir string - StaticPath string - Port string - Domain string - CORSOrigins []string - AdminEmail string - AdminPassword string - EncryptionKey string - JWTSigningKey string - RateLimitRequests int - RateLimitWindow time.Duration - IsDevelopment bool - LogLevel logging.LogLevel -} - Config holds the configuration for the application - -func DefaultConfig() *Config - DefaultConfig returns a new Config instance with default values - -func LoadConfig() (*Config, error) - LoadConfig creates a new Config instance with values from environment - variables - -func (c *Config) Redact() *Config - Redact redacts sensitive fields from a Config instance - -type Options struct { - Config *Config - Database db.Database - Storage storage.Manager - JWTManager auth.JWTManager - SessionManager auth.SessionManager - CookieService auth.CookieManager -} - Options holds all dependencies and configuration for the server - -func DefaultOptions(cfg *Config) (*Options, error) - DefaultOptions creates server options with default configuration - -type Server struct { - // Has unexported fields. -} - Server represents the HTTP server and its dependencies - -func NewServer(options *Options) *Server - NewServer creates a new server instance with the given options - -func (s *Server) Close() error - Close handles graceful shutdown of server dependencies - -func (s *Server) Router() chi.Router - Router returns the chi router for testing - -func (s *Server) Start() error - Start configures and starts the HTTP server - -``` - -## internal/auth - -```go -package auth // import "lemma/internal/auth" - -Package auth provides JWT token generation and validation - -Package auth provides JWT token generation and validation - -FUNCTIONS - -func NewSessionService(db db.SessionStore, jwtManager JWTManager) *sessionManager - NewSessionService creates a new session service with the given database and - JWT manager revive:disable:unexported-return - - -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 CookieManager interface { - GenerateAccessTokenCookie(token string) *http.Cookie - GenerateRefreshTokenCookie(token string) *http.Cookie - GenerateCSRFCookie(token string) *http.Cookie - InvalidateCookie(cookieType string) *http.Cookie -} - CookieManager interface defines methods for generating cookies - -func NewCookieService(isDevelopment bool, domain string) CookieManager - NewCookieService creates a new cookie service - -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, sessionID string) (string, error) - GenerateRefreshToken(userID int, role string, sessionID string) (string, error) - ValidateToken(tokenString string) (*Claims, 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, sessionManager SessionManager, cookieManager CookieManager) *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 SessionManager interface { - CreateSession(userID int, role string) (*models.Session, string, error) - RefreshSession(refreshToken string) (string, error) - ValidateSession(sessionID string) (*models.Session, error) - InvalidateSession(token string) error - CleanExpiredSessions() error -} - SessionManager is an interface for managing user sessions - -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/context - -```go -package context // import "lemma/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 - -```go -package db // import "lemma/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 DBField struct { - Name string - Value any - Type reflect.Type - OriginalName string - - // Has unexported fields. -} - -func StructTagsToFields(s any) ([]DBField, error) - StructTagsToFields converts a struct to a slice of DBField instances - -type DBType string - -const ( - DBTypeSQLite DBType = "sqlite3" - DBTypePostgres DBType = "postgres" -) -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(dbType DBType, dbURL string, secretsService secrets.Service) (Database, error) - Init initializes the database connection - -type JoinType string - -const ( - InnerJoin JoinType = "INNER JOIN" - LeftJoin JoinType = "LEFT JOIN" - RightJoin JoinType = "RIGHT JOIN" -) -type Query struct { - // Has unexported fields. -} - Query represents a SQL query with its parameters - -func NewQuery(dbType DBType, secretsService secrets.Service) *Query - NewQuery creates a new Query instance - -func (q *Query) AddArgs(args ...any) *Query - AddArgs adds arguments to the query - -func (q *Query) And(condition string) *Query - And adds an AND condition - -func (q *Query) Args() []any - Args returns the query arguments - -func (q *Query) Delete() *Query - Delete starts a DELETE statement - -func (q *Query) EndGroup() *Query - EndGroup ends a parenthetical group - -func (q *Query) From(table string) *Query - From adds a FROM clause - -func (q *Query) GroupBy(columns ...string) *Query - GroupBy adds a GROUP BY clause - -func (q *Query) Having(condition string) *Query - Having adds a HAVING clause for filtering groups - -func (q *Query) Insert(table string, columns ...string) *Query - Insert starts an INSERT statement - -func (q *Query) InsertStruct(s any, table string) (*Query, error) - InsertStruct creates an INSERT query from a struct - -func (q *Query) Join(joinType JoinType, table, condition string) *Query - Join adds a JOIN clause - -func (q *Query) Limit(limit int) *Query - Limit adds a LIMIT clause - -func (q *Query) Offset(offset int) *Query - Offset adds an OFFSET clause - -func (q *Query) Or(condition string) *Query - Or adds an OR condition - -func (q *Query) OrderBy(columns ...string) *Query - OrderBy adds an ORDER BY clause - -func (q *Query) Placeholder(arg any) *Query - Placeholder adds a placeholder for a single argument - -func (q *Query) Placeholders(n int) *Query - Placeholders adds n placeholders separated by commas - -func (q *Query) Returning(columns ...string) *Query - Returning adds a RETURNING clause for both PostgreSQL and SQLite (3.35.0+) - -func (q *Query) Select(columns ...string) *Query - Select adds a SELECT clause - -func (q *Query) SelectStruct(s any, table string) (*Query, error) - SelectStruct creates a SELECT query from a struct - -func (q *Query) Set(column string) *Query - Set adds a SET clause for updates - -func (q *Query) StartGroup() *Query - StartGroup starts a parenthetical group - -func (q *Query) String() string - String returns the formatted query string - -func (q *Query) Update(table string) *Query - Update starts an UPDATE statement - -func (q *Query) UpdateStruct(s any, table string) (*Query, error) - UpdateStruct creates an UPDATE query from a struct - -func (q *Query) Values(count int) *Query - Values adds a VALUES clause - -func (q *Query) Where(condition string) *Query - Where adds a WHERE clause - -func (q *Query) WhereIn(column string, count int) *Query - WhereIn adds a WHERE IN clause - -func (q *Query) Write(s string) *Query - Write adds a string to the query - -type Scanner interface { - Scan(dest ...any) error -} - Scanner is an interface that both sql.Row and sql.Rows satisfy - -type SessionStore interface { - CreateSession(session *models.Session) error - GetSessionByRefreshToken(refreshToken string) (*models.Session, error) - GetSessionByID(sessionID 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 - -```go -package git // import "lemma/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) (CommitHash, 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 CommitHash plumbing.Hash - CommitHash represents a Git commit hash - -func (h CommitHash) String() string - String returns the string representation of the CommitHash - -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 - -```go -package handlers // import "lemma/internal/handlers" - -Package handlers contains the request handlers for the api routes. - -TYPES - -type CommitRequest struct { - Message string `json:"message" example:"Initial commit"` -} - CommitRequest represents a request to commit changes - -type CommitResponse struct { - CommitHash string `json:"commitHash" example:"a1b2c3d4"` -} - CommitResponse represents a response to a commit request - -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 DeleteWorkspaceResponse struct { - NextWorkspaceName string `json:"nextWorkspaceName"` -} - DeleteWorkspaceResponse contains the name of the next workspace after - deleting the current one - -type ErrorResponse struct { - Message string `json:"message"` -} - ErrorResponse is a generic error response - -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 godoc @Summary Create a new user @Description Create a - new user as an admin @Tags Admin @Security CookieAuth @ID adminCreateUser - @Accept json @Produce json @Param user body CreateUserRequest true - "User details" @Success 200 {object} models.User @Failure 400 {object} - ErrorResponse "Invalid request body" @Failure 400 {object} ErrorResponse - "Email, password, and role are required" @Failure 400 {object} ErrorResponse - "Password must be at least 8 characters" @Failure 409 {object} ErrorResponse - "Email already exists" @Failure 500 {object} ErrorResponse "Failed to - hash password" @Failure 500 {object} ErrorResponse "Failed to create user" - @Failure 500 {object} ErrorResponse "Failed to initialize user workspace" - @Router /admin/users [post] - -func (h *Handler) AdminDeleteUser() http.HandlerFunc - AdminDeleteUser godoc @Summary Delete a specific user @Description - Delete a specific user as an admin @Tags Admin @Security CookieAuth @ID - adminDeleteUser @Param userId path int true "User ID" @Success 204 "No - Content" @Failure 400 {object} ErrorResponse "Invalid user ID" @Failure - 400 {object} ErrorResponse "Cannot delete your own account" @Failure 403 - {object} ErrorResponse "Cannot delete other admin users" @Failure 404 - {object} ErrorResponse "User not found" @Failure 500 {object} ErrorResponse - "Failed to delete user" @Router /admin/users/{userId} [delete] - -func (h *Handler) AdminGetSystemStats() http.HandlerFunc - AdminGetSystemStats godoc @Summary Get system statistics @Description Get - system-wide statistics as an admin @Tags Admin @Security CookieAuth @ID - adminGetSystemStats @Produce json @Success 200 {object} SystemStats @Failure - 500 {object} ErrorResponse "Failed to get user stats" @Failure 500 {object} - ErrorResponse "Failed to get file stats" @Router /admin/stats [get] - -func (h *Handler) AdminGetUser() http.HandlerFunc - AdminGetUser godoc @Summary Get a specific user @Description Get a specific - user as an admin @Tags Admin @Security CookieAuth @ID adminGetUser @Produce - json @Param userId path int true "User ID" @Success 200 {object} models.User - @Failure 400 {object} ErrorResponse "Invalid user ID" @Failure 404 {object} - ErrorResponse "User not found" @Router /admin/users/{userId} [get] - -func (h *Handler) AdminListUsers() http.HandlerFunc - AdminListUsers godoc @Summary List all users @Description Returns the list - of all users @Tags Admin @Security CookieAuth @ID adminListUsers @Produce - json @Success 200 {array} models.User @Failure 500 {object} ErrorResponse - "Failed to list users" @Router /admin/users [get] - -func (h *Handler) AdminListWorkspaces() http.HandlerFunc - AdminListWorkspaces godoc @Summary List all workspaces @Description List - all workspaces and their stats as an admin @Tags Admin @Security CookieAuth - @ID adminListWorkspaces @Produce json @Success 200 {array} WorkspaceStats - @Failure 500 {object} ErrorResponse "Failed to list workspaces" @Failure - 500 {object} ErrorResponse "Failed to get user" @Failure 500 {object} - ErrorResponse "Failed to get file stats" @Router /admin/workspaces [get] - -func (h *Handler) AdminUpdateUser() http.HandlerFunc - AdminUpdateUser godoc @Summary Update a specific user @Description - Update a specific user as an admin @Tags Admin @Security CookieAuth @ID - adminUpdateUser @Accept json @Produce json @Param userId path int true - "User ID" @Param user body UpdateUserRequest true "User details" @Success - 200 {object} models.User @Failure 400 {object} ErrorResponse "Invalid user - ID" @Failure 400 {object} ErrorResponse "Invalid request body" @Failure 404 - {object} ErrorResponse "User not found" @Failure 500 {object} ErrorResponse - "Failed to hash password" @Failure 500 {object} ErrorResponse "Failed to - update user" @Router /admin/users/{userId} [put] - -func (h *Handler) CreateWorkspace() http.HandlerFunc - CreateWorkspace godoc @Summary Create workspace @Description Creates a new - workspace @Tags workspaces @ID createWorkspace @Security CookieAuth @Accept - json @Produce json @Param body body models.Workspace true "Workspace" - @Success 200 {object} models.Workspace @Failure 400 {object} ErrorResponse - "Invalid request body" @Failure 400 {object} ErrorResponse "Invalid - workspace" @Failure 500 {object} ErrorResponse "Failed to create workspace" - @Failure 500 {object} ErrorResponse "Failed to initialize workspace - directory" @Failure 500 {object} ErrorResponse "Failed to setup git repo" - @Router /workspaces [post] - -func (h *Handler) DeleteAccount() http.HandlerFunc - DeleteAccount godoc @Summary Delete account @Description Deletes the user's - account and all associated data @Tags users @ID deleteAccount @Security - CookieAuth @Accept json @Produce json @Param body body DeleteAccountRequest - true "Account deletion request" @Success 204 "No Content - Account deleted - successfully" @Failure 400 {object} ErrorResponse "Invalid request body" - @Failure 401 {object} ErrorResponse "Password is incorrect" @Failure 403 - {object} ErrorResponse "Cannot delete the last admin account" @Failure 404 - {object} ErrorResponse "User not found" @Failure 500 {object} ErrorResponse - "Failed to verify admin status" @Failure 500 {object} ErrorResponse "Failed - to delete account" @Router /profile [delete] - -func (h *Handler) DeleteFile() http.HandlerFunc - DeleteFile godoc @Summary Delete file @Description Deletes a file in - the user's workspace @Tags files @ID deleteFile @Security CookieAuth - @Param workspace_name path string true "Workspace name" @Param - file_path path string true "File path" @Success 204 "No Content - File - deleted successfully" @Failure 400 {object} ErrorResponse "Invalid - file path" @Failure 404 {object} ErrorResponse "File not found" - @Failure 500 {object} ErrorResponse "Failed to delete file" @Router - /workspaces/{workspace_name}/files/{file_path} [delete] - -func (h *Handler) DeleteWorkspace() http.HandlerFunc - DeleteWorkspace godoc @Summary Delete workspace @Description Deletes - the current workspace @Tags workspaces @ID deleteWorkspace @Security - CookieAuth @Produce json @Param workspace_name path string true "Workspace - name" @Success 200 {object} DeleteWorkspaceResponse @Failure 400 {object} - ErrorResponse "Cannot delete the last workspace" @Failure 500 {object} - ErrorResponse "Failed to get workspaces" @Failure 500 {object} ErrorResponse - "Failed to start transaction" @Failure 500 {object} ErrorResponse "Failed - to update last workspace" @Failure 500 {object} ErrorResponse "Failed to - delete workspace" @Failure 500 {object} ErrorResponse "Failed to rollback - transaction" @Failure 500 {object} ErrorResponse "Failed to commit - transaction" @Router /workspaces/{workspace_name} [delete] - -func (h *Handler) GetCurrentUser() http.HandlerFunc - GetCurrentUser godoc @Summary Get current user @Description Returns - the current authenticated user @Tags auth @ID getCurrentUser @Security - CookieAuth @Produce json @Success 200 {object} models.User @Failure 404 - {object} ErrorResponse "User not found" @Router /auth/me [get] - -func (h *Handler) GetFileContent() http.HandlerFunc - GetFileContent godoc @Summary Get file content @Description Returns the - content of a file in the user's workspace @Tags files @ID getFileContent - @Security CookieAuth @Produce plain @Param workspace_name path string - true "Workspace name" @Param file_path path string true "File path" - @Success 200 {string} string "Raw file content" @Failure 400 {object} - ErrorResponse "Invalid file path" @Failure 404 {object} ErrorResponse - "File not found" @Failure 500 {object} ErrorResponse "Failed to read file" - @Failure 500 {object} ErrorResponse "Failed to write response" @Router - /workspaces/{workspace_name}/files/{file_path} [get] - -func (h *Handler) GetLastOpenedFile() http.HandlerFunc - GetLastOpenedFile godoc @Summary Get last opened file @Description - Returns the path of the last opened file in the user's workspace @Tags - files @ID getLastOpenedFile @Security CookieAuth @Produce json @Param - workspace_name path string true "Workspace name" @Success 200 {object} - LastOpenedFileResponse @Failure 400 {object} ErrorResponse "Invalid file - path" @Failure 500 {object} ErrorResponse "Failed to get last opened file" - @Router /workspaces/{workspace_name}/files/last [get] - -func (h *Handler) GetLastWorkspaceName() http.HandlerFunc - GetLastWorkspaceName godoc @Summary Get last workspace name @Description - Returns the name of the last opened workspace @Tags workspaces @ID - getLastWorkspaceName @Security CookieAuth @Produce json @Success 200 - {object} LastWorkspaceNameResponse @Failure 500 {object} ErrorResponse - "Failed to get last workspace" @Router /workspaces/last [get] - -func (h *Handler) GetWorkspace() http.HandlerFunc - GetWorkspace godoc @Summary Get workspace @Description Returns the current - workspace @Tags workspaces @ID getWorkspace @Security CookieAuth @Produce - json @Param workspace_name path string true "Workspace name" @Success 200 - {object} models.Workspace @Failure 500 {object} ErrorResponse "Internal - server error" @Router /workspaces/{workspace_name} [get] - -func (h *Handler) ListFiles() http.HandlerFunc - ListFiles godoc @Summary List files @Description Lists all files in the - user's workspace @Tags files @ID listFiles @Security CookieAuth @Produce - json @Param workspace_name path string true "Workspace name" @Success 200 - {array} storage.FileNode @Failure 500 {object} ErrorResponse "Failed to list - files" @Router /workspaces/{workspace_name}/files [get] - -func (h *Handler) ListWorkspaces() http.HandlerFunc - ListWorkspaces godoc @Summary List workspaces @Description Lists all - workspaces for the current user @Tags workspaces @ID listWorkspaces - @Security CookieAuth @Produce json @Success 200 {array} models.Workspace - @Failure 500 {object} ErrorResponse "Failed to list workspaces" @Router - /workspaces [get] - -func (h *Handler) Login(authManager auth.SessionManager, cookieService auth.CookieManager) http.HandlerFunc - Login godoc @Summary Login @Description Logs in a user and returns a - session with access and refresh tokens @Tags auth @Accept json @Produce - json @Param body body LoginRequest true "Login request" @Success 200 - {object} LoginResponse @Header 200 {string} X-CSRF-Token "CSRF token for - future requests" @Failure 400 {object} ErrorResponse "Invalid request - body" @Failure 400 {object} ErrorResponse "Email and password are required" - @Failure 401 {object} ErrorResponse "Invalid credentials" @Failure 500 - {object} ErrorResponse "Failed to create session" @Failure 500 {object} - ErrorResponse "Failed to generate CSRF token" @Router /auth/login [post] - -func (h *Handler) Logout(authManager auth.SessionManager, cookieService auth.CookieManager) http.HandlerFunc - Logout godoc @Summary Logout @Description Log out invalidates the user's - session @Tags auth @ID logout @Success 204 "No Content" @Failure 400 - {object} ErrorResponse "Session ID required" @Failure 500 {object} - ErrorResponse "Failed to logout" @Router /auth/logout [post] - -func (h *Handler) LookupFileByName() http.HandlerFunc - LookupFileByName godoc @Summary Lookup file by name @Description Returns the - paths of files with the given name in the user's workspace @Tags files @ID - lookupFileByName @Security CookieAuth @Produce json @Param workspace_name - path string true "Workspace name" @Param filename query string true - "File name" @Success 200 {object} LookupResponse @Failure 400 {object} - ErrorResponse "Filename is required" @Failure 404 {object} ErrorResponse - "File not found" @Router /workspaces/{workspace_name}/files/lookup [get] - -func (h *Handler) PullChanges() http.HandlerFunc - PullChanges godoc @Summary Pull changes from remote @Description Pulls - changes from the remote repository @Tags git @ID pullChanges @Security - CookieAuth @Produce json @Param workspace_name path string true "Workspace - name" @Success 200 {object} PullResponse @Failure 500 {object} ErrorResponse - "Failed to pull changes" @Router /workspaces/{workspace_name}/git/pull - [post] - -func (h *Handler) RefreshToken(authManager auth.SessionManager, cookieService auth.CookieManager) http.HandlerFunc - RefreshToken godoc @Summary Refresh token @Description Refreshes the access - token using the refresh token @Tags auth @ID refreshToken @Accept json - @Produce json @Success 200 @Header 200 {string} X-CSRF-Token "New CSRF - token" @Failure 400 {object} ErrorResponse "Refresh token required" @Failure - 401 {object} ErrorResponse "Invalid refresh token" @Failure 500 {object} - ErrorResponse "Failed to generate CSRF token" @Router /auth/refresh [post] - -func (h *Handler) SaveFile() http.HandlerFunc - SaveFile godoc @Summary Save file @Description Saves the content of a file - in the user's workspace @Tags files @ID saveFile @Security CookieAuth - @Accept plain @Produce json @Param workspace_name path string true - "Workspace name" @Param file_path path string true "File path" @Success - 200 {object} SaveFileResponse @Failure 400 {object} ErrorResponse "Failed - to read request body" @Failure 400 {object} ErrorResponse "Invalid file - path" @Failure 500 {object} ErrorResponse "Failed to save file" @Router - /workspaces/{workspace_name}/files/{file_path} [post] - -func (h *Handler) StageCommitAndPush() http.HandlerFunc - StageCommitAndPush godoc @Summary Stage, commit, and push changes - @Description Stages, commits, and pushes changes to the remote repository - @Tags git @ID stageCommitAndPush @Security CookieAuth @Produce json - @Param workspace_name path string true "Workspace name" @Param body body - CommitRequest true "Commit request" @Success 200 {object} CommitResponse - @Failure 400 {object} ErrorResponse "Invalid request body" @Failure - 400 {object} ErrorResponse "Commit message is required" @Failure 500 - {object} ErrorResponse "Failed to stage, commit, and push changes" @Router - /workspaces/{workspace_name}/git/commit [post] - -func (h *Handler) UpdateLastOpenedFile() http.HandlerFunc - UpdateLastOpenedFile godoc @Summary Update last opened file @Description - Updates the last opened file in the user's workspace @Tags files @ID - updateLastOpenedFile @Security CookieAuth @Accept json @Produce json - @Param workspace_name path string true "Workspace name" @Param body - body UpdateLastOpenedFileRequest true "Update last opened file request" - @Success 204 "No Content - Last opened file updated successfully" @Failure - 400 {object} ErrorResponse "Invalid request body" @Failure 400 {object} - ErrorResponse "Invalid file path" @Failure 404 {object} ErrorResponse "File - not found" @Failure 500 {object} ErrorResponse "Failed to update file" - @Router /workspaces/{workspace_name}/files/last [put] - -func (h *Handler) UpdateLastWorkspaceName() http.HandlerFunc - UpdateLastWorkspaceName godoc @Summary Update last workspace name - @Description Updates the name of the last opened workspace @Tags workspaces - @ID updateLastWorkspaceName @Security CookieAuth @Accept json @Produce json - @Success 204 "No Content - Last workspace updated successfully" @Failure - 400 {object} ErrorResponse "Invalid request body" @Failure 500 {object} - ErrorResponse "Failed to update last workspace" @Router /workspaces/last - [put] - -func (h *Handler) UpdateProfile() http.HandlerFunc - UpdateProfile godoc @Summary Update profile @Description Updates the - user's profile @Tags users @ID updateProfile @Security CookieAuth @Accept - json @Produce json @Param body body UpdateProfileRequest true "Profile - update request" @Success 200 {object} models.User @Failure 400 {object} - ErrorResponse "Invalid request body" @Failure 400 {object} ErrorResponse - "Current password is required to change password" @Failure 400 {object} - ErrorResponse "New password must be at least 8 characters long" @Failure - 400 {object} ErrorResponse "Current password is required to change email" - @Failure 401 {object} ErrorResponse "Current password is incorrect" - @Failure 404 {object} ErrorResponse "User not found" @Failure 409 {object} - ErrorResponse "Email already in use" @Failure 500 {object} ErrorResponse - "Failed to process new password" @Failure 500 {object} ErrorResponse "Failed - to update profile" @Router /profile [put] - -func (h *Handler) UpdateWorkspace() http.HandlerFunc - UpdateWorkspace godoc @Summary Update workspace @Description Updates - the current workspace @Tags workspaces @ID updateWorkspace @Security - CookieAuth @Accept json @Produce json @Param workspace_name path string - true "Workspace name" @Param body body models.Workspace true "Workspace" - @Success 200 {object} models.Workspace @Failure 400 {object} ErrorResponse - "Invalid request body" @Failure 500 {object} ErrorResponse "Failed to update - workspace" @Failure 500 {object} ErrorResponse "Failed to setup git repo" - @Router /workspaces/{workspace_name} [put] - -type LastOpenedFileResponse struct { - LastOpenedFilePath string `json:"lastOpenedFilePath"` -} - LastOpenedFileResponse represents a response to a last opened file request - -type LastWorkspaceNameResponse struct { - LastWorkspaceName string `json:"lastWorkspaceName"` -} - LastWorkspaceNameResponse contains the name of the last opened workspace - -type LoginRequest struct { - Email string `json:"email"` - Password string `json:"password"` -} - LoginRequest represents a user login request - -type LoginResponse struct { - User *models.User `json:"user"` - SessionID string `json:"sessionId,omitempty"` - ExpiresAt time.Time `json:"expiresAt,omitempty"` -} - LoginResponse represents a user login response - -type LookupResponse struct { - Paths []string `json:"paths"` -} - LookupResponse represents a response to a file lookup request - -type PullResponse struct { - Message string `json:"message" example:"Pulled changes from remote"` -} - PullResponse represents a response to a pull http request - -type SaveFileResponse struct { - FilePath string `json:"filePath"` - Size int64 `json:"size"` - UpdatedAt time.Time `json:"updatedAt"` -} - SaveFileResponse represents a response to a save file request - -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 UpdateLastOpenedFileRequest struct { - FilePath string `json:"filePath"` -} - UpdateLastOpenedFileRequest represents a request to update the last opened - file - -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/logging - -```go -package logging // import "lemma/internal/logging" - -Package logging provides a simple logging interface for the server. - -FUNCTIONS - -func Debug(msg string, args ...any) - Debug logs a debug message - -func Error(msg string, args ...any) - Error logs an error message - -func Info(msg string, args ...any) - Info logs an info message - -func Setup(minLevel LogLevel) - Setup initializes the logger with the given minimum log level - -func Warn(msg string, args ...any) - Warn logs a warning message - - -TYPES - -type LogLevel slog.Level - LogLevel represents the log level - -const ( - DEBUG LogLevel = LogLevel(slog.LevelDebug) - INFO LogLevel = LogLevel(slog.LevelInfo) - WARN LogLevel = LogLevel(slog.LevelWarn) - ERROR LogLevel = LogLevel(slog.LevelError) -) - Log levels - -func ParseLogLevel(level string) LogLevel - ParseLogLevel converts a string to a LogLevel - -type Logger interface { - Debug(msg string, args ...any) - Info(msg string, args ...any) - Warn(msg string, args ...any) - Error(msg string, args ...any) - WithGroup(name string) Logger - With(args ...any) Logger -} - Logger represents the interface for logging operations - -func With(args ...any) Logger - With adds key-value pairs to the logger context - -func WithGroup(name string) Logger - WithGroup adds a group to the logger context - -``` - -## internal/models - -```go -package models // import "lemma/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 `db:"id"` // Unique session identifier - UserID int `db:"user_id"` // ID of the user this session belongs to - RefreshToken string `db:"refresh_token"` // The refresh token associated with this session - ExpiresAt time.Time `db:"expires_at"` // When this session expires - CreatedAt time.Time `db:"created_at,default"` // When this session was created -} - Session represents a user session in the database - -type User struct { - ID int `json:"id" db:"id,default" validate:"required,min=1"` - Email string `json:"email" db:"email" validate:"required,email"` - DisplayName string `json:"displayName" db:"display_name"` - PasswordHash string `json:"-" db:"password_hash"` - Role UserRole `json:"role" db:"role" validate:"required,oneof=admin editor viewer"` - CreatedAt time.Time `json:"createdAt" db:"created_at,default"` - LastWorkspaceID int `json:"lastWorkspaceId" db:"last_workspace_id"` -} - 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" db:"id,default" validate:"required,min=1"` - UserID int `json:"userId" db:"user_id" validate:"required,min=1"` - Name string `json:"name" db:"name" validate:"required"` - CreatedAt time.Time `json:"createdAt" db:"created_at,default"` - LastOpenedFilePath string `json:"lastOpenedFilePath" db:"last_opened_file_path"` - - // Integrated settings - Theme string `json:"theme" db:"theme" validate:"oneof=light dark"` - AutoSave bool `json:"autoSave" db:"auto_save"` - ShowHiddenFiles bool `json:"showHiddenFiles" db:"show_hidden_files"` - GitEnabled bool `json:"gitEnabled" db:"git_enabled"` - GitURL string `json:"gitUrl" db:"git_url,ommitempty" validate:"required_if=GitEnabled true"` - GitUser string `json:"gitUser" db:"git_user,ommitempty" validate:"required_if=GitEnabled true"` - GitToken string `json:"gitToken" db:"git_token,ommitempty,encrypted" validate:"required_if=GitEnabled true"` - GitAutoCommit bool `json:"gitAutoCommit" db:"git_auto_commit"` - GitCommitMsgTemplate string `json:"gitCommitMsgTemplate" db:"git_commit_msg_template"` - GitCommitName string `json:"gitCommitName" db:"git_commit_name"` - GitCommitEmail string `json:"gitCommitEmail" db:"git_commit_email" 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 - -```go -package secrets // import "lemma/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 - -```go -package storage // import "lemma/internal/storage" - -Package storage provides functionalities to interact with the storage system -(filesystem). - -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) (git.CommitHash, 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) (git.CommitHash, 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. - -``` - -## internal/testenv - -```go -package testenv // import "lemma/internal/testenv" - -Package testenv provides a setup for testing the application. -``` -