Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// S3 Compatible Providers
export const S3_PROVIDERS: Array<{
key: string;
name: string;
Expand Down Expand Up @@ -131,3 +132,58 @@ export const S3_PROVIDERS: Array<{
name: "Any other S3 compatible provider",
},
];

// Non-S3 Providers (using rclone native protocols)
export const NON_S3_PROVIDERS: Array<{
key: string;
name: string;
}> = [
{
key: "ftp",
name: "FTP Server",
},
{
key: "sftp",
name: "SFTP (SSH File Transfer)",
},
{
key: "drive",
name: "Google Drive",
},
{
key: "onedrive",
name: "Microsoft OneDrive",
},
{
key: "dropbox",
name: "Dropbox",
},
{
key: "webdav",
name: "WebDAV",
},
{
key: "b2",
name: "Backblaze B2",
},
{
key: "mega",
name: "MEGA",
},
{
key: "pcloud",
name: "pCloud",
},
{
key: "box",
name: "Box",
},
{
key: "hubic",
name: "hubic",
},
{
key: "yandex",
name: "Yandex Disk",
},
];
77 changes: 61 additions & 16 deletions apps/dokploy/server/api/routers/destination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,74 @@ export const destinationRouter = createTRPCRouter({
});
}
}),
// Non-S3 providers that use rclone native protocols
const NON_S3_PROVIDERS = [
"ftp", "sftp", "drive", "onedrive", "dropbox", "webdav",
"b2", "mega", "pcloud", "box", "hubic", "yandex"
];
Comment on lines +43 to +47
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalid syntax: constant declaration inside router object definition will cause compilation error

Suggested change
// Non-S3 providers that use rclone native protocols
const NON_S3_PROVIDERS = [
"ftp", "sftp", "drive", "onedrive", "dropbox", "webdav",
"b2", "mega", "pcloud", "box", "hubic", "yandex"
];
});
// Non-S3 providers that use rclone native protocols
const NON_S3_PROVIDERS = [
"ftp", "sftp", "drive", "onedrive", "dropbox", "webdav",
"b2", "mega", "pcloud", "box", "hubic", "yandex"
];
export const destinationRouter = createTRPCRouter({
create: adminProcedure
.input(apiCreateDestination)
.mutation(async ({ input, ctx }) => {
try {
return await createDestintation(
input,
ctx.session.activeOrganizationId,
);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the destination",
cause: error,
});
}
}),


testConnection: adminProcedure
.input(apiCreateDestination)
.mutation(async ({ input }) => {
const { secretAccessKey, bucket, region, endpoint, accessKey, provider } =
input;

// Check if it's a non-S3 provider
const isNonS3 = provider && NON_S3_PROVIDERS.includes(provider);

try {
const rcloneFlags = [
`--s3-access-key-id="${accessKey}"`,
`--s3-secret-access-key="${secretAccessKey}"`,
`--s3-region="${region}"`,
`--s3-endpoint="${endpoint}"`,
"--s3-no-check-bucket",
"--s3-force-path-style",
"--retries 1",
"--low-level-retries 1",
"--timeout 10s",
"--contimeout 5s",
];
if (provider) {
rcloneFlags.unshift(`--s3-provider="${provider}"`);
let rcloneCommand: string;

if (isNonS3) {
// Non-S3 providers use rclone native protocols
const providerConfig: Record<string, string> = {
ftp: `:ftp:${bucket || ""}`,
sftp: `:sftp:${bucket || ""}`,
drive: `:drive:${bucket || ""}`,
onedrive: `:onedrive:${bucket || ""}`,
dropbox: `:dropbox:${bucket || ""}`,
webdav: `:webdav:${bucket || ""}`,
b2: `:b2:${bucket || ""}`,
mega: `:mega:${bucket || ""}`,
pcloud: `:pcloud:${bucket || ""}`,
box: `:box:${bucket || ""}`,
hubic: `:hubic:${bucket || ""}`,
yandex: `:yandex:${bucket || ""}`,
};
Comment on lines +61 to +76
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

several providers (Google Drive, OneDrive, Dropbox, Box) require OAuth authentication flow, not simple username/password. the current implementation using accessKey/secretAccessKey won't work for these providers - they need OAuth tokens or service account credentials configured through rclone config


const rcloneFlags = [
"--retries 1",
"--low-level-retries 1",
"--timeout 10s",
"--contimeout 5s",
];

// Add authentication if provided
if (accessKey) rcloneFlags.push(`--${provider}-user="${accessKey}"`);
if (secretAccessKey) rcloneFlags.push(`--${provider}-pass="${secretAccessKey}"`);
if (endpoint) rcloneFlags.push(`--${provider}-url="${endpoint}"`);
Comment on lines +86 to +88
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rclone flag format --${provider}-user is incorrect for most providers - each provider has specific flag names (e.g., FTP uses --ftp-user, SFTP uses --sftp-user, but Google Drive uses --drive-service-account-credentials, OneDrive uses --onedrive-client-id). check rclone documentation for correct flags per provider


rcloneCommand = `rclone ls ${rcloneFlags.join(" ")} "${providerConfig[provider]}"`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing type safety: accessing providerConfig[provider] without checking if provider exists in the mapping will cause runtime error if an unsupported provider is passed

Suggested change
rcloneCommand = `rclone ls ${rcloneFlags.join(" ")} "${providerConfig[provider]}"`;
const remoteConfig = providerConfig[provider];
if (!remoteConfig) {
throw new TRPCError({
code: "BAD_REQUEST",
message: `Unsupported provider: ${provider}`,
});
}
rcloneCommand = `rclone ls ${rcloneFlags.join(" ")} "${remoteConfig}"`;

} else {
// S3 compatible providers
const rcloneFlags = [
`--s3-access-key-id="${accessKey}"`,
`--s3-secret-access-key="${secretAccessKey}"`,
`--s3-region="${region}"`,
`--s3-endpoint="${endpoint}"`,
"--s3-no-check-bucket",
"--s3-force-path-style",
"--retries 1",
"--low-level-retries 1",
"--timeout 10s",
"--contimeout 5s",
];
if (provider) {
rcloneFlags.unshift(`--s3-provider="${provider}"`);
}
const rcloneDestination = `:s3:${bucket}`;
rcloneCommand = `rclone ls ${rcloneFlags.join(" ")} "${rcloneDestination}"`;
}
const rcloneDestination = `:s3:${bucket}`;
const rcloneCommand = `rclone ls ${rcloneFlags.join(" ")} "${rcloneDestination}"`;

if (IS_CLOUD && !input.serverId) {
throw new TRPCError({
Expand Down