// OSS Migration: Hangzhou → Shanghai (internal endpoint) // Server is in cn-shanghai, bucket must be same region for internal access const OSS = require('ali-oss'); const mysql = require('mysql2/promise'); const AK = 'LTAI5tBn4G5HMo8PqMdNsqHd'; const SK = 'f0R5nOQf6E1lIGESENKiK8cNzFl8wd'; const OLD_BUCKET = 'aaaa33dfsf32rfsf'; const OLD_REGION = 'oss-cn-hangzhou'; const NEW_BUCKET = 'aaaa33dfsf32rfsf-sh'; const NEW_REGION = 'oss-cn-shanghai'; async function main() { // Source: Hangzhou PUBLIC endpoint (internal is broken cross-region) const src = new OSS({ region: OLD_REGION, accessKeyId: AK, accessKeySecret: SK, bucket: OLD_BUCKET, secure: true, internal: false, // public endpoint }); // Dest: Shanghai INTERNAL endpoint const dst = new OSS({ region: NEW_REGION, accessKeyId: AK, accessKeySecret: SK, bucket: NEW_BUCKET, secure: true, internal: true, }); console.log('[1/4] Listing objects in Hangzhou bucket...'); let allObjects = []; let marker = null; do { const result = await src.list({ 'max-keys': 100, marker }); if (result.objects) allObjects.push(...result.objects); marker = result.nextMarker; } while (marker); console.log(` Found ${allObjects.length} objects`); if (allObjects.length === 0) { console.log(' No objects to migrate!'); return; } console.log('[2/4] Copying objects to Shanghai bucket...'); let copied = 0, failed = 0; for (const obj of allObjects) { try { // Download from Hangzhou public const data = await src.get(obj.name); // Upload to Shanghai internal (ACL inherited from bucket) await dst.put(obj.name, data.content, { mime: data.res?.headers?.['content-type'] || 'audio/mpeg', }); copied++; if (copied % 20 === 0) console.log(` Progress: ${copied}/${allObjects.length}`); } catch (e) { failed++; console.error(` FAIL ${obj.name}: ${e.message?.slice(0,80)}`); } } console.log(` Done: ${copied} copied, ${failed} failed`); // Update env console.log('[3/4] Updating environment config...'); const fs = require('fs'); const envPath = '/data/ai/audio/server/.env'; let env = fs.readFileSync(envPath, 'utf-8'); // Replace bucket name in OSS endpoints env = env.replace(/OSS_ENDPOINT=oss-cn-hangzhou/g, 'OSS_ENDPOINT=oss-cn-shanghai'); env = env.replace(/OSS_INTERNAL_ENDPOINT=oss-cn-hangzhou-internal/g, 'OSS_INTERNAL_ENDPOINT=oss-cn-shanghai-internal'); env = env.replace(new RegExp(OLD_BUCKET, 'g'), NEW_BUCKET); fs.writeFileSync(envPath, env, 'utf-8'); fs.writeFileSync(envPath + '.bak', env, 'utf-8'); // backup too console.log(' Updated .env with Shanghai endpoints & new bucket name'); // Update DB audioUrl references console.log('[4/4] Updating database audioUrl references...'); const db = await mysql.createConnection('mysql://root:123456@127.0.0.1:3306/audio_book'); const oldPrefix = `https://${OLD_BUCKET}.${OLD_REGION}.aliyuncs.com/`; const newPrefix = `https://${NEW_BUCKET}.${NEW_REGION}.aliyuncs.com/`; const [chCount] = await db.execute( `UPDATE BookChapter SET audioUrl = REPLACE(audioUrl, ?, ?) WHERE audioUrl LIKE ?`, [oldPrefix, newPrefix, `%${OLD_BUCKET}.${OLD_REGION}.aliyuncs.com%`] ); console.log(` BookChapter: ${chCount.changedRows || 0} rows updated`); const [arCount] = await db.execute( `UPDATE AudioRecord SET audioUrl = REPLACE(audioUrl, ?, ?) WHERE audioUrl LIKE ?`, [oldPrefix, newPrefix, `%${OLD_BUCKET}.${OLD_REGION}.aliyuncs.com%`] ); console.log(` AudioRecord: ${arCount.changedRows || 0} rows updated`); await db.end(); console.log('\n=== Migration complete ==='); console.log(`Copied: ${copied} files`); console.log(`Env updated: ${envPath}`); console.log(`Restart required: pm2 restart server`); } main().catch(e => { console.error('FATAL:', e); process.exit(1); });