Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ravendb",
"version": "7.1.6",
"version": "7.2.0",
"description": "RavenDB client for Node.js",
"files": [
"dist"
Expand Down
10 changes: 10 additions & 0 deletions src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ export const CONSTANTS = {
SHARD_CONTEXT_PARAMETER_NAME: "__shardContext",
},

JavaScript: {
VectorPropertyName: "$vector",
LoadVectorPropertyName: "$loadvector",
LoadVectorEmbeddingSourceDocumentId: "$embeddingSourceDocumentId",
LoadVectorEmbeddingSourceDocumentCollectionName: "$embeddingSourceDocumentCollectionName"
},

PeriodicBackup: {
FULL_BACKUP_EXTENSION: "ravendb-full-backup",
SNAPSHOT_EXTENSION: "ravendb-snapshot",
Expand Down Expand Up @@ -97,6 +104,9 @@ export const HEADERS = {
SHARDED: "Sharded",
ATTACHMENT_HASH: "Attachment-Hash",
ATTACHMENT_SIZE: "Attachment-Size",
ATTACHMENT_REMOTE_PARAMETERS_AT: "Attachment-RemoteParameters-At",
ATTACHMENT_REMOTE_PARAMETERS_FLAGS: "Attachment-RemoteParameters-Flags",
ATTACHMENT_REMOTE_PARAMETERS_IDENTIFIER: "Attachment-RemoteParameters-Identifier",
DATABASE_MISSING: "Database-Missing"
} as const;

Expand Down
4 changes: 4 additions & 0 deletions src/Documents/Attachments/RemoteAttachmentFlags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Flags that indicate the location and characteristics of an attachment.
*/
export type RemoteAttachmentFlags = "None" | "Remote"
70 changes: 70 additions & 0 deletions src/Documents/Attachments/RemoteAttachmentsAzureSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Configuration settings for storing attachments in Azure Blob Storage as part of the remote attachments feature.
*
* This class configures the remote storage destination for attachments using Microsoft Azure Blob Storage.
* Remote attachments allow offloading large attachment files from the local RavenDB database to cloud storage,
* helping to reduce database size and improve performance for scenarios with many or large attachments.
*
* Azure authentication can be configured using either:
* - Account Key: Using accountName and accountKey
* - Shared Access Signature (SAS): Using accountName and sasToken
*/
export class RemoteAttachmentsAzureSettings {
/**
* The name of the Azure Blob Storage container where attachments will be stored.
*
* The storage container name must follow Azure Blob Storage naming rules:
* - Must be between 3 and 63 characters long
* - Must contain only lowercase letters, numbers, and dashes
* - Must start and end with a letter or number
* - Cannot contain consecutive dashes
*
* This property is required for the configuration to be valid.
*/
public storageContainer: string;

/**
* Optional subfolder path within the storage container for organizing attachments.
*
* This property allows you to organize attachments into a specific folder structure within the Azure container.
* For example, you might use different folder names for different databases, environments, or tenants.
* The folder path can include forward slashes to create a multi-level directory structure.
*
* Example: "production/database1" or "attachments/2024"
*/
public remoteFolderName?: string;

/**
* The Azure storage account name.
*
* This is required for authentication and is used in combination with either accountKey or sasToken.
* The storage account must have the appropriate permissions to create and write blobs to the specified container.
*/
public accountName: string;

/**
* The Azure storage account key for authentication.
*
* The account key provides full access to the storage account. Use this property when authenticating
* with the primary or secondary access key from your Azure storage account.
*
* Security Warning: Store this securely and never commit to source control.
* Either accountKey or sasToken must be provided, but not both.
*/
public accountKey?: string;

/**
* The Shared Access Signature (SAS) token for authentication.
*
* SAS tokens provide limited access to resources in your storage account with specific permissions
* and expiration time. This is the recommended approach for production environments as it provides
* more granular control over access.
*
* Either accountKey or sasToken must be provided, but not both.
*/
public sasToken?: string;

public isConfigured(): boolean {
return !!this.storageContainer && !!this.accountName && (!!this.accountKey || !!this.sasToken);
}
}
104 changes: 104 additions & 0 deletions src/Documents/Attachments/RemoteAttachmentsConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { RemoteAttachmentsDestinationConfiguration } from "./RemoteAttachmentsDestinationConfiguration.js";
import { throwError } from "../../Exceptions/index.js";

/**
* Configuration for remote attachments functionality, including destinations, frequency, and upload settings.
*/
export class RemoteAttachmentsConfiguration {
/**
* Dictionary of named remote attachment destinations (case-insensitive keys).
* Key: destination identifier/name
* Value: destination configuration
*/
public destinations: { [key: string]: RemoteAttachmentsDestinationConfiguration };

/**
* The frequency (in seconds) at which the remote attachments process checks for new items to upload.
*/
public checkFrequencyInSec?: number;

/**
* The maximum number of items to process in a single batch.
*/
public maxItemsToProcess?: number;

/**
* The number of concurrent uploads allowed.
*/
public concurrentUploads?: number;

/**
* Whether remote attachments functionality is disabled.
*/
public disabled: boolean;

constructor() {
this.destinations = {};
this.disabled = false;
}

/**
* Checks if this configuration has any valid destination.
*/
public hasDestination(): boolean {
if (this.disabled) {
return false;
}

if (!this.destinations || Object.keys(this.destinations).length === 0) {
return false;
}

for (const key of Object.keys(this.destinations)) {
const destination = this.destinations[key];
if (destination?.hasUploader()) {
return true;
}
}

return false;
}

/**
* Validates the entire configuration.
* @param databaseName Optional database name for error messages
* @throws Error if configuration is invalid
*/
public assertConfiguration(databaseName?: string): void {
const databaseStr = databaseName ? ` for database '${databaseName}'` : "";

if (this.checkFrequencyInSec !== undefined && this.checkFrequencyInSec <= 0) {
throwError("InvalidOperationException", `Remote attachments check frequency${databaseStr} must be greater than 0.`);
}

if (this.maxItemsToProcess !== undefined && this.maxItemsToProcess <= 0) {
throwError("InvalidOperationException", `Max items to process${databaseStr} must be greater than 0.`);
}

if (this.concurrentUploads !== undefined && this.concurrentUploads <= 0) {
throwError("InvalidOperationException", `Concurrent attachments uploads${databaseStr} must be greater than 0.`);
}

if (!this.destinations || Object.keys(this.destinations).length === 0) {
// No destinations configured
return;
}

const keys = new Set<string>();
for (const key of Object.keys(this.destinations)) {
const destination = this.destinations[key];

const lowerKey = key.toLowerCase();
if (keys.has(lowerKey)) {
throwError("InvalidOperationException", `Destination key '${key}' is duplicate. Duplicate keys are not allowed in remote attachments configuration${databaseStr}.`);
}
keys.add(lowerKey);

if (!destination) {
throwError("InvalidOperationException", `Destination configuration for key ${key} is null${databaseStr}.`);
}

destination.assertConfiguration(key, databaseName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { RemoteAttachmentsAzureSettings } from "./RemoteAttachmentsAzureSettings.js";
import { RemoteAttachmentsS3Settings } from "./RemoteAttachmentsS3Settings.js";
import { throwError } from "../../Exceptions/index.js";

/**
* Configuration for a single remote attachment storage destination.
*
* A destination can be either Azure Blob Storage or Amazon S3, but not both.
* Each destination can be individually disabled without removing its configuration.
*/
export class RemoteAttachmentsDestinationConfiguration {
/**
* Whether this destination is disabled.
* When true, attachments will not be uploaded to this destination.
*/
public disabled: boolean;

/**
* Amazon S3 storage settings.
* Either s3Settings or azureSettings must be configured, but not both.
*/
public s3Settings?: RemoteAttachmentsS3Settings;

/**
* Azure Blob Storage settings.
* Either s3Settings or azureSettings must be configured, but not both.
*/
public azureSettings?: RemoteAttachmentsAzureSettings;

/**
* Checks if this destination has a configured uploader.
*/
public hasUploader(): boolean {
if (this.disabled) {
return false;
}

return (this.s3Settings?.isConfigured() ?? false) || (this.azureSettings?.isConfigured() ?? false);
}

/**
* Validates the destination configuration.
* @throws Error if configuration is invalid
*/
public assertConfiguration(key: string, databaseName?: string): void {
const databaseStr = databaseName ? ` for database '${databaseName}'` : "";

const s3Configured = this.s3Settings?.isConfigured() ?? false;
const azureConfigured = this.azureSettings?.isConfigured() ?? false;

if (!s3Configured && !azureConfigured) {
throwError("InvalidOperationException", `Exactly one uploader for RemoteAttachmentsDestinationConfiguration '${key}'${databaseStr} must be configured.`);
}

if (s3Configured && azureConfigured) {
throwError("InvalidOperationException", `Only one uploader for RemoteAttachmentsDestinationConfiguration '${key}'${databaseStr} can be configured.`);
}
}
}
98 changes: 98 additions & 0 deletions src/Documents/Attachments/RemoteAttachmentsS3Settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* Configuration settings for storing remote attachments in Amazon S3 or S3-compatible storage.
*
* This class provides the configuration required to upload and store RavenDB attachments in Amazon S3
* or S3-compatible object storage services.
*
* The configuration supports authentication via AWS access keys, secret keys, and optional session tokens
* for temporary credentials. It also allows customization of storage class, server URL, and path style options
* for compatibility with various S3-compatible storage providers.
*/
export class RemoteAttachmentsS3Settings {
/**
* AWS access key ID used for authentication with Amazon S3.
*
* This is the first part of the AWS credentials pair (Access Key ID and Secret Access Key).
* The access key ID is used to identify the AWS account or IAM user making requests to S3.
*
* For security best practices, consider using IAM roles or temporary credentials via
* awsSessionToken instead of long-term access keys when possible.
*/
public awsAccessKey: string;

/**
* AWS secret access key used for authentication with Amazon S3.
*
* This is the second part of the AWS credentials pair (Access Key ID and Secret Access Key).
* The secret key is used to sign requests to AWS services and should be kept confidential.
*
* Security Warning: Never commit secret keys to source control or expose them in logs or error messages.
* Store them securely using environment variables, configuration management systems, or secret management services.
*/
public awsSecretKey: string;

/**
* AWS session token for temporary security credentials.
*
* Session tokens are used when working with temporary security credentials obtained from
* AWS Security Token Service (STS). These are typically used with IAM roles, federated users,
* or when assuming roles across AWS accounts.
*
* Temporary credentials automatically expire after a specified duration, providing enhanced
* security compared to long-term access keys. This is the recommended authentication method
* for production environments.
*/
public awsSessionToken?: string;

/**
* AWS region name where the S3 bucket is located.
*
* Examples: "us-east-1", "eu-west-1", "ap-southeast-2"
* If not specified, the default region will be used.
*/
public awsRegionName?: string;

/**
* The name of the S3 bucket where attachments will be stored.
*
* The bucket must already exist and the configured credentials must have permission
* to write objects to this bucket.
*/
public bucketName: string;

/**
* Optional subfolder path within the S3 bucket for organizing attachments.
*
* This property allows you to organize attachments into a specific folder structure within the S3 bucket.
* For example, you might use different folder names for different databases, environments, or tenants.
* The folder path can include forward slashes to create a multi-level directory structure.
*
* Example: "production/database1" or "attachments/2024"
*/
public remoteFolderName?: string;

/**
* Custom S3 server URL for S3-compatible storage services.
*
* Use this when connecting to S3-compatible storage providers like MinIO, Wasabi, DigitalOcean Spaces,
* or on-premises S3-compatible solutions. Leave undefined for standard AWS S3.
*
* Example: "https://s3.wasabisys.com" or "https://nyc3.digitaloceanspaces.com"
*/
public customServerUrl?: string;

/**
* Force path-style requests instead of virtual-hosted-style requests.
*
* Path-style: https://s3.amazonaws.com/bucket-name/key
* Virtual-hosted-style: https://bucket-name.s3.amazonaws.com/key
*
* Some S3-compatible services require path-style requests. Set to true for compatibility
* with these services or when using custom domain names.
*/
public forcePathStyle?: boolean;

public isConfigured(): boolean {
return !!this.bucketName && !!this.awsAccessKey && !!this.awsSecretKey;
}
}
Loading