|
|
|
|
@@ -9,16 +9,16 @@
|
|
|
|
|
* File Server Handler Architecture:
|
|
|
|
|
* The system uses handler functions to abstract file server operations, allowing support
|
|
|
|
|
* for different file server implementations (e.g., Plik, AWS S3, custom HTTP server).
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Handler Function Signatures:
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* ```javascript
|
|
|
|
|
* // Upload handler - uploads data to file server and returns URL
|
|
|
|
|
* // The handler is passed to smartsend as fileserverUploadHandler parameter
|
|
|
|
|
* // It receives: (fileserver_url, dataname, data)
|
|
|
|
|
* // Returns: { status, uploadid, fileid, url }
|
|
|
|
|
* async function fileserverUploadHandler(fileserver_url, dataname, data) { ... }
|
|
|
|
|
*
|
|
|
|
|
* async function plik_oneshot_upload(fileserver_url, dataname, data) { ... }
|
|
|
|
|
*
|
|
|
|
|
* // Download handler - fetches data from file server URL with exponential backoff
|
|
|
|
|
* // The handler is passed to smartreceive as fileserverDownloadHandler parameter
|
|
|
|
|
* // It receives: (url, max_retries, base_delay, max_delay, correlation_id)
|
|
|
|
|
@@ -213,73 +213,16 @@ function _deserialize_data(data, type, correlation_id) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper: Upload data to file server
|
|
|
|
|
// Internal wrapper that adds correlation_id logging for smartsend
|
|
|
|
|
async function _upload_to_fileserver(fileserver_url, dataname, data, correlation_id) {
|
|
|
|
|
/**
|
|
|
|
|
* Upload data to HTTP file server (plik-like API)
|
|
|
|
|
*
|
|
|
|
|
* This function implements the plik one-shot upload mode:
|
|
|
|
|
* 1. Creates a one-shot upload session by sending POST request with {"OneShot": true}
|
|
|
|
|
* 2. Uploads the file data as multipart form data
|
|
|
|
|
* 3. Returns identifiers and download URL for the uploaded file
|
|
|
|
|
* Internal upload helper - wraps plik_oneshot_upload to add correlation_id logging
|
|
|
|
|
* This allows smartsend to pass correlation_id for tracing without changing the handler signature
|
|
|
|
|
*/
|
|
|
|
|
log_trace(correlation_id, `Uploading ${dataname} to fileserver: ${fileserver_url}`);
|
|
|
|
|
|
|
|
|
|
// Step 1: Get upload ID and token
|
|
|
|
|
const url_getUploadID = `${fileserver_url}/upload`;
|
|
|
|
|
const headers = {
|
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
|
};
|
|
|
|
|
const body = JSON.stringify({ OneShot: true });
|
|
|
|
|
|
|
|
|
|
let response = await fetch(url_getUploadID, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: headers,
|
|
|
|
|
body: body
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error(`Failed to get upload ID: ${response.status} ${response.statusText}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const responseJson = await response.json();
|
|
|
|
|
const uploadid = responseJson.id;
|
|
|
|
|
const uploadtoken = responseJson.uploadToken;
|
|
|
|
|
|
|
|
|
|
// Step 2: Upload file data
|
|
|
|
|
const url_upload = `${fileserver_url}/file/${uploadid}`;
|
|
|
|
|
|
|
|
|
|
// Create multipart form data
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
// Create a Blob from the Uint8Array
|
|
|
|
|
const blob = new Blob([data], { type: "application/octet-stream" });
|
|
|
|
|
formData.append("file", blob, dataname);
|
|
|
|
|
|
|
|
|
|
response = await fetch(url_upload, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"X-UploadToken": uploadtoken
|
|
|
|
|
},
|
|
|
|
|
body: formData
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error(`Failed to upload file: ${response.status} ${response.statusText}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fileResponseJson = await response.json();
|
|
|
|
|
const fileid = fileResponseJson.id;
|
|
|
|
|
|
|
|
|
|
// Build the download URL
|
|
|
|
|
const url = `${fileserver_url}/file/${uploadid}/${fileid}/${encodeURIComponent(dataname)}`;
|
|
|
|
|
|
|
|
|
|
log_trace(correlation_id, `Uploaded to URL: ${url}`);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
status: response.status,
|
|
|
|
|
uploadid: uploadid,
|
|
|
|
|
fileid: fileid,
|
|
|
|
|
url: url
|
|
|
|
|
};
|
|
|
|
|
const result = await plik_oneshot_upload(fileserver_url, dataname, data);
|
|
|
|
|
log_trace(correlation_id, `Uploaded to URL: ${result.url}`);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper: Fetch data from URL with exponential backoff
|
|
|
|
|
@@ -481,11 +424,14 @@ async function smartsend(subject, data, options = {}) {
|
|
|
|
|
* @param {string} options.receiver_name - Name of the receiver (default: "")
|
|
|
|
|
* @param {string} options.receiver_id - UUID of the receiver (default: "")
|
|
|
|
|
* @param {string} options.reply_to - Topic to reply to (default: "")
|
|
|
|
|
* @param {string} options.reply_to_msg_id - Message ID this message is replying to (default: "")
|
|
|
|
|
* @param {boolean} options.is_publish - Whether to automatically publish the message to NATS (default: true)
|
|
|
|
|
*
|
|
|
|
|
* @returns {Promise<Object>} - A tuple-like object with { env: MessageEnvelope, env_json_str: string }
|
|
|
|
|
*/
|
|
|
|
|
* @param {string} options.reply_to_msg_id - Message ID this message is replying to (default: "")
|
|
|
|
|
* @param {boolean} options.is_publish - Whether to automatically publish the message to NATS (default: true)
|
|
|
|
|
* - When true: Message is published to NATS automatically
|
|
|
|
|
* - When false: Returns (env, env_json_str) without publishing, allowing manual publishing
|
|
|
|
|
* @returns {Promise<Object>} - A tuple-like object with { env: MessageEnvelope, env_json_str: string }
|
|
|
|
|
* - env: MessageEnvelope object with all metadata and payloads
|
|
|
|
|
* - env_json_str: JSON string representation of the envelope for manual publishing
|
|
|
|
|
*/
|
|
|
|
|
const {
|
|
|
|
|
broker_url = DEFAULT_NATS_URL,
|
|
|
|
|
fileserver_url = DEFAULT_FILESERVER_URL,
|
|
|
|
|
@@ -541,8 +487,8 @@ async function smartsend(subject, data, options = {}) {
|
|
|
|
|
// Link path - Upload to HTTP server, send URL via NATS
|
|
|
|
|
log_trace(correlation_id, `Using link transport, uploading to fileserver`);
|
|
|
|
|
|
|
|
|
|
// Upload to HTTP server
|
|
|
|
|
const response = await fileserver_upload_handler(fileserver_url, dataname, payloadBytes, correlation_id);
|
|
|
|
|
// Upload to HTTP server using plik_oneshot_upload handler
|
|
|
|
|
const response = await fileserver_upload_handler(fileserver_url, dataname, payloadBytes);
|
|
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
|
throw new Error(`Failed to upload data to fileserver: ${response.status}`);
|
|
|
|
|
@@ -705,13 +651,18 @@ async function smartreceive(msg, options = {}) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// plik_oneshot_upload - matches Julia plik_oneshot_upload function
|
|
|
|
|
// Upload handler signature: plik_oneshot_upload(fileserver_url, dataname, data)
|
|
|
|
|
// Returns: { status, uploadid, fileid, url }
|
|
|
|
|
async function plik_oneshot_upload(file_server_url, dataname, data) {
|
|
|
|
|
/**
|
|
|
|
|
* Upload a single file to a plik server using one-shot mode
|
|
|
|
|
* This function uploads raw byte array to a plik server in one-shot mode (no upload session).
|
|
|
|
|
* It first creates a one-shot upload session by sending a POST request with {"OneShot": true},
|
|
|
|
|
* retrieves an upload ID and token, then uploads the file data as multipart form data using the token.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* This is the default upload handler used by smartsend.
|
|
|
|
|
* Custom handlers can be passed via the fileserver_upload_handler option.
|
|
|
|
|
*
|
|
|
|
|
* @param {string} file_server_url - Base URL of the plik server (e.g., "http://localhost:8080")
|
|
|
|
|
* @param {string} dataname - Name of the file being uploaded
|
|
|
|
|
* @param {Uint8Array} data - Raw byte data of the file content
|
|
|
|
|
|