Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add production database connection to payload #23

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion packages/core/installMachine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ const createInstallMachine = (initialContext: InstallMachineContext) => {
deployVercelProjectActor: createStepMachine(
fromPromise<void, InstallMachineContext, AnyEventObject>(async ({ input }) => {
try {
await deployVercelProject();
await deployVercelProject(input.stateData.options.usePayload);
input.stateData.stepsCompleted.deployVercelProject = true;
saveStateToRcFile(input.stateData, input.projectDir);
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { execAsync } from '../../../utils/execAsync';
import { logger } from '../../../utils/logger';

export const createMigration = async () => {
await logger.withSpinner('payload', 'Creating migration...', async (spinner) => {
try {
await execAsync('mkdir migrations');
await execAsync('npx payload migrate:create');
spinner.succeed('Migration created.');
} catch (error) {
spinner.fail('Failed to create migration.');
console.error(error);
process.exit(1);
}
});
};
11 changes: 8 additions & 3 deletions packages/core/installMachine/installSteps/payload/install.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { preparePayloadConfig } from './preparePayloadConfig';
import { prepareTsConfig } from './prepareTsConfig';
import { removeTurboFlag } from './removeTurboFlag';
import { updatePackages } from './updatePackages';
import { moveFilesToAppDir } from './moveFilesToAppDir';
import { runInstallCommand } from './runInstallCommand';
import { updatePackageJson } from './updatePackageJson';
import { preparePayloadConfig } from './preparePayloadConfig';
import { createMigration } from './createMigration';
import { updateTurboJson } from './updateTurboJson';

export const preparePayload = async () => {
process.chdir('./apps/web/');
Expand All @@ -12,8 +14,11 @@ export const preparePayload = async () => {
await updatePackages();
await moveFilesToAppDir();
await runInstallCommand();
await removeTurboFlag();
await updatePackageJson();
await preparePayloadConfig();
await createMigration();

process.chdir('../../');

await updateTurboJson();
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { existsSync, type PathLike } from 'fs';
import { existsSync } from 'fs';
import fs from 'fs/promises';
import { logger } from '../../../utils/logger';
import { join } from 'path';
Expand All @@ -14,19 +14,40 @@ export const preparePayloadConfig = async () => {
await logger.withSpinner('payload', 'Preparing config...', async (spinner) => {
try {
// Read the payload.config.ts file
const data = await fs.readFile(payloadConfigPath, 'utf8');
let data = await fs.readFile(payloadConfigPath, 'utf8');

// Use regex to find the "pool" object and append "schemaName: 'payload'" to the pool configuration
const updatedConfig = data.replace(/pool:\s*{([^}]*)connectionString[^}]*}/, (match, group1) => {
if (match.includes('schemaName')) {
return match; // If "schemaName" already exists, return the match unchanged
}
// Append schemaName to the existing pool configuration (avoiding the extra comma)
return match.replace(group1.trimEnd(), `${group1.trimEnd()} schemaName: 'payload',\n`);
});
const postgresAdapterImport = `import { postgresAdapter } from '@payloadcms/db-postgres'`;
const vercelPostgresAdapterImport = `import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'`;

// Add the vercelPostgresAdapter import after postgresAdapter if it's not already present
if (!data.includes(vercelPostgresAdapterImport)) {
data = data.replace(postgresAdapterImport, `${postgresAdapterImport}\n${vercelPostgresAdapterImport}`);
} else {
console.log('vercelPostgresAdapter import is already present.');
}

// Step 2: Replace the db configuration with conditional configuration
const newDbConfig = `db: process.env.POSTGRES_URL
? vercelPostgresAdapter({
schemaName: "payload",
pool: {
connectionString: process.env.POSTGRES_URL || "",
},
})
: postgresAdapter({
schemaName: "payload",
pool: {
connectionString: process.env.DATABASE_URI || "",
},
})`;

data = data.replace(
/db:\s*postgresAdapter\(\{[\s\S]*?pool:\s*\{[\s\S]*?connectionString:[\s\S]*?\}[\s\S]*?\}\)/m,
newDbConfig,
);

// Write the updated payload.config.ts back to the file
await fs.writeFile(payloadConfigPath, updatedConfig);
await fs.writeFile(payloadConfigPath, data);

spinner.succeed('Config prepared.');
} catch (err) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { execAsync } from '../../../utils/execAsync';
import { logger } from '../../../utils/logger';
import { loadEnvFile } from './utils/loadEnvFile';

export const runInstallCommand = async () => {
await logger.withSpinner('payload', 'Installing to Next.js...', async (spinner) => {
loadEnvFile('../../supabase/.env');
try {
await execAsync(
`echo y | npx create-payload-app@beta --db postgres --db-connection-string ${process.env.DB_URL}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { promises as fs } from 'fs';
import path from 'path';
import { logger } from '../../../utils/logger';

export const updatePackageJson = async () => {
const packageJsonPath = path.resolve('package.json');
logger.withSpinner('payload', 'Updating package.json...', async (spinner) => {
try {
// Read and parse package.json
const packageData = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));

// Add db script to package.json
packageData.scripts = {
...packageData.scripts,
'db:migrate': 'npx payload migrate',
};

// Payload doesn't work with Turbopack yet
// Remove '--turbo' flag from the "dev" script
if (packageData.scripts && packageData.scripts.dev) {
packageData.scripts.dev = packageData.scripts.dev.replace('--turbo', '').trim();
}

// Write the modified package.json
await fs.writeFile(packageJsonPath, JSON.stringify(packageData, null, 2));
spinner.succeed('Updated package.json');
} catch (error) {
spinner.fail('Failed to update package.json');
console.error('Error updating files:', error);
}
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const updatePackages = async () => {

await logger.withSpinner('payload', 'Installing necessary packages...', async (spinner) => {
try {
await execAsync(`pnpm i pg sharp --reporter silent`);
await execAsync(`pnpm i pg sharp @payloadcms/db-vercel-postgres --reporter silent`);
spinner.succeed('Installed necessary packages!');
} catch (error) {
spinner.fail('Failed to install necessary packages!');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { promises as fs } from 'fs';
import path from 'path';
import { logger } from '../../../utils/logger';

export const updateTurboJson = async () => {
const turboJsonPath = path.resolve('turbo.json');
await logger.withSpinner('payload', 'Updating turbo.json...', async (spinner) => {
try {
// Read and parse turbo.json
const turboData = JSON.parse(await fs.readFile(turboJsonPath, 'utf8'));

// Update turbo.json with the new structure
turboData.tasks = {
...turboData.tasks,
'web#db:migrate': {
cache: false,
},
build: {
dependsOn: ['^web#db:migrate', '^build'],
outputs: ['dist/**'],
},
'build:core': {
outputs: ['dist/**'],
},
};

turboData.globalEnv = [
'SUPABASE_JWT_SECRET',
'POSTGRES_URL',
'PAYLOAD_SECRET',
'SUPABASE_SERVICE_ROLE_KEY',
'NEXT_PUBLIC_*',
'PORT',
];

// Write the modified turbo.json
await fs.writeFile(turboJsonPath, JSON.stringify(turboData, null, 2));
spinner.succeed('turbo.json updated.');
} catch (error) {
spinner.fail('Failed to update turbo.json.');
console.error('Error updating files:', error);
}
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { execAsync } from "../../../utils/execAsync";
import { logger } from "../../../utils/logger";

export const initializeSupabaseProject = async () => {
await logger.withSpinner('supabase', 'Initializing project...', async (spinner) => {
try {
await execAsync(`npx supabase init`);
spinner.succeed('Project initialized.');
} catch (error: any) {
const errorMessage = error.stderr;
if (errorMessage.includes('file exists')) {
spinner.succeed('Configuration file already exists.');
} else {
spinner.fail('Failed to initialize project.');
console.error(
'Please review the error message below, follow the initialization instructions, and try running "create-stapler-app" again.',
);
process.exit(1);
}
}
});
};
44 changes: 6 additions & 38 deletions packages/core/installMachine/installSteps/supabase/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,9 @@ import { templateGenerator } from '../../../utils/generator/generator';
import { getTemplateDirectory } from '../../../utils/getTemplateDirectory';
import { logger } from '../../../utils/logger';
import { execAsync } from '../../../utils/execAsync';

const supabaseLogin = async () => {
await logger.withSpinner('supabase', 'Logging in...', async (spinner) => {
try {
await execAsync('npx supabase projects list');
spinner.succeed('Already logged in.');
} catch (error) {
try {
await execAsync('npx supabase login');
spinner.succeed('Logged in successfully.');
} catch {
spinner.fail('Failed to log in to Supabase.');
console.error('Please log in manually with "supabase login" and re-run "create-stapler-app".');
process.exit(1);
}
}
});
};

const initializeSupabaseProject = async () => {
await logger.withSpinner('supabase', 'Initializing project...', async (spinner) => {
try {
await execAsync(`npx supabase init`);
spinner.succeed('Project initialized.');
} catch (error: any) {
const errorMessage = error.stderr;
if (errorMessage.includes('file exists')) {
spinner.succeed('Configuration file already exists.');
} else {
spinner.fail('Failed to initialize project.');
console.error(
'Please review the error message below, follow the initialization instructions, and try running "create-stapler-app" again.',
);
process.exit(1);
}
}
});
};
import { initializeSupabaseProject } from './initializeSupabaseProject';
import { modifySupabaseConfig } from './modifySupabaseConfig';
import { supabaseLogin } from './supabaseLogin';

export const installSupabase = async (destinationDirectory: string) => {
try {
Expand All @@ -69,6 +34,9 @@ export const installSupabase = async (destinationDirectory: string) => {
spinner.succeed('Files added.');
});

// Modify supabase/config.toml to enable db.pooler
await modifySupabaseConfig(destinationDirectory);

process.chdir('supabase');

await logger.withSpinner('supabase', 'Installing dependencies...', async (spinner) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import path from 'path';
import fs from 'fs';
import { logger } from '../../../utils/logger';

export const modifySupabaseConfig = async (destinationDirectory: string) => {
const configPath = path.join(destinationDirectory, 'supabase', 'config.toml');
await logger.withSpinner('supabase', 'Modifying config.toml...', async (spinner) => {
if (!fs.existsSync(configPath)) {
console.error(`config.toml file not found at ${configPath}`);
process.exit(1);
}

try {
const configContent = fs.readFileSync(configPath, 'utf-8');

// Modify [db.pooler] enabled = false to enabled = true
let modifiedContent;
if (configContent.includes('[db.pooler]')) {
modifiedContent = configContent.replace(/\[db\.pooler\]\s+enabled\s*=\s*false/, '[db.pooler]\nenabled = true');
} else {
// Append the [db.pooler] section at the end if it doesn't exist
modifiedContent = `${configContent}\n[db.pooler]\nenabled = true\n`;
}

fs.writeFileSync(configPath, modifiedContent, 'utf-8');
spinner.succeed('config.toml modified.');
} catch (error) {
spinner.fail('Failed to modify config.toml.');
console.error(error);
process.exit(1);
}
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { execAsync } from "../../../utils/execAsync";
import { logger } from "../../../utils/logger";

export const supabaseLogin = async () => {
await logger.withSpinner('supabase', 'Logging in...', async (spinner) => {
try {
await execAsync('npx supabase projects list');
spinner.succeed('Already logged in.');
} catch (error) {
try {
await execAsync('npx supabase login');
spinner.succeed('Logged in successfully.');
} catch {
spinner.fail('Failed to log in to Supabase.');
console.error('Please log in manually with "supabase login" and re-run "create-stapler-app".');
process.exit(1);
}
}
});
};
Loading