wcz-layout
Version:
142 lines (141 loc) • 5.34 kB
JavaScript
import z$1 from "zod";
import axios from "axios";
import fs from "node:fs";
import https from "node:https";
import path from "node:path";
import { loadEnv } from "vite";
//#region src/models/VaultConfig.ts
const VaultConfigSchema = z$1.object({
VAULT_ADDRESS: z$1.url(),
VAULT_USERNAME: z$1.string().min(1),
VAULT_PASSWORD: z$1.string().min(1),
VAULT_SECRET_PATH: z$1.string().min(1),
VAULT_MOUNT_POINT: z$1.string().default("release")
});
//#endregion
//#region src/lib/vite-plugin.ts
async function fetchVaultSecrets(env) {
const { VAULT_ADDRESS, VAULT_USERNAME, VAULT_PASSWORD, VAULT_SECRET_PATH, VAULT_MOUNT_POINT } = VaultConfigSchema.parse(env);
const client = axios.create({
baseURL: VAULT_ADDRESS,
httpsAgent: new https.Agent({ rejectUnauthorized: false })
});
const { data: loginData } = await client.post(`/v1/auth/userpass/login/${VAULT_USERNAME}`, { password: VAULT_PASSWORD });
const { data: secretData } = await client.get(`/v1/${VAULT_MOUNT_POINT}/data/${VAULT_SECRET_PATH}`, { headers: { "X-Vault-Token": loginData.auth.client_token } });
const secrets = secretData?.data?.data;
if (!secrets) throw new Error("No secrets found");
return Object.fromEntries(Object.entries(secrets).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)]));
}
async function loadVaultSecrets(command) {
if (command !== "serve") return;
const env = loadEnv("development", process.cwd(), "");
if (!env.VAULT_ADDRESS) return;
try {
const secrets = await fetchVaultSecrets(env);
Object.entries(secrets).forEach(([key, value]) => {
process.env[key] ??= value;
});
} catch (e) {
console.warn("[vite:wcz-layout] Vault failed:", e instanceof Error ? e.message : e);
}
}
function loadLocaleResources(localesPath, addWatchFile) {
if (!fs.existsSync(localesPath)) fs.mkdirSync(localesPath, { recursive: true });
const enPath = path.join(localesPath, "en.json");
if (!fs.existsSync(enPath)) fs.writeFileSync(enPath, JSON.stringify({}));
const files = fs.readdirSync(localesPath);
const resources = {};
for (const file of files.filter((f) => f.endsWith(".json"))) {
const lang = path.basename(file, ".json");
const filePath = path.join(localesPath, file);
try {
resources[lang] = { translation: JSON.parse(fs.readFileSync(filePath, "utf-8")) };
} catch {
resources[lang] = { translation: {} };
}
addWatchFile(filePath);
}
return resources;
}
function ensurePermissionsFile(permissionsPath, addWatchFile) {
const dir = path.dirname(permissionsPath);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
if (!fs.existsSync(permissionsPath)) fs.writeFileSync(permissionsPath, "export const permissions = {\n admin: [\"wcz-developers\"]\n} as const;");
addWatchFile(permissionsPath);
}
function ensureScopesFile(scopesPath, addWatchFile) {
const dir = path.dirname(scopesPath);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
if (!fs.existsSync(scopesPath)) fs.writeFileSync(scopesPath, "export const scopes = {\n} as const;");
addWatchFile(scopesPath);
}
function viteWczLayout() {
const virtualModuleId = "virtual:wcz-layout";
const resolvedVirtualModuleId = "\0" + virtualModuleId;
let localesPath;
let permissionsPath;
let scopesPath;
return {
name: "vite:wcz-layout",
enforce: "pre",
configResolved(resolvedConfig) {
localesPath = path.resolve(resolvedConfig.root, "src/locales");
permissionsPath = path.resolve(resolvedConfig.root, "src/lib/auth/permissions.ts");
scopesPath = path.resolve(resolvedConfig.root, "src/lib/auth/scopes.ts");
},
async config(_, { command }) {
await loadVaultSecrets(command);
return { optimizeDeps: {
exclude: [virtualModuleId],
include: [
"prop-types",
"react-is",
"hoist-non-react-statics",
"url-parse",
"file-saver",
"attr-accept"
]
} };
},
configureServer(server) {
const refresh = (filePath) => {
if (filePath.includes(localesPath) || filePath === permissionsPath || filePath === scopesPath) {
const module = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
if (module) server.moduleGraph.invalidateModule(module);
server.ws.send({
type: "full-reload",
path: "*"
});
}
};
server.watcher.add([
localesPath,
permissionsPath,
scopesPath
]);
server.watcher.on("add", refresh);
server.watcher.on("change", refresh);
server.watcher.on("unlink", refresh);
},
resolveId(id) {
if (id === virtualModuleId) return resolvedVirtualModuleId;
return null;
},
load(id) {
if (id !== resolvedVirtualModuleId) return null;
const resources = loadLocaleResources(localesPath, this.addWatchFile.bind(this));
ensurePermissionsFile(permissionsPath, this.addWatchFile.bind(this));
ensureScopesFile(scopesPath, this.addWatchFile.bind(this));
const normalizedPermissionsPath = permissionsPath.split(path.sep).join(path.posix.sep);
const normalizedScopesPath = scopesPath.split(path.sep).join(path.posix.sep);
return `
export const resources = ${JSON.stringify(resources)};
export { permissions } from "${normalizedPermissionsPath}";
export { scopes } from "${normalizedScopesPath}";
`;
}
};
}
//#endregion
export { viteWczLayout };
//# sourceMappingURL=vite.js.map