1 | import * as fs from 'fs'
|
2 | import { _assert, _stringMapEntries, StringMap } from '@naturalcycles/js-lib'
|
3 | import { base64ToString } from '..'
|
4 | import { decryptRandomIVBuffer, decryptString } from './crypto.util'
|
5 |
|
6 | let loaded = false
|
7 |
|
8 | const secretMap: StringMap = {}
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | export function loadSecretsFromEnv(): void {
|
17 | require('dotenv').config()
|
18 |
|
19 | const secrets: StringMap = {}
|
20 | Object.keys(process.env)
|
21 | .filter(k => k.toUpperCase().startsWith('SECRET_'))
|
22 | .forEach(k => {
|
23 | secrets[k.toUpperCase()] = process.env[k]!
|
24 | secretMap[k.toUpperCase()] = process.env[k]!
|
25 | delete process.env[k]
|
26 | })
|
27 |
|
28 | loaded = true
|
29 | console.log(
|
30 | `${Object.keys(secrets).length} secret(s) loaded from process.env: ${Object.keys(secrets).join(
|
31 | ', ',
|
32 | )}`,
|
33 | )
|
34 | }
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | export function removeSecretsFromEnv(): void {
|
40 | Object.keys(process.env)
|
41 | .filter(k => k.toUpperCase().startsWith('SECRET_'))
|
42 | .forEach(k => delete process.env[k])
|
43 | }
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 | export function loadSecretsFromEncryptedJsonFile(
|
55 | filePath: string,
|
56 | secretEncryptionKey?: string,
|
57 | ): void {
|
58 | _assert(
|
59 | fs.existsSync(filePath),
|
60 | `loadSecretsFromEncryptedJsonFile() cannot load from path: ${filePath}`,
|
61 | )
|
62 |
|
63 | let secrets: StringMap
|
64 |
|
65 | if (secretEncryptionKey) {
|
66 | const buf = fs.readFileSync(filePath)
|
67 | const plain = decryptRandomIVBuffer(buf, secretEncryptionKey).toString('utf8')
|
68 | secrets = JSON.parse(plain)
|
69 | } else {
|
70 | secrets = JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
71 | }
|
72 |
|
73 | Object.entries(secrets).forEach(([k, v]) => (secretMap[k.toUpperCase()] = v))
|
74 |
|
75 | loaded = true
|
76 | console.log(
|
77 | `${Object.keys(secrets).length} secret(s) loaded from ${filePath}: ${Object.keys(secrets)
|
78 | .map(s => s.toUpperCase())
|
79 | .join(', ')}`,
|
80 | )
|
81 | }
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | export function loadSecretsFromEncryptedJsonFileValues(
|
88 | filePath: string,
|
89 | secretEncryptionKey?: string,
|
90 | ): void {
|
91 | _assert(
|
92 | fs.existsSync(filePath),
|
93 | `loadSecretsFromEncryptedJsonFileValues() cannot load from path: ${filePath}`,
|
94 | )
|
95 |
|
96 | const secrets: StringMap = JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
97 |
|
98 | if (secretEncryptionKey) {
|
99 | _stringMapEntries(secrets).forEach(([k, enc]) => {
|
100 | secrets[k] = decryptString(enc, secretEncryptionKey)
|
101 | })
|
102 | }
|
103 |
|
104 | Object.entries(secrets).forEach(([k, v]) => (secretMap[k.toUpperCase()] = v))
|
105 |
|
106 | loaded = true
|
107 | console.log(
|
108 | `${Object.keys(secrets).length} secret(s) loaded from ${filePath}: ${Object.keys(secrets)
|
109 | .map(s => s.toUpperCase())
|
110 | .join(', ')}`,
|
111 | )
|
112 | }
|
113 |
|
114 |
|
115 |
|
116 |
|
117 | export function secret<T = string>(k: string, json = false): T {
|
118 | const v = secretOptional(k, json)
|
119 | if (!v) {
|
120 | throw new Error(`secret(${k.toUpperCase()}) not found!`)
|
121 | }
|
122 |
|
123 | return v as any
|
124 | }
|
125 |
|
126 | export function secretOptional<T = string>(k: string, json = false): T | undefined {
|
127 | requireLoaded()
|
128 | const v = secretMap[k.toUpperCase()]
|
129 | return v && json ? JSON.parse(base64ToString(v)) : v
|
130 | }
|
131 |
|
132 | export function getSecretMap(): StringMap {
|
133 | requireLoaded()
|
134 | return secretMap
|
135 | }
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | export function setSecretMap(map: StringMap): void {
|
141 | Object.keys(secretMap).forEach(k => delete secretMap[k])
|
142 | Object.entries(map).forEach(([k, v]) => (secretMap[k.toUpperCase()] = v))
|
143 | console.log(
|
144 | `setSecretMap set ${Object.keys(secretMap).length} secret(s): ${Object.keys(map)
|
145 | .map(s => s.toUpperCase())
|
146 | .join(', ')}`,
|
147 | )
|
148 | }
|
149 |
|
150 | function requireLoaded(): void {
|
151 | if (!loaded) {
|
152 | throw new Error(`Secrets were not loaded! Call loadSecrets() before accessing secrets.`)
|
153 | }
|
154 | }
|