feat(13-04): wire all new backends into RemoteConfigStep
- Replace manual backendLabel record with registry-derived Object.fromEntries - Import OAuthInstructions and GdriveAuthToggle components - Refactor 3-branch ternary into renderBackendFields() switch function - Add gdrive branch: GdriveAuthToggle + generic loop for non-auth fields - Add onedrive branch: OAuthInstructions + generic loop - Add dropbox/box/pcloud branches: OAuthInstructions + generic loop - Default branch covers all other backends (azure-files, swift, ftp, webdav, smb, http, seafile)
This commit is contained in:
@@ -6,13 +6,185 @@ import { useForm } from 'react-hook-form';
|
|||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useWizard } from '../../store/context';
|
import { useWizard } from '../../store/context';
|
||||||
import { BACKEND_REGISTRY } from '../../schemas/registry';
|
import { BACKEND_REGISTRY } from '../../schemas/registry';
|
||||||
|
import type { BackendType } from '../../schemas/registry';
|
||||||
import { BACKEND_SCHEMAS } from '../../schemas';
|
import { BACKEND_SCHEMAS } from '../../schemas';
|
||||||
import { FieldRenderer } from '../ui/FieldRenderer';
|
import { FieldRenderer } from '../ui/FieldRenderer';
|
||||||
import { AzureAuthToggle } from './AzureAuthToggle';
|
import { AzureAuthToggle } from './AzureAuthToggle';
|
||||||
import { SftpAuthToggle } from './SftpAuthToggle';
|
import { SftpAuthToggle } from './SftpAuthToggle';
|
||||||
|
import { OAuthInstructions } from './OAuthInstructions';
|
||||||
|
import { GdriveAuthToggle } from './GdriveAuthToggle';
|
||||||
import type { FieldError, FieldErrors } from 'react-hook-form';
|
import type { FieldError, FieldErrors } from 'react-hook-form';
|
||||||
import { MD3_BTN_FILLED, MD3_BTN_OUTLINED } from '../../styles/md3-buttons';
|
import { MD3_BTN_FILLED, MD3_BTN_OUTLINED } from '../../styles/md3-buttons';
|
||||||
|
|
||||||
|
// backendLabel derived from registry — no manual duplication
|
||||||
|
const backendLabel = Object.fromEntries(
|
||||||
|
Object.entries(BACKEND_REGISTRY).map(([k, v]) => [k, v.displayName])
|
||||||
|
) as Record<BackendType, string>;
|
||||||
|
|
||||||
|
function renderBackendFields(
|
||||||
|
backendType: BackendType,
|
||||||
|
register: ReturnType<typeof useForm>['register'],
|
||||||
|
errors: Record<string, unknown>
|
||||||
|
) {
|
||||||
|
switch (backendType) {
|
||||||
|
case 'azureblob':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Account field via FieldRenderer */}
|
||||||
|
<FieldRenderer
|
||||||
|
field={BACKEND_REGISTRY.azureblob.fields.find(f => f.key === 'account')!}
|
||||||
|
register={register}
|
||||||
|
error={errors.account as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
{/* Auth toggle handles key + sas_url — both always registered */}
|
||||||
|
<AzureAuthToggle
|
||||||
|
register={register}
|
||||||
|
errors={{
|
||||||
|
key: errors.key as FieldError | undefined,
|
||||||
|
sas_url: errors.sas_url as FieldError | undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'sftp':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* host and user via FieldRenderer */}
|
||||||
|
<FieldRenderer
|
||||||
|
field={BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'host')!}
|
||||||
|
register={register}
|
||||||
|
error={errors.host as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
<FieldRenderer
|
||||||
|
field={BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'user')!}
|
||||||
|
register={register}
|
||||||
|
error={errors.user as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
{/* Auth toggle handles pass + key_pem — both always registered */}
|
||||||
|
<SftpAuthToggle
|
||||||
|
register={register}
|
||||||
|
errors={{
|
||||||
|
pass: errors.pass as FieldError | undefined,
|
||||||
|
key_pem: errors.key_pem as FieldError | undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'gdrive':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GdriveAuthToggle
|
||||||
|
register={register}
|
||||||
|
errors={{
|
||||||
|
token: errors.token as FieldError | undefined,
|
||||||
|
service_account_credentials: errors.service_account_credentials as FieldError | undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{BACKEND_REGISTRY.gdrive.fields
|
||||||
|
.filter(f => f.key !== 'token' && f.key !== 'service_account_credentials')
|
||||||
|
.map(field => (
|
||||||
|
<FieldRenderer
|
||||||
|
key={field.key}
|
||||||
|
field={field}
|
||||||
|
register={register}
|
||||||
|
error={errors[field.key] as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'onedrive':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<OAuthInstructions
|
||||||
|
backendName="OneDrive"
|
||||||
|
authorizeCommand='rclone authorize "onedrive"'
|
||||||
|
/>
|
||||||
|
{BACKEND_REGISTRY.onedrive.fields.map(field => (
|
||||||
|
<FieldRenderer
|
||||||
|
key={field.key}
|
||||||
|
field={field}
|
||||||
|
register={register}
|
||||||
|
error={errors[field.key] as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'dropbox':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<OAuthInstructions
|
||||||
|
backendName="Dropbox"
|
||||||
|
authorizeCommand='rclone authorize "dropbox"'
|
||||||
|
/>
|
||||||
|
{BACKEND_REGISTRY.dropbox.fields.map(field => (
|
||||||
|
<FieldRenderer
|
||||||
|
key={field.key}
|
||||||
|
field={field}
|
||||||
|
register={register}
|
||||||
|
error={errors[field.key] as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'box':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<OAuthInstructions
|
||||||
|
backendName="Box"
|
||||||
|
authorizeCommand='rclone authorize "box"'
|
||||||
|
/>
|
||||||
|
{BACKEND_REGISTRY.box.fields.map(field => (
|
||||||
|
<FieldRenderer
|
||||||
|
key={field.key}
|
||||||
|
field={field}
|
||||||
|
register={register}
|
||||||
|
error={errors[field.key] as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'pcloud':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<OAuthInstructions
|
||||||
|
backendName="pCloud"
|
||||||
|
authorizeCommand='rclone authorize "pcloud"'
|
||||||
|
/>
|
||||||
|
{BACKEND_REGISTRY.pcloud.fields.map(field => (
|
||||||
|
<FieldRenderer
|
||||||
|
key={field.key}
|
||||||
|
field={field}
|
||||||
|
register={register}
|
||||||
|
error={errors[field.key] as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
// All other backends (azure-files, swift, ftp, webdav, smb, http, seafile, s3, s3-compatible, gcs, b2):
|
||||||
|
// generic FieldRenderer loop driven by registry
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{BACKEND_REGISTRY[backendType].fields.map(field => (
|
||||||
|
<FieldRenderer
|
||||||
|
key={field.key}
|
||||||
|
field={field}
|
||||||
|
register={register}
|
||||||
|
error={errors[field.key] as FieldError | undefined}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function RemoteConfigStep() {
|
export function RemoteConfigStep() {
|
||||||
const { state, dispatch } = useWizard();
|
const { state, dispatch } = useWizard();
|
||||||
const backendType = state.remote.backendType;
|
const backendType = state.remote.backendType;
|
||||||
@@ -50,16 +222,6 @@ export function RemoteConfigStep() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const backendLabel: Record<NonNullable<typeof backendType>, string> = {
|
|
||||||
azureblob: 'Azure Blob Storage',
|
|
||||||
s3: 'Amazon S3',
|
|
||||||
's3-compatible': 'S3-Compatible Storage',
|
|
||||||
onedrive: 'OneDrive',
|
|
||||||
sftp: 'SFTP',
|
|
||||||
gcs: 'Google Cloud Storage',
|
|
||||||
b2: 'Backblaze B2',
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-on-surface mb-2">Step 2: Configure {backendLabel[backendType]}</h2>
|
<h2 className="text-2xl font-bold text-on-surface mb-2">Step 2: Configure {backendLabel[backendType]}</h2>
|
||||||
@@ -68,56 +230,7 @@ export function RemoteConfigStep() {
|
|||||||
generated rclone.conf and are never sent to any server.
|
generated rclone.conf and are never sent to any server.
|
||||||
</p>
|
</p>
|
||||||
<form onSubmit={handleSubmit(onNext, onInvalidSubmit)}>
|
<form onSubmit={handleSubmit(onNext, onInvalidSubmit)}>
|
||||||
{backendType === 'azureblob' ? (
|
{renderBackendFields(backendType, register, errors as Record<string, unknown>)}
|
||||||
<>
|
|
||||||
{/* Account field via FieldRenderer */}
|
|
||||||
<FieldRenderer
|
|
||||||
field={BACKEND_REGISTRY.azureblob.fields.find(f => f.key === 'account')!}
|
|
||||||
register={register}
|
|
||||||
error={errors.account as FieldError | undefined}
|
|
||||||
/>
|
|
||||||
{/* Auth toggle handles key + sas_url — both always registered */}
|
|
||||||
<AzureAuthToggle
|
|
||||||
register={register}
|
|
||||||
errors={{
|
|
||||||
key: errors.key as FieldError | undefined,
|
|
||||||
sas_url: errors.sas_url as FieldError | undefined,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : backendType === 'sftp' ? (
|
|
||||||
<>
|
|
||||||
{/* host and user via FieldRenderer */}
|
|
||||||
<FieldRenderer
|
|
||||||
field={BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'host')!}
|
|
||||||
register={register}
|
|
||||||
error={errors.host as FieldError | undefined}
|
|
||||||
/>
|
|
||||||
<FieldRenderer
|
|
||||||
field={BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'user')!}
|
|
||||||
register={register}
|
|
||||||
error={errors.user as FieldError | undefined}
|
|
||||||
/>
|
|
||||||
{/* Auth toggle handles pass + key_pem — both always registered */}
|
|
||||||
<SftpAuthToggle
|
|
||||||
register={register}
|
|
||||||
errors={{
|
|
||||||
pass: errors.pass as FieldError | undefined,
|
|
||||||
key_pem: errors.key_pem as FieldError | undefined,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
/* All others (onedrive, gcs, b2, s3, s3-compatible): full registry loop */
|
|
||||||
BACKEND_REGISTRY[backendType].fields.map(field => (
|
|
||||||
<FieldRenderer
|
|
||||||
key={field.key}
|
|
||||||
field={field}
|
|
||||||
register={register}
|
|
||||||
error={errors[field.key] as FieldError | undefined}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col sm:flex-row gap-3 mt-6">
|
<div className="flex flex-col sm:flex-row gap-3 mt-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
Reference in New Issue
Block a user