Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
run: echo "HUSKY=0" >> $GITHUB_ENV

- name: 📦 Setup pnpm
uses: pnpm/action-setup@v2
uses: pnpm/action-setup@v4
with:
version: 8

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
run: echo "HUSKY=0" >> $GITHUB_ENV

- name: 📦 Setup pnpm
uses: pnpm/action-setup@v2
uses: pnpm/action-setup@v4
with:
version: 8

Expand Down
23 changes: 18 additions & 5 deletions create-db/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,24 @@ class EventCaptureError extends Error {

class PosthogEventCapture {
async capture(eventName, properties = {}) {
const POSTHOG_CAPTURE_URL = process.env.POSTHOG_API_HOST
? process.env.POSTHOG_API_HOST + "/capture"
: "https://proxyhog.prisma-data.net/capture";
const POSTHOG_KEY = process.env.POSTHOG_API_KEY || "phc_cmc85avbWyuJ2JyKdGPdv7dxXli8xLdWDBPbvIXWJfs";
const POSTHOG_API_HOST = process.env.POSTHOG_API_HOST;
const POSTHOG_KEY = process.env.POSTHOG_API_KEY;

if (
!POSTHOG_API_HOST ||
!POSTHOG_KEY ||
POSTHOG_API_HOST.trim() === "" ||
POSTHOG_KEY.trim() === ""
) {
if (process.env.NODE_ENV === "development") {
console.warn(
"Analytics disabled: missing POSTHOG_API_HOST or POSTHOG_API_KEY."
);
}
return;
}

const POSTHOG_CAPTURE_URL = `${POSTHOG_API_HOST.replace(/\/+$/, "")}/capture`;

const payload = {
api_key: POSTHOG_KEY,
Expand All @@ -36,7 +50,6 @@ class PosthogEventCapture {
throw new EventCaptureError(eventName, response.statusText);
}
} catch (error) {
// Silently fail analytics to not disrupt user experience
if (process.env.NODE_ENV === "development") {
console.error("Analytics error:", error.message);
}
Expand Down
125 changes: 100 additions & 25 deletions create-db/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#!/usr/bin/env node

import dotenv from "dotenv";
import fs from "fs";
import path from "path";

dotenv.config();

import { select, spinner, intro, outro, log, cancel } from "@clack/prompts";
Expand Down Expand Up @@ -57,6 +60,31 @@ function getCommandName() {

const CLI_NAME = getCommandName();

function readUserEnvFile() {
const userCwd = process.cwd();
const envPath = path.join(userCwd, ".env");

if (!fs.existsSync(envPath)) {
return {};
}

const envContent = fs.readFileSync(envPath, "utf8");
const envVars = {};

envContent.split("\n").forEach((line) => {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith("#")) {
const [key, ...valueParts] = trimmed.split("=");
if (key && valueParts.length > 0) {
const value = valueParts.join("=").replace(/^["']|["']$/g, "");
envVars[key.trim()] = value.trim();
}
}
});

return envVars;
}

async function showHelp() {
let regionExamples = "us-east-1, eu-west-1";
try {
Expand Down Expand Up @@ -239,7 +267,7 @@ function handleError(message, extra = "") {
process.exit(1);
}

async function promptForRegion(defaultRegion) {
async function promptForRegion(defaultRegion, userAgent) {
let regions;
try {
regions = await getRegions();
Expand All @@ -264,17 +292,20 @@ async function promptForRegion(defaultRegion) {
}

try {
await analytics.capture("create_db:region_selected", {
const analyticsProps = {
command: CLI_NAME,
region: region,
"selection-method": "interactive",
});
"user-agent": userAgent,
};

await analytics.capture("create_db:region_selected", analyticsProps);
} catch (error) {}

return region;
}

async function createDatabase(name, region, returnJson = false) {
async function createDatabase(name, region, userAgent, returnJson = false) {
let s;
if (!returnJson) {
s = spinner();
Expand All @@ -284,7 +315,11 @@ async function createDatabase(name, region, returnJson = false) {
const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ region, name, utm_source: CLI_NAME }),
body: JSON.stringify({
region,
name,
utm_source: userAgent || CLI_NAME,
}),
});

if (resp.status === 429) {
Expand All @@ -304,12 +339,18 @@ async function createDatabase(name, region, returnJson = false) {
}

try {
await analytics.capture("create_db:database_creation_failed", {
const analyticsProps = {
command: CLI_NAME,
region: region,
"error-type": "rate_limit",
"status-code": 429,
});
"user-agent": userAgent,
};

await analytics.capture(
"create_db:database_creation_failed",
analyticsProps
);
} catch (error) {}

process.exit(1);
Expand All @@ -333,13 +374,19 @@ async function createDatabase(name, region, returnJson = false) {
s.stop("Unexpected response from create service.");
}
try {
await analytics.capture("create_db:database_creation_failed", {
const analyticsProps = {
command: CLI_NAME,
region,
"error-type": "invalid_json",
"status-code": resp.status,
});
} catch {}
"user-agent": userAgent,
};

await analytics.capture(
"create_db:database_creation_failed",
analyticsProps
);
} catch (error) {}
process.exit(1);
}

Expand All @@ -363,14 +410,14 @@ async function createDatabase(name, region, returnJson = false) {
const directDbName = directConnDetails?.database || "postgres";
const directConn =
directConnDetails && directHost
? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}`
? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}?sslmode=require`
: null;

const claimUrl = `${CLAIM_DB_WORKER_URL}?projectID=${projectId}&utm_source=${CLI_NAME}&utm_medium=cli`;
const claimUrl = `${CLAIM_DB_WORKER_URL}?projectID=${projectId}&utm_source=${userAgent}&utm_medium=cli`;
const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000);

if (returnJson && !result.error) {
return {
const jsonResponse = {
connectionString: prismaConn,
directConnectionString: directConn,
claimUrl: claimUrl,
Expand All @@ -379,6 +426,12 @@ async function createDatabase(name, region, returnJson = false) {
name: database?.name,
projectId: projectId,
};

if (userAgent) {
jsonResponse.userAgent = userAgent;
}

return jsonResponse;
}

if (result.error) {
Expand All @@ -397,12 +450,18 @@ async function createDatabase(name, region, returnJson = false) {
}

try {
await analytics.capture("create_db:database_creation_failed", {
const analyticsProps = {
command: CLI_NAME,
region: region,
"error-type": "api_error",
"error-message": result.error.message,
});
"user-agent": userAgent,
};

await analytics.capture(
"create_db:database_creation_failed",
analyticsProps
);
} catch (error) {}
process.exit(1);
}
Expand Down Expand Up @@ -459,8 +518,17 @@ async function createDatabase(name, region, returnJson = false) {
async function main() {
try {
const rawArgs = process.argv.slice(2);

const { flags } = await parseArgs();

let userAgent;
const userEnvVars = readUserEnvFile();
if (userEnvVars.PRISMA_ACTOR_NAME && userEnvVars.PRISMA_ACTOR_PROJECT) {
userAgent = `${userEnvVars.PRISMA_ACTOR_NAME}/${userEnvVars.PRISMA_ACTOR_PROJECT}`;
}

try {
await analytics.capture("create_db:cli_command_ran", {
const analyticsProps = {
command: CLI_NAME,
"full-command": `${CLI_NAME} ${rawArgs.join(" ")}`.trim(),
"has-region-flag":
Expand All @@ -470,13 +538,17 @@ async function main() {
"has-help-flag": rawArgs.includes("--help") || rawArgs.includes("-h"),
"has-list-regions-flag": rawArgs.includes("--list-regions"),
"has-json-flag": rawArgs.includes("--json") || rawArgs.includes("-j"),
"has-user-agent-from-env": !!userAgent,
"node-version": process.version,
platform: process.platform,
arch: process.arch,
});
} catch (error) {}
"user-agent": userAgent,
};

const { flags } = await parseArgs();
await analytics.capture("create_db:cli_command_ran", analyticsProps);
} catch (error) {
console.error("Error:", error.message);
}

if (!flags.help && !flags.json) {
await isOffline();
Expand All @@ -499,11 +571,14 @@ async function main() {
region = flags.region;

try {
await analytics.capture("create_db:region_selected", {
const analyticsProps = {
command: CLI_NAME,
region: region,
"selection-method": "flag",
});
"user-agent": userAgent,
};

await analytics.capture("create_db:region_selected", analyticsProps);
} catch (error) {}
}

Expand All @@ -514,11 +589,11 @@ async function main() {
if (flags.json) {
try {
if (chooseRegionPrompt) {
region = await promptForRegion(region);
region = await promptForRegion(region, userAgent);
} else {
await validateRegion(region, true);
}
const result = await createDatabase(name, region, true);
const result = await createDatabase(name, region, userAgent, true);
console.log(JSON.stringify(result, null, 2));
process.exit(0);
} catch (e) {
Expand All @@ -543,12 +618,12 @@ async function main() {
)
);
if (chooseRegionPrompt) {
region = await promptForRegion(region);
region = await promptForRegion(region, userAgent);
}

region = await validateRegion(region);

await createDatabase(name, region);
await createDatabase(name, region, userAgent);

outro("");
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion create-db/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-db",
"version": "1.0.2",
"version": "1.0.3",
"description": "Instantly create a temporary Prisma Postgres database with one command, then claim and persist it in your Prisma Data Platform project when ready.",
"main": "index.js",
"author": "",
Expand Down
2 changes: 1 addition & 1 deletion create-pg/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-pg",
"version": "1.0.2",
"version": "1.0.3",
"description": "Instantly create a temporary Prisma Postgres database with one command, then claim and persist it in your Prisma Data Platform project when ready.",
"main": "index.js",
"author": "",
Expand Down
2 changes: 1 addition & 1 deletion create-postgres/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-postgres",
"version": "1.0.2",
"version": "1.0.3",
"description": "Instantly create a temporary Prisma Postgres database with one command, then claim and persist it in your Prisma Data Platform project when ready.",
"main": "index.js",
"author": "",
Expand Down