diff --git a/app/src/App.scss b/app/src/App.scss
index 4f55b6a..eb90e3c 100644
--- a/app/src/App.scss
+++ b/app/src/App.scss
@@ -108,29 +108,3 @@ $navbar-height: 64px;
.tree {
padding-top: $padding;
}
-
-// Syntax highlighting themes
-@import 'highlight.js/styles/github.css' layer(light-theme);
-@import 'highlight.js/styles/github-dark.css' layer(dark-theme);
-
-// Show light theme by default
-@layer light-theme {
- [data-mantine-color-scheme='light'] .markdown-preview {
- pre code.hljs {
- display: block;
- overflow-x: auto;
- padding: 1em;
- }
- }
-}
-
-// Show dark theme in dark mode
-@layer dark-theme {
- [data-mantine-color-scheme='dark'] .markdown-preview {
- pre code.hljs {
- display: block;
- overflow-x: auto;
- padding: 1em;
- }
- }
-}
diff --git a/app/src/components/editor/MarkdownPreview.test.tsx b/app/src/components/editor/MarkdownPreview.test.tsx
index 444ee3a..268d5b5 100644
--- a/app/src/components/editor/MarkdownPreview.test.tsx
+++ b/app/src/components/editor/MarkdownPreview.test.tsx
@@ -120,6 +120,34 @@ describe('MarkdownPreview', () => {
});
});
+ it('renders code blocks with correct structure for theme switching', async () => {
+ const content = '```javascript\nconst hello = "world";\n```';
+
+ render(
+
+ );
+
+ await waitFor(() => {
+ // Check that rehype-highlight generates the correct structure
+ const preElement = screen
+ .getByRole('code', { hidden: true })
+ .closest('pre');
+ const codeElement = preElement?.querySelector('code');
+
+ expect(preElement).toBeInTheDocument();
+ expect(codeElement).toBeInTheDocument();
+
+ // The code element should have hljs class for theme switching to work
+ expect(codeElement).toHaveClass('hljs');
+
+ // Should also have language class
+ expect(codeElement).toHaveClass('language-javascript');
+ });
+ });
+
it('handles image loading errors gracefully', async () => {
const content = '';
diff --git a/app/src/components/editor/MarkdownPreview.tsx b/app/src/components/editor/MarkdownPreview.tsx
index 74ab924..9e72472 100644
--- a/app/src/components/editor/MarkdownPreview.tsx
+++ b/app/src/components/editor/MarkdownPreview.tsx
@@ -10,6 +10,7 @@ import * as prod from 'react/jsx-runtime';
import { notifications } from '@mantine/notifications';
import { remarkWikiLinks } from '../../utils/remarkWikiLinks';
import { useWorkspace } from '../../hooks/useWorkspace';
+import { useHighlightTheme } from '../../hooks/useHighlightTheme';
interface MarkdownPreviewProps {
content: string;
@@ -28,12 +29,6 @@ interface MarkdownLinkProps {
[key: string]: unknown;
}
-interface MarkdownCodeProps {
- children: ReactNode;
- className?: string;
- [key: string]: unknown;
-}
-
const MarkdownPreview: React.FC = ({
content,
handleFileSelect,
@@ -42,7 +37,10 @@ const MarkdownPreview: React.FC = ({
null
);
const baseUrl = window.API_BASE_URL;
- const { currentWorkspace } = useWorkspace();
+ const { currentWorkspace, colorScheme } = useWorkspace();
+
+ // Use the highlight theme hook
+ useHighlightTheme(colorScheme === 'auto' ? 'light' : colorScheme);
const processor = useMemo(() => {
const handleLinkClick = (
@@ -107,13 +105,6 @@ const MarkdownPreview: React.FC = ({
{children}
),
- code: ({ children, className, ...props }: MarkdownCodeProps) => {
- return (
-
- {children}
-
- );
- },
},
} as Options);
}, [currentWorkspace?.name, baseUrl, handleFileSelect]);
diff --git a/app/src/hooks/useHighlightTheme.ts b/app/src/hooks/useHighlightTheme.ts
new file mode 100644
index 0000000..c814ee7
--- /dev/null
+++ b/app/src/hooks/useHighlightTheme.ts
@@ -0,0 +1,36 @@
+import { useEffect } from 'react';
+// Import theme CSS as text that will be bundled
+import atomOneLightTheme from 'highlight.js/styles/atom-one-light.css?inline';
+import atomOneDarkTheme from 'highlight.js/styles/atom-one-dark.css?inline';
+
+export const useHighlightTheme = (colorScheme: 'light' | 'dark') => {
+ useEffect(() => {
+ // Remove existing highlight theme
+ const existingStylesheet = document.querySelector(
+ 'style[data-highlight-theme]'
+ );
+ if (existingStylesheet) {
+ existingStylesheet.remove();
+ }
+
+ // Add new theme stylesheet using bundled CSS
+ const style = document.createElement('style');
+ style.setAttribute('data-highlight-theme', 'true');
+
+ if (colorScheme === 'dark') {
+ style.textContent = atomOneDarkTheme as string;
+ } else {
+ style.textContent = atomOneLightTheme as string;
+ }
+
+ document.head.appendChild(style);
+
+ return () => {
+ // Cleanup on unmount
+ const stylesheet = document.querySelector('style[data-highlight-theme]');
+ if (stylesheet) {
+ stylesheet.remove();
+ }
+ };
+ }, [colorScheme]);
+};
diff --git a/app/src/types/css-inline.d.ts b/app/src/types/css-inline.d.ts
new file mode 100644
index 0000000..a880406
--- /dev/null
+++ b/app/src/types/css-inline.d.ts
@@ -0,0 +1,5 @@
+// Type declarations for CSS imports with ?inline modifier
+declare module '*.css?inline' {
+ const content: string;
+ export default content;
+}
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.
-```
-