@hamak/filesystem-server-impl 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es2015/index.js +32 -0
- package/dist/es2015/middleware/workspace-validator.js +31 -0
- package/dist/es2015/plugin/filesystem-server-plugin.js +82 -0
- package/dist/es2015/routing/create-router.js +39 -0
- package/dist/es2015/routing/file-router.js +119 -0
- package/dist/es2015/services/workspace-manager.js +156 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/middleware/workspace-validator.d.ts +15 -0
- package/dist/middleware/workspace-validator.d.ts.map +1 -0
- package/dist/middleware/workspace-validator.js +27 -0
- package/dist/plugin/filesystem-server-plugin.d.ts +42 -0
- package/dist/plugin/filesystem-server-plugin.d.ts.map +1 -0
- package/dist/plugin/filesystem-server-plugin.js +76 -0
- package/dist/routing/create-router.d.ts +16 -0
- package/dist/routing/create-router.d.ts.map +1 -0
- package/dist/routing/create-router.js +31 -0
- package/dist/routing/file-router.d.ts +22 -0
- package/dist/routing/file-router.d.ts.map +1 -0
- package/dist/routing/file-router.js +94 -0
- package/dist/services/workspace-manager.d.ts +24 -0
- package/dist/services/workspace-manager.d.ts.map +1 -0
- package/dist/services/workspace-manager.js +108 -0
- package/package.json +54 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @hamak/filesystem-server-impl
|
|
4
|
+
*
|
|
5
|
+
* Backend filesystem server implementation
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
19
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
// Export plugin (main entry point)
|
|
23
|
+
__exportStar(require("./plugin/filesystem-server-plugin"), exports);
|
|
24
|
+
// Export services
|
|
25
|
+
__exportStar(require("./services/workspace-manager"), exports);
|
|
26
|
+
// Export routing
|
|
27
|
+
__exportStar(require("./routing/create-router"), exports);
|
|
28
|
+
__exportStar(require("./routing/file-router"), exports);
|
|
29
|
+
// Export middleware
|
|
30
|
+
__exportStar(require("./middleware/workspace-validator"), exports);
|
|
31
|
+
// Re-export API types for convenience
|
|
32
|
+
__exportStar(require("@hamak/filesystem-server-api"), exports);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Workspace Validator Middleware
|
|
4
|
+
*
|
|
5
|
+
* Validates workspace ID exists in configuration
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.createWorkspaceValidator = void 0;
|
|
9
|
+
/**
|
|
10
|
+
* Create middleware to validate workspace exists
|
|
11
|
+
*
|
|
12
|
+
* @param config - Server configuration with workspaces map
|
|
13
|
+
* @returns Express middleware function
|
|
14
|
+
*/
|
|
15
|
+
function createWorkspaceValidator(config) {
|
|
16
|
+
const workspaces = config.workspaces;
|
|
17
|
+
return (req, res, next) => {
|
|
18
|
+
const workspace = req.params.workspace;
|
|
19
|
+
if (!workspace) {
|
|
20
|
+
return res.status(400).json({ error: 'Workspace ID is required' });
|
|
21
|
+
}
|
|
22
|
+
if (!workspaces[workspace]) {
|
|
23
|
+
return res.status(404).json({ error: `Workspace '${workspace}' not found` });
|
|
24
|
+
}
|
|
25
|
+
// Store workspace info in request for downstream handlers
|
|
26
|
+
req.workspaceId = workspace;
|
|
27
|
+
req.workspacePath = workspaces[workspace];
|
|
28
|
+
next();
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
exports.createWorkspaceValidator = createWorkspaceValidator;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* FileSystem Server Plugin
|
|
4
|
+
*
|
|
5
|
+
* Microkernel plugin for backend filesystem server
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.createFileSystemServerPlugin = exports.FileSystemServerPlugin = void 0;
|
|
9
|
+
const filesystem_server_api_1 = require("@hamak/filesystem-server-api");
|
|
10
|
+
const workspace_manager_1 = require("../services/workspace-manager");
|
|
11
|
+
const create_router_1 = require("../routing/create-router");
|
|
12
|
+
/**
|
|
13
|
+
* FileSystem Server Plugin Implementation
|
|
14
|
+
*
|
|
15
|
+
* Provides backend filesystem operations via Express routes
|
|
16
|
+
*/
|
|
17
|
+
class FileSystemServerPlugin {
|
|
18
|
+
/**
|
|
19
|
+
* Initialize phase - register services in DI container
|
|
20
|
+
*/
|
|
21
|
+
initialize(ctx) {
|
|
22
|
+
console.log('[FileSystemServerPlugin] Initializing...');
|
|
23
|
+
// Resolve configuration
|
|
24
|
+
const config = ctx.resolve(filesystem_server_api_1.FILESYSTEM_SERVER_CONFIG_TOKEN);
|
|
25
|
+
// Create and register WorkspaceManager service
|
|
26
|
+
const workspaceManager = new workspace_manager_1.WorkspaceManager(config.workspaces, {
|
|
27
|
+
baseDirectory: config.baseDirectory,
|
|
28
|
+
});
|
|
29
|
+
ctx.provide({
|
|
30
|
+
provide: filesystem_server_api_1.WORKSPACE_MANAGER_TOKEN,
|
|
31
|
+
useValue: workspaceManager,
|
|
32
|
+
});
|
|
33
|
+
console.log('[FileSystemServerPlugin] WorkspaceManager service registered');
|
|
34
|
+
console.log(`[FileSystemServerPlugin] Base directory: ${config.baseDirectory}`);
|
|
35
|
+
console.log(`[FileSystemServerPlugin] Workspaces:`, Object.keys(config.workspaces));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Activate phase - create router and make ready for use
|
|
39
|
+
*/
|
|
40
|
+
activate(ctx) {
|
|
41
|
+
var _a;
|
|
42
|
+
console.log('[FileSystemServerPlugin] Activating...');
|
|
43
|
+
// Resolve dependencies
|
|
44
|
+
const workspaceManager = ctx.resolve(filesystem_server_api_1.WORKSPACE_MANAGER_TOKEN);
|
|
45
|
+
const config = ctx.resolve(filesystem_server_api_1.FILESYSTEM_SERVER_CONFIG_TOKEN);
|
|
46
|
+
// Create Express router
|
|
47
|
+
this.router = (0, create_router_1.createFileSystemRouter)(workspaceManager, config);
|
|
48
|
+
// Note: Router is available via getRouter() method, not registered in DI
|
|
49
|
+
// This avoids circular dependencies and keeps the plugin self-contained
|
|
50
|
+
console.log('[FileSystemServerPlugin] Activated successfully');
|
|
51
|
+
console.log(`[FileSystemServerPlugin] Mount path: ${(_a = config.mountPath) !== null && _a !== void 0 ? _a : '/api/workspaces'}`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Deactivate phase - cleanup
|
|
55
|
+
*/
|
|
56
|
+
deactivate() {
|
|
57
|
+
console.log('[FileSystemServerPlugin] Deactivating...');
|
|
58
|
+
this.router = undefined;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the Express router for mounting in application
|
|
62
|
+
*
|
|
63
|
+
* @returns Express router with filesystem routes
|
|
64
|
+
* @throws Error if plugin not activated
|
|
65
|
+
*/
|
|
66
|
+
getRouter() {
|
|
67
|
+
if (!this.router) {
|
|
68
|
+
throw new Error('[FileSystemServerPlugin] Plugin not activated. Call kernel.activate() first.');
|
|
69
|
+
}
|
|
70
|
+
return this.router;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.FileSystemServerPlugin = FileSystemServerPlugin;
|
|
74
|
+
/**
|
|
75
|
+
* Factory function to create plugin instance
|
|
76
|
+
*
|
|
77
|
+
* @returns FileSystemServerPlugin instance
|
|
78
|
+
*/
|
|
79
|
+
function createFileSystemServerPlugin() {
|
|
80
|
+
return new FileSystemServerPlugin();
|
|
81
|
+
}
|
|
82
|
+
exports.createFileSystemServerPlugin = createFileSystemServerPlugin;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Router Factory
|
|
4
|
+
*
|
|
5
|
+
* Creates and configures the Express router for filesystem operations
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.createFileSystemRouter = void 0;
|
|
12
|
+
const express_1 = require("express");
|
|
13
|
+
const cors_1 = __importDefault(require("cors"));
|
|
14
|
+
const body_parser_1 = __importDefault(require("body-parser"));
|
|
15
|
+
const filesystem_server_api_1 = require("@hamak/filesystem-server-api");
|
|
16
|
+
const file_router_1 = require("./file-router");
|
|
17
|
+
/**
|
|
18
|
+
* Create filesystem Express router with middleware
|
|
19
|
+
*
|
|
20
|
+
* @param workspaceManager - Workspace manager instance
|
|
21
|
+
* @param config - Server configuration
|
|
22
|
+
* @returns Configured Express router
|
|
23
|
+
*/
|
|
24
|
+
function createFileSystemRouter(workspaceManager, config) {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
const app = (0, express_1.Router)();
|
|
27
|
+
// Apply CORS middleware if enabled
|
|
28
|
+
if ((_a = config.enableCors) !== null && _a !== void 0 ? _a : filesystem_server_api_1.DEFAULT_FILESYSTEM_SERVER_CONFIG.enableCors) {
|
|
29
|
+
app.use((0, cors_1.default)());
|
|
30
|
+
}
|
|
31
|
+
// Apply body parser for JSON payloads
|
|
32
|
+
app.use(body_parser_1.default.json());
|
|
33
|
+
// Create and mount file router
|
|
34
|
+
const fileRouter = new file_router_1.FileRouter(workspaceManager);
|
|
35
|
+
const mountPath = (_b = config.mountPath) !== null && _b !== void 0 ? _b : filesystem_server_api_1.DEFAULT_FILESYSTEM_SERVER_CONFIG.mountPath;
|
|
36
|
+
app.use(mountPath, fileRouter.router);
|
|
37
|
+
return app;
|
|
38
|
+
}
|
|
39
|
+
exports.createFileSystemRouter = createFileSystemRouter;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File Router
|
|
4
|
+
*
|
|
5
|
+
* Express routes for filesystem operations
|
|
6
|
+
* Migrated from amk/libs/server/ws/ws-server
|
|
7
|
+
*/
|
|
8
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
9
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
10
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
11
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
12
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
13
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
14
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.FileRouter = void 0;
|
|
19
|
+
const express_1 = require("express");
|
|
20
|
+
class FileRouter {
|
|
21
|
+
constructor(workspaceManager) {
|
|
22
|
+
this.router = (0, express_1.Router)();
|
|
23
|
+
this.workspaceManager = workspaceManager;
|
|
24
|
+
this.initializeRoutes();
|
|
25
|
+
}
|
|
26
|
+
initializeRoutes() {
|
|
27
|
+
this.router.get('/:workspace/files/*', this.listFiles.bind(this));
|
|
28
|
+
this.router.post('/:workspace/mkdir/*', this.createDirectory.bind(this));
|
|
29
|
+
this.router.get('/:workspace/get/*', this.getFile.bind(this));
|
|
30
|
+
this.router.get('/:workspace/read/*', this.readFile.bind(this));
|
|
31
|
+
this.router.post('/:workspace/post/*', this.writeFile.bind(this));
|
|
32
|
+
this.router.put('/:workspace/put/*', this.writeFile.bind(this));
|
|
33
|
+
this.router.delete('/:workspace/delete/*', this.deleteFile.bind(this));
|
|
34
|
+
}
|
|
35
|
+
parsePath(pathString) {
|
|
36
|
+
// Split path string into segments, filter out empty segments
|
|
37
|
+
return pathString.split('/').filter(seg => seg.length > 0);
|
|
38
|
+
}
|
|
39
|
+
listFiles(req, res) {
|
|
40
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
try {
|
|
42
|
+
const { workspace } = req.params;
|
|
43
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
44
|
+
const files = yield this.workspaceManager.listFiles(workspace, pathSegments);
|
|
45
|
+
res.json(files);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
res.status(500).json({ error: error.message });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
createDirectory(req, res) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
try {
|
|
55
|
+
const { workspace } = req.params;
|
|
56
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
57
|
+
const dirInfo = yield this.workspaceManager.createDirectory(workspace, pathSegments);
|
|
58
|
+
res.json(dirInfo);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
res.status(500).json({ error: error.message });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
getFile(req, res) {
|
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
try {
|
|
68
|
+
const { workspace } = req.params;
|
|
69
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
70
|
+
const fileInfo = yield this.workspaceManager.getFile(workspace, pathSegments);
|
|
71
|
+
res.json(fileInfo);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
res.status(500).json({ error: error.message });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
readFile(req, res) {
|
|
79
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
try {
|
|
81
|
+
const { workspace } = req.params;
|
|
82
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
83
|
+
const fileInfo = yield this.workspaceManager.readFile(workspace, pathSegments);
|
|
84
|
+
res.json(fileInfo);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
res.status(500).json({ error: error.message });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
writeFile(req, res) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
try {
|
|
94
|
+
const { workspace } = req.params;
|
|
95
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
96
|
+
const { content } = req.body;
|
|
97
|
+
const fileInfo = yield this.workspaceManager.writeFile(workspace, pathSegments, content);
|
|
98
|
+
res.json(fileInfo);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
res.status(500).json({ error: error.message });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
deleteFile(req, res) {
|
|
106
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
try {
|
|
108
|
+
const { workspace } = req.params;
|
|
109
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
110
|
+
const fileInfo = yield this.workspaceManager.deleteFile(workspace, pathSegments);
|
|
111
|
+
res.json(fileInfo);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
res.status(500).json({ error: error.message });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.FileRouter = FileRouter;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Workspace Manager Implementation
|
|
4
|
+
*
|
|
5
|
+
* Provides filesystem operations for workspace-based storage
|
|
6
|
+
* Migrated from amk/libs/server/ws/ws-server
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
25
|
+
if (mod && mod.__esModule) return mod;
|
|
26
|
+
var result = {};
|
|
27
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
28
|
+
__setModuleDefault(result, mod);
|
|
29
|
+
return result;
|
|
30
|
+
};
|
|
31
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
32
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
33
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
34
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
35
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
36
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
37
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.WorkspaceManager = void 0;
|
|
42
|
+
const fs_1 = require("fs");
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
class WorkspaceManager {
|
|
45
|
+
constructor(workspaces, options) {
|
|
46
|
+
this.workspaces = workspaces;
|
|
47
|
+
this.options = options;
|
|
48
|
+
}
|
|
49
|
+
resolvePath(workspace, filePath) {
|
|
50
|
+
const basePath = path.resolve(this.options.baseDirectory, this.workspaces[workspace]);
|
|
51
|
+
if (!basePath) {
|
|
52
|
+
throw new Error(`Workspace ${workspace} not found`);
|
|
53
|
+
}
|
|
54
|
+
const relativePath = Array.isArray(filePath) ? filePath.join(path.sep) : filePath;
|
|
55
|
+
return path.resolve(basePath, relativePath);
|
|
56
|
+
}
|
|
57
|
+
listFiles(workspace, filePath) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
60
|
+
const files = yield fs_1.promises.readdir(absolutePath, { withFileTypes: true });
|
|
61
|
+
const fileInfos = yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
const fullPath = path.join(absolutePath, file.name);
|
|
63
|
+
const stats = yield fs_1.promises.stat(fullPath);
|
|
64
|
+
return {
|
|
65
|
+
name: file.name,
|
|
66
|
+
path: fullPath,
|
|
67
|
+
isDirectory: file.isDirectory(),
|
|
68
|
+
size: stats.size,
|
|
69
|
+
createdAt: stats.birthtime,
|
|
70
|
+
updatedAt: stats.mtime,
|
|
71
|
+
};
|
|
72
|
+
})));
|
|
73
|
+
return fileInfos;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
createDirectory(workspace, filePath) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
79
|
+
yield fs_1.promises.mkdir(absolutePath, { recursive: true });
|
|
80
|
+
const stats = yield fs_1.promises.stat(absolutePath);
|
|
81
|
+
return {
|
|
82
|
+
name: path.basename(absolutePath),
|
|
83
|
+
path: absolutePath,
|
|
84
|
+
isDirectory: true,
|
|
85
|
+
size: stats.size,
|
|
86
|
+
createdAt: stats.birthtime,
|
|
87
|
+
updatedAt: stats.mtime,
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
getFile(workspace, filePath) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
94
|
+
const stats = yield fs_1.promises.stat(absolutePath);
|
|
95
|
+
if (stats.isDirectory()) {
|
|
96
|
+
throw new Error('Path is a directory');
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
name: path.basename(absolutePath),
|
|
100
|
+
path: absolutePath,
|
|
101
|
+
isDirectory: false,
|
|
102
|
+
size: stats.size,
|
|
103
|
+
createdAt: stats.birthtime,
|
|
104
|
+
updatedAt: stats.mtime,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
readFile(workspace, filePath) {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
111
|
+
const data = yield fs_1.promises.readFile(absolutePath, 'utf-8');
|
|
112
|
+
const stats = yield fs_1.promises.stat(absolutePath);
|
|
113
|
+
return {
|
|
114
|
+
name: path.basename(absolutePath),
|
|
115
|
+
path: absolutePath,
|
|
116
|
+
isDirectory: false,
|
|
117
|
+
size: stats.size,
|
|
118
|
+
createdAt: stats.birthtime,
|
|
119
|
+
updatedAt: stats.mtime,
|
|
120
|
+
content: data, // Include file content
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
writeFile(workspace, filePath, content) {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
127
|
+
const data = typeof content === "string" ? content : JSON.stringify(content !== null && content !== void 0 ? content : "");
|
|
128
|
+
yield fs_1.promises.writeFile(absolutePath, data);
|
|
129
|
+
const stats = yield fs_1.promises.stat(absolutePath);
|
|
130
|
+
return {
|
|
131
|
+
name: path.basename(absolutePath),
|
|
132
|
+
path: absolutePath,
|
|
133
|
+
isDirectory: false,
|
|
134
|
+
size: stats.size,
|
|
135
|
+
createdAt: stats.birthtime,
|
|
136
|
+
updatedAt: stats.mtime,
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
deleteFile(workspace, filePath) {
|
|
141
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
143
|
+
const stats = yield fs_1.promises.stat(absolutePath);
|
|
144
|
+
yield fs_1.promises.rm(absolutePath, { recursive: true, force: true });
|
|
145
|
+
return {
|
|
146
|
+
name: path.basename(absolutePath),
|
|
147
|
+
path: absolutePath,
|
|
148
|
+
isDirectory: stats.isDirectory(),
|
|
149
|
+
size: stats.size,
|
|
150
|
+
createdAt: stats.birthtime,
|
|
151
|
+
updatedAt: stats.mtime,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
exports.WorkspaceManager = WorkspaceManager;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hamak/filesystem-server-impl
|
|
3
|
+
*
|
|
4
|
+
* Backend filesystem server implementation
|
|
5
|
+
*/
|
|
6
|
+
export * from './plugin/filesystem-server-plugin';
|
|
7
|
+
export * from './services/workspace-manager';
|
|
8
|
+
export * from './routing/create-router';
|
|
9
|
+
export * from './routing/file-router';
|
|
10
|
+
export * from './middleware/workspace-validator';
|
|
11
|
+
export * from '@hamak/filesystem-server-api';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,mCAAmC,CAAC;AAGlD,cAAc,8BAA8B,CAAC;AAG7C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,kCAAkC,CAAC;AAGjD,cAAc,8BAA8B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hamak/filesystem-server-impl
|
|
3
|
+
*
|
|
4
|
+
* Backend filesystem server implementation
|
|
5
|
+
*/
|
|
6
|
+
// Export plugin (main entry point)
|
|
7
|
+
export * from './plugin/filesystem-server-plugin';
|
|
8
|
+
// Export services
|
|
9
|
+
export * from './services/workspace-manager';
|
|
10
|
+
// Export routing
|
|
11
|
+
export * from './routing/create-router';
|
|
12
|
+
export * from './routing/file-router';
|
|
13
|
+
// Export middleware
|
|
14
|
+
export * from './middleware/workspace-validator';
|
|
15
|
+
// Re-export API types for convenience
|
|
16
|
+
export * from '@hamak/filesystem-server-api';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Validator Middleware
|
|
3
|
+
*
|
|
4
|
+
* Validates workspace ID exists in configuration
|
|
5
|
+
*/
|
|
6
|
+
import { Request, Response, NextFunction } from 'express';
|
|
7
|
+
import { FileSystemServerConfig } from '@hamak/filesystem-server-api';
|
|
8
|
+
/**
|
|
9
|
+
* Create middleware to validate workspace exists
|
|
10
|
+
*
|
|
11
|
+
* @param config - Server configuration with workspaces map
|
|
12
|
+
* @returns Express middleware function
|
|
13
|
+
*/
|
|
14
|
+
export declare function createWorkspaceValidator(config: FileSystemServerConfig): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
|
|
15
|
+
//# sourceMappingURL=workspace-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-validator.d.ts","sourceRoot":"","sources":["../../src/middleware/workspace-validator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAEtE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,sBAAsB,SAGxD,OAAO,OAAO,QAAQ,QAAQ,YAAY,oDAiBxD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Validator Middleware
|
|
3
|
+
*
|
|
4
|
+
* Validates workspace ID exists in configuration
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Create middleware to validate workspace exists
|
|
8
|
+
*
|
|
9
|
+
* @param config - Server configuration with workspaces map
|
|
10
|
+
* @returns Express middleware function
|
|
11
|
+
*/
|
|
12
|
+
export function createWorkspaceValidator(config) {
|
|
13
|
+
const workspaces = config.workspaces;
|
|
14
|
+
return (req, res, next) => {
|
|
15
|
+
const workspace = req.params.workspace;
|
|
16
|
+
if (!workspace) {
|
|
17
|
+
return res.status(400).json({ error: 'Workspace ID is required' });
|
|
18
|
+
}
|
|
19
|
+
if (!workspaces[workspace]) {
|
|
20
|
+
return res.status(404).json({ error: `Workspace '${workspace}' not found` });
|
|
21
|
+
}
|
|
22
|
+
// Store workspace info in request for downstream handlers
|
|
23
|
+
req.workspaceId = workspace;
|
|
24
|
+
req.workspacePath = workspaces[workspace];
|
|
25
|
+
next();
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileSystem Server Plugin
|
|
3
|
+
*
|
|
4
|
+
* Microkernel plugin for backend filesystem server
|
|
5
|
+
*/
|
|
6
|
+
import { Router } from 'express';
|
|
7
|
+
import { PluginModule, InitializationContext } from '@hamak/microkernel-spi';
|
|
8
|
+
import { ActivateContext } from '@hamak/microkernel-api';
|
|
9
|
+
/**
|
|
10
|
+
* FileSystem Server Plugin Implementation
|
|
11
|
+
*
|
|
12
|
+
* Provides backend filesystem operations via Express routes
|
|
13
|
+
*/
|
|
14
|
+
export declare class FileSystemServerPlugin implements PluginModule {
|
|
15
|
+
private router?;
|
|
16
|
+
/**
|
|
17
|
+
* Initialize phase - register services in DI container
|
|
18
|
+
*/
|
|
19
|
+
initialize(ctx: InitializationContext): void;
|
|
20
|
+
/**
|
|
21
|
+
* Activate phase - create router and make ready for use
|
|
22
|
+
*/
|
|
23
|
+
activate(ctx: ActivateContext): void;
|
|
24
|
+
/**
|
|
25
|
+
* Deactivate phase - cleanup
|
|
26
|
+
*/
|
|
27
|
+
deactivate(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Get the Express router for mounting in application
|
|
30
|
+
*
|
|
31
|
+
* @returns Express router with filesystem routes
|
|
32
|
+
* @throws Error if plugin not activated
|
|
33
|
+
*/
|
|
34
|
+
getRouter(): Router;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Factory function to create plugin instance
|
|
38
|
+
*
|
|
39
|
+
* @returns FileSystemServerPlugin instance
|
|
40
|
+
*/
|
|
41
|
+
export declare function createFileSystemServerPlugin(): PluginModule;
|
|
42
|
+
//# sourceMappingURL=filesystem-server-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-server-plugin.d.ts","sourceRoot":"","sources":["../../src/plugin/filesystem-server-plugin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAWzD;;;;GAIG;AACH,qBAAa,sBAAuB,YAAW,YAAY;IACzD,OAAO,CAAC,MAAM,CAAC,CAAS;IAExB;;OAEG;IACH,UAAU,CAAC,GAAG,EAAE,qBAAqB,GAAG,IAAI;IAqB5C;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAiBpC;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;;;;OAKG;IACH,SAAS,IAAI,MAAM;CAQpB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,YAAY,CAE3D"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileSystem Server Plugin
|
|
3
|
+
*
|
|
4
|
+
* Microkernel plugin for backend filesystem server
|
|
5
|
+
*/
|
|
6
|
+
import { FILESYSTEM_SERVER_CONFIG_TOKEN, WORKSPACE_MANAGER_TOKEN, } from '@hamak/filesystem-server-api';
|
|
7
|
+
import { WorkspaceManager } from '../services/workspace-manager';
|
|
8
|
+
import { createFileSystemRouter } from '../routing/create-router';
|
|
9
|
+
/**
|
|
10
|
+
* FileSystem Server Plugin Implementation
|
|
11
|
+
*
|
|
12
|
+
* Provides backend filesystem operations via Express routes
|
|
13
|
+
*/
|
|
14
|
+
export class FileSystemServerPlugin {
|
|
15
|
+
/**
|
|
16
|
+
* Initialize phase - register services in DI container
|
|
17
|
+
*/
|
|
18
|
+
initialize(ctx) {
|
|
19
|
+
console.log('[FileSystemServerPlugin] Initializing...');
|
|
20
|
+
// Resolve configuration
|
|
21
|
+
const config = ctx.resolve(FILESYSTEM_SERVER_CONFIG_TOKEN);
|
|
22
|
+
// Create and register WorkspaceManager service
|
|
23
|
+
const workspaceManager = new WorkspaceManager(config.workspaces, {
|
|
24
|
+
baseDirectory: config.baseDirectory,
|
|
25
|
+
});
|
|
26
|
+
ctx.provide({
|
|
27
|
+
provide: WORKSPACE_MANAGER_TOKEN,
|
|
28
|
+
useValue: workspaceManager,
|
|
29
|
+
});
|
|
30
|
+
console.log('[FileSystemServerPlugin] WorkspaceManager service registered');
|
|
31
|
+
console.log(`[FileSystemServerPlugin] Base directory: ${config.baseDirectory}`);
|
|
32
|
+
console.log(`[FileSystemServerPlugin] Workspaces:`, Object.keys(config.workspaces));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Activate phase - create router and make ready for use
|
|
36
|
+
*/
|
|
37
|
+
activate(ctx) {
|
|
38
|
+
console.log('[FileSystemServerPlugin] Activating...');
|
|
39
|
+
// Resolve dependencies
|
|
40
|
+
const workspaceManager = ctx.resolve(WORKSPACE_MANAGER_TOKEN);
|
|
41
|
+
const config = ctx.resolve(FILESYSTEM_SERVER_CONFIG_TOKEN);
|
|
42
|
+
// Create Express router
|
|
43
|
+
this.router = createFileSystemRouter(workspaceManager, config);
|
|
44
|
+
// Note: Router is available via getRouter() method, not registered in DI
|
|
45
|
+
// This avoids circular dependencies and keeps the plugin self-contained
|
|
46
|
+
console.log('[FileSystemServerPlugin] Activated successfully');
|
|
47
|
+
console.log(`[FileSystemServerPlugin] Mount path: ${config.mountPath ?? '/api/workspaces'}`);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Deactivate phase - cleanup
|
|
51
|
+
*/
|
|
52
|
+
deactivate() {
|
|
53
|
+
console.log('[FileSystemServerPlugin] Deactivating...');
|
|
54
|
+
this.router = undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the Express router for mounting in application
|
|
58
|
+
*
|
|
59
|
+
* @returns Express router with filesystem routes
|
|
60
|
+
* @throws Error if plugin not activated
|
|
61
|
+
*/
|
|
62
|
+
getRouter() {
|
|
63
|
+
if (!this.router) {
|
|
64
|
+
throw new Error('[FileSystemServerPlugin] Plugin not activated. Call kernel.activate() first.');
|
|
65
|
+
}
|
|
66
|
+
return this.router;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Factory function to create plugin instance
|
|
71
|
+
*
|
|
72
|
+
* @returns FileSystemServerPlugin instance
|
|
73
|
+
*/
|
|
74
|
+
export function createFileSystemServerPlugin() {
|
|
75
|
+
return new FileSystemServerPlugin();
|
|
76
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates and configures the Express router for filesystem operations
|
|
5
|
+
*/
|
|
6
|
+
import { Router } from 'express';
|
|
7
|
+
import { IWorkspaceManager, FileSystemServerConfig } from '@hamak/filesystem-server-api';
|
|
8
|
+
/**
|
|
9
|
+
* Create filesystem Express router with middleware
|
|
10
|
+
*
|
|
11
|
+
* @param workspaceManager - Workspace manager instance
|
|
12
|
+
* @param config - Server configuration
|
|
13
|
+
* @returns Configured Express router
|
|
14
|
+
*/
|
|
15
|
+
export declare function createFileSystemRouter(workspaceManager: IWorkspaceManager, config: FileSystemServerConfig): Router;
|
|
16
|
+
//# sourceMappingURL=create-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-router.d.ts","sourceRoot":"","sources":["../../src/routing/create-router.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAoC,MAAM,8BAA8B,CAAC;AAG3H;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,gBAAgB,EAAE,iBAAiB,EACnC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,CAiBR"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates and configures the Express router for filesystem operations
|
|
5
|
+
*/
|
|
6
|
+
import { Router } from 'express';
|
|
7
|
+
import cors from 'cors';
|
|
8
|
+
import bodyParser from 'body-parser';
|
|
9
|
+
import { DEFAULT_FILESYSTEM_SERVER_CONFIG } from '@hamak/filesystem-server-api';
|
|
10
|
+
import { FileRouter } from './file-router';
|
|
11
|
+
/**
|
|
12
|
+
* Create filesystem Express router with middleware
|
|
13
|
+
*
|
|
14
|
+
* @param workspaceManager - Workspace manager instance
|
|
15
|
+
* @param config - Server configuration
|
|
16
|
+
* @returns Configured Express router
|
|
17
|
+
*/
|
|
18
|
+
export function createFileSystemRouter(workspaceManager, config) {
|
|
19
|
+
const app = Router();
|
|
20
|
+
// Apply CORS middleware if enabled
|
|
21
|
+
if (config.enableCors ?? DEFAULT_FILESYSTEM_SERVER_CONFIG.enableCors) {
|
|
22
|
+
app.use(cors());
|
|
23
|
+
}
|
|
24
|
+
// Apply body parser for JSON payloads
|
|
25
|
+
app.use(bodyParser.json());
|
|
26
|
+
// Create and mount file router
|
|
27
|
+
const fileRouter = new FileRouter(workspaceManager);
|
|
28
|
+
const mountPath = config.mountPath ?? DEFAULT_FILESYSTEM_SERVER_CONFIG.mountPath;
|
|
29
|
+
app.use(mountPath, fileRouter.router);
|
|
30
|
+
return app;
|
|
31
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Router
|
|
3
|
+
*
|
|
4
|
+
* Express routes for filesystem operations
|
|
5
|
+
* Migrated from amk/libs/server/ws/ws-server
|
|
6
|
+
*/
|
|
7
|
+
import { Router } from 'express';
|
|
8
|
+
import { IWorkspaceManager } from '@hamak/filesystem-server-api';
|
|
9
|
+
export declare class FileRouter {
|
|
10
|
+
router: Router;
|
|
11
|
+
private workspaceManager;
|
|
12
|
+
constructor(workspaceManager: IWorkspaceManager);
|
|
13
|
+
private initializeRoutes;
|
|
14
|
+
private parsePath;
|
|
15
|
+
private listFiles;
|
|
16
|
+
private createDirectory;
|
|
17
|
+
private getFile;
|
|
18
|
+
private readFile;
|
|
19
|
+
private writeFile;
|
|
20
|
+
private deleteFile;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=file-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-router.d.ts","sourceRoot":"","sources":["../../src/routing/file-router.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,qBAAa,UAAU;IACd,MAAM,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,gBAAgB,CAAoB;gBAEhC,gBAAgB,EAAE,iBAAiB;IAM/C,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,SAAS;YAKH,SAAS;YAWT,eAAe;YAWf,OAAO;YAWP,QAAQ;YAWR,SAAS;YAYT,UAAU;CAUzB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Router
|
|
3
|
+
*
|
|
4
|
+
* Express routes for filesystem operations
|
|
5
|
+
* Migrated from amk/libs/server/ws/ws-server
|
|
6
|
+
*/
|
|
7
|
+
import { Router } from 'express';
|
|
8
|
+
export class FileRouter {
|
|
9
|
+
constructor(workspaceManager) {
|
|
10
|
+
this.router = Router();
|
|
11
|
+
this.workspaceManager = workspaceManager;
|
|
12
|
+
this.initializeRoutes();
|
|
13
|
+
}
|
|
14
|
+
initializeRoutes() {
|
|
15
|
+
this.router.get('/:workspace/files/*', this.listFiles.bind(this));
|
|
16
|
+
this.router.post('/:workspace/mkdir/*', this.createDirectory.bind(this));
|
|
17
|
+
this.router.get('/:workspace/get/*', this.getFile.bind(this));
|
|
18
|
+
this.router.get('/:workspace/read/*', this.readFile.bind(this));
|
|
19
|
+
this.router.post('/:workspace/post/*', this.writeFile.bind(this));
|
|
20
|
+
this.router.put('/:workspace/put/*', this.writeFile.bind(this));
|
|
21
|
+
this.router.delete('/:workspace/delete/*', this.deleteFile.bind(this));
|
|
22
|
+
}
|
|
23
|
+
parsePath(pathString) {
|
|
24
|
+
// Split path string into segments, filter out empty segments
|
|
25
|
+
return pathString.split('/').filter(seg => seg.length > 0);
|
|
26
|
+
}
|
|
27
|
+
async listFiles(req, res) {
|
|
28
|
+
try {
|
|
29
|
+
const { workspace } = req.params;
|
|
30
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
31
|
+
const files = await this.workspaceManager.listFiles(workspace, pathSegments);
|
|
32
|
+
res.json(files);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
res.status(500).json({ error: error.message });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async createDirectory(req, res) {
|
|
39
|
+
try {
|
|
40
|
+
const { workspace } = req.params;
|
|
41
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
42
|
+
const dirInfo = await this.workspaceManager.createDirectory(workspace, pathSegments);
|
|
43
|
+
res.json(dirInfo);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
res.status(500).json({ error: error.message });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async getFile(req, res) {
|
|
50
|
+
try {
|
|
51
|
+
const { workspace } = req.params;
|
|
52
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
53
|
+
const fileInfo = await this.workspaceManager.getFile(workspace, pathSegments);
|
|
54
|
+
res.json(fileInfo);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
res.status(500).json({ error: error.message });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async readFile(req, res) {
|
|
61
|
+
try {
|
|
62
|
+
const { workspace } = req.params;
|
|
63
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
64
|
+
const fileInfo = await this.workspaceManager.readFile(workspace, pathSegments);
|
|
65
|
+
res.json(fileInfo);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
res.status(500).json({ error: error.message });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async writeFile(req, res) {
|
|
72
|
+
try {
|
|
73
|
+
const { workspace } = req.params;
|
|
74
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
75
|
+
const { content } = req.body;
|
|
76
|
+
const fileInfo = await this.workspaceManager.writeFile(workspace, pathSegments, content);
|
|
77
|
+
res.json(fileInfo);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
res.status(500).json({ error: error.message });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async deleteFile(req, res) {
|
|
84
|
+
try {
|
|
85
|
+
const { workspace } = req.params;
|
|
86
|
+
const pathSegments = this.parsePath(req.params[0] || '');
|
|
87
|
+
const fileInfo = await this.workspaceManager.deleteFile(workspace, pathSegments);
|
|
88
|
+
res.json(fileInfo);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
res.status(500).json({ error: error.message });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Manager Implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides filesystem operations for workspace-based storage
|
|
5
|
+
* Migrated from amk/libs/server/ws/ws-server
|
|
6
|
+
*/
|
|
7
|
+
import { FileInfo } from '@hamak/shared-utils';
|
|
8
|
+
import { IWorkspaceManager } from '@hamak/filesystem-server-api';
|
|
9
|
+
export interface WorkspaceManagerOptions {
|
|
10
|
+
baseDirectory: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class WorkspaceManager implements IWorkspaceManager {
|
|
13
|
+
private workspaces;
|
|
14
|
+
private options;
|
|
15
|
+
constructor(workspaces: Record<string, string>, options: WorkspaceManagerOptions);
|
|
16
|
+
private resolvePath;
|
|
17
|
+
listFiles(workspace: string, filePath: string | string[]): Promise<FileInfo[]>;
|
|
18
|
+
createDirectory(workspace: string, filePath: string | string[]): Promise<FileInfo>;
|
|
19
|
+
getFile(workspace: string, filePath: string | string[]): Promise<FileInfo>;
|
|
20
|
+
readFile(workspace: string, filePath: string | string[]): Promise<FileInfo>;
|
|
21
|
+
writeFile(workspace: string, filePath: string | string[], content: string): Promise<FileInfo>;
|
|
22
|
+
deleteFile(workspace: string, filePath: string | string[]): Promise<FileInfo>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=workspace-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-manager.d.ts","sourceRoot":"","sources":["../../src/services/workspace-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE,MAAM,WAAW,uBAAuB;IACtC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,OAAO,CAA0B;gBAE7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,uBAAuB;IAKhF,OAAO,CAAC,WAAW;IASN,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAoB9E,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAclF,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAgB1E,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAgB3E,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAe7F,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;CAa3F"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Manager Implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides filesystem operations for workspace-based storage
|
|
5
|
+
* Migrated from amk/libs/server/ws/ws-server
|
|
6
|
+
*/
|
|
7
|
+
import { promises as fs } from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
export class WorkspaceManager {
|
|
10
|
+
constructor(workspaces, options) {
|
|
11
|
+
this.workspaces = workspaces;
|
|
12
|
+
this.options = options;
|
|
13
|
+
}
|
|
14
|
+
resolvePath(workspace, filePath) {
|
|
15
|
+
const basePath = path.resolve(this.options.baseDirectory, this.workspaces[workspace]);
|
|
16
|
+
if (!basePath) {
|
|
17
|
+
throw new Error(`Workspace ${workspace} not found`);
|
|
18
|
+
}
|
|
19
|
+
const relativePath = Array.isArray(filePath) ? filePath.join(path.sep) : filePath;
|
|
20
|
+
return path.resolve(basePath, relativePath);
|
|
21
|
+
}
|
|
22
|
+
async listFiles(workspace, filePath) {
|
|
23
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
24
|
+
const files = await fs.readdir(absolutePath, { withFileTypes: true });
|
|
25
|
+
const fileInfos = await Promise.all(files.map(async (file) => {
|
|
26
|
+
const fullPath = path.join(absolutePath, file.name);
|
|
27
|
+
const stats = await fs.stat(fullPath);
|
|
28
|
+
return {
|
|
29
|
+
name: file.name,
|
|
30
|
+
path: fullPath,
|
|
31
|
+
isDirectory: file.isDirectory(),
|
|
32
|
+
size: stats.size,
|
|
33
|
+
createdAt: stats.birthtime,
|
|
34
|
+
updatedAt: stats.mtime,
|
|
35
|
+
};
|
|
36
|
+
}));
|
|
37
|
+
return fileInfos;
|
|
38
|
+
}
|
|
39
|
+
async createDirectory(workspace, filePath) {
|
|
40
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
41
|
+
await fs.mkdir(absolutePath, { recursive: true });
|
|
42
|
+
const stats = await fs.stat(absolutePath);
|
|
43
|
+
return {
|
|
44
|
+
name: path.basename(absolutePath),
|
|
45
|
+
path: absolutePath,
|
|
46
|
+
isDirectory: true,
|
|
47
|
+
size: stats.size,
|
|
48
|
+
createdAt: stats.birthtime,
|
|
49
|
+
updatedAt: stats.mtime,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async getFile(workspace, filePath) {
|
|
53
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
54
|
+
const stats = await fs.stat(absolutePath);
|
|
55
|
+
if (stats.isDirectory()) {
|
|
56
|
+
throw new Error('Path is a directory');
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
name: path.basename(absolutePath),
|
|
60
|
+
path: absolutePath,
|
|
61
|
+
isDirectory: false,
|
|
62
|
+
size: stats.size,
|
|
63
|
+
createdAt: stats.birthtime,
|
|
64
|
+
updatedAt: stats.mtime,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async readFile(workspace, filePath) {
|
|
68
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
69
|
+
const data = await fs.readFile(absolutePath, 'utf-8');
|
|
70
|
+
const stats = await fs.stat(absolutePath);
|
|
71
|
+
return {
|
|
72
|
+
name: path.basename(absolutePath),
|
|
73
|
+
path: absolutePath,
|
|
74
|
+
isDirectory: false,
|
|
75
|
+
size: stats.size,
|
|
76
|
+
createdAt: stats.birthtime,
|
|
77
|
+
updatedAt: stats.mtime,
|
|
78
|
+
content: data, // Include file content
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async writeFile(workspace, filePath, content) {
|
|
82
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
83
|
+
const data = typeof content === "string" ? content : JSON.stringify(content ?? "");
|
|
84
|
+
await fs.writeFile(absolutePath, data);
|
|
85
|
+
const stats = await fs.stat(absolutePath);
|
|
86
|
+
return {
|
|
87
|
+
name: path.basename(absolutePath),
|
|
88
|
+
path: absolutePath,
|
|
89
|
+
isDirectory: false,
|
|
90
|
+
size: stats.size,
|
|
91
|
+
createdAt: stats.birthtime,
|
|
92
|
+
updatedAt: stats.mtime,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async deleteFile(workspace, filePath) {
|
|
96
|
+
const absolutePath = this.resolvePath(workspace, filePath);
|
|
97
|
+
const stats = await fs.stat(absolutePath);
|
|
98
|
+
await fs.rm(absolutePath, { recursive: true, force: true });
|
|
99
|
+
return {
|
|
100
|
+
name: path.basename(absolutePath),
|
|
101
|
+
path: absolutePath,
|
|
102
|
+
isDirectory: stats.isDirectory(),
|
|
103
|
+
size: stats.size,
|
|
104
|
+
createdAt: stats.birthtime,
|
|
105
|
+
updatedAt: stats.mtime,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hamak/filesystem-server-impl",
|
|
3
|
+
"version": "0.4.8",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "FileSystem Server Implementation - Backend filesystem server with Express routes",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/amah/app-framework.git",
|
|
16
|
+
"directory": "packages/backend/filesystem-server/filesystem-server-impl"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -p tsconfig.json && tsc -p tsconfig.es2015.json",
|
|
23
|
+
"clean": "rm -rf dist",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest"
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"require": "./dist/es2015/index.js",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./es2015": {
|
|
35
|
+
"require": "./dist/es2015/index.js",
|
|
36
|
+
"default": "./dist/es2015/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@hamak/filesystem-server-api": "0.4.7",
|
|
41
|
+
"@hamak/filesystem-server-spi": "0.4.7",
|
|
42
|
+
"@hamak/microkernel-api": "0.4.7",
|
|
43
|
+
"@hamak/microkernel-spi": "0.4.7",
|
|
44
|
+
"@hamak/shared-utils": "0.4.7",
|
|
45
|
+
"cors": "^2.8.5",
|
|
46
|
+
"express": "^4.18.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/cors": "^2.8.0",
|
|
50
|
+
"@types/express": "^4.17.0",
|
|
51
|
+
"typescript": "~5.4.0",
|
|
52
|
+
"vitest": "^2.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|