@buildappolis/env-manager
Version:
Enterprise-grade environment variable management system with encryption, snapshots, and validation
188 lines (150 loc) • 5.36 kB
text/typescript
import type { APIRoute } from 'astro';
import EnvDatabase from '../../lib/database.js';
import { getDatabase } from '../../lib/session.js';
import fs from 'fs/promises';
import path from 'path';
export const POST: APIRoute = async ({ request }) => {
try {
const db = getDatabase();
// Check if authenticated
if (!db.isAuthenticated()) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
const { type, password } = await request.json();
if (!type || !['env', 'env-example'].includes(type)) {
return new Response(JSON.stringify({ error: 'Invalid export type. Must be "env" or "env-example"' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// For actual .env export, require password confirmation
if (type === 'env') {
if (!password) {
return new Response(JSON.stringify({ error: 'Password confirmation required for .env export' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// Validate password by attempting authentication
const testDb = new EnvDatabase();
const isPasswordValid = testDb.authenticate(password);
if (!isPasswordValid) {
return new Response(JSON.stringify({ error: 'Invalid password' }), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
}
const variables = db.getAllVariables();
let content = '';
let filename = '';
if (type === 'env-example') {
content = generateEnvExample(variables);
filename = '.env.example';
} else {
content = generateEnv(variables);
filename = '.env';
}
// Add warning notice at the top
const warningNotice = type === 'env'
? `# WARNING: To use this .env file, you must disable env-manager in your config\n# Otherwise, env-manager will override these values\n# Set ENV_MANAGER_ENABLED=false in your config to disable\n\n`
: `# Environment Variables Example\n# Copy this file to .env and fill in the actual values\n# Remember to disable env-manager in config if using .env files directly\n\n`;
content = warningNotice + content;
// Write file to project root (go up from env-manager directory)
const projectRoot = process.env.PROJECT_ROOT || path.resolve(process.cwd(), '..');
const filePath = path.join(projectRoot, filename);
await fs.writeFile(filePath, content, 'utf-8');
return new Response(JSON.stringify({
success: true,
message: `${filename} has been created in your project root`,
path: filePath
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
console.error('Export error:', error);
return new Response(JSON.stringify({ error: 'Failed to export file' }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
function generateEnvExample(variables: any[]): string {
const categories = groupVariablesByCategory(variables);
let content = '';
for (const [category, vars] of Object.entries(categories)) {
content += `# ${category}\n`;
for (const variable of vars as any[]) {
if (variable.description) {
content += `# ${variable.description}\n`;
}
const exampleValue = generateExampleValue(variable);
content += `${variable.name}=${exampleValue}\n`;
}
content += '\n';
}
return content;
}
function generateEnv(variables: any[]): string {
const categories = groupVariablesByCategory(variables);
let content = '';
for (const [category, vars] of Object.entries(categories)) {
content += `# ${category}\n`;
for (const variable of vars as any[]) {
if (variable.description) {
content += `# ${variable.description}\n`;
}
content += `${variable.name}=${variable.value}\n`;
}
content += '\n';
}
return content;
}
function groupVariablesByCategory(variables: any[]): Record<string, any[]> {
const categories: Record<string, any[]> = {};
for (const variable of variables) {
const category = variable.category || 'General';
if (!categories[category]) {
categories[category] = [];
}
categories[category].push(variable);
}
return categories;
}
function generateExampleValue(variable: any): string {
if (variable.sensitive) {
if (variable.name.includes('SECRET') || variable.name.includes('KEY')) {
return 'your_secret_key_here';
}
if (variable.name.includes('PASSWORD')) {
return 'your_password_here';
}
if (variable.name.includes('TOKEN')) {
return 'your_token_here';
}
return 'your_sensitive_value_here';
}
if (variable.type === 'url') {
if (variable.name.includes('DATABASE')) {
return 'postgresql://user:password@localhost:5432/database';
}
return 'https://example.com';
}
if (variable.type === 'boolean') {
return 'true';
}
if (variable.type === 'number') {
return '3000';
}
if (variable.name.includes('PORT')) {
return '3000';
}
if (variable.name.includes('EMAIL')) {
return 'user@example.com';
}
return 'example_value';
}