mbkbucket 3.5.0 → 3.6.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 (42) hide show
  1. package/.env.example +14 -14
  2. package/.mbkbucket.config.example.json +14 -14
  3. package/LICENSE +20 -20
  4. package/README.md +377 -350
  5. package/index.js +30 -30
  6. package/lib/cli.js +649 -649
  7. package/lib/public/bucketadmin.css +1420 -1420
  8. package/lib/public/bucketadmin.js +1229 -1229
  9. package/lib/public/mbkbucket-helper-demo.html +1184 -1184
  10. package/lib/public/mbkbucket-helper.css +1235 -1235
  11. package/lib/public/mbkbucket-helper.js +1557 -1557
  12. package/lib/src/app.js +109 -109
  13. package/lib/src/cli/auth-helper.js +150 -150
  14. package/lib/src/config/index.js +112 -112
  15. package/lib/src/config/validation.js +73 -73
  16. package/lib/src/controllers/api.controller.js +410 -410
  17. package/lib/src/controllers/dashboard.controller.js +48 -48
  18. package/lib/src/controllers/info.controller.js +34 -34
  19. package/lib/src/controllers/view.controller.js +233 -233
  20. package/lib/src/middleware/async-handler.js +23 -23
  21. package/lib/src/middleware/bucket-resolver.js +37 -37
  22. package/lib/src/middleware/error-handler.js +54 -54
  23. package/lib/src/middleware/rate-limiter.js +22 -22
  24. package/lib/src/middleware/security.js +27 -27
  25. package/lib/src/routes/api.routes.js +54 -40
  26. package/lib/src/routes/index.js +40 -21
  27. package/lib/src/routes/info.routes.js +9 -9
  28. package/lib/src/routes/view.routes.js +25 -20
  29. package/lib/src/services/s3.service.js +603 -603
  30. package/lib/src/utils/errors.js +54 -54
  31. package/lib/src/utils/helpers.js +251 -251
  32. package/lib/src/utils/logger.js +17 -17
  33. package/package.json +83 -83
  34. package/views/bucket.handlebars +196 -196
  35. package/views/bucketadmin_alerts.handlebars +15 -15
  36. package/views/bucketadmin_delete_modal.handlebars +36 -36
  37. package/views/bucketadmin_filelist_skeleton.handlebars +32 -32
  38. package/views/bucketadmin_newfolder_modal.handlebars +29 -29
  39. package/views/bucketadmin_preview_modal.handlebars +40 -40
  40. package/views/bucketadmin_upload_modal.handlebars +54 -54
  41. package/views/bucketadmincontent.handlebars +141 -141
  42. package/views/mbkbucket_info.handlebars +232 -232
@@ -1,112 +1,112 @@
1
- /**
2
- * MBKBucket
3
- * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
- * Licensed under the MIT License.
5
- * Source: https://github.com/MIbnEKhalid/mbkbucket
6
- */
7
-
8
- import { createRequire } from "module";
9
- import dotenv from "dotenv";
10
- import { parseAndValidateMbkbucketVar, parseAndValidateBucketConnection } from "./validation.js";
11
- import { createLogger } from "#logger";
12
-
13
- dotenv.config();
14
- const debugConfig = createLogger('config');
15
- const require = createRequire(import.meta.url);
16
-
17
- // Load mbkbucket package.json
18
- let packageJson;
19
- try {
20
- packageJson = require("mbkbucket/package.json");
21
- } catch {
22
- packageJson = require("../../../package.json");
23
- }
24
-
25
- // Parent project version
26
- let appVersion = "unknown";
27
- try {
28
- appVersion = require("../../../../package.json")?.version ?? require(`${process.cwd()}/package.json`)?.version ?? "unknown";
29
- } catch {
30
- try {
31
- appVersion = require(`${process.cwd()}/package.json`)?.version ?? "unknown";
32
- } catch {}
33
- }
34
-
35
- export function compareVersions(a = '', b = '') {
36
- const left = String(a).split(/[.-]/).map(p => parseInt(p, 10) || 0);
37
- const right = String(b).split(/[.-]/).map(p => parseInt(p, 10) || 0);
38
- const len = Math.max(left.length, right.length);
39
-
40
- for (let i = 0; i < len; i++) {
41
- const diff = (left[i] || 0) - (right[i] || 0);
42
- if (diff !== 0) return diff;
43
- }
44
- return 0;
45
- }
46
-
47
- export async function getLatestVersion() {
48
- try {
49
- const res = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkbucket/master/package.json');
50
- if (!res.ok) {
51
- debugConfig('GitHub API responded with status %s', res.status);
52
- return "0.0.0";
53
- }
54
- const data = await res.json();
55
- return data.version;
56
- } catch (err) {
57
- debugConfig('Error fetching latest version from GitHub: %s', err.message);
58
- return null;
59
- }
60
- }
61
-
62
- export async function checkVersion() {
63
- try {
64
- const latest = await getLatestVersion();
65
- if (!latest) return;
66
- const diff = compareVersions(latest, packageJson.version);
67
- if (diff > 0) {
68
- console.log(`[mbkbucket] ⚠️ Update available: v${latest} (current: v${packageJson.version})`);
69
- } else {
70
- debugConfig('Running latest version (%s).', packageJson.version);
71
- }
72
- } catch (err) {
73
- debugConfig('Failed to check for updates: %s', err.message);
74
- }
75
- }
76
-
77
- export function validateConfiguration() {
78
- try {
79
- return parseAndValidateMbkbucketVar(process.env.mbkbucketVar);
80
- } catch (err) {
81
- throw new Error(`[mbkbucket] Configuration Validation Failed:\n - ${err.message}`);
82
- }
83
- }
84
-
85
- export function validateBucketConnection() {
86
- if (!process.env.BucketConnection) {
87
- console.warn(`[mbkbucket] ⚠️ BucketConnection not configured. You need to set this environment variable.`);
88
- console.warn(`[mbkbucket] Example format:\n[mbkbucket] BucketConnection={"r2":{"BUCKET_NAME":"my-bucket","ACCESS_KEY_ID":"key","SECRET_ACCESS_KEY":"secret","ENDPOINT":"https://..."}}`);
89
- return;
90
- }
91
- try {
92
- parseAndValidateBucketConnection(process.env.BucketConnection);
93
- debugConfig('BucketConnection validated');
94
- } catch (err) {
95
- throw new Error(`[mbkbucket] BucketConnection validation failed:\n - ${err.message}`);
96
- }
97
- }
98
-
99
- export function validateAllConfiguration() {
100
- try {
101
- validateConfiguration();
102
- validateBucketConnection();
103
- return true;
104
- } catch (err) {
105
- console.error(err.message);
106
- return false;
107
- }
108
- }
109
-
110
- export const mbkbucketVar = validateConfiguration();
111
-
112
- export { packageJson, appVersion };
1
+ /**
2
+ * MBKBucket
3
+ * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
+ * Licensed under the MIT License.
5
+ * Source: https://github.com/MIbnEKhalid/mbkbucket
6
+ */
7
+
8
+ import { createRequire } from "module";
9
+ import dotenv from "dotenv";
10
+ import { parseAndValidateMbkbucketVar, parseAndValidateBucketConnection } from "./validation.js";
11
+ import { createLogger } from "#logger";
12
+
13
+ dotenv.config();
14
+ const debugConfig = createLogger('config');
15
+ const require = createRequire(import.meta.url);
16
+
17
+ // Load mbkbucket package.json
18
+ let packageJson;
19
+ try {
20
+ packageJson = require("mbkbucket/package.json");
21
+ } catch {
22
+ packageJson = require("../../../package.json");
23
+ }
24
+
25
+ // Parent project version
26
+ let appVersion = "unknown";
27
+ try {
28
+ appVersion = require("../../../../package.json")?.version ?? require(`${process.cwd()}/package.json`)?.version ?? "unknown";
29
+ } catch {
30
+ try {
31
+ appVersion = require(`${process.cwd()}/package.json`)?.version ?? "unknown";
32
+ } catch {}
33
+ }
34
+
35
+ export function compareVersions(a = '', b = '') {
36
+ const left = String(a).split(/[.-]/).map(p => parseInt(p, 10) || 0);
37
+ const right = String(b).split(/[.-]/).map(p => parseInt(p, 10) || 0);
38
+ const len = Math.max(left.length, right.length);
39
+
40
+ for (let i = 0; i < len; i++) {
41
+ const diff = (left[i] || 0) - (right[i] || 0);
42
+ if (diff !== 0) return diff;
43
+ }
44
+ return 0;
45
+ }
46
+
47
+ export async function getLatestVersion() {
48
+ try {
49
+ const res = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkbucket/master/package.json');
50
+ if (!res.ok) {
51
+ debugConfig('GitHub API responded with status %s', res.status);
52
+ return "0.0.0";
53
+ }
54
+ const data = await res.json();
55
+ return data.version;
56
+ } catch (err) {
57
+ debugConfig('Error fetching latest version from GitHub: %s', err.message);
58
+ return null;
59
+ }
60
+ }
61
+
62
+ export async function checkVersion() {
63
+ try {
64
+ const latest = await getLatestVersion();
65
+ if (!latest) return;
66
+ const diff = compareVersions(latest, packageJson.version);
67
+ if (diff > 0) {
68
+ console.log(`[mbkbucket] ⚠️ Update available: v${latest} (current: v${packageJson.version})`);
69
+ } else {
70
+ debugConfig('Running latest version (%s).', packageJson.version);
71
+ }
72
+ } catch (err) {
73
+ debugConfig('Failed to check for updates: %s', err.message);
74
+ }
75
+ }
76
+
77
+ export function validateConfiguration() {
78
+ try {
79
+ return parseAndValidateMbkbucketVar(process.env.mbkbucketVar);
80
+ } catch (err) {
81
+ throw new Error(`[mbkbucket] Configuration Validation Failed:\n - ${err.message}`);
82
+ }
83
+ }
84
+
85
+ export function validateBucketConnection() {
86
+ if (!process.env.BucketConnection) {
87
+ console.warn(`[mbkbucket] ⚠️ BucketConnection not configured. You need to set this environment variable.`);
88
+ console.warn(`[mbkbucket] Example format:\n[mbkbucket] BucketConnection={"r2":{"BUCKET_NAME":"my-bucket","ACCESS_KEY_ID":"key","SECRET_ACCESS_KEY":"secret","ENDPOINT":"https://..."}}`);
89
+ return;
90
+ }
91
+ try {
92
+ parseAndValidateBucketConnection(process.env.BucketConnection);
93
+ debugConfig('BucketConnection validated');
94
+ } catch (err) {
95
+ throw new Error(`[mbkbucket] BucketConnection validation failed:\n - ${err.message}`);
96
+ }
97
+ }
98
+
99
+ export function validateAllConfiguration() {
100
+ try {
101
+ validateConfiguration();
102
+ validateBucketConnection();
103
+ return true;
104
+ } catch (err) {
105
+ console.error(err.message);
106
+ return false;
107
+ }
108
+ }
109
+
110
+ export const mbkbucketVar = validateConfiguration();
111
+
112
+ export { packageJson, appVersion };
@@ -1,73 +1,73 @@
1
- const TRUE_VALUES = new Set(['true', 't', '1', 'yes', 'y', 'on']);
2
- const FALSE_VALUES = new Set(['false', 'f', '0', 'no', 'n', 'off']);
3
-
4
- export function normalizeBooleanLike(value) {
5
- if (typeof value === 'boolean') return value;
6
- if (typeof value === 'string') {
7
- const norm = value.trim().toLowerCase();
8
- if (TRUE_VALUES.has(norm)) return true;
9
- if (FALSE_VALUES.has(norm)) return false;
10
- }
11
- return value;
12
- }
13
-
14
- export function parseAndValidateMbkbucketVar(rawValue) {
15
- let parsed;
16
- try {
17
- parsed = rawValue ? JSON.parse(rawValue) : {};
18
- } catch {
19
- throw new Error('Invalid JSON in process.env.mbkbucketVar');
20
- }
21
-
22
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
23
- throw new Error('mbkbucketVar must be a valid object');
24
- }
25
-
26
- const defaults = { p_view_inline: true, publiView_enabled: false };
27
- for (const [key, defaultVal] of Object.entries(defaults)) {
28
- const val = parsed[key];
29
- if (val === undefined || (typeof val === 'string' && val.trim() === '')) {
30
- parsed[key] = defaultVal;
31
- }
32
- }
33
-
34
- parsed.p_view_inline = normalizeBooleanLike(parsed.p_view_inline);
35
- parsed.publiView_enabled = normalizeBooleanLike(parsed.publiView_enabled);
36
-
37
- const errors = [];
38
- if (typeof parsed.p_view_inline !== 'boolean') errors.push('mbkbucketVar.p_view_inline must be a boolean if provided');
39
- if (typeof parsed.publiView_enabled !== 'boolean') errors.push('mbkbucketVar.publiView_enabled must be a boolean if provided');
40
- if (errors.length) throw new Error(errors.join(' | '));
41
-
42
- return parsed;
43
- }
44
-
45
- export function parseAndValidateBucketConnection(rawValue) {
46
- if (!rawValue) return null;
47
-
48
- let parsed;
49
- try {
50
- parsed = JSON.parse(rawValue);
51
- } catch {
52
- throw new Error('BucketConnection is not valid JSON');
53
- }
54
-
55
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || !Object.keys(parsed).length) {
56
- throw new Error('BucketConnection must be a non-empty object');
57
- }
58
-
59
- const requiredFields = ['BUCKET_NAME', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY', 'ENDPOINT'];
60
-
61
- for (const [bucketName, config] of Object.entries(parsed)) {
62
- if (!config || typeof config !== 'object' || Array.isArray(config)) {
63
- throw new Error(`Bucket '${bucketName}' configuration must be an object, not a string. Remove quotes around the inner JSON object.`);
64
- }
65
-
66
- const missingFields = requiredFields.filter(field => !config[field]);
67
- if (missingFields.length) {
68
- throw new Error(`Bucket '${bucketName}' is missing required fields: ${missingFields.join(', ')}`);
69
- }
70
- }
71
-
72
- return parsed;
73
- }
1
+ const TRUE_VALUES = new Set(['true', 't', '1', 'yes', 'y', 'on']);
2
+ const FALSE_VALUES = new Set(['false', 'f', '0', 'no', 'n', 'off']);
3
+
4
+ export function normalizeBooleanLike(value) {
5
+ if (typeof value === 'boolean') return value;
6
+ if (typeof value === 'string') {
7
+ const norm = value.trim().toLowerCase();
8
+ if (TRUE_VALUES.has(norm)) return true;
9
+ if (FALSE_VALUES.has(norm)) return false;
10
+ }
11
+ return value;
12
+ }
13
+
14
+ export function parseAndValidateMbkbucketVar(rawValue) {
15
+ let parsed;
16
+ try {
17
+ parsed = rawValue ? JSON.parse(rawValue) : {};
18
+ } catch {
19
+ throw new Error('Invalid JSON in process.env.mbkbucketVar');
20
+ }
21
+
22
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
23
+ throw new Error('mbkbucketVar must be a valid object');
24
+ }
25
+
26
+ const defaults = { p_view_inline: true, publiView_enabled: false };
27
+ for (const [key, defaultVal] of Object.entries(defaults)) {
28
+ const val = parsed[key];
29
+ if (val === undefined || (typeof val === 'string' && val.trim() === '')) {
30
+ parsed[key] = defaultVal;
31
+ }
32
+ }
33
+
34
+ parsed.p_view_inline = normalizeBooleanLike(parsed.p_view_inline);
35
+ parsed.publiView_enabled = normalizeBooleanLike(parsed.publiView_enabled);
36
+
37
+ const errors = [];
38
+ if (typeof parsed.p_view_inline !== 'boolean') errors.push('mbkbucketVar.p_view_inline must be a boolean if provided');
39
+ if (typeof parsed.publiView_enabled !== 'boolean') errors.push('mbkbucketVar.publiView_enabled must be a boolean if provided');
40
+ if (errors.length) throw new Error(errors.join(' | '));
41
+
42
+ return parsed;
43
+ }
44
+
45
+ export function parseAndValidateBucketConnection(rawValue) {
46
+ if (!rawValue) return null;
47
+
48
+ let parsed;
49
+ try {
50
+ parsed = JSON.parse(rawValue);
51
+ } catch {
52
+ throw new Error('BucketConnection is not valid JSON');
53
+ }
54
+
55
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || !Object.keys(parsed).length) {
56
+ throw new Error('BucketConnection must be a non-empty object');
57
+ }
58
+
59
+ const requiredFields = ['BUCKET_NAME', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY', 'ENDPOINT'];
60
+
61
+ for (const [bucketName, config] of Object.entries(parsed)) {
62
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
63
+ throw new Error(`Bucket '${bucketName}' configuration must be an object, not a string. Remove quotes around the inner JSON object.`);
64
+ }
65
+
66
+ const missingFields = requiredFields.filter(field => !config[field]);
67
+ if (missingFields.length) {
68
+ throw new Error(`Bucket '${bucketName}' is missing required fields: ${missingFields.join(', ')}`);
69
+ }
70
+ }
71
+
72
+ return parsed;
73
+ }