@starklabs/backend-core 1.1.0 → 1.2.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 +21 -0
- package/README.md +7 -7
- package/dist/js/config/cloudinary.js +18 -0
- package/dist/js/config/config.js +11 -0
- package/dist/js/config/duration.js +22 -0
- package/dist/js/core/app.js +122 -0
- package/dist/js/core/auth/OTP.js +115 -0
- package/dist/js/core/auth/auth.controller.js +63 -0
- package/dist/js/core/auth/auth.service.js +290 -0
- package/dist/js/core/auth/auth.validation.js +95 -0
- package/dist/js/core/crud/crud.controller.js +95 -0
- package/dist/js/core/crud/crud.service.js +296 -0
- package/dist/js/core/index.js +3 -0
- package/dist/js/index.js +44 -55
- package/dist/js/lib/db.js +40 -0
- package/dist/js/lib/field.types.js +174 -0
- package/dist/js/lib/model.factory.js +19 -0
- package/dist/js/lib/model.registry.js +4 -0
- package/dist/js/lib/schema.builder.js +35 -0
- package/dist/js/lib/zod.validations.js +247 -0
- package/dist/js/middleware/auth.middleware.js +51 -0
- package/dist/js/middleware/error.middleware.js +28 -0
- package/dist/js/middleware/socket.middleware.js +29 -0
- package/dist/js/utils/AppLog.js +2 -1
- package/dist/js/utils/deleteFile.js +22 -0
- package/dist/js/utils/index.js +10 -1
- package/dist/js/utils/jwt.js +12 -20
- package/dist/js/utils/libsodium.js +19 -3
- package/dist/js/utils/rateLimiter.js +25 -0
- package/dist/js/utils/uploadFile.js +43 -0
- package/handlerMap.js +33 -0
- package/package.json +18 -5
- package/test.js +36 -0
- package/dist/cjs/db.cjs +0 -17
- package/dist/cjs/index.cjs +0 -59
- package/dist/cjs/utils/AppError.cjs +0 -13
- package/dist/cjs/utils/AppLog.cjs +0 -13
- package/dist/cjs/utils/asyncHandler.cjs +0 -6
- package/dist/cjs/utils/jwt.cjs +0 -38
- package/dist/cjs/utils/libsodium.cjs +0 -145
- package/dist/cjs/utils/successResponse.cjs +0 -13
- package/dist/js/db.js +0 -19
package/.env.example
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# development@v3
|
|
2
|
+
PORT=4000
|
|
3
|
+
API_VERSION=1 # must be an integer
|
|
4
|
+
|
|
5
|
+
# Duration format: 1m | 5m | 10m | 15m | 30m | 1h | 6h | 12h | 1d | 3d | 7d | 14d | 30d
|
|
6
|
+
TOKEN_EXPIRY=7d
|
|
7
|
+
JWT_SECRET=
|
|
8
|
+
MASTER_KEY=
|
|
9
|
+
ISOFFLINE=true
|
|
10
|
+
ENV=development
|
|
11
|
+
INTERNAL_ROLES=["developer","admin"]
|
|
12
|
+
RESEND_API_KEY=
|
|
13
|
+
|
|
14
|
+
# Duration format: 1m | 5m | 10m | 15m | 30m | 1h | 6h | 12h | 1d | 3d | 7d | 14d | 30d
|
|
15
|
+
RATE_LIMIT_DURATION=15m
|
|
16
|
+
RATE_LIMIT_REQ=200
|
|
17
|
+
RATE_LIMIT_MSG="Too many requests, please try again later."
|
|
18
|
+
CLOUDINARY_API_SECRET=
|
|
19
|
+
CLOUDINARY_API_KEY=5
|
|
20
|
+
CLOUDINARY_CLOUD_NAME=
|
|
21
|
+
CLOUDINARY_FOLDER_NAME=uploads
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ npm install @starklabs/backend-core
|
|
|
23
23
|
The following environment variables are required:
|
|
24
24
|
|
|
25
25
|
```env
|
|
26
|
-
|
|
26
|
+
MONGODB_URI=mongodb://localhost:27017
|
|
27
27
|
DB_NAME=your_database_name
|
|
28
28
|
MASTER_KEY=your_base64_encoded_32_byte_key
|
|
29
29
|
JWT_SECRET=your_jwt_secret_string
|
|
@@ -62,7 +62,7 @@ import starkAuth, { crypto, AppError, AppLog, asyncHandler } from '@starklabs/ba
|
|
|
62
62
|
|
|
63
63
|
// Initialize
|
|
64
64
|
const auth = await starkAuth.create({
|
|
65
|
-
|
|
65
|
+
MONGODB_URI: process.env.MONGODB_URI,
|
|
66
66
|
DB_NAME: process.env.DB_NAME,
|
|
67
67
|
MASTER_KEY: process.env.MASTER_KEY,
|
|
68
68
|
JWT_SECRET: process.env.JWT_SECRET,
|
|
@@ -97,7 +97,7 @@ const starkAuth = require('@starklabs/backend-core');
|
|
|
97
97
|
|
|
98
98
|
// Initialize
|
|
99
99
|
const auth = await starkAuth.create({
|
|
100
|
-
|
|
100
|
+
MONGODB_URI: process.env.MONGODB_URI,
|
|
101
101
|
DB_NAME: process.env.DB_NAME,
|
|
102
102
|
MASTER_KEY: process.env.MASTER_KEY,
|
|
103
103
|
JWT_SECRET: process.env.JWT_SECRET,
|
|
@@ -118,7 +118,7 @@ const payload = auth.verifyJWT(token);
|
|
|
118
118
|
Initializes database connection and returns a new StarkAuth instance.
|
|
119
119
|
|
|
120
120
|
**Parameters:**
|
|
121
|
-
- `config.
|
|
121
|
+
- `config.MONGODB_URI` (string): MongoDB connection URI
|
|
122
122
|
- `config.DB_NAME` (string): Database name
|
|
123
123
|
- `config.MASTER_KEY` (string): Base64-encoded 32-byte encryption key
|
|
124
124
|
- `config.JWT_SECRET` (string): Secret for JWT signing
|
|
@@ -128,7 +128,7 @@ Initializes database connection and returns a new StarkAuth instance.
|
|
|
128
128
|
|
|
129
129
|
```javascript
|
|
130
130
|
const auth = await starkAuth.create({
|
|
131
|
-
|
|
131
|
+
MONGODB_URI: 'mongodb://localhost:27017',
|
|
132
132
|
DB_NAME: 'myapp',
|
|
133
133
|
MASTER_KEY: 'eZkZDoy2s+DvGe44QGa7AdU41nKhglEaIjFsxfQCKao=',
|
|
134
134
|
JWT_SECRET: 'your-secret-key',
|
|
@@ -335,7 +335,7 @@ let auth;
|
|
|
335
335
|
app.use(async (req, res, next) => {
|
|
336
336
|
if (!auth) {
|
|
337
337
|
auth = await starkAuth.create({
|
|
338
|
-
|
|
338
|
+
MONGODB_URI: process.env.MONGODB_URI,
|
|
339
339
|
DB_NAME: process.env.DB_NAME,
|
|
340
340
|
MASTER_KEY: process.env.MASTER_KEY,
|
|
341
341
|
JWT_SECRET: process.env.JWT_SECRET,
|
|
@@ -456,7 +456,7 @@ Ensure `JWT_EXPIRY` is one of the supported values: `"2m"`, `"10m"`, `"1h"`, `"6
|
|
|
456
456
|
### "Error while connecting!"
|
|
457
457
|
Check that:
|
|
458
458
|
- MongoDB is running and accessible
|
|
459
|
-
- `
|
|
459
|
+
- `MONGODB_URI` is correct
|
|
460
460
|
- Network/firewall allows connection
|
|
461
461
|
|
|
462
462
|
### "Invalid or expired token"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import multer from "multer";
|
|
2
|
+
import { v2 as cloudinary } from "cloudinary";
|
|
3
|
+
|
|
4
|
+
const upload = multer({ storage: multer.memoryStorage() });
|
|
5
|
+
|
|
6
|
+
const configureCloudinary = ({
|
|
7
|
+
cloudinaryAPIKey,
|
|
8
|
+
cloudinaryCloudName,
|
|
9
|
+
cloudinaryAPISecret,
|
|
10
|
+
}) => {
|
|
11
|
+
cloudinary.config({
|
|
12
|
+
cloud_name: cloudinaryCloudName,
|
|
13
|
+
api_key: cloudinaryAPIKey,
|
|
14
|
+
api_secret: cloudinaryAPISecret,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { cloudinary, configureCloudinary, upload };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const DURATIONS = {
|
|
2
|
+
"1m": 60 * 1000,
|
|
3
|
+
"5m": 5 * 60 * 1000,
|
|
4
|
+
"10m": 10 * 60 * 1000,
|
|
5
|
+
"15m": 15 * 60 * 1000,
|
|
6
|
+
"30m": 30 * 60 * 1000,
|
|
7
|
+
"1h": 60 * 60 * 1000,
|
|
8
|
+
"6h": 6 * 60 * 60 * 1000,
|
|
9
|
+
"12h": 12 * 60 * 60 * 1000,
|
|
10
|
+
"1d": 24 * 60 * 60 * 1000,
|
|
11
|
+
"3d": 3 * 24 * 60 * 60 * 1000,
|
|
12
|
+
"7d": 7 * 24 * 60 * 60 * 1000,
|
|
13
|
+
"14d": 14 * 24 * 60 * 60 * 1000,
|
|
14
|
+
"30d": 30 * 24 * 60 * 60 * 1000,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const getDuration = (key) => {
|
|
18
|
+
if (!DURATIONS[key]) throw new Error("Invalid expiry key");
|
|
19
|
+
return DURATIONS[key];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default getDuration;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import AppLog from "../utils/AppLog.js";
|
|
3
|
+
import connectDB from "../lib/db.js";
|
|
4
|
+
import { crud, auth } from "./index.js";
|
|
5
|
+
import mongoose from "mongoose";
|
|
6
|
+
import createModel from "../lib/model.factory.js";
|
|
7
|
+
import registerModel from "../lib/model.registry.js";
|
|
8
|
+
import errorMiddleware from "../middleware/error.middleware.js";
|
|
9
|
+
import { getConfig, config } from "../config/config.js";
|
|
10
|
+
import AppError from "../utils/AppError.js";
|
|
11
|
+
import helmet from "helmet";
|
|
12
|
+
import cors from "cors";
|
|
13
|
+
import cookieParser from "cookie-parser";
|
|
14
|
+
import { apiLimiter } from "../utils/rateLimiter.js";
|
|
15
|
+
import { configureCloudinary } from "../config/cloudinary.js";
|
|
16
|
+
|
|
17
|
+
const app = express();
|
|
18
|
+
app.use(express.json());
|
|
19
|
+
app.use(express.urlencoded({ extended: true }));
|
|
20
|
+
|
|
21
|
+
app.use(express.json());
|
|
22
|
+
|
|
23
|
+
const registerCollections = (collections, apiVersion) => {
|
|
24
|
+
collections.forEach(
|
|
25
|
+
({
|
|
26
|
+
route,
|
|
27
|
+
modelName,
|
|
28
|
+
mongooseSchema,
|
|
29
|
+
otpSchema = false,
|
|
30
|
+
routes,
|
|
31
|
+
validations,
|
|
32
|
+
}) => {
|
|
33
|
+
// 1. create model dynamically
|
|
34
|
+
if (modelName && mongooseSchema) {
|
|
35
|
+
const model = createModel(modelName, mongooseSchema);
|
|
36
|
+
|
|
37
|
+
// register model
|
|
38
|
+
registerModel[modelName] = model;
|
|
39
|
+
|
|
40
|
+
if (otpSchema) {
|
|
41
|
+
const otpModel = createModel("otpUser", otpSchema);
|
|
42
|
+
registerModel["otpModel"] = otpModel;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 3. register CRUD routes
|
|
47
|
+
if (route === "auth") {
|
|
48
|
+
auth(route, routes, modelName, validations, apiVersion);
|
|
49
|
+
config.userModel = modelName;
|
|
50
|
+
} else {
|
|
51
|
+
crud(route, routes, modelName, validations, apiVersion);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const configValidation = ({ port, collections }) => {
|
|
58
|
+
if (!port) {
|
|
59
|
+
throw new Error("Port is missing in StarkCore.create({})");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!collections) {
|
|
63
|
+
throw new Error("Collections array is missing in StarkCore.create({})");
|
|
64
|
+
} else if (!Array.isArray(collections)) {
|
|
65
|
+
throw new Error("Collections must be an array datatype");
|
|
66
|
+
} else if (collections.length === 0) {
|
|
67
|
+
throw new Error("Collections shouldn't be empty in StarkCore.create({})");
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const startServer = async () => {
|
|
72
|
+
try {
|
|
73
|
+
const config = getConfig();
|
|
74
|
+
configValidation(config);
|
|
75
|
+
const {
|
|
76
|
+
port,
|
|
77
|
+
collections,
|
|
78
|
+
rateLimitDuration,
|
|
79
|
+
maxReqLimit,
|
|
80
|
+
rateLimitMsg,
|
|
81
|
+
cloudinaryAPIKey,
|
|
82
|
+
cloudinaryCloudName,
|
|
83
|
+
cloudinaryAPISecret,
|
|
84
|
+
apiVersion,
|
|
85
|
+
} = config;
|
|
86
|
+
|
|
87
|
+
app.use(cors());
|
|
88
|
+
app.use(helmet());
|
|
89
|
+
app.use(cookieParser());
|
|
90
|
+
app.use(apiLimiter({ rateLimitDuration, maxReqLimit, rateLimitMsg }));
|
|
91
|
+
|
|
92
|
+
connectDB();
|
|
93
|
+
|
|
94
|
+
configureCloudinary({
|
|
95
|
+
cloudinaryAPIKey,
|
|
96
|
+
cloudinaryCloudName,
|
|
97
|
+
cloudinaryAPISecret,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
registerCollections(collections, apiVersion);
|
|
101
|
+
|
|
102
|
+
app.use((req, res) => {
|
|
103
|
+
res.status(404).json({
|
|
104
|
+
success: false,
|
|
105
|
+
message: `Cannot ${req.method} ${req.originalUrl}`,
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
app.use(errorMiddleware);
|
|
110
|
+
|
|
111
|
+
app.listen(port, () => {
|
|
112
|
+
console.log(
|
|
113
|
+
`Server running at http://localhost:${port}/api/v${apiVersion}`,
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.log(err);
|
|
118
|
+
AppLog("X", "startServer", err.message);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export { startServer, app };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// module imports
|
|
2
|
+
import { Resend } from "resend";
|
|
3
|
+
import crypto from "crypto";
|
|
4
|
+
|
|
5
|
+
// custom imports
|
|
6
|
+
import AppError from "../../utils/AppError.js";
|
|
7
|
+
import { getConfig } from "../../config/config.js";
|
|
8
|
+
|
|
9
|
+
// sendOTP()
|
|
10
|
+
const sendOTP = async (email) => {
|
|
11
|
+
// OTP
|
|
12
|
+
const OTP = crypto.randomInt(100000, 1000000).toString();
|
|
13
|
+
const otpExpiry = Date.now() + 1000 * 60 * 5;
|
|
14
|
+
|
|
15
|
+
// config
|
|
16
|
+
const { isOffline, resendAPIKey } = getConfig();
|
|
17
|
+
if (isOffline) return { OTP, otpExpiry };
|
|
18
|
+
|
|
19
|
+
if (!resendAPIKey)
|
|
20
|
+
throw new AppError("resendAPIKey is missing in StarkCore.create({})");
|
|
21
|
+
|
|
22
|
+
// html
|
|
23
|
+
const htmlTemp = `<!DOCTYPE html>
|
|
24
|
+
<html>
|
|
25
|
+
<head>
|
|
26
|
+
<meta charset="UTF-8" />
|
|
27
|
+
<title>Email Verification</title>
|
|
28
|
+
</head>
|
|
29
|
+
<body style="margin:0; padding:0; font-family:Arial, sans-serif;">
|
|
30
|
+
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="padding:40px 0; padding-top: 0px;">
|
|
31
|
+
<tr>
|
|
32
|
+
<td align="center">
|
|
33
|
+
<table width="500" cellpadding="0" cellspacing="0" border="0" style="border-radius:12px; box-shadow:0 10px 25px rgba(0,0,0,0.08); padding:40px; padding-top: 10px;">
|
|
34
|
+
|
|
35
|
+
<tr>
|
|
36
|
+
<td align="center" style="padding-bottom:20px;">
|
|
37
|
+
<h2 style="margin:0; color:#111827; font-weight:600;">Stark Vault</h2>
|
|
38
|
+
</td>
|
|
39
|
+
</tr>
|
|
40
|
+
|
|
41
|
+
<tr>
|
|
42
|
+
<td align="center" style="padding-bottom:20px;">
|
|
43
|
+
<h1 style="margin:0; font-size:22px; color:#111827;">
|
|
44
|
+
Email Verification Code
|
|
45
|
+
</h1>
|
|
46
|
+
</td>
|
|
47
|
+
</tr>
|
|
48
|
+
|
|
49
|
+
<tr>
|
|
50
|
+
<td align="center" style="padding-bottom:30px; color:#6b7280; font-size:15px; line-height:1.6;">
|
|
51
|
+
Use the verification code below to complete your sign-in process.
|
|
52
|
+
This code will expire in <strong>5 minutes</strong>.
|
|
53
|
+
</td>
|
|
54
|
+
</tr>
|
|
55
|
+
|
|
56
|
+
<tr>
|
|
57
|
+
<td align="center" style="padding-bottom:30px;">
|
|
58
|
+
<div style="
|
|
59
|
+
display:inline-block;
|
|
60
|
+
padding:18px 32px;
|
|
61
|
+
font-size:28px;
|
|
62
|
+
letter-spacing:8px;
|
|
63
|
+
font-weight:bold;
|
|
64
|
+
color:#111827;
|
|
65
|
+
background:#f3f4f6;
|
|
66
|
+
border-radius:8px;
|
|
67
|
+
border:1px solid #e5e7eb;">
|
|
68
|
+
${OTP}
|
|
69
|
+
</div>
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
72
|
+
|
|
73
|
+
<tr>
|
|
74
|
+
<td align="center" style="color:#9ca3af; font-size:13px; line-height:1.5;">
|
|
75
|
+
If you didn’t request this code, you can safely ignore this email.
|
|
76
|
+
Never share your verification code with anyone.
|
|
77
|
+
</td>
|
|
78
|
+
</tr>
|
|
79
|
+
|
|
80
|
+
<tr>
|
|
81
|
+
<td style="padding-top:30px;">
|
|
82
|
+
<hr style="border:none; border-top:1px solid #e5e7eb;" />
|
|
83
|
+
</td>
|
|
84
|
+
</tr>
|
|
85
|
+
|
|
86
|
+
<tr>
|
|
87
|
+
<td align="center" style="padding-top:15px; font-size:12px; color:#9ca3af;">
|
|
88
|
+
© 2026 Your Company. All rights reserved.
|
|
89
|
+
</td>
|
|
90
|
+
</tr>
|
|
91
|
+
|
|
92
|
+
</table>
|
|
93
|
+
</td>
|
|
94
|
+
</tr>
|
|
95
|
+
</table>
|
|
96
|
+
</body>
|
|
97
|
+
</html>`;
|
|
98
|
+
|
|
99
|
+
const resend = new Resend(resendAPIKey);
|
|
100
|
+
const { data, error } = await resend.emails.send({
|
|
101
|
+
from: "Stark Vault <noreply@vault.musastark.space>",
|
|
102
|
+
to: email,
|
|
103
|
+
subject: "OTP - StarkVault",
|
|
104
|
+
html: htmlTemp,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (error) {
|
|
108
|
+
console.log("[OTP.js] 102: ", error);
|
|
109
|
+
throw new AppError(error.message, 409);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { OTP, otpExpiry };
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default sendOTP;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { app } from "../app.js";
|
|
2
|
+
import mongoose, { model } from "mongoose";
|
|
3
|
+
import asyncHandler from "../../utils/asyncHandler.js";
|
|
4
|
+
import authService from "./auth.service.js";
|
|
5
|
+
import registerModel from "../../lib/model.registry.js";
|
|
6
|
+
import getDuration from "../../config/duration.js";
|
|
7
|
+
import AppError from "../../utils/AppError.js";
|
|
8
|
+
import zodValidations from "../../lib/zod.validations.js";
|
|
9
|
+
import z from "zod";
|
|
10
|
+
import { getConfig } from "../../config/config.js";
|
|
11
|
+
|
|
12
|
+
const auth = (route, routes, modelName, validations, apiVersion) => {
|
|
13
|
+
const { ENV, tokenExpiry } = getConfig();
|
|
14
|
+
|
|
15
|
+
routes.forEach((el) => {
|
|
16
|
+
app[el.method](
|
|
17
|
+
`/api/v${apiVersion}/${route}${el.path}`,
|
|
18
|
+
asyncHandler(async (req, res) => {
|
|
19
|
+
const Model = registerModel[modelName];
|
|
20
|
+
if (!Model) throw new Error(`Model not found for endpoint: ${el.path}`);
|
|
21
|
+
const OTPModel = registerModel["otpModel"];
|
|
22
|
+
if (!OTPModel)
|
|
23
|
+
throw new AppError("otpSchema is missing in auth collection");
|
|
24
|
+
|
|
25
|
+
const zodObj = z.object(validations[el.handler]);
|
|
26
|
+
const isValid = await zodObj.safeParse(req.body || {});
|
|
27
|
+
|
|
28
|
+
if (!isValid.success) {
|
|
29
|
+
const issue = isValid.error.issues[0];
|
|
30
|
+
|
|
31
|
+
if (issue.code === "invalid_type")
|
|
32
|
+
throw new AppError(`${issue.path.join(".")} is required`);
|
|
33
|
+
|
|
34
|
+
throw new AppError(issue.message, 409);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// send data to service
|
|
38
|
+
const result = await authService[el.handler]({
|
|
39
|
+
body: isValid.data,
|
|
40
|
+
Model,
|
|
41
|
+
OTPModel,
|
|
42
|
+
});
|
|
43
|
+
if (result?.token) {
|
|
44
|
+
res.cookie("authToken", result.token, {
|
|
45
|
+
httpOnly: true,
|
|
46
|
+
maxAge: getDuration(tokenExpiry),
|
|
47
|
+
sameSite: ENV === "production" ? "none" : "lax",
|
|
48
|
+
secure: ENV === "production" ? true : false,
|
|
49
|
+
domain: ENV === "production" ? ".musastark.space" : undefined,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return res.json({
|
|
54
|
+
success: true,
|
|
55
|
+
user: result?.user,
|
|
56
|
+
message: result?.msg,
|
|
57
|
+
});
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default auth;
|