@objectstack/service-storage 4.0.3 → 4.0.5
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 +141 -0
- package/dist/index.cjs +1117 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7032 -22
- package/dist/index.d.ts +7032 -22
- package/dist/index.js +1110 -69
- package/dist/index.js.map +1 -1
- package/package.json +43 -6
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -169
- package/src/index.ts +0 -8
- package/src/local-storage-adapter.test.ts +0 -91
- package/src/local-storage-adapter.ts +0 -100
- package/src/s3-storage-adapter.ts +0 -88
- package/src/storage-service-plugin.ts +0 -66
- package/tsconfig.json +0 -17
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @objectstack/service-storage
|
|
2
|
+
|
|
3
|
+
Storage Service for ObjectStack — implements `IStorageService` with local filesystem and S3-compatible adapters, REST routes for front-end uploads, and presigned URL support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multiple Adapters**: Local filesystem (development) and S3-compatible storage (production)
|
|
8
|
+
- **Presigned Uploads**: Browser-direct upload via presigned URLs (S3 native, local HMAC-signed tokens)
|
|
9
|
+
- **Chunked / Multipart Upload**: Resumable large file uploads with progress tracking
|
|
10
|
+
- **File Metadata Store**: `system_file` object tracks fileId → key mapping and lifecycle status
|
|
11
|
+
- **REST Routes**: Auto-mounted `/api/v1/storage/*` endpoints consumed by `@objectstack/client`
|
|
12
|
+
- **Type-Safe**: Full TypeScript support with Zod-validated API contracts
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @objectstack/service-storage
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For S3 adapter (optional peer dependencies):
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Basic Usage
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { ObjectKernel } from '@objectstack/core';
|
|
29
|
+
import { StorageServicePlugin } from '@objectstack/service-storage';
|
|
30
|
+
|
|
31
|
+
const kernel = new ObjectKernel();
|
|
32
|
+
kernel.use(new StorageServicePlugin({
|
|
33
|
+
adapter: 'local',
|
|
34
|
+
local: { rootDir: './uploads' },
|
|
35
|
+
}));
|
|
36
|
+
await kernel.bootstrap();
|
|
37
|
+
|
|
38
|
+
// Programmatic access
|
|
39
|
+
const storage = kernel.getService('file-storage');
|
|
40
|
+
await storage.upload('files/hello.txt', Buffer.from('hello'));
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
### Local Filesystem Adapter (Development)
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
new StorageServicePlugin({
|
|
49
|
+
adapter: 'local',
|
|
50
|
+
local: {
|
|
51
|
+
rootDir: './uploads',
|
|
52
|
+
baseUrl: 'http://localhost:3000', // for presigned URLs
|
|
53
|
+
signingSecret: 'dev-secret', // auto-generated if omitted
|
|
54
|
+
},
|
|
55
|
+
presignedTtl: 3600, // 1 hour
|
|
56
|
+
sessionTtl: 86400, // 24 hours for chunked uploads
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### S3 Adapter (Production)
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
new StorageServicePlugin({
|
|
64
|
+
adapter: 's3',
|
|
65
|
+
s3: {
|
|
66
|
+
bucket: 'my-bucket',
|
|
67
|
+
region: 'us-east-1',
|
|
68
|
+
// Optional for S3-compatible services (R2, MinIO, Spaces):
|
|
69
|
+
// endpoint: 'https://r2.cloudflarestorage.com/account-id',
|
|
70
|
+
// forcePathStyle: true,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## REST API Endpoints
|
|
76
|
+
|
|
77
|
+
All routes are mounted at `/api/v1/storage` (configurable via `basePath`).
|
|
78
|
+
|
|
79
|
+
| Method | Path | Description |
|
|
80
|
+
|--------|------|-------------|
|
|
81
|
+
| POST | `/upload/presigned` | Get presigned upload URL |
|
|
82
|
+
| POST | `/upload/complete` | Mark upload as committed |
|
|
83
|
+
| POST | `/upload/chunked` | Initiate chunked upload |
|
|
84
|
+
| PUT | `/upload/chunked/:uploadId/chunk/:chunkIndex` | Upload a chunk |
|
|
85
|
+
| POST | `/upload/chunked/:uploadId/complete` | Complete chunked upload |
|
|
86
|
+
| GET | `/upload/chunked/:uploadId/progress` | Get upload progress |
|
|
87
|
+
| GET | `/files/:fileId/url` | Get download URL |
|
|
88
|
+
| PUT | `/_local/raw/:token` | Local raw upload (presigned) |
|
|
89
|
+
| GET | `/_local/raw/:token` | Local raw download (presigned) |
|
|
90
|
+
|
|
91
|
+
## Client SDK Usage
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { ObjectStackClient } from '@objectstack/client';
|
|
95
|
+
|
|
96
|
+
const client = new ObjectStackClient({ baseUrl: 'http://localhost:3000' });
|
|
97
|
+
|
|
98
|
+
// Simple upload (presigned URL flow)
|
|
99
|
+
const result = await client.storage.upload(file, 'user');
|
|
100
|
+
|
|
101
|
+
// Chunked upload for large files
|
|
102
|
+
const session = await client.storage.initChunkedUpload({
|
|
103
|
+
filename: 'large-video.mp4',
|
|
104
|
+
mimeType: 'video/mp4',
|
|
105
|
+
totalSize: file.size,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Resume interrupted upload
|
|
109
|
+
const completed = await client.storage.resumeUpload(
|
|
110
|
+
session.data.uploadId,
|
|
111
|
+
file,
|
|
112
|
+
session.data.chunkSize,
|
|
113
|
+
session.data.resumeToken,
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Architecture
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
┌──────────────┐ ┌─────────────────────┐ ┌──────────────────┐
|
|
121
|
+
│ Client SDK │────▶│ REST Routes │────▶│ IStorageService │
|
|
122
|
+
│ (browser) │ │ /api/v1/storage/* │ │ (adapter) │
|
|
123
|
+
└──────────────┘ └─────────────────────┘ └──────────────────┘
|
|
124
|
+
│ │
|
|
125
|
+
▼ ▼
|
|
126
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
127
|
+
│ MetadataStore │ │ Filesystem / S3 │
|
|
128
|
+
│ (system_file) │ │ (actual bytes) │
|
|
129
|
+
└─────────────────┘ └─────────────────┘
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## System Objects
|
|
133
|
+
|
|
134
|
+
The plugin registers two system objects via the manifest service:
|
|
135
|
+
|
|
136
|
+
- **`system_file`** — File metadata (fileId, key, name, mimeType, size, scope, status)
|
|
137
|
+
- **`system_upload_session`** — Chunked upload state (progress, parts, resumeToken)
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
Apache-2.0
|