powr-sdk-api 4.8.8 → 4.9.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/dist/routes/auth.js +74 -20
- package/dist/routes/users.js +5 -1
- package/dist/utils/phone.js +94 -0
- package/package.json +74 -72
package/dist/routes/auth.js
CHANGED
|
@@ -20,6 +20,57 @@ const {
|
|
|
20
20
|
const {
|
|
21
21
|
getClientIp
|
|
22
22
|
} = require("../utils/getClientIp");
|
|
23
|
+
const {
|
|
24
|
+
normalizePhone,
|
|
25
|
+
getPhoneLookupVariants
|
|
26
|
+
} = require("../utils/phone");
|
|
27
|
+
function buildIdentifierQuery({
|
|
28
|
+
username,
|
|
29
|
+
phoneOrEmail
|
|
30
|
+
}) {
|
|
31
|
+
if (username) {
|
|
32
|
+
return {
|
|
33
|
+
username
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (!phoneOrEmail) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (phoneOrEmail.includes("@")) {
|
|
40
|
+
return {
|
|
41
|
+
$or: [{
|
|
42
|
+
phoneNumber: phoneOrEmail
|
|
43
|
+
}, {
|
|
44
|
+
email: phoneOrEmail
|
|
45
|
+
}]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const variants = getPhoneLookupVariants(phoneOrEmail);
|
|
49
|
+
return {
|
|
50
|
+
phoneNumber: {
|
|
51
|
+
$in: variants
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function resolveStoredContact(phoneOrEmail) {
|
|
56
|
+
if (!phoneOrEmail) {
|
|
57
|
+
return {
|
|
58
|
+
email: "",
|
|
59
|
+
phoneNumber: ""
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (phoneOrEmail.includes("@")) {
|
|
63
|
+
return {
|
|
64
|
+
email: phoneOrEmail,
|
|
65
|
+
phoneNumber: phoneOrEmail
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const phoneNumber = normalizePhone(phoneOrEmail);
|
|
69
|
+
return {
|
|
70
|
+
email: "",
|
|
71
|
+
phoneNumber
|
|
72
|
+
};
|
|
73
|
+
}
|
|
23
74
|
|
|
24
75
|
// Register User
|
|
25
76
|
router.post("/register", async (req, res) => {
|
|
@@ -50,15 +101,16 @@ router.post("/register", async (req, res) => {
|
|
|
50
101
|
message: "Full name is required"
|
|
51
102
|
});
|
|
52
103
|
}
|
|
53
|
-
let q =
|
|
54
|
-
username
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
104
|
+
let q = buildIdentifierQuery({
|
|
105
|
+
username,
|
|
106
|
+
phoneOrEmail
|
|
107
|
+
});
|
|
108
|
+
if (!q) {
|
|
109
|
+
return res.status(400).json({
|
|
110
|
+
success: false,
|
|
111
|
+
message: "Username or phone number or email is required"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
62
114
|
console.log(q);
|
|
63
115
|
const db = await getDb();
|
|
64
116
|
const existingUser = await db.collection("users").findOne(q);
|
|
@@ -70,10 +122,11 @@ router.post("/register", async (req, res) => {
|
|
|
70
122
|
}
|
|
71
123
|
const saltRounds = 10;
|
|
72
124
|
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
|
125
|
+
const contact = resolveStoredContact(phoneOrEmail);
|
|
73
126
|
const newUser = {
|
|
74
127
|
fullName,
|
|
75
|
-
email:
|
|
76
|
-
phoneNumber:
|
|
128
|
+
email: contact.email,
|
|
129
|
+
phoneNumber: contact.phoneNumber,
|
|
77
130
|
username,
|
|
78
131
|
password: hashedPassword,
|
|
79
132
|
createdAt: new Date()
|
|
@@ -126,15 +179,16 @@ router.post("/login", async (req, res) => {
|
|
|
126
179
|
message: "Password is required"
|
|
127
180
|
});
|
|
128
181
|
}
|
|
129
|
-
let q =
|
|
130
|
-
username
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
182
|
+
let q = buildIdentifierQuery({
|
|
183
|
+
username,
|
|
184
|
+
phoneOrEmail
|
|
185
|
+
});
|
|
186
|
+
if (!q) {
|
|
187
|
+
return res.status(400).json({
|
|
188
|
+
success: false,
|
|
189
|
+
message: "Username or phone number or email is required"
|
|
190
|
+
});
|
|
191
|
+
}
|
|
138
192
|
console.log(q);
|
|
139
193
|
const db = await getDb();
|
|
140
194
|
const user = await db.collection("users").findOne(q);
|
package/dist/routes/users.js
CHANGED
|
@@ -14,6 +14,9 @@ const {
|
|
|
14
14
|
const {
|
|
15
15
|
getClientIp
|
|
16
16
|
} = require("../utils/getClientIp");
|
|
17
|
+
const {
|
|
18
|
+
normalizePhone
|
|
19
|
+
} = require("../utils/phone");
|
|
17
20
|
|
|
18
21
|
// Create User
|
|
19
22
|
router.post("/", verifyToken, async (req, res) => {
|
|
@@ -155,9 +158,10 @@ router.put("/:id", verifyToken, async (req, res) => {
|
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
// Update user
|
|
161
|
+
const normalizedPhone = phoneNumber && !String(phoneNumber).includes("@") ? normalizePhone(phoneNumber) : phoneNumber;
|
|
158
162
|
const updateData = {
|
|
159
163
|
...userData,
|
|
160
|
-
phoneNumber,
|
|
164
|
+
phoneNumber: normalizedPhone,
|
|
161
165
|
email,
|
|
162
166
|
updatedAt: new Date()
|
|
163
167
|
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
parsePhoneNumberFromString,
|
|
5
|
+
getCountries,
|
|
6
|
+
getCountryCallingCode
|
|
7
|
+
} = require('libphonenumber-js');
|
|
8
|
+
const DEFAULT_COUNTRY = 'IN';
|
|
9
|
+
const DEFAULT_COUNTRY_CODE = '91';
|
|
10
|
+
const CALLING_CODE_TO_COUNTRY = {
|
|
11
|
+
'91': 'IN',
|
|
12
|
+
'1': 'US',
|
|
13
|
+
'44': 'GB',
|
|
14
|
+
'971': 'AE',
|
|
15
|
+
'65': 'SG',
|
|
16
|
+
'61': 'AU'
|
|
17
|
+
};
|
|
18
|
+
function digitsOnly(value) {
|
|
19
|
+
return (value || '').replace(/\D/g, '');
|
|
20
|
+
}
|
|
21
|
+
function parsePhone(input, defaultCountry = DEFAULT_COUNTRY) {
|
|
22
|
+
var _parsed, _parsed2, _parsed3;
|
|
23
|
+
if (!input || typeof input !== 'string' || input.includes('@')) return null;
|
|
24
|
+
const trimmed = input.trim();
|
|
25
|
+
if (!trimmed) return null;
|
|
26
|
+
let parsed = parsePhoneNumberFromString(trimmed);
|
|
27
|
+
if (!((_parsed = parsed) !== null && _parsed !== void 0 && _parsed.isValid())) {
|
|
28
|
+
parsed = parsePhoneNumberFromString(trimmed, defaultCountry);
|
|
29
|
+
}
|
|
30
|
+
if (!((_parsed2 = parsed) !== null && _parsed2 !== void 0 && _parsed2.isValid()) && !trimmed.startsWith('+')) {
|
|
31
|
+
parsed = parsePhoneNumberFromString(`+${digitsOnly(trimmed)}`);
|
|
32
|
+
}
|
|
33
|
+
return (_parsed3 = parsed) !== null && _parsed3 !== void 0 && _parsed3.isValid() ? parsed : null;
|
|
34
|
+
}
|
|
35
|
+
function normalizePhone(input, defaultCountry = DEFAULT_COUNTRY) {
|
|
36
|
+
if (!input || typeof input !== 'string') return '';
|
|
37
|
+
if (input.includes('@')) return input.trim();
|
|
38
|
+
const parsed = parsePhone(input, defaultCountry);
|
|
39
|
+
return parsed ? parsed.format('E.164') : '';
|
|
40
|
+
}
|
|
41
|
+
function isValidPhone(input, defaultCountry = DEFAULT_COUNTRY) {
|
|
42
|
+
if (!input || typeof input !== 'string' || input.includes('@')) return false;
|
|
43
|
+
return !!parsePhone(input, defaultCountry);
|
|
44
|
+
}
|
|
45
|
+
function getLocalPhoneNumber(input, defaultCountry = DEFAULT_COUNTRY) {
|
|
46
|
+
const parsed = parsePhone(input, defaultCountry);
|
|
47
|
+
return parsed ? parsed.nationalNumber : digitsOnly(input).slice(-15);
|
|
48
|
+
}
|
|
49
|
+
function legacyIndiaVariants(input) {
|
|
50
|
+
const digits = digitsOnly(input);
|
|
51
|
+
if (!digits) return [];
|
|
52
|
+
const variants = new Set();
|
|
53
|
+
if (digits.length === 10) {
|
|
54
|
+
variants.add(digits);
|
|
55
|
+
variants.add(`91${digits}`);
|
|
56
|
+
variants.add(`+91${digits}`);
|
|
57
|
+
variants.add(`0${digits}`);
|
|
58
|
+
}
|
|
59
|
+
if (digits.length === 12 && digits.startsWith('91')) {
|
|
60
|
+
const local = digits.slice(2);
|
|
61
|
+
variants.add(local);
|
|
62
|
+
variants.add(`+${digits}`);
|
|
63
|
+
variants.add(digits);
|
|
64
|
+
variants.add(`0${local}`);
|
|
65
|
+
}
|
|
66
|
+
return [...variants];
|
|
67
|
+
}
|
|
68
|
+
function getPhoneLookupVariants(input, defaultCountry = DEFAULT_COUNTRY) {
|
|
69
|
+
if (!input || typeof input !== 'string') return [];
|
|
70
|
+
if (input.includes('@')) return [input.trim()];
|
|
71
|
+
const variants = new Set([input.trim()]);
|
|
72
|
+
const parsed = parsePhone(input, defaultCountry);
|
|
73
|
+
if (parsed) {
|
|
74
|
+
const e164 = parsed.format('E.164');
|
|
75
|
+
variants.add(e164);
|
|
76
|
+
variants.add(e164.replace(/^\+/, ''));
|
|
77
|
+
variants.add(parsed.nationalNumber);
|
|
78
|
+
variants.add(`+${parsed.countryCallingCode}${parsed.nationalNumber}`);
|
|
79
|
+
variants.add(`${parsed.countryCallingCode}${parsed.nationalNumber}`);
|
|
80
|
+
}
|
|
81
|
+
legacyIndiaVariants(input).forEach(v => variants.add(v));
|
|
82
|
+
return [...variants].filter(Boolean);
|
|
83
|
+
}
|
|
84
|
+
module.exports = {
|
|
85
|
+
DEFAULT_COUNTRY,
|
|
86
|
+
DEFAULT_COUNTRY_CODE,
|
|
87
|
+
normalizePhone,
|
|
88
|
+
getLocalPhoneNumber,
|
|
89
|
+
isValidPhone,
|
|
90
|
+
getPhoneLookupVariants,
|
|
91
|
+
parsePhone,
|
|
92
|
+
getCountries,
|
|
93
|
+
getCountryCallingCode
|
|
94
|
+
};
|
package/package.json
CHANGED
|
@@ -1,72 +1,74 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "powr-sdk-api",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Shared API core library for PowrStack projects. Zero dependencies - works with Express, Next.js API routes, and other frameworks. All features are optional and install only what you need.",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"files": [
|
|
8
|
-
"dist",
|
|
9
|
-
"README.md"
|
|
10
|
-
],
|
|
11
|
-
"scripts": {
|
|
12
|
-
"test": "jest --passWithNoTests",
|
|
13
|
-
"lint": "eslint .",
|
|
14
|
-
"build": "babel src -d dist",
|
|
15
|
-
"prepare": "npm run build",
|
|
16
|
-
"prepublishOnly": "npm run test"
|
|
17
|
-
},
|
|
18
|
-
"keywords": [
|
|
19
|
-
"api",
|
|
20
|
-
"express",
|
|
21
|
-
"nextjs",
|
|
22
|
-
"middleware",
|
|
23
|
-
"error-handling",
|
|
24
|
-
"response-formatting",
|
|
25
|
-
"logging",
|
|
26
|
-
"swagger",
|
|
27
|
-
"documentation",
|
|
28
|
-
"optional-dependencies",
|
|
29
|
-
"modular",
|
|
30
|
-
"framework-agnostic"
|
|
31
|
-
],
|
|
32
|
-
"author": "Lawazia Tech",
|
|
33
|
-
"license": "ISC",
|
|
34
|
-
"repository": {
|
|
35
|
-
"type": "git",
|
|
36
|
-
"url": "git+https://github.com/powrstack/powr-sdk-api.git"
|
|
37
|
-
},
|
|
38
|
-
"bugs": {
|
|
39
|
-
"url": "https://github.com/powrstack/powr-sdk-api/issues"
|
|
40
|
-
},
|
|
41
|
-
"homepage": "https://github.com/powrstack/powr-sdk-api#readme",
|
|
42
|
-
"dependencies": {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"@babel/
|
|
62
|
-
"@
|
|
63
|
-
"@
|
|
64
|
-
"@types/
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "powr-sdk-api",
|
|
3
|
+
"version": "4.9.0",
|
|
4
|
+
"description": "Shared API core library for PowrStack projects. Zero dependencies - works with Express, Next.js API routes, and other frameworks. All features are optional and install only what you need.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "jest --passWithNoTests",
|
|
13
|
+
"lint": "eslint .",
|
|
14
|
+
"build": "babel src -d dist",
|
|
15
|
+
"prepare": "npm run build",
|
|
16
|
+
"prepublishOnly": "npm run test"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"api",
|
|
20
|
+
"express",
|
|
21
|
+
"nextjs",
|
|
22
|
+
"middleware",
|
|
23
|
+
"error-handling",
|
|
24
|
+
"response-formatting",
|
|
25
|
+
"logging",
|
|
26
|
+
"swagger",
|
|
27
|
+
"documentation",
|
|
28
|
+
"optional-dependencies",
|
|
29
|
+
"modular",
|
|
30
|
+
"framework-agnostic"
|
|
31
|
+
],
|
|
32
|
+
"author": "Lawazia Tech",
|
|
33
|
+
"license": "ISC",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/powrstack/powr-sdk-api.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/powrstack/powr-sdk-api/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/powrstack/powr-sdk-api#readme",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"libphonenumber-js": "^1.12.9"
|
|
44
|
+
},
|
|
45
|
+
"optionalDependencies": {
|
|
46
|
+
"@aws-sdk/client-s3": "^3.787.0",
|
|
47
|
+
"@google-cloud/storage": "^7.16.0",
|
|
48
|
+
"axios": "^1.6.0",
|
|
49
|
+
"bcrypt": "^5.1.1",
|
|
50
|
+
"cron-parser": "^4.9.0",
|
|
51
|
+
"date-fns": "^4.1.0",
|
|
52
|
+
"express": "^4.18.2",
|
|
53
|
+
"jsonwebtoken": "^9.0.2",
|
|
54
|
+
"mongodb": "^6.3.0",
|
|
55
|
+
"multer": "^1.4.5-lts.1",
|
|
56
|
+
"nodemailer": "^6.10.0",
|
|
57
|
+
"swagger-jsdoc": "^6.2.8",
|
|
58
|
+
"winston": "^3.17.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@babel/cli": "^7.23.9",
|
|
62
|
+
"@babel/core": "^7.24.0",
|
|
63
|
+
"@babel/preset-env": "^7.24.0",
|
|
64
|
+
"@types/express": "^4.17.21",
|
|
65
|
+
"@types/swagger-jsdoc": "^6.0.4",
|
|
66
|
+
"@types/swagger-ui-express": "^4.1.6",
|
|
67
|
+
"eslint": "^8.57.0",
|
|
68
|
+
"jest": "^29.7.0",
|
|
69
|
+
"typescript": "^5.3.3"
|
|
70
|
+
},
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=14.0.0"
|
|
73
|
+
}
|
|
74
|
+
}
|