diff --git a/src/gateway/r2.ts b/src/gateway/r2.ts index c95efc40b..835f44c89 100644 --- a/src/gateway/r2.ts +++ b/src/gateway/r2.ts @@ -1,78 +1,53 @@ import type { Sandbox } from '@cloudflare/sandbox'; import type { MoltbotEnv } from '../types'; -import { R2_MOUNT_PATH, getR2BucketName } from '../config'; + +const R2_MOUNT_PATH = '/data/moltbot'; /** - * Check if R2 is already mounted by looking at the mount table + * Mount R2 bucket for persistent storage */ -async function isR2Mounted(sandbox: Sandbox): Promise { - try { - const proc = await sandbox.startProcess(`mount | grep "s3fs on ${R2_MOUNT_PATH}"`); - // Wait for the command to complete - let attempts = 0; - while (proc.status === 'running' && attempts < 10) { - // eslint-disable-next-line no-await-in-loop -- intentional sequential polling - await new Promise((r) => setTimeout(r, 200)); - attempts++; +export async function mountR2Storage(sandbox: Sandbox, env: MoltbotEnv): Promise { + // SIMPLE FIX: Use MOLTBOT_BUCKET binding instead of R2 API credentials + if (env.MOLTBOT_BUCKET && env.R2_BUCKET_NAME) { + try { + console.log('[R2] Mounting R2 storage...'); + await sandbox.exec(`mkdir -p ${R2_MOUNT_PATH} && chmod 777 ${R2_MOUNT_PATH}`); + await sandbox.mountBucket(env.MOLTBOT_BUCKET, R2_MOUNT_PATH, { + readOnly: false, + }); + console.log('[R2] R2 storage mounted successfully'); + return true; + } catch (error) { + console.error('[R2] Mount failed:', error); + return false; } - const logs = await proc.getLogs(); - // If stdout has content, the mount exists - const mounted = !!(logs.stdout && logs.stdout.includes('s3fs')); - console.log('isR2Mounted check:', mounted, 'stdout:', logs.stdout?.slice(0, 100)); - return mounted; - } catch (err) { - console.log('isR2Mounted error:', err); - return false; } + + console.log('[R2] R2 not configured (missing MOLTBOT_BUCKET or R2_BUCKET_NAME)'); + return false; } /** - * Mount R2 bucket for persistent storage - * - * @param sandbox - The sandbox instance - * @param env - Worker environment bindings - * @returns true if mounted successfully, false otherwise + * Sync data to R2 */ -export async function mountR2Storage(sandbox: Sandbox, env: MoltbotEnv): Promise { - // Skip if R2 credentials are not configured - if (!env.R2_ACCESS_KEY_ID || !env.R2_SECRET_ACCESS_KEY || !env.CF_ACCOUNT_ID) { - console.log( - 'R2 storage not configured (missing R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, or CF_ACCOUNT_ID)', - ); - return false; - } - - // Check if already mounted first - this avoids errors and is faster - if (await isR2Mounted(sandbox)) { - console.log('R2 bucket already mounted at', R2_MOUNT_PATH); - return true; - } - - const bucketName = getR2BucketName(env); +export async function syncToR2(sandbox: Sandbox, env: MoltbotEnv): Promise<{ success: boolean; error?: string }> { try { - console.log('Mounting R2 bucket', bucketName, 'at', R2_MOUNT_PATH); - await sandbox.mountBucket(bucketName, R2_MOUNT_PATH, { - endpoint: `https://${env.CF_ACCOUNT_ID}.r2.cloudflarestorage.com`, - // Pass credentials explicitly since we use R2_* naming instead of AWS_* - credentials: { - accessKeyId: env.R2_ACCESS_KEY_ID, - secretAccessKey: env.R2_SECRET_ACCESS_KEY, - }, - }); - console.log('R2 bucket mounted successfully - moltbot data will persist across sessions'); - return true; - } catch (err) { - const errorMessage = err instanceof Error ? err.message : String(err); - console.log('R2 mount error:', errorMessage); - - // Check again if it's mounted - the error might be misleading - if (await isR2Mounted(sandbox)) { - console.log('R2 bucket is mounted despite error'); - return true; + if (!env.R2_BUCKET_NAME) { + return { success: false, error: 'R2_BUCKET_NAME not set' }; } - // Don't fail if mounting fails - moltbot can still run without persistent storage - console.error('Failed to mount R2 bucket:', err); - return false; + // Create directories + await sandbox.exec(`mkdir -p ${R2_MOUNT_PATH}/openclaw`); + + // Sync data + const result = await sandbox.exec( + `rsync -av /root/.openclaw/ ${R2_MOUNT_PATH}/openclaw/ 2>&1` + ); + + console.log('[R2] Sync completed:', result.stdout); + return { success: true }; + } catch (error) { + console.error('[R2] Sync failed:', error); + return { success: false, error: String(error) }; } }