mbkbucket 3.3.1 → 3.3.2
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/index.js +2 -12
- package/lib/cli.js +178 -512
- package/lib/public/bucketadmin.js +1 -1
- package/lib/src/app.js +37 -50
- package/lib/src/cli/auth-helper.js +24 -103
- package/lib/src/config/index.js +69 -94
- package/lib/src/config/validation.js +14 -30
- package/lib/src/controllers/api.controller.js +85 -176
- package/lib/src/controllers/dashboard.controller.js +12 -23
- package/lib/src/controllers/info.controller.js +17 -20
- package/lib/src/controllers/view.controller.js +23 -70
- package/lib/src/middleware/async-handler.js +1 -16
- package/lib/src/middleware/bucket-resolver.js +7 -13
- package/lib/src/middleware/error-handler.js +6 -14
- package/lib/src/middleware/rate-limiter.js +2 -10
- package/lib/src/middleware/security.js +1 -17
- package/lib/src/routes/api.routes.js +21 -47
- package/lib/src/routes/index.js +1 -9
- package/lib/src/routes/info.routes.js +2 -3
- package/lib/src/routes/view.routes.js +3 -8
- package/lib/src/services/s3.service.js +157 -349
- package/lib/src/utils/errors.js +28 -59
- package/lib/src/utils/helpers.js +124 -70
- package/package.json +6 -1
- /package/{lib/views → views}/bucket.handlebars +0 -0
- /package/{lib/views → views}/bucketadmin_alerts.handlebars +0 -0
- /package/{lib/views → views}/bucketadmin_delete_modal.handlebars +0 -0
- /package/{lib/views → views}/bucketadmin_filelist_skeleton.handlebars +0 -0
- /package/{lib/views → views}/bucketadmin_newfolder_modal.handlebars +0 -0
- /package/{lib/views → views}/bucketadmin_preview_modal.handlebars +0 -0
- /package/{lib/views → views}/bucketadmin_upload_modal.handlebars +0 -0
- /package/{lib/views → views}/bucketadmincontent.handlebars +0 -0
- /package/{lib/views → views}/bucketportal.handlebars +0 -0
- /package/{lib/views → views}/mbkbucket_info.handlebars +0 -0
package/lib/src/utils/errors.js
CHANGED
|
@@ -1,76 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Centralized error classification for API routes.
|
|
2
|
+
* Centralized error classification for API and View routes.
|
|
3
3
|
* Maps error messages to HTTP status codes and machine-readable error codes.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
const API_ERROR_RULES = [
|
|
7
|
+
{ match: 'bucketconnection environment variable is not set', status: 503, code: 'BUCKET_CONFIG_ERROR' },
|
|
8
|
+
{ match: ['no bucket selected', 'not found in bucketconnection'], status: 400, code: 'INVALID_BUCKET' },
|
|
9
|
+
{ match: 'already exists', status: 409, code: 'CONFLICT' },
|
|
10
|
+
{ match: 'not found', status: 404, code: 'NOT_FOUND' },
|
|
11
|
+
{ match: 'access denied', status: 403, code: 'ACCESS_DENIED' },
|
|
12
|
+
{ match: ['required', 'invalid', 'must be'], status: 400, code: 'VALIDATION_ERROR' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const VIEW_ERROR_RULES = [
|
|
16
|
+
{ match: ['no bucket selected', 'not found in bucketconnection'], status: 400, code: 'INVALID_BUCKET' },
|
|
17
|
+
{ match: 'access denied', status: 403, code: 'ACCESS_DENIED' },
|
|
18
|
+
{ match: 'not found', status: 404, code: 'NOT_FOUND' },
|
|
19
|
+
{ match: ['invalid', 'required'], status: 400, code: 'VALIDATION_ERROR' },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
function matchError(rules, error, fallbackMessage) {
|
|
13
23
|
const message = String(error?.message || fallbackMessage);
|
|
14
24
|
const lower = message.toLowerCase();
|
|
25
|
+
const found = rules.find(r => Array.isArray(r.match) ? r.match.some(m => lower.includes(m)) : lower.includes(r.match));
|
|
26
|
+
return { status: found?.status ?? 500, code: found?.code ?? 'INTERNAL_ERROR', message };
|
|
27
|
+
}
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
if (lower.includes('no bucket selected') || lower.includes('not found in bucketconnection')) {
|
|
20
|
-
return { status: 400, code: 'INVALID_BUCKET', message };
|
|
21
|
-
}
|
|
22
|
-
if (lower.includes('already exists')) {
|
|
23
|
-
return { status: 409, code: 'CONFLICT', message };
|
|
24
|
-
}
|
|
25
|
-
if (lower.includes('not found')) {
|
|
26
|
-
return { status: 404, code: 'NOT_FOUND', message };
|
|
27
|
-
}
|
|
28
|
-
if (lower.includes('access denied')) {
|
|
29
|
-
return { status: 403, code: 'ACCESS_DENIED', message };
|
|
30
|
-
}
|
|
31
|
-
if (lower.includes('required') || lower.includes('invalid') || lower.includes('must be')) {
|
|
32
|
-
return { status: 400, code: 'VALIDATION_ERROR', message };
|
|
33
|
-
}
|
|
34
|
-
return { status: 500, code: 'INTERNAL_ERROR', message };
|
|
29
|
+
export function classifyApiError(error, fallbackMessage = 'Request failed') {
|
|
30
|
+
return matchError(API_ERROR_RULES, error, fallbackMessage);
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
/**
|
|
38
|
-
* Classify a view-level error into { status, code, message }.
|
|
39
|
-
* @param {Error} error
|
|
40
|
-
* @param {string} fallbackMessage
|
|
41
|
-
* @returns {{ status: number, code: string, message: string }}
|
|
42
|
-
*/
|
|
43
33
|
export function classifyViewError(error, fallbackMessage = 'Request failed') {
|
|
44
|
-
|
|
45
|
-
const lower = message.toLowerCase();
|
|
46
|
-
|
|
47
|
-
if (lower.includes('no bucket selected') || lower.includes('not found in bucketconnection')) {
|
|
48
|
-
return { status: 400, code: 'INVALID_BUCKET', message };
|
|
49
|
-
}
|
|
50
|
-
if (lower.includes('access denied')) {
|
|
51
|
-
return { status: 403, code: 'ACCESS_DENIED', message };
|
|
52
|
-
}
|
|
53
|
-
if (lower.includes('not found')) {
|
|
54
|
-
return { status: 404, code: 'NOT_FOUND', message };
|
|
55
|
-
}
|
|
56
|
-
if (lower.includes('invalid') || lower.includes('required')) {
|
|
57
|
-
return { status: 400, code: 'VALIDATION_ERROR', message };
|
|
58
|
-
}
|
|
59
|
-
return { status: 500, code: 'INTERNAL_ERROR', message };
|
|
34
|
+
return matchError(VIEW_ERROR_RULES, error, fallbackMessage);
|
|
60
35
|
}
|
|
61
36
|
|
|
62
|
-
/**
|
|
63
|
-
* Send a classified API error as JSON.
|
|
64
|
-
*/
|
|
65
37
|
export function sendApiError(res, error, fallbackMessage = 'Request failed') {
|
|
66
|
-
const
|
|
67
|
-
return res.status(
|
|
38
|
+
const { status, code, message } = classifyApiError(error, fallbackMessage);
|
|
39
|
+
return res.status(status).json({ success: false, code, error: message });
|
|
68
40
|
}
|
|
69
41
|
|
|
70
|
-
/**
|
|
71
|
-
* Send a classified view error as JSON.
|
|
72
|
-
*/
|
|
73
42
|
export function sendViewError(res, error, fallbackMessage = 'Request failed') {
|
|
74
|
-
const
|
|
75
|
-
return res.status(
|
|
43
|
+
const { status, code, message } = classifyViewError(error, fallbackMessage);
|
|
44
|
+
return res.status(status).json({ code, message });
|
|
76
45
|
}
|
package/lib/src/utils/helpers.js
CHANGED
|
@@ -1,30 +1,98 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared helper utilities used across controllers and
|
|
2
|
+
* Shared helper utilities used across controllers, services, and CLI.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
5
|
+
const HTML_ESCAPE_MAP = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
|
6
|
+
|
|
8
7
|
export function escapeHtml(value = '') {
|
|
9
|
-
return String(value)
|
|
10
|
-
.replace(/&/g, '&')
|
|
11
|
-
.replace(/</g, '<')
|
|
12
|
-
.replace(/>/g, '>')
|
|
13
|
-
.replace(/"/g, '"')
|
|
14
|
-
.replace(/'/g, ''');
|
|
8
|
+
return String(value ?? '').replace(/[&<>"']/g, c => HTML_ESCAPE_MAP[c]);
|
|
15
9
|
}
|
|
16
10
|
|
|
17
|
-
/**
|
|
18
|
-
* Extract the base filename from a path (everything after the last '/').
|
|
19
|
-
*/
|
|
20
11
|
export function getBaseName(path = '') {
|
|
21
|
-
const value = String(path
|
|
12
|
+
const value = String(path ?? '');
|
|
22
13
|
const lastSlashIndex = value.lastIndexOf('/');
|
|
23
|
-
return lastSlashIndex === -1 ? value : value.
|
|
14
|
+
return lastSlashIndex === -1 ? value : value.slice(lastSlashIndex + 1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getFileExt(fileName = '') {
|
|
18
|
+
const name = getBaseName(fileName);
|
|
19
|
+
const dotIndex = name.lastIndexOf('.');
|
|
20
|
+
return dotIndex === -1 ? '' : name.slice(dotIndex + 1).toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function trimSlashes(str = '') {
|
|
24
|
+
return String(str ?? '').replace(/^\/+/, '').replace(/\/+$/, '');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function trimLeadingSlashes(str = '') {
|
|
28
|
+
return String(str ?? '').replace(/^\/+/, '');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getFolderPath(key = '') {
|
|
32
|
+
const s = String(key ?? '');
|
|
33
|
+
const lastSlash = s.lastIndexOf('/');
|
|
34
|
+
return lastSlash === -1 ? '' : s.slice(0, lastSlash + 1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function normalizeKeyParam(rawKey) {
|
|
38
|
+
return Array.isArray(rawKey) ? rawKey.join('/') : String(rawKey || '');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function buildKey(prefix, fileName) {
|
|
42
|
+
const cleanPrefix = trimSlashes(prefix);
|
|
43
|
+
const cleanName = trimLeadingSlashes(fileName);
|
|
44
|
+
return cleanPrefix ? `${cleanPrefix}/${cleanName}` : cleanName;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function nowIso() {
|
|
48
|
+
return new Date().toISOString();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function formatBytes(bytes) {
|
|
52
|
+
if (!bytes || bytes === 0) return '0 B';
|
|
53
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
54
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
55
|
+
return `${(bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function formatDate(iso) {
|
|
59
|
+
try {
|
|
60
|
+
return new Date(iso).toLocaleString();
|
|
61
|
+
} catch {
|
|
62
|
+
return iso || '—';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function streamToBuffer(stream) {
|
|
67
|
+
if (Buffer.isBuffer(stream)) return stream;
|
|
68
|
+
const chunks = [];
|
|
69
|
+
if (stream?.[Symbol.asyncIterator] || stream?.transformToWeb) {
|
|
70
|
+
for await (const chunk of stream) {
|
|
71
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
72
|
+
}
|
|
73
|
+
return Buffer.concat(chunks);
|
|
74
|
+
}
|
|
75
|
+
if (stream?.getReader) {
|
|
76
|
+
const reader = stream.getReader();
|
|
77
|
+
while (true) {
|
|
78
|
+
const { done, value } = await reader.read();
|
|
79
|
+
if (done) break;
|
|
80
|
+
chunks.push(Buffer.isBuffer(value) ? value : Buffer.from(value));
|
|
81
|
+
}
|
|
82
|
+
return Buffer.concat(chunks);
|
|
83
|
+
}
|
|
84
|
+
return Buffer.concat(chunks);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function destroyStream(stream) {
|
|
88
|
+
if (stream && typeof stream.destroy === 'function') {
|
|
89
|
+
stream.removeAllListeners?.();
|
|
90
|
+
stream.destroy();
|
|
91
|
+
}
|
|
24
92
|
}
|
|
25
93
|
|
|
26
94
|
// ---------------------------------------------------------------------------
|
|
27
|
-
// MIME type & file extension constants
|
|
95
|
+
// MIME type & file extension constants
|
|
28
96
|
// ---------------------------------------------------------------------------
|
|
29
97
|
|
|
30
98
|
export const VIEWABLE_TYPES = new Set([
|
|
@@ -37,91 +105,79 @@ export const VIEWABLE_TYPES = new Set([
|
|
|
37
105
|
'toml', 'ini', 'conf', 'pdf'
|
|
38
106
|
]);
|
|
39
107
|
|
|
40
|
-
export const VIEWABLE_TYPES_SORTED =
|
|
41
|
-
|
|
108
|
+
export const VIEWABLE_TYPES_SORTED = [...VIEWABLE_TYPES].sort();
|
|
42
109
|
export const STATIC_ASSET_TYPES = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'css', 'js']);
|
|
43
110
|
export const VIDEO_TYPES = new Set(['mp4', 'webm', 'ogg', 'avi', 'mov']);
|
|
44
111
|
export const AUDIO_TYPES = new Set(['mp3', 'wav', 'flac', 'aac', 'm4a']);
|
|
45
112
|
|
|
46
113
|
export const MIME_TYPES = {
|
|
47
114
|
// Images
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
115
|
+
jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png',
|
|
116
|
+
gif: 'image/gif', webp: 'image/webp', svg: 'image/svg+xml',
|
|
117
|
+
bmp: 'image/bmp', ico: 'image/x-icon',
|
|
51
118
|
// Videos
|
|
52
|
-
|
|
53
|
-
|
|
119
|
+
mp4: 'video/mp4', webm: 'video/webm', ogg: 'video/ogg',
|
|
120
|
+
avi: 'video/x-msvideo', mov: 'video/quicktime',
|
|
54
121
|
// Audio
|
|
55
|
-
|
|
56
|
-
|
|
122
|
+
mp3: 'audio/mpeg', wav: 'audio/wav', flac: 'audio/flac',
|
|
123
|
+
aac: 'audio/aac', m4a: 'audio/mp4',
|
|
57
124
|
// Text / Code
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Documents
|
|
74
|
-
'
|
|
125
|
+
txt: 'text/plain; charset=utf-8', md: 'text/markdown; charset=utf-8',
|
|
126
|
+
json: 'application/json; charset=utf-8', xml: 'application/xml; charset=utf-8',
|
|
127
|
+
csv: 'text/csv; charset=utf-8', log: 'text/plain; charset=utf-8',
|
|
128
|
+
js: 'text/javascript; charset=utf-8', ts: 'text/typescript; charset=utf-8',
|
|
129
|
+
html: 'text/html; charset=utf-8', htm: 'text/html; charset=utf-8',
|
|
130
|
+
css: 'text/css; charset=utf-8', php: 'text/x-php; charset=utf-8',
|
|
131
|
+
py: 'text/x-python; charset=utf-8', java: 'text/x-java-source; charset=utf-8',
|
|
132
|
+
cpp: 'text/x-c++src; charset=utf-8', c: 'text/x-csrc; charset=utf-8',
|
|
133
|
+
h: 'text/x-chdr; charset=utf-8', cs: 'text/x-csharp; charset=utf-8',
|
|
134
|
+
rb: 'text/x-ruby; charset=utf-8', go: 'text/x-go; charset=utf-8',
|
|
135
|
+
rs: 'text/x-rust; charset=utf-8', sql: 'text/x-sql; charset=utf-8',
|
|
136
|
+
sh: 'text/x-shellscript; charset=utf-8', bat: 'text/x-msdos-batch; charset=utf-8',
|
|
137
|
+
ps1: 'text/x-powershell; charset=utf-8', yaml: 'text/x-yaml; charset=utf-8',
|
|
138
|
+
yml: 'text/x-yaml; charset=utf-8', toml: 'text/x-toml; charset=utf-8',
|
|
139
|
+
ini: 'text/x-ini; charset=utf-8', conf: 'text/x-conf; charset=utf-8',
|
|
140
|
+
// Archives & Documents
|
|
141
|
+
zip: 'application/zip', gz: 'application/gzip', tar: 'application/x-tar',
|
|
142
|
+
pdf: 'application/pdf'
|
|
75
143
|
};
|
|
76
144
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
145
|
+
export function getMimeType(fileName, fallback = 'application/octet-stream') {
|
|
146
|
+
return MIME_TYPES[getFileExt(fileName)] || fallback;
|
|
147
|
+
}
|
|
148
|
+
|
|
80
149
|
export function isSensitiveTextType(contentType = '') {
|
|
81
|
-
const type = String(contentType
|
|
82
|
-
return type.startsWith('text/')
|
|
83
|
-
|| type.includes('json')
|
|
84
|
-
|| type.includes('xml')
|
|
85
|
-
|| type.includes('javascript')
|
|
86
|
-
|| type.includes('typescript');
|
|
150
|
+
const type = String(contentType ?? '').toLowerCase();
|
|
151
|
+
return type.startsWith('text/') || /json|xml|javascript|typescript/.test(type);
|
|
87
152
|
}
|
|
88
153
|
|
|
89
|
-
/**
|
|
90
|
-
* Build a Cache-Control header value based on content type and flags.
|
|
91
|
-
*/
|
|
92
154
|
export function buildCacheControl(contentType, { isStaticAsset = false, supportsRanges = false, publicCache = false } = {}) {
|
|
93
155
|
if (publicCache) {
|
|
94
156
|
if (isStaticAsset) return 'public, max-age=3600, immutable';
|
|
95
157
|
if (supportsRanges) return 'public, max-age=86400, must-revalidate';
|
|
96
158
|
return 'public, max-age=300, must-revalidate';
|
|
97
159
|
}
|
|
98
|
-
|
|
99
160
|
if (isSensitiveTextType(contentType)) return 'private, no-store';
|
|
100
|
-
|
|
101
|
-
return 'private, max-age=300, must-revalidate';
|
|
161
|
+
return supportsRanges ? 'private, max-age=1800, must-revalidate' : 'private, max-age=300, must-revalidate';
|
|
102
162
|
}
|
|
103
163
|
|
|
104
164
|
// ---------------------------------------------------------------------------
|
|
105
|
-
// Range header parsing
|
|
165
|
+
// Range header parsing
|
|
106
166
|
// ---------------------------------------------------------------------------
|
|
107
167
|
|
|
108
168
|
const DEFAULT_CHUNK_SIZE = 8 * 1024 * 1024; // 8 MB
|
|
109
169
|
|
|
110
|
-
/**
|
|
111
|
-
* Parse an HTTP Range header (`bytes=start-end`) and compute validated byte offsets.
|
|
112
|
-
* Returns `null` if the range is invalid or unsatisfiable (caller should send 416).
|
|
113
|
-
*/
|
|
114
170
|
export function parseRangeHeader(rangeHeader, totalSize) {
|
|
115
171
|
if (!totalSize || totalSize <= 0) return null;
|
|
116
172
|
|
|
117
|
-
const match = /^bytes=(\d*)-(\d*)$/.exec(String(rangeHeader
|
|
173
|
+
const match = /^bytes=(\d*)-(\d*)$/.exec(String(rangeHeader ?? '').trim());
|
|
118
174
|
if (!match) return null;
|
|
119
175
|
|
|
120
176
|
const [, startStr, endStr] = match;
|
|
121
|
-
let start
|
|
177
|
+
let start;
|
|
178
|
+
let end;
|
|
122
179
|
|
|
123
180
|
if (startStr === '') {
|
|
124
|
-
// suffix range: bytes=-N (last N bytes)
|
|
125
181
|
const suffix = parseInt(endStr, 10);
|
|
126
182
|
if (isNaN(suffix) || suffix <= 0) return null;
|
|
127
183
|
start = Math.max(0, totalSize - suffix);
|
|
@@ -138,12 +194,11 @@ export function parseRangeHeader(rangeHeader, totalSize) {
|
|
|
138
194
|
}
|
|
139
195
|
|
|
140
196
|
if (start >= totalSize || start > end || end >= totalSize) return null;
|
|
141
|
-
|
|
142
197
|
return { start, end, total: totalSize, size: end - start + 1 };
|
|
143
198
|
}
|
|
144
199
|
|
|
145
200
|
// ---------------------------------------------------------------------------
|
|
146
|
-
// Player page HTML template
|
|
201
|
+
// Player page HTML template
|
|
147
202
|
// ---------------------------------------------------------------------------
|
|
148
203
|
|
|
149
204
|
export function renderPlayerPage(safeFileName, encodedKey, bucketQuery) {
|
|
@@ -186,12 +241,11 @@ export const commonHandlebarsHelpers = {
|
|
|
186
241
|
gte: (a, b) => a >= b,
|
|
187
242
|
lt: (a, b) => a < b,
|
|
188
243
|
lte: (a, b) => a <= b,
|
|
189
|
-
includes: (arr, val) =>
|
|
244
|
+
includes: (arr, val) => Array.isArray(arr) && arr.includes(val),
|
|
190
245
|
json: (obj) => JSON.stringify(obj, null, 2),
|
|
191
246
|
jsonStringify: (obj) => JSON.stringify(obj),
|
|
192
247
|
getInitials: (username) => {
|
|
193
248
|
if (!username) return '?';
|
|
194
|
-
|
|
195
|
-
return parts.map(p => p.charAt(0)).join('').toUpperCase().substring(0, 2);
|
|
249
|
+
return username.split(/[._]/).map(p => p.charAt(0)).join('').toUpperCase().slice(0, 2);
|
|
196
250
|
},
|
|
197
251
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mbkbucket",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"description": "Flexible S3/R2 bucket management — library, Express router, and CLI for mbktech.org applications. Provides upload, download, listing, multipart utilities, signed URLs, and a command-line interface with built-in configuration validation and health checks",
|
|
5
5
|
"author": "Muhammad Bin Khalid",
|
|
6
6
|
"homepage": "https://github.com/MIbnEKhalid/mbkbucket#readme",
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
"dev": "cross-env NODE_ENV=dev nodemon index.js",
|
|
16
16
|
"test": "node --test"
|
|
17
17
|
},
|
|
18
|
+
"imports": {
|
|
19
|
+
"#helpers": "./lib/src/utils/helpers.js",
|
|
20
|
+
"#logger": "./lib/src/utils/logger.js"
|
|
21
|
+
},
|
|
18
22
|
"dependencies": {
|
|
19
23
|
"@aws-sdk/client-s3": "^3.968.0",
|
|
20
24
|
"@aws-sdk/s3-request-presigner": "^3.1000.0",
|
|
@@ -46,6 +50,7 @@
|
|
|
46
50
|
},
|
|
47
51
|
"files": [
|
|
48
52
|
"lib",
|
|
53
|
+
"views",
|
|
49
54
|
".env.example",
|
|
50
55
|
".mbkbucket.config.example.json",
|
|
51
56
|
"cli.js",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|