UNPKG

project-indexer

Version:

Local project indexer with browser trigger support

237 lines (210 loc) 7.74 kB
// import dotenv from 'dotenv'; import axios from 'axios'; import FormData from 'form-data'; import fs from 'fs'; import path from 'path'; import { DEFAULT_EXTENSIONS, getFocusedFiles } from './fileValidator.js'; // dotenv.config(); /** * Indexes the project files and uploads them to the server * @param {string} projectPath - Path to the project directory * @param {string} serverUrl - URL of the server * @param {string} projectId - Project ID * @param {string} token - Authorization token * @param {string} deviceId - Device ID * @param {string[]} extensions - Array of file extensions to include * @returns {Promise<Object>} - Server response */ export async function indexProject( projectPath, serverUrl, projectId, projectName, token, deviceId, extensions = DEFAULT_EXTENSIONS ) { const baseDir = path.resolve(projectPath); console.log(`[Indexing] Starting project indexing from ${baseDir}`); try { // Get files to be indexed using the validator module const files = await getFocusedFiles(baseDir, extensions); console.log(`[Indexing] Found ${files.length} core project files.`); if (files.length === 0) { console.warn('[Warning] No files found to index. Check your ignore patterns and extensions.'); return { success: false, message: 'No files found to index' }; } // Upload the files const result = await uploadFiles(baseDir, files, serverUrl, projectId, projectName, token, deviceId); return result; } catch (error) { console.error(`[Error] Failed to index project: ${error.message}`); throw error; } } /** * Uploads files to the server * @param {string} baseDir - Base directory of the project * @param {string[]} files - Array of absolute file paths * @param {string} serverUrl - URL of the server * @param {string} projectId - Project ID * @param {string} token - Authorization token * @param {string} deviceId - Device ID * @returns {Promise<Object>} - Server response */ async function uploadFiles(baseDir, files, serverUrl, projectId, projectName, token, deviceId) { // Prepare form data for upload const form = new FormData(); const filePaths = []; // Add each file to the form data files.forEach((absPath) => { try { const relativePath = path.relative(baseDir, absPath).replace(/\\/g, '/'); const fileStream = fs.createReadStream(absPath); form.append('files', fileStream, { filename: path.basename(absPath) }); filePaths.push(projectName + '/' + relativePath); } catch (error) { console.warn(`[Warning] Could not read file ${absPath}: ${error.message}`); } }); // Add file paths to the form data filePaths.forEach(fp => form.append('filePaths', fp)); try { console.log(`[Uploading] Uploading ${filePaths.length} files to server...`); filePaths.forEach(fp=>console.log(fp)); // Make the request to the server const response = await axios.post( `${serverUrl}`, form, { headers: { ...form.getHeaders(), 'X-Device-Id': deviceId, Authorization: `Bearer ${token}`, }, maxBodyLength: Infinity, maxContentLength: Infinity, timeout: 60000, // 60 seconds timeout } ); console.log(`[Success] Successfully uploaded ${filePaths.length} files.`); return response.data; } catch (err) { handleUploadError(err); throw err; } } /** * Handles upload errors with detailed logging * @param {Error} err - The error object */ function handleUploadError(err) { console.error(`[Upload failed] ${err.message}`); if (err.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx console.error(`[Server response] Status: ${err.response.status}`); console.error(`[Server response] Headers:`, err.response.headers); console.error(`[Server response] Data:`, err.response.data); } else if (err.request) { // The request was made but no response was received console.error('[Network error] No response received from server'); console.error(`[Request details]:`, err.request); } else { // Something happened in setting up the request that triggered an Error console.error('[Request setup error]', err.message); } if (err.code === 'ECONNABORTED') { console.error('[Timeout] The request timed out. Consider increasing the timeout value or reducing the number of files.'); } } // /** // * Estimates the total size of files to be uploaded // * @param {string[]} files - Array of absolute file paths // * @returns {Promise<number>} - Total size in bytes // */ // export async function estimateUploadSize(files) { // let totalSize = 0; // for (const file of files) { // try { // const stats = await fs.promises.stat(file); // totalSize += stats.size; // } catch (error) { // console.warn(`[Warning] Could not get size of file ${file}: ${error.message}`); // } // } // return totalSize; // } // /** // * Validates server connection before attempting upload // * @param {string} serverUrl - URL of the server // * @param {string} token - Authorization token // * @returns {Promise<boolean>} - True if connection is valid // */ // export async function validateServerConnection(serverUrl, token) { // try { // const response = await axios.get( // `${serverUrl}/health`, // { // headers: { // Authorization: `Bearer ${token}` // }, // timeout: 5000 // 5 seconds timeout // } // ); // return response.status === 200; // } catch (error) { // console.error(`[Connection validation failed] ${error.message}`); // return false; // } // } // /** // * Entry point function that handles the entire indexing process // * @param {Object} options - Options for indexing // * @returns {Promise<Object>} - Result of the indexing process // */ // export async function runIndexing(options) { // const { // projectPath, // serverUrl, // token, // deviceId, // projectId, // projectName, // extensions // } = options; // try { // // Validate server connection // const isConnected = await validateServerConnection(serverUrl, token); // if (!isConnected) { // console.error('[Error] Could not connect to the server. Please check your server URL and token.'); // return { success: false, message: 'Server connection failed' }; // } // // Get or create project ID // let finalProjectId = projectId; // if (!finalProjectId && projectName) { // const project = await createProject(serverUrl, token, deviceId, { name: projectName }); // finalProjectId = project.projectId; // } // if (!finalProjectId) { // console.error('[Error] Project ID is required. Either provide a project ID or a project name to create a new project.'); // return { success: false, message: 'Project ID is required' }; // } // // Run the indexing // const result = await indexProject( // projectPath, // serverUrl, // finalProjectId, // token, // deviceId, // extensions // ); // return { success: true, ...result, projectId: finalProjectId }; // } catch (error) { // console.error(`[Indexing failed] ${error.message}`); // return { success: false, message: error.message }; // } // }