mbkbucket 3.7.1 → 3.8.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/README.md +8 -8
- package/lib/public/bucketadmin.css +13 -0
- package/lib/public/bucketadmin.js +60 -3
- package/lib/public/mbkbucket-helper-demo.html +2 -2
- package/lib/src/app.js +3 -3
- package/lib/src/controllers/dashboard.controller.js +2 -2
- package/lib/src/controllers/info.controller.js +1 -1
- package/lib/src/middleware/error-handler.js +5 -3
- package/lib/src/middleware/rate-limiter.js +4 -2
- package/lib/src/utils/helpers.js +1 -0
- package/package.json +3 -2
- package/views/{bucket.handlebars → bucket.hbs} +1 -1
- package/views/{bucketadmincontent.handlebars → bucketadmincontent.hbs} +3 -0
- package/views/{mbkbucket_info.handlebars → mbkbucket_info.hbs} +1 -1
- /package/views/{bucketadmin_alerts.handlebars → bucketadmin_alerts.hbs} +0 -0
- /package/views/{bucketadmin_delete_modal.handlebars → bucketadmin_delete_modal.hbs} +0 -0
- /package/views/{bucketadmin_filelist_skeleton.handlebars → bucketadmin_filelist_skeleton.hbs} +0 -0
- /package/views/{bucketadmin_newfolder_modal.handlebars → bucketadmin_newfolder_modal.hbs} +0 -0
- /package/views/{bucketadmin_preview_modal.handlebars → bucketadmin_preview_modal.hbs} +0 -0
- /package/views/{bucketadmin_upload_modal.handlebars → bucketadmin_upload_modal.hbs} +0 -0
- /package/views/{bucketportal.handlebars → bucketportal.hbs} +0 -0
package/README.md
CHANGED
|
@@ -267,14 +267,14 @@ All functions are exported from the package root. Full TypeScript declarations i
|
|
|
267
267
|
|
|
268
268
|
| Template | Description |
|
|
269
269
|
|---|---|
|
|
270
|
-
| `bucketportal.
|
|
271
|
-
| `bucketadmincontent.
|
|
272
|
-
| `bucketadmin_alerts.
|
|
273
|
-
| `bucketadmin_delete_modal.
|
|
274
|
-
| `bucketadmin_preview_modal.
|
|
275
|
-
| `bucketadmin_filelist_skeleton.
|
|
276
|
-
| `bucket.
|
|
277
|
-
| `mbkbucket_info.
|
|
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
278
|
|
|
279
279
|
---
|
|
280
280
|
|
|
@@ -675,10 +675,23 @@
|
|
|
675
675
|
position: relative;
|
|
676
676
|
}
|
|
677
677
|
|
|
678
|
+
.view-btn#view-thumbs {
|
|
679
|
+
border-left: 1px solid var(--border-color);
|
|
680
|
+
}
|
|
681
|
+
|
|
678
682
|
.fm-item-thumb {
|
|
679
683
|
width: 100%;
|
|
680
684
|
height: 100%;
|
|
681
685
|
object-fit: cover;
|
|
686
|
+
transition: opacity 0.25s ease-in-out;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.fm-item-thumb.fm-lazy-thumb {
|
|
690
|
+
opacity: 0;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.fm-item-thumb.fm-lazy-thumb.is-loaded {
|
|
694
|
+
opacity: 1;
|
|
682
695
|
}
|
|
683
696
|
|
|
684
697
|
.fm-item-icon .ext {
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
let currentOrder = 'asc';
|
|
21
21
|
let currentViewMode = localStorage.getItem('mbk_view_mode') || 'grid'; // 'grid' or 'list'
|
|
22
22
|
let currentQuickFilter = 'all'; // 'all', 'media', 'docs', 'code', 'starred'
|
|
23
|
+
let showThumbnails = localStorage.getItem('mbk_show_thumbnails') === 'true'; // Fast Mode by default (saves Vercel resources & tokens)
|
|
24
|
+
let thumbObserver = null;
|
|
23
25
|
let pageFiles = [];
|
|
24
26
|
let pageFolders = [];
|
|
25
27
|
let currentApiPrefix = '';
|
|
@@ -502,15 +504,16 @@
|
|
|
502
504
|
const formattedSize = file.Size ? formatFileSize(file.Size) : '—';
|
|
503
505
|
const formattedDate = file.LastModified ? formatDate(file.LastModified) : '—';
|
|
504
506
|
|
|
507
|
+
const showImgThumb = isImg && showThumbnails;
|
|
505
508
|
let iconInner = `<span class="ext">${typeCfg.label}</span>`;
|
|
506
|
-
if (
|
|
507
|
-
iconInner = `<img src="${getViewUrl(rawKey)}" class="fm-item-thumb" alt="${safeName}"
|
|
509
|
+
if (showImgThumb) {
|
|
510
|
+
iconInner = `<img data-src="${getViewUrl(rawKey)}" class="fm-item-thumb fm-lazy-thumb" alt="${safeName}" onload="this.classList.add('is-loaded')" onerror="this.outerHTML='<span class=\\'ext\\'>IMG</span>'">`;
|
|
508
511
|
}
|
|
509
512
|
|
|
510
513
|
if (currentViewMode === 'grid') {
|
|
511
514
|
html += `
|
|
512
515
|
<div class="fm-item ${isStarred ? 'starred' : ''}" data-type="file" data-key="${safeKey}" tabindex="0">
|
|
513
|
-
<div class="fm-item-icon" style="${
|
|
516
|
+
<div class="fm-item-icon" style="${showImgThumb ? '' : `background:${typeCfg.bg};color:${typeCfg.color}`}">
|
|
514
517
|
${iconInner}
|
|
515
518
|
</div>
|
|
516
519
|
<div class="fm-item-details">
|
|
@@ -549,6 +552,44 @@
|
|
|
549
552
|
});
|
|
550
553
|
|
|
551
554
|
gridEl.innerHTML = html;
|
|
555
|
+
observeThumbnails();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function observeThumbnails() {
|
|
559
|
+
const lazyImages = document.querySelectorAll('img.fm-lazy-thumb[data-src]');
|
|
560
|
+
if (!lazyImages.length) return;
|
|
561
|
+
|
|
562
|
+
if (!('IntersectionObserver' in window)) {
|
|
563
|
+
lazyImages.forEach(img => {
|
|
564
|
+
if (img.dataset.src) {
|
|
565
|
+
img.src = img.dataset.src;
|
|
566
|
+
img.removeAttribute('data-src');
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (thumbObserver) {
|
|
573
|
+
thumbObserver.disconnect();
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
thumbObserver = new IntersectionObserver((entries, observer) => {
|
|
577
|
+
entries.forEach(entry => {
|
|
578
|
+
if (entry.isIntersecting) {
|
|
579
|
+
const img = entry.target;
|
|
580
|
+
if (img.dataset.src) {
|
|
581
|
+
img.src = img.dataset.src;
|
|
582
|
+
img.removeAttribute('data-src');
|
|
583
|
+
}
|
|
584
|
+
observer.unobserve(img);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
}, {
|
|
588
|
+
rootMargin: '100px 0px',
|
|
589
|
+
threshold: 0.01
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
lazyImages.forEach(img => thumbObserver.observe(img));
|
|
552
593
|
}
|
|
553
594
|
|
|
554
595
|
// Load Files API Call
|
|
@@ -996,6 +1037,19 @@
|
|
|
996
1037
|
// View mode toggle
|
|
997
1038
|
const gridBtn = document.getElementById('view-grid');
|
|
998
1039
|
const listBtn = document.getElementById('view-list');
|
|
1040
|
+
const thumbsBtn = document.getElementById('view-thumbs');
|
|
1041
|
+
|
|
1042
|
+
if (showThumbnails) {
|
|
1043
|
+
thumbsBtn?.classList.add('active');
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
thumbsBtn?.addEventListener('click', () => {
|
|
1047
|
+
showThumbnails = !showThumbnails;
|
|
1048
|
+
localStorage.setItem('mbk_show_thumbnails', String(showThumbnails));
|
|
1049
|
+
thumbsBtn.classList.toggle('active', showThumbnails);
|
|
1050
|
+
renderFiles();
|
|
1051
|
+
showToast(showThumbnails ? 'Image previews enabled' : 'Fast Mode: Previews disabled (saving tokens & bandwidth)', 'info');
|
|
1052
|
+
});
|
|
999
1053
|
|
|
1000
1054
|
gridBtn?.addEventListener('click', () => {
|
|
1001
1055
|
currentViewMode = 'grid';
|
|
@@ -1207,6 +1261,9 @@
|
|
|
1207
1261
|
document.getElementById('view-list')?.classList.add('active');
|
|
1208
1262
|
document.getElementById('view-grid')?.classList.remove('active');
|
|
1209
1263
|
}
|
|
1264
|
+
if (showThumbnails) {
|
|
1265
|
+
document.getElementById('view-thumbs')?.classList.add('active');
|
|
1266
|
+
}
|
|
1210
1267
|
|
|
1211
1268
|
initEventListeners();
|
|
1212
1269
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<title>Helper Utils Demo — MBKBucket</title>
|
|
14
14
|
<meta name="description" content="Interactive demo of MBKBucket frontend helper utilities: gallery picker, file input, upload widget, multi-select, live preview binding, and API helpers.">
|
|
15
15
|
|
|
16
|
-
<!-- Fonts (same as bucket.
|
|
16
|
+
<!-- Fonts (same as bucket.hbs) -->
|
|
17
17
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
18
18
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
19
19
|
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<link rel="stylesheet" href="/mbkbucket/mbkbucket-helper.css">
|
|
24
24
|
|
|
25
25
|
<style>
|
|
26
|
-
/* ── Page shell (mirrors bucket.
|
|
26
|
+
/* ── Page shell (mirrors bucket.hbs body) ───────────── */
|
|
27
27
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
28
28
|
|
|
29
29
|
body {
|
package/lib/src/app.js
CHANGED
|
@@ -72,8 +72,8 @@ export function createApp() {
|
|
|
72
72
|
].filter(dir => fs.existsSync(dir));
|
|
73
73
|
|
|
74
74
|
// Handlebars template engine
|
|
75
|
-
server.engine("
|
|
76
|
-
extname: ".
|
|
75
|
+
server.engine("hbs", engine({
|
|
76
|
+
extname: ".hbs",
|
|
77
77
|
defaultLayout: false,
|
|
78
78
|
partialsDir: [...new Set(partialsDir)],
|
|
79
79
|
cache: process.env.NODE_ENV === "production",
|
|
@@ -83,7 +83,7 @@ export function createApp() {
|
|
|
83
83
|
}
|
|
84
84
|
}));
|
|
85
85
|
|
|
86
|
-
server.set("view engine", "
|
|
86
|
+
server.set("view engine", "hbs");
|
|
87
87
|
server.set("views", [...new Set(viewDirs)]);
|
|
88
88
|
|
|
89
89
|
server.use(mbkautheRoutes);
|
|
@@ -11,10 +11,10 @@ export function renderBucketDashboard(req, res) {
|
|
|
11
11
|
const allBuckets = getAvailableBucketNames();
|
|
12
12
|
const configuredDefaultBucket = mbkautheVar?.bucket;
|
|
13
13
|
const isPortal = mbkautheVar?.APP_NAME === "portal";
|
|
14
|
-
const bucketfilePath = isPortal ? "bucketportal.
|
|
14
|
+
const bucketfilePath = isPortal ? "bucketportal.hbs" : "bucket.hbs";
|
|
15
15
|
|
|
16
16
|
const mainLayoutPath = path.resolve(process.cwd(), "views", "layouts", "main");
|
|
17
|
-
const hasMainLayout = fs.existsSync(`${mainLayoutPath}.
|
|
17
|
+
const hasMainLayout = fs.existsSync(`${mainLayoutPath}.hbs`) || fs.existsSync(mainLayoutPath);
|
|
18
18
|
const portalLayout = (isPortal && hasMainLayout) && mainLayoutPath;
|
|
19
19
|
|
|
20
20
|
let selectedBucket;
|
|
@@ -19,7 +19,7 @@ async function getInfoData() {
|
|
|
19
19
|
* Render the mbkbucket info page (Handlebars).
|
|
20
20
|
*/
|
|
21
21
|
export async function infoPage(req, res) {
|
|
22
|
-
renderPage(req, res, "mbkbucket_info.
|
|
22
|
+
renderPage(req, res, "mbkbucket_info.hbs", false, {
|
|
23
23
|
page: "MBK Bucket Info",
|
|
24
24
|
...(await getInfoData())
|
|
25
25
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createLogger } from "#logger";
|
|
2
|
-
import { isJsonRequest, sendError, sanitizeErrorDetails } from "mbkauthe";
|
|
2
|
+
import { isJsonRequest, sendError, sanitizeErrorDetails, renderPage } from "mbkauthe";
|
|
3
3
|
|
|
4
4
|
const debugServer = createLogger('server');
|
|
5
5
|
const BASE_ERROR_DATA = { layout: false, pagename: "Home", page: "/" };
|
|
@@ -18,7 +18,8 @@ export function notFoundHandler(req, res, next) {
|
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
res.status(404);
|
|
22
|
+
return renderPage(req, res, "Error/dError.hbs", false, {
|
|
22
23
|
...BASE_ERROR_DATA,
|
|
23
24
|
code: 404,
|
|
24
25
|
error: "Not Found",
|
|
@@ -40,7 +41,8 @@ export function globalErrorHandler(err, req, res, _next) {
|
|
|
40
41
|
|
|
41
42
|
const sanitizedDetails = err.message ? sanitizeErrorDetails(err.message) : undefined;
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
res.status(statusCode);
|
|
45
|
+
return renderPage(req, res, "Error/dError.hbs", false, {
|
|
44
46
|
...BASE_ERROR_DATA,
|
|
45
47
|
code: statusCode,
|
|
46
48
|
error: statusCode >= 500 ? "Internal app Error" : (err.name || "Client Error"),
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import rateLimit from "express-rate-limit";
|
|
2
|
+
import { renderPage } from "mbkauthe";
|
|
2
3
|
|
|
3
4
|
export const generalLimiter = rateLimit({
|
|
4
5
|
windowMs: 2 * 60 * 1000,
|
|
5
6
|
max: 150,
|
|
6
7
|
standardHeaders: true,
|
|
7
8
|
legacyHeaders: false,
|
|
8
|
-
handler: (
|
|
9
|
-
res.status(429)
|
|
9
|
+
handler: (req, res) => {
|
|
10
|
+
res.status(429);
|
|
11
|
+
return renderPage(req, res, 'error.hbs', false, { message: 'Too many requests from your IP. Try again later.', code: 429 });
|
|
10
12
|
}
|
|
11
13
|
});
|
|
12
14
|
|
package/lib/src/utils/helpers.js
CHANGED
|
@@ -128,6 +128,7 @@ export function buildCacheControl(contentType, { isStaticAsset = false, supports
|
|
|
128
128
|
return supportsRanges ? 'public, max-age=86400, must-revalidate' : 'public, max-age=300, must-revalidate';
|
|
129
129
|
}
|
|
130
130
|
if (isSensitiveTextType(contentType)) return 'private, no-store';
|
|
131
|
+
if (isStaticAsset) return 'private, max-age=86400, stale-while-revalidate=604800';
|
|
131
132
|
return supportsRanges ? 'private, max-age=1800, must-revalidate' : 'private, max-age=300, must-revalidate';
|
|
132
133
|
}
|
|
133
134
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mbkbucket",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
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",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
15
|
"dev": "cross-env NODE_ENV=dev nodemon index.js",
|
|
16
|
+
"publish": "node scripts/publish.js",
|
|
16
17
|
"test": "vitest run"
|
|
17
18
|
},
|
|
18
19
|
"imports": {
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
"express": "^5.1.0",
|
|
29
30
|
"express-handlebars": "^9.0.1",
|
|
30
31
|
"express-rate-limit": "^7.5.1",
|
|
31
|
-
"mbkauthe": "
|
|
32
|
+
"mbkauthe": "6.2.0",
|
|
32
33
|
"multer": "^2.0.2",
|
|
33
34
|
"pg": "^8.16.3"
|
|
34
35
|
},
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
17
17
|
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
18
18
|
<script src="/mbkauthe/main.js" defer></script>
|
|
19
|
-
<script src="https://portal.mbktech.org/
|
|
19
|
+
<script src="https://portal.mbktech.org/qssets/js/auto-ids.js" defer></script>
|
|
20
20
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
|
21
21
|
<link rel="icon" type="image/x-icon" href="/icon.svg">
|
|
22
22
|
<style>
|
|
@@ -89,6 +89,9 @@
|
|
|
89
89
|
<button type="button" class="view-btn" id="view-list" aria-label="List view" title="List view">
|
|
90
90
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><line x1="2" y1="4" x2="14" y2="4"/><line x1="2" y1="8" x2="14" y2="8"/><line x1="2" y1="12" x2="14" y2="12"/></svg>
|
|
91
91
|
</button>
|
|
92
|
+
<button type="button" class="view-btn" id="view-thumbs" aria-label="Toggle image previews" title="Toggle image previews (Fast Mode saves bandwidth & tokens)">
|
|
93
|
+
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="2" width="12" height="12" rx="2"/><circle cx="5.5" cy="5.5" r="1.5"/><path d="M14 10l-4-4-6 6"/></svg>
|
|
94
|
+
</button>
|
|
92
95
|
</div>
|
|
93
96
|
</div>
|
|
94
97
|
</div>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
content="Log in to portal.mbktech.org to access your resources and manage projects securely.">
|
|
16
16
|
<meta name="keywords" content="MBK Tech, Web-Portal, Web, Portal, Admin-Panel, Admin, login">
|
|
17
17
|
<meta property="og:title" content="Version and Configuration Information | MBK Tech" />
|
|
18
|
-
<meta property="og:image" content="https://mbktech.org/
|
|
18
|
+
<meta property="og:image" content="https://mbktech.org/assets/images/logo.png" />
|
|
19
19
|
<meta property="og:url" content="/mbkbucket/info">
|
|
20
20
|
<title>Version and Configuration Information | MBK Tech Portal</title>
|
|
21
21
|
<link rel="icon" type="image/svg+xml" href="/icon.svg">
|
|
File without changes
|
|
File without changes
|
/package/views/{bucketadmin_filelist_skeleton.handlebars → bucketadmin_filelist_skeleton.hbs}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|