436 lines
12 KiB
JavaScript
436 lines
12 KiB
JavaScript
/**
|
|
* Workspace Manager Module
|
|
* Handles request collections, workspaces, environments, and persistence
|
|
*/
|
|
const WorkspaceManager = (() => {
|
|
"use strict";
|
|
|
|
const STORAGE_KEY = 'DoxaApi.workspaces';
|
|
const CURRENT_WS_KEY = 'DoxaApi.currentWorkspace';
|
|
const ENV_KEY = 'DoxaApi.environments';
|
|
|
|
/**
|
|
* Workspace structure
|
|
*/
|
|
class Workspace {
|
|
constructor(name = 'Default') {
|
|
this.id = generateId();
|
|
this.name = name;
|
|
this.description = '';
|
|
this.collections = [];
|
|
this.createdAt = Date.now();
|
|
this.updatedAt = Date.now();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collection structure (folders for organizing requests)
|
|
*/
|
|
class Collection {
|
|
constructor(name = 'New Collection') {
|
|
this.id = generateId();
|
|
this.name = name;
|
|
this.description = '';
|
|
this.requests = [];
|
|
this.folders = [];
|
|
this.createdAt = Date.now();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Request in a collection
|
|
*/
|
|
class SavedRequest {
|
|
constructor(name = 'New Request', method = 'GET', path = '') {
|
|
this.id = generateId();
|
|
this.name = name;
|
|
this.method = method;
|
|
this.path = path;
|
|
this.description = '';
|
|
this.tags = [];
|
|
this.params = {
|
|
path: {},
|
|
query: {},
|
|
header: {},
|
|
cookie: {}
|
|
};
|
|
this.body = null;
|
|
this.bodyType = 'json';
|
|
this.formData = {};
|
|
this.auth = null;
|
|
this.createdAt = Date.now();
|
|
this.updatedAt = Date.now();
|
|
}
|
|
|
|
clone() {
|
|
const copy = new SavedRequest(this.name, this.method, this.path);
|
|
copy.id = generateId();
|
|
copy.description = this.description;
|
|
copy.tags = [...this.tags];
|
|
copy.params = JSON.parse(JSON.stringify(this.params));
|
|
copy.body = this.body;
|
|
copy.bodyType = this.bodyType;
|
|
copy.formData = { ...this.formData };
|
|
copy.auth = this.auth;
|
|
return copy;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Environment for variable substitution
|
|
*/
|
|
class Environment {
|
|
constructor(name = 'Default') {
|
|
this.id = generateId();
|
|
this.name = name;
|
|
this.description = '';
|
|
this.variables = {};
|
|
this.isActive = false;
|
|
this.createdAt = Date.now();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate unique ID
|
|
*/
|
|
function generateId() {
|
|
return 'id_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
}
|
|
|
|
/**
|
|
* Get all workspaces
|
|
*/
|
|
function getWorkspaces() {
|
|
try {
|
|
const data = localStorage.getItem(STORAGE_KEY);
|
|
return data ? JSON.parse(data).map(deserializeWorkspace) : [];
|
|
} catch (e) {
|
|
console.error('Failed to load workspaces:', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get current active workspace
|
|
*/
|
|
function getCurrentWorkspace() {
|
|
const workspaces = getWorkspaces();
|
|
if (workspaces.length === 0) {
|
|
const ws = new Workspace('Default');
|
|
saveWorkspace(ws);
|
|
return ws;
|
|
}
|
|
return workspaces[0];
|
|
}
|
|
|
|
/**
|
|
* Save workspace
|
|
*/
|
|
function saveWorkspace(workspace) {
|
|
workspace.updatedAt = Date.now();
|
|
const workspaces = getWorkspaces();
|
|
const index = workspaces.findIndex(w => w.id === workspace.id);
|
|
|
|
if (index >= 0) {
|
|
workspaces[index] = workspace;
|
|
} else {
|
|
workspaces.push(workspace);
|
|
}
|
|
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(workspaces));
|
|
return workspace;
|
|
}
|
|
|
|
/**
|
|
* Delete workspace
|
|
*/
|
|
function deleteWorkspace(workspaceId) {
|
|
let workspaces = getWorkspaces();
|
|
workspaces = workspaces.filter(w => w.id !== workspaceId);
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(workspaces));
|
|
}
|
|
|
|
/**
|
|
* Add collection to workspace
|
|
*/
|
|
function addCollection(workspaceId, collection) {
|
|
const workspace = getWorkspaces().find(w => w.id === workspaceId);
|
|
if (workspace) {
|
|
workspace.collections.push(collection);
|
|
saveWorkspace(workspace);
|
|
}
|
|
return collection;
|
|
}
|
|
|
|
/**
|
|
* Save request to collection
|
|
*/
|
|
function saveRequestToCollection(workspaceId, collectionId, request) {
|
|
const workspace = getWorkspaces().find(w => w.id === workspaceId);
|
|
if (!workspace) return null;
|
|
|
|
const collection = findCollectionRecursive(workspace.collections, collectionId);
|
|
if (!collection) return null;
|
|
|
|
request.updatedAt = Date.now();
|
|
const index = collection.requests.findIndex(r => r.id === request.id);
|
|
|
|
if (index >= 0) {
|
|
collection.requests[index] = request;
|
|
} else {
|
|
collection.requests.push(request);
|
|
}
|
|
|
|
saveWorkspace(workspace);
|
|
return request;
|
|
}
|
|
|
|
/**
|
|
* Find collection recursively in folder structure
|
|
*/
|
|
function findCollectionRecursive(collections, collectionId) {
|
|
for (const col of collections) {
|
|
if (col.id === collectionId) return col;
|
|
if (col.folders) {
|
|
const found = findCollectionRecursive(col.folders, collectionId);
|
|
if (found) return found;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get all requests in workspace
|
|
*/
|
|
function getAllRequests(workspaceId) {
|
|
const workspace = getWorkspaces().find(w => w.id === workspaceId);
|
|
if (!workspace) return [];
|
|
|
|
const requests = [];
|
|
|
|
function traverse(collections) {
|
|
collections.forEach(col => {
|
|
requests.push(...(col.requests || []));
|
|
if (col.folders) {
|
|
traverse(col.folders);
|
|
}
|
|
});
|
|
}
|
|
|
|
traverse(workspace.collections);
|
|
return requests;
|
|
}
|
|
|
|
/**
|
|
* Search requests in workspace
|
|
*/
|
|
function searchRequests(workspaceId, query) {
|
|
const allRequests = getAllRequests(workspaceId);
|
|
const lowerQuery = query.toLowerCase();
|
|
|
|
return allRequests.filter(req =>
|
|
req.name.toLowerCase().includes(lowerQuery) ||
|
|
req.path.toLowerCase().includes(lowerQuery) ||
|
|
req.description.toLowerCase().includes(lowerQuery) ||
|
|
(req.tags && req.tags.some(t => t.toLowerCase().includes(lowerQuery)))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get environments
|
|
*/
|
|
function getEnvironments() {
|
|
try {
|
|
const data = localStorage.getItem(ENV_KEY);
|
|
return data ? JSON.parse(data).map(deserializeEnvironment) : [];
|
|
} catch (e) {
|
|
console.error('Failed to load environments:', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save environment
|
|
*/
|
|
function saveEnvironment(environment) {
|
|
const environments = getEnvironments();
|
|
const index = environments.findIndex(e => e.id === environment.id);
|
|
|
|
if (index >= 0) {
|
|
environments[index] = environment;
|
|
} else {
|
|
environments.push(environment);
|
|
}
|
|
|
|
localStorage.setItem(ENV_KEY, JSON.stringify(environments));
|
|
return environment;
|
|
}
|
|
|
|
/**
|
|
* Delete environment
|
|
*/
|
|
function deleteEnvironment(environmentId) {
|
|
let environments = getEnvironments();
|
|
environments = environments.filter(e => e.id !== environmentId);
|
|
localStorage.setItem(ENV_KEY, JSON.stringify(environments));
|
|
}
|
|
|
|
/**
|
|
* Get active environment
|
|
*/
|
|
function getActiveEnvironment() {
|
|
const environments = getEnvironments();
|
|
return environments.find(e => e.isActive) || null;
|
|
}
|
|
|
|
/**
|
|
* Set active environment
|
|
*/
|
|
function setActiveEnvironment(environmentId) {
|
|
const environments = getEnvironments();
|
|
environments.forEach(e => e.isActive = e.id === environmentId);
|
|
localStorage.setItem(ENV_KEY, JSON.stringify(environments));
|
|
}
|
|
|
|
/**
|
|
* Substitute variables in string
|
|
*/
|
|
function substituteVariables(str, environment) {
|
|
if (!str || !environment) return str;
|
|
|
|
let result = str;
|
|
Object.entries(environment.variables).forEach(([name, value]) => {
|
|
result = result.replace(new RegExp(`\\$\\{${name}\\}|\\$${name}\\b`, 'g'), String(value || ''));
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Export workspace as JSON
|
|
*/
|
|
function exportWorkspace(workspaceId) {
|
|
const workspace = getWorkspaces().find(w => w.id === workspaceId);
|
|
if (!workspace) return null;
|
|
|
|
return {
|
|
version: '1.0',
|
|
exported: new Date().toISOString(),
|
|
workspace: workspace,
|
|
environments: getEnvironments()
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Import workspace from JSON
|
|
*/
|
|
function importWorkspace(json) {
|
|
try {
|
|
const data = typeof json === 'string' ? JSON.parse(json) : json;
|
|
|
|
// Validate version
|
|
if (!data.version) throw new Error('Invalid format: missing version');
|
|
|
|
// Create new workspace with imported data
|
|
const workspace = data.workspace;
|
|
workspace.id = generateId();
|
|
workspace.createdAt = Date.now();
|
|
workspace.updatedAt = Date.now();
|
|
|
|
// Import environments
|
|
if (data.environments && Array.isArray(data.environments)) {
|
|
data.environments.forEach(env => {
|
|
env.id = generateId();
|
|
saveEnvironment(env);
|
|
});
|
|
}
|
|
|
|
// Save workspace
|
|
return saveWorkspace(workspace);
|
|
} catch (e) {
|
|
console.error('Failed to import workspace:', e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Duplicate request
|
|
*/
|
|
function duplicateRequest(request) {
|
|
const duplicate = request.clone();
|
|
duplicate.name = `${request.name} (copy)`;
|
|
return duplicate;
|
|
}
|
|
|
|
/**
|
|
* Duplicate collection
|
|
*/
|
|
function duplicateCollection(collection) {
|
|
const duplicate = new Collection(collection.name + ' (copy)');
|
|
duplicate.description = collection.description;
|
|
duplicate.requests = collection.requests.map(r => r.clone ? r.clone() : r);
|
|
if (collection.folders) {
|
|
duplicate.folders = collection.folders.map(f => duplicateCollection(f));
|
|
}
|
|
return duplicate;
|
|
}
|
|
|
|
/**
|
|
* Get recent requests
|
|
*/
|
|
function getRecentRequests(workspaceId, limit = 10) {
|
|
return getAllRequests(workspaceId)
|
|
.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0))
|
|
.slice(0, limit);
|
|
}
|
|
|
|
/**
|
|
* Deserialize workspace (helper for JSON parsing)
|
|
*/
|
|
function deserializeWorkspace(data) {
|
|
const ws = Object.assign(new Workspace(data.name), data);
|
|
return ws;
|
|
}
|
|
|
|
/**
|
|
* Deserialize environment (helper for JSON parsing)
|
|
*/
|
|
function deserializeEnvironment(data) {
|
|
const env = Object.assign(new Environment(data.name), data);
|
|
return env;
|
|
}
|
|
|
|
// Public API
|
|
return {
|
|
Workspace,
|
|
Collection,
|
|
SavedRequest,
|
|
Environment,
|
|
generateId,
|
|
getWorkspaces,
|
|
getCurrentWorkspace,
|
|
saveWorkspace,
|
|
deleteWorkspace,
|
|
addCollection,
|
|
saveRequestToCollection,
|
|
getAllRequests,
|
|
searchRequests,
|
|
getEnvironments,
|
|
saveEnvironment,
|
|
deleteEnvironment,
|
|
getActiveEnvironment,
|
|
setActiveEnvironment,
|
|
substituteVariables,
|
|
exportWorkspace,
|
|
importWorkspace,
|
|
duplicateRequest,
|
|
duplicateCollection,
|
|
getRecentRequests
|
|
};
|
|
})();
|
|
|
|
// Export for use in other modules
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = WorkspaceManager;
|
|
}
|