mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 23:44:22 +00:00
Split filesystem.go file
This commit is contained in:
153
backend/internal/filesystem/files.go
Normal file
153
backend/internal/filesystem/files.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
// Package filesystem provides functionalities to interact with the file system,
|
||||||
|
// including listing files, finding files by name, getting file content, saving files, and deleting files.
|
||||||
|
package filesystem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileNode represents a file or directory in the file system.
|
||||||
|
type FileNode struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Children []FileNode `json:"children,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFilesRecursively returns a list of all files in the workspace directory and its subdirectories.
|
||||||
|
func (fs *FileSystem) ListFilesRecursively(userID, workspaceID int) ([]FileNode, error) {
|
||||||
|
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
||||||
|
return fs.walkDirectory(workspacePath, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileSystem) walkDirectory(dir, prefix string) ([]FileNode, error) {
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split entries into directories and files
|
||||||
|
var dirs, files []os.DirEntry
|
||||||
|
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()
|
||||||
|
path := filepath.Join(prefix, 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{
|
||||||
|
ID: path,
|
||||||
|
Name: name,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFileByName returns a list of file paths that match the given filename.
|
||||||
|
func (fs *FileSystem) FindFileByName(userID, workspaceID int, filename string) ([]string, error) {
|
||||||
|
var foundPaths []string
|
||||||
|
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
||||||
|
|
||||||
|
err := filepath.Walk(workspacePath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
relPath, err := filepath.Rel(workspacePath, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if strings.EqualFold(info.Name(), filename) {
|
||||||
|
foundPaths = append(foundPaths, relPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(foundPaths) == 0 {
|
||||||
|
return nil, errors.New("file not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundPaths, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileContent returns the content of the file at the given path.
|
||||||
|
func (fs *FileSystem) GetFileContent(userID, workspaceID int, filePath string) ([]byte, error) {
|
||||||
|
fullPath, err := fs.ValidatePath(userID, workspaceID, filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.ReadFile(fullPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveFile writes the content to the file at the given path.
|
||||||
|
func (fs *FileSystem) SaveFile(userID, workspaceID int, filePath string, content []byte) error {
|
||||||
|
fullPath, err := fs.ValidatePath(userID, workspaceID, filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := filepath.Dir(fullPath)
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(fullPath, content, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFile deletes the file at the given path.
|
||||||
|
func (fs *FileSystem) DeleteFile(userID, workspaceID int, filePath string) error {
|
||||||
|
fullPath, err := fs.ValidatePath(userID, workspaceID, filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Remove(fullPath)
|
||||||
|
}
|
||||||
@@ -1,27 +1,19 @@
|
|||||||
package filesystem
|
package filesystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"novamd/internal/gitutils"
|
"novamd/internal/gitutils"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FileSystem represents the file system structure.
|
||||||
type FileSystem struct {
|
type FileSystem struct {
|
||||||
RootDir string
|
RootDir string
|
||||||
GitRepos map[int]map[int]*gitutils.GitRepo // map[userID]map[workspaceID]*gitutils.GitRepo
|
GitRepos map[int]map[int]*gitutils.GitRepo // map[userID]map[workspaceID]*gitutils.GitRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileNode struct {
|
// New creates a new FileSystem instance.
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Path string `json:"path"`
|
|
||||||
Children []FileNode `json:"children,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(rootDir string) *FileSystem {
|
func New(rootDir string) *FileSystem {
|
||||||
return &FileSystem{
|
return &FileSystem{
|
||||||
RootDir: rootDir,
|
RootDir: rootDir,
|
||||||
@@ -29,30 +21,7 @@ func New(rootDir string) *FileSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileSystem) GetWorkspacePath(userID, workspaceID int) string {
|
// ValidatePath validates the given path and returns the cleaned path if it is valid.
|
||||||
return filepath.Join(fs.RootDir, fmt.Sprintf("%d", userID), fmt.Sprintf("%d", workspaceID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) InitializeUserWorkspace(userID, workspaceID int) error {
|
|
||||||
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
|
||||||
err := os.MkdirAll(workspacePath, 0755)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create workspace directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) DeleteUserWorkspace(userID, workspaceID int) error {
|
|
||||||
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
|
||||||
err := os.RemoveAll(workspacePath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to delete workspace directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) ValidatePath(userID, workspaceID int, path string) (string, error) {
|
func (fs *FileSystem) ValidatePath(userID, workspaceID int, path string) (string, error) {
|
||||||
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
||||||
fullPath := filepath.Join(workspacePath, path)
|
fullPath := filepath.Join(workspacePath, path)
|
||||||
@@ -64,186 +33,3 @@ func (fs *FileSystem) ValidatePath(userID, workspaceID int, path string) (string
|
|||||||
|
|
||||||
return cleanPath, nil
|
return cleanPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileSystem) ListFilesRecursively(userID, workspaceID int) ([]FileNode, error) {
|
|
||||||
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
|
||||||
return fs.walkDirectory(workspacePath, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) walkDirectory(dir, prefix string) ([]FileNode, error) {
|
|
||||||
entries, err := os.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split entries into directories and files
|
|
||||||
var dirs, files []os.DirEntry
|
|
||||||
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()
|
|
||||||
path := filepath.Join(prefix, 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{
|
|
||||||
ID: path,
|
|
||||||
Name: name,
|
|
||||||
Path: path,
|
|
||||||
}
|
|
||||||
nodes = append(nodes, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) FindFileByName(userID, workspaceID int, filename string) ([]string, error) {
|
|
||||||
var foundPaths []string
|
|
||||||
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
|
||||||
|
|
||||||
err := filepath.Walk(workspacePath, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !info.IsDir() {
|
|
||||||
relPath, err := filepath.Rel(workspacePath, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if strings.EqualFold(info.Name(), filename) {
|
|
||||||
foundPaths = append(foundPaths, relPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(foundPaths) == 0 {
|
|
||||||
return nil, errors.New("file not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return foundPaths, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) GetFileContent(userID, workspaceID int, filePath string) ([]byte, error) {
|
|
||||||
fullPath, err := fs.ValidatePath(userID, workspaceID, filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return os.ReadFile(fullPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) SaveFile(userID, workspaceID int, filePath string, content []byte) error {
|
|
||||||
fullPath, err := fs.ValidatePath(userID, workspaceID, filePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := filepath.Dir(fullPath)
|
|
||||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.WriteFile(fullPath, content, 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) DeleteFile(userID, workspaceID int, filePath string) error {
|
|
||||||
fullPath, err := fs.ValidatePath(userID, workspaceID, filePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Remove(fullPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) CreateWorkspaceDirectory(userID, workspaceID int) error {
|
|
||||||
dir := fs.GetWorkspacePath(userID, workspaceID)
|
|
||||||
return os.MkdirAll(dir, 0755)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) SetupGitRepo(userID, workspaceID int, gitURL, gitUser, gitToken string) error {
|
|
||||||
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
|
||||||
if _, ok := fs.GitRepos[userID]; !ok {
|
|
||||||
fs.GitRepos[userID] = make(map[int]*gitutils.GitRepo)
|
|
||||||
}
|
|
||||||
fs.GitRepos[userID][workspaceID] = gitutils.New(gitURL, gitUser, gitToken, workspacePath)
|
|
||||||
return fs.GitRepos[userID][workspaceID].EnsureRepo()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) DisableGitRepo(userID, workspaceID int) {
|
|
||||||
if userRepos, ok := fs.GitRepos[userID]; ok {
|
|
||||||
delete(userRepos, workspaceID)
|
|
||||||
if len(userRepos) == 0 {
|
|
||||||
delete(fs.GitRepos, userID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) StageCommitAndPush(userID, workspaceID int, message string) error {
|
|
||||||
repo, ok := fs.getGitRepo(userID, workspaceID)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("git settings not configured for this workspace")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := repo.Commit(message); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo.Push()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) Pull(userID, workspaceID int) error {
|
|
||||||
repo, ok := fs.getGitRepo(userID, workspaceID)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("git settings not configured for this workspace")
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo.Pull()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *FileSystem) getGitRepo(userID, workspaceID int) (*gitutils.GitRepo, bool) {
|
|
||||||
userRepos, ok := fs.GitRepos[userID]
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
repo, ok := userRepos[workspaceID]
|
|
||||||
return repo, ok
|
|
||||||
}
|
|
||||||
|
|||||||
60
backend/internal/filesystem/git.go
Normal file
60
backend/internal/filesystem/git.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package filesystem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"novamd/internal/gitutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetupGitRepo sets up a Git repository for the given user and workspace IDs.
|
||||||
|
func (fs *FileSystem) SetupGitRepo(userID, workspaceID int, gitURL, gitUser, gitToken string) error {
|
||||||
|
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
||||||
|
if _, ok := fs.GitRepos[userID]; !ok {
|
||||||
|
fs.GitRepos[userID] = make(map[int]*gitutils.GitRepo)
|
||||||
|
}
|
||||||
|
fs.GitRepos[userID][workspaceID] = gitutils.New(gitURL, gitUser, gitToken, workspacePath)
|
||||||
|
return fs.GitRepos[userID][workspaceID].EnsureRepo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableGitRepo disables the Git repository for the given user and workspace IDs.
|
||||||
|
func (fs *FileSystem) DisableGitRepo(userID, workspaceID int) {
|
||||||
|
if userRepos, ok := fs.GitRepos[userID]; ok {
|
||||||
|
delete(userRepos, workspaceID)
|
||||||
|
if len(userRepos) == 0 {
|
||||||
|
delete(fs.GitRepos, userID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StageCommitAndPush stages, commits, and pushes the changes to the Git repository.
|
||||||
|
func (fs *FileSystem) StageCommitAndPush(userID, workspaceID int, message string) error {
|
||||||
|
repo, ok := fs.getGitRepo(userID, workspaceID)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("git settings not configured for this workspace")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo.Commit(message); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.Push()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull pulls the changes from the remote Git repository.
|
||||||
|
func (fs *FileSystem) Pull(userID, workspaceID int) error {
|
||||||
|
repo, ok := fs.getGitRepo(userID, workspaceID)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("git settings not configured for this workspace")
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.Pull()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGitRepo returns the Git repository for the given user and workspace IDs.
|
||||||
|
func (fs *FileSystem) getGitRepo(userID, workspaceID int) (*gitutils.GitRepo, bool) {
|
||||||
|
userRepos, ok := fs.GitRepos[userID]
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
repo, ok := userRepos[workspaceID]
|
||||||
|
return repo, ok
|
||||||
|
}
|
||||||
40
backend/internal/filesystem/workspace.go
Normal file
40
backend/internal/filesystem/workspace.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package filesystem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetWorkspacePath returns the path to the workspace directory for the given user and workspace IDs.
|
||||||
|
func (fs *FileSystem) GetWorkspacePath(userID, workspaceID int) string {
|
||||||
|
return filepath.Join(fs.RootDir, fmt.Sprintf("%d", userID), fmt.Sprintf("%d", workspaceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeUserWorkspace creates the workspace directory for the given user and workspace IDs.
|
||||||
|
func (fs *FileSystem) InitializeUserWorkspace(userID, workspaceID int) error {
|
||||||
|
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
||||||
|
err := os.MkdirAll(workspacePath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create workspace directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserWorkspace deletes the workspace directory for the given user and workspace IDs.
|
||||||
|
func (fs *FileSystem) DeleteUserWorkspace(userID, workspaceID int) error {
|
||||||
|
workspacePath := fs.GetWorkspacePath(userID, workspaceID)
|
||||||
|
err := os.RemoveAll(workspacePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete workspace directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateWorkspaceDirectory creates the workspace directory for the given user and workspace IDs.
|
||||||
|
func (fs *FileSystem) CreateWorkspaceDirectory(userID, workspaceID int) error {
|
||||||
|
dir := fs.GetWorkspacePath(userID, workspaceID)
|
||||||
|
return os.MkdirAll(dir, 0755)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user