@@ -4,55 +4,65 @@ import path from 'node:path'
44import  {  log ,  runCommand  }  from  '@stacksjs/cli' 
55import  forge ,  {  pki ,  tls  }  from  'node-forge' 
66import  {  config ,  resolveConfig  }  from  './config' 
7- import  type  {  GenerateCertOptions  }  from  './types' 
7+ import  type  {  AddCertOptions ,   GenerateCertOptions  }  from  './types' 
88
9- const  makeNumberPositive  =  ( hexString : string )  =>  { 
10-   let  mostSignificativeHexDigitAsInt  =  Number . parseInt ( hexString [ 0 ] ,  16 ) 
11- 
12-   if  ( mostSignificativeHexDigitAsInt  <  8 )  return  hexString 
13- 
14-   mostSignificativeHexDigitAsInt  -=  8 
15-   return  mostSignificativeHexDigitAsInt . toString ( )  +  hexString . substring ( 1 ) 
16- } 
17- 
18- // Generate a random serial number for the Certificate 
19- const  randomSerialNumber  =  ( )  =>  { 
9+ /** 
10+  * Generate a random serial number for the Certificate 
11+  * @returns  The serial number for the Certificate 
12+  */ 
13+ export  const  randomSerialNumber  =  ( ) : string  =>  { 
2014  return  makeNumberPositive ( forge . util . bytesToHex ( forge . random . getBytesSync ( 20 ) ) ) 
2115} 
2216
23- // Get the Not Before Date for a Certificate (will be valid from 2 days ago) 
24- const  getCertNotBefore  =  ( )  =>  { 
17+ /** 
18+  * Get the Not Before Date for a Certificate (will be valid from 2 days ago) 
19+  * @returns  The Not Before Date for the Certificate 
20+  */ 
21+ export  const  getCertNotBefore  =  ( ) : Date  =>  { 
2522  const  twoDaysAgo  =  new  Date ( Date . now ( )  -  60  *  60  *  24  *  2  *  1000 ) 
2623  const  year  =  twoDaysAgo . getFullYear ( ) 
2724  const  month  =  ( twoDaysAgo . getMonth ( )  +  1 ) . toString ( ) . padStart ( 2 ,  '0' ) 
2825  const  day  =  twoDaysAgo . getDate ( ) 
2926  return  new  Date ( `${ year } ${ month } ${ day }  ) 
3027} 
3128
32- // Get Certificate Expiration Date (Valid for 90 Days) 
33- const  getCertNotAfter  =  ( notBefore : any )  =>  { 
29+ /** 
30+  * Get the Not After Date for a Certificate (Valid for 90 Days) 
31+  * @param  notBefore - The Not Before Date for the Certificate 
32+  * @returns  The Not After Date for the Certificate 
33+  */ 
34+ export  const  getCertNotAfter  =  ( notBefore : Date ) : Date  =>  { 
3435  const  ninetyDaysLater  =  new  Date ( notBefore . getTime ( )  +  60  *  60  *  24  *  90  *  1000 ) 
3536  const  year  =  ninetyDaysLater . getFullYear ( ) 
3637  const  month  =  ( ninetyDaysLater . getMonth ( )  +  1 ) . toString ( ) . padStart ( 2 ,  '0' ) 
3738  const  day  =  ninetyDaysLater . getDate ( ) 
39+ 
3840  return  new  Date ( `${ year } ${ month } ${ day }  ) 
3941} 
4042
41- // Get CA Expiration Date (Valid for 100 Years) 
42- const  getCANotAfter  =  ( notBefore : any )  =>  { 
43+ /** 
44+  * Get the CA Not After Date (Valid for 100 Years) 
45+  * @param  notBefore - The Not Before Date for the CA 
46+  * @returns  The Not After Date for the CA 
47+  */ 
48+ export  const  getCANotAfter  =  ( notBefore : Date ) : Date  =>  { 
4349  const  year  =  notBefore . getFullYear ( )  +  100 
4450  const  month  =  ( notBefore . getMonth ( )  +  1 ) . toString ( ) . padStart ( 2 ,  '0' ) 
4551  const  day  =  notBefore . getDate ( ) 
52+ 
4653  return  new  Date ( `${ year } ${ month } ${ day }  ) 
4754} 
4855
49- const  DEFAULT_C  =  'US' 
50- const  DEFAULT_ST  =  'California' 
51- const  DEFAULT_L  =  'Playa Vista' 
52- const  DEFAULT_O  =  config ?. ssl ?. organizationName  ??  'Stacks.js' 
56+ export   const  DEFAULT_C  =  'US' 
57+ export   const  DEFAULT_ST  =  'California' 
58+ export   const  DEFAULT_L  =  'Playa Vista' 
59+ export   const  DEFAULT_O :  string  =  config ?. ssl ?. organizationName  ??  'Stacks.js' 
5360
54- // Generate a new Root CA Certificate 
55- export  async  function  createRootCA ( )  { 
61+ /** 
62+  * Create a new Root CA Certificate 
63+  * @returns  The Root CA Certificate 
64+  */ 
65+ export  async  function  createRootCA ( ) : Promise < GenerateCertReturn >  { 
5666  // Create a new Keypair for the Root CA 
5767  const  {  privateKey,  publicKey }  =  pki . rsa . generateKeyPair ( 2048 ) 
5868
@@ -119,7 +129,19 @@ export async function createRootCA() {
119129  } 
120130} 
121131
122- export  async  function  generateCert ( options ?: GenerateCertOptions )  { 
132+ type  GenerateCertReturn  =  { 
133+   certificate : string 
134+   privateKey : string 
135+   notBefore : Date 
136+   notAfter : Date 
137+ } 
138+ 
139+ /** 
140+  * Generate a new Host Certificate 
141+  * @param  options - The options for generating the certificate 
142+  * @returns  The generated certificate 
143+  */ 
144+ export  async  function  generateCert ( options ?: GenerateCertOptions ) : Promise < GenerateCertReturn >  { 
123145  log . debug ( 'generateCert' ,  options ) 
124146
125147  if  ( ! options ?. hostCertCN . toString ( ) . trim ( ) )  throw  new  Error ( '"hostCertCN" must be a String' ) 
@@ -210,18 +232,16 @@ export async function generateCert(options?: GenerateCertOptions) {
210232  return  { 
211233    certificate : pemHostCert , 
212234    privateKey : pemHostKey , 
235+     notBefore : newHostCert . validity . notBefore , 
236+     notAfter : newHostCert . validity . notAfter , 
213237  } 
214238} 
215239
216- export  interface  AddCertOptions  { 
217-   customCertPath ?: string 
218- } 
219- 
220240export  async  function  addCertToSystemTrustStoreAndSaveCerts ( 
221241  cert : {  certificate : string ;  privateKey : string  } , 
222242  CAcert : string , 
223243  options ?: AddCertOptions , 
224- )  { 
244+ ) :  Promise < string >  { 
225245  const  certPath  =  storeCert ( cert ,  options ) 
226246  const  CAcertPath  =  storeCACert ( CAcert ,  options ) 
227247
@@ -240,35 +260,6 @@ export async function addCertToSystemTrustStoreAndSaveCerts(
240260    // Linux (This might vary based on the distro) 
241261    // for Ubuntu/Debian based systems 
242262
243-     // return all directories that contain cert9.db file using fs.readdirSync 
244-     function  findFoldersWithFile ( rootDir : string ,  fileName : string ) : string [ ]  { 
245-       const  result : string [ ]  =  [ ] 
246- 
247-       function  search ( dir : string )  { 
248-         try  { 
249-           const  files  =  fs . readdirSync ( dir ) 
250- 
251-           for  ( const  file  of  files )  { 
252-             const  filePath  =  path . join ( dir ,  file ) 
253-             const  stats  =  fs . lstatSync ( filePath )  // Use fs.lstatSync instead 
254- 
255-             if  ( stats . isDirectory ( ) )  { 
256-               search ( filePath ) 
257-             }  else  if  ( file  ===  fileName )  { 
258-               result . push ( dir ) 
259-             } 
260-           } 
261-         }  catch  ( error )  { 
262-           // Handle any errors (e.g., broken links, permission issues) 
263-           console . warn ( `Error reading directory ${ dir } ${ error }  ) 
264-         } 
265-       } 
266- 
267-       search ( rootDir ) 
268-       return  result 
269-     } 
270- 
271-     // 
272263    const  rootDirectory  =  `${ os . homedir ( ) }  
273264    const  targetFileName  =  'cert9.db' 
274265    const  foldersWithFile  =  findFoldersWithFile ( rootDirectory ,  targetFileName ) 
@@ -306,7 +297,7 @@ export async function addCertToSystemTrustStoreAndSaveCerts(
306297  return  certPath 
307298} 
308299
309- export  function  storeCert ( cert : {  certificate : string ;  privateKey : string  } ,  options ?: AddCertOptions )  { 
300+ export  function  storeCert ( cert : {  certificate : string ;  privateKey : string  } ,  options ?: AddCertOptions ) :  string  { 
310301  // Construct the path using os.homedir() and path.join() 
311302  const  certPath  =  options ?. customCertPath  ||  path . join ( os . homedir ( ) ,  '.stacks' ,  'ssl' ,  `stacks.localhost.crt` ) 
312303  const  certKeyPath  =  options ?. customCertPath  ||  path . join ( os . homedir ( ) ,  '.stacks' ,  'ssl' ,  `stacks.localhost.crt.key` ) 
@@ -325,7 +316,13 @@ export function storeCert(cert: { certificate: string; privateKey: string }, opt
325316  return  certPath 
326317} 
327318
328- export  function  storeCACert ( CAcert : string ,  options ?: AddCertOptions )  { 
319+ /** 
320+  * Store the CA Certificate 
321+  * @param  CAcert - The CA Certificate 
322+  * @param  options - The options for storing the CA Certificate 
323+  * @returns  The path to the CA Certificate 
324+  */ 
325+ export  function  storeCACert ( CAcert : string ,  options ?: AddCertOptions ) : string  { 
329326  // Construct the path using os.homedir() and path.join() 
330327  const  CAcertPath  =  options ?. customCertPath  ||  path . join ( os . homedir ( ) ,  '.stacks' ,  'ssl' ,  `stacks.localhost.ca.crt` ) 
331328
@@ -338,4 +335,40 @@ export function storeCACert(CAcert: string, options?: AddCertOptions) {
338335  return  CAcertPath 
339336} 
340337
338+ function  findFoldersWithFile ( rootDir : string ,  fileName : string ) : string [ ]  { 
339+   const  result : string [ ]  =  [ ] 
340+ 
341+   function  search ( dir : string )  { 
342+     try  { 
343+       const  files  =  fs . readdirSync ( dir ) 
344+ 
345+       for  ( const  file  of  files )  { 
346+         const  filePath  =  path . join ( dir ,  file ) 
347+         const  stats  =  fs . lstatSync ( filePath )  // Use fs.lstatSync instead 
348+ 
349+         if  ( stats . isDirectory ( ) )  { 
350+           search ( filePath ) 
351+         }  else  if  ( file  ===  fileName )  { 
352+           result . push ( dir ) 
353+         } 
354+       } 
355+     }  catch  ( error )  { 
356+       // Handle any errors (e.g., broken links, permission issues) 
357+       console . warn ( `Error reading directory ${ dir } ${ error }  ) 
358+     } 
359+   } 
360+ 
361+   search ( rootDir ) 
362+   return  result 
363+ } 
364+ 
365+ const  makeNumberPositive  =  ( hexString : string )  =>  { 
366+   let  mostSignificativeHexDigitAsInt  =  Number . parseInt ( hexString [ 0 ] ,  16 ) 
367+ 
368+   if  ( mostSignificativeHexDigitAsInt  <  8 )  return  hexString 
369+ 
370+   mostSignificativeHexDigitAsInt  -=  8 
371+   return  mostSignificativeHexDigitAsInt . toString ( )  +  hexString . substring ( 1 ) 
372+ } 
373+ 
341374export  {  tls ,  pki ,  forge  } 
0 commit comments