mbkbucket 1.0.2 → 1.1.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.
- package/.env.example +8 -1
- package/README.md +7 -1
- package/index.d.ts +235 -4
- package/index.js +193 -4
- package/lib/bucket.js +646 -0
- package/lib/s3.js +504 -0
- package/package.json +32 -20
- package/views/bucket.handlebars +226 -0
- package/views/bucketadmincontent.handlebars +2933 -0
package/.env.example
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
mbkbucketVar={"APP_NAME":"mbkbucket","loginRedirectURL":"/dashboard","bucket":""}
|
|
2
2
|
mbkauthShared={"Main_SECRET_TOKEN": 123,"DOMAIN":"mbktech.org","IS_DEPLOYED":"false","EncPass":"false","DEVICE_TRUST_DURATION_DAYS":7,"SESSION_SECRET_KEY":"123","LOGIN_DB":"postgres://","MBKAUTH_TWO_FA_ENABLE":"true","COOKIE_EXPIRE_TIME":2,"GITHUB_LOGIN_ENABLED":"f","GITHUB_CLIENT_ID":"123","GITHUB_CLIENT_SECRET":"123","GOOGLE_LOGIN_ENABLED":"f","GOOGLE_CLIENT_ID":"123","GOOGLE_CLIENT_SECRET":"123"}
|
|
3
|
-
# See @node_modules/mbkauthe/docs/env.md for more details
|
|
3
|
+
# See @node_modules/mbkauthe/docs/env.md for more details
|
|
4
|
+
NODE_ENV=development
|
|
5
|
+
|
|
6
|
+
R2_Bucket={"BUCKET_NAME":"","ACCESS_KEY_ID":"","SECRET_ACCESS_KEY":"","ENDPOINT":""}
|
|
7
|
+
E2_Bucket={"BUCKET_NAME":"","ACCESS_KEY_ID":"","SECRET_ACCESS_KEY":"","ENDPOINT":"","Region":"ap-southeast-1"}
|
|
8
|
+
# Region is optional
|
|
9
|
+
|
|
10
|
+
PORT=3005
|
package/README.md
CHANGED
|
@@ -3,4 +3,10 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/mbkbucket)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
|
-
[](https://github.com/MIbnEKhalid/mbkbucket/actions/workflows/publish.yml)
|
|
6
|
+
[](https://github.com/MIbnEKhalid/mbkbucket/actions/workflows/publish.yml)
|
|
7
|
+
|
|
8
|
+
## Contact
|
|
9
|
+
|
|
10
|
+
For questions or contributions, please contact Muhammad Bin Khalid at [mbktechstudio.com/Support](https://mbktechstudio.com/Support/?Project=MIbnEKhalidWeb), [support@mbktechstudio.com](mailto:support@mbktechstudio.com) or [chmuhammadbinkhalid28@gmail.com](mailto:chmuhammadbinkhalid28@gmail.com).
|
|
11
|
+
|
|
12
|
+
Developed by [Muhammad Bin Khalid](https://github.com/MIbnEKhalid) at [MBK Tech Studio](https://mbktech.org/).
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,237 @@
|
|
|
1
|
-
// Type definitions for mbkbucket
|
|
1
|
+
// Type definitions for mbkbucket 1.1.0
|
|
2
2
|
// Project: https://github.com/MIbnEKhalid/mbkbucket
|
|
3
|
-
// Definitions by: Muhammad Bin Khalid <https://github.com/MIbnEKhalid>
|
|
3
|
+
// Definitions by: Muhammad Bin Khalid <https://github.com/MIbnEKhalid">
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { Readable } from "stream";
|
|
6
|
+
import { Router, Application } from "express";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Shared types used across the package
|
|
10
|
+
*/
|
|
11
|
+
export interface BucketConfig {
|
|
12
|
+
BUCKET_NAME: string;
|
|
13
|
+
ENDPOINT?: string;
|
|
14
|
+
ACCESS_KEY_ID?: string;
|
|
15
|
+
SECRET_ACCESS_KEY?: string;
|
|
16
|
+
region?: string;
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface S3ObjectResult {
|
|
21
|
+
Body: Readable;
|
|
22
|
+
ContentLength?: number;
|
|
23
|
+
ContentType?: string;
|
|
24
|
+
LastModified?: Date;
|
|
25
|
+
Key?: string;
|
|
26
|
+
[key: string]: any;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* S3-related functions (available from `mbkbucket/lib/s3` and re-exported at package root)
|
|
31
|
+
*/
|
|
32
|
+
export function getBucketConfig(bucketName?: string): BucketConfig;
|
|
33
|
+
export const bucketClient: any;
|
|
34
|
+
export function getAppName(): string;
|
|
35
|
+
export function ensureKeyHasAppPrefix(key?: string): string;
|
|
36
|
+
export function ensurePrefix(prefix?: string): string;
|
|
37
|
+
export function getBucketClient(bucketName?: string): any;
|
|
38
|
+
export function checkR2Health(): Promise<{ status: string; responseTime?: number; bucket?: string; error?: string; checkedAt: string; region?: string }>;
|
|
39
|
+
|
|
40
|
+
export function uploadFile(
|
|
41
|
+
key: string,
|
|
42
|
+
fileBuffer: Buffer | Uint8Array,
|
|
43
|
+
contentType: string,
|
|
44
|
+
options?: {
|
|
45
|
+
metadata?: Record<string, string>;
|
|
46
|
+
cacheControl?: string;
|
|
47
|
+
storageClass?: string;
|
|
48
|
+
serverSideEncryption?: string;
|
|
49
|
+
bucketName?: string;
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
}
|
|
52
|
+
): Promise<{ fileSize?: number; key: string; contentType?: string; uploadedAt?: string } & Record<string, any>>;
|
|
53
|
+
|
|
54
|
+
export function downloadFile(
|
|
55
|
+
key: string,
|
|
56
|
+
options?: {
|
|
57
|
+
range?: string | null;
|
|
58
|
+
ifNoneMatch?: string | null;
|
|
59
|
+
ifModifiedSince?: Date | string | null;
|
|
60
|
+
responseCacheControl?: string | null;
|
|
61
|
+
responseContentType?: string | null;
|
|
62
|
+
bucketName?: string;
|
|
63
|
+
[key: string]: any;
|
|
64
|
+
}
|
|
65
|
+
): Promise<S3ObjectResult>;
|
|
66
|
+
|
|
67
|
+
export function deleteFile(key: string, bucketName?: string): Promise<{ key: string; deletedAt?: string } & Record<string, any>>;
|
|
68
|
+
|
|
69
|
+
export function deleteFiles(keys: string[], bucketName?: string): Promise<{
|
|
70
|
+
results: any[];
|
|
71
|
+
deletedCount: number;
|
|
72
|
+
errors: any[];
|
|
73
|
+
deletedAt: string;
|
|
74
|
+
}>;
|
|
75
|
+
|
|
76
|
+
export function listfiles(
|
|
77
|
+
prefix?: string,
|
|
78
|
+
options?: {
|
|
79
|
+
maxKeys?: number;
|
|
80
|
+
continuationToken?: string | null;
|
|
81
|
+
delimiter?: string | null;
|
|
82
|
+
fetchOwner?: boolean;
|
|
83
|
+
startAfter?: string | null;
|
|
84
|
+
bucketName?: string;
|
|
85
|
+
[key: string]: any;
|
|
86
|
+
}
|
|
87
|
+
): Promise<Record<string, any>>;
|
|
88
|
+
|
|
89
|
+
export function getFileMetadata(key: string, bucketName?: string): Promise<{
|
|
90
|
+
key: string;
|
|
91
|
+
exists: boolean;
|
|
92
|
+
queriedAt: string;
|
|
93
|
+
ContentLength?: number;
|
|
94
|
+
ContentType?: string;
|
|
95
|
+
LastModified?: Date;
|
|
96
|
+
[key: string]: any;
|
|
97
|
+
}>;
|
|
98
|
+
|
|
99
|
+
export function fileExists(key: string, bucketName?: string): Promise<boolean>;
|
|
100
|
+
export function getFileSize(key: string, bucketName?: string): Promise<number | null>;
|
|
101
|
+
|
|
102
|
+
export function generateSignedUrl(
|
|
103
|
+
key: string,
|
|
104
|
+
operation?: 'getObject' | 'putObject' | string,
|
|
105
|
+
expiresIn?: number
|
|
106
|
+
): Promise<{
|
|
107
|
+
url: string;
|
|
108
|
+
key: string;
|
|
109
|
+
operation: string;
|
|
110
|
+
expiresIn: number;
|
|
111
|
+
expiresAt: string;
|
|
112
|
+
generatedAt: string;
|
|
113
|
+
}>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Exported router from `lib/bucket` (re-exported at package root as `bucket`)
|
|
117
|
+
*/
|
|
118
|
+
export const bucket: Router;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Root default export: the configured Express application
|
|
122
|
+
*/
|
|
123
|
+
declare const server: Application;
|
|
124
|
+
export default server;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Module declarations for subpath imports so TypeScript consumers can import:
|
|
128
|
+
* import { uploadFile } from 'mbkbucket/lib/s3';
|
|
129
|
+
* import { packageJson } from 'mbkbucket/lib/config/index';
|
|
130
|
+
* import bucketRouter from 'mbkbucket/lib/bucket';
|
|
131
|
+
*/
|
|
132
|
+
declare module "mbkbucket/lib/s3" {
|
|
133
|
+
import { Readable } from "stream";
|
|
134
|
+
export interface BucketConfig {
|
|
135
|
+
BUCKET_NAME: string;
|
|
136
|
+
ENDPOINT?: string;
|
|
137
|
+
ACCESS_KEY_ID?: string;
|
|
138
|
+
SECRET_ACCESS_KEY?: string;
|
|
139
|
+
region?: string;
|
|
140
|
+
[key: string]: any;
|
|
141
|
+
}
|
|
142
|
+
export interface S3ObjectResult {
|
|
143
|
+
Body: Readable;
|
|
144
|
+
ContentLength?: number;
|
|
145
|
+
ContentType?: string;
|
|
146
|
+
LastModified?: Date;
|
|
147
|
+
Key?: string;
|
|
148
|
+
[key: string]: any;
|
|
149
|
+
}
|
|
150
|
+
export function getBucketConfig(bucketName?: string): BucketConfig;
|
|
151
|
+
export const bucketClient: any;
|
|
152
|
+
export function getAppName(): string;
|
|
153
|
+
export function ensureKeyHasAppPrefix(key?: string): string;
|
|
154
|
+
export function ensurePrefix(prefix?: string): string;
|
|
155
|
+
export function getBucketClient(bucketName?: string): any;
|
|
156
|
+
export function checkR2Health(): Promise<{ status: string; responseTime?: number; bucket?: string; error?: string; checkedAt: string; region?: string }>;
|
|
157
|
+
export function uploadFile(
|
|
158
|
+
key: string,
|
|
159
|
+
fileBuffer: Buffer | Uint8Array,
|
|
160
|
+
contentType: string,
|
|
161
|
+
options?: {
|
|
162
|
+
metadata?: Record<string, string>;
|
|
163
|
+
cacheControl?: string;
|
|
164
|
+
storageClass?: string;
|
|
165
|
+
serverSideEncryption?: string;
|
|
166
|
+
bucketName?: string;
|
|
167
|
+
[key: string]: any;
|
|
168
|
+
}
|
|
169
|
+
): Promise<{ fileSize?: number; key: string; contentType?: string; uploadedAt?: string } & Record<string, any>>;
|
|
170
|
+
export function downloadFile(
|
|
171
|
+
key: string,
|
|
172
|
+
options?: {
|
|
173
|
+
range?: string | null;
|
|
174
|
+
ifNoneMatch?: string | null;
|
|
175
|
+
ifModifiedSince?: Date | string | null;
|
|
176
|
+
responseCacheControl?: string | null;
|
|
177
|
+
responseContentType?: string | null;
|
|
178
|
+
bucketName?: string;
|
|
179
|
+
[key: string]: any;
|
|
180
|
+
}
|
|
181
|
+
): Promise<S3ObjectResult>;
|
|
182
|
+
export function deleteFile(key: string, bucketName?: string): Promise<{ key: string; deletedAt?: string } & Record<string, any>>;
|
|
183
|
+
export function deleteFiles(keys: string[], bucketName?: string): Promise<{
|
|
184
|
+
results: any[];
|
|
185
|
+
deletedCount: number;
|
|
186
|
+
errors: any[];
|
|
187
|
+
deletedAt: string;
|
|
188
|
+
}>;
|
|
189
|
+
export function listfiles(
|
|
190
|
+
prefix?: string,
|
|
191
|
+
options?: {
|
|
192
|
+
maxKeys?: number;
|
|
193
|
+
continuationToken?: string | null;
|
|
194
|
+
delimiter?: string | null;
|
|
195
|
+
fetchOwner?: boolean;
|
|
196
|
+
startAfter?: string | null;
|
|
197
|
+
bucketName?: string;
|
|
198
|
+
[key: string]: any;
|
|
199
|
+
}
|
|
200
|
+
): Promise<Record<string, any>>;
|
|
201
|
+
export function getFileMetadata(key: string, bucketName?: string): Promise<{
|
|
202
|
+
key: string;
|
|
203
|
+
exists: boolean;
|
|
204
|
+
queriedAt: string;
|
|
205
|
+
ContentLength?: number;
|
|
206
|
+
ContentType?: string;
|
|
207
|
+
LastModified?: Date;
|
|
208
|
+
[key: string]: any;
|
|
209
|
+
}>;
|
|
210
|
+
export function fileExists(key: string, bucketName?: string): Promise<boolean>;
|
|
211
|
+
export function getFileSize(key: string, bucketName?: string): Promise<number | null>;
|
|
212
|
+
export function generateSignedUrl(
|
|
213
|
+
key: string,
|
|
214
|
+
operation?: 'getObject' | 'putObject' | string,
|
|
215
|
+
expiresIn?: number
|
|
216
|
+
): Promise<{
|
|
217
|
+
url: string;
|
|
218
|
+
key: string;
|
|
219
|
+
operation: string;
|
|
220
|
+
expiresIn: number;
|
|
221
|
+
expiresAt: string;
|
|
222
|
+
generatedAt: string;
|
|
223
|
+
}>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
declare module "mbkbucket/lib/config/index" {
|
|
227
|
+
export const packageJson: Record<string, any>;
|
|
228
|
+
export const appVersion: string;
|
|
229
|
+
export function getLatestVersion(): Promise<string | null>;
|
|
230
|
+
export function checkVersion(): Promise<void>;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
declare module "mbkbucket/lib/bucket" {
|
|
234
|
+
import { Router } from "express";
|
|
235
|
+
const router: Router;
|
|
236
|
+
export default router;
|
|
237
|
+
}
|
package/index.js
CHANGED
|
@@ -1,9 +1,198 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import mbkauthe from "mbkauthe";
|
|
6
|
+
import { engine } from "express-handlebars";
|
|
7
|
+
import compression from "compression";
|
|
8
|
+
import rateLimit from 'express-rate-limit';
|
|
9
|
+
import bucket from "./lib/bucket.js";
|
|
1
10
|
import { checkVersion } from "./lib/config/index.js";
|
|
2
11
|
|
|
12
|
+
dotenv.config();
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
|
|
17
|
+
const server = express();
|
|
18
|
+
server.set('trust proxy', 1);
|
|
19
|
+
|
|
20
|
+
server.use(compression());
|
|
21
|
+
|
|
22
|
+
// Rate limiting: general limiter for typical browsing/API usage and a stricter
|
|
23
|
+
// limiter for dashboard (admin) routes.
|
|
24
|
+
const generalLimiter = rateLimit({
|
|
25
|
+
windowMs: 2 * 60 * 1000, // 2 minutes
|
|
26
|
+
max: 150, // limit each IP to 150 requests per windowMs
|
|
27
|
+
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
|
28
|
+
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
29
|
+
handler: (req, res) => {
|
|
30
|
+
res.status(429).render('error.handlebars', { message: 'Too many requests from your IP. Try again later.', code: 429 });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
server.use(express.json());
|
|
35
|
+
server.use(express.urlencoded({ extended: true }));
|
|
36
|
+
|
|
37
|
+
// Configure Handlebars (single setup)
|
|
38
|
+
server.engine("handlebars", engine({
|
|
39
|
+
extname: ".handlebars",
|
|
40
|
+
defaultLayout: "main",
|
|
41
|
+
partialsDir: [
|
|
42
|
+
path.join(__dirname, "views/templates"),
|
|
43
|
+
path.join(__dirname, "views/templates/notice"),
|
|
44
|
+
path.join(__dirname, "views"),
|
|
45
|
+
path.join(__dirname, "views/partial"),
|
|
46
|
+
path.join(__dirname, "node_modules/mbkauthe/views"),
|
|
47
|
+
],
|
|
48
|
+
cache: process.env.NODE_ENV === "production",
|
|
49
|
+
helpers: {
|
|
50
|
+
formatDate: (date) => {
|
|
51
|
+
return new Date(date).toLocaleString('en-US', {
|
|
52
|
+
year: 'numeric',
|
|
53
|
+
month: 'long',
|
|
54
|
+
day: 'numeric',
|
|
55
|
+
hour: '2-digit',
|
|
56
|
+
minute: '2-digit',
|
|
57
|
+
second: '2-digit',
|
|
58
|
+
hour12: true
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
in: function (value, list) {
|
|
62
|
+
if (!list || !Array.isArray(list)) return false;
|
|
63
|
+
return list.includes(parseInt(value) || value);
|
|
64
|
+
},
|
|
65
|
+
trim: function (str) {
|
|
66
|
+
return str ? str.trim() : '';
|
|
67
|
+
},
|
|
68
|
+
eq: function (a, b) {
|
|
69
|
+
return a === b;
|
|
70
|
+
},
|
|
71
|
+
encodeURIComponent: function (str) {
|
|
72
|
+
return encodeURIComponent(str);
|
|
73
|
+
},
|
|
74
|
+
formatTimestamp: function (timestamp) {
|
|
75
|
+
return new Date(timestamp).toLocaleString();
|
|
76
|
+
},
|
|
77
|
+
jsonStringify: function (context) {
|
|
78
|
+
return JSON.stringify(context);
|
|
79
|
+
},
|
|
80
|
+
truncate: (str, len) => {
|
|
81
|
+
if (!str) return '';
|
|
82
|
+
if (str.length > len) {
|
|
83
|
+
return str.substring(0, len) + '...';
|
|
84
|
+
}
|
|
85
|
+
return str;
|
|
86
|
+
},
|
|
87
|
+
section: function (name, options) {
|
|
88
|
+
if (!this._sections) this._sections = {};
|
|
89
|
+
this._sections[name] = options.fn(this);
|
|
90
|
+
return null;
|
|
91
|
+
},
|
|
92
|
+
getCanonicalUrl: function (req, path) {
|
|
93
|
+
const protocol = req.protocol || 'https';
|
|
94
|
+
const host = req.get('host') || 'blog.mbktechstudio.com';
|
|
95
|
+
return `${protocol}://${host}${path}`;
|
|
96
|
+
},
|
|
97
|
+
index: function (array, idx) {
|
|
98
|
+
return array ? array[idx] : null;
|
|
99
|
+
},
|
|
100
|
+
add: function (a, b) {
|
|
101
|
+
return Number(a) + Number(b);
|
|
102
|
+
},
|
|
103
|
+
subtract: function (a, b) {
|
|
104
|
+
return Number(a) - Number(b);
|
|
105
|
+
},
|
|
106
|
+
gt: function (a, b) {
|
|
107
|
+
return Number(a) > Number(b);
|
|
108
|
+
},
|
|
109
|
+
gte: function (a, b) {
|
|
110
|
+
return Number(a) >= Number(b);
|
|
111
|
+
},
|
|
112
|
+
lte: function (a, b) {
|
|
113
|
+
return Number(a) <= Number(b);
|
|
114
|
+
},
|
|
115
|
+
and: function () {
|
|
116
|
+
return Array.prototype.slice.call(arguments, 0, -1).every(Boolean);
|
|
117
|
+
},
|
|
118
|
+
or: function () {
|
|
119
|
+
return Array.prototype.slice.call(arguments, 0, -1).some(Boolean);
|
|
120
|
+
},
|
|
121
|
+
range: function (start, end) {
|
|
122
|
+
const result = [];
|
|
123
|
+
for (let i = start; i < end; i++) {
|
|
124
|
+
result.push(i);
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
server.set("view engine", "handlebars");
|
|
132
|
+
server.set("views", [
|
|
133
|
+
path.join(__dirname, "views"),
|
|
134
|
+
path.join(__dirname, "node_modules/mbkauthe/views"),
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
server.use(mbkauthe);
|
|
138
|
+
server.use(generalLimiter);
|
|
139
|
+
server.use(bucket);
|
|
140
|
+
|
|
3
141
|
if (process.env.test === "dev") {
|
|
4
|
-
|
|
142
|
+
|
|
143
|
+
console.log("[mbkbucket] Dev mode is enabled. Starting server in dev mode.");
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
// Request timing middleware: logs method, url, status and elapsed ms
|
|
147
|
+
server.use((req, res, next) => {
|
|
148
|
+
const start = process.hrtime();
|
|
149
|
+
res.on('finish', () => {
|
|
150
|
+
const diff = process.hrtime(start);
|
|
151
|
+
const ms = diff[0] * 1000 + diff[1] / 1e6;
|
|
152
|
+
console.log(`[mbkbucket] [${new Date().toISOString()}] ${req.method} ${req.originalUrl} ${res.statusCode} - ${ms.toFixed(3)} ms`);
|
|
153
|
+
});
|
|
154
|
+
next();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
server.get("/", async (req, res) => {
|
|
158
|
+
res.redirect("/mbkbucket");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// 404 handler
|
|
162
|
+
server.use((req, res) => {
|
|
163
|
+
console.log(`[mbkbucket] Path not found: ${req.method} ${req.url}`);
|
|
164
|
+
return res.status(404).render("Error/dError.handlebars", {
|
|
165
|
+
layout: false,
|
|
166
|
+
code: 404,
|
|
167
|
+
error: "Not Found",
|
|
168
|
+
message: "The requested page was not found.",
|
|
169
|
+
pagename: "Home",
|
|
170
|
+
page: "/",
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Error handler
|
|
175
|
+
server.use((err, req, res, next) => {
|
|
176
|
+
console.error(`[mbkbucket] ${err.stack}`);
|
|
177
|
+
return res.status(500).render("Error/dError.handlebars", {
|
|
178
|
+
layout: false,
|
|
179
|
+
code: 500,
|
|
180
|
+
error: "Internal app Error",
|
|
181
|
+
message: "An unexpected error occurred on the app.",
|
|
182
|
+
details: err.message,
|
|
183
|
+
pagename: "Home",
|
|
184
|
+
page: "/",
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const port = process.env.PORT || 3004;
|
|
189
|
+
server.listen(port, async () => {
|
|
190
|
+
console.log(`[mbkbucket] Server running on http://localhost:${port}`);
|
|
191
|
+
});
|
|
192
|
+
|
|
5
193
|
}
|
|
6
194
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
195
|
+
await checkVersion();
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
export default server;
|