dh secret manager
This commit is contained in:
parent
df20a47253
commit
2f3f2fd81e
2
.gitignore
vendored
2
.gitignore
vendored
@ -40,3 +40,5 @@ next-env.d.ts
|
|||||||
|
|
||||||
temp.json
|
temp.json
|
||||||
temp.md
|
temp.md
|
||||||
|
|
||||||
|
.dragonshoard/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Inter } from "next/font/google";
|
import { Roboto } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import {
|
import {
|
||||||
BookOpenIcon,
|
BookOpenIcon,
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { DevToolboxContextProvider } from "@/components/devtools/context";
|
import { DevToolboxContextProvider } from "@/components/devtools/context";
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
const roboto = Roboto({ subsets: ["latin"], weight: "400" });
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Tabletop Commander",
|
title: "Tabletop Commander",
|
||||||
@ -51,11 +51,9 @@ export default function RootLayout({
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log(process.env.NODE_ENV);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className={inter.className + " flex min-h-[100vh]"}>
|
<body className={roboto.className + " flex min-h-[100vh]"}>
|
||||||
<nav className="h-[100vh] sticky top-0 left-0 bottom-0 p-8 rounded-r-3xl dark:bg-mixed-300 bg-primary-400 w-max shadow-2xl">
|
<nav className="h-[100vh] sticky top-0 left-0 bottom-0 p-8 rounded-r-3xl dark:bg-mixed-300 bg-primary-400 w-max shadow-2xl">
|
||||||
<h1 className="text-lg font-bold pb-6 border-b dark:border-dark-500 border-primary-600">
|
<h1 className="text-lg font-bold pb-6 border-b dark:border-dark-500 border-primary-600">
|
||||||
<Link href="/">Tabletop Commander</Link>
|
<Link href="/">Tabletop Commander</Link>
|
||||||
|
@ -34,7 +34,6 @@ export const DevToolboxContextProvider: FC<
|
|||||||
> = (
|
> = (
|
||||||
{ children, isDev },
|
{ children, isDev },
|
||||||
) => {
|
) => {
|
||||||
console.log(isDev);
|
|
||||||
const [tools, setTools] = useState<Record<string, ReactNode>>({});
|
const [tools, setTools] = useState<Record<string, ReactNode>>({});
|
||||||
const [shouldShowDevTools, setShouldShowDevTools] = useState(isDev);
|
const [shouldShowDevTools, setShouldShowDevTools] = useState(isDev);
|
||||||
|
|
||||||
|
@ -37,8 +37,6 @@ export const TTCMD: FC<Props> = (
|
|||||||
setHasEscapedTOC(escapeTOC(toc));
|
setHasEscapedTOC(escapeTOC(toc));
|
||||||
}, [escapeTOC, toc]);
|
}, [escapeTOC, toc]);
|
||||||
|
|
||||||
console.log("mdId", parserId);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<MDSkeletonLoader />}>
|
<Suspense fallback={<MDSkeletonLoader />}>
|
||||||
<DevTool id={parserId}>
|
<DevTool id={parserId}>
|
||||||
|
5
global.d.ts
vendored
Normal file
5
global.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { DHSecretClient } from "./lib/secret";
|
||||||
|
|
||||||
|
export declare global {
|
||||||
|
var Secrets: DHSecretClient;
|
||||||
|
}
|
99
lib/secret/index.ts
Normal file
99
lib/secret/index.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||||
|
// import { writeFile } from "fs/promises";
|
||||||
|
|
||||||
|
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 });
|
||||||
|
// writeFileSync(this.cacheLocation, "{}", { encoding: "utf-8", flag: "wx" });
|
||||||
|
// 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() {
|
||||||
|
// const cache = readFileSync(this.cacheLocation, "utf-8");
|
||||||
|
|
||||||
|
// this.cache = JSON.parse(cache || "{}");
|
||||||
|
// }
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
12
lib/secret/init.ts
Normal file
12
lib/secret/init.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { DHSecretClient } from ".";
|
||||||
|
|
||||||
|
if (!globalThis.Secrets) {
|
||||||
|
globalThis.Secrets = new DHSecretClient(
|
||||||
|
"https://dragonshoard.cyborggrizzly.com",
|
||||||
|
process.env.NODE_ENV === "development"
|
||||||
|
? "./.dragonshoard"
|
||||||
|
: "/.dragonshoard",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SecretClient = () => globalThis.Secrets;
|
@ -1,4 +1,6 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {};
|
const nextConfig = {
|
||||||
|
output: "standalone",
|
||||||
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
5532
package-lock.json
generated
Normal file
5532
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -32,9 +32,12 @@
|
|||||||
"next-env.d.ts",
|
"next-env.d.ts",
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
".next/types/**/*.ts"
|
".next/types/**/*.ts",
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"global.d.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user