Skip to content

Commit 82ebe36

Browse files
committed
Create UploadInterceptor orchestrator and plugin structure
- Add UploadInterceptor.js orchestrator for managing upload system patches - Implement plugin registration system with hook-based architecture - Create plugin folder structure for ChecksumValidation and UploadVolumeChecker - Add PluginInterface.js documentation for plugin developers - Integrate UploadInterceptor system into main index.js - Support hooks: beforeUpload, onStatusChange, onUploadComplete, onUploadError
1 parent 22c9902 commit 82ebe36

File tree

5 files changed

+334
-0
lines changed

5 files changed

+334
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Plugin Interface Documentation
3+
*
4+
* This file documents the expected structure for UploadInterceptor plugins.
5+
* Plugins should export an object with the following structure:
6+
*/
7+
8+
/**
9+
* Example plugin structure:
10+
*
11+
* export default {
12+
* name: 'ExamplePlugin',
13+
*
14+
* hooks: {
15+
* // Called before upload starts
16+
* beforeUpload: (uploadItem) => {
17+
* console.log('Upload starting for:', uploadItem._label);
18+
* },
19+
*
20+
* // Called when upload status changes
21+
* onStatusChange: (uploadItem, status) => {
22+
* console.log('Upload status changed to:', status);
23+
* },
24+
*
25+
* // Called when upload completes successfully
26+
* onUploadComplete: (uploadItem) => {
27+
* console.log('Upload completed for:', uploadItem._label);
28+
* },
29+
*
30+
* // Called when upload encounters an error
31+
* onUploadError: (uploadItem) => {
32+
* console.error('Upload failed for:', uploadItem._label);
33+
* }
34+
* }
35+
* };
36+
*/
37+
38+
/**
39+
* Available uploadItem properties (from UploaderModel.UploadItem):
40+
* - _file: The File object being uploaded
41+
* - _label: Display name of the file
42+
* - _targetNode: Destination node information
43+
* - _observers: Observer system for status changes
44+
* - Plus other Pydio-specific properties
45+
*/
46+
47+
/**
48+
* Hook execution order:
49+
* 1. beforeUpload - Called immediately when upload starts
50+
* 2. onStatusChange - Called for each status change during upload
51+
* 3. onUploadComplete OR onUploadError - Called when upload finishes
52+
*/
53+
54+
export default {
55+
// This file is for documentation only
56+
// Actual plugins should be in the plugins/ directory
57+
};
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/**
2+
* UploadInterceptor - Orchestrates upload interception and plugin system
3+
*
4+
* This class manages the monkey patching of Pydio's upload system and
5+
* provides a plugin architecture for extending upload functionality.
6+
*/
7+
class UploadInterceptor {
8+
constructor() {
9+
this.plugins = new Map();
10+
this.isPatched = false;
11+
this.originalUploadPresigned = null;
12+
}
13+
14+
/**
15+
* Register a plugin with the upload interceptor
16+
* @param {Object} plugin - Plugin object with name and hooks
17+
* @param {string} plugin.name - Unique plugin name
18+
* @param {Object} plugin.hooks - Event hooks object
19+
* @param {Function} [plugin.hooks.beforeUpload] - Called before upload starts
20+
* @param {Function} [plugin.hooks.onUploadComplete] - Called when upload completes
21+
* @param {Function} [plugin.hooks.onUploadError] - Called when upload errors
22+
* @param {Function} [plugin.hooks.onStatusChange] - Called when upload status changes
23+
*/
24+
register(plugin) {
25+
if (!plugin.name) {
26+
throw new Error('Plugin must have a name');
27+
}
28+
29+
if (this.plugins.has(plugin.name)) {
30+
console.warn(`Plugin '${plugin.name}' is already registered. Overwriting.`);
31+
}
32+
33+
this.plugins.set(plugin.name, plugin);
34+
console.log(`UploadInterceptor: Registered plugin '${plugin.name}'`);
35+
36+
// If we haven't patched yet, patch now that we have plugins
37+
if (!this.isPatched) {
38+
this.patchUploadSystem();
39+
}
40+
}
41+
42+
/**
43+
* Unregister a plugin
44+
* @param {string} pluginName - Name of plugin to remove
45+
*/
46+
unregister(pluginName) {
47+
if (this.plugins.delete(pluginName)) {
48+
console.log(`UploadInterceptor: Unregistered plugin '${pluginName}'`);
49+
}
50+
}
51+
52+
/**
53+
* Get list of registered plugin names
54+
* @returns {string[]} Array of plugin names
55+
*/
56+
getRegisteredPlugins() {
57+
return Array.from(this.plugins.keys());
58+
}
59+
60+
/**
61+
* Monkey patch the Pydio upload system
62+
*/
63+
patchUploadSystem() {
64+
if (this.isPatched) {
65+
console.warn('UploadInterceptor: Upload system already patched');
66+
return;
67+
}
68+
69+
// Wait for UploaderModel to be available
70+
this.waitForUploaderModel().then(() => {
71+
this.applyUploadPatch();
72+
});
73+
}
74+
75+
/**
76+
* Wait for UploaderModel to be defined in the global scope
77+
* @returns {Promise<void>}
78+
*/
79+
async waitForUploaderModel() {
80+
while (typeof UploaderModel === "undefined" || !UploaderModel.UploadItem) {
81+
await new Promise((resolve) => setTimeout(resolve, 100));
82+
}
83+
}
84+
85+
/**
86+
* Apply the actual upload system patch
87+
*/
88+
applyUploadPatch() {
89+
// Store the original uploadPresigned function
90+
this.originalUploadPresigned = UploaderModel.UploadItem.prototype.uploadPresigned;
91+
92+
// Override the uploadPresigned method
93+
UploaderModel.UploadItem.prototype.uploadPresigned = this.createPatchedUploadMethod();
94+
95+
this.isPatched = true;
96+
console.log('UploadInterceptor: Successfully patched upload system');
97+
}
98+
99+
/**
100+
* Create the patched upload method
101+
* @returns {Function} Patched upload method
102+
*/
103+
createPatchedUploadMethod() {
104+
const self = this;
105+
106+
return function() {
107+
// 'this' refers to the UploaderModel.UploadItem instance
108+
const uploadItem = this;
109+
110+
// Execute the original upload logic first
111+
const originalPromise = self.originalUploadPresigned.apply(uploadItem, arguments);
112+
113+
// Call beforeUpload hooks
114+
self.callPluginHooks('beforeUpload', uploadItem);
115+
116+
// Create observer to watch for upload status changes
117+
const observer = (status) => {
118+
// Call onStatusChange hooks
119+
self.callPluginHooks('onStatusChange', uploadItem, status);
120+
121+
// Handle upload completion
122+
if (status === "loaded") {
123+
// Remove observer to prevent multiple calls
124+
self.removeObserver(uploadItem, observer);
125+
126+
// Call onUploadComplete hooks
127+
self.callPluginHooks('onUploadComplete', uploadItem);
128+
} else if (status === "error") {
129+
// Remove observer
130+
self.removeObserver(uploadItem, observer);
131+
132+
// Call onUploadError hooks
133+
self.callPluginHooks('onUploadError', uploadItem);
134+
}
135+
};
136+
137+
// Add observer to the upload item
138+
self.addObserver(uploadItem, observer);
139+
140+
return originalPromise;
141+
};
142+
}
143+
144+
/**
145+
* Call a specific hook on all registered plugins
146+
* @param {string} hookName - Name of hook to call
147+
* @param {...any} args - Arguments to pass to hook
148+
*/
149+
callPluginHooks(hookName, ...args) {
150+
for (const [pluginName, plugin] of this.plugins) {
151+
try {
152+
if (plugin.hooks && typeof plugin.hooks[hookName] === 'function') {
153+
plugin.hooks[hookName](...args);
154+
}
155+
} catch (error) {
156+
console.error(`UploadInterceptor: Error in plugin '${pluginName}' hook '${hookName}':`, error);
157+
}
158+
}
159+
}
160+
161+
/**
162+
* Add observer to upload item
163+
* @param {Object} uploadItem - Upload item instance
164+
* @param {Function} observer - Observer function
165+
*/
166+
addObserver(uploadItem, observer) {
167+
if (!uploadItem._observers) uploadItem._observers = {};
168+
if (!uploadItem._observers.status) uploadItem._observers.status = [];
169+
170+
if (!uploadItem._observers.status.includes(observer)) {
171+
uploadItem._observers.status.push(observer);
172+
}
173+
}
174+
175+
/**
176+
* Remove observer from upload item
177+
* @param {Object} uploadItem - Upload item instance
178+
* @param {Function} observer - Observer function to remove
179+
*/
180+
removeObserver(uploadItem, observer) {
181+
if (uploadItem._observers && uploadItem._observers.status) {
182+
const index = uploadItem._observers.status.indexOf(observer);
183+
if (index > -1) {
184+
uploadItem._observers.status.splice(index, 1);
185+
}
186+
}
187+
}
188+
189+
/**
190+
* Restore the original upload system (for testing/cleanup)
191+
*/
192+
unpatch() {
193+
if (this.isPatched && this.originalUploadPresigned) {
194+
UploaderModel.UploadItem.prototype.uploadPresigned = this.originalUploadPresigned;
195+
this.isPatched = false;
196+
console.log('UploadInterceptor: Unpatched upload system');
197+
}
198+
}
199+
}
200+
201+
// Create singleton instance
202+
const uploadInterceptor = new UploadInterceptor();
203+
204+
export default uploadInterceptor;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* UploadInterceptor System Initialization
3+
*
4+
* This file initializes the upload interceptor system and registers plugins.
5+
* Import this file to activate the upload interception system.
6+
*/
7+
8+
import UploadInterceptor from './UploadInterceptor.js';
9+
import UploadVolumeChecker from './plugins/UploadVolumeChecker/UploadVolumeChecker.js';
10+
11+
// Initialize the upload interceptor system when DOM loads
12+
window.addEventListener("load", () => {
13+
console.log('UploadInterceptor: Initializing upload interception system');
14+
15+
// Register plugins
16+
UploadInterceptor.register(UploadVolumeChecker);
17+
18+
// Note: ChecksumValidation plugin will be registered here after refactoring
19+
20+
console.log('UploadInterceptor: System initialized with plugins:', UploadInterceptor.getRegisteredPlugins());
21+
});
22+
23+
export default UploadInterceptor;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* UploadVolumeChecker Plugin
3+
*
4+
* Monitors and restricts upload volume based on configurable thresholds.
5+
* Provides warnings and blocks uploads that exceed specified limits.
6+
*/
7+
8+
// TODO: Implement volume checking logic
9+
// This is a placeholder for the actual implementation
10+
11+
export default {
12+
name: 'UploadVolumeChecker',
13+
14+
// Configuration (can be made configurable later)
15+
config: {
16+
maxFileCount: 1000, // Maximum number of files in a single upload
17+
warningFileCount: 500, // Show warning at this threshold
18+
maxTotalSize: 10 * 1024 * 1024 * 1024, // 10GB max total size
19+
warningTotalSize: 5 * 1024 * 1024 * 1024, // 5GB warning threshold
20+
},
21+
22+
hooks: {
23+
beforeUpload: (uploadItem) => {
24+
console.log('UploadVolumeChecker: Checking upload volume for:', uploadItem._label);
25+
26+
// TODO: Implement volume checking logic
27+
// - Count files in current upload
28+
// - Calculate total size
29+
// - Check against thresholds
30+
// - Show warnings or block upload
31+
},
32+
33+
onUploadComplete: (uploadItem) => {
34+
console.log('UploadVolumeChecker: Upload completed for:', uploadItem._label);
35+
36+
// TODO: Implement post-upload cleanup
37+
// - Reset counters
38+
// - Log upload statistics
39+
},
40+
41+
onUploadError: (uploadItem) => {
42+
console.log('UploadVolumeChecker: Upload failed for:', uploadItem._label);
43+
44+
// TODO: Implement error handling
45+
// - Reset counters
46+
// - Log error statistics
47+
}
48+
}
49+
};

src/js/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import "../js/core/OAIHarvestClient.js";
66
import "../js/core/publicAccessModifier.js";
77
import "../js/core/tourModifier.js";
88
import "../js/core/ChecksumValidation.js";
9+
import "../js/core/UploadInterceptor/index.js";
910
import "../js/core/PassOaiToChildren.js";
1011
import "../js/core/consoleModifier.js";
1112
import "../js/core/messageModifier.js";

0 commit comments

Comments
 (0)