codalware-auth
Version:
Complete authentication system with enterprise security, attack protection, team workspaces, waitlist, billing, UI components, 2FA, and account recovery - production-ready in 5 minutes. Enhanced CLI with verification, rollback, and App Router scaffolding.
1,008 lines (807 loc) • 31.1 kB
JavaScript
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
/**
* AuthCore Full Template Copy Script
* Copies ALL source code into the developer's project for complete customization
*/
/**
* Copy directory recursively
*/
function copyDirectoryRecursive(sourceDir, targetDir, options = {}) {
const { rewriteImports = false, excludePatterns = [], importAlias = '@/', rewriteBase } = options;
if (!fs.existsSync(sourceDir)) {
console.warn(`⚠️ Source directory not found: ${sourceDir}`);
return [];
}
const copied = [];
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
const sourcePath = path.join(sourceDir, entry.name);
const targetPath = path.join(targetDir, entry.name);
// Check exclusion patterns
const shouldExclude = excludePatterns.some(pattern => {
if (typeof pattern === 'string') {
return entry.name === pattern;
}
if (pattern instanceof RegExp) {
return pattern.test(entry.name);
}
return false;
});
if (shouldExclude) {
continue;
}
if (entry.isDirectory()) {
copied.push(...copyDirectoryRecursive(sourcePath, targetPath, options));
} else {
copied.push(copyFileWithTransforms(sourcePath, targetPath, { rewriteImports, importAlias, rewriteBase }));
}
}
return copied;
}
/**
* Copy a single file applying optional transforms
*/
function copyFileWithTransforms(sourcePath, targetPath, options = {}) {
const { rewriteImports = false, importAlias = '@/', rewriteBase } = options;
const ext = path.extname(sourcePath);
const isCodeFile = ['.ts', '.tsx', '.js', '.jsx', '.cts', '.mts'].includes(ext);
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
if (!rewriteImports || !isCodeFile) {
fs.copyFileSync(sourcePath, targetPath);
return targetPath;
}
const baseDir = rewriteBase || path.dirname(sourcePath);
let relativePath = path.relative(baseDir, sourcePath);
const isWithinBase = relativePath && !relativePath.startsWith('..') && !path.isAbsolute(relativePath);
if (!isWithinBase) {
relativePath = '';
}
const normalizedRelative = relativePath ? relativePath.replace(/\\/g, '/') : '';
const rewriteBaseDir = isWithinBase ? baseDir : undefined;
let content = fs.readFileSync(sourcePath, 'utf8');
content = rewriteImportsToAlias(content, importAlias, normalizedRelative, rewriteBaseDir);
fs.writeFileSync(targetPath, content);
return targetPath;
}
/**
* Rewrite imports from 'codalware-auth' to local paths
*/
function rewriteImportsToAlias(content, importAlias = '@/', fileRelativePath = '', baseDir) {
const aliasPrefix = importAlias.endsWith('/') ? importAlias : `${importAlias}/`;
const posix = path.posix;
const toAliasPath = (relativePath) => {
const cleaned = relativePath.replace(/^\/+/, '');
return `${aliasPrefix}${cleaned}`;
};
const codalwareMappings = [
['codalware-auth/styles/theme.css', 'styles/theme.css'],
['codalware-auth/styles/tailwind-preset', 'styles/tailwind-preset'],
['codalware-auth/styles', 'styles'],
['codalware-auth/server', 'lib/auth'],
['codalware-auth/validation', 'lib/validation'],
['codalware-auth/utils', 'lib/utils'],
['codalware-auth/hooks', 'hooks'],
['codalware-auth/types', 'types'],
['codalware-auth/email', 'email'],
['codalware-auth/i18n', 'i18n'],
['codalware-auth/components', 'components'],
['codalware-auth', 'components']
];
const transformSpecifier = (specifier) => {
if (!specifier) return specifier;
for (const [pkgPath, targetPath] of codalwareMappings) {
if (specifier === pkgPath) {
return toAliasPath(targetPath);
}
if (specifier.startsWith(`${pkgPath}/`)) {
const remainder = specifier.slice(pkgPath.length + 1);
return toAliasPath(`${targetPath}/${remainder}`);
}
}
if (specifier.startsWith('.')) {
if (!fileRelativePath || !baseDir) {
return specifier;
}
const fromDir = posix.dirname(fileRelativePath || '.');
const absoluteTarget = path.resolve(baseDir, fromDir, specifier);
let relativeTarget = path.relative(baseDir, absoluteTarget);
if (!relativeTarget || relativeTarget.startsWith('..') || path.isAbsolute(relativeTarget)) {
return specifier;
}
relativeTarget = relativeTarget.replace(/\\/g, '/');
return toAliasPath(relativeTarget);
}
return specifier;
};
const replaceWithAlias = (match, prefix, quote, specifier, suffix = '') => {
const updated = transformSpecifier(specifier);
if (updated === specifier) {
return match;
}
return `${prefix}${quote}${updated}${quote}${suffix}`;
};
let rewritten = content;
rewritten = rewritten.replace(/(from\s+)(['"])([^'"\n]+)\2/g, (match, prefix, quote, spec) =>
replaceWithAlias(match, prefix, quote, spec)
);
rewritten = rewritten.replace(/(import\()(\s*)(['"])([^'"\n]+)\3(\s*)(\))/g, (match, open, preSpace, quote, spec, postSpace, close) => {
const updated = transformSpecifier(spec);
if (updated === spec) {
return match;
}
return `${open}${preSpace}${quote}${updated}${quote}${postSpace}${close}`;
});
rewritten = rewritten.replace(/(require\()(\s*)(['"])([^'"\n]+)\3(\s*)(\))/g, (match, open, preSpace, quote, spec, postSpace, close) => {
const updated = transformSpecifier(spec);
if (updated === spec) {
return match;
}
return `${open}${preSpace}${quote}${updated}${quote}${postSpace}${close}`;
});
return rewritten;
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* Copy all components
*/
function copyComponents(packageRoot, projectRoot) {
console.log('📦 Copying components...');
const sourceDir = path.join(packageRoot, 'src', 'components');
const targetDir = path.join(projectRoot, 'src', 'components', 'authcore');
const copied = copyDirectoryRecursive(sourceDir, targetDir, {
rewriteImports: true,
excludePatterns: ['.eslintrc.json', 'index.ts'],
rewriteBase: path.join(packageRoot, 'src')
});
console.log(`✅ Copied ${copied.length} component files to src/components/authcore/\n`);
return copied;
}
/**
* Copy all hooks
*/
function copyHooks(packageRoot, projectRoot) {
console.log('📦 Copying hooks...');
const sourceDir = path.join(packageRoot, 'src', 'hooks');
const targetDir = path.join(projectRoot, 'src', 'hooks', 'authcore');
const copied = copyDirectoryRecursive(sourceDir, targetDir, {
rewriteImports: true,
excludePatterns: ['.eslintrc.json', 'index.ts'],
rewriteBase: path.join(packageRoot, 'src')
});
console.log(`✅ Copied ${copied.length} hook files to src/hooks/authcore/\n`);
return copied;
}
/**
* Copy server utilities
*/
function copyServerUtils(packageRoot, projectRoot) {
console.log('📦 Copying server utilities...');
const copied = [];
// Copy auth logic
const authSource = path.join(packageRoot, 'src', 'auth');
const authTarget = path.join(projectRoot, 'src', 'lib', 'auth');
copied.push(...copyDirectoryRecursive(authSource, authTarget, {
rewriteImports: true,
excludePatterns: ['.eslintrc.json'],
rewriteBase: path.join(packageRoot, 'src')
}));
// Copy validation
const validationSource = path.join(packageRoot, 'src', 'validation');
const validationTarget = path.join(projectRoot, 'src', 'lib', 'validation');
copied.push(...copyDirectoryRecursive(validationSource, validationTarget, {
rewriteImports: true,
excludePatterns: ['.eslintrc.json'],
rewriteBase: path.join(packageRoot, 'src')
}));
// Copy utils
const utilsSource = path.join(packageRoot, 'src', 'utils');
const utilsTarget = path.join(projectRoot, 'src', 'lib', 'utils');
if (fs.existsSync(utilsSource)) {
copied.push(...copyDirectoryRecursive(utilsSource, utilsTarget, {
rewriteImports: true,
excludePatterns: ['.eslintrc.json'],
rewriteBase: path.join(packageRoot, 'src')
}));
}
console.log(`✅ Copied ${copied.length} server utility files to src/lib/\n`);
return copied;
}
/**
* Copy types
*/
function copyTypes(packageRoot, projectRoot) {
console.log('📦 Copying type definitions...');
const sourceDir = path.join(packageRoot, 'src', 'types');
const targetDir = path.join(projectRoot, 'src', 'types', 'authcore');
const copied = copyDirectoryRecursive(sourceDir, targetDir, {
rewriteImports: true,
rewriteBase: path.join(packageRoot, 'src')
});
console.log(`✅ Copied ${copied.length} type files to src/types/authcore/\n`);
return copied;
}
/**
* Copy i18n
*/
function copyI18n(packageRoot, projectRoot) {
console.log('📦 Copying i18n translations...');
const sourceDir = path.join(packageRoot, 'src', 'i18n');
const targetDir = path.join(projectRoot, 'src', 'lib', 'i18n');
const copied = copyDirectoryRecursive(sourceDir, targetDir, {
rewriteImports: true,
rewriteBase: path.join(packageRoot, 'src')
});
// Copy locale files
const localesSource = path.join(packageRoot, 'locales');
const localesTarget = path.join(projectRoot, 'locales');
if (fs.existsSync(localesSource)) {
copied.push(...copyDirectoryRecursive(localesSource, localesTarget));
}
console.log(`✅ Copied ${copied.length} i18n files\n`);
return copied;
}
/**
* Copy API routes from templates
* Respects src/ directory structure if it exists
*/
function copyApiRoutes(packageRoot, projectRoot, routerType = 'app') {
console.log('🔌 Copying API routes...');
const copied = [];
// Detect if project uses src/ directory
const useSrcDirectory = fs.existsSync(path.join(projectRoot, 'src'));
const baseTarget = useSrcDirectory ? path.join(projectRoot, 'src') : projectRoot;
if (useSrcDirectory) {
console.log(' 📂 Using src/ directory structure for API routes');
}
if (routerType === 'app') {
// App Router API routes
const appApiSource = path.join(packageRoot, 'templates', 'app', 'api');
const appApiTarget = path.join(baseTarget, 'app', 'api');
if (fs.existsSync(appApiSource)) {
copied.push(...copyDirectoryRecursive(appApiSource, appApiTarget, {
rewriteImports: true,
rewriteBase: path.join(packageRoot, 'templates', 'app')
}));
}
// App Router actions
const appActionsSource = path.join(packageRoot, 'templates', 'app', 'actions');
const appActionsTarget = path.join(baseTarget, 'app', 'actions');
if (fs.existsSync(appActionsSource)) {
copied.push(...copyDirectoryRecursive(appActionsSource, appActionsTarget, {
rewriteImports: true,
rewriteBase: path.join(packageRoot, 'templates', 'app')
}));
}
// App Router services
const appServicesSource = path.join(packageRoot, 'templates', 'app', 'services');
const appServicesTarget = path.join(baseTarget, 'app', 'services');
if (fs.existsSync(appServicesSource)) {
copied.push(...copyDirectoryRecursive(appServicesSource, appServicesTarget, {
rewriteImports: true,
rewriteBase: path.join(packageRoot, 'templates', 'app')
}));
}
} else if (routerType === 'pages') {
// Pages Router API routes
const pagesApiSource = path.join(packageRoot, 'templates', 'pages', 'api');
const pagesApiTarget = path.join(baseTarget, 'pages', 'api');
if (fs.existsSync(pagesApiSource)) {
copied.push(...copyDirectoryRecursive(pagesApiSource, pagesApiTarget, {
rewriteImports: true,
rewriteBase: path.join(packageRoot, 'templates', 'pages')
}));
}
}
console.log(`✅ Copied ${copied.length} API route files\n`);
return copied;
}
/**
* Create index files for easy imports
*/
function createIndexFiles(projectRoot) {
console.log('📝 Creating index files...');
// Components index
const componentsIndex = `/**
* AuthCore Components
* All authentication components - fully customizable
*
* Generated by: npx authcore init --template-mode=full
* You own this code - modify as needed!
*/
// Auth Forms
export { default as LoginForm } from './LoginForm';
export { default as RegisterForm } from './RegisterForm';
export { default as AuthForm } from './AuthForm';
// 2FA
export { default as TwoFactorSetup } from './TwoFactorSetup';
// User Management
export { default as UserProfile } from './UserProfile';
export { default as ProfileDropdown } from './ProfileDropdown';
export { default as ProfileTabs } from './ProfileTabs';
// Organization
export { CreateOrganization } from './organization/CreateOrganization';
export { OrganizationSwitcher } from './organization/OrganizationSwitcher';
// Security
export { SecuritySettings } from './security/SecuritySettings';
export { EmailListManager } from './security/EmailListManager';
// Layout
export { default as DashboardLayout } from './DashboardLayout';
export { default as PageLayout } from './PageLayout';
// HOCs
export { default as withAuthentication } from './withAuthentication';
// Utility
export { default as LoadingSpinner } from './LoadingSpinner';
export { default as ThemeSwitcher } from './ThemeSwitcher';
`;
fs.writeFileSync(
path.join(projectRoot, 'src', 'components', 'authcore', 'index.ts'),
componentsIndex
);
// Hooks index
const hooksIndex = `/**
* AuthCore Hooks
* All authentication hooks - fully customizable
*
* Generated by: npx authcore init --template-mode=full
* You own this code - modify as needed!
*/
export { useAuth } from './useAuth';
export { useLogin } from './useLogin';
export { useRegister } from './useRegister';
export { useTwoFactor } from './useTwoFactor';
export { useAuthPolicy } from './useAuthPolicy';
export { useMagicLink } from './useMagicLink';
export { usePasswordChange } from './usePasswordChange';
export { useUserProfile } from './useUserProfile';
export { useUserDevices } from './useUserDevices';
export { useAuditLogs } from './useAuditLogs';
export { useMutations } from './useMutations';
`;
fs.writeFileSync(
path.join(projectRoot, 'src', 'hooks', 'authcore', 'index.ts'),
hooksIndex
);
console.log('✅ Created index files\n');
/**
* Create README explaining the template
*/
function createTemplateReadme(projectRoot) {
const readme = `# AuthCore - Full Template Mode
You've installed AuthCore in **Full Template Mode**! 🎉
## What This Means
Instead of importing components from \`codalware-auth\`, you now have:
- ✅ **Complete source code** in your project
- ✅ **Full customization** - modify any file
- ✅ **No dependency lock-in** - the code is yours
- ✅ **Easy debugging** - all code is visible
## Project Structure
\`\`\`
src/
├── components/authcore/ ← All UI components
│ ├── LoginForm.tsx
│ ├── RegisterForm.tsx
│ ├── AuthForm.tsx
│ └── ... (all other components)
│
├── hooks/authcore/ ← All authentication hooks
│ ├── useAuth.ts
│ ├── useLogin.ts
│ └── ... (all other hooks)
│
├── lib/
│ ├── auth/ ← Server-side auth logic
│ ├── validation/ ← Form validation schemas
│ ├── i18n/ ← Internationalization
│ └── utils/ ← Utility functions
│
├── types/authcore/ ← TypeScript definitions
│
└── app/api/auth/ ← API routes (already generated)
styles/ ← AuthCore styles and Tailwind preset
├── tailwind-preset.js ← Tailwind CSS preset
└── authcore-theme.css ← Compiled theme styles
\`\`\`
## Usage
### Import Components
\`\`\`typescript
// Instead of:
// import { LoginForm } from 'codalware-auth';
// Use:
import { LoginForm } from '@/components/authcore';
\`\`\`
### Import Hooks
\`\`\`typescript
// Instead of:
// import { useAuth } from 'codalware-auth';
// Use:
import { useAuth } from '@/hooks/authcore';
\`\`\`
### Import Server Utils
\`\`\`typescript
// Instead of:
// import { AuthService } from 'codalware-auth/server';
// Use:
import { AuthService } from '@/lib/auth';
\`\`\`
## Customization
### Modify Components
Want to change the LoginForm design? Just edit:
\`src/components/authcore/LoginForm.tsx\`
### Modify Auth Logic
Want to change registration flow? Edit:
\`src/lib/auth/logic/auth.ts\`
### Modify Validation
Want custom password rules? Edit:
\`src/lib/validation/schemas.ts\`
## Package Dependency
The \`codalware-auth\` package is now **optional**. You can:
1. Keep it for potential updates
2. Remove it completely - your code will still work!
To remove the dependency:
\`\`\`bash
npm uninstall codalware-auth
\`\`\`
## Updates
Since you own the code, updates work differently:
1. Check for new features: \`npx authcore check-updates\`
2. See what changed: \`npx authcore diff\`
3. Manually apply updates you want
Or regenerate specific parts:
\`\`\`bash
npx authcore generate components # Regenerate components
npx authcore generate hooks # Regenerate hooks
\`\`\`
## Support
- **Documentation**: https://authcore-liard.vercel.app/
- **Examples**: https://authcore-liard.vercel.app/examples
- **Customization Guide**: https://authcore-liard.vercel.app/guides/customization
---
**You own this code!** Modify it however you want. Good luck! 🚀
`;
fs.writeFileSync(
path.join(projectRoot, 'AUTHCORE_TEMPLATE_README.md'),
readme
);
console.log('✅ Created AUTHCORE_TEMPLATE_README.md\n');
}
async function copyMainTemplateWrapper(options = {}) {
return copyFullTemplate(options);
}
// ESM main guard: run when executed directly via node
if (import.meta.url === `file://${process.argv[1]}` || import.meta.url === pathToFileURL(process.argv[1]).href) {
// Running as CLI: copy full template
copyFullTemplate().then(() => {
console.log('Full template copied successfully.');
}).catch(err => {
console.error('Failed to copy full template:', err && err.message ? err.message : err);
process.exit(1);
});
}
/**
* Create .env.example file for environment setup
*/
function createEnvExample(projectRoot) {
const envPath = path.join(projectRoot, '.env.example');
if (fs.existsSync(envPath)) {
console.log('✅ .env.example already exists\n');
return;
}
console.log('📝 Creating .env.example...');
const envExample = `# AuthCore Environment Variables
# Copy this file to .env.local and fill in your values
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/authcore"
# Email Configuration (choose one method)
# Method 1: Gmail (App Password)
GMAIL_ACCOUNT_EMAIL="your-email@gmail.com"
GOOGLE_PASS="your-app-password"
# Method 2: SMTP Server
EMAIL_SERVER_HOST="smtp.gmail.com"
EMAIL_SERVER_PORT="587"
EMAIL_SERVER_USER="your-email@gmail.com"
EMAIL_SERVER_PASSWORD="your-password"
# Admin User (for initial setup)
ADMIN_EMAIL="admin@example.com"
ADMIN_PASSWORD="secure-admin-password"
ADMIN_NAME="Admin User"
# Development
NODE_ENV="development"
# Skip wizard on startup (optional)
SKIP_AUTHCORE_WIZARD="false"
# Optional: NextAuth (if using NextAuth provider)
NEXTAUTH_SECRET="your-random-secret-generate-with-openssl-rand-base64-32"
NEXTAUTH_URL="http://localhost:3000"
# Optional: Social Providers
# GOOGLE_CLIENT_ID="your-google-client-id"
# GOOGLE_CLIENT_SECRET="your-google-client-secret"
# GITHUB_CLIENT_ID="your-github-client-id"
# GITHUB_CLIENT_SECRET="your-github-client-secret"
# Optional: Stripe (for billing features)
# STRIPE_SECRET_KEY="sk_test_..."
# STRIPE_WEBHOOK_SECRET="whsec_..."
# NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_..."
# Optional: Resend (alternative to SMTP)
# RESEND_API_KEY="re_..."
# Optional: Development settings
# LOG_LEVEL="debug"
`;
fs.writeFileSync(envPath, envExample);
console.log('✅ Created .env.example\n');
}
/**
* Copy AuthCore as main source code (flat structure)
* This is the actual implementation for main template mode
*/
async function copyMainTemplate(options = {}) {
const packageRoot = path.join(__dirname, '..');
const projectRoot = process.cwd();
const srcBase = path.join(packageRoot, 'src');
console.log('🏠 Copying AuthCore as main source code...\n');
console.log('📍 From:', packageRoot);
console.log('📍 To:', projectRoot);
console.log('');
// Detect project structure (src or root)
const useSrcDirectory = fs.existsSync(path.join(projectRoot, 'src'));
const baseTarget = useSrcDirectory ? path.join(projectRoot, 'src') : projectRoot;
if (useSrcDirectory) {
console.log('📂 Detected "src" directory structure.');
} else {
console.log('📂 Detected root directory structure.');
}
console.log('');
const allCopied = [];
try {
console.log('📦 Copying AuthCore source tree...');
const srcEntries = fs.readdirSync(srcBase, { withFileTypes: true });
for (const entry of srcEntries) {
const sourcePath = path.join(srcBase, entry.name);
const targetPath = path.join(baseTarget, entry.name);
if (entry.isDirectory()) {
allCopied.push(...copyDirectoryRecursive(sourcePath, targetPath, {
rewriteImports: true,
rewriteBase: srcBase
}));
} else {
const rewriteImports = ['.ts', '.tsx', '.js', '.jsx', '.cts', '.mts'].includes(path.extname(entry.name));
const copied = copyFileWithTransforms(sourcePath, targetPath, {
rewriteImports,
rewriteBase: srcBase
});
if (copied) {
allCopied.push(copied);
}
}
}
console.log(`✅ Copied ${allCopied.length} files from src/ tree\n`);
// Copy API routes - detect router type
const hasAppDir = fs.existsSync(path.join(projectRoot, 'app')) ||
(useSrcDirectory && fs.existsSync(path.join(projectRoot, 'src', 'app')));
const hasPagesDir = fs.existsSync(path.join(projectRoot, 'pages')) ||
(useSrcDirectory && fs.existsSync(path.join(projectRoot, 'src', 'pages')));
const routerType = hasAppDir ? 'app' : (hasPagesDir ? 'pages' : 'app'); // default to app
allCopied.push(...copyApiRoutes(packageRoot, projectRoot, routerType));
// Copy supporting root-level directories
const rootDirectories = ['config', 'locales', 'styles', 'prisma'];
for (const dir of rootDirectories) {
const dirSource = path.join(packageRoot, dir);
if (fs.existsSync(dirSource)) {
const dirTarget = path.join(projectRoot, dir);
allCopied.push(...copyDirectoryRecursive(dirSource, dirTarget));
}
}
// Copy root-level config file
const rootConfigPath = path.join(packageRoot, 'config.ts');
if (fs.existsSync(rootConfigPath)) {
const copied = copyFileWithTransforms(rootConfigPath, path.join(projectRoot, 'config.ts'), {
rewriteImports: false
});
if (copied) {
allCopied.push(copied);
}
}
// Create README
createMainTemplateReadme(projectRoot);
// Create .env.example
createEnvExample(projectRoot);
console.log('✨ Main template copy complete!\n');
console.log(`📦 Copied ${allCopied.length} source files`);
console.log('');
console.log('📖 Read AUTHCORE_TEMPLATE_README.md for usage instructions');
console.log('');
console.log('💡 Quick Start:');
console.log(` import { LoginForm } from '@/components';`);
console.log(` import { useAuth } from '@/hooks';`);
console.log('');
console.log(`🏠 Your ${useSrcDirectory ? 'src/' : ''} directory is now AuthCore - customize everything!`);
console.log('');
return allCopied;
} catch (error) {
console.error('❌ Failed to copy main template:', error.message);
if (process.env.DEBUG) {
console.error(error);
}
process.exit(1);
}
}
/**
* Create README for main template
*/
function createMainTemplateReadme(projectRoot) {
const readmePath = path.join(projectRoot, 'AUTHCORE_TEMPLATE_README.md');
const content = `# AuthCore Main Template
This project was initialized with AuthCore as the main source code. AuthCore components, hooks, and utilities are now your primary codebase.
## What's Included
- **Components**: All AuthCore UI components in \`src/components/\`
- **Hooks**: Authentication hooks in \`src/hooks/\`
- **Server Utils**: Authentication logic in \`src/lib/auth/\`
- **Validation**: Form validation utilities in \`src/lib/validation/\`
- **Utils**: Helper functions in \`src/lib/utils/\`
- **I18n**: Internationalization in \`src/lib/i18n/\`
- **Types**: TypeScript definitions in \`src/types/\`
- **Styles**: Tailwind preset and CSS in \`styles/\`
- **Locales**: Translation files in \`locales/\`
## Quick Start
\`\`\`tsx
import { LoginForm, RegisterForm } from '@/components';
import { useAuth } from '@/hooks';
export default function App() {
const { user, login, logout } = useAuth();
return (
<div>
{user ? (
<div>
<p>Welcome, {user.email}!</p>
<button onClick={logout}>Logout</button>
</div>
) : (
<LoginForm onSubmit={login} />
)}
</div>
);
}
\`\`\`
## Configuration
1. Copy \`.env.example\` to \`.env.local\`
2. Configure your database and email settings
3. Run database migrations: \`npm run migrate\`
4. Start the development server: \`npm run dev\`
## Customization
Since AuthCore is now your main source code, you can:
- Modify any component in \`src/components/\`
- Add new authentication flows in \`src/hooks/\`
- Extend server utilities in \`src/lib/\`
- Update styles in \`styles/\`
- Add new locales in \`locales/\`
## Database Setup
This template includes Prisma schema and migrations. To set up:
\`\`\`bash
# Install dependencies
npm install
# Set up database
npm run setup-db
# Run migrations
npm run migrate
# Seed database (optional)
npm run seed
\`\`\`
## API Routes
AuthCore provides the following API routes:
- \`POST /api/auth/login\` - User login
- \`POST /api/auth/register\` - User registration
- \`POST /api/auth/logout\` - User logout
- \`GET /api/auth/session\` - Get current session
- \`POST /api/auth/forgot-password\` - Request password reset
- \`POST /api/auth/reset-password\` - Reset password
## Next Steps
1. Review and customize the components in \`src/components/\`
2. Configure your authentication providers in \`src/lib/auth/\`
3. Set up your database connection in \`.env.local\`
4. Test the authentication flows
5. Deploy your application
## Support
For questions or issues, check the [AuthCore documentation](https://authcore.dev/docs) or create an issue on GitHub.
`;
fs.writeFileSync(readmePath, content);
}
/**
* Copy AuthCore as full template with authcore/ subdirectories
*/
function copyFullTemplate(options = {}) {
const packageRoot = path.join(__dirname, '..');
const projectRoot = process.cwd();
const srcRoot = path.join(projectRoot, 'src');
console.log('📦 Copying AuthCore full template...\n');
console.log('📍 From:', packageRoot);
console.log('📍 To:', projectRoot);
console.log('');
if (!fs.existsSync(srcRoot)) {
console.warn('⚠️ No src/ directory detected. Creating src/ before copying template files.');
fs.mkdirSync(srcRoot, { recursive: true });
}
const allCopied = [];
try {
const srcDirectories = [
'adapters',
'auth',
'components',
'hooks',
'i18n',
'lib',
'types',
'utils',
'validation',
'email',
'config'
];
const srcBase = path.join(packageRoot, 'src');
for (const dir of srcDirectories) {
const sourceDir = path.join(srcBase, dir);
const targetDir = path.join(srcRoot, dir);
allCopied.push(...copyDirectoryRecursive(sourceDir, targetDir, {
rewriteImports: true,
rewriteBase: srcBase
}));
}
const srcFiles = ['errors.ts', 'index.ts', 'pack-index.ts'];
for (const file of srcFiles) {
const copied = copyFileWithTransforms(
path.join(srcBase, file),
path.join(srcRoot, file),
{ rewriteImports: true, rewriteBase: srcBase }
);
if (copied) {
allCopied.push(copied);
}
}
const rootDirectories = ['config', 'locales', 'styles', 'prisma'];
for (const dir of rootDirectories) {
const sourceDir = path.join(packageRoot, dir);
const targetDir = path.join(projectRoot, dir);
allCopied.push(...copyDirectoryRecursive(sourceDir, targetDir));
}
const rootFiles = ['config.ts'];
for (const file of rootFiles) {
const copied = copyFileWithTransforms(
path.join(packageRoot, file),
path.join(projectRoot, file),
{ rewriteImports: false }
);
if (copied) {
allCopied.push(copied);
}
}
// Create .env.example if missing
createEnvExample(projectRoot);
console.log('✨ Full template copy complete!\n');
console.log(`📦 Copied ${allCopied.length} files into your project`);
console.log('');
console.log('💡 Quick Start:');
console.log(' import { LoginForm } from \"@/components\";');
console.log(' import { useAuth } from \"@/hooks\";');
console.log('');
console.log('🔧 AuthCore source is now available directly under src/ — customize freely.');
console.log('');
return allCopied;
} catch (error) {
console.error('❌ Failed to copy full template:', error.message);
if (process.env.DEBUG) {
console.error(error);
}
process.exit(1);
}
}
// Export for use in CLI (CommonJS exports)
module.exports = { copyFullTemplate, copyMainTemplate };
// Run if called directly
if (require.main === module) {
copyFullTemplate().catch(err => {
console.error('Failed:', err);
process.exit(1);
});
}