Files
Ready2Blob/src/schemas/registry.ts
T
kawa b8c64937ea feat(13-01): add 18 backends to BACKEND_REGISTRY with category field
- Removed explicit BackendType union literal; derived via keyof typeof BACKEND_REGISTRY
- Added BackendCategory type and category field to all 18 backend entries
- Added BackendMeta interface for individual entry typing
- Removed circular Record<BackendType,...> cast; replaced with as const
- Added 11 new backends: azure-files, swift, gdrive, dropbox, box, pcloud, ftp, webdav, smb, http, seafile
- Updated registry.test.ts: 48 tests covering all 18 backends, category assertions, field validation
2026-04-01 16:16:58 +02:00

643 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// src/schemas/registry.ts
// Backend Schema Registry — single source of truth for all rclone backend field definitions.
// IMPORTANT: FieldDef.key values MUST match rclone config key names exactly.
// These keys are used by Phase 2 generators to build rclone.conf INI content.
// Verify against https://rclone.org/azureblob/ and https://rclone.org/s3/ before Phase 2.
//
// BackendType is DERIVED from keyof typeof BACKEND_REGISTRY — do NOT add a manual union.
// BackendCategory is the grouping used for UI categorization and search.
export type BackendCategory = 'cloud-object-storage' | 'cloud-drives' | 'protocol-based';
export interface FieldDef {
key: string; // MUST match rclone config key exactly (snake_case)
label: string;
inputType: 'text' | 'password' | 'select' | 'toggle';
required: boolean;
placeholder?: string;
helpText?: string;
options?: { value: string; label: string }[]; // for inputType: 'select'
validate?: { regex: RegExp; message: string };
tooltipText?: string;
}
export interface BackendMeta {
displayName: string;
description: string;
category: BackendCategory;
fields: FieldDef[];
}
export const BACKEND_REGISTRY = {
azureblob: {
displayName: 'Azure Blob Storage',
description: 'Microsoft Azure cloud storage',
category: 'cloud-object-storage' as const,
fields: [
{
key: 'account',
label: 'Storage Account Name',
inputType: 'text' as const,
required: true,
placeholder: 'mystorageaccount',
helpText: 'The storage account name (not the full URL)',
validate: {
regex: /^[a-z0-9]{3,24}$/,
message: 'Must be 324 lowercase alphanumeric characters (no hyphens or uppercase)',
},
},
{
key: 'key',
label: 'Access Key',
inputType: 'password' as const,
required: false,
helpText: 'Base64-encoded storage account key. Provide either this or a SAS URL, not both.',
tooltipText: 'The full storage account key grants unrestricted read/write access to all containers in the account. Keep this secret. If you only need limited access, use a SAS URL instead.',
},
{
key: 'sas_url',
label: 'SAS URL',
inputType: 'password' as const,
required: false,
placeholder: 'https://mystorageaccount.blob.core.windows.net/?sv=...',
helpText: 'Full SAS URL including account and container. Provide either this or an access key, not both.',
tooltipText: 'A SAS URL (Shared Access Signature) bundles the storage endpoint with a time-limited, scope-limited token. It grants access only to containers you specify and expires automatically. Use this if you want limited-access credentials. If you have the full account key, switch to Access Key.',
},
],
},
s3: {
displayName: 'Amazon S3',
description: 'AWS Simple Storage Service',
category: 'cloud-object-storage' as const,
fields: [
{
key: 'provider',
label: 'Provider',
inputType: 'select' as const,
required: true,
options: [{ value: 'AWS', label: 'Amazon S3' }],
},
{
key: 'access_key_id',
label: 'Access Key ID',
inputType: 'text' as const,
required: true,
placeholder: 'AKIAIOSFODNN7EXAMPLE',
helpText: 'The access key ID from your IAM credentials (starts with AKIA for long-term keys).',
tooltipText: 'Find this in the AWS Console under IAM > Users > select user > Security credentials > Access keys. If you only have the secret key, you cannot recover the Key ID — create a new access key pair instead.',
},
{
key: 'secret_access_key',
label: 'Secret Access Key',
inputType: 'password' as const,
required: true,
helpText: 'The secret access key paired with your Access Key ID. Only shown once at creation time.',
tooltipText: 'This value is shown only once when you create an access key in IAM. If you did not save it, delete the existing key and create a new one. Never share or commit this value — treat it as a password.',
},
{
key: 'region',
label: 'Region',
inputType: 'text' as const,
required: true,
placeholder: 'us-east-1',
tooltipText: 'The AWS region where your S3 bucket is located. Find this in the S3 console next to your bucket name (e.g. us-east-1, eu-west-2, ap-southeast-1).',
validate: {
regex: /^[a-z][a-z0-9-]+[a-z0-9]$/,
message: 'Must be a valid AWS region format (e.g. us-east-1)',
},
},
],
},
's3-compatible': {
displayName: 'S3-Compatible',
description: 'Wasabi, MinIO, Cloudflare R2, and others',
category: 'cloud-object-storage' as const,
fields: [
{
key: 'provider',
label: 'Provider',
inputType: 'select' as const,
required: true,
options: [{ value: 'Other', label: 'S3-Compatible' }],
helpText: 'Covers Wasabi, MinIO, Cloudflare R2, and any S3-compatible storage',
},
{
key: 'access_key_id',
label: 'Access Key ID',
inputType: 'text' as const,
required: true,
helpText: 'The access key ID from your storage provider\'s dashboard.',
tooltipText: 'Where to find this varies by provider: Wasabi — Account > Access Keys; Cloudflare R2 — R2 > Manage R2 API Tokens; MinIO — admin console > Identity > Users or Service Accounts.',
},
{
key: 'secret_access_key',
label: 'Secret Access Key',
inputType: 'password' as const,
required: true,
helpText: 'The secret key paired with your Access Key ID.',
tooltipText: 'This value is typically shown only once when you create the key. Store it securely — if lost, you will need to generate a new key pair.',
},
{
key: 'endpoint',
label: 'Endpoint URL',
inputType: 'text' as const,
required: true,
placeholder: 'https://s3.wasabisys.com',
helpText: 'The S3-compatible endpoint URL for your storage provider',
tooltipText: 'Common endpoints: Wasabi — https://s3.wasabisys.com (or region-specific, e.g. https://s3.us-west-1.wasabisys.com); Cloudflare R2 — https://{account_id}.r2.cloudflarestorage.com; MinIO — your server URL with port (e.g. https://minio.example.com:9000).',
},
{
key: 'region',
label: 'Region',
inputType: 'text' as const,
required: false,
placeholder: 'us-east-1',
helpText: 'Optional for most S3-compatible providers',
},
],
},
gcs: {
displayName: 'Google Cloud Storage',
description: 'Google Cloud Storage',
category: 'cloud-object-storage' as const,
fields: [
{
key: 'project_number',
label: 'Project Number',
inputType: 'text' as const,
required: true,
placeholder: '123456789',
helpText: 'Your GCP project number (not project ID)',
tooltipText: 'Found in Google Cloud Console > select your project > Dashboard > Project info card. This is the numeric ID (e.g. 123456789012) — not the text-based project ID like "my-project-name".',
validate: {
regex: /^\d+$/,
message: 'Must contain digits only',
},
},
{
key: 'service_account_credentials',
label: 'Service Account JSON',
inputType: 'password' as const,
required: true,
placeholder: '{"type":"service_account","project_id":"..."}',
helpText: 'Paste the full content of your service account JSON key file',
tooltipText: 'Create a service account key at Google Cloud Console > IAM & Admin > Service Accounts > select or create an account > Keys > Add Key > Create new key > JSON. Paste the entire contents of the downloaded JSON file here. See https://cloud.google.com/iam/docs/keys-create-delete for detailed steps.',
},
],
},
b2: {
displayName: 'Backblaze B2',
description: 'Backblaze B2 Cloud Storage',
category: 'cloud-object-storage' as const,
fields: [
{
key: 'account',
label: 'Application Key ID',
inputType: 'text' as const,
required: true,
placeholder: 'your-application-key-id',
helpText: 'The applicationKeyId — not the master account ID',
tooltipText: 'Found in Backblaze B2 > App Keys. The applicationKeyId is the shorter alphanumeric ID listed next to your key — not the master account ID shown in your account settings.',
},
{
key: 'key',
label: 'Application Key',
inputType: 'password' as const,
required: true,
helpText: 'The application key (secret value from the B2 dashboard)',
tooltipText: 'The application key value is shown only once when you create a new app key in B2 > App Keys. Copy it immediately — it cannot be retrieved later. If lost, delete the key and create a new one.',
},
],
},
'azure-files': {
displayName: 'Azure Files',
description: 'Microsoft Azure Files (SMB/REST cloud file shares)',
category: 'cloud-object-storage' as const,
fields: [
{
key: 'account',
label: 'Storage Account Name',
inputType: 'text' as const,
required: true,
placeholder: 'mystorageaccount',
helpText: 'The Azure storage account name',
validate: {
regex: /^[a-z0-9]{3,24}$/,
message: 'Must be 324 lowercase alphanumeric characters',
},
},
{
key: 'key',
label: 'Storage Account Key',
inputType: 'password' as const,
required: true,
helpText: 'The base64-encoded storage account key from the Azure portal',
tooltipText: 'Found in Azure Portal > your storage account > Security + Networking > Access keys. Use key1 or key2 — either works.',
},
],
},
swift: {
displayName: 'OpenStack Swift',
description: 'OpenStack Swift object storage',
category: 'cloud-object-storage' as const,
fields: [
{
key: 'user',
label: 'Username',
inputType: 'text' as const,
required: true,
helpText: 'The OpenStack username (or tenant:user for v2 auth)',
},
{
key: 'key',
label: 'API Key / Password',
inputType: 'password' as const,
required: true,
helpText: 'The OpenStack API key or password for this user',
},
{
key: 'auth',
label: 'Auth URL',
inputType: 'text' as const,
required: true,
placeholder: 'https://auth.example.com/v3',
helpText: 'The OpenStack identity (Keystone) auth endpoint URL',
tooltipText: 'This is the Keystone endpoint for your OpenStack provider (v2 or v3). Check your provider\'s dashboard or ask your administrator.',
},
{
key: 'tenant',
label: 'Tenant / Project Name',
inputType: 'text' as const,
required: true,
helpText: 'The OpenStack tenant (v2) or project name (v3)',
},
{
key: 'region',
label: 'Region',
inputType: 'text' as const,
required: false,
helpText: 'The OpenStack region (optional — leave blank if your provider has a single region)',
},
],
},
onedrive: {
displayName: 'OneDrive',
description: 'Microsoft OneDrive (paste pre-obtained rclone token)',
category: 'cloud-drives' as const,
fields: [
{
key: 'token',
label: 'OAuth Token (JSON)',
inputType: 'password' as const,
required: true,
placeholder: '{"access_token":"...","token_type":"Bearer","refresh_token":"...","expiry":"..."}',
helpText: 'Paste the JSON token from: rclone authorize "onedrive"',
tooltipText: 'This is the JSON token obtained by running `rclone authorize "onedrive"` on a machine with a browser. The command opens a browser window, you authenticate, and rclone prints a JSON token — paste that entire JSON blob here.',
},
{
key: 'drive_id',
label: 'Drive ID',
inputType: 'text' as const,
required: true,
placeholder: 'b!...',
helpText: 'The drive ID from your OneDrive. Found in the rclone authorize output.',
tooltipText: 'The drive ID appears in the JSON output of `rclone authorize "onedrive"` — look for the "drive_id" field in the response. It typically starts with "b!" for business/SharePoint drives. Personal OneDrive drive IDs look like a UUID.',
},
{
key: 'drive_type',
label: 'Drive Type',
inputType: 'select' as const,
required: true,
options: [
{ value: 'personal', label: 'Personal' },
{ value: 'business', label: 'Business' },
{ value: 'documentLibrary', label: 'SharePoint Document Library' },
],
helpText: 'Personal for consumer OneDrive, Business for Microsoft 365',
},
],
},
gdrive: {
displayName: 'Google Drive',
description: 'Google Drive (paste pre-obtained rclone token or service account)',
category: 'cloud-drives' as const,
fields: [
{
key: 'token',
label: 'OAuth Token (JSON)',
inputType: 'password' as const,
required: false,
helpText: 'Paste the JSON token from: rclone authorize "drive". Use this OR a service account JSON, not both.',
tooltipText: 'Run `rclone authorize "drive"` on a machine with a browser. A browser window opens, you sign in with Google, and rclone prints a JSON token — paste that entire JSON blob here.',
},
{
key: 'service_account_credentials',
label: 'Service Account JSON',
inputType: 'password' as const,
required: false,
helpText: 'For unattended/server use: paste the full service account JSON key file content. Use this OR an OAuth token, not both.',
tooltipText: 'Create a service account at Google Cloud Console > IAM & Admin > Service Accounts, share the target Drive with that service account email, then download the JSON key and paste its contents here.',
},
{
key: 'root_folder_id',
label: 'Root Folder ID',
inputType: 'text' as const,
required: false,
helpText: 'Optional: restrict rclone to a specific folder. Leave blank to access the full Drive.',
tooltipText: 'The folder ID is the last segment of the Google Drive folder URL: https://drive.google.com/drive/folders/{FOLDER_ID}',
},
],
},
dropbox: {
displayName: 'Dropbox',
description: 'Dropbox (paste pre-obtained rclone token)',
category: 'cloud-drives' as const,
fields: [
{
key: 'token',
label: 'OAuth Token (JSON)',
inputType: 'password' as const,
required: true,
helpText: 'Paste the JSON token from: rclone authorize "dropbox"',
tooltipText: 'Run `rclone authorize "dropbox"` on a machine with a browser. Sign in to Dropbox when the browser opens, then paste the resulting JSON token here.',
},
],
},
box: {
displayName: 'Box',
description: 'Box cloud storage (paste pre-obtained rclone token)',
category: 'cloud-drives' as const,
fields: [
{
key: 'token',
label: 'OAuth Token (JSON)',
inputType: 'password' as const,
required: true,
helpText: 'Paste the JSON token from: rclone authorize "box"',
tooltipText: 'Run `rclone authorize "box"` on a machine with a browser. Sign in to Box when the browser opens, then paste the resulting JSON token here.',
},
{
key: 'box_sub_type',
label: 'Account Type',
inputType: 'select' as const,
required: false,
options: [
{ value: 'user', label: 'User account (default)' },
{ value: 'enterprise', label: 'Enterprise / Service Account' },
],
helpText: 'Use Enterprise for Box App Users or service accounts',
},
],
},
pcloud: {
displayName: 'pCloud',
description: 'pCloud cloud storage (paste pre-obtained rclone token)',
category: 'cloud-drives' as const,
fields: [
{
key: 'token',
label: 'OAuth Token (JSON)',
inputType: 'password' as const,
required: true,
helpText: 'Paste the JSON token from: rclone authorize "pcloud"',
tooltipText: 'Run `rclone authorize "pcloud"` on a machine with a browser. Sign in to pCloud when the browser opens, then paste the resulting JSON token here.',
},
{
key: 'hostname',
label: 'Server Region',
inputType: 'select' as const,
required: false,
options: [
{ value: 'api.pcloud.com', label: 'US (default)' },
{ value: 'eapi.pcloud.com', label: 'EU' },
],
helpText: 'Select EU if your pCloud account is registered in Europe',
tooltipText: 'pCloud has separate server infrastructure for US and EU users. EU-registered accounts must use eapi.pcloud.com — using the wrong region will return authentication errors.',
},
],
},
sftp: {
displayName: 'SFTP',
description: 'SSH File Transfer Protocol',
category: 'protocol-based' as const,
fields: [
{
key: 'host',
label: 'Host',
inputType: 'text' as const,
required: true,
placeholder: 'sftp.example.com',
helpText: 'The SSH server hostname or IP address',
},
{
key: 'user',
label: 'Username',
inputType: 'text' as const,
required: true,
placeholder: 'admin',
},
{
key: 'pass',
label: 'Password',
inputType: 'password' as const,
required: false,
helpText: 'SFTP password. Note: rclone may require the password to be obscured using `rclone obscure <password>`. If authentication fails, use the obscured value instead of the plain password.',
},
{
key: 'key_pem',
label: 'Private Key (PEM)',
inputType: 'password' as const,
required: false,
helpText: 'Paste your private key in PEM format (-----BEGIN ... PRIVATE KEY-----)',
},
],
},
ftp: {
displayName: 'FTP',
description: 'File Transfer Protocol (supports plain FTP and FTPS)',
category: 'protocol-based' as const,
fields: [
{
key: 'host',
label: 'Host',
inputType: 'text' as const,
required: true,
placeholder: 'ftp.example.com',
helpText: 'The FTP server hostname or IP address',
},
{
key: 'user',
label: 'Username',
inputType: 'text' as const,
required: false,
placeholder: 'anonymous',
helpText: 'Leave blank or enter "anonymous" for anonymous FTP access',
},
{
key: 'pass',
label: 'Password',
inputType: 'password' as const,
required: false,
helpText: 'FTP password. Leave blank for anonymous access.',
},
{
key: 'port',
label: 'Port',
inputType: 'text' as const,
required: false,
placeholder: '21',
helpText: 'FTP port (default: 21)',
},
{
key: 'explicit_tls',
label: 'TLS Mode',
inputType: 'select' as const,
required: false,
options: [
{ value: '', label: 'Plain FTP (no encryption)' },
{ value: 'true', label: 'Explicit FTPS (STARTTLS)' },
],
helpText: 'Use Explicit FTPS for encrypted FTP connections (port 21 + STARTTLS)',
},
],
},
webdav: {
displayName: 'WebDAV',
description: 'WebDAV (NextCloud, SharePoint, OwnCloud, and others)',
category: 'protocol-based' as const,
fields: [
{
key: 'url',
label: 'WebDAV URL',
inputType: 'text' as const,
required: true,
placeholder: 'https://example.com/dav',
helpText: 'The full WebDAV endpoint URL',
tooltipText: 'NextCloud: https://yourserver/remote.php/dav/files/username/ | OwnCloud: https://yourserver/remote.php/webdav/ | SharePoint: https://company.sharepoint.com/sites/mysite/Documents',
},
{
key: 'user',
label: 'Username',
inputType: 'text' as const,
required: true,
helpText: 'The WebDAV username',
},
{
key: 'pass',
label: 'Password',
inputType: 'password' as const,
required: true,
helpText: 'The WebDAV password',
},
{
key: 'vendor',
label: 'Vendor / Server Type',
inputType: 'select' as const,
required: false,
options: [
{ value: 'nextcloud', label: 'NextCloud' },
{ value: 'owncloud', label: 'OwnCloud' },
{ value: 'sharepoint', label: 'SharePoint (Microsoft 365 OAuth)' },
{ value: 'sharepoint-ntlm', label: 'SharePoint (NTLM / on-premises)' },
{ value: 'other', label: 'Other WebDAV server' },
],
helpText: 'Selecting the correct vendor enables server-specific optimizations',
},
],
},
smb: {
displayName: 'SMB / Windows Share',
description: 'SMB / CIFS Windows file shares',
category: 'protocol-based' as const,
fields: [
{
key: 'host',
label: 'Host',
inputType: 'text' as const,
required: true,
placeholder: 'fileserver.example.com',
helpText: 'The SMB server hostname or IP address',
},
{
key: 'user',
label: 'Username',
inputType: 'text' as const,
required: true,
helpText: 'The SMB username (can include domain: DOMAIN\\user)',
},
{
key: 'pass',
label: 'Password',
inputType: 'password' as const,
required: false,
helpText: 'The SMB password. Leave blank for guest/anonymous access.',
},
{
key: 'domain',
label: 'Domain',
inputType: 'text' as const,
required: false,
placeholder: 'WORKGROUP',
helpText: 'Windows domain name (optional, defaults to WORKGROUP)',
},
{
key: 'port',
label: 'Port',
inputType: 'text' as const,
required: false,
placeholder: '445',
helpText: 'SMB port (default: 445)',
},
],
},
http: {
displayName: 'HTTP (read-only)',
description: 'Read-only access to HTTP/HTTPS file listings',
category: 'protocol-based' as const,
fields: [
{
key: 'url',
label: 'URL',
inputType: 'text' as const,
required: true,
placeholder: 'https://example.com/path/',
helpText: 'The base URL of the HTTP file listing. Must expose directory listings.',
tooltipText: 'The HTTP backend is read-only and works with Apache/Nginx directory listings or any server that returns HTML with links. Ensure the URL ends with a trailing slash for directory access.',
},
],
},
seafile: {
displayName: 'Seafile',
description: 'Seafile self-hosted cloud storage',
category: 'protocol-based' as const,
fields: [
{
key: 'url',
label: 'Server URL',
inputType: 'text' as const,
required: true,
placeholder: 'https://cloud.seafile.com',
helpText: 'The Seafile server URL (without trailing slash)',
},
{
key: 'user',
label: 'Email / Username',
inputType: 'text' as const,
required: true,
helpText: 'Your Seafile account email address',
},
{
key: 'pass',
label: 'Password',
inputType: 'password' as const,
required: true,
helpText: 'Your Seafile account password',
},
],
},
} as const;
// BackendType is derived from registry keys — no manual union needed.
// Adding a backend to BACKEND_REGISTRY automatically extends BackendType.
export type BackendType = keyof typeof BACKEND_REGISTRY;