@urbackend/sdk 0.1.0 → 0.1.1
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/.prettierrc +7 -0
- package/README.md +85 -82
- package/eslint.config.js +15 -0
- package/package.json +3 -11
- package/src/client.ts +107 -0
- package/src/errors.ts +90 -0
- package/src/index.ts +13 -0
- package/src/modules/auth.ts +47 -0
- package/src/modules/database.ts +53 -0
- package/src/modules/storage.ts +41 -0
- package/src/types/index.ts +58 -0
- package/tests/auth.test.ts +108 -0
- package/tests/database.test.ts +118 -0
- package/tests/storage.test.ts +62 -0
- package/tests/tsconfig.json +9 -0
- package/tsconfig.json +18 -0
- package/tsup.config.ts +16 -0
- package/vitest.config.ts +12 -0
- package/dist/index.cjs +0 -315
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.mts +0 -158
- package/dist/index.d.ts +0 -158
- package/dist/index.mjs +0 -281
- package/dist/index.mjs.map +0 -1
package/.prettierrc
ADDED
package/README.md
CHANGED
|
@@ -1,82 +1,85 @@
|
|
|
1
|
-
# urbackend-sdk
|
|
2
|
-
|
|
3
|
-
Official TypeScript SDK for [urBackend](https://urbackend.bitbros.in) — the instant Backend-as-a-Service for frontend developers.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
npm install @urbackend/sdk
|
|
7
|
-
|
|
8
|
-
## Quick Start
|
|
9
|
-
```javascript
|
|
10
|
-
import urBackend from '@urbackend/sdk';
|
|
11
|
-
|
|
12
|
-
const client = urBackend({ apiKey: 'YOUR_API_KEY' });
|
|
13
|
-
|
|
14
|
-
// Auth
|
|
15
|
-
await client.auth.signUp({ email, password, name });
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
await client.db.
|
|
21
|
-
await client.db.
|
|
22
|
-
await client.db.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
###
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
|
38
|
-
|
|
39
|
-
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
|
46
|
-
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1
|
+
# urbackend-sdk
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for [urBackend](https://urbackend.bitbros.in) — the instant Backend-as-a-Service for frontend developers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
npm install @urbackend/sdk
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
```javascript
|
|
10
|
+
import urBackend from '@urbackend/sdk';
|
|
11
|
+
|
|
12
|
+
const client = urBackend({ apiKey: 'YOUR_API_KEY' });
|
|
13
|
+
|
|
14
|
+
// Auth
|
|
15
|
+
await client.auth.signUp({ email, password, name });
|
|
16
|
+
await client.auth.login({ email, password });
|
|
17
|
+
await client.auth.me();
|
|
18
|
+
|
|
19
|
+
// Database — collections are auto-created on first insert
|
|
20
|
+
await client.db.insert('products', { name: 'Chair', price: 99 });
|
|
21
|
+
await client.db.getAll('products');
|
|
22
|
+
await client.db.getOne('products', id);
|
|
23
|
+
await client.db.update('products', id, { price: 79 });
|
|
24
|
+
await client.db.delete('products', id);
|
|
25
|
+
|
|
26
|
+
// Storage
|
|
27
|
+
await client.storage.upload(file);
|
|
28
|
+
await client.storage.deleteFile(path);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API Reference
|
|
32
|
+
|
|
33
|
+
### Client initialization
|
|
34
|
+
`urBackend({ apiKey: string, baseUrl?: string })`
|
|
35
|
+
|
|
36
|
+
### Auth
|
|
37
|
+
| Method | Params | Returns |
|
|
38
|
+
|--------|--------|---------|
|
|
39
|
+
| signUp | { email, password, name? } | AuthUser |
|
|
40
|
+
| login | { email, password } | { token, user } |
|
|
41
|
+
| me | token? | AuthUser |
|
|
42
|
+
| logout | — | void |
|
|
43
|
+
|
|
44
|
+
### Database
|
|
45
|
+
| Method | Params | Returns |
|
|
46
|
+
|--------|--------|---------|
|
|
47
|
+
| getAll<T> | collection | T[] |
|
|
48
|
+
| getOne<T> | collection, id | T |
|
|
49
|
+
| insert<T> | collection, data | T |
|
|
50
|
+
| update<T> | collection, id, data | T |
|
|
51
|
+
| delete | collection, id | { deleted: boolean } |
|
|
52
|
+
|
|
53
|
+
### Storage
|
|
54
|
+
| Method | Params | Returns |
|
|
55
|
+
|--------|--------|---------|
|
|
56
|
+
| upload | file, filename? | { url, path } |
|
|
57
|
+
| deleteFile | path | { deleted: boolean } |
|
|
58
|
+
|
|
59
|
+
## Error Handling
|
|
60
|
+
```javascript
|
|
61
|
+
import { AuthError, NotFoundError, RateLimitError } from '@urbackend/sdk';
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await client.db.getOne('products', id);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
if (e instanceof NotFoundError) console.log('Not found');
|
|
67
|
+
if (e instanceof RateLimitError) console.log('Slow down');
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## TypeScript Support
|
|
72
|
+
```typescript
|
|
73
|
+
interface Product { _id: string; name: string; price: number; }
|
|
74
|
+
const products = await client.db.getAll<Product>('products');
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Limits
|
|
78
|
+
- Rate limit: 100 requests / 15 mins per IP
|
|
79
|
+
- Database: 50 MB per project
|
|
80
|
+
- Storage: 100 MB per project
|
|
81
|
+
- File upload: 5 MB per file
|
|
82
|
+
|
|
83
|
+
## Security
|
|
84
|
+
⚠️ Never expose your API key in client-side/browser code.
|
|
85
|
+
Use environment variables: `process.env.URBACKEND_API_KEY`
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import eslint from '@eslint/js';
|
|
2
|
+
import tseslint from 'typescript-eslint';
|
|
3
|
+
|
|
4
|
+
export default tseslint.config(
|
|
5
|
+
eslint.configs.recommended,
|
|
6
|
+
...tseslint.configs.recommended,
|
|
7
|
+
{
|
|
8
|
+
rules: {
|
|
9
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
ignores: ['dist/**', 'node_modules/**', 'tests/**'],
|
|
14
|
+
},
|
|
15
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urbackend/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Official TypeScript SDK for urBackend BaaS",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -12,16 +12,11 @@
|
|
|
12
12
|
"require": "./dist/index.cjs"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"README.md"
|
|
18
|
-
],
|
|
19
15
|
"scripts": {
|
|
20
16
|
"build": "tsup",
|
|
21
17
|
"test": "vitest run --coverage",
|
|
22
18
|
"lint": "eslint src",
|
|
23
|
-
"format": "prettier --write \"src/**/*.ts\""
|
|
24
|
-
"prepublishOnly": "npm run build && npm test"
|
|
19
|
+
"format": "prettier --write \"src/**/*.ts\""
|
|
25
20
|
},
|
|
26
21
|
"keywords": [
|
|
27
22
|
"urbackend",
|
|
@@ -53,8 +48,5 @@
|
|
|
53
48
|
"typescript": "^5.9.3",
|
|
54
49
|
"typescript-eslint": "^8.57.2",
|
|
55
50
|
"vitest": "^4.1.2"
|
|
56
|
-
},
|
|
57
|
-
"dependencies": {
|
|
58
|
-
"@urbackend/sdk": "^1.0.0"
|
|
59
51
|
}
|
|
60
|
-
}
|
|
52
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { UrBackendConfig, RequestOptions } from './types';
|
|
2
|
+
import { UrBackendError, parseApiError } from './errors';
|
|
3
|
+
import { AuthModule } from './modules/auth';
|
|
4
|
+
import { DatabaseModule } from './modules/database';
|
|
5
|
+
import { StorageModule } from './modules/storage';
|
|
6
|
+
|
|
7
|
+
export class UrBackendClient {
|
|
8
|
+
private apiKey: string;
|
|
9
|
+
private baseUrl: string;
|
|
10
|
+
private _auth?: AuthModule;
|
|
11
|
+
private _db?: DatabaseModule;
|
|
12
|
+
private _storage?: StorageModule;
|
|
13
|
+
private headers: Record<string, string>;
|
|
14
|
+
|
|
15
|
+
constructor(config: UrBackendConfig) {
|
|
16
|
+
this.apiKey = config.apiKey;
|
|
17
|
+
this.baseUrl = config.baseUrl || 'https://api.ub.bitbros.in';
|
|
18
|
+
this.headers = config.headers || {};
|
|
19
|
+
|
|
20
|
+
if (typeof window !== 'undefined') {
|
|
21
|
+
console.warn(
|
|
22
|
+
'⚠️ urbackend-sdk: Avoid exposing your SK-API key in client-side code(instead use pk_live key). This can lead to unauthorized access to your account and data.',
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get auth(): AuthModule {
|
|
28
|
+
if (!this._auth) {
|
|
29
|
+
this._auth = new AuthModule(this);
|
|
30
|
+
}
|
|
31
|
+
return this._auth;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get db(): DatabaseModule {
|
|
35
|
+
if (!this._db) {
|
|
36
|
+
this._db = new DatabaseModule(this);
|
|
37
|
+
}
|
|
38
|
+
return this._db;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get storage(): StorageModule {
|
|
42
|
+
if (!this._storage) {
|
|
43
|
+
this._storage = new StorageModule(this);
|
|
44
|
+
}
|
|
45
|
+
return this._storage;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Internal request handler
|
|
50
|
+
*/
|
|
51
|
+
public async request<T>(
|
|
52
|
+
method: string,
|
|
53
|
+
path: string,
|
|
54
|
+
options: RequestOptions = {},
|
|
55
|
+
): Promise<T> {
|
|
56
|
+
const url = `${this.baseUrl}${path}`;
|
|
57
|
+
const headers: Record<string, string> = {
|
|
58
|
+
'x-api-key': this.apiKey,
|
|
59
|
+
'User-Agent': `urbackend-sdk-js/0.1.1`,
|
|
60
|
+
...this.headers,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
if (options.token) {
|
|
64
|
+
headers['Authorization'] = `Bearer ${options.token}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let requestBody: BodyInit | undefined;
|
|
68
|
+
|
|
69
|
+
if (options.isMultipart) {
|
|
70
|
+
// Fetch handles FormData content type and boundary
|
|
71
|
+
requestBody = options.body as FormData;
|
|
72
|
+
} else if (options.body) {
|
|
73
|
+
headers['Content-Type'] = 'application/json';
|
|
74
|
+
requestBody = JSON.stringify(options.body);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(url, {
|
|
79
|
+
method,
|
|
80
|
+
headers,
|
|
81
|
+
body: requestBody,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
throw await parseApiError(response);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const contentType = response.headers.get('content-type');
|
|
89
|
+
if (contentType && contentType.includes('application/json')) {
|
|
90
|
+
const json = await response.json();
|
|
91
|
+
// The API returns { data, success, message }
|
|
92
|
+
return json.data !== undefined ? json.data : json;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (await response.text()) as unknown as T;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error instanceof UrBackendError) {
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
throw new UrBackendError(
|
|
101
|
+
error instanceof Error ? error.message : 'Network request failed',
|
|
102
|
+
0,
|
|
103
|
+
path,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export class UrBackendError extends Error {
|
|
2
|
+
constructor(
|
|
3
|
+
public message: string,
|
|
4
|
+
public statusCode: number,
|
|
5
|
+
public endpoint: string,
|
|
6
|
+
) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'UrBackendError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class AuthError extends UrBackendError {
|
|
13
|
+
constructor(message: string, statusCode: number, endpoint: string) {
|
|
14
|
+
super(message, statusCode, endpoint);
|
|
15
|
+
this.name = 'AuthError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class NotFoundError extends UrBackendError {
|
|
20
|
+
constructor(message: string, endpoint: string) {
|
|
21
|
+
super(message, 404, endpoint);
|
|
22
|
+
this.name = 'NotFoundError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class RateLimitError extends UrBackendError {
|
|
27
|
+
public retryAfter?: number;
|
|
28
|
+
|
|
29
|
+
constructor(message: string, endpoint: string, retryAfter?: number) {
|
|
30
|
+
super(message, 429, endpoint);
|
|
31
|
+
this.name = 'RateLimitError';
|
|
32
|
+
this.retryAfter = retryAfter;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class StorageError extends UrBackendError {
|
|
37
|
+
constructor(message: string, statusCode: number, endpoint: string) {
|
|
38
|
+
super(message, statusCode, endpoint);
|
|
39
|
+
this.name = 'StorageError';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class ValidationError extends UrBackendError {
|
|
44
|
+
constructor(message: string, endpoint: string) {
|
|
45
|
+
super(message, 400, endpoint);
|
|
46
|
+
this.name = 'ValidationError';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function parseApiError(response: Response): Promise<UrBackendError> {
|
|
51
|
+
const endpoint = new URL(response.url).pathname;
|
|
52
|
+
let message = 'An unexpected error occurred';
|
|
53
|
+
let data: unknown;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
data = await response.json();
|
|
57
|
+
if (typeof data === 'object' && data !== null && 'message' in data) {
|
|
58
|
+
message = (data as { message: string }).message || message;
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// If not JSON, use status text
|
|
62
|
+
message = response.statusText || message;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const status = response.status;
|
|
66
|
+
|
|
67
|
+
if (status === 401 || status === 403) {
|
|
68
|
+
return new AuthError(message, status, endpoint);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (status === 404) {
|
|
72
|
+
return new NotFoundError(message, endpoint);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (status === 429) {
|
|
76
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
77
|
+
return new RateLimitError(message, endpoint, retryAfter ? parseInt(retryAfter, 10) : undefined);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (status === 400) {
|
|
81
|
+
return new ValidationError(message, endpoint);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Default for 5xx or other 4xx
|
|
85
|
+
if (endpoint.includes('/api/storage')) {
|
|
86
|
+
return new StorageError(message, status, endpoint);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return new UrBackendError(message, status, endpoint);
|
|
90
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { UrBackendClient } from './client';
|
|
2
|
+
import { UrBackendConfig } from './types';
|
|
3
|
+
|
|
4
|
+
export * from './types';
|
|
5
|
+
export * from './errors';
|
|
6
|
+
export { UrBackendClient };
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Factory function to create a new urBackend client
|
|
10
|
+
*/
|
|
11
|
+
export default function urBackend(config: UrBackendConfig): UrBackendClient {
|
|
12
|
+
return new UrBackendClient(config);
|
|
13
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { UrBackendClient } from '../client';
|
|
2
|
+
import { AuthUser, AuthResponse, SignUpPayload, LoginPayload } from '../types';
|
|
3
|
+
import { AuthError } from '../errors';
|
|
4
|
+
|
|
5
|
+
export class AuthModule {
|
|
6
|
+
private sessionToken?: string;
|
|
7
|
+
|
|
8
|
+
constructor(private client: UrBackendClient) {}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a new user account
|
|
12
|
+
*/
|
|
13
|
+
public async signUp(payload: SignUpPayload): Promise<AuthUser> {
|
|
14
|
+
return this.client.request<AuthUser>('POST', '/api/userAuth/signup', { body: payload });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Log in an existing user and store the session token
|
|
19
|
+
*/
|
|
20
|
+
public async login(payload: LoginPayload): Promise<AuthResponse> {
|
|
21
|
+
const response = await this.client.request<AuthResponse>('POST', '/api/userAuth/login', {
|
|
22
|
+
body: payload,
|
|
23
|
+
});
|
|
24
|
+
this.sessionToken = response.token;
|
|
25
|
+
return response;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get the current authenticated user's profile
|
|
30
|
+
*/
|
|
31
|
+
public async me(token?: string): Promise<AuthUser> {
|
|
32
|
+
const activeToken = token || this.sessionToken;
|
|
33
|
+
|
|
34
|
+
if (!activeToken) {
|
|
35
|
+
throw new AuthError('Authentication token is required for /me endpoint', 401, '/api/userAuth/me');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return this.client.request<AuthUser>('GET', '/api/userAuth/me', { token: activeToken });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clear the local session token
|
|
43
|
+
*/
|
|
44
|
+
public logout(): void {
|
|
45
|
+
this.sessionToken = undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { UrBackendClient } from '../client';
|
|
2
|
+
import { DocumentData, InsertPayload, UpdatePayload } from '../types';
|
|
3
|
+
import { NotFoundError } from '../errors';
|
|
4
|
+
|
|
5
|
+
export class DatabaseModule {
|
|
6
|
+
constructor(private client: UrBackendClient) {}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fetch all documents from a collection
|
|
10
|
+
*/
|
|
11
|
+
public async getAll<T extends DocumentData>(collection: string): Promise<T[]> {
|
|
12
|
+
try {
|
|
13
|
+
return await this.client.request<T[]>('GET', `/api/data/${collection}`);
|
|
14
|
+
} catch (e) {
|
|
15
|
+
if (e instanceof NotFoundError) {
|
|
16
|
+
return [] as T[];
|
|
17
|
+
}
|
|
18
|
+
throw e;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fetch a single document by its ID
|
|
24
|
+
*/
|
|
25
|
+
public async getOne<T extends DocumentData>(collection: string, id: string): Promise<T> {
|
|
26
|
+
return this.client.request<T>('GET', `/api/data/${collection}/${id}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Insert a new document into a collection
|
|
31
|
+
*/
|
|
32
|
+
public async insert<T extends DocumentData>(collection: string, data: InsertPayload): Promise<T> {
|
|
33
|
+
return this.client.request<T>('POST', `/api/data/${collection}`, { body: data });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Update an existing document by its ID
|
|
38
|
+
*/
|
|
39
|
+
public async update<T extends DocumentData>(
|
|
40
|
+
collection: string,
|
|
41
|
+
id: string,
|
|
42
|
+
data: UpdatePayload,
|
|
43
|
+
): Promise<T> {
|
|
44
|
+
return this.client.request<T>('PUT', `/api/data/${collection}/${id}`, { body: data });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Delete a document by its ID
|
|
49
|
+
*/
|
|
50
|
+
public async delete(collection: string, id: string): Promise<{ deleted: boolean }> {
|
|
51
|
+
return this.client.request<{ deleted: boolean }>('DELETE', `/api/data/${collection}/${id}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
import { UrBackendClient } from '../client';
|
|
3
|
+
import { UploadResponse } from '../types';
|
|
4
|
+
|
|
5
|
+
export class StorageModule {
|
|
6
|
+
constructor(private client: UrBackendClient) {}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Upload a file to storage
|
|
10
|
+
*/
|
|
11
|
+
public async upload(file: unknown, filename?: string): Promise<UploadResponse> {
|
|
12
|
+
const formData = new FormData();
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
typeof window === 'undefined' &&
|
|
16
|
+
typeof Buffer !== 'undefined' &&
|
|
17
|
+
Buffer.isBuffer(file)
|
|
18
|
+
) {
|
|
19
|
+
// In Node.js environment, convert Buffer to Blob for standard FormData
|
|
20
|
+
const blob = new Blob([file as unknown as BlobPart]);
|
|
21
|
+
formData.append('file', blob, filename || 'file');
|
|
22
|
+
} else {
|
|
23
|
+
// Browser File/Blob or Node.js Blob/File
|
|
24
|
+
formData.append('file', file as unknown as Blob, filename);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return this.client.request<UploadResponse>('POST', '/api/storage/upload', {
|
|
28
|
+
body: formData,
|
|
29
|
+
isMultipart: true,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Delete a file from storage by its path
|
|
35
|
+
*/
|
|
36
|
+
public async deleteFile(path: string): Promise<{ deleted: boolean }> {
|
|
37
|
+
return this.client.request<{ deleted: boolean }>('DELETE', '/api/storage/file', {
|
|
38
|
+
body: { path },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface UrBackendConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface RequestOptions {
|
|
8
|
+
body?: unknown;
|
|
9
|
+
token?: string;
|
|
10
|
+
isMultipart?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SignUpPayload {
|
|
14
|
+
email: string;
|
|
15
|
+
password: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface LoginPayload {
|
|
20
|
+
email: string;
|
|
21
|
+
password: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AuthUser {
|
|
25
|
+
_id: string;
|
|
26
|
+
email: string;
|
|
27
|
+
name?: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AuthResponse {
|
|
32
|
+
token: string;
|
|
33
|
+
user: AuthUser;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface DocumentData {
|
|
37
|
+
_id: string;
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface InsertPayload {
|
|
42
|
+
[key: string]: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface UpdatePayload {
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface UploadResponse {
|
|
50
|
+
url: string;
|
|
51
|
+
path: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ApiResponse<T> {
|
|
55
|
+
data: T;
|
|
56
|
+
success: boolean;
|
|
57
|
+
message?: string;
|
|
58
|
+
}
|