| noteId | c761f2f0b7ee11f08d56a990e7f97797 |
|---|---|
| tags |
Panduan lengkap untuk migrasi EPUB files dari IPFS/Pinata ke Supabase Storage bucket.
Ikuti panduan di SUPABASE_STORAGE_INTEGRATION.md untuk:
- ✅ Buat bucket
libere-books(private) - ✅ Setup storage policies untuk authenticated access
- ✅ Verify environment variables di
.env
Option A: Migrate all books (Recommended):
npm run migrate:simpleOption B: Migrate specific books by ID:
npm run migrate:simple -- 1 2 3Option C: Advanced migration with batching and retry:
npm run migrate:epubs- Check Supabase Dashboard → Storage → libere-books
- Check database:
SELECT id, title, epub FROM "Book" LIMIT 10;
- Test di web app: buka book dan read EPUB
Sebelum menjalankan migration, pastikan:
- Supabase bucket
libere-bookssudah dibuat - Bucket setting: Private (not public)
- Storage policies sudah disetup
- Environment variables sudah benar di
.env:VITE_SUPABASE_URL=https://xxx.supabase.co VITE_SUPABASE_API_KEY=your_anon_key
- Dependencies sudah terinstall (
npm install) - Database backup sudah dibuat (optional, recommended)
Recommended untuk most use cases.
Features:
- Simple and fast
- Clear progress logging
- Migrate all or specific books
- 1 second delay between books
- Error handling with clear messages
Usage:
# Migrate all books
npm run migrate:simple
# Migrate specific books
tsx scripts/migrate-simple.ts 1 2 3Process:
- Fetch book from database
- Check if already migrated (skip if yes)
- Download EPUB from IPFS
- Upload to Supabase Storage
- Update database with new URL
- Show summary report
For large-scale migrations with advanced features.
Features:
- Batch processing (5 books per batch)
- Retry logic (3 attempts per book)
- Detailed JSON report saved to file
- Rate limiting protection (3s delay between batches)
- Comprehensive error handling
Usage:
npm run migrate:epubsOutput:
- Console logs with progress
migration-report.jsonwith detailed results
📚 Fetching books from database...
✅ Found 10 books
- IPFS (needs migration): 8
- Supabase Storage (already migrated): 2
📖 Migrating Book ID: 1
Title: Introduction to Blockchain
📥 Downloading from IPFS...
✅ Downloaded 2.34 MB
📤 Uploading to Supabase Storage...
✅ Uploaded to Supabase
New URL: https://xxx.supabase.co/storage/v1/object/public/libere-books/1/book.epub
💾 Updating database...
✅ Database updated
🎉 Migration completed for Book 1!
============================================================
📊 MIGRATION SUMMARY
============================================================
✅ Successful: 8
❌ Failed: 0
⏭️ Skipped: 2 (already migrated)
============================================================
Supabase Storage Bucket: libere-books/
├── 1/
│ └── book.epub (Book ID 1)
├── 2/
│ └── book.epub (Book ID 2)
├── 3/
│ └── book.epub (Book ID 3)
└── ...
Database Book table:
id | title | epub
----|------------------------|----------------------------------------
1 | Introduction to... | https://xxx.supabase.co/storage/v1/...
2 | Advanced Programming | https://xxx.supabase.co/storage/v1/...
3 | Web Development | https://ipfs.io/ipfs/QmXXX... (not migrated yet)
- Go to: https://supabase.com/dashboard
- Select your project
- Click: Storage → libere-books
- Verify files exist with structure:
{bookId}/book.epub
-- Check migrated books
SELECT id, title, epub
FROM "Book"
WHERE epub LIKE '%supabase.co/storage%';
-- Check IPFS books (not yet migrated)
SELECT id, title, epub
FROM "Book"
WHERE epub LIKE '%ipfs%' OR epub LIKE '%pinata%';npm run dev- Login to app
- Go to Bookshelf (if you own books)
- Click "Read Now" on any book
- Book should open with EPUB from Supabase Storage
- Check browser console for logs:
✅ [LoadBook] EPUB URL ready: https://xxx.supabase.co/storage/v1/object/sign/...
Open browser console di EPUB reader page:
// Should see logs like:
🔐 [LoadBook] Generating signed URL for Supabase Storage...
✅ [LoadBook] Signed URL generated (expires in 1 hour)
📖 [Reader] Rendition loadedSymptoms:
❌ Migration failed: Upload failed: The resource already exists
Causes:
- File already exists in bucket
- Permissions issue
- Bucket doesn't exist
Solutions:
-
Check bucket exists:
- Go to Supabase Dashboard → Storage
- Verify
libere-booksbucket exists
-
Check permissions:
-- Run in SQL Editor SELECT * FROM storage.policies WHERE bucket_id = 'libere-books';
-
Manual cleanup (if needed):
// Remove existing file await supabase.storage .from('libere-books') .remove(['1/book.epub']);
Symptoms:
❌ Migration failed: Failed to download from IPFS: timeout of 60000ms exceeded
Causes:
- IPFS gateway slow or down
- Network issues
- File doesn't exist
Solutions:
- Test IPFS URL manually in browser
- Increase timeout in script:
// Edit migrate-simple.ts timeout: 120000, // 2 minutes instead of 1
- Try different gateway:
- Change
gateway.pinata.cloudtoipfs.io - Or use
cloudflare-ipfs.com
- Change
Symptoms:
❌ Migration failed: Database update failed: permission denied
Causes:
- Invalid API key
- Table permissions
- Row Level Security (RLS) blocking update
Solutions:
-
Check API key:
# In .env file VITE_SUPABASE_API_KEY=your_anon_key # Should be anon/public key
-
Check RLS policies:
-- Temporarily disable RLS for migration (re-enable after!) ALTER TABLE "Book" DISABLE ROW LEVEL SECURITY; -- Run migration... -- Re-enable RLS ALTER TABLE "Book" ENABLE ROW LEVEL SECURITY;
-
Use service role key (for migration only):
# Create .env.migration VITE_SUPABASE_URL=https://xxx.supabase.co VITE_SUPABASE_API_KEY=your_service_role_key # Service role (admin)
WARNING: Never commit service role key to git!
Symptoms:
- Migration says book already migrated
- But EPUB doesn't load in reader
Causes:
- Signed URL expired
- File actually doesn't exist in bucket
- Storage policies not setup
Solutions:
-
Verify file exists:
- Check Supabase Dashboard → Storage → libere-books
- Look for
{bookId}/book.epub
-
Test signed URL generation:
import { getSignedEpubUrl } from './src/utils/supabaseStorage'; const url = await getSignedEpubUrl(1); console.log(url); // Should return signed URL
-
Re-upload specific book:
# Force re-migration of book ID 1 tsx scripts/migrate-simple.ts 1
Normal behavior - Script skips:
- Books already migrated to Supabase Storage
- Books with unknown URL format
To check:
-- See all book URL types
SELECT
id,
title,
CASE
WHEN epub LIKE '%supabase.co%' THEN 'Supabase'
WHEN epub LIKE '%ipfs%' OR epub LIKE '%pinata%' THEN 'IPFS'
ELSE 'Unknown'
END as storage_type
FROM "Book"
ORDER BY id;For debugging or custom migrations:
import { createClient } from '@supabase/supabase-js';
import axios from 'axios';
const supabase = createClient(supabaseUrl, supabaseKey);
async function manualMigrate(bookId: number) {
// 1. Get book
const { data: book } = await supabase
.from('Book')
.select('*')
.eq('id', bookId)
.single();
console.log('Current EPUB URL:', book.epub);
// 2. Download from IPFS
const response = await axios.get(book.epub, {
responseType: 'arraybuffer',
timeout: 60000,
});
const buffer = Buffer.from(response.data);
console.log('Downloaded:', buffer.length, 'bytes');
// 3. Upload to Supabase
const filePath = `${bookId}/book.epub`;
const { error: uploadError } = await supabase.storage
.from('libere-books')
.upload(filePath, buffer, {
contentType: 'application/epub+zip',
upsert: true, // Overwrite if exists
});
if (uploadError) {
console.error('Upload error:', uploadError);
return;
}
// 4. Get new URL
const { data: urlData } = supabase.storage
.from('libere-books')
.getPublicUrl(filePath);
console.log('New URL:', urlData.publicUrl);
// 5. Update database
const { error: updateError } = await supabase
.from('Book')
.update({ epub: urlData.publicUrl })
.eq('id', bookId);
if (updateError) {
console.error('Update error:', updateError);
return;
}
console.log('Migration completed!');
}
// Run
manualMigrate(1);If you need to rollback migration:
# Migration report saved as migration-report.json
cat migration-report.json// Rollback script
import report from './migration-report.json';
for (const result of report) {
if (result.success && result.oldUrl !== result.newUrl) {
// Restore old IPFS URL
await supabase
.from('Book')
.update({ epub: result.oldUrl })
.eq('id', result.bookId);
}
}-- If you have backup table
UPDATE "Book"
SET epub = backup."Book".epub
FROM backup."Book"
WHERE "Book".id = backup."Book".id;-
Use batching:
npm run migrate:epubs # Uses batch processing -
Parallel processing (modify script):
// Process 5 books in parallel const batchPromises = batch.map(book => migrateBook(book)); await Promise.all(batchPromises);
-
Increase timeouts for large files:
timeout: 180000, // 3 minutes for large EPUBs
- Free tier: 1 GB storage, 2 GB transfer
- Pro: $0.021/GB storage, $0.09/GB transfer
- 100 books × 2MB average = 200MB
- Well within free tier
- Migration transfer: ~200MB (one-time)
| Feature | IPFS | Supabase Storage |
|---|---|---|
| Cost | Gateway fees | Free tier: 1GB |
| Speed | Variable | Fast (CDN) |
| Privacy | Public | Private (authenticated) |
| Reliability | Depends on pins | 99.9% uptime |
| Offline | Via gateway | Signed URLs |
Supabase Dashboard → Storage → libere-books
- Check total size
- Monitor growth
- Setup alerts (optional)
After verifying migration successful:
- Unpin files from Pinata (to save costs)
- Keep metadata for 30 days as backup
- Update CLAUDE.md if needed
- Note migration completion date
- Document any issues encountered
A: Yes, but not recommended. Pick one:
- IPFS: Public, decentralized
- Supabase: Private, authenticated
Current setup: Cover images on IPFS, EPUBs on Supabase.
A: Safe to re-run! Script skips already migrated books.
A: Depends on:
- Number of books
- File sizes
- Network speed
Estimate: ~10-30 seconds per book
Example:
- 10 books: ~2-5 minutes
- 100 books: ~20-50 minutes
A: Yes! App supports both IPFS and Supabase URLs:
- Old books: Still use IPFS
- New books: Use Supabase
- Migrated books: Use Supabase
A: Cover images stay on IPFS (public, for display). Only EPUBs migrate to Supabase (private, for authenticated reading).
- Full docs: SUPABASE_STORAGE_INTEGRATION.md
- Scripts docs: scripts/README.md
- Issues: Check console logs and Supabase Dashboard logs
- Test reading books in app
- Monitor storage usage
- Setup automated backups
- Consider implementing offline caching
- Update publish flow (already done in CreateBookV2Screen)