109 lines
2.6 KiB
TypeScript
109 lines
2.6 KiB
TypeScript
// import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
import { writeFile } from "fs/promises";
|
|
|
|
import { mkdirSync, readFileSync } from "fs";
|
|
|
|
export class DHSecretClient {
|
|
private token!: Promise<string>; //Set by init
|
|
|
|
private headerName = "x-hoard-auth-token";
|
|
|
|
private cache: Record<string, { value: string; expires?: number }> = {};
|
|
private cacheLocation: string;
|
|
|
|
/**
|
|
* @param dhBaseUri uri for hosted Dragon's Hoard instance
|
|
* @param cacheDir path to cache dir
|
|
*/
|
|
constructor(
|
|
private dhBaseUri: string,
|
|
private cacheDir: string,
|
|
) {
|
|
this.cacheLocation = this.cacheDir.trim().replace(/\/^/, "") + "/.dh_cache";
|
|
mkdirSync(this.cacheDir, { recursive: true });
|
|
|
|
this.readDiskCache();
|
|
this.token = this.fetchToken();
|
|
}
|
|
|
|
private async fetchToken() {
|
|
const cacheKey = "token";
|
|
if (this.cache[cacheKey]) {
|
|
return this.cache[cacheKey].value;
|
|
}
|
|
|
|
const req = await fetch(this.dhBaseUri + "/api/access/token");
|
|
|
|
if (req.status !== 200) throw Error(await req.text());
|
|
|
|
const token = await req.text();
|
|
|
|
if (!token) throw Error("Token not included in response body");
|
|
|
|
this.writeCache(cacheKey, token);
|
|
|
|
return token;
|
|
}
|
|
|
|
private readDiskCache() {
|
|
try {
|
|
const cache = readFileSync(this.cacheLocation, "utf-8");
|
|
this.cache = JSON.parse(cache || "{}");
|
|
} catch {
|
|
this.cache = {};
|
|
this.writeDiskCache().then(this.readDiskCache);
|
|
}
|
|
}
|
|
private async writeDiskCache() {
|
|
await writeFile(this.cacheLocation, JSON.stringify(this.cache), "utf-8");
|
|
}
|
|
|
|
private writeCache(key: string, value: string, expires?: number) {
|
|
this.cache[key] = { value, expires };
|
|
|
|
this.writeDiskCache();
|
|
}
|
|
|
|
private readCache(key: string) {
|
|
const item = this.cache[key];
|
|
|
|
if (!item) return null;
|
|
|
|
if (item && item.expires && item.expires < Date.now()) {
|
|
delete this.cache[key];
|
|
// this.writeDiskCache();
|
|
return null;
|
|
}
|
|
|
|
return item.value;
|
|
}
|
|
|
|
async fetchSecret(secret_name: string, environment?: string) {
|
|
const uri =
|
|
this.dhBaseUri +
|
|
"/api/keys/" +
|
|
secret_name +
|
|
(environment ? "?env=" + environment : "");
|
|
|
|
const cached = this.readCache(secret_name);
|
|
|
|
if (cached !== null) return cached;
|
|
|
|
const req = await fetch(uri, {
|
|
headers: {
|
|
[this.headerName]: await this.token,
|
|
},
|
|
});
|
|
|
|
if (req.status !== 200) throw Error(await req.text());
|
|
|
|
const secret = await req.text();
|
|
|
|
if (!secret) throw Error("Secret not included in response body");
|
|
|
|
this.writeCache(secret_name, secret);
|
|
|
|
return secret;
|
|
}
|
|
}
|