mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 16:04:23 +00:00
@@ -39,7 +39,13 @@ func main() {
|
|||||||
|
|
||||||
// Initialize user service
|
// Initialize user service
|
||||||
userService := user.NewUserService(database, fs)
|
userService := user.NewUserService(database, fs)
|
||||||
if _, err := userService.SetupAdminUser(); err != nil {
|
|
||||||
|
adminEmail := os.Getenv("NOVAMD_ADMIN_EMAIL")
|
||||||
|
adminPassword := os.Getenv("NOVAMD_ADMIN_PASSWORD")
|
||||||
|
if adminEmail == "" || adminPassword == "" {
|
||||||
|
log.Fatal("NOVAMD_ADMIN_EMAIL and NOVAMD_ADMIN_PASSWORD environment variables must be set")
|
||||||
|
}
|
||||||
|
if _, err := userService.SetupAdminUser(adminEmail, adminPassword); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func SetupRoutes(r chi.Router, db *db.DB, fs *filesystem.FileSystem) {
|
|||||||
// Workspace routes
|
// Workspace routes
|
||||||
r.Route("/workspaces", func(r chi.Router) {
|
r.Route("/workspaces", func(r chi.Router) {
|
||||||
r.Get("/", ListWorkspaces(db))
|
r.Get("/", ListWorkspaces(db))
|
||||||
r.Post("/", CreateWorkspace(db))
|
r.Post("/", CreateWorkspace(db, fs))
|
||||||
r.Get("/last", GetLastWorkspace(db))
|
r.Get("/last", GetLastWorkspace(db))
|
||||||
r.Put("/last", UpdateLastWorkspace(db))
|
r.Put("/last", UpdateLastWorkspace(db))
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func ListWorkspaces(db *db.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateWorkspace(db *db.DB) http.HandlerFunc {
|
func CreateWorkspace(db *db.DB, fs *filesystem.FileSystem) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userID, err := getUserID(r)
|
userID, err := getUserID(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -47,6 +47,11 @@ func CreateWorkspace(db *db.DB) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := fs.InitializeUserWorkspace(workspace.UserID, workspace.ID); err != nil {
|
||||||
|
http.Error(w, "Failed to initialize workspace directory", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
respondJSON(w, workspace)
|
respondJSON(w, workspace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"novamd/internal/gitutils"
|
"novamd/internal/gitutils"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,13 +39,6 @@ func (fs *FileSystem) InitializeUserWorkspace(userID, workspaceID int) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create workspace directory: %w", err)
|
return fmt.Errorf("failed to create workspace directory: %w", err)
|
||||||
}
|
}
|
||||||
// Optionally, create a welcome file in the new workspace
|
|
||||||
// welcomeFilePath := filepath.Join(workspacePath, "Welcome.md")
|
|
||||||
// welcomeContent := []byte("# Welcome to Your Main Workspace\n\nThis is your default workspace in NovaMD. You can start creating and editing files right away!")
|
|
||||||
// err = os.WriteFile(welcomeFilePath, welcomeContent, 0644)
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("failed to create welcome file: %w", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -82,26 +76,57 @@ func (fs *FileSystem) walkDirectory(dir, prefix string) ([]FileNode, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := make([]FileNode, 0)
|
// Split entries into directories and files
|
||||||
|
var dirs, files []os.DirEntry
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
dirs = append(dirs, entry)
|
||||||
|
} else {
|
||||||
|
files = append(files, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort directories and files separately
|
||||||
|
sort.Slice(dirs, func(i, j int) bool {
|
||||||
|
return strings.ToLower(dirs[i].Name()) < strings.ToLower(dirs[j].Name())
|
||||||
|
})
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
return strings.ToLower(files[i].Name()) < strings.ToLower(files[j].Name())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create combined slice with directories first, then files
|
||||||
|
nodes := make([]FileNode, 0, len(entries))
|
||||||
|
|
||||||
|
// Add directories first
|
||||||
|
for _, entry := range dirs {
|
||||||
name := entry.Name()
|
name := entry.Name()
|
||||||
path := filepath.Join(prefix, name)
|
path := filepath.Join(prefix, name)
|
||||||
fullPath := filepath.Join(dir, name)
|
fullPath := filepath.Join(dir, name)
|
||||||
|
|
||||||
|
children, err := fs.walkDirectory(fullPath, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
node := FileNode{
|
||||||
|
ID: path,
|
||||||
|
Name: name,
|
||||||
|
Path: path,
|
||||||
|
Children: children,
|
||||||
|
}
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then add files
|
||||||
|
for _, entry := range files {
|
||||||
|
name := entry.Name()
|
||||||
|
path := filepath.Join(prefix, name)
|
||||||
|
|
||||||
node := FileNode{
|
node := FileNode{
|
||||||
ID: path,
|
ID: path,
|
||||||
Name: name,
|
Name: name,
|
||||||
Path: path,
|
Path: path,
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.IsDir() {
|
|
||||||
children, err := fs.walkDirectory(fullPath, path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
node.Children = children
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes = append(nodes, node)
|
nodes = append(nodes, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package user
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
@@ -24,18 +23,13 @@ func NewUserService(database *db.DB, fs *filesystem.FileSystem) *UserService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UserService) SetupAdminUser() (*models.User, error) {
|
func (s *UserService) SetupAdminUser(adminEmail, adminPassword string) (*models.User, error) {
|
||||||
// Get admin email and password from environment variables
|
|
||||||
adminEmail := os.Getenv("NOVAMD_ADMIN_EMAIL")
|
|
||||||
adminPassword := os.Getenv("NOVAMD_ADMIN_PASSWORD")
|
|
||||||
if adminEmail == "" || adminPassword == "" {
|
|
||||||
return nil, fmt.Errorf("NOVAMD_ADMIN_EMAIL and NOVAMD_ADMIN_PASSWORD environment variables must be set")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if admin user exists
|
// Check if admin user exists
|
||||||
adminUser, err := s.DB.GetUserByEmail(adminEmail)
|
adminUser, err := s.DB.GetUserByEmail(adminEmail)
|
||||||
if adminUser != nil {
|
if adminUser != nil {
|
||||||
return adminUser, nil // Admin user already exists
|
return adminUser, nil // Admin user already exists
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash the password
|
// Hash the password
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { fetchFileList } from '../services/api';
|
import { fetchFileList } from '../services/api';
|
||||||
import { useWorkspace } from '../contexts/WorkspaceContext';
|
import { useWorkspace } from '../contexts/WorkspaceContext';
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export const useFileList = () => {
|
|||||||
console.error('Failed to load file list:', error);
|
console.error('Failed to load file list:', error);
|
||||||
setFiles([]);
|
setFiles([]);
|
||||||
}
|
}
|
||||||
}, [currentWorkspace]);
|
}, [currentWorkspace, workspaceLoading]);
|
||||||
|
|
||||||
return { files, loadFileList };
|
return { files, loadFileList };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback, useEffect } from 'react'; // Added useEffect
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
import { lookupFileByName } from '../services/api';
|
import { lookupFileByName } from '../services/api';
|
||||||
import { DEFAULT_FILE } from '../utils/constants';
|
import { DEFAULT_FILE } from '../utils/constants';
|
||||||
@@ -10,8 +10,8 @@ export const useFileNavigation = () => {
|
|||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const handleFileSelect = useCallback((filePath) => {
|
const handleFileSelect = useCallback((filePath) => {
|
||||||
setSelectedFile(filePath);
|
setSelectedFile(filePath || DEFAULT_FILE.path);
|
||||||
setIsNewFile(filePath === DEFAULT_FILE.path);
|
setIsNewFile(filePath ? false : true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleLinkClick = useCallback(
|
const handleLinkClick = useCallback(
|
||||||
@@ -38,8 +38,13 @@ export const useFileNavigation = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currentWorkspace]
|
[currentWorkspace, handleFileSelect]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Reset to default file when workspace changes
|
||||||
|
useEffect(() => {
|
||||||
|
handleFileSelect(null);
|
||||||
|
}, [currentWorkspace, handleFileSelect]);
|
||||||
|
|
||||||
return { handleLinkClick, selectedFile, isNewFile, handleFileSelect };
|
return { handleLinkClick, selectedFile, isNewFile, handleFileSelect };
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user