mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 15:44:21 +00:00
1290 lines
48 KiB
Markdown
1290 lines
48 KiB
Markdown
# 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.
|
|
```
|
|
|