mbkbucket 3.8.2 โ†’ 4.0.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.
Files changed (38) hide show
  1. package/.env.example +15 -14
  2. package/README.md +561 -377
  3. package/index.js +32 -30
  4. package/lib/cli.js +154 -565
  5. package/lib/public/bucketadmin.css +2042 -1708
  6. package/lib/public/bucketadmin.js +1622 -1286
  7. package/lib/public/mbkbucket-helper-demo.html +4 -24
  8. package/lib/public/mbkbucket-helper.css +28 -34
  9. package/lib/public/mbkbucket-helper.js +4 -3
  10. package/lib/src/config/validation.js +86 -73
  11. package/lib/src/controllers/api.controller.js +459 -410
  12. package/lib/src/controllers/dashboard.controller.js +69 -49
  13. package/lib/src/controllers/info.controller.js +34 -3
  14. package/lib/src/controllers/view.controller.js +233 -231
  15. package/lib/src/core/storage/errors.js +62 -0
  16. package/lib/src/core/storage/gdrive-auth.js +193 -0
  17. package/lib/src/core/storage/gdrive-provider.js +656 -0
  18. package/lib/src/core/storage/index.js +14 -0
  19. package/lib/src/core/storage/models.js +80 -0
  20. package/lib/src/core/storage/path-strategy.js +65 -0
  21. package/lib/src/core/storage/s3-provider.js +491 -0
  22. package/lib/src/core/storage/storage-manager.js +203 -0
  23. package/lib/src/core/storage/storage-provider.js +217 -0
  24. package/lib/src/middleware/bucket-resolver.js +37 -37
  25. package/lib/src/routes/api.routes.js +5 -1
  26. package/lib/src/routes/index.js +60 -60
  27. package/lib/src/routes/view.routes.js +28 -25
  28. package/lib/src/services/s3.service.js +42 -594
  29. package/lib/src/services/storage.service.js +213 -0
  30. package/lib/src/utils/errors.js +56 -46
  31. package/package.json +11 -12
  32. package/views/bucket.hbs +47 -71
  33. package/views/bucketadmin_move_modal.hbs +35 -0
  34. package/views/bucketadmin_preview_modal.hbs +108 -40
  35. package/views/bucketadmincontent.hbs +193 -144
  36. package/views/mbkbucket_info.hbs +134 -232
  37. package/.mbkbucket.config.example.json +0 -14
  38. package/lib/src/cli/auth-helper.js +0 -141
package/README.md CHANGED
@@ -1,378 +1,562 @@
1
- # mbkbucket
2
-
3
- > Flexible S3/R2 bucket management โ€” library, Express router, and CLI for mbktech.org applications.
4
-
5
- [![Version](https://img.shields.io/npm/v/mbkbucket.svg)](https://www.npmjs.com/package/mbkbucket)
6
- [![Downloads](https://img.shields.io/npm/dm/mbkbucket.svg)](https://www.npmjs.com/package/mbkbucket)
7
- [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
- [![Node.js](https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen.svg)](https://nodejs.org/)
9
- [![Check npm version](https://github.com/MIbnEKhalid/mbkbucket/actions/workflows/checkLatestVersion.yml/badge.svg)]
10
-
11
- ---
12
-
13
- ## Table of Contents
14
-
15
- - [Installation](#installation-)
16
- - [Quick Start](#quick-start-)
17
- - [CLI Usage](#cli-usage-)
18
- - [Library API](#library-api-)
19
- - [Express Router](#express-router-)
20
- - [Environment Configuration](#environment-configuration-)
21
- - [Supported Storage Providers](#supported-storage-providers-)
22
- - [Tests](#automated-tests-)
23
- - [Support](#contact--support)
24
-
25
- ---
26
-
27
- ## Installation ๐Ÿ“ฆ
28
-
29
- ```bash
30
- npm install mbkbucket
31
- ```
32
-
33
- ---
34
-
35
- ## Quick Start ๐Ÿš€
36
-
37
- ### As a Library
38
-
39
- ```js
40
- import { uploadFile, downloadFile, listfiles, deleteFile, deleteFolder, getFileMetadata, generateSignedUrl,
41
- bucket, // Express router
42
- packageJson // Package metadata
43
- } from 'mbkbucket';
44
-
45
- // Upload a file
46
- const result = await uploadFile(
47
- 'photos/sunset.jpg',
48
- fileBuffer,
49
- 'image/jpeg',
50
- { bucketName: 'R2_Bucket' }
51
- );
52
-
53
- // List files
54
- const { Contents, hasMore } = await listfiles('photos/', {
55
- delimiter: '/',
56
- maxKeys: 100
57
- });
58
-
59
- // Download a file
60
- const { Body, ContentType } = await downloadFile('photos/sunset.jpg');
61
-
62
- // Generate a pre-signed URL (valid for 1 hour)
63
- const { url } = await generateSignedUrl('photos/sunset.jpg', 'getObject', 3600);
64
- ```
65
-
66
- ### As Express Middleware
67
-
68
- ```js
69
- import mbkbucket from 'mbkbucket';
70
-
71
- // mbkbucket is a fully configured Express app with auth, sessions, and bucket routes
72
- mbkbucket.listen(3004, () => {
73
- console.log('mbkbucket server running on http://localhost:3004');
74
- });
75
- ```
76
-
77
- ### Host-controlled permissions
78
-
79
- The default `bucket` router remains superadmin-only. A host application can
80
- provide its own mbkauthe permission instead:
81
-
82
- ```js
83
- import { createBucketRouter } from 'mbkbucket';
84
- import { sessPerm } from 'mbkauthe';
85
- import { Permissions } from './permissions.js';
86
-
87
- app.use(createBucketRouter({
88
- authorization: {
89
- view: sessPerm(Permissions.storage.view),
90
- upload: sessPerm(Permissions.storage.upload),
91
- delete: sessPerm(Permissions.storage.delete),
92
- },
93
- }));
94
- ```
95
-
96
- Define `Permissions.storage.view`, `Permissions.storage.upload`, and
97
- `Permissions.storage.delete` in the host application's permission manifest and
98
- sync that manifest with mbkauthe. The supplied middleware controls the
99
- dashboard, private views, and bucket APIs; public views and info endpoints keep
100
- their existing behavior. When no middleware is supplied, mbkbucket uses
101
- `sessRole('superadmin')` for every private operation. A single middleware may
102
- also be passed as `authorization` to protect all three operations uniformly.
103
-
104
- The Express app includes:
105
- - **`/mbkbucket`** โ€” Admin dashboard (superadmin only)
106
- - **`/mbkbucket/api/*`** โ€” REST API for file operations
107
- - **`/mbkbucket/p_view/:key(*)`** โ€” Public file view
108
- - **`/mbkbucket/info`** โ€” Health & config info
109
-
110
- ---
111
-
112
- ## CLI Usage ๐Ÿ’ป
113
-
114
- > **โš ๏ธ Under active development.** The CLI is functional but some features may change.
115
-
116
- mbkbucket ships with a command-line interface for managing buckets directly from the terminal.
117
-
118
- ### Setup
119
-
120
- ```bash
121
- # Set your mbkauthe server URL (one-time)
122
- mbkbucket config set serverUrl https://your-server.example.com
123
-
124
- # Set your API token profile key (one-time, optional)
125
- mbkbucket config set profileKey your-profile-key
126
-
127
- # Authenticate via device flow
128
- mbkbucket login
129
- ```
130
-
131
- ### Commands
132
-
133
- | Command | Alias | Description |
134
- |---|---|---|
135
- | `mbkbucket login` | โ€” | Authenticate via mbkauthe device flow |
136
- | `mbkbucket logout` | โ€” | Clear stored credentials |
137
- | `mbkbucket whoami` | โ€” | Show login status |
138
- | | | |
139
- | `mbkbucket list [prefix]` | `ls` | List files and folders |
140
- | `mbkbucket upload <file> [key]` | `up` | Upload a file or folder |
141
- | `mbkbucket download <key> [dest]` | `dl` | Download a file or folder |
142
- | `mbkbucket delete <key>` | `rm` | Delete a file |
143
- | `mbkbucket delete-folder <prefix>` | `rmdir` | Recursively delete a folder |
144
- | `mbkbucket info <key>` | `stat` | Show file metadata (size, type, etc.) |
145
- | `mbkbucket signed-url <key>` | `sign` | Generate a pre-signed download URL |
146
- | | | |
147
- | `mbkbucket config` | `cfg` | Show all config values |
148
- | `mbkbucket config get <key>` | โ€” | Get a specific config value |
149
- | `mbkbucket config set <k> <v>` | โ€” | Set a config value |
150
- | `mbkbucket config unset <key>` | โ€” | Remove a config value |
151
- | `mbkbucket config path` | โ€” | Show config file location |
152
- | `mbkbucket config reset` | โ€” | Reset to demo values |
153
- | `mbkbucket config edit` | โ€” | Open config file in editor |
154
-
155
- ### Global Options
156
-
157
- | Flag | Description |
158
- |---|---|
159
- | `--app`, `-a <name>` | Override `APP_NAME` for key prefix isolation |
160
- | `--bucket`, `-b <name>` | Override default bucket name |
161
- | `--help`, `-h` | Show help |
162
-
163
- ### Login Options
164
-
165
- | Flag | Description |
166
- |---|---|
167
- | `--server <url>` | mbkauthe server URL |
168
- | `--profile-key <k>` | API token profile key |
169
-
170
- ### Config File
171
-
172
- CLI configuration is stored at `~/.mbkbucket/config.json`. A demo config is created automatically on first use.
173
-
174
- ---
175
-
176
- ## Library API ๐Ÿ“š
177
-
178
- All functions are exported from the package root. Full TypeScript declarations in [index.d.ts](index.d.ts).
179
-
180
- ### File Operations
181
-
182
- | Function | Description |
183
- |---|---|
184
- | `uploadFile(key, buffer, contentType, options?)` | Upload a file |
185
- | `downloadFile(key, options?)` | Download a file |
186
- | `deleteFile(key, bucketName?)` | Delete a single file |
187
- | `deleteFiles(keys, bucketName?)` | Batch delete up to 1000 files |
188
- | `deleteFolder(prefix, bucketName?)` | Recursively delete all files under a prefix |
189
- | `listfiles(prefix?, options?)` | List files with pagination and filtering |
190
- | `getFileMetadata(key, bucketName?)` | Get file metadata without downloading |
191
- | `fileExists(key, bucketName?)` | Check if a file exists |
192
- | `getFileSize(key, bucketName?)` | Get file size in bytes |
193
- | `generateSignedUrl(key, operation?, expiresIn?, bucketName?)` | Generate a pre-signed URL |
194
-
195
- ### Multipart Upload
196
-
197
- | Function | Description |
198
- |---|---|
199
- | `createMultipartUpload(key, contentType?, metadata?, bucketName?)` | Start a multipart upload |
200
- | `uploadPart(key, uploadId, partNumber, buffer, bucketName?)` | Upload a single part |
201
- | `completeMultipartUpload(key, uploadId, parts, bucketName?)` | Complete a multipart upload |
202
- | `abortMultipartUpload(key, uploadId, bucketName?)` | Abort a multipart upload |
203
- | `listIncompleteMultipartUploads(prefix?, bucketName?)` | List incomplete uploads |
204
- | `cleanupIncompleteMultipartUploads(olderThanDays?, prefix?, bucketName?)` | Clean up stale uploads |
205
-
206
- ### Bucket Management
207
-
208
- | Function | Description |
209
- |---|---|
210
- | `getAvailableBucketNames()` | List configured bucket names |
211
- | `resolveBucketName(bucketName?)` | Resolve and validate a bucket name |
212
- | `getBucketConfig(bucketName?)` | Get bucket credentials/config |
213
- | `getBucketClient(bucketName?)` | Get an S3 client instance |
214
- | `checkHealth()` | Check S3 connectivity |
215
-
216
- ### Key Prefixing
217
-
218
- | Function | Description |
219
- |---|---|
220
- | `ensureKeyHasAppPrefix(key?)` | Prefix a key with the app name |
221
- | `ensurePrefix(prefix?)` | Prefix a folder path with the app name |
222
- | `getAppName()` | Get the current app name |
223
-
224
- ### Config
225
-
226
- | Export | Description |
227
- |---|---|
228
- | `packageJson` | Package metadata (version, etc.) |
229
- | `appVersion` | Parent project version |
230
- | `mbkbucketVar` | Parsed mbkbucketVar config |
231
- | `checkVersion()` | Check for newer package versions |
232
-
233
- ### Express
234
-
235
- | Export | Description |
236
- |---|---|
237
- | `bucket` (named) | Express Router with all bucket routes |
238
- | `default` | Fully configured Express application |
239
-
240
- ---
241
-
242
- ## Express Router ๐Ÿ–ฅ๏ธ
243
-
244
- ### Routes
245
-
246
- | Route | Auth | Description |
247
- |---|---|---|
248
- | `GET /mbkbucket` | superadmin | Admin dashboard |
249
- | `GET /mbkbucket/info` | Any | Health check & config info |
250
- | `GET /mbkbucket/p_view/:key(*)` | Public | Public file viewer |
251
- | `POST /mbkbucket/api/list` | superadmin | List files (JSON) |
252
- | `POST /mbkbucket/api/upload` | superadmin | Upload file(s) |
253
- | `POST /mbkbucket/api/delete` | superadmin | Delete file(s) |
254
- | `POST /mbkbucket/api/delete-folder` | superadmin | Delete folder |
255
- | `POST /mbkbucket/api/create-folder` | superadmin | Create folder marker |
256
- | `POST /mbkbucket/api/rename` | superadmin | Rename/move a file |
257
- | `GET /mbkbucket/api/download/:key(*)` | superadmin | Download a file |
258
- | `GET /mbkbucket/api/metadata/:key(*)` | superadmin | Get file metadata |
259
- | `POST /mbkbucket/api/multipart/init` | superadmin | Init multipart upload |
260
- | `POST /mbkbucket/api/multipart/part` | superadmin | Upload multipart chunk |
261
- | `POST /mbkbucket/api/multipart/complete` | superadmin | Complete multipart |
262
- | `POST /mbkbucket/api/multipart/abort` | superadmin | Abort multipart |
263
- | `GET /mbkbucket/api/multipart/list` | superadmin | List incomplete uploads |
264
- | `POST /mbkbucket/api/multipart/cleanup` | superadmin | Clean up stale uploads |
265
-
266
- ### Views
267
-
268
- | Template | Description |
269
- |---|---|
270
- | `bucketportal.hbs` | Admin dashboard shell |
271
- | `bucketadmincontent.hbs` | File listing & management UI |
272
- | `bucketadmin_alerts.hbs` | Alert/notification partial |
273
- | `bucketadmin_delete_modal.hbs` | Delete confirmation modal |
274
- | `bucketadmin_preview_modal.hbs` | File preview modal |
275
- | `bucketadmin_filelist_skeleton.hbs` | Loading skeleton |
276
- | `bucket.hbs` | Public file viewer |
277
- | `mbkbucket_info.hbs` | Info/health page |
278
-
279
- ---
280
-
281
- ## Environment Configuration โš™๏ธ
282
-
283
- ### `BucketConnection` (required)
284
-
285
- JSON mapping of bucket names to S3-compatible credentials:
286
-
287
- ```env
288
- BucketConnection={"R2_Bucket":{"BUCKET_NAME":"my-bucket","ACCESS_KEY_ID":"...","SECRET_ACCESS_KEY":"...","ENDPOINT":"https://<id>.r2.cloudflarestorage.com"}}
289
- ```
290
-
291
- | Field | Required | Description |
292
- |---|---|---|
293
- | `BUCKET_NAME` | Yes | Target bucket name |
294
- | `ACCESS_KEY_ID` | Yes | Access key |
295
- | `SECRET_ACCESS_KEY` | Yes | Secret key |
296
- | `ENDPOINT` | Yes | S3-compatible endpoint URL |
297
- | `region` | No | AWS region (default: `auto`) |
298
-
299
- ### `mbkautheVar` (required)
300
-
301
- Standard mbkauthe configuration. Key fields for mbkbucket:
302
-
303
- | Field | Description |
304
- |---|---|
305
- | `APP_NAME` | App name used for key prefix isolation |
306
- | `bucket` | Default bucket name (falls back to first in `BucketConnection`) |
307
- | `CLI_AUTH_ENABLED` | Enable CLI device-flow login (`"true"` / `"false"`) |
308
-
309
- ### `mbkbucketVar` (optional)
310
-
311
- | Field | Default | Description |
312
- |---|---|---|
313
- | `publiView_enabled` | `"false"` | Enable public file viewer |
314
- | `p_view_inline` | `"false"` | Show files inline vs download |
315
-
316
- ### Common Mistakes
317
-
318
- ```env
319
- # โŒ Wrong โ€” inner object quoted as string
320
- BucketConnection={"R2_Bucket":"{\"BUCKET_NAME\":\"...\"}"}
321
-
322
- # โœ… Correct โ€” proper JSON nesting
323
- BucketConnection={"R2_Bucket":{"BUCKET_NAME":"...","ACCESS_KEY_ID":"...","SECRET_ACCESS_KEY":"...","ENDPOINT":"https://..."}}
324
- ```
325
-
326
- ---
327
-
328
- ## Supported Storage Providers ๐Ÿ—„๏ธ
329
-
330
- Any S3-compatible object storage:
331
-
332
- - **AWS S3**
333
- - **Cloudflare R2**
334
- - **IDrive e2**
335
- - **MinIO**
336
- - **Backblaze B2** (via S3-compatible API)
337
- - **DigitalOcean Spaces**
338
-
339
- ---
340
-
341
- ## Automated Tests ๐Ÿงช
342
-
343
- ```bash
344
- npm test
345
- ```
346
-
347
- Covers:
348
- - `mbkbucketVar` parsing, defaults, and boolean normalization
349
- - `BucketConnection` shape validation and required fields
350
-
351
- ---
352
-
353
- ## Contact & Support
354
-
355
- - **Website**: [mbktech.org/Support](https://mbktech.org/Support/?Project=MIbnEKhalidWeb)
356
- - **Email**: [support@mbktech.org](mailto:support@mbktech.org)
357
- - **GitHub**: [MIbnEKhalid/mbkbucket](https://github.com/MIbnEKhalid/mbkbucket)
358
-
359
- ---
360
-
361
- ## About
362
-
363
- Developed by [Muhammad Bin Khalid](https://github.com/MIbnEKhalid)
364
- Part of [MBK Tech](https://mbktech.org/)
365
-
366
- ---
367
-
368
- ## License
369
-
370
- Licensed under the MIT License. See [LICENSE](LICENSE) for details.
371
-
372
-
373
- <!--
374
- * MBKBucket
375
- * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
376
- * Licensed under the MIT License.
377
- * Source: https://github.com/MIbnEKhalid/mbkbucket
1
+ # mbkbucket
2
+
3
+ > Flexible S3, Cloudflare R2, and Google Drive cloud storage management โ€” library, Express router, dashboard, and standalone server CLI for mbktech.org applications.
4
+
5
+ [![Version](https://img.shields.io/npm/v/mbkbucket.svg)](https://www.npmjs.com/package/mbkbucket)
6
+ [![Downloads](https://img.shields.io/npm/dm/mbkbucket.svg)](https://www.npmjs.com/package/mbkbucket)
7
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)
9
+ [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests)
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ - [What's New in v4.0.0](#whats-new-in-v400-)
16
+ - [Installation](#installation-)
17
+ - [Quick Start](#quick-start-)
18
+ - [Supported Storage Providers](#supported-storage-providers-)
19
+ - [CLI Usage](#cli-usage-)
20
+ - [Library API](#library-api-)
21
+ - [File Operations](#file-operations)
22
+ - [Folder, Copy & Move Operations](#folder-copy--move-operations)
23
+ - [Multipart Upload](#multipart-upload)
24
+ - [Storage & Connection Management](#storage--connection-management)
25
+ - [Storage Core & Custom Drivers](#storage-core--custom-drivers)
26
+ - [Google Drive Authentication Helpers](#google-drive-authentication-helpers)
27
+ - [Key Prefixing & App Isolation](#key-prefixing--app-isolation)
28
+ - [Config & Diagnostics](#config--diagnostics)
29
+ - [Express Integration](#express-integration)
30
+ - [Express Router & HTTP API](#express-router--http-api-)
31
+ - [Endpoints](#endpoints)
32
+ - [Host-Controlled Permissions](#host-controlled-permissions)
33
+ - [Templates & Views](#templates--views)
34
+ - [Frontend Helper Client](#frontend-helper-client-)
35
+ - [Environment Configuration](#environment-configuration-)
36
+ - [BucketConnection](#bucketconnection-required)
37
+ - [mbkautheVar](#mbkauthevar-required)
38
+ - [mbkbucketVar](#mbkbucketvar-optional)
39
+ - [Automated Tests](#automated-tests-)
40
+ - [Contact & Support](#contact--support)
41
+ - [License](#license)
42
+
43
+ ---
44
+
45
+ ## What's New in v4.0.0 ๐ŸŒŸ
46
+
47
+ - **Provider-Independent Storage Architecture**: Unified `StorageProvider` base class and dynamic `StorageManager` registry. Seamlessly switch between or combine multiple storage backends.
48
+ - **Native Google Drive Support**: First-class Google Drive provider supporting both OAuth 2.0 (user authorization code / refresh token flow) and Google Cloud Service Account credentials with folder-level targeting (`folder_id`).
49
+ - **File Move & Copy Operations**: Built-in `moveFile` and `copyFile` operations across both library functions, REST APIs, and UI modals.
50
+ - **Native Folder Creation**: Create directory markers and structure via `createFolder` with automatic folder marker resolution.
51
+ - **Redesigned Admin Dashboard**: Clean modern UI with connection health badges, provider tags, storage connection switcher dropdown, dedicated Move/Rename modal, and enhanced media previews (video range streaming, PDF, audio, and code view).
52
+ - **Frontend Drop-in Client Library (`mbkbucket-helper.js`)**: Bundled client helper with a modal gallery browser, input file picker, drag-and-drop chunked uploader, URL builders, and toast notifications. Demo page included at `/mbkbucket/helper-demo`.
53
+ - **Custom Storage Drivers**: Register custom storage engines via `storageManager.registerDriver('custom', factory)`.
54
+ - **Full Backward Compatibility**: All legacy S3 helper methods remain available and map cleanly to the underlying storage provider.
55
+
56
+ ---
57
+
58
+ ## Installation ๐Ÿ“ฆ
59
+
60
+ ```bash
61
+ npm install mbkbucket
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Quick Start ๐Ÿš€
67
+
68
+ ### As a Library
69
+
70
+ ```js
71
+ import { uploadFile, downloadFile, listfiles, copyFile, moveFile, createFolder, deleteFile, generateSignedUrl, storageManager } from 'mbkbucket';
72
+
73
+ // Upload a file to default storage (or specify connectionName / bucketName)
74
+ const upload = await uploadFile(
75
+ 'documents/invoice.pdf',
76
+ fileBuffer,
77
+ 'application/pdf',
78
+ { connectionName: 'R2_Bucket' }
79
+ );
80
+
81
+ // List files and directories
82
+ const { Contents, CommonPrefixes } = await listfiles('documents/', {
83
+ delimiter: '/',
84
+ maxKeys: 100
85
+ });
86
+
87
+ // Copy or move files
88
+ await copyFile('documents/invoice.pdf', 'backup/invoice.pdf');
89
+ await moveFile('documents/invoice.pdf', 'archive/invoice-2026.pdf');
90
+
91
+ // Create a folder marker
92
+ await createFolder('projects/2026');
93
+
94
+ // Download a file stream
95
+ const { Body, ContentType, ContentLength } = await downloadFile('archive/invoice-2026.pdf');
96
+
97
+ // Generate pre-signed URL (for S3-compatible providers)
98
+ const { url } = await generateSignedUrl('archive/invoice-2026.pdf', 'getObject', 3600);
99
+
100
+ // Check health across active connections
101
+ const health = await storageManager.checkHealth('Google_Drive');
102
+ console.log('Google Drive status:', health.status);
103
+ ```
104
+
105
+ ### As Express Middleware
106
+
107
+ ```js
108
+ import express from 'express';
109
+ import { createBucketRouter } from 'mbkbucket';
110
+ import { sessRole, sessPerm } from 'mbkauthe';
111
+
112
+ const app = express();
113
+
114
+ // Mount the bucket dashboard and API under /mbkbucket
115
+ app.use(createBucketRouter({
116
+ authorization: {
117
+ view: sessRole('admin'),
118
+ upload: sessPerm('storage.upload'),
119
+ delete: sessRole('superadmin'),
120
+ },
121
+ publiViewEnabled: true,
122
+ }));
123
+
124
+ app.listen(3004, () => {
125
+ console.log('Server running on http://localhost:3004');
126
+ });
127
+ ```
128
+
129
+ ### As a Standalone Application
130
+
131
+ ```js
132
+ import mbkbucket from 'mbkbucket';
133
+
134
+ // mbkbucket is a pre-configured Express app with mbkauthe sessions, views, and routes
135
+ mbkbucket.listen(3004, () => {
136
+ console.log('mbkbucket running on http://localhost:3004/mbkbucket');
137
+ });
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Supported Storage Providers ๐Ÿ—„๏ธ
143
+
144
+ mbkbucket v4.0.0 supports multiple object and file storage providers simultaneously:
145
+
146
+ | Provider | Type Identifier | Features / Capabilities |
147
+ |---|---|---|
148
+ | **AWS S3** | `s3` | Multipart upload, signed URLs, range streaming, server-side copy/move |
149
+ | **Cloudflare R2** | `s3` / `r2` | S3-compatible, zero egress fees, signed URLs, multipart upload |
150
+ | **Google Drive** | `gdrive` / `google-drive` | OAuth 2.0 & Service Account auth, native folder hierarchy, direct download URLs, range requests |
151
+ | **MinIO** | `s3` / `minio` | Self-hosted S3-compatible storage, multipart, signed URLs |
152
+ | **IDrive e2** | `s3` | S3-compatible high-performance object storage |
153
+ | **Backblaze B2** | `s3` | S3-compatible API endpoints |
154
+ | **DigitalOcean Spaces** | `s3` | S3-compatible object storage |
155
+ | **Custom Drivers** | Any custom name | Register via `storageManager.registerDriver(type, factory)` |
156
+
157
+ ---
158
+
159
+ ## CLI Usage ๐Ÿ’ป
160
+
161
+ mbkbucket includes a standalone CLI server runner with built-in configuration verification, port binding, and browser launching.
162
+
163
+ ### Run Standalone Server
164
+
165
+ ```bash
166
+ # Run with npx without installation
167
+ npx mbkbucket
168
+
169
+ # Or after installing globally or in your repository
170
+ mbkbucket
171
+ ```
172
+
173
+ ### CLI Options
174
+
175
+ | Option | Shorthand | Description |
176
+ |---|---|---|
177
+ | `--port <number>` | `-p` | Port to listen on (default: `3004` or `$PORT`) |
178
+ | `--host <host>` | `-H` | Host address to bind to (default: `0.0.0.0` or `$HOST`) |
179
+ | `--app <name>` | `-a` | Override `APP_NAME` for bucket key prefix isolation |
180
+ | `--bucket <name>` | `-b` | Override default bucket / storage connection name |
181
+ | `--env <path>` | `-e` | Load environment variables from a custom `.env` file |
182
+ | `--dev` | `-d` | Run in development mode (`NODE_ENV=dev`) |
183
+ | `--open` | `-o` | Automatically open dashboard in default browser |
184
+ | `--version` | `-v` | Show version number |
185
+ | `--help` | `-h` | Show help text |
186
+
187
+ ### CLI Examples
188
+
189
+ ```bash
190
+ # Start server on custom port and open browser
191
+ mbkbucket -p 8080 --open
192
+
193
+ # Start with a specific production .env file and application prefix
194
+ mbkbucket --env ./configs/.env.prod -a portalapp
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Library API ๐Ÿ“š
200
+
201
+ Full TypeScript declarations are available in [index.d.ts](index.d.ts).
202
+
203
+ ### File Operations
204
+
205
+ | Function | Description |
206
+ |---|---|
207
+ | `uploadFile(key, buffer, contentType, options?)` | Upload a file buffer/stream with options (`preventOverwrite`, `metadata`, `bucketName`/`connectionName`) |
208
+ | `downloadFile(key, options?)` | Download a file as a readable stream with metadata and headers |
209
+ | `deleteFile(key, connectionName?)` | Delete a single file |
210
+ | `deleteFiles(keys, connectionName?)` | Batch delete an array of files |
211
+ | `listfiles(prefix?, options?)` | List files and folders with pagination, search, or delimiter filtering |
212
+ | `getFileMetadata(key, connectionName?)` | Retrieve file metadata without downloading content |
213
+ | `fileExists(key, connectionName?)` | Check if a file exists |
214
+ | `getFileSize(key, connectionName?)` | Get file size in bytes (returns `number` or `null`) |
215
+ | `generateSignedUrl(key, operation?, expiresIn?, connectionName?)` | Generate a pre-signed URL (`getObject` or `putObject`) |
216
+
217
+ ### Folder, Copy & Move Operations
218
+
219
+ | Function | Description |
220
+ |---|---|
221
+ | `createFolder(prefix, connectionName?, options?)` | Create a folder / directory marker |
222
+ | `copyFile(sourceKey, destKey, options?, connectionName?)` | Copy a file within storage |
223
+ | `moveFile(sourceKey, destKey, options?, connectionName?)` | Move or rename a file within storage |
224
+ | `deleteFolder(prefix, connectionName?)` | Recursively delete all files under a folder prefix |
225
+
226
+ ### Multipart Upload
227
+
228
+ *(Supported on S3-compatible providers)*
229
+
230
+ | Function | Description |
231
+ |---|---|
232
+ | `createMultipartUpload(key, contentType?, metadata?, connectionName?)` | Initialize a multipart upload session |
233
+ | `uploadPart(key, uploadId, partNumber, buffer, connectionName?)` | Upload an individual chunk part |
234
+ | `completeMultipartUpload(key, uploadId, parts, connectionName?)` | Finalize and assemble multipart upload |
235
+ | `abortMultipartUpload(key, uploadId, connectionName?)` | Abort and cancel a multipart upload |
236
+ | `listIncompleteMultipartUploads(prefix?, connectionName?)` | List pending / incomplete multipart uploads |
237
+ | `cleanupIncompleteMultipartUploads(olderThanDays?, prefix?, connectionName?)` | Clean up abandoned multipart chunks |
238
+
239
+ ### Storage & Connection Management
240
+
241
+ | Function / Export | Description |
242
+ |---|---|
243
+ | `getAvailableStorageNames()` / `getAvailableBucketNames()` | List all configured storage connection identifiers |
244
+ | `resolveStorageName(name?)` / `resolveBucketName(name?)` | Resolve and validate target connection name (falls back to default) |
245
+ | `getStorageProvider(name?)` | Get the initialized `StorageProvider` instance for a connection |
246
+ | `getStorageConfig(name?)` / `getBucketConfig(name?)` | Retrieve connection credentials and configuration object |
247
+ | `getBucketClient(bucketName?)` | Get the underlying AWS SDK S3Client instance (for S3 connections) |
248
+ | `checkHealth(name?)` | Execute health and connectivity check on a storage connection |
249
+ | `runHealthCheck(name?)` | Run diagnostic health check with console / debug output |
250
+
251
+ ### Storage Core & Custom Drivers
252
+
253
+ mbkbucket exposes its core abstraction classes so you can extend or inspect providers programmatically:
254
+
255
+ ```js
256
+ import { StorageProvider, StorageManager, storageManager } from 'mbkbucket';
257
+
258
+ // Register a custom storage driver
259
+ storageManager.registerDriver('local-disk', (name, config) => {
260
+ return new MyCustomLocalStorageProvider(name, config);
261
+ });
262
+
263
+ // Register a runtime connection using the driver
264
+ storageManager.registerConnection('local_backup', {
265
+ type: 'local-disk',
266
+ basePath: '/var/storage'
267
+ });
268
+ ```
269
+
270
+ Exported classes:
271
+ - `StorageProvider`: Abstract base class with required contracts (`uploadFile`, `downloadFile`, `listFiles`, `deleteFile`, `copyFile`, `moveFile`, `createFolder`, etc.) and `capabilities` flags.
272
+ - `S3StorageProvider`: S3, Cloudflare R2, MinIO implementation.
273
+ - `GoogleDriveStorageProvider`: Google Drive implementation.
274
+ - `StorageManager` / `storageManager`: Connection registry, driver repository, and health orchestrator.
275
+ - `StorageFile`, `StorageFolder`, `StorageListResult`: Standardized metadata models.
276
+ - Custom Errors: `StorageError`, `StorageNotFoundError`, `StorageAccessDeniedError`, `StorageConflictError`, `StorageValidationError`, `StorageConfigError`, `StorageQuotaExceededError`, `StorageUnsupportedOperationError`.
277
+
278
+ ### Google Drive Authentication Helpers
279
+
280
+ mbkbucket exports helper functions for handling Google Drive OAuth 2.0 flows:
281
+
282
+ | Export | Description |
283
+ |---|---|
284
+ | `getGoogleAuthUrl(options)` | Generate Google OAuth 2.0 consent URL (`clientId`, `redirectUri`, `state`) |
285
+ | `exchangeCodeForTokens(options)` | Exchange authorization code for access and refresh tokens |
286
+ | `refreshGoogleAccessToken(options)` | Refresh an expired access token using a refresh token |
287
+ | `getServiceAccountAccessToken(options)` | Generate access token using Google Cloud Service Account credentials |
288
+ | `GOOGLE_DRIVE_SCOPES` | Standard OAuth scope strings for Google Drive (`drive.file`, `drive`, etc.) |
289
+
290
+ ### Key Prefixing & App Isolation
291
+
292
+ | Function | Description |
293
+ |---|---|
294
+ | `getAppName()` | Retrieve the active application namespace from `mbkautheVar.APP_NAME` |
295
+ | `ensureKeyHasAppPrefix(key?)` | Prefix a file key with the app prefix to enforce tenant isolation |
296
+ | `ensurePrefix(prefix?)` | Prefix a folder path with the app prefix |
297
+ | `validateKeySafety(str, isKey?)` | Guard against path traversal attacks (`..`, null bytes, illegal characters) |
298
+
299
+ ### Config & Diagnostics
300
+
301
+ | Export | Description |
302
+ |---|---|
303
+ | `packageJson` | mbkbucket package metadata (version, license, etc.) |
304
+ | `appVersion` | Detected version of the host application |
305
+ | `mbkbucketVar` | Parsed and validated `mbkbucketVar` configuration |
306
+ | `checkVersion()` | Check GitHub releases for newer versions of mbkbucket |
307
+ | `validateConfiguration()` | Validate `mbkbucketVar` settings |
308
+ | `validateBucketConnection()` | Validate `BucketConnection` syntax and provider schemas |
309
+ | `validateAllConfiguration()` | Run all configuration validations |
310
+
311
+ ### Express Integration
312
+
313
+ | Export | Description |
314
+ |---|---|
315
+ | `bucket` (named) | Pre-configured Express router with superadmin protection |
316
+ | `createBucketRouter(options)` | Configurable router factory with custom permission middleware |
317
+ | `default` | Standalone Express server application ready for `.listen()` |
318
+
319
+ ---
320
+
321
+ ## Express Router & HTTP API ๐Ÿ–ฅ๏ธ
322
+
323
+ ### Endpoints
324
+
325
+ All routes are mounted relative to the router path (typically `/mbkbucket`):
326
+
327
+ | Endpoint | Method | Default Access | Description |
328
+ |---|---|---|---|
329
+ | `/mbkbucket` | `GET` | `view` auth | Admin file manager dashboard UI |
330
+ | `/mbkbucket/info` | `GET` | Public / Any | Health check, provider capabilities, and runtime config info |
331
+ | `/mbkbucket/view/*key` | `GET` | `view` auth | Inline viewer with byte-range streaming for video, audio, PDF, images, text |
332
+ | `/mbkbucket/player/*key` | `GET` | `view` auth | Dedicated media player page for video and audio playback |
333
+ | `/mbkbucket/p_view/*key` | `GET` | Public | Public file viewer (enabled when `publiView_enabled` is `"true"`) |
334
+ | `/mbkbucket/download/*key` | `GET` | `view` auth | Download file with `Content-Disposition: attachment` headers |
335
+ | `/mbkbucket/api/files` | `GET` | `view` auth | List files and folders with pagination, search, and continuation tokens |
336
+ | `/mbkbucket/api/file-info/*key`| `GET` | `view` auth | Get metadata (Content-Type, size, ETag, last modified) for a key |
337
+ | `/mbkbucket/api/health` | `GET` | `view` auth | Check connectivity for active storage connection |
338
+ | `/mbkbucket/api/incomplete-uploads` | `GET` | `view` auth | List incomplete multipart uploads |
339
+ | `/mbkbucket/api/cleanup-uploads` | `POST` | `delete` auth | Clean up incomplete multipart uploads older than specified days |
340
+ | `/mbkbucket/upload` | `POST` | `upload` auth | Upload a single file via `multipart/form-data` with conflict detection |
341
+ | `/mbkbucket/upload-init` | `POST` | `upload` auth | Initiate multipart upload session |
342
+ | `/mbkbucket/upload-chunk` | `POST` | `upload` auth | Upload individual multipart chunk (up to 50MB) |
343
+ | `/mbkbucket/upload-complete` | `POST` | `upload` auth | Assemble and complete multipart upload |
344
+ | `/mbkbucket/upload-abort` | `POST` | `delete` auth | Abort pending multipart upload session |
345
+ | `/mbkbucket/create-folder` | `POST` | `upload` auth | Create a new directory folder marker |
346
+ | `/mbkbucket/delete` | `POST` | `delete` auth | Delete a single file, batch array of keys, or folder |
347
+ | `/mbkbucket/move` | `POST` | `upload` auth | Move or rename a file (`sourceKey` to `destKey`) |
348
+ | `/mbkbucket/copy` | `POST` | `upload` auth | Copy or duplicate a file (`sourceKey` to `destKey`) |
349
+
350
+ ### Host-Controlled Permissions
351
+
352
+ You can configure granular authorization per operation using `createBucketRouter`:
353
+
354
+ ```js
355
+ import { createBucketRouter } from 'mbkbucket';
356
+ import { sessPerm } from 'mbkauthe';
357
+ import { Permissions } from './permissions.js';
358
+
359
+ app.use(createBucketRouter({
360
+ authorization: {
361
+ view: sessPerm(Permissions.storage.view),
362
+ upload: sessPerm(Permissions.storage.upload),
363
+ delete: sessPerm(Permissions.storage.delete),
364
+ },
365
+ publiViewEnabled: false,
366
+ }));
367
+ ```
368
+
369
+ - When no custom authorization is passed, `sessRole('superadmin')` protects all private endpoints by default.
370
+ - Passing a single middleware function to `authorization` applies that guard across all three operations.
371
+
372
+ ### Templates & Views
373
+
374
+ The Express router renders Handlebars (`.hbs`) views located in `views/`:
375
+
376
+ | Template / Partial | Description |
377
+ |---|---|
378
+ | `bucketportal.hbs` | Dashboard shell layout |
379
+ | `bucketadmincontent.hbs` | File manager interface with tree sidebar and storage switcher |
380
+ | `bucketadmin_alerts.hbs` | UI alerts and toast partial |
381
+ | `bucketadmin_delete_modal.hbs` | Item and folder deletion confirmation modal |
382
+ | `bucketadmin_move_modal.hbs` | File move and rename modal with destination selection |
383
+ | `bucketadmin_newfolder_modal.hbs` | New folder creation modal |
384
+ | `bucketadmin_preview_modal.hbs` | Full-screen media preview modal (images, video, audio, code, PDF) |
385
+ | `bucketadmin_upload_modal.hbs` | Chunked file upload modal with progress bars |
386
+ | `bucketadmin_filelist_skeleton.hbs`| Loading state placeholder skeleton |
387
+ | `bucket.hbs` | Public file viewer page |
388
+ | `mbkbucket_info.hbs` | Diagnostics, active connections, and environment info page |
389
+
390
+ ---
391
+
392
+ ## Frontend Helper Client ๐ŸŽจ
393
+
394
+ mbkbucket bundles a lightweight, zero-dependency client library served directly from your app:
395
+
396
+ - Script: `/mbkbucket/mbkbucket-helper.js`
397
+ - Styles: `/mbkbucket/mbkbucket-helper.css`
398
+ - Live interactive demo: `/mbkbucket/helper-demo`
399
+
400
+ ### Including the Helper
401
+
402
+ ```html
403
+ <link rel="stylesheet" href="/mbkbucket/mbkbucket-helper.css">
404
+ <script src="/mbkbucket/mbkbucket-helper.js"></script>
405
+ ```
406
+
407
+ ### Features
408
+
409
+ #### 1. Gallery Browser Modal
410
+ Open a rich modal browser to allow users to select one or multiple files directly from storage:
411
+
412
+ ```js
413
+ MBKBucket.openGallery({
414
+ title: 'Choose Profile Banner',
415
+ filter: 'image', // 'image' | 'video' | 'audio' | 'pdf' | 'all'
416
+ multiple: false,
417
+ onSelect: (selectedFiles) => {
418
+ console.log('Selected:', selectedFiles[0].url, selectedFiles[0].key);
419
+ }
420
+ });
421
+ ```
422
+
423
+ #### 2. File Picker Inputs
424
+ Bind an existing `<input>` element with auto-attached browse buttons and live thumbnail previews:
425
+
426
+ ```html
427
+ <input type="text" data-mbk-picker data-filter="image" data-preview="#avatar-preview">
428
+ <img id="avatar-preview" src="/placeholder.png" alt="Preview">
429
+ ```
430
+
431
+ Or programmatically:
432
+ ```js
433
+ MBKBucket.createPicker(document.getElementById('my-input'), {
434
+ filter: 'image',
435
+ onSelect: (file) => console.log('Picked:', file)
436
+ });
437
+ ```
438
+
439
+ #### 3. Upload Widget
440
+ Add a drag-and-drop uploader with automatic chunking for large files:
441
+
442
+ ```html
443
+ <div data-mbk-uploader data-prefix="uploads/users" data-allowed="image/*,video/*"></div>
444
+ ```
445
+
446
+ #### 4. Client URL Builders
447
+ ```js
448
+ MBKBucket.url.view('photos/cover.jpg'); // -> /mbkbucket/view/photos/cover.jpg
449
+ MBKBucket.url.download('reports/data.xlsx'); // -> /mbkbucket/download/reports/data.xlsx
450
+ MBKBucket.url.publicView('public/logo.png'); // -> /mbkbucket/p_view/public/logo.png
451
+ MBKBucket.url.player('media/intro.mp4'); // -> /mbkbucket/player/media/intro.mp4
452
+ ```
453
+
454
+ ---
455
+
456
+ ## Environment Configuration โš™๏ธ
457
+
458
+ ### `BucketConnection` (required)
459
+
460
+ A JSON string defining one or more storage connections. You can configure **S3/R2** connections and **Google Drive** connections together:
461
+
462
+ ```env
463
+ BucketConnection={"R2_Bucket":{"BUCKET_NAME":"my-bucket","ACCESS_KEY_ID":"your-key","SECRET_ACCESS_KEY":"your-secret","ENDPOINT":"https://<account-id>.r2.cloudflarestorage.com"},"Google_Drive":{"type":"gdrive","client_id":"your-client-id.apps.googleusercontent.com","client_secret":"your-client-secret","refresh_token":"your-refresh-token","folder_id":"root"}}
464
+ ```
465
+
466
+ #### S3 / Cloudflare R2 Connection Fields
467
+
468
+ | Field | Required | Description |
469
+ |---|---|---|
470
+ | `BUCKET_NAME` | Yes | Target bucket name |
471
+ | `ACCESS_KEY_ID` | Yes | S3 Access Key ID |
472
+ | `SECRET_ACCESS_KEY` | Yes | S3 Secret Access Key |
473
+ | `ENDPOINT` | Yes | S3 API endpoint URL (e.g. `https://<id>.r2.cloudflarestorage.com` or `https://s3.us-east-1.amazonaws.com`) |
474
+ | `region` | No | Region (defaults to `'auto'` for Cloudflare R2 / custom endpoints) |
475
+
476
+ #### Google Drive Connection Fields
477
+
478
+ | Field | Required | Description |
479
+ |---|---|---|
480
+ | `type` | Yes | Provider tag: `"gdrive"` or `"google-drive"` |
481
+ | `client_id` | OAuth | Google OAuth 2.0 Client ID |
482
+ | `client_secret` | OAuth | Google OAuth 2.0 Client Secret |
483
+ | `refresh_token` | OAuth | Long-lived Google OAuth 2.0 refresh token |
484
+ | `folder_id` | No | Target root folder ID in Google Drive (defaults to `"root"`) |
485
+ | `client_email` | Service Account | Service Account client email (alternative to OAuth) |
486
+ | `private_key` | Service Account | Service Account PEM private key (alternative to OAuth) |
487
+
488
+ #### Google Drive via Service Account Example
489
+
490
+ ```env
491
+ BucketConnection={"Google_Drive_SA":{"type":"gdrive","client_email":"storage-sa@project.iam.gserviceaccount.com","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----","folder_id":"1A2B3C4D5E6F"}}
492
+ ```
493
+
494
+ ### `mbkautheVar` (required)
495
+
496
+ Standard mbkauthe configuration. Key fields relevant to storage:
497
+
498
+ | Field | Required | Description |
499
+ |---|---|---|
500
+ | `APP_NAME` | Yes | Namespace used for automatic key prefix isolation |
501
+ | `bucket` | No | Default active connection name (falls back to first key in `BucketConnection`) |
502
+ | `loginRedirectURL` | No | URL to redirect upon authentication |
503
+
504
+ ### `mbkbucketVar` (optional)
505
+
506
+ Optional behavioral toggles for viewers and streaming:
507
+
508
+ ```env
509
+ mbkbucketVar={"publiView_enabled":"true","p_view_inline":"true"}
510
+ ```
511
+
512
+ | Field | Default | Description |
513
+ |---|---|---|
514
+ | `publiView_enabled` | `false` | Enable or disable unauthenticated public file access under `/mbkbucket/p_view/*` |
515
+ | `p_view_inline` | `true` | Stream files inline in browser response (vs redirecting to external signed URLs) |
516
+
517
+ ---
518
+
519
+ ## Automated Tests ๐Ÿงช
520
+
521
+ mbkbucket uses [Vitest](https://vitest.dev/) for unit and integration testing:
522
+
523
+ ```bash
524
+ npm test
525
+ ```
526
+
527
+ ### Test Suites Covered
528
+
529
+ - **Storage Provider & Core Abstractions**: `StorageProvider` interface contracts, capability inspection, `PathStrategy` traversal security, `StorageError` hierarchy, Google Drive consent URL generation.
530
+ - **S3 & Google Drive Drivers**: Health checks, mock provider operations, connection switching, custom driver registration.
531
+ - **API Routes Logic**: File listing, single and chunked multipart uploads, conflict detection (409), folder creation, single/batch deletion, move and copy operations, health endpoint, metadata inspection.
532
+ - **View Controller & Streaming**: Inline viewer, video byte-range streaming (`206 Partial Content`), unsupported MIME rejection (`415`), public file viewing.
533
+ - **Configuration Validation**: S3 and Google Drive connection validation, malformed JSON recovery, boolean normalization, and error messaging.
534
+ - **Public API Exports**: Verification of all exported functions, classes, and proxy objects from package root.
535
+
536
+ ---
537
+
538
+ ## Contact & Support
539
+
540
+ - **Website**: [mbktech.org/Support](https://mbktech.org/Support/?Project=MIbnEKhalidWeb)
541
+ - **Email**: [support@mbktech.org](mailto:support@mbktech.org)
542
+ - **GitHub**: [MIbnEKhalid/mbkbucket](https://github.com/MIbnEKhalid/mbkbucket)
543
+
544
+ ---
545
+
546
+ ## About
547
+
548
+ Developed by [Muhammad Bin Khalid](https://github.com/MIbnEKhalid)
549
+ Part of [MBK Tech](https://mbktech.org/)
550
+
551
+ ---
552
+
553
+ ## License
554
+
555
+ Licensed under the MIT License. See [LICENSE](LICENSE) for details.
556
+
557
+ <!--
558
+ * MBKBucket
559
+ * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
560
+ * Licensed under the MIT License.
561
+ * Source: https://github.com/MIbnEKhalid/mbkbucket
378
562
  -->