@@ -4,55 +4,65 @@ import path from 'node:path'
4
4
import { log , runCommand } from '@stacksjs/cli'
5
5
import forge , { pki , tls } from 'node-forge'
6
6
import { config , resolveConfig } from './config'
7
- import type { GenerateCertOptions } from './types'
7
+ import type { AddCertOptions , GenerateCertOptions } from './types'
8
8
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 => {
20
14
return makeNumberPositive ( forge . util . bytesToHex ( forge . random . getBytesSync ( 20 ) ) )
21
15
}
22
16
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 => {
25
22
const twoDaysAgo = new Date ( Date . now ( ) - 60 * 60 * 24 * 2 * 1000 )
26
23
const year = twoDaysAgo . getFullYear ( )
27
24
const month = ( twoDaysAgo . getMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' )
28
25
const day = twoDaysAgo . getDate ( )
29
26
return new Date ( `${ year } -${ month } -${ day } 00:00:00Z` )
30
27
}
31
28
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 => {
34
35
const ninetyDaysLater = new Date ( notBefore . getTime ( ) + 60 * 60 * 24 * 90 * 1000 )
35
36
const year = ninetyDaysLater . getFullYear ( )
36
37
const month = ( ninetyDaysLater . getMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' )
37
38
const day = ninetyDaysLater . getDate ( )
39
+
38
40
return new Date ( `${ year } -${ month } -${ day } 23:59:59Z` )
39
41
}
40
42
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 => {
43
49
const year = notBefore . getFullYear ( ) + 100
44
50
const month = ( notBefore . getMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' )
45
51
const day = notBefore . getDate ( )
52
+
46
53
return new Date ( `${ year } -${ month } -${ day } 23:59:59Z` )
47
54
}
48
55
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'
53
60
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 > {
56
66
// Create a new Keypair for the Root CA
57
67
const { privateKey, publicKey } = pki . rsa . generateKeyPair ( 2048 )
58
68
@@ -119,7 +129,19 @@ export async function createRootCA() {
119
129
}
120
130
}
121
131
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 > {
123
145
log . debug ( 'generateCert' , options )
124
146
125
147
if ( ! options ?. hostCertCN . toString ( ) . trim ( ) ) throw new Error ( '"hostCertCN" must be a String' )
@@ -210,18 +232,16 @@ export async function generateCert(options?: GenerateCertOptions) {
210
232
return {
211
233
certificate : pemHostCert ,
212
234
privateKey : pemHostKey ,
235
+ notBefore : newHostCert . validity . notBefore ,
236
+ notAfter : newHostCert . validity . notAfter ,
213
237
}
214
238
}
215
239
216
- export interface AddCertOptions {
217
- customCertPath ?: string
218
- }
219
-
220
240
export async function addCertToSystemTrustStoreAndSaveCerts (
221
241
cert : { certificate : string ; privateKey : string } ,
222
242
CAcert : string ,
223
243
options ?: AddCertOptions ,
224
- ) {
244
+ ) : Promise < string > {
225
245
const certPath = storeCert ( cert , options )
226
246
const CAcertPath = storeCACert ( CAcert , options )
227
247
@@ -240,35 +260,6 @@ export async function addCertToSystemTrustStoreAndSaveCerts(
240
260
// Linux (This might vary based on the distro)
241
261
// for Ubuntu/Debian based systems
242
262
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
- //
272
263
const rootDirectory = `${ os . homedir ( ) } `
273
264
const targetFileName = 'cert9.db'
274
265
const foldersWithFile = findFoldersWithFile ( rootDirectory , targetFileName )
@@ -306,7 +297,7 @@ export async function addCertToSystemTrustStoreAndSaveCerts(
306
297
return certPath
307
298
}
308
299
309
- export function storeCert ( cert : { certificate : string ; privateKey : string } , options ?: AddCertOptions ) {
300
+ export function storeCert ( cert : { certificate : string ; privateKey : string } , options ?: AddCertOptions ) : string {
310
301
// Construct the path using os.homedir() and path.join()
311
302
const certPath = options ?. customCertPath || path . join ( os . homedir ( ) , '.stacks' , 'ssl' , `stacks.localhost.crt` )
312
303
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
325
316
return certPath
326
317
}
327
318
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 {
329
326
// Construct the path using os.homedir() and path.join()
330
327
const CAcertPath = options ?. customCertPath || path . join ( os . homedir ( ) , '.stacks' , 'ssl' , `stacks.localhost.ca.crt` )
331
328
@@ -338,4 +335,40 @@ export function storeCACert(CAcert: string, options?: AddCertOptions) {
338
335
return CAcertPath
339
336
}
340
337
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
+
341
374
export { tls , pki , forge }
0 commit comments