@ecopex/ecopex-framework 1.0.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 +73 -0
- package/README.md +248 -0
- package/bun.lockb +0 -0
- package/config/swagger/admin.js +44 -0
- package/config/swagger/api.js +19 -0
- package/database/migrations/20240000135243_timezones.js +22 -0
- package/database/migrations/20240000135244_countries.js +23 -0
- package/database/migrations/20240000135244_create_admins_table.js +66 -0
- package/database/migrations/20240000135244_currencies.js +21 -0
- package/database/migrations/20240000135244_languages.js +21 -0
- package/database/migrations/20240000135244_taxes.js +10 -0
- package/database/migrations/20240000135245_sites.js +37 -0
- package/database/migrations/20240000135246_payment_methods.js +33 -0
- package/database/migrations/20251016113547_devices.js +37 -0
- package/database/migrations/20251019192600_users.js +62 -0
- package/database/migrations/20251019213551_language_lines.js +35 -0
- package/database/migrations/20251222214131_category_groups.js +18 -0
- package/database/migrations/20251222214619_categories.js +27 -0
- package/database/migrations/20251222214848_brands.js +23 -0
- package/database/migrations/20251222214946_products.js +30 -0
- package/database/migrations/20251222215428_product_images.js +18 -0
- package/database/migrations/20251222215553_options.js +30 -0
- package/database/migrations/20251222215806_variants.js +23 -0
- package/database/migrations/20251222215940_attributes.js +25 -0
- package/database/migrations/20251222220135_discounts.js +15 -0
- package/database/migrations/20251222220253_reviews.js +22 -0
- package/database/migrations/20251222220341_favorites.js +10 -0
- package/database/migrations/20251222220422_search_logs.js +17 -0
- package/database/migrations/20251222220636_orders.js +16 -0
- package/database/migrations/20251222220806_order_items.js +19 -0
- package/database/migrations/20251222221317_order_statuses.js +10 -0
- package/database/migrations/20251222221446_order_payments.js +13 -0
- package/database/migrations/20251222221654_order_addresses.js +23 -0
- package/database/migrations/20251222221807_order_status_logs.js +13 -0
- package/database/seeds/admins.js +37 -0
- package/database/seeds/countries.js +203 -0
- package/database/seeds/currencies.js +165 -0
- package/database/seeds/languages.js +113 -0
- package/database/seeds/timezones.js +149 -0
- package/ecosystem.config.js +26 -0
- package/env.example +73 -0
- package/knexfile.js +3 -0
- package/libraries/2fa.js +22 -0
- package/libraries/aws.js +63 -0
- package/libraries/bcrypt.js +284 -0
- package/libraries/controls.js +113 -0
- package/libraries/date.js +14 -0
- package/libraries/general.js +8 -0
- package/libraries/image.js +57 -0
- package/libraries/jwt.js +178 -0
- package/libraries/knex.js +7 -0
- package/libraries/slug.js +14 -0
- package/libraries/stores.js +22 -0
- package/libraries/upload.js +194 -0
- package/locales/en/messages.json +4 -0
- package/locales/en/sql.json +3 -0
- package/locales/en/validation.json +52 -0
- package/locales/es/validation.json +52 -0
- package/locales/tr/validation.json +59 -0
- package/package.json +75 -0
- package/routes/admin/auto/admins.json +63 -0
- package/routes/admin/auto/devices.json +37 -0
- package/routes/admin/auto/migrations.json +21 -0
- package/routes/admin/auto/users.json +61 -0
- package/routes/admin/middlewares/index.js +87 -0
- package/routes/admin/spec/auth.js +626 -0
- package/routes/admin/spec/users.js +3 -0
- package/routes/auto/handler.js +635 -0
- package/routes/common/auto/countries.json +28 -0
- package/routes/common/auto/currencies.json +26 -0
- package/routes/common/auto/languages.json +26 -0
- package/routes/common/auto/taxes.json +46 -0
- package/routes/common/auto/timezones.json +29 -0
- package/stores/base.js +73 -0
- package/stores/index.js +195 -0
- package/utils/i18n.js +187 -0
- package/utils/jsonRouteLoader.js +587 -0
- package/utils/middleware.js +154 -0
- package/utils/routeLoader.js +227 -0
- package/workers/admin.js +124 -0
- package/workers/api.js +106 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const emailValidation = (email = '') => {
|
|
2
|
+
return String(email).toLowerCase().match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
const phoneValidation = (phone = '') => {
|
|
6
|
+
return String(phone).match(/^[\+]?([0-9][\s]?|[0-9]?)([(][0-9]{3}[)][\s]?|[0-9]{3}[-\s\.]?)[0-9]{3}[-\s\.]?[0-9]{4,6}$/im);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function randomChars(length) {
|
|
10
|
+
var result = '';
|
|
11
|
+
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
12
|
+
var charactersLength = characters.length;
|
|
13
|
+
for (var i = 0; i < length; i++) {
|
|
14
|
+
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const randomNumber = (min, max) => {
|
|
20
|
+
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getIpAddress(req = {}) {
|
|
24
|
+
return req.headers['x-forwarded-for'] ||
|
|
25
|
+
req.socket.remoteAddress ||
|
|
26
|
+
null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const roundTo = (n, digits) => {
|
|
30
|
+
if (digits === undefined) {
|
|
31
|
+
digits = 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var multiplicator = Math.pow(10, digits);
|
|
35
|
+
n = parseFloat((n * multiplicator).toFixed(11));
|
|
36
|
+
var test =(Math.round(n) / multiplicator);
|
|
37
|
+
return +(test.toFixed(digits));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function validateLatLng(lat, lng) {
|
|
41
|
+
let pattern = new RegExp('^-?([1-8]?[1-9]|[1-9]0)\\.{1}\\d{1,6}');
|
|
42
|
+
|
|
43
|
+
return pattern.test(lat) && pattern.test(lng);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const checkCoordinates = (address, paths) => {
|
|
47
|
+
|
|
48
|
+
if (paths) {
|
|
49
|
+
|
|
50
|
+
const allPaths = paths.c[0].reduce((o, n) => {
|
|
51
|
+
o.lngs.push(parseFloat(n.lng));
|
|
52
|
+
o.lats.push(parseFloat(n.lat));
|
|
53
|
+
return o
|
|
54
|
+
}, { lngs: [], lats: [] })
|
|
55
|
+
|
|
56
|
+
const minimumLng = Math.min(...allPaths.lngs)
|
|
57
|
+
const maximumLng = Math.max(...allPaths.lngs)
|
|
58
|
+
const minimumLat = Math.min(...allPaths.lats)
|
|
59
|
+
const maximumLat = Math.max(...allPaths.lats)
|
|
60
|
+
|
|
61
|
+
if (parseFloat(address.lng) >= minimumLng && parseFloat(address.lng) <= maximumLng && parseFloat(address.lat) >= minimumLat && parseFloat(address.lat) <= maximumLat) {
|
|
62
|
+
return true
|
|
63
|
+
} else {
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
} else {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const checkPassoword = (password) => {
|
|
74
|
+
return /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])\S{8,}$/.test(password)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const sleep = async (ms) => {
|
|
78
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const ipcheck = (req, whitelists = '') => {
|
|
82
|
+
|
|
83
|
+
const client_ip = req.headers['cf-connecting-ip'] || (req.headers['x-real-ip'] || req.ip)
|
|
84
|
+
|
|
85
|
+
// whitelist split
|
|
86
|
+
const whiteips = typeof whitelists === 'string' && whitelists != '' ? whitelists.split(',').map(n => n.trim()) : []
|
|
87
|
+
|
|
88
|
+
const status = whiteips.length > 0 ? whiteips.includes(client_ip) : true
|
|
89
|
+
|
|
90
|
+
if(!status) {
|
|
91
|
+
console.error(client_ip, whiteips, req.headers);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// check if exists
|
|
95
|
+
return {
|
|
96
|
+
status: status,
|
|
97
|
+
client_ip: client_ip
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
emailValidation,
|
|
103
|
+
phoneValidation,
|
|
104
|
+
randomChars,
|
|
105
|
+
randomNumber,
|
|
106
|
+
validateLatLng,
|
|
107
|
+
checkCoordinates,
|
|
108
|
+
getIpAddress,
|
|
109
|
+
checkPassoword,
|
|
110
|
+
ipcheck,
|
|
111
|
+
sleep,
|
|
112
|
+
roundTo
|
|
113
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const dayjs = require('dayjs');
|
|
2
|
+
const utc = require('dayjs/plugin/utc')
|
|
3
|
+
const timezone = require("dayjs/plugin/timezone");
|
|
4
|
+
dayjs.extend(utc)
|
|
5
|
+
dayjs.extend(timezone)
|
|
6
|
+
|
|
7
|
+
const getUtcFormated = (date = new Date()) => {
|
|
8
|
+
return dayjs(date).utc().format('YYYY-MM-DD HH:mm:ss')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
getUtcFormated,
|
|
13
|
+
dayjs
|
|
14
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const resizer = require('node-image-resizer');
|
|
2
|
+
const basePath = __dirname + '../uploads/'
|
|
3
|
+
|
|
4
|
+
let resizerOpts = {
|
|
5
|
+
all: {
|
|
6
|
+
path: basePath + 'thumbnails/',
|
|
7
|
+
quality: 80
|
|
8
|
+
},
|
|
9
|
+
versions: [{
|
|
10
|
+
prefix: '',
|
|
11
|
+
width: 800,
|
|
12
|
+
height: 800
|
|
13
|
+
}]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const resize = async (filePath, resizeSetup = false, quality = 80, directory = false) => {
|
|
17
|
+
|
|
18
|
+
if(directory) {
|
|
19
|
+
resizerOpts.all.path = basePath + directory + '/'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const resizeOptsAll = JSON.parse(JSON.stringify(resizerOpts))
|
|
23
|
+
resizeOptsAll.all.quality = quality
|
|
24
|
+
|
|
25
|
+
if (resizeSetup) {
|
|
26
|
+
resizeOptsAll.versions = resizeSetup.reduce((o,n) => {
|
|
27
|
+
const version = Object.assign({}, resizeOptsAll.versions[0], n)
|
|
28
|
+
version.prefix = 'resize_' + version.width + 'x' + version.height + '_'
|
|
29
|
+
o.push(version)
|
|
30
|
+
return o
|
|
31
|
+
}, [])
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const resizedImages = await resizer(basePath + filePath, resizeOptsAll);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
status: true,
|
|
39
|
+
files: resizedImages.reduce((o,n,k) => {
|
|
40
|
+
o.push({
|
|
41
|
+
width: resizeOptsAll.versions[k].width,
|
|
42
|
+
height: resizeOptsAll.versions[k].height,
|
|
43
|
+
fileUrl: n.replace(basePath, '')
|
|
44
|
+
})
|
|
45
|
+
return o
|
|
46
|
+
},[])
|
|
47
|
+
};
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
status: false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
resize
|
|
57
|
+
}
|
package/libraries/jwt.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
const jwt = require('jsonwebtoken');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JWT Library Class
|
|
5
|
+
* Provides methods for JWT token operations including sign, verify, and decode
|
|
6
|
+
*/
|
|
7
|
+
class JWT {
|
|
8
|
+
constructor(secretKey = process.env.JWT_SECRET || 'default-secret-key') {
|
|
9
|
+
this.secretKey = secretKey;
|
|
10
|
+
this.defaultExpiresIn = '1d';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Sign a payload and create a JWT token
|
|
15
|
+
* @param {Object} payload - The payload to encode
|
|
16
|
+
* @param {Object} options - JWT options (expiresIn, algorithm, etc.)
|
|
17
|
+
* @returns {string} JWT token
|
|
18
|
+
*/
|
|
19
|
+
sign(payload, options = {}) {
|
|
20
|
+
try {
|
|
21
|
+
const defaultOptions = {
|
|
22
|
+
expiresIn: this.defaultExpiresIn,
|
|
23
|
+
algorithm: 'HS256'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const signOptions = { ...defaultOptions, ...options };
|
|
27
|
+
|
|
28
|
+
return jwt.sign(payload, this.secretKey, signOptions);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new Error(`JWT Sign Error: ${error.message}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Verify and decode a JWT token
|
|
36
|
+
* @param {string} token - The JWT token to verify
|
|
37
|
+
* @param {Object} options - Verification options
|
|
38
|
+
* @returns {Object} Decoded payload
|
|
39
|
+
*/
|
|
40
|
+
verify(token, options = {}) {
|
|
41
|
+
try {
|
|
42
|
+
const verifyOptions = {
|
|
43
|
+
algorithms: ['HS256'],
|
|
44
|
+
...options
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return jwt.verify(token, this.secretKey, verifyOptions);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error.name === 'TokenExpiredError') {
|
|
50
|
+
return false;
|
|
51
|
+
} else if (error.name === 'JsonWebTokenError') {
|
|
52
|
+
return false;
|
|
53
|
+
} else if (error.name === 'NotBeforeError') {
|
|
54
|
+
return false;
|
|
55
|
+
} else {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Decode a JWT token without verification
|
|
63
|
+
* @param {string} token - The JWT token to decode
|
|
64
|
+
* @param {Object} options - Decode options
|
|
65
|
+
* @returns {Object} Decoded payload (not verified)
|
|
66
|
+
*/
|
|
67
|
+
decode(token, options = {}) {
|
|
68
|
+
try {
|
|
69
|
+
return jwt.decode(token, options);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw new Error(`JWT Decode Error: ${error.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a token is expired without throwing an error
|
|
77
|
+
* @param {string} token - The JWT token to check
|
|
78
|
+
* @returns {boolean} True if token is expired, false otherwise
|
|
79
|
+
*/
|
|
80
|
+
isExpired(token) {
|
|
81
|
+
try {
|
|
82
|
+
const decoded = this.decode(token);
|
|
83
|
+
if (!decoded || !decoded.exp) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
88
|
+
return decoded.exp < currentTime;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get token expiration time
|
|
96
|
+
* @param {string} token - The JWT token
|
|
97
|
+
* @returns {Date|null} Expiration date or null if invalid
|
|
98
|
+
*/
|
|
99
|
+
getExpirationTime(token) {
|
|
100
|
+
try {
|
|
101
|
+
const decoded = this.decode(token);
|
|
102
|
+
if (!decoded || !decoded.exp) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return new Date(decoded.exp * 1000);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Refresh a token by creating a new one with the same payload
|
|
114
|
+
* @param {string} token - The JWT token to refresh
|
|
115
|
+
* @param {Object} options - New token options
|
|
116
|
+
* @returns {string} New JWT token
|
|
117
|
+
*/
|
|
118
|
+
refresh(token, options = {}) {
|
|
119
|
+
try {
|
|
120
|
+
const decoded = this.verify(token);
|
|
121
|
+
|
|
122
|
+
// Remove standard JWT claims that shouldn't be copied
|
|
123
|
+
const { iat, exp, nbf, jti, ...payload } = decoded;
|
|
124
|
+
|
|
125
|
+
return this.sign(payload, options);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new Error(`JWT Refresh Error: ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Create a token with custom expiration
|
|
133
|
+
* @param {Object} payload - The payload to encode
|
|
134
|
+
* @param {string|number} expiresIn - Expiration time (e.g., '1h', '7d', 3600)
|
|
135
|
+
* @returns {string} JWT token
|
|
136
|
+
*/
|
|
137
|
+
createToken(payload, expiresIn = this.defaultExpiresIn) {
|
|
138
|
+
return this.sign(payload, { expiresIn });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Create a short-lived token (e.g., for email verification)
|
|
143
|
+
* @param {Object} payload - The payload to encode
|
|
144
|
+
* @param {string|number} expiresIn - Expiration time (default: '15m')
|
|
145
|
+
* @returns {string} JWT token
|
|
146
|
+
*/
|
|
147
|
+
createShortToken(payload, expiresIn = '15m') {
|
|
148
|
+
return this.sign(payload, { expiresIn });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Create a long-lived token (e.g., for refresh tokens)
|
|
153
|
+
* @param {Object} payload - The payload to encode
|
|
154
|
+
* @param {string|number} expiresIn - Expiration time (default: '30d')
|
|
155
|
+
* @returns {string} JWT token
|
|
156
|
+
*/
|
|
157
|
+
createLongToken(payload, expiresIn = '30d') {
|
|
158
|
+
return this.sign(payload, { expiresIn });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Set a new secret key
|
|
163
|
+
* @param {string} secretKey - New secret key
|
|
164
|
+
*/
|
|
165
|
+
setSecretKey(secretKey) {
|
|
166
|
+
this.secretKey = secretKey;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get the current secret key (masked for security)
|
|
171
|
+
* @returns {string} Masked secret key
|
|
172
|
+
*/
|
|
173
|
+
getSecretKey() {
|
|
174
|
+
return this.secretKey ? `${this.secretKey.substring(0, 4)}...` : 'Not set';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = JWT;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const slugify = require('slugify')
|
|
2
|
+
|
|
3
|
+
const returnSlug = (title = "") => {
|
|
4
|
+
return slugify(title.toLowerCase(), {
|
|
5
|
+
replacement: '-', // replace spaces with replacement character, defaults to `-`
|
|
6
|
+
remove: undefined, // remove characters that match regex, defaults to `undefined`
|
|
7
|
+
lower: false, // convert to lower case, defaults to `false`
|
|
8
|
+
strict: false, // strip special characters except replacement, defaults to `false`
|
|
9
|
+
locale: 'en', // language code of the locale to use
|
|
10
|
+
trim: true // trim leading and trailing replacement chars, defaults to `true`
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = returnSlug
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const db = require('@root/libraries/knex');
|
|
2
|
+
|
|
3
|
+
class Store {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.db = db;
|
|
6
|
+
this.currencies = new Map();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async init() {
|
|
10
|
+
await this.get_currencies();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async get_currencies() {
|
|
14
|
+
const currencies = await this.db('currencies').select('currency_id', 'name', 'code', 'symbol');
|
|
15
|
+
currencies.forEach(currency => {
|
|
16
|
+
this.currencies.set(currency.currency_id, currency);
|
|
17
|
+
});
|
|
18
|
+
return this.currencies;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = new Store();
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const { randomChars, sleep } = require('./controls');
|
|
4
|
+
const uploadBase = __dirname + '/../uploads/'
|
|
5
|
+
const { resize: resizeImage } = require('./image')
|
|
6
|
+
const sizeOf = require('image-size');
|
|
7
|
+
const returnSlug = require('./slug')
|
|
8
|
+
const { uploadAws } = require('./aws');
|
|
9
|
+
|
|
10
|
+
const uploadFile = async (fileObject = {}, prefix = '', directory = '', resizeOpts = false) => {
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
|
|
14
|
+
if (directory != '' && !fs.existsSync(uploadBase + directory)) {
|
|
15
|
+
await fs.mkdirSync(uploadBase + directory);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let fileExt = fileObject.filename.split('.')
|
|
19
|
+
fileExt = fileExt[fileExt.length - 1];
|
|
20
|
+
const fileName = returnSlug((prefix != '' ? prefix + '_' : '') + '_' + randomChars(16)) + '.' + fileExt
|
|
21
|
+
directory = directory != '' ? directory + '/' : ''
|
|
22
|
+
|
|
23
|
+
await fs.writeFileSync(uploadBase + directory + fileName, fileObject._buf)
|
|
24
|
+
|
|
25
|
+
return await createReturnData(fileName, directory, resizeOpts, fileObject)
|
|
26
|
+
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return {
|
|
29
|
+
status: false,
|
|
30
|
+
error
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const createReturnData = async (fileName = '', directory = '', resizeOpts = false, fileObject = false) => {
|
|
38
|
+
|
|
39
|
+
let returnData = {
|
|
40
|
+
status: false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let awsError = false
|
|
44
|
+
|
|
45
|
+
if (resizeOpts) {
|
|
46
|
+
returnData = await resizeImage(directory + fileName, resizeOpts, 90, directory.replace(/\//g, ''))
|
|
47
|
+
returnData.resize = true
|
|
48
|
+
} else {
|
|
49
|
+
let dimensions = {}
|
|
50
|
+
|
|
51
|
+
if(fileObject && fileObject.mimetype.includes('image')) {
|
|
52
|
+
try {
|
|
53
|
+
dimensions = sizeOf(uploadBase + directory + fileName)
|
|
54
|
+
} catch (error) {
|
|
55
|
+
dimensions = {}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
returnData = {
|
|
60
|
+
status: true,
|
|
61
|
+
resize: false,
|
|
62
|
+
file: {
|
|
63
|
+
width: dimensions.width || 'none',
|
|
64
|
+
height: dimensions.height || 'none',
|
|
65
|
+
field: fileObject.fieldname,
|
|
66
|
+
encoding: fileObject.encoding,
|
|
67
|
+
mimetype: fileObject.mimetype,
|
|
68
|
+
fileUrl: directory + fileName
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if(process.env.AWS_STATUS == 'true') {
|
|
74
|
+
|
|
75
|
+
await sleep(1000);
|
|
76
|
+
|
|
77
|
+
if (returnData.resize) {
|
|
78
|
+
if (returnData.files.length > 0) {
|
|
79
|
+
for (let rfi = 0; rfi < returnData.files.length; rfi++) {
|
|
80
|
+
const file = returnData.files[rfi];
|
|
81
|
+
let statusUploadAws = await uploadAws(file.fileUrl, fileObject.mimetype)
|
|
82
|
+
|
|
83
|
+
if(!statusUploadAws.status) {
|
|
84
|
+
awsError = {
|
|
85
|
+
status: false,
|
|
86
|
+
msg: "aws_upload_error"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fs.unlinkSync(uploadBase + file.fileUrl)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
let statusUploadAws = await uploadAws(returnData.file.fileUrl, returnData.file.mimetype)
|
|
95
|
+
|
|
96
|
+
if (!statusUploadAws.status) {
|
|
97
|
+
awsError = {
|
|
98
|
+
status: false,
|
|
99
|
+
messages: "aws_upload_error"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
fs.unlinkSync(uploadBase + returnData.file.fileUrl)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (returnData.resize) {
|
|
107
|
+
fs.unlinkSync(uploadBase + directory + fileName)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if(awsError) {
|
|
111
|
+
return awsError
|
|
112
|
+
} else {
|
|
113
|
+
return returnData
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const downloadFile = async (fileUrl = "", directory = '') => {
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
|
|
122
|
+
if (directory != '' && !fs.existsSync(uploadBase + directory)) {
|
|
123
|
+
await fs.mkdirSync(uploadBase + directory);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const fileName = randomChars(3) + new Date().getTime() + '.jpg'
|
|
127
|
+
directory = directory != '' ? directory + '/' : ''
|
|
128
|
+
|
|
129
|
+
const writer = await fs.createWriteStream(uploadBase + directory + fileName);
|
|
130
|
+
|
|
131
|
+
axios({
|
|
132
|
+
method: 'GET',
|
|
133
|
+
url: fileUrl,
|
|
134
|
+
responseType: 'stream',
|
|
135
|
+
}).then(response => {
|
|
136
|
+
|
|
137
|
+
//ensure that the user can call `then()` only when the file has
|
|
138
|
+
//been downloaded entirely.
|
|
139
|
+
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
|
|
142
|
+
response.data.pipe(writer);
|
|
143
|
+
let error = null;
|
|
144
|
+
|
|
145
|
+
writer.on('error', err => {
|
|
146
|
+
error = err;
|
|
147
|
+
writer.close();
|
|
148
|
+
reject(err);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
writer.on('close', () => {
|
|
152
|
+
if (!error) {
|
|
153
|
+
resolve(true);
|
|
154
|
+
}
|
|
155
|
+
//no need to call the reject here, as it will have been called in the
|
|
156
|
+
//'error' stream;
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return createReturnData(fileName, directory, false)
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return {
|
|
165
|
+
status: false,
|
|
166
|
+
messages: error
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const deleteFile = async (fileName = '') => {
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
|
|
175
|
+
if (fs.existsSync(uploadBase + fileName)) {
|
|
176
|
+
await fs.unlinkSync(uploadBase + fileName)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
status: true
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
return {
|
|
184
|
+
status: false
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = {
|
|
191
|
+
uploadFile,
|
|
192
|
+
downloadFile,
|
|
193
|
+
deleteFile
|
|
194
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"validation": {
|
|
3
|
+
"required": "The field '{field}' is required",
|
|
4
|
+
"minLength": "The field '{field}' must be at least {min} characters long",
|
|
5
|
+
"maxLength": "The field '{field}' must be at most {max} characters long",
|
|
6
|
+
"min": "The field '{field}' must be at least {min}",
|
|
7
|
+
"max": "The field '{field}' must be at most {max}",
|
|
8
|
+
"email": "The field '{field}' must be a valid email address",
|
|
9
|
+
"integer": "The field '{field}' must be an integer",
|
|
10
|
+
"string": "The field '{field}' must be a string",
|
|
11
|
+
"boolean": "The field '{field}' must be a boolean",
|
|
12
|
+
"enum": "The field '{field}' must be one of: {enum}",
|
|
13
|
+
"format": "The field '{field}' format is invalid",
|
|
14
|
+
"pattern": "The field '{field}' does not match the required pattern",
|
|
15
|
+
"unique": "The field '{field}' must be unique",
|
|
16
|
+
"exists": "The field '{field}' does not exist",
|
|
17
|
+
"invalid": "The field '{field}' is invalid",
|
|
18
|
+
"tooShort": "The field '{field}' is too short",
|
|
19
|
+
"tooLong": "The field '{field}' is too long",
|
|
20
|
+
"invalidEmail": "Please provide a valid email address",
|
|
21
|
+
"weakPassword": "Password must be at least 6 characters long",
|
|
22
|
+
"invalidRole": "Role must be one of: admin, user, moderator"
|
|
23
|
+
},
|
|
24
|
+
"errors": {
|
|
25
|
+
"validation": "Validation Error",
|
|
26
|
+
"server": "Internal Server Error",
|
|
27
|
+
"notFound": "Resource not found",
|
|
28
|
+
"unauthorized": "Unauthorized access",
|
|
29
|
+
"forbidden": "Access forbidden",
|
|
30
|
+
"conflict": "Resource conflict",
|
|
31
|
+
"badRequest": "Bad Request"
|
|
32
|
+
},
|
|
33
|
+
"messages": {
|
|
34
|
+
"userCreated": "User created successfully",
|
|
35
|
+
"userUpdated": "User updated successfully",
|
|
36
|
+
"userDeleted": "User deleted successfully",
|
|
37
|
+
"userNotFound": "User not found",
|
|
38
|
+
"emailExists": "Email already exists",
|
|
39
|
+
"invalidCredentials": "Invalid credentials",
|
|
40
|
+
"accessDenied": "Access denied",
|
|
41
|
+
"languageChanged": "Language changed to {language}",
|
|
42
|
+
"adminCreated": "Admin created successfully",
|
|
43
|
+
"adminUpdated": "Admin updated successfully",
|
|
44
|
+
"adminDeleted": "Admin deleted successfully",
|
|
45
|
+
"adminNotFound": "Admin not found",
|
|
46
|
+
"twoFactorEnabled": "Two-factor authentication enabled",
|
|
47
|
+
"twoFactorDisabled": "Two-factor authentication disabled",
|
|
48
|
+
"phoneVerified": "Phone number verified successfully",
|
|
49
|
+
"invalidVerificationCode": "Invalid verification code",
|
|
50
|
+
"verificationCodeExpired": "Verification code has expired"
|
|
51
|
+
}
|
|
52
|
+
}
|