@jk3labs/paperclip-plugin-file-browser 0.2.5 → 0.2.6

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/src/index.ts DELETED
@@ -1,128 +0,0 @@
1
- import express from 'express';
2
- import fs from 'fs-extra';
3
- import path from 'path';
4
- import archiver from 'archiver';
5
-
6
- interface PluginContext {
7
- addRouter: (basePath: string, router: express.Router) => void;
8
- environment: {
9
- rootDir: string;
10
- };
11
- };
12
-
13
- const plugin = {
14
- id: 'file-browser',
15
- name: 'File Browser Plugin',
16
- description: 'A plugin to browse, download, and zip files in the Paperclip workspace.',
17
- register: (context: PluginContext) => {
18
- const router = express.Router();
19
- const rootDir = context.environment.rootDir;
20
-
21
- // Validate rootDir exists
22
- if (!rootDir || !(fs.existsSync(rootDir))) {
23
- throw new Error(`Invalid root directory: ${rootDir}`);
24
- }
25
-
26
- router.get('/list', async (req, res) => {
27
- try {
28
- const relativePath = req.query.path as string || '';
29
- const targetPath = path.join(rootDir, relativePath);
30
-
31
- // Security: Ensure targetPath is within rootDir
32
- if (!targetPath.startsWith(rootDir)) {
33
- return res.status(403).json({ error: 'Access denied' });
34
- }
35
-
36
- const entries = await fs.readdir(targetPath, { withFileTypes: true });
37
- const files = entries
38
- .filter(dirent => dirent.isFile())
39
- .map(dirent => dirent.name);
40
- const directories = entries
41
- .filter(dirent => dirent.isDirectory())
42
- .map(dirent => dirent.name);
43
-
44
- res.json({
45
- files,
46
- directories,
47
- currentPath: path.relative(rootDir, targetPath),
48
- rootDir
49
- });
50
- } catch (error) {
51
- const message = error instanceof Error ? error.message : String(error);
52
- const statusCode = (error as NodeJS.ErrnoException).code === 'ENOENT' ? 404 : 500;
53
- res.status(statusCode).json({ error: message });
54
- }
55
- });
56
-
57
- router.get('/download', async (req, res) => {
58
- try {
59
- const relativePath = req.query.path as string;
60
- if (!relativePath) {
61
- return res.status(400).json({ error: 'Path parameter is required' });
62
- }
63
-
64
- const targetPath = path.join(rootDir, relativePath);
65
-
66
- // Security: Ensure targetPath is within rootDir
67
- if (!targetPath.startsWith(rootDir)) {
68
- return res.status(403).json({ error: 'Access denied' });
69
- }
70
-
71
- // Check if path exists and is a file
72
- const stats = await fs.stat(targetPath);
73
- if (!stats.isFile()) {
74
- return res.status(404).json({ error: 'File not found' });
75
- }
76
-
77
- // Stream file for download
78
- res.download(targetPath, path.basename(targetPath));
79
- } catch (error) {
80
- const message = error instanceof Error ? error.message : String(error);
81
- const statusCode = (error as NodeJS.ErrnoException).code === 'ENOENT' ? 404 : 500;
82
- res.status(statusCode).json({ error: message });
83
- }
84
- });
85
-
86
- router.get('/zip', async (req, res) => {
87
- try {
88
- const relativePath = req.query.path as string;
89
- if (!relativePath) {
90
- return res.status(400).json({ error: 'Path parameter is required' });
91
- }
92
-
93
- const targetPath = path.join(rootDir, relativePath);
94
-
95
- // Security: Ensure targetPath is within rootDir
96
- if (!targetPath.startsWith(rootDir)) {
97
- return res.status(403).json({ error: 'Access denied' });
98
- }
99
-
100
- // Check if path exists and is a directory
101
- const stats = await fs.stat(targetPath);
102
- if (!stats.isDirectory()) {
103
- return res.status(404).json({ error: 'Directory not found' });
104
- }
105
-
106
- // Set headers for ZIP download
107
- res.attachment(`${path.basename(targetPath)}.zip`);
108
-
109
- // Create ZIP stream
110
- const archive = archiver('zip', {
111
- zlib: { level: 9 } // Maximum compression
112
- });
113
-
114
- archive.pipe(res);
115
- archive.directory(targetPath, false);
116
- await archive.finalize();
117
- } catch (error) {
118
- const message = error instanceof Error ? error.message : String(error);
119
- const statusCode = (error as NodeJS.ErrnoException).code === 'ENOENT' ? 404 : 500;
120
- res.status(statusCode).json({ error: message });
121
- }
122
- });
123
-
124
- context.addRouter('/files', router);
125
- },
126
- };
127
-
128
- export default plugin;
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "commonjs",
5
- "outDir": "./dist",
6
- "rootDir": "./src",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "types": ["jest", "node"]
12
- },
13
- "include": ["src/**/*", "src/__tests__/**/*"],
14
- "exclude": ["node_modules"]
15
- }