Expand env heuristics from common examples
All checks were successful
Build Windows App / build-windows (push) Successful in 16m37s
All checks were successful
Build Windows App / build-windows (push) Successful in 16m37s
This commit is contained in:
66
src/App.tsx
66
src/App.tsx
@@ -329,10 +329,26 @@ function inferDefaultValue(key: string, value: string): string | null {
|
|||||||
const isEmpty = value.length === 0;
|
const isEmpty = value.length === 0;
|
||||||
const shouldFillGeneric = hasPlaceholder || isEmpty;
|
const shouldFillGeneric = hasPlaceholder || isEmpty;
|
||||||
|
|
||||||
|
if (signal === "APP_ENV" || signal === "RAILS_ENV") {
|
||||||
|
return "production";
|
||||||
|
}
|
||||||
|
|
||||||
if (signal === "NODE_ENV") {
|
if (signal === "NODE_ENV") {
|
||||||
return "production";
|
return "production";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signal.includes("LOG_LEVEL")) {
|
||||||
|
return "info";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.includes("TELEMETRY_ENABLED") || signal.includes("PROMETHEUS_METRICS") || signal.includes("METRICS_ENABLED")) {
|
||||||
|
return "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.includes("CORS_CREDENTIALS") || signal.includes("USE_SSL") || signal.includes("REQUIRE_SSL")) {
|
||||||
|
return "true";
|
||||||
|
}
|
||||||
|
|
||||||
if (signal.includes("BOOTSTRAP_ADMIN_EMAIL") || signal.includes("ADMIN_EMAIL")) {
|
if (signal.includes("BOOTSTRAP_ADMIN_EMAIL") || signal.includes("ADMIN_EMAIL")) {
|
||||||
return "admin@example.local";
|
return "admin@example.local";
|
||||||
}
|
}
|
||||||
@@ -349,10 +365,50 @@ function inferDefaultValue(key: string, value: string): string | null {
|
|||||||
return "admin@example.local";
|
return "admin@example.local";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldFillGeneric && (signal.includes("BASE_URL") || signal.endsWith("_URL") || signal.endsWith("_URI") || signal.includes("ORIGIN"))) {
|
if (shouldFillGeneric && (signal.includes("BASE_URL") || signal.endsWith("_URL") || signal.endsWith("_URI") || signal.includes("ORIGIN") || signal.includes("SITE_URL"))) {
|
||||||
return "https://example.local";
|
return "https://example.local";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && signal.includes("CORS_ORIGIN")) {
|
||||||
|
return "https://example.local";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && signal.includes("SMTP_HOST")) {
|
||||||
|
return "smtp.example.local";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && signal.includes("SMTP_PORT")) {
|
||||||
|
return "587";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && signal.includes("SMTP_USER")) {
|
||||||
|
return "smtp-user";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && (signal.includes("SMTP_FROM") || signal.includes("MAIL_FROM") || signal.includes("SENDER_EMAIL"))) {
|
||||||
|
return "noreply@example.local";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && (signal.includes("S3_BUCKET") || signal.includes("BUCKET_NAME"))) {
|
||||||
|
return "app-bucket";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && (signal.includes("S3_REGION") || signal.includes("AWS_REGION") || signal.includes("SQS_REGION"))) {
|
||||||
|
return "eu-central-1";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && signal.includes("S3_ENDPOINT")) {
|
||||||
|
return "https://s3.example.local";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && signal.includes("RABBITMQ_URI")) {
|
||||||
|
return "amqp://rabbitmq:5672";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFillGeneric && signal.includes("REDIS_URL")) {
|
||||||
|
return "redis://redis:6379/0";
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldFillGeneric && signal.includes("PORT")) {
|
if (shouldFillGeneric && signal.includes("PORT")) {
|
||||||
if (signal.includes("POSTGRES")) {
|
if (signal.includes("POSTGRES")) {
|
||||||
return "5432";
|
return "5432";
|
||||||
@@ -362,6 +418,14 @@ function inferDefaultValue(key: string, value: string): string | null {
|
|||||||
return "6379";
|
return "6379";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signal.includes("SMTP")) {
|
||||||
|
return "587";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.includes("S3") || signal.includes("MINIO")) {
|
||||||
|
return "9000";
|
||||||
|
}
|
||||||
|
|
||||||
return "3000";
|
return "3000";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
108
src/env.ts
108
src/env.ts
@@ -145,6 +145,8 @@ type Requirement =
|
|||||||
| { type: "integer"; min: number; max: number; label: string }
|
| { type: "integer"; min: number; max: number; label: string }
|
||||||
| { type: "url"; label: string }
|
| { type: "url"; label: string }
|
||||||
| { type: "email"; label: string }
|
| { type: "email"; label: string }
|
||||||
|
| { type: "external"; value: string; label: string }
|
||||||
|
| { type: "prefixed"; prefix: string; length: number; alphabet: string; label: string }
|
||||||
| { type: "secret"; length: number; label: string }
|
| { type: "secret"; length: number; label: string }
|
||||||
| { type: "password"; length: number; urlSafe: boolean; label: string };
|
| { type: "password"; length: number; urlSafe: boolean; label: string };
|
||||||
|
|
||||||
@@ -179,10 +181,84 @@ function inferRequirement(placeholder: string, key: string): Requirement {
|
|||||||
return { type: "integer", min: 1024, max: 49151, label: "TCP port number" };
|
return { type: "integer", min: 1024, max: 49151, label: "TCP port number" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["BOOLEAN", "BOOL", "ENABLED", "DISABLED", "USE_SSL", "REQUIRE_SSL"])) {
|
||||||
|
return { type: "external", value: "false", label: "boolean flag" };
|
||||||
|
}
|
||||||
|
|
||||||
if (placeholderSignal.includes("EMAIL") || keySignal.includes("EMAIL")) {
|
if (placeholderSignal.includes("EMAIL") || keySignal.includes("EMAIL")) {
|
||||||
return { type: "email", label: "email address" };
|
return { type: "email", label: "email address" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["STRIPE_WEBHOOK_SECRET", "WEBHOOK_SECRET"])) {
|
||||||
|
return {
|
||||||
|
type: "prefixed",
|
||||||
|
prefix: "whsec_",
|
||||||
|
length: 32,
|
||||||
|
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
||||||
|
label: "Stripe-style webhook secret"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["STRIPE_SECRET_KEY"])) {
|
||||||
|
return {
|
||||||
|
type: "prefixed",
|
||||||
|
prefix: "sk_test_",
|
||||||
|
length: 32,
|
||||||
|
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
||||||
|
label: "Stripe-style secret key"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["STRIPE_PUBLISHABLE_KEY", "PUBLISHABLE_KEY"])) {
|
||||||
|
return {
|
||||||
|
type: "prefixed",
|
||||||
|
prefix: "pk_test_",
|
||||||
|
length: 32,
|
||||||
|
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
||||||
|
label: "Stripe-style publishable key"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["RESEND_API_KEY"])) {
|
||||||
|
return {
|
||||||
|
type: "prefixed",
|
||||||
|
prefix: "re_",
|
||||||
|
length: 24,
|
||||||
|
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
||||||
|
label: "Resend-style API key"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["OPENAI_API_KEY"])) {
|
||||||
|
return {
|
||||||
|
type: "prefixed",
|
||||||
|
prefix: "sk-",
|
||||||
|
length: 48,
|
||||||
|
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
||||||
|
label: "OpenAI-style API key placeholder"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["ANTHROPIC_API_KEY"])) {
|
||||||
|
return {
|
||||||
|
type: "prefixed",
|
||||||
|
prefix: "sk-ant-",
|
||||||
|
length: 42,
|
||||||
|
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
||||||
|
label: "Anthropic-style API key placeholder"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["AWS_ACCESS_KEY_ID", "S3_ACCESS_KEY", "ACCESS_KEY_ID"])) {
|
||||||
|
return {
|
||||||
|
type: "prefixed",
|
||||||
|
prefix: "AKIA",
|
||||||
|
length: 16,
|
||||||
|
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||||
|
label: "AWS/S3 access key ID format"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (placeholderSignal.includes("PASSWORD") || keySignal.includes("PASSWORD")) {
|
if (placeholderSignal.includes("PASSWORD") || keySignal.includes("PASSWORD")) {
|
||||||
const length = placeholderSignal.includes("LONG") || keySignal.includes("ADMIN") ? 28 : 24;
|
const length = placeholderSignal.includes("LONG") || keySignal.includes("ADMIN") ? 28 : 24;
|
||||||
return {
|
return {
|
||||||
@@ -193,6 +269,30 @@ function inferRequirement(placeholder: string, key: string): Requirement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["SMTP_HOST"])) {
|
||||||
|
return { type: "external", value: "smtp.example.local", label: "SMTP host" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["SMTP_USER"])) {
|
||||||
|
return { type: "external", value: "smtp-user", label: "SMTP username" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["S3_BUCKET", "BUCKET_NAME"])) {
|
||||||
|
return { type: "external", value: "app-bucket", label: "S3 bucket name" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["S3_REGION", "AWS_REGION", "SQS_REGION"])) {
|
||||||
|
return { type: "external", value: "eu-central-1", label: "cloud region" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["LOG_LEVEL"])) {
|
||||||
|
return { type: "external", value: "info", label: "log level" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includesAny(combinedSignal, ["CORS_ORIGIN", "IFRAME_ANCESTORS", "ALLOWED_ORIGINS", "ALLOWED_HOSTS"])) {
|
||||||
|
return { type: "url", label: "allowed HTTPS origin" };
|
||||||
|
}
|
||||||
|
|
||||||
if (placeholderSignal.includes("URL") || placeholderSignal.includes("URI") || placeholderSignal.includes("ORIGIN")) {
|
if (placeholderSignal.includes("URL") || placeholderSignal.includes("URI") || placeholderSignal.includes("ORIGIN")) {
|
||||||
return { type: "url", label: "HTTPS URL" };
|
return { type: "url", label: "HTTPS URL" };
|
||||||
}
|
}
|
||||||
@@ -234,6 +334,10 @@ function generateValue(requirement: Requirement): string {
|
|||||||
return "https://example.local";
|
return "https://example.local";
|
||||||
case "email":
|
case "email":
|
||||||
return "admin@example.local";
|
return "admin@example.local";
|
||||||
|
case "external":
|
||||||
|
return requirement.value;
|
||||||
|
case "prefixed":
|
||||||
|
return `${requirement.prefix}${randomString(requirement.length, requirement.alphabet)}`;
|
||||||
case "password":
|
case "password":
|
||||||
return randomString(
|
return randomString(
|
||||||
requirement.length,
|
requirement.length,
|
||||||
@@ -273,6 +377,10 @@ function bytesToBase64(bytes: Uint8Array): string {
|
|||||||
return btoa(binary);
|
return btoa(binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function includesAny(signal: string, needles: string[]): boolean {
|
||||||
|
return needles.some((needle) => signal.includes(needle));
|
||||||
|
}
|
||||||
|
|
||||||
function extractNumberBefore(signal: string, marker: string): number | null {
|
function extractNumberBefore(signal: string, marker: string): number | null {
|
||||||
const match = signal.match(new RegExp(`(\\d+)_${marker}`));
|
const match = signal.match(new RegExp(`(\\d+)_${marker}`));
|
||||||
return match ? Number(match[1]) : null;
|
return match ? Number(match[1]) : null;
|
||||||
|
|||||||
Reference in New Issue
Block a user