bytex-sdk 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -30
- package/index.js +211 -0
- package/package.json +12 -17
- package/postinstall.js +26 -0
- package/src/index.js +0 -104
package/README.md
CHANGED
|
@@ -1,45 +1,56 @@
|
|
|
1
|
-
# ByteX
|
|
1
|
+
# ByteX SDK ⚡
|
|
2
|
+
> The official SDK for the ByteX Cloud Ecosystem.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
ByteX SDK provides a powerful, easy-to-use interface for interacting with ByteX Cloud storage and infrastructure. It supports both Supabase and Firebase backends, enabling a true "Bring Your Own Cloud" (BYOC) experience.
|
|
4
5
|
|
|
5
|
-
## Features
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
6
|
+
## ✨ Features
|
|
7
|
+
- **Multi-Provider**: Seamlessly switch between Supabase and Firebase.
|
|
8
|
+
- **Advanced Encryption**: Built-in AES-CTR encryption for secure file storage.
|
|
9
|
+
- **Streaming Support**: Direct streaming for media files (video/audio).
|
|
10
|
+
- **CDN Integration**: Purge and manage global CDN cache.
|
|
11
|
+
- **Middleware System**: Intercept and optimize uploads (e.g., auto-convert to WebP).
|
|
12
|
+
- **Bulk Operations**: Download or export entire projects as ZIP.
|
|
10
13
|
|
|
11
|
-
## Installation
|
|
14
|
+
## 📦 Installation
|
|
12
15
|
```bash
|
|
13
|
-
npm install
|
|
16
|
+
npm install bytex-sdk
|
|
14
17
|
```
|
|
15
18
|
|
|
16
|
-
## Quick Start
|
|
17
|
-
|
|
18
|
-
### Initialize
|
|
19
|
+
## 🚀 Quick Start
|
|
19
20
|
```javascript
|
|
20
|
-
import { BytexCloud } from '
|
|
21
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
21
|
+
import { BytexCloud } from 'bytex-sdk';
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
// Initialize with your API Key
|
|
24
|
+
const bytex = new BytexCloud({
|
|
25
|
+
apiKey: 'BTX-USER-XXXXXXXX',
|
|
26
|
+
dbProvider: 'supabase' // or 'firebase'
|
|
27
27
|
});
|
|
28
|
-
```
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
// Upload a file
|
|
30
|
+
await bytex.upload('hello.txt', 'Hello World');
|
|
31
|
+
|
|
32
|
+
// Get a streaming URL
|
|
33
|
+
const streamUrl = bytex.stream('video.mp4');
|
|
34
|
+
|
|
35
|
+
// List files
|
|
36
|
+
const files = await bytex.listFiles();
|
|
37
|
+
console.log(files);
|
|
36
38
|
```
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
## 🛠 Advanced Usage: Middleware
|
|
39
41
|
```javascript
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
import { BytexCloud, BytexMiddlewares } from 'bytex-sdk';
|
|
43
|
+
|
|
44
|
+
const bytex = new BytexCloud({ apiKey: '...' });
|
|
45
|
+
|
|
46
|
+
// Automatically optimize images to WebP before upload
|
|
47
|
+
bytex.use(BytexMiddlewares.imageOptimizer(0.8));
|
|
48
|
+
|
|
49
|
+
await bytex.upload('photo.jpg', fileData); // Uploads as photo.webp
|
|
42
50
|
```
|
|
43
51
|
|
|
44
|
-
##
|
|
45
|
-
|
|
52
|
+
## 🛡 Security
|
|
53
|
+
All storage operations are authenticated via API keys. Data can be encrypted locally using standard or custom encryption keys, ensuring that your storage provider never sees raw data.
|
|
54
|
+
|
|
55
|
+
## 📄 License
|
|
56
|
+
ISC © ByteX Ecosystem
|
package/index.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { createClient } from '@supabase/supabase-js';
|
|
2
|
+
|
|
3
|
+
const SUPABASE_URL = 'https://vkiddclfbwmslaiyyftl.supabase.co';
|
|
4
|
+
const SUPABASE_ANON_KEY = 'sb_publishable_hXMlH9OmJG1_n1s-3lbXKg_6V-88Lj9';
|
|
5
|
+
const WORKER_URL = 'https://api.bytex.work/';
|
|
6
|
+
|
|
7
|
+
export class BytexCloud {
|
|
8
|
+
constructor(config = {}) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.apiKey = config.apiKey || null;
|
|
11
|
+
this.workerUrl = config.workerUrl || WORKER_URL;
|
|
12
|
+
|
|
13
|
+
const supabaseConfig = {
|
|
14
|
+
auth: {
|
|
15
|
+
persistSession: true,
|
|
16
|
+
autoRefreshToken: true,
|
|
17
|
+
detectSessionInUrl: false
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
if (config.storage) {
|
|
21
|
+
supabaseConfig.auth.storage = config.storage;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.supabase = createClient(config.supabaseUrl || SUPABASE_URL, config.supabaseKey || SUPABASE_ANON_KEY, supabaseConfig);
|
|
25
|
+
this._listeners = {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// --- AUTO X-RAY WRAPPER ---
|
|
29
|
+
async _execute(fn, actionName) {
|
|
30
|
+
try {
|
|
31
|
+
return await fn();
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.warn(`[ByteX SDK] Error in ${actionName}. Running Auto X-RAY...`);
|
|
34
|
+
const diagnostic = await this.xray();
|
|
35
|
+
const advice = diagnostic.advice.length > 0 ? `\n[X-RAY Advice]: ${diagnostic.advice.join(' ')}` : '';
|
|
36
|
+
throw new Error(`${actionName} failed: ${e.message}${advice}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// --- CORE METHODS (ENHANCED WITH AUTO X-RAY) ---
|
|
41
|
+
async login(email, password) {
|
|
42
|
+
return this._execute(async () => {
|
|
43
|
+
const { data, error } = await this.supabase.auth.signInWithPassword({ email, password });
|
|
44
|
+
if (error) throw error;
|
|
45
|
+
return data.user;
|
|
46
|
+
}, 'Login');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async upload(name, data) {
|
|
50
|
+
return this._execute(async () => {
|
|
51
|
+
this._requireKey();
|
|
52
|
+
const fd = new FormData();
|
|
53
|
+
fd.append('file', data instanceof Blob ? data : new Blob([data]), name);
|
|
54
|
+
const res = await fetch(`${this.workerUrl}?action=upload&key=${this.apiKey}`, { method: 'POST', body: fd });
|
|
55
|
+
if (!res.ok) throw new Error(await res.text() || res.statusText);
|
|
56
|
+
return await res.json();
|
|
57
|
+
}, 'Upload');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async downloadData(name) {
|
|
61
|
+
return this._execute(async () => {
|
|
62
|
+
this._requireKey();
|
|
63
|
+
const streamName = name.endsWith('.btx') ? name : `${name}.stream.btx`;
|
|
64
|
+
const url = `${this.workerUrl}?action=view&key=${this.apiKey}&file=${encodeURIComponent(streamName)}&_cb=${Date.now()}`;
|
|
65
|
+
const { data: { session } } = await this.supabase.auth.getSession();
|
|
66
|
+
const res = await fetch(url, { headers: { 'Authorization': `Bearer ${session?.access_token || ''}` } });
|
|
67
|
+
if (!res.ok) throw new Error(`Status ${res.status}`);
|
|
68
|
+
const text = await res.text();
|
|
69
|
+
try { return JSON.parse(text); } catch (e) { return text.split('\n').filter(Boolean).map(line => JSON.parse(line)); }
|
|
70
|
+
}, 'Download');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async delete(name) {
|
|
74
|
+
return this._execute(async () => {
|
|
75
|
+
this._requireKey();
|
|
76
|
+
const streamName = name.endsWith('.btx') ? name : `${name}.stream.btx`;
|
|
77
|
+
const { data: { session } } = await this.supabase.auth.getSession();
|
|
78
|
+
const res = await fetch(`${this.workerUrl}?action=delete&key=${this.apiKey}&file=${encodeURIComponent(streamName)}`, {
|
|
79
|
+
method: 'DELETE',
|
|
80
|
+
headers: { 'Authorization': `Bearer ${session?.access_token || ''}` }
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok) throw new Error(`Status ${res.status}`);
|
|
83
|
+
return true;
|
|
84
|
+
}, 'Delete');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* ByteX X-RAY v2.5 (Self-Healing Diagnostic)
|
|
89
|
+
*/
|
|
90
|
+
async xray() {
|
|
91
|
+
const report = { timestamp: new Date().toISOString(), sdk_version: '2.1.0', checks: {}, advice: [] };
|
|
92
|
+
const { data: { session } } = await this.supabase.auth.getSession();
|
|
93
|
+
|
|
94
|
+
// 1. Connectivity Check
|
|
95
|
+
const start = Date.now();
|
|
96
|
+
try {
|
|
97
|
+
const res = await fetch(`${this.workerUrl}?action=list&key=${this.apiKey}`);
|
|
98
|
+
report.checks.connectivity = res.ok ? 'OK' : 'ERROR';
|
|
99
|
+
report.latency = Date.now() - start;
|
|
100
|
+
} catch (e) {
|
|
101
|
+
report.checks.connectivity = 'OFFLINE';
|
|
102
|
+
report.advice.push('ByteX Cloud is unreachable. Check your internet.');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 2. API Key Check
|
|
106
|
+
if (!this.apiKey) {
|
|
107
|
+
report.checks.api_key = 'MISSING';
|
|
108
|
+
report.advice.push('No API Key detected. Please provide an apiKey in the constructor.');
|
|
109
|
+
} else { report.checks.api_key = 'OK'; }
|
|
110
|
+
|
|
111
|
+
// 3. Auth Check
|
|
112
|
+
report.checks.auth = session ? 'AUTHENTICATED' : 'GUEST';
|
|
113
|
+
if (!session) report.advice.push('User is not logged in. Some operations may fail.');
|
|
114
|
+
|
|
115
|
+
// 4. Platform Audit (Mobile/Web)
|
|
116
|
+
if (!this.config.storage && typeof navigator !== 'undefined') {
|
|
117
|
+
report.advice.push('Storage engine (AsyncStorage) is missing. Sessions will not persist.');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return report;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_requireKey() { if (!this.apiKey) throw new Error('API Key required!'); }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* ByteX AI (BYOM) v1.0
|
|
128
|
+
* Bring Your Own Model — connect your own AI API keys (OpenAI, Claude, Gemini)
|
|
129
|
+
*
|
|
130
|
+
* Usage:
|
|
131
|
+
* const ai = new BytexAI({ openai: 'sk-...', claude: 'sk-ant-...', gemini: 'AIza...' });
|
|
132
|
+
* const reply = await ai.chat('openai', 'gpt-4o', [{ role: 'user', content: 'Hello!' }]);
|
|
133
|
+
*/
|
|
134
|
+
export class BytexAI {
|
|
135
|
+
constructor(keys = {}) {
|
|
136
|
+
this.keys = keys;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Universal chat method.
|
|
141
|
+
* @param {'openai'|'claude'|'gemini'} provider - The AI provider
|
|
142
|
+
* @param {string} model - The model name (e.g. 'gpt-4o', 'claude-3-5-sonnet-20241022', 'gemini-1.5-pro')
|
|
143
|
+
* @param {Array<{role: string, content: string}>} messages - Chat messages
|
|
144
|
+
* @param {object} options - Optional params (temperature, maxTokens, etc.)
|
|
145
|
+
* @returns {Promise<string>} - The AI reply text
|
|
146
|
+
*/
|
|
147
|
+
async chat(provider, model, messages, options = {}) {
|
|
148
|
+
switch (provider) {
|
|
149
|
+
case 'openai': return this._openai(model, messages, options);
|
|
150
|
+
case 'claude': return this._claude(model, messages, options);
|
|
151
|
+
case 'gemini': return this._gemini(model, messages, options);
|
|
152
|
+
default: throw new Error(`[BytexAI] Unknown provider: "${provider}". Use 'openai', 'claude', or 'gemini'.`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async _openai(model, messages, options) {
|
|
157
|
+
if (!this.keys.openai) throw new Error('[BytexAI] OpenAI API key is missing. Pass it via: new BytexAI({ openai: "sk-..." })');
|
|
158
|
+
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
159
|
+
method: 'POST',
|
|
160
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.keys.openai}` },
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
model: model || 'gpt-4o-mini',
|
|
163
|
+
messages,
|
|
164
|
+
temperature: options.temperature ?? 0.7,
|
|
165
|
+
max_tokens: options.maxTokens ?? 1024,
|
|
166
|
+
})
|
|
167
|
+
});
|
|
168
|
+
if (!res.ok) throw new Error(`[BytexAI] OpenAI error: ${await res.text()}`);
|
|
169
|
+
const data = await res.json();
|
|
170
|
+
return data.choices[0].message.content;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async _claude(model, messages, options) {
|
|
174
|
+
if (!this.keys.claude) throw new Error('[BytexAI] Claude API key is missing. Pass it via: new BytexAI({ claude: "sk-ant-..." })');
|
|
175
|
+
const system = messages.find(m => m.role === 'system')?.content;
|
|
176
|
+
const chatMessages = messages.filter(m => m.role !== 'system');
|
|
177
|
+
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
headers: {
|
|
180
|
+
'Content-Type': 'application/json',
|
|
181
|
+
'x-api-key': this.keys.claude,
|
|
182
|
+
'anthropic-version': '2023-06-01'
|
|
183
|
+
},
|
|
184
|
+
body: JSON.stringify({
|
|
185
|
+
model: model || 'claude-3-5-haiku-20241022',
|
|
186
|
+
max_tokens: options.maxTokens ?? 1024,
|
|
187
|
+
...(system && { system }),
|
|
188
|
+
messages: chatMessages
|
|
189
|
+
})
|
|
190
|
+
});
|
|
191
|
+
if (!res.ok) throw new Error(`[BytexAI] Claude error: ${await res.text()}`);
|
|
192
|
+
const data = await res.json();
|
|
193
|
+
return data.content[0].text;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async _gemini(model, messages, options) {
|
|
197
|
+
if (!this.keys.gemini) throw new Error('[BytexAI] Gemini API key is missing. Pass it via: new BytexAI({ gemini: "AIza..." })');
|
|
198
|
+
const contents = messages
|
|
199
|
+
.filter(m => m.role !== 'system')
|
|
200
|
+
.map(m => ({ role: m.role === 'assistant' ? 'model' : 'user', parts: [{ text: m.content }] }));
|
|
201
|
+
const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/${model || 'gemini-1.5-flash'}:generateContent?key=${this.keys.gemini}`;
|
|
202
|
+
const res = await fetch(endpoint, {
|
|
203
|
+
method: 'POST',
|
|
204
|
+
headers: { 'Content-Type': 'application/json' },
|
|
205
|
+
body: JSON.stringify({ contents, generationConfig: { temperature: options.temperature ?? 0.7, maxOutputTokens: options.maxTokens ?? 1024 } })
|
|
206
|
+
});
|
|
207
|
+
if (!res.ok) throw new Error(`[BytexAI] Gemini error: ${await res.text()}`);
|
|
208
|
+
const data = await res.json();
|
|
209
|
+
return data.candidates[0].content.parts[0].text;
|
|
210
|
+
}
|
|
211
|
+
}
|
package/package.json
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bytex-sdk",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
|
+
"description": "Official ByteX Cloud SDK with AI (BYOM) support",
|
|
5
|
+
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
+
"postinstall": "node postinstall.js"
|
|
8
9
|
},
|
|
9
|
-
"keywords": [
|
|
10
|
-
"bytex",
|
|
11
|
-
"cloud",
|
|
12
|
-
"saas",
|
|
13
|
-
"byoc",
|
|
14
|
-
"supabase",
|
|
15
|
-
"huggingface"
|
|
16
|
-
],
|
|
17
|
-
"author": "ByteX Guru",
|
|
18
|
-
"license": "MIT",
|
|
19
10
|
"dependencies": {
|
|
20
|
-
"@supabase/supabase-js": "^2.
|
|
21
|
-
"
|
|
22
|
-
}
|
|
11
|
+
"@supabase/supabase-js": "^2.105.4",
|
|
12
|
+
"chalk": "^5.3.0"
|
|
13
|
+
},
|
|
14
|
+
"keywords": ["bytex", "cloud", "storage", "ai", "byom", "byoc"],
|
|
15
|
+
"author": "ByteX Team",
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"type": "module"
|
|
23
18
|
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
console.log('');
|
|
4
|
+
console.log(chalk.blue.bold('========================================='));
|
|
5
|
+
console.log(chalk.cyan.bold(' 🚀 Successfully installed ByteX SDK!'));
|
|
6
|
+
console.log(chalk.blue.bold('========================================='));
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log(chalk.white('Get started by importing the SDK into your project:'));
|
|
9
|
+
console.log('');
|
|
10
|
+
console.log(chalk.gray(' // 1. Initialize ByteX'));
|
|
11
|
+
console.log(chalk.green(" import { BytexCloud } from 'bytex-sdk';"));
|
|
12
|
+
console.log(chalk.green(" const bytex = new BytexCloud();"));
|
|
13
|
+
console.log('');
|
|
14
|
+
console.log(chalk.gray(' // 2. Login & Generate/Select API Key directly in code'));
|
|
15
|
+
console.log(chalk.green(" await bytex.login('your@email.com', 'password');"));
|
|
16
|
+
console.log(chalk.green(" const myKey = await bytex.createKey('My Web App');"));
|
|
17
|
+
console.log(chalk.gray(' // (The active API Key is now set automatically!)'));
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log(chalk.gray(' // 3. Upload a file'));
|
|
20
|
+
console.log(chalk.green(" await bytex.upload('document.pdf', fileData);"));
|
|
21
|
+
console.log('');
|
|
22
|
+
console.log(chalk.white('Want to do this via terminal instead? Try the CLI:'));
|
|
23
|
+
console.log(chalk.cyan(' npx bytex-cli init'));
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log(chalk.gray('For full SDK documentation, visit: https://bytex.work/docs'));
|
|
26
|
+
console.log('');
|
package/src/index.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { createClient } from '@supabase/supabase-js';
|
|
2
|
-
|
|
3
|
-
const SUPABASE_URL = 'https://vkiddclfbwmslaiyyftl.supabase.co';
|
|
4
|
-
const SUPABASE_ANON_KEY = 'sb_publishable_hXMlH9OmJG1_n1s-3lbXKg_6V-88Lj9';
|
|
5
|
-
const WORKER_URL = 'https://api.bytex.work/';
|
|
6
|
-
|
|
7
|
-
export class BytexCloud {
|
|
8
|
-
constructor(config = {}) {
|
|
9
|
-
this.config = config;
|
|
10
|
-
this.apiKey = config.apiKey || null;
|
|
11
|
-
this.workerUrl = config.workerUrl || WORKER_URL;
|
|
12
|
-
|
|
13
|
-
const supabaseConfig = {
|
|
14
|
-
auth: {
|
|
15
|
-
persistSession: true,
|
|
16
|
-
autoRefreshToken: true,
|
|
17
|
-
detectSessionInUrl: false
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
if (config.storage) {
|
|
21
|
-
supabaseConfig.auth.storage = config.storage;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, supabaseConfig);
|
|
25
|
-
this._listeners = {};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async _execute(fn, action) {
|
|
29
|
-
try {
|
|
30
|
-
return await fn();
|
|
31
|
-
} catch (e) {
|
|
32
|
-
const diag = await this.xray();
|
|
33
|
-
const advice = diag.advice.length > 0 ? `\n[X-RAY Advice]: ${diag.advice.join(' ')}` : '';
|
|
34
|
-
throw new Error(`${action} failed: ${e.message}${advice}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async login(email, password) {
|
|
39
|
-
return this._execute(async () => {
|
|
40
|
-
const { data, error } = await this.supabase.auth.signInWithPassword({ email, password });
|
|
41
|
-
if (error) throw error;
|
|
42
|
-
return data.user;
|
|
43
|
-
}, 'Login');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async uploadData(name, data) {
|
|
47
|
-
return this._execute(async () => {
|
|
48
|
-
this._requireKey();
|
|
49
|
-
const streamName = name.endsWith('.btx') ? name : `${name}.stream.btx`;
|
|
50
|
-
const url = `${this.workerUrl}?action=upload&key=${this.apiKey}&file=${encodeURIComponent(streamName)}`;
|
|
51
|
-
const { data: { session } } = await this.supabase.auth.getSession();
|
|
52
|
-
|
|
53
|
-
const payload = typeof data === 'string' ? data : JSON.stringify(data);
|
|
54
|
-
|
|
55
|
-
const res = await fetch(url, {
|
|
56
|
-
method: 'POST',
|
|
57
|
-
headers: {
|
|
58
|
-
'Authorization': `Bearer ${session?.access_token || ''}`,
|
|
59
|
-
'Content-Type': 'application/json'
|
|
60
|
-
},
|
|
61
|
-
body: payload
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
if (!res.ok) throw new Error(`Upload failed with status ${res.status}`);
|
|
65
|
-
return await res.json();
|
|
66
|
-
}, 'Upload');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async downloadData(name) {
|
|
70
|
-
return this._execute(async () => {
|
|
71
|
-
this._requireKey();
|
|
72
|
-
const streamName = name.endsWith('.btx') ? name : `${name}.stream.btx`;
|
|
73
|
-
const url = `${this.workerUrl}?action=view&key=${this.apiKey}&file=${encodeURIComponent(streamName)}&_cb=${Date.now()}`;
|
|
74
|
-
const { data: { session } } = await this.supabase.auth.getSession();
|
|
75
|
-
const res = await fetch(url, {
|
|
76
|
-
headers: { 'Authorization': `Bearer ${session?.access_token || ''}` }
|
|
77
|
-
});
|
|
78
|
-
if (!res.ok) throw new Error(`Status ${res.status}`);
|
|
79
|
-
const text = await res.text();
|
|
80
|
-
try { return JSON.parse(text); } catch (e) { return text.split('\n').filter(Boolean).map(line => JSON.parse(line)); }
|
|
81
|
-
}, 'Download');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async xray() {
|
|
85
|
-
const report = { timestamp: new Date().toISOString(), checks: {}, advice: [] };
|
|
86
|
-
const { data: { session } } = await this.supabase.auth.getSession();
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const res = await fetch(`${this.workerUrl}?action=list&key=${this.apiKey}`);
|
|
90
|
-
report.checks.connectivity = res.ok ? 'OK' : 'ERROR';
|
|
91
|
-
} catch (e) {
|
|
92
|
-
report.checks.connectivity = 'OFFLINE';
|
|
93
|
-
report.advice.push('ByteX Cloud unreachable. Check internet connection.');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!this.apiKey) report.advice.push('API Key is missing.');
|
|
97
|
-
if (!session) report.advice.push('User is not logged in.');
|
|
98
|
-
if (!this.config.storage) report.advice.push('AsyncStorage engine is missing.');
|
|
99
|
-
|
|
100
|
-
return report;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
_requireKey() { if (!this.apiKey) throw new Error('API Key required!'); }
|
|
104
|
-
}
|