@infuro/cms-core 1.0.39 → 1.0.41
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/admin.cjs +214 -111
- package/dist/admin.js +215 -112
- package/dist/api.cjs +32 -32
- package/dist/api.d.cts +4 -1
- package/dist/api.d.ts +4 -1
- package/dist/api.js +1 -1
- package/dist/auth.cjs +12 -12
- package/dist/auth.js +1 -1
- package/dist/{chunk-SBRBR3VO.js → chunk-4BG7XYSH.js} +1 -0
- package/dist/{chunk-SFDZMGHD.js → chunk-EG4ICILK.js} +460 -102
- package/dist/{chunk-E6WVP7BK.cjs → chunk-ETZN4OQI.cjs} +1 -0
- package/dist/{chunk-ERJK5QXN.cjs → chunk-QU554KAZ.cjs} +461 -100
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +210 -198
- package/dist/index.d.cts +84 -68
- package/dist/index.d.ts +84 -68
- package/dist/index.js +3 -3
- package/package.json +1 -1
|
@@ -6189,6 +6189,225 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6189
6189
|
};
|
|
6190
6190
|
}
|
|
6191
6191
|
chunkUSNT2KNT_cjs.__name(createCrudByIdHandler, "createCrudByIdHandler");
|
|
6192
|
+
var VENDOR_INVITE_METADATA_TOKEN = "inviteToken";
|
|
6193
|
+
var VENDOR_INVITE_METADATA_EXPIRES = "inviteExpiresAt";
|
|
6194
|
+
var VENDOR_INVITE_METADATA_SENT = "inviteSentAt";
|
|
6195
|
+
async function loadOwnerVendorForUser(dataSource, vendorEntity, userId) {
|
|
6196
|
+
const repo = dataSource.getRepository(vendorEntity);
|
|
6197
|
+
const row = await repo.findOne({
|
|
6198
|
+
where: {
|
|
6199
|
+
userId,
|
|
6200
|
+
deleted: false
|
|
6201
|
+
}
|
|
6202
|
+
});
|
|
6203
|
+
if (!row) return null;
|
|
6204
|
+
const v = row;
|
|
6205
|
+
return {
|
|
6206
|
+
id: v.id,
|
|
6207
|
+
inviteStatus: v.inviteStatus,
|
|
6208
|
+
metadata: v.metadata && typeof v.metadata === "object" && !Array.isArray(v.metadata) ? v.metadata : null
|
|
6209
|
+
};
|
|
6210
|
+
}
|
|
6211
|
+
chunkUSNT2KNT_cjs.__name(loadOwnerVendorForUser, "loadOwnerVendorForUser");
|
|
6212
|
+
function invalidatedInviteMessage() {
|
|
6213
|
+
return "This invitation is no longer valid. Ask an admin to resend the invite.";
|
|
6214
|
+
}
|
|
6215
|
+
chunkUSNT2KNT_cjs.__name(invalidatedInviteMessage, "invalidatedInviteMessage");
|
|
6216
|
+
async function validateInviteTokenForActivation(dataSource, entityMap, token) {
|
|
6217
|
+
const trimmedToken = String(token ?? "").trim();
|
|
6218
|
+
if (!trimmedToken) {
|
|
6219
|
+
return {
|
|
6220
|
+
ok: false,
|
|
6221
|
+
error: "Invalid invite link. Token is missing."
|
|
6222
|
+
};
|
|
6223
|
+
}
|
|
6224
|
+
const userRepo = entityMap.users ? dataSource.getRepository(entityMap.users) : null;
|
|
6225
|
+
if (entityMap.vendors && userRepo) {
|
|
6226
|
+
const vendorMatch = await findVendorByInviteToken(dataSource, entityMap.vendors, trimmedToken);
|
|
6227
|
+
if (vendorMatch) {
|
|
6228
|
+
const vendorRepo = dataSource.getRepository(entityMap.vendors);
|
|
6229
|
+
const freshVendor = await vendorRepo.findOne({
|
|
6230
|
+
where: {
|
|
6231
|
+
id: vendorMatch.id,
|
|
6232
|
+
deleted: false
|
|
6233
|
+
}
|
|
6234
|
+
});
|
|
6235
|
+
const v = freshVendor;
|
|
6236
|
+
if (!v?.userId) {
|
|
6237
|
+
return {
|
|
6238
|
+
ok: false,
|
|
6239
|
+
error: invalidatedInviteMessage()
|
|
6240
|
+
};
|
|
6241
|
+
}
|
|
6242
|
+
if (v.inviteStatus === "accepted") {
|
|
6243
|
+
return {
|
|
6244
|
+
ok: false,
|
|
6245
|
+
error: "This invitation has already been accepted"
|
|
6246
|
+
};
|
|
6247
|
+
}
|
|
6248
|
+
if (v.inviteStatus === "expired") {
|
|
6249
|
+
await vendorRepo.update(v.id, {
|
|
6250
|
+
inviteStatus: "pending"
|
|
6251
|
+
});
|
|
6252
|
+
v.inviteStatus = "pending";
|
|
6253
|
+
}
|
|
6254
|
+
const user2 = await userRepo.findOne({
|
|
6255
|
+
where: {
|
|
6256
|
+
id: v.userId
|
|
6257
|
+
},
|
|
6258
|
+
select: [
|
|
6259
|
+
"id",
|
|
6260
|
+
"email",
|
|
6261
|
+
"blocked",
|
|
6262
|
+
"password"
|
|
6263
|
+
]
|
|
6264
|
+
});
|
|
6265
|
+
if (!user2) return {
|
|
6266
|
+
ok: false,
|
|
6267
|
+
error: "User not found"
|
|
6268
|
+
};
|
|
6269
|
+
const hasPassword2 = !!user2.password;
|
|
6270
|
+
if (!user2.blocked && hasPassword2) {
|
|
6271
|
+
return {
|
|
6272
|
+
ok: false,
|
|
6273
|
+
error: "User is already active"
|
|
6274
|
+
};
|
|
6275
|
+
}
|
|
6276
|
+
return {
|
|
6277
|
+
ok: true,
|
|
6278
|
+
target: {
|
|
6279
|
+
userId: user2.id,
|
|
6280
|
+
email: String(user2.email),
|
|
6281
|
+
vendorId: v.id
|
|
6282
|
+
}
|
|
6283
|
+
};
|
|
6284
|
+
}
|
|
6285
|
+
}
|
|
6286
|
+
let email;
|
|
6287
|
+
try {
|
|
6288
|
+
email = Buffer.from(trimmedToken, "base64").toString("utf8").trim();
|
|
6289
|
+
} catch {
|
|
6290
|
+
return {
|
|
6291
|
+
ok: false,
|
|
6292
|
+
error: invalidatedInviteMessage()
|
|
6293
|
+
};
|
|
6294
|
+
}
|
|
6295
|
+
if (!email.includes("@")) {
|
|
6296
|
+
return {
|
|
6297
|
+
ok: false,
|
|
6298
|
+
error: invalidatedInviteMessage()
|
|
6299
|
+
};
|
|
6300
|
+
}
|
|
6301
|
+
if (!userRepo) {
|
|
6302
|
+
return {
|
|
6303
|
+
ok: false,
|
|
6304
|
+
error: "User not found"
|
|
6305
|
+
};
|
|
6306
|
+
}
|
|
6307
|
+
const user = await userRepo.findOne({
|
|
6308
|
+
where: {
|
|
6309
|
+
email
|
|
6310
|
+
},
|
|
6311
|
+
select: [
|
|
6312
|
+
"id",
|
|
6313
|
+
"email",
|
|
6314
|
+
"blocked",
|
|
6315
|
+
"password"
|
|
6316
|
+
]
|
|
6317
|
+
});
|
|
6318
|
+
if (!user) return {
|
|
6319
|
+
ok: false,
|
|
6320
|
+
error: "User not found"
|
|
6321
|
+
};
|
|
6322
|
+
const hasPassword = !!user.password;
|
|
6323
|
+
if (!user.blocked && hasPassword) {
|
|
6324
|
+
return {
|
|
6325
|
+
ok: false,
|
|
6326
|
+
error: "User is already active"
|
|
6327
|
+
};
|
|
6328
|
+
}
|
|
6329
|
+
if (entityMap.vendors) {
|
|
6330
|
+
const ownerVendor = await loadOwnerVendorForUser(dataSource, entityMap.vendors, user.id);
|
|
6331
|
+
if (ownerVendor && readVendorInviteToken(ownerVendor.metadata)) {
|
|
6332
|
+
return {
|
|
6333
|
+
ok: false,
|
|
6334
|
+
error: invalidatedInviteMessage()
|
|
6335
|
+
};
|
|
6336
|
+
}
|
|
6337
|
+
}
|
|
6338
|
+
return {
|
|
6339
|
+
ok: true,
|
|
6340
|
+
target: {
|
|
6341
|
+
userId: user.id,
|
|
6342
|
+
email: String(user.email)
|
|
6343
|
+
}
|
|
6344
|
+
};
|
|
6345
|
+
}
|
|
6346
|
+
chunkUSNT2KNT_cjs.__name(validateInviteTokenForActivation, "validateInviteTokenForActivation");
|
|
6347
|
+
function buildVendorInviteLink(baseUrl, token) {
|
|
6348
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
6349
|
+
return `${base}/admin/invite?token=${encodeURIComponent(token)}`;
|
|
6350
|
+
}
|
|
6351
|
+
chunkUSNT2KNT_cjs.__name(buildVendorInviteLink, "buildVendorInviteLink");
|
|
6352
|
+
function applyRotatingVendorInvite(existing) {
|
|
6353
|
+
const next = {
|
|
6354
|
+
...existing ?? {}
|
|
6355
|
+
};
|
|
6356
|
+
next[VENDOR_INVITE_METADATA_TOKEN] = crypto2.randomBytes(32).toString("hex");
|
|
6357
|
+
next[VENDOR_INVITE_METADATA_SENT] = (/* @__PURE__ */ new Date()).toISOString();
|
|
6358
|
+
delete next[VENDOR_INVITE_METADATA_EXPIRES];
|
|
6359
|
+
return next;
|
|
6360
|
+
}
|
|
6361
|
+
chunkUSNT2KNT_cjs.__name(applyRotatingVendorInvite, "applyRotatingVendorInvite");
|
|
6362
|
+
function clearVendorInviteMetadata(existing) {
|
|
6363
|
+
const next = {
|
|
6364
|
+
...existing ?? {}
|
|
6365
|
+
};
|
|
6366
|
+
delete next[VENDOR_INVITE_METADATA_TOKEN];
|
|
6367
|
+
delete next[VENDOR_INVITE_METADATA_EXPIRES];
|
|
6368
|
+
delete next[VENDOR_INVITE_METADATA_SENT];
|
|
6369
|
+
return Object.keys(next).length > 0 ? next : null;
|
|
6370
|
+
}
|
|
6371
|
+
chunkUSNT2KNT_cjs.__name(clearVendorInviteMetadata, "clearVendorInviteMetadata");
|
|
6372
|
+
function readVendorInviteToken(metadata) {
|
|
6373
|
+
const raw = metadata?.[VENDOR_INVITE_METADATA_TOKEN];
|
|
6374
|
+
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
|
6375
|
+
}
|
|
6376
|
+
chunkUSNT2KNT_cjs.__name(readVendorInviteToken, "readVendorInviteToken");
|
|
6377
|
+
async function findVendorByInviteToken(dataSource, vendorEntity, token) {
|
|
6378
|
+
const trimmed = token.trim();
|
|
6379
|
+
if (!trimmed) return null;
|
|
6380
|
+
const repo = dataSource.getRepository(vendorEntity);
|
|
6381
|
+
const row = await repo.createQueryBuilder("v").where("v.deleted = false").andWhere(`v.metadata->>'${VENDOR_INVITE_METADATA_TOKEN}' = :token`, {
|
|
6382
|
+
token: trimmed
|
|
6383
|
+
}).getOne();
|
|
6384
|
+
if (!row) return null;
|
|
6385
|
+
const v = row;
|
|
6386
|
+
return {
|
|
6387
|
+
id: v.id,
|
|
6388
|
+
userId: v.userId ?? null,
|
|
6389
|
+
inviteStatus: v.inviteStatus,
|
|
6390
|
+
metadata: v.metadata && typeof v.metadata === "object" && !Array.isArray(v.metadata) ? v.metadata : null
|
|
6391
|
+
};
|
|
6392
|
+
}
|
|
6393
|
+
chunkUSNT2KNT_cjs.__name(findVendorByInviteToken, "findVendorByInviteToken");
|
|
6394
|
+
async function completeVendorInviteAccept(dataSource, vendorEntity, vendorId) {
|
|
6395
|
+
const repo = dataSource.getRepository(vendorEntity);
|
|
6396
|
+
const row = await repo.findOne({
|
|
6397
|
+
where: {
|
|
6398
|
+
id: vendorId,
|
|
6399
|
+
deleted: false
|
|
6400
|
+
}
|
|
6401
|
+
});
|
|
6402
|
+
if (!row) return;
|
|
6403
|
+
const metadata = row.metadata;
|
|
6404
|
+
const cleared = clearVendorInviteMetadata(metadata);
|
|
6405
|
+
await repo.update(vendorId, {
|
|
6406
|
+
metadata: cleared,
|
|
6407
|
+
inviteStatus: "accepted"
|
|
6408
|
+
});
|
|
6409
|
+
}
|
|
6410
|
+
chunkUSNT2KNT_cjs.__name(completeVendorInviteAccept, "completeVendorInviteAccept");
|
|
6192
6411
|
|
|
6193
6412
|
// src/api/auth-handlers.ts
|
|
6194
6413
|
function createForgotPasswordHandler(config) {
|
|
@@ -6324,49 +6543,27 @@ function createInviteAcceptHandler(config) {
|
|
|
6324
6543
|
}, {
|
|
6325
6544
|
status: 400
|
|
6326
6545
|
});
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
email = Buffer.from(token, "base64").toString("utf8");
|
|
6330
|
-
} catch {
|
|
6546
|
+
const validation = await validateInviteTokenForActivation(dataSource, entityMap, token);
|
|
6547
|
+
if (!validation.ok) {
|
|
6331
6548
|
return json({
|
|
6332
|
-
error:
|
|
6333
|
-
}, {
|
|
6334
|
-
status: 400
|
|
6335
|
-
});
|
|
6336
|
-
}
|
|
6337
|
-
const userRepo = dataSource.getRepository(entityMap.users);
|
|
6338
|
-
const user = await userRepo.findOne({
|
|
6339
|
-
where: {
|
|
6340
|
-
email
|
|
6341
|
-
},
|
|
6342
|
-
select: [
|
|
6343
|
-
"id",
|
|
6344
|
-
"blocked",
|
|
6345
|
-
"password"
|
|
6346
|
-
]
|
|
6347
|
-
});
|
|
6348
|
-
if (!user) return json({
|
|
6349
|
-
error: "User not found"
|
|
6350
|
-
}, {
|
|
6351
|
-
status: 400
|
|
6352
|
-
});
|
|
6353
|
-
const hasPassword = !!user.password;
|
|
6354
|
-
if (!user.blocked && hasPassword) {
|
|
6355
|
-
return json({
|
|
6356
|
-
error: "User is already active"
|
|
6549
|
+
error: validation.error
|
|
6357
6550
|
}, {
|
|
6358
6551
|
status: 400
|
|
6359
6552
|
});
|
|
6360
6553
|
}
|
|
6554
|
+
const { userId, email, vendorId } = validation.target;
|
|
6361
6555
|
if (entityMap.contacts) {
|
|
6362
|
-
await linkUnclaimedContactToUser(dataSource, entityMap.contacts,
|
|
6556
|
+
await linkUnclaimedContactToUser(dataSource, entityMap.contacts, userId, email);
|
|
6363
6557
|
}
|
|
6364
|
-
if (beforeActivate) await beforeActivate(email,
|
|
6558
|
+
if (beforeActivate) await beforeActivate(email, userId);
|
|
6365
6559
|
const hashedPassword = await hashPassword(password);
|
|
6366
|
-
await
|
|
6560
|
+
await dataSource.getRepository(entityMap.users).update(userId, {
|
|
6367
6561
|
password: hashedPassword,
|
|
6368
6562
|
blocked: false
|
|
6369
6563
|
});
|
|
6564
|
+
if (vendorId != null && entityMap.vendors) {
|
|
6565
|
+
await completeVendorInviteAccept(dataSource, entityMap.vendors, vendorId);
|
|
6566
|
+
}
|
|
6370
6567
|
return json({
|
|
6371
6568
|
message: "User account activated successfully"
|
|
6372
6569
|
}, {
|
|
@@ -6382,6 +6579,35 @@ function createInviteAcceptHandler(config) {
|
|
|
6382
6579
|
}, "POST");
|
|
6383
6580
|
}
|
|
6384
6581
|
chunkUSNT2KNT_cjs.__name(createInviteAcceptHandler, "createInviteAcceptHandler");
|
|
6582
|
+
function createInviteValidateHandler(config) {
|
|
6583
|
+
const { dataSource, entityMap, json } = config;
|
|
6584
|
+
return /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async function GET(request) {
|
|
6585
|
+
try {
|
|
6586
|
+
const token = new URL(request.url).searchParams.get("token");
|
|
6587
|
+
const validation = await validateInviteTokenForActivation(dataSource, entityMap, token ?? "");
|
|
6588
|
+
if (!validation.ok) {
|
|
6589
|
+
return json({
|
|
6590
|
+
valid: false,
|
|
6591
|
+
error: validation.error
|
|
6592
|
+
}, {
|
|
6593
|
+
status: 400
|
|
6594
|
+
});
|
|
6595
|
+
}
|
|
6596
|
+
return json({
|
|
6597
|
+
valid: true
|
|
6598
|
+
});
|
|
6599
|
+
} catch (err) {
|
|
6600
|
+
console.error("[users/invite GET] validate failed", err);
|
|
6601
|
+
return json({
|
|
6602
|
+
valid: false,
|
|
6603
|
+
error: "Server Error"
|
|
6604
|
+
}, {
|
|
6605
|
+
status: 500
|
|
6606
|
+
});
|
|
6607
|
+
}
|
|
6608
|
+
}, "GET");
|
|
6609
|
+
}
|
|
6610
|
+
chunkUSNT2KNT_cjs.__name(createInviteValidateHandler, "createInviteValidateHandler");
|
|
6385
6611
|
function createChangePasswordHandler(config) {
|
|
6386
6612
|
const { dataSource, entityMap, json, comparePassword, hashPassword, getSession, minPasswordLength = 6, beforeUpdate } = config;
|
|
6387
6613
|
return /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async function POST(request) {
|
|
@@ -6460,12 +6686,22 @@ function createUserAuthApiRouter(config) {
|
|
|
6460
6686
|
const forgot = createForgotPasswordHandler(config);
|
|
6461
6687
|
const setPass = createSetPasswordHandler(config);
|
|
6462
6688
|
const invite = createInviteAcceptHandler(config);
|
|
6689
|
+
const inviteValidate = createInviteValidateHandler(config);
|
|
6463
6690
|
const changePass = config.getSession ? createChangePasswordHandler({
|
|
6464
6691
|
...config,
|
|
6465
6692
|
getSession: config.getSession,
|
|
6466
6693
|
beforeUpdate: config.beforeChangePasswordUpdate
|
|
6467
6694
|
}) : null;
|
|
6468
6695
|
return {
|
|
6696
|
+
async GET(req, pathname) {
|
|
6697
|
+
const path2 = pathname.replace(/\/$/, "");
|
|
6698
|
+
if (path2 === "invite") return inviteValidate(req);
|
|
6699
|
+
return config.json({
|
|
6700
|
+
error: "Not found"
|
|
6701
|
+
}, {
|
|
6702
|
+
status: 404
|
|
6703
|
+
});
|
|
6704
|
+
},
|
|
6469
6705
|
async POST(req, pathname) {
|
|
6470
6706
|
const path2 = pathname.replace(/\/$/, "");
|
|
6471
6707
|
if (!USER_AUTH_PATHS.includes(path2)) {
|
|
@@ -8871,13 +9107,31 @@ function createUsersApiHandlers(config) {
|
|
|
8871
9107
|
}, {
|
|
8872
9108
|
status: 404
|
|
8873
9109
|
});
|
|
8874
|
-
|
|
8875
|
-
const inviteLink = `${baseUrl}/admin/invite?token=${emailToken}`;
|
|
8876
|
-
await trySendInviteEmail(user.email, inviteLink, user.name ?? "");
|
|
9110
|
+
let inviteLink = `${baseUrl}/admin/invite?token=${Buffer.from(user.email).toString("base64")}`;
|
|
8877
9111
|
if (entityMap.vendors) {
|
|
9112
|
+
const vendorRepo = dataSource.getRepository(entityMap.vendors);
|
|
9113
|
+
const vendor = await vendorRepo.findOne({
|
|
9114
|
+
where: {
|
|
9115
|
+
userId: user.id,
|
|
9116
|
+
deleted: false
|
|
9117
|
+
}
|
|
9118
|
+
});
|
|
9119
|
+
const v = vendor;
|
|
9120
|
+
if (v && (v.inviteStatus === "pending" || v.inviteStatus === "expired")) {
|
|
9121
|
+
const rotatedMetadata = applyRotatingVendorInvite(v.metadata);
|
|
9122
|
+
await vendorRepo.update(v.id, {
|
|
9123
|
+
metadata: rotatedMetadata,
|
|
9124
|
+
inviteStatus: "pending"
|
|
9125
|
+
});
|
|
9126
|
+
const token = readVendorInviteToken(rotatedMetadata);
|
|
9127
|
+
if (token) {
|
|
9128
|
+
inviteLink = buildVendorInviteLink(baseUrl, token);
|
|
9129
|
+
}
|
|
9130
|
+
}
|
|
8878
9131
|
const { markVendorInvitePendingForUser } = await import('./vendor-invite-status-77O7ZQ3U.cjs');
|
|
8879
9132
|
await markVendorInvitePendingForUser(dataSource, entityMap.vendors, user.id);
|
|
8880
9133
|
}
|
|
9134
|
+
await trySendInviteEmail(user.email, inviteLink, user.name ?? "");
|
|
8881
9135
|
return json({
|
|
8882
9136
|
message: "New invite link generated successfully",
|
|
8883
9137
|
inviteLink
|
|
@@ -12012,6 +12266,13 @@ function createVendorOnboardHandlers(config) {
|
|
|
12012
12266
|
status: 400
|
|
12013
12267
|
});
|
|
12014
12268
|
}
|
|
12269
|
+
if (body.activation === "password" || body.user?.password) {
|
|
12270
|
+
return json({
|
|
12271
|
+
error: "Setting a password during vendor onboard is no longer supported. Create the vendor, then send an invite or copy the invite link so the owner can set their own password."
|
|
12272
|
+
}, {
|
|
12273
|
+
status: 400
|
|
12274
|
+
});
|
|
12275
|
+
}
|
|
12015
12276
|
const profile = parseVendorProfileFromBody(body.vendor, {
|
|
12016
12277
|
defaultRegistrationStatus: "approved"
|
|
12017
12278
|
});
|
|
@@ -12021,34 +12282,8 @@ function createVendorOnboardHandlers(config) {
|
|
|
12021
12282
|
}, {
|
|
12022
12283
|
status: 400
|
|
12023
12284
|
});
|
|
12024
|
-
const activation =
|
|
12025
|
-
const
|
|
12026
|
-
let ownerPasswordHash = null;
|
|
12027
|
-
if (activation === "password") {
|
|
12028
|
-
const plain = body.user?.password?.trim();
|
|
12029
|
-
if (!plain) {
|
|
12030
|
-
return json({
|
|
12031
|
-
error: "Password is required when activating with a set password"
|
|
12032
|
-
}, {
|
|
12033
|
-
status: 400
|
|
12034
|
-
});
|
|
12035
|
-
}
|
|
12036
|
-
if (plain.length < minPasswordLength) {
|
|
12037
|
-
return json({
|
|
12038
|
-
error: `Password must be at least ${minPasswordLength} characters`
|
|
12039
|
-
}, {
|
|
12040
|
-
status: 400
|
|
12041
|
-
});
|
|
12042
|
-
}
|
|
12043
|
-
if (!hashPassword) {
|
|
12044
|
-
return json({
|
|
12045
|
-
error: "Password hashing is not configured on the server"
|
|
12046
|
-
}, {
|
|
12047
|
-
status: 501
|
|
12048
|
-
});
|
|
12049
|
-
}
|
|
12050
|
-
ownerPasswordHash = await hashPassword(plain);
|
|
12051
|
-
}
|
|
12285
|
+
const activation = "invite";
|
|
12286
|
+
const ownerPasswordHash = null;
|
|
12052
12287
|
const slug = trimOrNull(body.vendor?.slug) || slugify(vendorName);
|
|
12053
12288
|
if (!slug) return json({
|
|
12054
12289
|
error: "Could not derive vendor slug"
|
|
@@ -12105,7 +12340,7 @@ function createVendorOnboardHandlers(config) {
|
|
|
12105
12340
|
email: userEmail,
|
|
12106
12341
|
phone: ownerPhone,
|
|
12107
12342
|
password: ownerPasswordHash,
|
|
12108
|
-
blocked:
|
|
12343
|
+
blocked: true,
|
|
12109
12344
|
groupId: ownerGroup.id,
|
|
12110
12345
|
adminAccess: true,
|
|
12111
12346
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -12122,10 +12357,14 @@ function createVendorOnboardHandlers(config) {
|
|
|
12122
12357
|
email: userEmail,
|
|
12123
12358
|
phone: ownerPhone,
|
|
12124
12359
|
password: ownerPasswordHash,
|
|
12125
|
-
blocked:
|
|
12360
|
+
blocked: true,
|
|
12126
12361
|
groupId: ownerGroup.id,
|
|
12127
12362
|
adminAccess: true
|
|
12128
12363
|
}));
|
|
12364
|
+
const metadata = applyRotatingVendorInvite(buildVendorMetadata(null, {
|
|
12365
|
+
ownerDesignation,
|
|
12366
|
+
termsAccepted: true
|
|
12367
|
+
}));
|
|
12129
12368
|
const vendor = await vendorRepo.save(vendorRepo.create({
|
|
12130
12369
|
name: vendorName,
|
|
12131
12370
|
slug,
|
|
@@ -12145,10 +12384,7 @@ function createVendorOnboardHandlers(config) {
|
|
|
12145
12384
|
gstin: profile.gstin,
|
|
12146
12385
|
pan: profile.pan,
|
|
12147
12386
|
registrationStatus: profile.registrationStatus,
|
|
12148
|
-
metadata
|
|
12149
|
-
ownerDesignation,
|
|
12150
|
-
termsAccepted: true
|
|
12151
|
-
}),
|
|
12387
|
+
metadata,
|
|
12152
12388
|
active: body.vendor?.active !== false,
|
|
12153
12389
|
deleted: false,
|
|
12154
12390
|
userId: newUser.id,
|
|
@@ -12196,37 +12432,14 @@ function createVendorOnboardHandlers(config) {
|
|
|
12196
12432
|
};
|
|
12197
12433
|
});
|
|
12198
12434
|
let inviteLink;
|
|
12199
|
-
|
|
12200
|
-
if (
|
|
12201
|
-
|
|
12202
|
-
inviteLink = `${baseUrl}/admin/invite?token=${emailToken}`;
|
|
12435
|
+
const token = readVendorInviteToken(result.vendor.metadata);
|
|
12436
|
+
if (token) {
|
|
12437
|
+
inviteLink = buildVendorInviteLink(baseUrl, token);
|
|
12203
12438
|
}
|
|
12204
|
-
if (sendOwnerEmail) {
|
|
12205
|
-
emailSent = await trySendVendorOnboardEmails({
|
|
12206
|
-
vendorName,
|
|
12207
|
-
vendorSlug: slug,
|
|
12208
|
-
ownerName: userName,
|
|
12209
|
-
ownerEmail: userEmail,
|
|
12210
|
-
activation,
|
|
12211
|
-
inviteLink,
|
|
12212
|
-
sendToOwner: true
|
|
12213
|
-
});
|
|
12214
|
-
} else if (getCms) {
|
|
12215
|
-
await trySendVendorOnboardEmails({
|
|
12216
|
-
vendorName,
|
|
12217
|
-
vendorSlug: slug,
|
|
12218
|
-
ownerName: userName,
|
|
12219
|
-
ownerEmail: userEmail,
|
|
12220
|
-
activation,
|
|
12221
|
-
inviteLink,
|
|
12222
|
-
sendToOwner: false
|
|
12223
|
-
});
|
|
12224
|
-
}
|
|
12225
|
-
const message = activation === "password" ? emailSent ? "Vendor onboarded successfully. Welcome email sent to the owner." : sendOwnerEmail ? "Vendor onboarded successfully. Owner can sign in with the password you set (welcome email may not have been sent \u2014 check email plugin)." : "Vendor onboarded successfully. Owner can sign in with the password you set." : emailSent ? "Vendor onboarded successfully. Invite email sent." : sendOwnerEmail ? "Vendor onboarded successfully. Invite link created (email may not have been sent \u2014 check email plugin)." : "Vendor onboarded successfully. Share the invite link with the owner.";
|
|
12226
12439
|
return json({
|
|
12227
|
-
message,
|
|
12440
|
+
message: "Vendor created successfully.",
|
|
12228
12441
|
activation,
|
|
12229
|
-
emailSent,
|
|
12442
|
+
emailSent: false,
|
|
12230
12443
|
vendor: result.vendor,
|
|
12231
12444
|
user: {
|
|
12232
12445
|
id: result.user.id,
|
|
@@ -12241,6 +12454,137 @@ function createVendorOnboardHandlers(config) {
|
|
|
12241
12454
|
return vendorOnboardErrorResponse(json, e);
|
|
12242
12455
|
}
|
|
12243
12456
|
},
|
|
12457
|
+
/** POST /api/admin/vendors/:id/resend-invite — rotate owner invite token and resend email */
|
|
12458
|
+
async resendInvite(req, vendorIdStr) {
|
|
12459
|
+
const err = await gateAdmin();
|
|
12460
|
+
if (err) return err;
|
|
12461
|
+
if (!entityMap.vendors || !entityMap.users) {
|
|
12462
|
+
return json({
|
|
12463
|
+
error: "Vendor entities not configured"
|
|
12464
|
+
}, {
|
|
12465
|
+
status: 500
|
|
12466
|
+
});
|
|
12467
|
+
}
|
|
12468
|
+
const vendorId = Number(vendorIdStr);
|
|
12469
|
+
if (!Number.isFinite(vendorId) || vendorId <= 0) {
|
|
12470
|
+
return json({
|
|
12471
|
+
error: "Invalid vendor id"
|
|
12472
|
+
}, {
|
|
12473
|
+
status: 400
|
|
12474
|
+
});
|
|
12475
|
+
}
|
|
12476
|
+
let sendEmail = true;
|
|
12477
|
+
let rotate = true;
|
|
12478
|
+
try {
|
|
12479
|
+
const body = await req.json().catch(() => ({}));
|
|
12480
|
+
if (body.sendEmail === false) sendEmail = false;
|
|
12481
|
+
if (body.rotate === false) rotate = false;
|
|
12482
|
+
} catch {
|
|
12483
|
+
}
|
|
12484
|
+
try {
|
|
12485
|
+
const vendorRepo = dataSource.getRepository(entityMap.vendors);
|
|
12486
|
+
const vendor = await vendorRepo.findOne({
|
|
12487
|
+
where: {
|
|
12488
|
+
id: vendorId,
|
|
12489
|
+
deleted: false
|
|
12490
|
+
}
|
|
12491
|
+
});
|
|
12492
|
+
if (!vendor) return json({
|
|
12493
|
+
error: "Vendor not found"
|
|
12494
|
+
}, {
|
|
12495
|
+
status: 404
|
|
12496
|
+
});
|
|
12497
|
+
const v = vendor;
|
|
12498
|
+
if (v.inviteStatus === "accepted") {
|
|
12499
|
+
return json({
|
|
12500
|
+
error: "Owner has already accepted the invite"
|
|
12501
|
+
}, {
|
|
12502
|
+
status: 400
|
|
12503
|
+
});
|
|
12504
|
+
}
|
|
12505
|
+
if (v.inviteStatus === "none") {
|
|
12506
|
+
return json({
|
|
12507
|
+
error: "This vendor was not created with an invite link"
|
|
12508
|
+
}, {
|
|
12509
|
+
status: 400
|
|
12510
|
+
});
|
|
12511
|
+
}
|
|
12512
|
+
if (v.inviteStatus !== "pending" && v.inviteStatus !== "expired") {
|
|
12513
|
+
return json({
|
|
12514
|
+
error: "Invite cannot be resent for this vendor"
|
|
12515
|
+
}, {
|
|
12516
|
+
status: 400
|
|
12517
|
+
});
|
|
12518
|
+
}
|
|
12519
|
+
if (!v.userId) {
|
|
12520
|
+
return json({
|
|
12521
|
+
error: "Vendor has no owner user linked"
|
|
12522
|
+
}, {
|
|
12523
|
+
status: 400
|
|
12524
|
+
});
|
|
12525
|
+
}
|
|
12526
|
+
const userRepo = dataSource.getRepository(entityMap.users);
|
|
12527
|
+
const owner = await userRepo.findOne({
|
|
12528
|
+
where: {
|
|
12529
|
+
id: v.userId,
|
|
12530
|
+
deleted: false
|
|
12531
|
+
}
|
|
12532
|
+
});
|
|
12533
|
+
if (!owner) return json({
|
|
12534
|
+
error: "Owner user not found"
|
|
12535
|
+
}, {
|
|
12536
|
+
status: 404
|
|
12537
|
+
});
|
|
12538
|
+
const ownerRow = owner;
|
|
12539
|
+
const hasPassword = !!(ownerRow.password && String(ownerRow.password).trim());
|
|
12540
|
+
if (!ownerRow.blocked && hasPassword) {
|
|
12541
|
+
return json({
|
|
12542
|
+
error: "Owner account is already active"
|
|
12543
|
+
}, {
|
|
12544
|
+
status: 400
|
|
12545
|
+
});
|
|
12546
|
+
}
|
|
12547
|
+
const nextMetadata = rotate ? applyRotatingVendorInvite(v.metadata) : {
|
|
12548
|
+
...v.metadata ?? {}
|
|
12549
|
+
};
|
|
12550
|
+
if (rotate || v.inviteStatus !== "pending") {
|
|
12551
|
+
await vendorRepo.update(vendorId, {
|
|
12552
|
+
metadata: nextMetadata,
|
|
12553
|
+
inviteStatus: "pending"
|
|
12554
|
+
});
|
|
12555
|
+
}
|
|
12556
|
+
const token = readVendorInviteToken(nextMetadata);
|
|
12557
|
+
if (!token) {
|
|
12558
|
+
return json({
|
|
12559
|
+
error: "Failed to generate invite token"
|
|
12560
|
+
}, {
|
|
12561
|
+
status: 500
|
|
12562
|
+
});
|
|
12563
|
+
}
|
|
12564
|
+
const inviteLink = buildVendorInviteLink(baseUrl, token);
|
|
12565
|
+
let emailSent = false;
|
|
12566
|
+
if (sendEmail) {
|
|
12567
|
+
emailSent = await trySendVendorOnboardEmails({
|
|
12568
|
+
vendorName: v.name,
|
|
12569
|
+
vendorSlug: v.slug,
|
|
12570
|
+
ownerName: ownerRow.name,
|
|
12571
|
+
ownerEmail: ownerRow.email,
|
|
12572
|
+
activation: "invite",
|
|
12573
|
+
inviteLink,
|
|
12574
|
+
sendToOwner: true
|
|
12575
|
+
});
|
|
12576
|
+
}
|
|
12577
|
+
const rotatedNote = rotate ? " Previous invite link is no longer valid." : "";
|
|
12578
|
+
return json({
|
|
12579
|
+
message: emailSent ? `Invite email sent.${rotatedNote}` : sendEmail ? `Invite link ready (email may not have been sent \u2014 check email plugin).${rotatedNote}` : rotate ? "New invite link created. Previous invite link is no longer valid." : "Invite link ready.",
|
|
12580
|
+
emailSent,
|
|
12581
|
+
inviteLink,
|
|
12582
|
+
inviteStatus: "pending"
|
|
12583
|
+
});
|
|
12584
|
+
} catch (e) {
|
|
12585
|
+
return vendorOnboardErrorResponse(json, e);
|
|
12586
|
+
}
|
|
12587
|
+
},
|
|
12244
12588
|
/** POST /api/admin/vendor/switch — set active vendor in session (JWT update via client) */
|
|
12245
12589
|
async switchVendor(req) {
|
|
12246
12590
|
const u = await getSessionUser();
|
|
@@ -25602,6 +25946,14 @@ function createCmsApiHandler(config) {
|
|
|
25602
25946
|
});
|
|
25603
25947
|
return vendorHandlers.onboard(req);
|
|
25604
25948
|
}
|
|
25949
|
+
if (path2[0] === "admin" && path2[1] === "vendors" && path2.length === 4 && path2[3] === "resend-invite" && m === "POST") {
|
|
25950
|
+
if (!vendorHandlers) return config.json({
|
|
25951
|
+
error: "Not found"
|
|
25952
|
+
}, {
|
|
25953
|
+
status: 404
|
|
25954
|
+
});
|
|
25955
|
+
return vendorHandlers.resendInvite(req, path2[2]);
|
|
25956
|
+
}
|
|
25605
25957
|
if (path2[0] === "admin" && path2[1] === "vendor" && path2[2] === "switch" && path2.length === 3 && m === "POST") {
|
|
25606
25958
|
if (!vendorHandlers) return config.json({
|
|
25607
25959
|
error: "Not found"
|
|
@@ -26193,6 +26545,15 @@ function createCmsApiHandler(config) {
|
|
|
26193
26545
|
if (m === "PUT" || m === "PATCH") return formSaveHandlers.PUT(req, path2[1]);
|
|
26194
26546
|
}
|
|
26195
26547
|
}
|
|
26548
|
+
if (path2[0] === "users" && path2.length === 2 && userAuthRouter) {
|
|
26549
|
+
const authSegment = path2[1];
|
|
26550
|
+
if (m === "GET" && authSegment === "invite" && userAuthRouter.GET) {
|
|
26551
|
+
return userAuthRouter.GET(req, authSegment);
|
|
26552
|
+
}
|
|
26553
|
+
if (m === "POST" && (authSegment === "invite" || authSegment === "forgot-password" || authSegment === "set-password" || authSegment === "reset-password")) {
|
|
26554
|
+
return userAuthRouter.POST(req, authSegment);
|
|
26555
|
+
}
|
|
26556
|
+
}
|
|
26196
26557
|
if (path2[0] === "users" && usersHandlers) {
|
|
26197
26558
|
if (path2.length === 1) {
|
|
26198
26559
|
if (m === "GET") return usersHandlers.list(req);
|
|
@@ -26213,9 +26574,6 @@ function createCmsApiHandler(config) {
|
|
|
26213
26574
|
return usersHandlers.regenerateInvite(req, path2[1]);
|
|
26214
26575
|
}
|
|
26215
26576
|
}
|
|
26216
|
-
if (path2[0] === "users" && path2.length === 2 && userAuthRouter && m === "POST") {
|
|
26217
|
-
return userAuthRouter.POST(req, path2[1]);
|
|
26218
|
-
}
|
|
26219
26577
|
if (path2[0] === "social-media" && path2[1] === "linkedin" && socialMediaHandlers && path2.length === 3) {
|
|
26220
26578
|
const tail = path2[2];
|
|
26221
26579
|
if (tail === "status" && m === "GET") {
|
|
@@ -30610,12 +30968,14 @@ exports.BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG = BLOG_METADATA_ENRICHER_LLM_AGENT
|
|
|
30610
30968
|
exports.BlogGeneratorService = BlogGeneratorService;
|
|
30611
30969
|
exports.CMS_ENTITY_MAP = CMS_ENTITY_MAP;
|
|
30612
30970
|
exports.ZIP_MIME_TYPES = ZIP_MIME_TYPES;
|
|
30971
|
+
exports.applyRotatingVendorInvite = applyRotatingVendorInvite;
|
|
30613
30972
|
exports.applyVendorCustomersContactFilter = applyVendorCustomersContactFilter;
|
|
30614
30973
|
exports.assertCaptchaOk = assertCaptchaOk;
|
|
30615
30974
|
exports.assertContactAllowedForVendorOrder = assertContactAllowedForVendorOrder;
|
|
30616
30975
|
exports.buildBlogMetadataUserPrompt = buildBlogMetadataUserPrompt;
|
|
30617
30976
|
exports.buildCronFromSchedule = buildCronFromSchedule;
|
|
30618
30977
|
exports.buildRssUserPromptFromFeeds = buildRssUserPromptFromFeeds;
|
|
30978
|
+
exports.buildVendorInviteLink = buildVendorInviteLink;
|
|
30619
30979
|
exports.calculateOrderRefundPreview = calculateOrderRefundPreview;
|
|
30620
30980
|
exports.calculateRefundFromPolicy = calculateRefundFromPolicy;
|
|
30621
30981
|
exports.checkEventsEnabled = checkEventsEnabled;
|
|
@@ -30660,6 +31020,7 @@ exports.ensureMessagingPluginsOnCms = ensureMessagingPluginsOnCms;
|
|
|
30660
31020
|
exports.ensureScheduleQueueWorker = ensureScheduleQueueWorker;
|
|
30661
31021
|
exports.ensureVendorCustomerForOrderContact = ensureVendorCustomerForOrderContact;
|
|
30662
31022
|
exports.findActiveRefundPolicyForVendor = findActiveRefundPolicyForVendor;
|
|
31023
|
+
exports.findVendorByInviteToken = findVendorByInviteToken;
|
|
30663
31024
|
exports.formatTierRange = formatTierRange;
|
|
30664
31025
|
exports.generateNumericOtp = generateNumericOtp;
|
|
30665
31026
|
exports.getPublicSettingsGroup = getPublicSettingsGroup;
|