Skip to content

Commit 9e4741d

Browse files
committed
feat: add KEYPAIR_NOT_FOUND error and enhance logging in seed process
- Introduced a new error constant KEYPAIR_NOT_FOUND to handle cases where keypair data is missing. - Replaced console.log statements with consola for improved logging during the seeding of subscription templates and XTLS config. - Updated the generateMasterCerts utility to use a custom random string generator for certificate naming.
1 parent 379bef6 commit 9e4741d

File tree

4 files changed

+109
-69
lines changed

4 files changed

+109
-69
lines changed

libs/contract/constants/errors/errors.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,9 @@ export const ERRORS = {
453453
message: 'Get user usage by range error',
454454
httpCode: 500,
455455
},
456+
KEYPAIR_NOT_FOUND: {
457+
code: 'A092',
458+
message: 'Keypair not found. Restart app.',
459+
httpCode: 500,
460+
},
456461
} as const;

prisma/seed/config.seed.ts

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from '@libs/contracts/constants';
66
import { KeygenEntity } from '@modules/keygen/entities/keygen.entity';
77
import { PrismaClient } from '@prisma/client';
8+
import consola from 'consola';
89

910
export const XTLSDefaultConfig = {
1011
log: {
@@ -560,7 +561,7 @@ const prisma = new PrismaClient({
560561
});
561562

562563
async function seedSubscriptionTemplate() {
563-
console.log('Seeding subscription templates...');
564+
consola.start('Seeding subscription templates...');
564565
for (const templateType of SUBSCRIPTION_TEMPLATE_TYPE_VALUES) {
565566
const existingConfig = await prisma.subscriptionTemplate.findUnique({
566567
where: {
@@ -571,7 +572,7 @@ async function seedSubscriptionTemplate() {
571572
switch (templateType) {
572573
case SUBSCRIPTION_TEMPLATE_TYPE.STASH:
573574
if (existingConfig) {
574-
console.log(`Default ${templateType} config already exists!`);
575+
consola.info(`Default ${templateType} config already exists!`);
575576
continue;
576577
}
577578

@@ -582,7 +583,7 @@ async function seedSubscriptionTemplate() {
582583
break;
583584
case SUBSCRIPTION_TEMPLATE_TYPE.MIHOMO:
584585
if (existingConfig) {
585-
console.log(`Default ${templateType} config already exists!`);
586+
consola.info(`Default ${templateType} config already exists!`);
586587
continue;
587588
}
588589

@@ -592,7 +593,7 @@ async function seedSubscriptionTemplate() {
592593
break;
593594
case SUBSCRIPTION_TEMPLATE_TYPE.SINGBOX:
594595
if (existingConfig) {
595-
console.log(`Default ${templateType} config already exists!`);
596+
consola.info(`Default ${templateType} config already exists!`);
596597
continue;
597598
}
598599

@@ -602,7 +603,7 @@ async function seedSubscriptionTemplate() {
602603
break;
603604
case SUBSCRIPTION_TEMPLATE_TYPE.SINGBOX_LEGACY:
604605
if (existingConfig) {
605-
console.log(`Default ${templateType} config already exists!`);
606+
consola.info(`Default ${templateType} config already exists!`);
606607
continue;
607608
}
608609

@@ -612,7 +613,7 @@ async function seedSubscriptionTemplate() {
612613
break;
613614
case SUBSCRIPTION_TEMPLATE_TYPE.XRAY_JSON:
614615
if (existingConfig) {
615-
console.log(`Default ${templateType} config already exists!`);
616+
consola.info(`Default ${templateType} config already exists!`);
616617
continue;
617618
}
618619

@@ -623,7 +624,7 @@ async function seedSubscriptionTemplate() {
623624
break;
624625
case SUBSCRIPTION_TEMPLATE_TYPE.CLASH:
625626
if (existingConfig) {
626-
console.log(`Default ${templateType} config already exists!`);
627+
consola.info(`Default ${templateType} config already exists!`);
627628
continue;
628629
}
629630

@@ -633,7 +634,8 @@ async function seedSubscriptionTemplate() {
633634

634635
break;
635636
default:
636-
throw new Error(`Unknown template type: ${templateType}`);
637+
consola.error(`Unknown template type: ${templateType}`);
638+
process.exit(1);
637639
}
638640
}
639641
}
@@ -668,8 +670,10 @@ async function seedSubscriptionSettings() {
668670
async function seedConfigVariables() {
669671
const existingConfig = await prisma.xrayConfig.findFirst();
670672

673+
consola.start('🔐 Seeding XTLS config...');
674+
671675
if (existingConfig) {
672-
console.log('Default XTLS config already seeded!');
676+
consola.success('🔐 Default XTLS config already seeded!');
673677
return;
674678
}
675679

@@ -680,60 +684,80 @@ async function seedConfigVariables() {
680684
});
681685

682686
if (!config) {
683-
throw new Error('Failed to create default config!');
687+
consola.error('🔐 Failed to create default config!');
688+
process.exit(1);
684689
}
685690

686-
console.log('Default XTLS config seeded!');
691+
consola.success('🔐 Default XTLS config seeded!');
687692
}
688693

689694
async function seedKeygen() {
690-
const existingConfig = await prisma.keygen.findFirst();
691-
692-
if (!existingConfig) {
693-
const { publicKey, privateKey } = await generateJwtKeypair();
694-
const { caCertPem, caKeyPem, clientCertPem, clientKeyPem } = await generateMasterCerts();
695-
696-
const keygenEntity = new KeygenEntity({
697-
caCert: caCertPem,
698-
caKey: caKeyPem,
699-
clientCert: clientCertPem,
700-
clientKey: clientKeyPem,
701-
pubKey: publicKey,
702-
privKey: privateKey,
703-
});
704-
705-
await prisma.keygen.create({
706-
data: keygenEntity,
707-
});
708-
709-
console.log('🔐 Keygen seeded!');
710-
711-
return;
712-
}
713-
714-
if (
715-
existingConfig.pubKey &&
716-
existingConfig.privKey &&
717-
(!existingConfig.caCert ||
718-
!existingConfig.caKey ||
719-
!existingConfig.clientCert ||
720-
!existingConfig.clientKey)
721-
) {
722-
const { caCertPem, caKeyPem, clientCertPem, clientKeyPem } = await generateMasterCerts();
723-
724-
await prisma.keygen.update({
725-
where: { uuid: existingConfig.uuid },
726-
data: {
727-
caCert: caCertPem,
728-
caKey: caKeyPem,
729-
clientCert: clientCertPem,
730-
clientKey: clientKeyPem,
731-
},
732-
});
733-
734-
console.log('🔐 Keygen updated!');
735-
return;
736-
}
695+
consola.start('🔐 Seeding keygen...');
696+
697+
// TODO: Remove after testing
698+
process.exit(1);
699+
700+
// const existingConfig = await prisma.keygen.findFirst();
701+
702+
// if (!existingConfig) {
703+
// try {
704+
// const { publicKey, privateKey } = await generateJwtKeypair();
705+
// const { caCertPem, caKeyPem, clientCertPem, clientKeyPem } =
706+
// await generateMasterCerts();
707+
708+
// const keygenEntity = new KeygenEntity({
709+
// caCert: caCertPem,
710+
// caKey: caKeyPem,
711+
// clientCert: clientCertPem,
712+
// clientKey: clientKeyPem,
713+
// pubKey: publicKey,
714+
// privKey: privateKey,
715+
// });
716+
717+
// await prisma.keygen.create({
718+
// data: keygenEntity,
719+
// });
720+
721+
// consola.success('🔐 Keygen seeded!');
722+
723+
// return;
724+
// } catch (error) {
725+
// consola.error('🔐 Failed to seed keygen:', error);
726+
// process.exit(1);
727+
// }
728+
// }
729+
730+
// if (
731+
// existingConfig.pubKey &&
732+
// existingConfig.privKey &&
733+
// (!existingConfig.caCert ||
734+
// !existingConfig.caKey ||
735+
// !existingConfig.clientCert ||
736+
// !existingConfig.clientKey)
737+
// ) {
738+
// try {
739+
// const { caCertPem, caKeyPem, clientCertPem, clientKeyPem } =
740+
// await generateMasterCerts();
741+
742+
// await prisma.keygen.update({
743+
// where: { uuid: existingConfig.uuid },
744+
// data: {
745+
// caCert: caCertPem,
746+
// caKey: caKeyPem,
747+
// clientCert: clientCertPem,
748+
// clientKey: clientKeyPem,
749+
// },
750+
// });
751+
752+
// consola.success('🔐 Keygen updated!');
753+
// return;
754+
// } catch (error) {
755+
// consola.error('🔐 Failed to update keygen:', error);
756+
// process.exit(1);
757+
// }
758+
// }
759+
760+
// consola.success('🔐 Keygen already seeded!');
737761
}
738762

739763
async function checkDatabaseConnection() {

src/common/utils/certs/generate-certs.util.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import {
99
} from '@peculiar/x509';
1010
import { generateKeyPair } from 'node:crypto';
1111
import { Crypto } from '@peculiar/webcrypto';
12+
import { customAlphabet } from 'nanoid';
1213
import { promisify } from 'node:util';
13-
import { nanoid } from 'nanoid';
1414

1515
const generateKeyPairAsync = promisify(generateKeyPair);
1616

1717
export async function generateMasterCerts() {
1818
const crypto = new Crypto();
19-
const cn = nanoid(48);
19+
const cn = genRandomString();
2020
cryptoProvider.set(crypto);
2121

2222
// === CA (Certificate Authority) ===
@@ -64,7 +64,7 @@ export async function generateMasterCerts() {
6464

6565
const clientCert = await X509CertificateGenerator.create({
6666
serialNumber: '02',
67-
subject: `CN=${nanoid(48)}`,
67+
subject: `CN=${genRandomString()}`,
6868
notBefore: new Date(),
6969
notAfter: new Date(new Date().setFullYear(new Date().getFullYear() + 3)),
7070
issuer: caCert.subjectName,
@@ -104,7 +104,6 @@ export async function generateNodeCert(
104104
const crypto = new Crypto();
105105
cryptoProvider.set(crypto);
106106

107-
const cn = nanoid(48);
108107
const caCert = new X509Certificate(caCertPem);
109108

110109
const caPrivateKey = await crypto.subtle.importKey(
@@ -131,7 +130,7 @@ export async function generateNodeCert(
131130

132131
const nodeCert = await X509CertificateGenerator.create({
133132
serialNumber: Date.now().toString(),
134-
subject: `CN=${cn}`,
133+
subject: `CN=${genRandomString()}`,
135134
issuer: caCert.subjectName,
136135
notBefore: new Date(),
137136
notAfter: new Date(new Date().setFullYear(new Date().getFullYear() + 3)),
@@ -195,3 +194,11 @@ function pemToArrayBuffer(pem: string): Uint8Array {
195194
.replace(/\s+/g, '');
196195
return new Uint8Array(Buffer.from(b64, 'base64'));
197196
}
197+
198+
function genRandomString(): string {
199+
const alphabet = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghjkmnopqrstuvwxyz-';
200+
const length = Math.floor(Math.random() * 27) + 20;
201+
const nanoid = customAlphabet(alphabet, length);
202+
203+
return nanoid();
204+
}

src/modules/keygen/keygen.service.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@ export class KeygenService {
2525
};
2626
}
2727

28-
const { nodeCertPem, nodeKeyPem } = await generateNodeCert(
29-
pubKey.caCert!,
30-
pubKey.caKey!,
31-
);
28+
if (!pubKey.caCert || !pubKey.caKey || !pubKey.clientCert || !pubKey.clientKey) {
29+
return {
30+
isOk: false,
31+
...ERRORS.KEYPAIR_NOT_FOUND,
32+
};
33+
}
34+
35+
const { nodeCertPem, nodeKeyPem } = await generateNodeCert(pubKey.caCert, pubKey.caKey);
3236

3337
const nodePayload = encodeCertPayload({
3438
nodeCertPem,
3539
nodeKeyPem,
36-
caCertPem: pubKey.caCert!,
37-
jwtPublicKey: pubKey.pubKey!,
40+
caCertPem: pubKey.caCert,
41+
jwtPublicKey: pubKey.pubKey,
3842
});
3943

4044
return {

0 commit comments

Comments
 (0)