mbkbucket 3.3.1 → 3.4.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 (35) hide show
  1. package/README.md +17 -24
  2. package/index.js +2 -12
  3. package/lib/cli.js +191 -525
  4. package/lib/public/bucketadmin.js +1 -1
  5. package/lib/src/app.js +37 -50
  6. package/lib/src/cli/auth-helper.js +39 -112
  7. package/lib/src/config/index.js +68 -96
  8. package/lib/src/config/validation.js +14 -30
  9. package/lib/src/controllers/api.controller.js +85 -176
  10. package/lib/src/controllers/dashboard.controller.js +12 -23
  11. package/lib/src/controllers/info.controller.js +17 -20
  12. package/lib/src/controllers/view.controller.js +23 -70
  13. package/lib/src/middleware/async-handler.js +1 -16
  14. package/lib/src/middleware/bucket-resolver.js +7 -13
  15. package/lib/src/middleware/error-handler.js +40 -20
  16. package/lib/src/middleware/rate-limiter.js +2 -10
  17. package/lib/src/middleware/security.js +1 -17
  18. package/lib/src/routes/api.routes.js +21 -47
  19. package/lib/src/routes/index.js +2 -10
  20. package/lib/src/routes/info.routes.js +2 -3
  21. package/lib/src/routes/view.routes.js +3 -8
  22. package/lib/src/services/s3.service.js +157 -349
  23. package/lib/src/utils/errors.js +37 -59
  24. package/lib/src/utils/helpers.js +124 -70
  25. package/package.json +13 -5
  26. /package/{lib/views → views}/bucket.handlebars +0 -0
  27. /package/{lib/views → views}/bucketadmin_alerts.handlebars +0 -0
  28. /package/{lib/views → views}/bucketadmin_delete_modal.handlebars +0 -0
  29. /package/{lib/views → views}/bucketadmin_filelist_skeleton.handlebars +0 -0
  30. /package/{lib/views → views}/bucketadmin_newfolder_modal.handlebars +0 -0
  31. /package/{lib/views → views}/bucketadmin_preview_modal.handlebars +0 -0
  32. /package/{lib/views → views}/bucketadmin_upload_modal.handlebars +0 -0
  33. /package/{lib/views → views}/bucketadmincontent.handlebars +0 -0
  34. /package/{lib/views → views}/bucketportal.handlebars +0 -0
  35. /package/{lib/views → views}/mbkbucket_info.handlebars +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * MBKBucket File Manager - Gentelella v4 UI & Client Logic
2
+ * MBKBucket File Manager - UI & Client Logic
3
3
  */
4
4
 
5
5
  (function () {
package/lib/src/app.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import express from "express";
2
2
  import path from "path";
3
+ import fs from "fs";
3
4
  import { fileURLToPath } from "url";
4
5
  import { engine } from "express-handlebars";
5
6
  import compression from "compression";
@@ -8,34 +9,25 @@ import { generalLimiter } from "./middleware/rate-limiter.js";
8
9
  import { notFoundHandler, globalErrorHandler } from "./middleware/error-handler.js";
9
10
  import bucketRoutes from "./routes/index.js";
10
11
  import { packageJson } from "./config/index.js";
11
- import { createLogger } from "./utils/logger.js";
12
+ import { createLogger } from "#logger";
12
13
  import { commonHandlebarsHelpers } from "./utils/helpers.js";
13
14
 
14
15
  const debugServer = createLogger('server');
15
-
16
16
  const __filename = fileURLToPath(import.meta.url);
17
17
  const __dirname = path.dirname(__filename);
18
18
  const isDevMode = process.env.NODE_ENV === "dev";
19
19
 
20
- /**
21
- * Creates and configures the Express application.
22
- * @returns {express.Application}
23
- */
24
20
  export function createApp() {
25
21
  const server = express();
26
22
  server.set('trust proxy', 1);
27
23
 
28
- // Compression
29
24
  server.use(compression());
30
-
31
- // Body parsing
32
25
  server.use(express.json({ limit: '10mb' }));
33
26
  server.use(express.urlencoded({ extended: true, limit: '10mb' }));
34
27
 
35
- // Static assets (CSS/JS for dashboard)
36
- const dashboardAssetCacheControl = 'public, max-age=300, must-revalidate';
37
- const serveAsset = (filename) => (req, res) => {
38
- res.set('Cache-Control', dashboardAssetCacheControl);
28
+ // Static dashboard & helper assets
29
+ const serveAsset = (filename) => (_req, res) => {
30
+ res.set('Cache-Control', 'public, max-age=300, must-revalidate');
39
31
  res.sendFile(path.join(__dirname, '..', 'public', filename), (err) => {
40
32
  if (err) res.status(err.status || 404).end();
41
33
  });
@@ -43,71 +35,66 @@ export function createApp() {
43
35
 
44
36
  server.get('/mbkbucket/bucketadmin.css', serveAsset('bucketadmin.css'));
45
37
  server.get('/mbkbucket/bucketadmin.js', serveAsset('bucketadmin.js'));
46
-
47
- // Frontend helper utilities (drop-in for any HTML page)
48
38
  server.get('/mbkbucket/mbkbucket-helper.css', serveAsset('mbkbucket-helper.css'));
49
39
  server.get('/mbkbucket/mbkbucket-helper.js', serveAsset('mbkbucket-helper.js'));
50
-
51
- // Helper component demo page (no auth — purely static HTML)
52
40
  server.get('/mbkbucket/helper-demo', serveAsset('mbkbucket-helper-demo.html'));
53
41
 
54
- // Handlebars configuration
42
+ const rootDir = path.resolve(__dirname, "..", "..");
43
+ const libDir = path.resolve(__dirname, "..");
44
+ const cwd = process.cwd();
45
+
46
+ const viewDirs = [
47
+ path.join(rootDir, "views"),
48
+ path.join(libDir, "views"),
49
+ path.join(rootDir, "node_modules", "mbkauthe", "views"),
50
+ path.resolve(cwd, "views"),
51
+ path.resolve(cwd, "node_modules", "mbkauthe", "views"),
52
+ ].filter(dir => fs.existsSync(dir));
53
+
54
+ const partialsDir = [
55
+ path.join(rootDir, "views", "templates"),
56
+ path.join(rootDir, "views", "templates", "notice"),
57
+ path.join(rootDir, "views"),
58
+ path.join(rootDir, "views", "partial"),
59
+ path.join(libDir, "views", "templates"),
60
+ path.join(libDir, "views", "templates", "notice"),
61
+ path.join(libDir, "views"),
62
+ path.join(libDir, "views", "partial"),
63
+ path.join(rootDir, "node_modules", "mbkauthe", "views"),
64
+ path.resolve(cwd, "node_modules", "mbkauthe", "views"),
65
+ ].filter(dir => fs.existsSync(dir));
66
+
67
+ // Handlebars template engine
55
68
  server.engine("handlebars", engine({
56
69
  extname: ".handlebars",
57
70
  defaultLayout: false,
58
- partialsDir: [
59
- path.join(__dirname, "..", "views", "templates"),
60
- path.join(__dirname, "..", "views", "templates", "notice"),
61
- path.join(__dirname, "..", "views"),
62
- path.join(__dirname, "..", "views", "partial"),
63
- path.join(__dirname, "..", "node_modules", "mbkauthe", "views"),
64
- path.join(__dirname, "..", "..", "mbkauthe", "views")
65
- ],
71
+ partialsDir: [...new Set(partialsDir)],
66
72
  cache: process.env.NODE_ENV === "production",
67
73
  helpers: {
68
74
  ...commonHandlebarsHelpers,
69
- mbkbucket_cachebuster: function () {
70
- return "?v=" + packageJson.version;
71
- }
75
+ mbkbucket_cachebuster: () => `?v=${packageJson.version}`
72
76
  }
73
77
  }));
74
78
 
75
79
  server.set("view engine", "handlebars");
76
- server.set("views", [
77
- path.join(__dirname, "..", "views"),
78
- path.join(__dirname, "..", "node_modules", "mbkauthe", "views"),
79
- path.join(__dirname, "..", "..", "mbkauthe", "views")
80
- ]);
80
+ server.set("views", [...new Set(viewDirs)]);
81
81
 
82
- // Auth routes
83
82
  server.use(mbkautheRoutes);
84
-
85
- // Rate limiting
86
83
  server.use(generalLimiter);
87
-
88
- // Bucket routes
89
84
  server.use(bucketRoutes);
90
85
 
91
- // Dev-mode request timing
92
86
  if (isDevMode) {
93
- debugServer("Dev mode is enabled. Starting server in dev mode.");
94
-
95
87
  server.use((req, res, next) => {
96
- const start = process.hrtime();
88
+ const start = Date.now();
97
89
  res.on('finish', () => {
98
- const diff = process.hrtime(start);
99
- const ms = diff[0] * 1000 + diff[1] / 1e6;
100
- debugServer("[%s] %s %s %s - %s ms", new Date().toISOString(), req.method, req.originalUrl, res.statusCode, ms.toFixed(3));
90
+ debugServer('%s %s %s (%sms)', req.method, req.originalUrl, res.statusCode, Date.now() - start);
101
91
  });
102
92
  next();
103
93
  });
104
94
 
105
- server.get("/", async (req, res) => {
106
- res.redirect("/mbkbucket");
107
- });
95
+ server.get("/", (_req, res) => res.redirect("/mbkbucket"));
108
96
  }
109
97
 
110
- // Error handlers
111
98
  server.use(notFoundHandler);
112
99
  server.use(globalErrorHandler);
113
100
 
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * CLI Authentication Helper
3
- *
4
3
  * Handles mbkauthe device-flow login for the mbkbucket CLI.
5
4
  * Stores credentials in ~/.mbkbucket/config.json
6
5
  */
@@ -8,39 +7,27 @@
8
7
  import fs from 'node:fs';
9
8
  import path from 'node:path';
10
9
  import os from 'node:os';
11
- import { createLogger } from '../utils/logger.js';
10
+ import { createLogger } from '#logger';
12
11
 
13
12
  const debugCli = createLogger('cli:auth');
14
13
 
15
14
  export const CONFIG_DIR = path.join(os.homedir(), '.mbkbucket');
16
15
  export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
17
- const POLL_INTERVAL_MS = 5000;
18
16
  const MAX_POLL_TIME_MS = 15 * 60 * 1000; // 15 minutes
17
+ const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
19
18
 
20
- /**
21
- * Read the stored CLI config.
22
- * @returns {{ token?: string, tokenPrefix?: string, username?: string, serverUrl?: string, profile?: object }}
23
- */
24
19
  export function readConfig() {
25
20
  try {
26
- if (!fs.existsSync(CONFIG_FILE)) return {};
27
- const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
28
- return JSON.parse(raw);
21
+ return fs.existsSync(CONFIG_FILE) ? JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8')) : {};
29
22
  } catch (err) {
30
23
  debugCli('Failed to read config: %s', err.message);
31
24
  return {};
32
25
  }
33
26
  }
34
27
 
35
- /**
36
- * Write config to disk.
37
- * @param {object} config
38
- */
39
28
  export function writeConfig(config) {
40
29
  try {
41
- if (!fs.existsSync(CONFIG_DIR)) {
42
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
43
- }
30
+ if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
44
31
  fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
45
32
  } catch (err) {
46
33
  console.error(`❌ Failed to write config: ${err.message}`);
@@ -48,14 +35,9 @@ export function writeConfig(config) {
48
35
  }
49
36
  }
50
37
 
51
- /**
52
- * Clear stored credentials (logout).
53
- */
54
38
  export function clearConfig() {
55
39
  try {
56
- if (fs.existsSync(CONFIG_FILE)) {
57
- fs.unlinkSync(CONFIG_FILE);
58
- }
40
+ if (fs.existsSync(CONFIG_FILE)) fs.unlinkSync(CONFIG_FILE);
59
41
  return true;
60
42
  } catch (err) {
61
43
  debugCli('Failed to clear config: %s', err.message);
@@ -63,49 +45,27 @@ export function clearConfig() {
63
45
  }
64
46
  }
65
47
 
66
- /**
67
- * Merge partial config into stored config.
68
- * @param {object} partial
69
- */
70
48
  export function updateConfig(partial) {
71
- const current = readConfig();
72
- const updated = { ...current, ...partial };
49
+ const updated = { ...readConfig(), ...partial };
73
50
  writeConfig(updated);
74
51
  return updated;
75
52
  }
76
53
 
77
- /**
78
- * Check if the user is logged in (has a stored token).
79
- */
80
54
  export function isLoggedIn() {
81
- const cfg = readConfig();
82
- return !!(cfg.token && cfg.serverUrl);
55
+ const { token, server_url } = readConfig();
56
+ return Boolean(token && server_url);
83
57
  }
84
58
 
85
- /**
86
- * Get the stored API token.
87
- */
88
59
  export function getStoredToken() {
89
60
  return readConfig().token || null;
90
61
  }
91
62
 
92
- /**
93
- * Get the stored server URL.
94
- */
95
63
  export function getStoredServerUrl() {
96
- return readConfig().serverUrl || null;
64
+ return readConfig().server_url || null;
97
65
  }
98
66
 
99
- /**
100
- * Perform a device-flow login against an mbkauthe-powered server.
101
- *
102
- * @param {{ serverUrl: string, profileKey?: string }} options
103
- * @returns {Promise<{ token: string, tokenPrefix: string, username: string, profile: object }>}
104
- */
105
- export async function deviceFlowLogin({ serverUrl, profileKey }) {
106
- const baseUrl = String(serverUrl).replace(/\/+$/, '');
107
-
108
- // --- Step 1: Request a device code ---
67
+ export async function deviceFlowLogin({ server_url, profile_key }) {
68
+ const baseUrl = String(server_url).replace(/\/+$/, '');
109
69
  console.log(`🔑 Requesting device login from ${baseUrl} ...`);
110
70
 
111
71
  let deviceResp;
@@ -114,54 +74,38 @@ export async function deviceFlowLogin({ serverUrl, profileKey }) {
114
74
  method: 'POST',
115
75
  headers: { 'Content-Type': 'application/json' },
116
76
  body: JSON.stringify({
117
- clientName: `mbkbucket-cli-${os.hostname()}`,
118
- ...(profileKey ? { profileKey } : {}),
77
+ client_name: `mbkbucket-cli-${os.hostname()}`,
78
+ ...(profile_key && { profile_key }),
119
79
  }),
120
80
  });
121
-
122
81
  deviceResp = await res.json();
123
-
124
- if (!deviceResp.success) {
125
- throw new Error(deviceResp.message || 'Failed to initiate device login');
126
- }
82
+ if (!deviceResp.success) throw new Error(deviceResp.message || 'Failed to initiate device login');
127
83
  } catch (err) {
128
84
  if (err.message.includes('Failed to initiate') || err.message.includes('fetch')) {
129
- throw new Error(
130
- `Could not reach mbkauthe server at ${baseUrl}/api/cli/device.\n` +
131
- `Verify the server URL and that CLI_AUTH_ENABLED=true on the server.`
132
- );
85
+ throw new Error(`Could not reach mbkauthe server at ${baseUrl}/api/cli/device.\nVerify the server URL and that CLI_AUTH_ENABLED=true on the server.`);
133
86
  }
134
87
  throw err;
135
88
  }
136
89
 
137
- const {
138
- verificationUrl,
139
- userCode,
140
- deviceCode,
141
- expiresIn,
142
- interval,
143
- profile,
144
- } = deviceResp;
145
-
146
- // --- Step 2: Prompt user to approve ---
147
- console.log('');
148
- console.log('┌─────────────────────────────────────────────────────────┐');
90
+ const verification_url = deviceResp.verification_url;
91
+ const user_code = deviceResp.user_code;
92
+ const device_code = deviceResp.device_code;
93
+ const expires_in = deviceResp.expires_in;
94
+ const interval = deviceResp.interval;
95
+ const profile = deviceResp.profile;
96
+
97
+ console.log('\n┌─────────────────────────────────────────────────────────┐');
98
+ console.log('│ │');
99
+ console.log('│ 🌐 Open this URL in your browser to approve: │');
100
+ console.log(`│ ${(verification_url || '').padEnd(53)}│`);
149
101
  console.log('│ │');
150
- console.log(`│ 🌐 Open this URL in your browser to approve: │`);
151
- console.log(`│ ${verificationUrl.padEnd(53)}│`);
152
- console.log(`│ │`);
153
- console.log(`│ 🔢 Or enter this code: ${userCode.padEnd(30)}│`);
102
+ console.log(`│ 🔢 Or enter this code: ${(user_code || '').padEnd(30)}│`);
154
103
  console.log('│ │');
155
- console.log('└─────────────────────────────────────────────────────────┘');
156
- console.log('');
104
+ console.log('└─────────────────────────────────────────────────────────┘\n');
157
105
 
158
- if (profile) {
159
- console.log(` Profile: ${profile.name} (scope: ${profile.scope || 'full'})`);
160
- }
161
- console.log(` Waiting for approval (expires in ${Math.floor(expiresIn / 60)} minutes)...`);
162
- console.log('');
106
+ if (profile) console.log(` Profile: ${profile.name} (scope: ${profile.scope || 'full'})`);
107
+ console.log(` Waiting for approval (expires in ${Math.floor((expires_in || 900) / 60)} minutes)...\n`);
163
108
 
164
- // --- Step 3: Poll for token ---
165
109
  const pollInterval = (interval || 5) * 1000;
166
110
  const deadline = Date.now() + MAX_POLL_TIME_MS;
167
111
  let attempts = 0;
@@ -174,50 +118,33 @@ export async function deviceFlowLogin({ serverUrl, profileKey }) {
174
118
  const pollRes = await fetch(`${baseUrl}/api/cli/device/token`, {
175
119
  method: 'POST',
176
120
  headers: { 'Content-Type': 'application/json' },
177
- body: JSON.stringify({ deviceCode }),
121
+ body: JSON.stringify({ device_code }),
178
122
  });
179
123
 
180
124
  const pollBody = await pollRes.json();
181
125
 
182
126
  if (pollBody.status === 'approved' && pollBody.success) {
127
+ const token_prefix = pollBody.token_prefix || '';
183
128
  console.log(`✅ Login approved! Welcome, ${pollBody.username}.`);
184
- console.log(` Token prefix: ${pollBody.tokenPrefix}...`);
129
+ console.log(` Token prefix: ${token_prefix}...`);
185
130
  return {
186
131
  token: pollBody.token,
187
- tokenPrefix: pollBody.tokenPrefix,
132
+ token_prefix: token_prefix,
188
133
  username: pollBody.username,
189
134
  profile: profile || null,
190
135
  };
191
136
  }
192
137
 
193
- if (pollBody.status === 'denied') {
194
- throw new Error('Login request was denied.');
195
- }
138
+ if (pollBody.status === 'denied') throw new Error('Login request was denied.');
139
+ if (pollBody.status === 'expired') throw new Error('Login request expired. Please try again.');
140
+ if (pollBody.status === 'completed') throw new Error('This login session has already been completed.');
196
141
 
197
- if (pollBody.status === 'expired') {
198
- throw new Error('Login request expired. Please try again.');
199
- }
200
-
201
- if (pollBody.status === 'completed') {
202
- throw new Error('This login session has already been completed.');
203
- }
204
-
205
- // Still pending — show a dot every 5 polls
206
- if (attempts % 5 === 0) {
207
- process.stdout.write('.');
208
- }
142
+ if (attempts % 5 === 0) process.stdout.write('.');
209
143
  } catch (err) {
210
- if (err.message.includes('denied') || err.message.includes('expired') || err.message.includes('completed')) {
211
- throw err;
212
- }
213
- // Network errors during poll — retry
144
+ if (['denied', 'expired', 'completed'].some(k => err.message.includes(k))) throw err;
214
145
  debugCli('Poll error (will retry): %s', err.message);
215
146
  }
216
147
  }
217
148
 
218
149
  throw new Error('Login timed out. Please try again.');
219
150
  }
220
-
221
- function sleep(ms) {
222
- return new Promise((resolve) => setTimeout(resolve, ms));
223
- }
@@ -1,133 +1,105 @@
1
1
  import { createRequire } from "module";
2
2
  import dotenv from "dotenv";
3
3
  import { parseAndValidateMbkbucketVar, parseAndValidateBucketConnection } from "./validation.js";
4
- import { createLogger } from "../utils/logger.js";
4
+ import { createLogger } from "#logger";
5
5
 
6
6
  dotenv.config();
7
7
  const debugConfig = createLogger('config');
8
-
9
- // Load package.json from mbkbucket package (not parent project)
10
8
  const require = createRequire(import.meta.url);
9
+
10
+ // Load mbkbucket package.json
11
11
  let packageJson;
12
12
  try {
13
- // Try to load from mbkbucket package directory
14
- packageJson = require("mbkbucket/package.json");
13
+ packageJson = require("mbkbucket/package.json");
15
14
  } catch {
16
- // Fallback to relative path (for development/testing)
17
- packageJson = require("../../../package.json");
15
+ packageJson = require("../../../package.json");
18
16
  }
19
17
 
20
18
  // Parent project version
21
- let appVersion;
19
+ let appVersion = "unknown";
22
20
  try {
23
- appVersion = require("../../../../package.json")?.version || "unknown";
21
+ appVersion = require("../../../../package.json")?.version ?? require(`${process.cwd()}/package.json`)?.version ?? "unknown";
24
22
  } catch {
25
- // Fallback if path doesn't work
26
- try {
27
- appVersion = require(process.cwd() + "/package.json")?.version || "unknown";
28
- } catch {
29
- appVersion = "unknown";
30
- }
23
+ try {
24
+ appVersion = require(`${process.cwd()}/package.json`)?.version ?? "unknown";
25
+ } catch {}
31
26
  }
32
27
 
33
- function compareVersions(a, b) {
34
- const left = String(a || '').split(/[.-]/).map((part) => parseInt(part, 10) || 0);
35
- const right = String(b || '').split(/[.-]/).map((part) => parseInt(part, 10) || 0);
36
- const length = Math.max(left.length, right.length);
28
+ export function compareVersions(a = '', b = '') {
29
+ const left = String(a).split(/[.-]/).map(p => parseInt(p, 10) || 0);
30
+ const right = String(b).split(/[.-]/).map(p => parseInt(p, 10) || 0);
31
+ const len = Math.max(left.length, right.length);
37
32
 
38
- for (let i = 0; i < length; i += 1) {
39
- const diff = (left[i] || 0) - (right[i] || 0);
40
- if (diff !== 0) return diff;
41
- }
42
- return 0;
33
+ for (let i = 0; i < len; i++) {
34
+ const diff = (left[i] || 0) - (right[i] || 0);
35
+ if (diff !== 0) return diff;
36
+ }
37
+ return 0;
43
38
  }
44
39
 
45
- // Fetch latest version from GitHub
46
- async function getLatestVersion() {
40
+ export async function getLatestVersion() {
47
41
  try {
48
- const response = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkbucket/master/package.json');
49
- if (!response.ok) {
50
- debugConfig('GitHub API responded with status %s', response.status);
42
+ const res = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkbucket/master/package.json');
43
+ if (!res.ok) {
44
+ debugConfig('GitHub API responded with status %s', res.status);
51
45
  return "0.0.0";
52
46
  }
53
-
54
- const latestPackageJson = await response.json();
55
- return latestPackageJson.version;
56
- } catch (error) {
57
- debugConfig('Error fetching latest version from GitHub: %s', error.message);
47
+ const data = await res.json();
48
+ return data.version;
49
+ } catch (err) {
50
+ debugConfig('Error fetching latest version from GitHub: %s', err.message);
58
51
  return null;
59
52
  }
60
53
  }
61
54
 
62
- // Version check with error handling
63
- async function checkVersion() {
64
- try {
65
- const latestVersion = await getLatestVersion();
66
- if (latestVersion && compareVersions(latestVersion, packageJson.version) > 0) {
67
- debugConfig('Current version (%s) is outdated. Latest version: %s.', packageJson.version, latestVersion);
68
- } else if (latestVersion && compareVersions(latestVersion, packageJson.version) < 0) {
69
- debugConfig('Current version (%s) is newer than published version: %s.', packageJson.version, latestVersion);
70
- } else if (latestVersion) {
71
- debugConfig('Running latest version (%s).', packageJson.version);
72
- }
73
- } catch (error) {
74
- debugConfig('Failed to check for updates: %s', error.message);
55
+ export async function checkVersion() {
56
+ try {
57
+ const latest = await getLatestVersion();
58
+ if (!latest) return;
59
+ const diff = compareVersions(latest, packageJson.version);
60
+ if (diff > 0) {
61
+ console.log(`[mbkbucket] ⚠️ Update available: v${latest} (current: v${packageJson.version})`);
62
+ } else {
63
+ debugConfig('Running latest version (%s).', packageJson.version);
75
64
  }
65
+ } catch (err) {
66
+ debugConfig('Failed to check for updates: %s', err.message);
67
+ }
76
68
  }
77
69
 
78
- // Comprehensive validation function
79
- function validateConfiguration() {
80
- try {
81
- const parsed = parseAndValidateMbkbucketVar(process.env.mbkbucketVar);
82
- if (!process.env.mbkbucketVar) {
83
- debugConfig('mbkbucketVar not defined, using defaults');
84
- }
85
- debugConfig('Configuration loaded');
86
- return parsed;
87
- } catch (error) {
88
- throw new Error(`[mbkbucket] Configuration Validation Failed:\n - ${error.message}`);
89
- }
70
+ export function validateConfiguration() {
71
+ try {
72
+ return parseAndValidateMbkbucketVar(process.env.mbkbucketVar);
73
+ } catch (err) {
74
+ throw new Error(`[mbkbucket] Configuration Validation Failed:\n - ${err.message}`);
75
+ }
90
76
  }
91
77
 
92
- // Validate BucketConnection configuration
93
- function validateBucketConnection() {
94
- if (!process.env.BucketConnection) {
95
- console.warn(`[mbkbucket] ⚠️ BucketConnection not configured. You need to set this environment variable.`);
96
- console.warn(`[mbkbucket] Example format:`);
97
- console.warn(`[mbkbucket] BucketConnection={"r2":{"BUCKET_NAME":"my-bucket","ACCESS_KEY_ID":"key","SECRET_ACCESS_KEY":"secret","ENDPOINT":"https://..."}}`);
98
- return;
99
- }
100
-
101
- try {
102
- parseAndValidateBucketConnection(process.env.BucketConnection);
103
- debugConfig('BucketConnection configuration validated successfully');
104
- } catch (error) {
105
- throw new Error(`[mbkbucket] BucketConnection validation failed:\n - ${error.message}`);
106
- }
78
+ export function validateBucketConnection() {
79
+ if (!process.env.BucketConnection) {
80
+ console.warn(`[mbkbucket] ⚠️ BucketConnection not configured. You need to set this environment variable.`);
81
+ console.warn(`[mbkbucket] Example format:\n[mbkbucket] BucketConnection={"r2":{"BUCKET_NAME":"my-bucket","ACCESS_KEY_ID":"key","SECRET_ACCESS_KEY":"secret","ENDPOINT":"https://..."}}`);
82
+ return;
83
+ }
84
+ try {
85
+ parseAndValidateBucketConnection(process.env.BucketConnection);
86
+ debugConfig('BucketConnection validated');
87
+ } catch (err) {
88
+ throw new Error(`[mbkbucket] BucketConnection validation failed:\n - ${err.message}`);
89
+ }
107
90
  }
108
91
 
109
- // Validate all configuration at startup
110
- function validateAllConfiguration() {
111
- try {
112
- validateConfiguration();
113
- validateBucketConnection();
114
- debugConfig('All configurations validated successfully');
115
- return true;
116
- } catch (error) {
117
- console.error(error.message);
118
- return false;
119
- }
92
+ export function validateAllConfiguration() {
93
+ try {
94
+ validateConfiguration();
95
+ validateBucketConnection();
96
+ return true;
97
+ } catch (err) {
98
+ console.error(err.message);
99
+ return false;
100
+ }
120
101
  }
121
102
 
122
- const mbkbucketVar = validateConfiguration();
103
+ export const mbkbucketVar = validateConfiguration();
123
104
 
124
- export {
125
- packageJson,
126
- appVersion,
127
- mbkbucketVar,
128
- checkVersion,
129
- getLatestVersion,
130
- validateConfiguration,
131
- validateBucketConnection,
132
- validateAllConfiguration
133
- };
105
+ export { packageJson, appVersion };