feat(prisma): add db:resetadmin and db:setup scripts for database management

This commit is contained in:
Fatih Kadir Akın
2026-01-06 10:47:08 +03:00
parent 4e4d170b94
commit 9296d2c2f6
5 changed files with 434 additions and 904 deletions

View File

@@ -13,6 +13,8 @@
"db:push": "prisma db push",
"db:studio": "prisma studio",
"db:seed": "prisma db seed",
"db:resetadmin": "npx tsx prisma/reset-admin.ts",
"db:setup": "prisma generate && prisma migrate dev && db:seed",
"setup": "node scripts/setup.js",
"generate:examples": "npx tsx scripts/generate-examples.ts",
"postinstall": "prisma generate"

View File

@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "users" ALTER COLUMN "dailyGenerationLimit" SET DEFAULT 3,
ALTER COLUMN "generationCreditsRemaining" SET DEFAULT 3;

40
prisma/reset-admin.ts Normal file
View File

@@ -0,0 +1,40 @@
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcryptjs";
const prisma = new PrismaClient();
async function main() {
console.log("🔐 Resetting admin user...");
const password = await bcrypt.hash("password123", 12);
const admin = await prisma.user.upsert({
where: { email: "admin@prompts.chat" },
update: {
password: password,
role: "ADMIN",
},
create: {
email: "admin@prompts.chat",
username: "admin",
name: "Admin User",
password: password,
role: "ADMIN",
locale: "en",
},
});
console.log("✅ Admin user reset successfully!");
console.log("\n📋 Credentials:");
console.log(" Email: admin@prompts.chat");
console.log(" Password: password123");
}
main()
.catch((e) => {
console.error("❌ Failed to reset admin:", e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,86 @@
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const p = require('@clack/prompts');
const color = require('picocolors');
const CONFIG_FILE = path.join(__dirname, '..', 'prompts.config.ts');
const ENV_FILE = path.join(__dirname, '..', '.env');
function generateAuthSecret() {
return crypto.randomBytes(32).toString('base64');
}
function buildDatabaseUrl(config) {
const { host, port, username, password, database } = config;
return `postgresql://${username}:${password}@${host}:${port}/${database}`;
}
function generateEnvFile(config) {
const isLocalhost = config.env.domain.includes('localhost');
const protocol = isLocalhost ? 'http' : 'https';
let envContent = `# Generated by setup script
# prompts.chat environment configuration
# Database
DATABASE_URL="${config.env.databaseUrl}"
# Authentication
AUTH_SECRET="${config.env.authSecret}"
AUTH_URL="${protocol}://${config.env.domain}"
AUTH_TRUST_HOST=true
# Cron Job Secret
CRON_SECRET="${crypto.randomBytes(16).toString('hex')}"
`;
if (config.auth.providers.includes('github')) {
envContent += `
# GitHub OAuth (configure at https://github.com/settings/developers)
AUTH_GITHUB_ID=""
AUTH_GITHUB_SECRET=""
`;
}
if (config.auth.providers.includes('google')) {
envContent += `
# Google OAuth (configure at https://console.cloud.google.com/apis/credentials)
AUTH_GOOGLE_ID=""
AUTH_GOOGLE_SECRET=""
`;
}
if (config.auth.providers.includes('apple')) {
envContent += `
# Apple Sign In
AUTH_APPLE_ID=""
AUTH_APPLE_SECRET=""
`;
}
if (config.auth.providers.includes('azure')) {
envContent += `
# Microsoft Azure AD
AUTH_AZURE_AD_CLIENT_ID=""
AUTH_AZURE_AD_CLIENT_SECRET=""
AUTH_AZURE_AD_ISSUER=""
`;
}
if (config.features.aiSearch || config.features.aiGeneration) {
envContent += `
# AI Features
OPENAI_API_KEY=""
# OPENAI_BASE_URL=https://api.openai.com/v1
# OPENAI_EMBEDDING_MODEL=text-embedding-3-small
# OPENAI_GENERATIVE_MODEL=gpt-4o-mini
`;
}
return envContent;
}
function generateConfig(config) {
const sponsorsSection = config.sponsors.length > 0
@@ -97,6 +173,7 @@ async function main() {
p.intro(color.bgCyan(color.black(' prompts.chat - Private Clone Setup ')));
const config = {
env: {},
branding: {},
theme: {},
auth: {},
@@ -105,6 +182,53 @@ async function main() {
sponsors: []
};
// === ENVIRONMENT ===
p.log.step(color.cyan('Environment Configuration'));
const envConfig = await p.group({
domain: () => p.text({
message: 'Application domain',
placeholder: 'localhost:3000',
defaultValue: 'localhost:3000',
}),
}, { onCancel: handleCancel });
config.env.domain = envConfig.domain;
config.env.authSecret = generateAuthSecret();
// === DATABASE ===
p.log.step(color.cyan('Database Configuration'));
const dbConfig = await p.group({
host: () => p.text({
message: 'Database host',
placeholder: 'localhost',
defaultValue: 'localhost',
}),
port: () => p.text({
message: 'Database port',
placeholder: '5432',
defaultValue: '5432',
}),
database: () => p.text({
message: 'Database name',
placeholder: 'prompts',
defaultValue: 'prompts',
}),
username: () => p.text({
message: 'Database username',
placeholder: 'postgres',
defaultValue: 'postgres',
}),
password: () => p.password({
message: 'Database password',
mask: '*',
}),
}, { onCancel: handleCancel });
config.env.database = dbConfig;
config.env.databaseUrl = buildDatabaseUrl(dbConfig);
// === BRANDING ===
p.log.step(color.cyan('Branding'));
@@ -190,12 +314,13 @@ async function main() {
const authProviders = await p.multiselect({
message: 'Select authentication providers',
options: [
{ value: 'credentials', label: 'Email/Password', hint: 'Traditional auth (recommended)' },
{ value: 'github', label: 'GitHub OAuth', hint: 'Most popular for developers' },
{ value: 'google', label: 'Google OAuth', hint: 'Widely used' },
{ value: 'apple', label: 'Apple Sign In', hint: 'Sign in with Apple' },
{ value: 'azure', label: 'Microsoft Azure AD', hint: 'Enterprise SSO' },
{ value: 'credentials', label: 'Email/Password', hint: 'Traditional auth' },
],
initialValues: ['credentials'],
required: false,
});
@@ -370,57 +495,56 @@ async function main() {
s.stop('Generated prompts.config.ts');
// === ENV FILE ===
const envExample = path.join(__dirname, '..', '.env.example');
const envFile = path.join(__dirname, '..', '.env');
s.start('Creating .env file...');
const envContent = generateEnvFile(config);
fs.writeFileSync(ENV_FILE, envContent);
s.stop('Created .env file');
if (!fs.existsSync(envFile) && fs.existsSync(envExample)) {
const createEnv = await p.confirm({
message: 'Create .env file from .env.example?',
initialValue: true,
});
if (!p.isCancel(createEnv) && createEnv) {
fs.copyFileSync(envExample, envFile);
p.log.success('Created .env file');
}
}
// === REQUIRED ENV VARS ===
const envVars = [
'DATABASE_URL - PostgreSQL connection string',
'AUTH_SECRET - NextAuth secret (openssl rand -base64 32)',
];
// === ADDITIONAL ENV VARS NEEDED ===
const additionalEnvVars = [];
if (config.auth.providers.includes('github')) {
envVars.push('AUTH_GITHUB_ID - GitHub OAuth client ID');
envVars.push('AUTH_GITHUB_SECRET - GitHub OAuth client secret');
additionalEnvVars.push('AUTH_GITHUB_ID - GitHub OAuth client ID');
additionalEnvVars.push('AUTH_GITHUB_SECRET - GitHub OAuth client secret');
}
if (config.auth.providers.includes('google')) {
envVars.push('AUTH_GOOGLE_ID - Google OAuth client ID');
envVars.push('AUTH_GOOGLE_SECRET - Google OAuth client secret');
additionalEnvVars.push('AUTH_GOOGLE_ID - Google OAuth client ID');
additionalEnvVars.push('AUTH_GOOGLE_SECRET - Google OAuth client secret');
}
if (config.auth.providers.includes('azure')) {
envVars.push('AUTH_AZURE_AD_CLIENT_ID - Azure AD client ID');
envVars.push('AUTH_AZURE_AD_CLIENT_SECRET - Azure AD client secret');
envVars.push('AUTH_AZURE_AD_ISSUER - Azure AD issuer URL');
additionalEnvVars.push('AUTH_AZURE_AD_CLIENT_ID - Azure AD client ID');
additionalEnvVars.push('AUTH_AZURE_AD_CLIENT_SECRET - Azure AD client secret');
additionalEnvVars.push('AUTH_AZURE_AD_ISSUER - Azure AD issuer URL');
}
if (config.auth.providers.includes('apple')) {
envVars.push('AUTH_APPLE_ID - Apple Services ID');
envVars.push('AUTH_APPLE_SECRET - Apple secret key');
additionalEnvVars.push('AUTH_APPLE_ID - Apple Services ID');
additionalEnvVars.push('AUTH_APPLE_SECRET - Apple secret key');
}
if (config.features.aiSearch || config.features.aiGeneration) {
envVars.push('OPENAI_API_KEY - OpenAI API key for AI features');
additionalEnvVars.push('OPENAI_API_KEY - OpenAI API key for AI features');
}
p.note(envVars.join('\n'), 'Required environment variables');
if (additionalEnvVars.length > 0) {
p.log.warn(color.yellow('⚠️ Action required: You must configure the following in .env before the app will work:'));
additionalEnvVars.forEach(v => p.log.message(` ${color.yellow('→')} ${v}`));
p.log.message('');
}
// === NEXT STEPS ===
p.note(
`1. Edit ${color.cyan('.env')} with your database and auth credentials\n` +
`2. Add your logo files to the ${color.cyan('public/')} folder\n` +
`3. Run: ${color.cyan('npm run db:push')}\n` +
`4. Run: ${color.cyan('npm run dev')}`,
'Next steps'
);
const nextSteps = additionalEnvVars.length > 0
? `1. Edit ${color.cyan('.env')} to add OAuth credentials (if using OAuth providers)\n` +
`2. Add your logo files to the ${color.cyan('public/')} folder\n` +
`3. Run: ${color.cyan('npm run db:push')}\n` +
`4. Run: ${color.cyan('npm run db:seed')} (optional - seed with prompts)\n` +
`5. Run: ${color.cyan('npm run dev')}`
: `1. Add your logo files to the ${color.cyan('public/')} folder\n` +
`2. Run: ${color.cyan('npm run db:push')}\n` +
`3. Run: ${color.cyan('npm run db:seed')} (optional - seed with prompts)\n` +
`4. Run: ${color.cyan('npm run dev')}`;
p.note(nextSteps, 'Next steps');
p.outro(color.green('Setup complete! See SELF-HOSTING.md for more details.'));
}