بنقرة واحدة
volcano-storage
Detailed guidance for storage buckets, file paths, and upload or download flows with Volcano
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Detailed guidance for storage buckets, file paths, and upload or download flows with Volcano
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Install or upgrade the Volcano CLI from a plugin-shipped skills environment.
Detailed guidance for authentication flows built with the Volcano SDK
Detailed guidance for browser and function data access with the Volcano query builder
Reusable error handling patterns for Volcano SDK apps: centralized error dispatcher with action enum, useApiCall React hook for loading/error/data state, retry with exponential backoff, retry with toast notifications, and cross-domain error message catalog.
Detailed guidance for server-side function invocation and orchestration with Volcano
Detailed guidance for using the Volcano SDK correctly in Next.js environments
| name | volcano-storage |
| description | Detailed guidance for storage buckets, file paths, and upload or download flows with Volcano |
Implement secure storage flows with explicit bucket, path, and access-control intent. This skill is self-contained: bucket selection, every file operation, public/private semantics, resumable uploads, and error handling are embedded.
const avatars = volcano.storage.from('avatars');
const documents = volcano.storage.from('documents');
Buckets are created in the dashboard; reference by name.
avatars/<userId>/profile.jpg
documents/<userId>/reports/<year>/<filename>.pdf
projects/<projectId>/assets/<assetType>/<filename>
Avoid: random/timestamp-only paths (file-${Date.now()}.pdf).
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = fileInput.files![0];
const { data, error } = await volcano.storage
.from('avatars')
.upload(`${user.id}/profile.jpg`, file);
// data: { name, size, mime_type, ... }
await volcano.storage
.from('documents')
.upload('reports/annual-2024.pdf', file, { contentType: 'application/pdf' });
const blob = new Blob(['hello'], { type: 'text/plain' });
await volcano.storage.from('uploads').upload('notes/hello.txt', blob);
const buffer = await fetchSomeData();
await volcano.storage.from('uploads').upload('data/export.bin', buffer, {
contentType: 'application/octet-stream',
});
const { data: blob, error } = await volcano.storage
.from('documents')
.download('reports/annual-2024.pdf');
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'annual-2024.pdf';
a.click();
URL.revokeObjectURL(url);
const { data: blob } = await volcano.storage.from('avatars').download(`${userId}/profile.jpg`);
imgEl.src = URL.createObjectURL(blob);
const { data: blob } = await volcano.storage
.from('documents')
.download('large-file.zip', { range: 'bytes=0-1023' });
const { data: files, error } = await volcano.storage.from('uploads').list();
// file fields: name, size, mime_type, is_public, created_at, owner_id, public_url?
const { data: files } = await volcano.storage.from('uploads').list('images/');
const { data: mine } = await volcano.storage.from('documents').list(`${userId}/`);
const { data: files, nextCursor } = await volcano.storage
.from('uploads')
.list('', { limit: 100 });
if (nextCursor) {
const { data: more } = await volcano.storage
.from('uploads')
.list('', { limit: 100, cursor: nextCursor });
}
// Single
await volcano.storage.from('uploads').remove('old-file.txt');
// Multiple
const { data } = await volcano.storage.from('uploads').remove([
'temp/file1.txt',
'temp/file2.txt',
'temp/file3.txt',
]);
// data.deleted is the list of removed paths
await volcano.storage.from('documents').move('drafts/report.pdf', 'published/report.pdf');
await volcano.storage.from('documents').copy('templates/invoice.pdf', 'invoices/2024-001.pdf');
Files require an authenticated session to download.
const { data } = await volcano.storage
.from('avatars')
.updateVisibility(`${userId}/profile.jpg`, true);
// data.public_url present when true
await volcano.storage
.from('avatars')
.updateVisibility(`${userId}/profile.jpg`, false);
const { data } = volcano.storage.from('avatars').getPublicUrl(`${userId}/profile.jpg`);
// data.publicUrl: 'https://api.<project>.volcano.dev/public/<project-id>/avatars/<path>'
Public URLs require no auth, work in any browser, are CDN-friendly, and 403 if the file is later made private.
Use for files >100 MB or unreliable networks.
const { data, error } = await volcano.storage
.from('uploads')
.uploadResumable('large-video.mp4', file, {
onProgress: (uploaded, total) => {
const pct = Math.round((uploaded / total) * 100);
progressBar.value = pct;
},
});
// 1. Create session
const { data: session } = await volcano.storage
.from('uploads')
.createUploadSession('large-video.mp4', {
totalSize: file.size,
contentType: 'video/mp4',
partSize: 10 * 1024 * 1024, // 10 MB parts
});
// session: { session_id, total_parts, part_size }
// 2. Upload each part
for (let i = 1; i <= session.total_parts; i++) {
const start = (i - 1) * session.part_size;
const end = Math.min(start + session.part_size, file.size);
const partData = file.slice(start, end);
const { error } = await volcano.storage
.from('uploads')
.uploadPart('large-video.mp4', session.session_id, i, partData);
if (error) break; // can retry this part
}
// 3. Complete
await volcano.storage
.from('uploads')
.completeUploadSession('large-video.mp4', session.session_id);
const { data: status } = await volcano.storage
.from('uploads')
.getUploadSession('large-video.mp4', sessionId);
// status: { uploaded_parts, total_parts, uploaded_bytes, total_size, missing_parts, part_size }
for (const partNumber of status.missing_parts) {
const start = (partNumber - 1) * status.part_size;
const end = Math.min(start + status.part_size, file.size);
await volcano.storage
.from('uploads')
.uploadPart('large-video.mp4', sessionId, partNumber, file.slice(start, end));
}
await volcano.storage.from('uploads').completeUploadSession('large-video.mp4', sessionId);
await volcano.storage.from('uploads').abortUploadSession('large-video.mp4', sessionId);
Common error messages:
No active session — sign in first.File not found — wrong path or bucket.Permission denied — access policy violation.Bucket not found — invalid bucket name.File too large — exceeds size limit.const { data, error } = await volcano.storage.from('uploads').upload('file.txt', file);
if (error) {
console.error('Upload failed:', error.message);
}
// Allowed by typical "path starts with user's id" policy
await volcano.storage.from('uploads').upload(`${user.id}/avatar.jpg`, file);
// Denied
await volcano.storage.from('uploads').upload(`other-user-id/avatar.jpg`, file);
is_public).Policies inspect the user's role from the JWT (admin/user/etc.) — same client API, different rows visible.
const MAX = 10 * 1024 * 1024;
const ALLOWED = ['image/jpeg', 'image/png', 'image/gif'];
if (file.size > MAX) return showError('File too large');
if (!ALLOWED.includes(file.type)) return showError('Invalid file type');
const useResumable = file.size > 100 * 1024 * 1024;
const url = URL.createObjectURL(blob);
img.src = url;
// later:
URL.revokeObjectURL(url);
onProgress.