boilercraft 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +171 -0
- package/bin/boilercraft.js +110 -0
- package/cli/app.js +596 -0
- package/cli/ui.js +356 -0
- package/docs/STACK_ENGINE.md +93 -0
- package/docs/assets/cli-create.svg +52 -0
- package/docs/assets/cli-menu.svg +47 -0
- package/docs/stacks/dotnet-core.md +69 -0
- package/docs/stacks/laravel-vue.md +46 -0
- package/docs/stacks/nextjs.md +58 -0
- package/docs/stacks/node-express.md +58 -0
- package/docs/stacks/raw-php.md +130 -0
- package/main.js +82 -0
- package/package.json +86 -0
- package/public/index.html +721 -0
- package/public/logo.png +17 -0
- package/public/logo.svg +17 -0
- package/server.js +84 -0
- package/services/componentGenerator.js +195 -0
- package/services/engine/scaffoldEngine.js +517 -0
- package/services/engine/toolInstaller.js +232 -0
- package/services/engine/versionCatalog.js +115 -0
- package/services/generate.js +46 -0
- package/services/projectGenerator.js +686 -0
- package/services/readmeGenerator.js +221 -0
- package/services/themeGenerator.js +150 -0
- package/services/versionResolver.js +176 -0
- package/stacks/dotnet-core/overlays/api/Controllers/ProductsController.cs +33 -0
- package/stacks/dotnet-core/overlays/api/Program.cs +33 -0
- package/stacks/dotnet-core/overlays/api/wwwroot/index.html +38 -0
- package/stacks/dotnet-core/overlays/api-auth/Controllers/AuthController.cs +87 -0
- package/stacks/dotnet-core/overlays/common/Data/ApplicationDbContext.cs +25 -0
- package/stacks/dotnet-core/overlays/common/Models/Product.cs +19 -0
- package/stacks/dotnet-core/overlays/mvc/Controllers/ProductsController.cs +84 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Home/Index.cshtml +23 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/Create.cshtml +16 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/Delete.cshtml +16 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/Edit.cshtml +17 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/Index.cshtml +46 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/_Form.cshtml +19 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Controllers/AccountController.cs +98 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Models/AppUser.cs +48 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Views/Account/Login.cshtml +35 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Views/Account/Register.cshtml +41 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Views/Shared/_AuthNav.cshtml +22 -0
- package/stacks/dotnet-core/snippets/packages-auth.xml +4 -0
- package/stacks/dotnet-core/snippets/packages-common.xml +5 -0
- package/stacks/dotnet-core/snippets/packages-ef.xml +4 -0
- package/stacks/dotnet-core/snippets/packages-mssql.xml +4 -0
- package/stacks/dotnet-core/snippets/packages-mysql.xml +4 -0
- package/stacks/dotnet-core/snippets/packages-swagger.xml +4 -0
- package/stacks/dotnet-core/stack.json +426 -0
- package/stacks/laravel/overlays/auth/app/Http/Controllers/Api/AuthController.php +67 -0
- package/stacks/laravel/overlays/common/app/Http/Controllers/BoilerCraftController.php +14 -0
- package/stacks/laravel/overlays/common/resources/views/boilercraft.blade.php +36 -0
- package/stacks/laravel/overlays/inertia-common/resources/views/app.blade.php +15 -0
- package/stacks/laravel/overlays/inertia-react/resources/js/Pages/Welcome.jsx +18 -0
- package/stacks/laravel/overlays/inertia-react/resources/js/app.jsx +13 -0
- package/stacks/laravel/overlays/inertia-vue/resources/js/Pages/Welcome.vue +23 -0
- package/stacks/laravel/overlays/inertia-vue/resources/js/app.js +13 -0
- package/stacks/laravel/snippets/api-routes.php +9 -0
- package/stacks/laravel/snippets/web-routes-inertia.php +7 -0
- package/stacks/laravel/snippets/web-routes.php +4 -0
- package/stacks/laravel/stack.json +477 -0
- package/stacks/nextjs/overlays/auth/src/lib/auth.js +36 -0
- package/stacks/nextjs/overlays/auth-app/src/app/api/auth/login/route.js +11 -0
- package/stacks/nextjs/overlays/auth-app/src/app/api/auth/register/route.js +14 -0
- package/stacks/nextjs/overlays/auth-pages/src/pages/api/auth/login.js +14 -0
- package/stacks/nextjs/overlays/auth-pages/src/pages/api/auth/register.js +17 -0
- package/stacks/nextjs/overlays/common/.env.example +3 -0
- package/stacks/nextjs/overlays/common/.env.local +3 -0
- package/stacks/nextjs/overlays/db/mongodb/src/lib/db.js +12 -0
- package/stacks/nextjs/overlays/db/mssql/src/lib/db.js +17 -0
- package/stacks/nextjs/overlays/db/mysql/src/lib/db.js +13 -0
- package/stacks/nextjs/overlays/router-app/src/app/api/health/route.js +10 -0
- package/stacks/nextjs/overlays/router-app/src/app/layout.js +16 -0
- package/stacks/nextjs/overlays/router-app/src/app/page.js +19 -0
- package/stacks/nextjs/overlays/router-pages/src/pages/_app.js +7 -0
- package/stacks/nextjs/overlays/router-pages/src/pages/_document.js +13 -0
- package/stacks/nextjs/overlays/router-pages/src/pages/api/health.js +8 -0
- package/stacks/nextjs/overlays/router-pages/src/pages/index.js +27 -0
- package/stacks/nextjs/stack.json +257 -0
- package/stacks/node-express/overlays/auth/src/controllers/authController.js +53 -0
- package/stacks/node-express/overlays/auth/src/middlewares/auth.js +36 -0
- package/stacks/node-express/overlays/auth/src/routes/authRoutes.js +16 -0
- package/stacks/node-express/overlays/common/.env.example +5 -0
- package/stacks/node-express/overlays/common/public/index.html +31 -0
- package/stacks/node-express/overlays/common/src/server.js +32 -0
- package/stacks/node-express/overlays/db/mongodb/src/db.js +13 -0
- package/stacks/node-express/overlays/db/mssql/src/db.js +22 -0
- package/stacks/node-express/overlays/db/mysql/src/db.js +22 -0
- package/stacks/node-express/stack.json +165 -0
- package/templates/dotnet-bootstrap-mssql/Controllers/AuthController.cs +74 -0
- package/templates/dotnet-bootstrap-mssql/Controllers/ProductsController.cs +33 -0
- package/templates/dotnet-bootstrap-mssql/Data/ApplicationDbContext.cs +24 -0
- package/templates/dotnet-bootstrap-mssql/Models/Product.cs +10 -0
- package/templates/dotnet-core/Controllers/AuthController.cs +74 -0
- package/templates/laravel-full/app/Http/Controllers/AuthController.php +79 -0
- package/templates/laravel-full/app/Http/Controllers/HomeController.php +22 -0
- package/templates/laravel-full/app/Models/User.php +49 -0
- package/templates/laravel-full/artisan +22 -0
- package/templates/laravel-full/bootstrap/app.php +21 -0
- package/templates/laravel-full/config/database.php +54 -0
- package/templates/laravel-full/database/migrations/2024_01_01_000001_create_users_table.php +27 -0
- package/templates/laravel-full/public/index.php +17 -0
- package/templates/laravel-full/resources/css/app.css +3 -0
- package/templates/laravel-full/resources/js/Pages/Home.vue +76 -0
- package/templates/laravel-full/resources/js/app.js +21 -0
- package/templates/laravel-full/resources/js/bootstrap.js +3 -0
- package/templates/laravel-full/resources/views/app.blade.php +15 -0
- package/templates/laravel-full/routes/api.php +8 -0
- package/templates/laravel-full/routes/console.php +7 -0
- package/templates/laravel-full/routes/web.php +24 -0
- package/templates/laravel-full/tailwind.config.js +12 -0
- package/templates/laravel-full/vite.config.js +20 -0
- package/templates/laravel-vue/app/Http/Controllers/Auth/AuthenticatedSessionController.php +43 -0
- package/templates/laravel-vue-mysql/app/Http/Controllers/HomeController.php +25 -0
- package/templates/laravel-vue-mysql/resources/js/Pages/Home.vue +70 -0
- package/templates/nextjs/src/app/api/auth/login/route.js +28 -0
- package/templates/nextjs-mongodb/src/app/api/health/route.js +17 -0
- package/templates/nextjs-mongodb/src/lib/mongodb.js +18 -0
- package/templates/nextjs-mssql/src/app/api/health/route.js +24 -0
- package/templates/nextjs-mssql/src/lib/mssql.js +38 -0
- package/templates/node-express/src/controllers/authController.js +53 -0
- package/templates/node-express/src/middlewares/auth.js +36 -0
- package/templates/node-express/src/routes/authRoutes.js +16 -0
- package/templates/node-mongodb/src/controllers/userController.js +30 -0
- package/templates/node-mongodb/src/middlewares/errorHandler.js +8 -0
- package/templates/node-mongodb/src/models/User.js +23 -0
- package/templates/node-mongodb/src/routes/api.js +9 -0
- package/templates/raw-php-mysql/app/Controllers/AuthController.php +73 -0
- package/templates/raw-php-mysql/app/Controllers/HomeController.php +65 -0
- package/templates/raw-php-mysql/app/Models/User.php +60 -0
- package/templates/raw-php-mysql/app/Views/404.php +15 -0
- package/templates/raw-php-mysql/app/Views/auth/login.php +66 -0
- package/templates/raw-php-mysql/app/Views/auth/register.php +71 -0
- package/templates/raw-php-mysql/app/Views/dashboard.php +50 -0
- package/templates/raw-php-mysql/app/Views/home.php +83 -0
- package/templates/raw-php-mysql/config/Auth.php +62 -0
- package/templates/raw-php-mysql/config/Database.php +100 -0
- package/templates/raw-php-mysql/config/Env.php +30 -0
- package/templates/raw-php-mysql/config/Middleware.php +53 -0
- package/templates/raw-php-mysql/config/QueryBuilder.php +109 -0
- package/templates/raw-php-mysql/config/Router.php +33 -0
- package/templates/raw-php-mysql/public/index.php +57 -0
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const { resolveDependencies } = require('./versionResolver');
|
|
5
|
+
const { generateThemeCSS, getThemeSwitcherJS } = require('./themeGenerator');
|
|
6
|
+
const { generateDynamicStackReadme } = require('./readmeGenerator');
|
|
7
|
+
const {
|
|
8
|
+
getSkeletonComponent,
|
|
9
|
+
getSpinnerComponent,
|
|
10
|
+
getModalComponent,
|
|
11
|
+
getOffcanvasComponent,
|
|
12
|
+
getSwalFireComponent,
|
|
13
|
+
getToastrSonnerComponent
|
|
14
|
+
} = require('./componentGenerator');
|
|
15
|
+
|
|
16
|
+
function ensureDir(p) {
|
|
17
|
+
if (!fs.existsSync(p)) fs.mkdirSync(p, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Escapes user-supplied text before it is interpolated into generated
|
|
21
|
+
// HTML/JSX templates, so a malicious appTitle/description/author/logoUrl
|
|
22
|
+
// in the generator config can't inject markup into the scaffolded output.
|
|
23
|
+
function escapeHtml(value) {
|
|
24
|
+
return String(value ?? '')
|
|
25
|
+
.replace(/&/g, '&')
|
|
26
|
+
.replace(/</g, '<')
|
|
27
|
+
.replace(/>/g, '>')
|
|
28
|
+
.replace(/"/g, '"')
|
|
29
|
+
.replace(/'/g, ''');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function copyFolderRecursive(source, target, replaceMap = {}) {
|
|
33
|
+
ensureDir(target);
|
|
34
|
+
const files = fs.readdirSync(source);
|
|
35
|
+
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const srcPath = path.join(source, file);
|
|
38
|
+
const destPath = path.join(target, file);
|
|
39
|
+
const stat = fs.statSync(srcPath);
|
|
40
|
+
|
|
41
|
+
if (stat.isDirectory()) {
|
|
42
|
+
copyFolderRecursive(srcPath, destPath, replaceMap);
|
|
43
|
+
} else {
|
|
44
|
+
let content = fs.readFileSync(srcPath, 'utf8');
|
|
45
|
+
for (const [key, val] of Object.entries(replaceMap)) {
|
|
46
|
+
content = content.split(key).join(val);
|
|
47
|
+
}
|
|
48
|
+
fs.writeFileSync(destPath, content, 'utf8');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Universal Database Connection Generator for any backend
|
|
55
|
+
*/
|
|
56
|
+
function getUniversalDbHelper(backend, database, projectName) {
|
|
57
|
+
if (backend === 'node' || backend === 'nextjs') {
|
|
58
|
+
if (database === 'mongodb') {
|
|
59
|
+
return `
|
|
60
|
+
const mongoose = require('mongoose');
|
|
61
|
+
async function connectDB() {
|
|
62
|
+
const uri = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/${projectName}';
|
|
63
|
+
try {
|
|
64
|
+
await mongoose.connect(uri);
|
|
65
|
+
console.log('⚡ Connected to MongoDB (Mongoose)');
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error('❌ MongoDB Connection Error:', err.message);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
module.exports = { connectDB };
|
|
71
|
+
`;
|
|
72
|
+
} else if (database === 'mysql') {
|
|
73
|
+
return `
|
|
74
|
+
const mysql = require('mysql2/promise');
|
|
75
|
+
async function connectDB() {
|
|
76
|
+
try {
|
|
77
|
+
const pool = mysql.createPool({
|
|
78
|
+
host: process.env.DB_HOST || '127.0.0.1',
|
|
79
|
+
user: process.env.DB_USER || 'root',
|
|
80
|
+
password: process.env.DB_PASSWORD || '',
|
|
81
|
+
database: process.env.DB_NAME || '${projectName}_db',
|
|
82
|
+
port: parseInt(process.env.DB_PORT || '3306')
|
|
83
|
+
});
|
|
84
|
+
console.log('⚡ Connected to MySQL');
|
|
85
|
+
return pool;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error('❌ MySQL Connection Error:', err.message);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
module.exports = { connectDB };
|
|
91
|
+
`;
|
|
92
|
+
} else if (database === 'mssql') {
|
|
93
|
+
return `
|
|
94
|
+
const sql = require('mssql');
|
|
95
|
+
const config = {
|
|
96
|
+
user: process.env.DB_USER || 'sa',
|
|
97
|
+
password: process.env.DB_PASSWORD || 'YourStrong@Password',
|
|
98
|
+
server: process.env.DB_HOST || 'localhost',
|
|
99
|
+
database: process.env.DB_NAME || '${projectName}_db',
|
|
100
|
+
port: parseInt(process.env.DB_PORT || '1433'),
|
|
101
|
+
options: { encrypt: false, trustServerCertificate: true }
|
|
102
|
+
};
|
|
103
|
+
async function connectDB() {
|
|
104
|
+
try {
|
|
105
|
+
const pool = await sql.connect(config);
|
|
106
|
+
console.log('⚡ Connected to MSSQL (SQL Server)');
|
|
107
|
+
return pool;
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error('❌ MSSQL Connection Error:', err.message);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
module.exports = { connectDB, sql };
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return '';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Universal Project Generator supporting ANY COMBINATION of:
|
|
121
|
+
* - Backend: Raw PHP, Laravel, Node.js Express, Next.js, .NET Core
|
|
122
|
+
* - Frontend / UI: Vue, React, Blade, Vanilla
|
|
123
|
+
* - Database: MySQL, MongoDB, MSSQL
|
|
124
|
+
* - Styling: Tailwind CSS, Bootstrap 5, Vanilla CSS
|
|
125
|
+
* - UI Components: Skeleton, Spinner, Modal, Offcanvas
|
|
126
|
+
* - Themes: Midnight, Emerald, Royal, Crimson & Dark/Light/Device
|
|
127
|
+
*/
|
|
128
|
+
async function generateProject(config, outputBasePath = null) {
|
|
129
|
+
const projectName = (config.projectName || 'my-app').trim().toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
130
|
+
const targetDir = outputBasePath
|
|
131
|
+
? path.join(outputBasePath, projectName)
|
|
132
|
+
: path.join(__dirname, '..', 'generated', projectName);
|
|
133
|
+
|
|
134
|
+
ensureDir(targetDir);
|
|
135
|
+
|
|
136
|
+
const backend = config.backend || config.stack || 'node-express';
|
|
137
|
+
const database = config.database || 'mysql';
|
|
138
|
+
const styling = config.styling || 'tailwind';
|
|
139
|
+
const themeKey = config.theme || 'midnight';
|
|
140
|
+
const colorMode = config.colorMode || 'all';
|
|
141
|
+
|
|
142
|
+
const themeCSS = generateThemeCSS(themeKey, colorMode);
|
|
143
|
+
const themeJS = getThemeSwitcherJS();
|
|
144
|
+
|
|
145
|
+
// Sanitized copies of user-supplied text, safe to interpolate into HTML/JSX.
|
|
146
|
+
const safeAppTitle = escapeHtml(config.appTitle || projectName);
|
|
147
|
+
const safeDescription = escapeHtml(config.description || 'Enterprise grade multi-combination application');
|
|
148
|
+
const safeAuthor = escapeHtml(config.author || 'ARG RABBI');
|
|
149
|
+
const safeLogoUrl = config.logoUrl ? escapeHtml(config.logoUrl) : '';
|
|
150
|
+
|
|
151
|
+
let uiComponents = '';
|
|
152
|
+
if (config.components?.skeleton) uiComponents += getSkeletonComponent(styling) + '\n';
|
|
153
|
+
if (config.components?.spinner) uiComponents += getSpinnerComponent(styling) + '\n';
|
|
154
|
+
if (config.components?.modal) uiComponents += getModalComponent(styling) + '\n';
|
|
155
|
+
if (config.components?.offcanvas) uiComponents += getOffcanvasComponent(styling) + '\n';
|
|
156
|
+
if (config.components?.swalfire) uiComponents += getSwalFireComponent() + '\n';
|
|
157
|
+
if (config.components?.toastr) uiComponents += getToastrSonnerComponent() + '\n';
|
|
158
|
+
|
|
159
|
+
const logoHtml = safeLogoUrl
|
|
160
|
+
? `<img src="${safeLogoUrl}" alt="${safeAppTitle}" style="max-height: 60px; margin: 0 auto 1.5rem auto; display: block;" />`
|
|
161
|
+
: `<div style="width: 56px; height: 56px; border-radius: 14px; background: var(--color-primary); color: white; display: flex; align-items: center; justify-content: center; font-size: 26px; font-weight: bold; margin: 0 auto 1.5rem auto;">${escapeHtml((config.projectName || 'B')[0].toUpperCase())}</div>`;
|
|
162
|
+
|
|
163
|
+
const stylesheetLink = styling === 'bootstrap'
|
|
164
|
+
? '<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">'
|
|
165
|
+
: styling === 'tailwind'
|
|
166
|
+
? '<script src="https://cdn.tailwindcss.com"></script>'
|
|
167
|
+
: '';
|
|
168
|
+
|
|
169
|
+
const replaceMap = {
|
|
170
|
+
'{{PROJECT_NAME}}': projectName,
|
|
171
|
+
'{{APP_TITLE}}': safeAppTitle,
|
|
172
|
+
'{{APP_DESC}}': safeDescription,
|
|
173
|
+
'{{AUTHOR}}': safeAuthor,
|
|
174
|
+
'{{DB_NAME}}': `${projectName}_db`,
|
|
175
|
+
'{{DB_TYPE}}': database,
|
|
176
|
+
'{{COLOR_MODE}}': colorMode === 'all' || colorMode === 'device' ? 'dark' : colorMode,
|
|
177
|
+
'{{LOGO_HTML}}': logoHtml,
|
|
178
|
+
'{{STYLESHEET_LINK}}': stylesheetLink,
|
|
179
|
+
'{{UI_COMPONENTS}}': uiComponents
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// ==========================================
|
|
183
|
+
// 1. .NET CORE (ANY STYLING: Tailwind / Bootstrap + MSSQL / MySQL)
|
|
184
|
+
// ==========================================
|
|
185
|
+
if (backend === 'dotnet-core' || backend.includes('dotnet')) {
|
|
186
|
+
const srcDir = path.join(__dirname, '..', 'templates', 'dotnet-bootstrap-mssql');
|
|
187
|
+
copyFolderRecursive(srcDir, targetDir, replaceMap);
|
|
188
|
+
|
|
189
|
+
const dotnetVer = config.versions?.dotnet || 'net8.0';
|
|
190
|
+
const efCoreVer = dotnetVer === 'net9.0' ? '9.0.*' : '8.0.*';
|
|
191
|
+
const csproj = `<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
192
|
+
<PropertyGroup>
|
|
193
|
+
<TargetFramework>${dotnetVer}</TargetFramework>
|
|
194
|
+
<Nullable>enable</Nullable>
|
|
195
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
196
|
+
<RootNamespace>${projectName.replace(/-/g, '_')}</RootNamespace>
|
|
197
|
+
</PropertyGroup>
|
|
198
|
+
|
|
199
|
+
<ItemGroup>
|
|
200
|
+
${database === 'mysql' ? `<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="${efCoreVer}" />` : `<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="${efCoreVer}" />`}
|
|
201
|
+
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${efCoreVer}" />
|
|
202
|
+
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
|
203
|
+
</ItemGroup>
|
|
204
|
+
</Project>
|
|
205
|
+
`;
|
|
206
|
+
fs.writeFileSync(path.join(targetDir, `${projectName}.csproj`), csproj);
|
|
207
|
+
|
|
208
|
+
// Visual Studio Solution (.sln) file
|
|
209
|
+
const projectGuid = crypto.randomUUID().toUpperCase();
|
|
210
|
+
const solutionGuid = crypto.randomUUID().toUpperCase();
|
|
211
|
+
const sln = `Microsoft Visual Studio Solution File, Format Version 12.00
|
|
212
|
+
# Visual Studio Version 17
|
|
213
|
+
VisualStudioVersion = 17.0.31903.59
|
|
214
|
+
MinimumVisualStudioVersion = 10.0.40219.1
|
|
215
|
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "${projectName}", "${projectName}.csproj", "{${projectGuid}}"
|
|
216
|
+
EndProject
|
|
217
|
+
Global
|
|
218
|
+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
219
|
+
Debug|Any CPU = Debug|Any CPU
|
|
220
|
+
Release|Any CPU = Release|Any CPU
|
|
221
|
+
EndGlobalSection
|
|
222
|
+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
223
|
+
{${projectGuid}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
224
|
+
{${projectGuid}}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
225
|
+
{${projectGuid}}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
226
|
+
{${projectGuid}}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
227
|
+
EndGlobalSection
|
|
228
|
+
GlobalSection(SolutionProperties) = preSolution
|
|
229
|
+
HideSolutionNode = FALSE
|
|
230
|
+
EndGlobalSection
|
|
231
|
+
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
232
|
+
SolutionGuid = {${solutionGuid}}
|
|
233
|
+
EndGlobalSection
|
|
234
|
+
EndGlobal
|
|
235
|
+
`;
|
|
236
|
+
fs.writeFileSync(path.join(targetDir, `${projectName}.sln`), sln);
|
|
237
|
+
|
|
238
|
+
// Dynamic Program.cs with user's selected styling (Tailwind OR Bootstrap)
|
|
239
|
+
const programCs = `
|
|
240
|
+
using Microsoft.EntityFrameworkCore;
|
|
241
|
+
using DotNetBootstrapMssql.Data;
|
|
242
|
+
|
|
243
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
244
|
+
|
|
245
|
+
builder.Services.AddControllers();
|
|
246
|
+
builder.Services.AddEndpointsApiExplorer();
|
|
247
|
+
builder.Services.AddSwaggerGen();
|
|
248
|
+
|
|
249
|
+
// Configure Database: ${database.toUpperCase()}
|
|
250
|
+
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
251
|
+
{
|
|
252
|
+
${database === 'mysql'
|
|
253
|
+
? `var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? "Server=localhost;Port=3306;Database=${projectName};Uid=root;Pwd=;";
|
|
254
|
+
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));`
|
|
255
|
+
: `options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")
|
|
256
|
+
?? "Server=localhost,1433;Database=${projectName};User Id=sa;Password=YourStrong@Password;TrustServerCertificate=True;");`
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
var app = builder.Build();
|
|
261
|
+
|
|
262
|
+
if (app.Environment.IsDevelopment())
|
|
263
|
+
{
|
|
264
|
+
app.UseSwagger();
|
|
265
|
+
app.UseSwaggerUI();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
app.UseHttpsRedirection();
|
|
269
|
+
app.UseStaticFiles();
|
|
270
|
+
app.UseAuthorization();
|
|
271
|
+
app.MapControllers();
|
|
272
|
+
|
|
273
|
+
app.MapGet("/", () => Results.Content(@"
|
|
274
|
+
<!DOCTYPE html>
|
|
275
|
+
<html lang=""en"" data-theme=""${colorMode}"">
|
|
276
|
+
<head>
|
|
277
|
+
<meta charset=""utf-8"" />
|
|
278
|
+
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"" />
|
|
279
|
+
<title>${safeAppTitle}</title>
|
|
280
|
+
${stylesheetLink}
|
|
281
|
+
<style>${themeCSS}</style>
|
|
282
|
+
</head>
|
|
283
|
+
<body class=""p-8"">
|
|
284
|
+
<div style=""max-width: 900px; margin: 0 auto; text-align: center;"">
|
|
285
|
+
${logoHtml}
|
|
286
|
+
<h1 style=""font-size: 2.5rem; font-weight: 800;"">${safeAppTitle}</h1>
|
|
287
|
+
<p style=""color: var(--color-text-muted);"">${config.description ? safeDescription : '.NET Core 8 Web API with ' + styling.toUpperCase() + ' and ' + database.toUpperCase()}</p>
|
|
288
|
+
<p style=""font-size: 0.85rem; color: var(--color-primary); font-weight: bold;"">Developer: ${safeAuthor}</p>
|
|
289
|
+
|
|
290
|
+
<div style=""margin: 1.5rem 0;"">
|
|
291
|
+
<a href=""/swagger"" style=""padding: 10px 20px; background: var(--color-primary); color: white; border-radius: 8px; text-decoration: none; font-weight: bold; display: inline-block;"">
|
|
292
|
+
Explore Swagger API ↗
|
|
293
|
+
</a>
|
|
294
|
+
</div>
|
|
295
|
+
|
|
296
|
+
<div style=""text-align: left; padding: 2rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-base); margin-top: 2rem; box-shadow: 0 10px 30px rgba(0,0,0,0.15);"">
|
|
297
|
+
<h3 style=""margin-top: 0;"">⚡ Active Architecture:</h3>
|
|
298
|
+
<ul>
|
|
299
|
+
<li><strong>Backend:</strong> .NET Core 8 Web API</li>
|
|
300
|
+
<li><strong>Database Engine:</strong> ${database.toUpperCase()} (EF Core)</li>
|
|
301
|
+
<li><strong>Styling Engine:</strong> ${styling.toUpperCase()}</li>
|
|
302
|
+
<li><strong>Theme:</strong> ${themeKey} (${colorMode})</li>
|
|
303
|
+
</ul>
|
|
304
|
+
|
|
305
|
+
<hr style=""border: none; border-top: 1px solid var(--color-border); margin: 1.5rem 0;"" />
|
|
306
|
+
<h4>Dynamic Selected UI Components:</h4>
|
|
307
|
+
${uiComponents}
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
<script>${themeJS}</script>
|
|
311
|
+
</body>
|
|
312
|
+
</html>", "text/html"));
|
|
313
|
+
|
|
314
|
+
app.Run();
|
|
315
|
+
`;
|
|
316
|
+
fs.writeFileSync(path.join(targetDir, 'Program.cs'), programCs);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ==========================================
|
|
320
|
+
// 2. RAW PHP (ANY DATABASE + ANY STYLING)
|
|
321
|
+
// ==========================================
|
|
322
|
+
else if (backend === 'raw-php') {
|
|
323
|
+
const srcDir = path.join(__dirname, '..', 'templates', 'raw-php-mysql');
|
|
324
|
+
copyFolderRecursive(srcDir, targetDir, replaceMap);
|
|
325
|
+
fs.writeFileSync(path.join(targetDir, 'public', 'css', 'theme.css'), themeCSS);
|
|
326
|
+
fs.writeFileSync(path.join(targetDir, 'public', 'js', 'theme.js'), themeJS);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ==========================================
|
|
330
|
+
// 3. LARAVEL (Genuine Full Laravel 11 Structure + Vue 3 / Blade + Artisan + Migrations)
|
|
331
|
+
// ==========================================
|
|
332
|
+
else if (backend === 'laravel' || backend === 'laravel-vue-mysql') {
|
|
333
|
+
const srcDir = path.join(__dirname, '..', 'templates', 'laravel-full');
|
|
334
|
+
copyFolderRecursive(srcDir, targetDir, replaceMap);
|
|
335
|
+
|
|
336
|
+
const chosenLaravelVer = config.versions?.laravel || '^11.0';
|
|
337
|
+
const isAuth = config.auth ? config.auth.enabled !== false : true;
|
|
338
|
+
|
|
339
|
+
const laravelDeps = {
|
|
340
|
+
"php": "^8.2",
|
|
341
|
+
"laravel/framework": chosenLaravelVer,
|
|
342
|
+
"laravel/tinker": "^2.9",
|
|
343
|
+
"inertiajs/inertia-laravel": "^1.0"
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
if (isAuth) {
|
|
347
|
+
laravelDeps["laravel/sanctum"] = "^4.0";
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const composerJson = {
|
|
351
|
+
name: `developer/${projectName}`,
|
|
352
|
+
type: "project",
|
|
353
|
+
description: config.description || `Laravel ${chosenLaravelVer.replace(/[^0-9.]/g, '')} Full Stack Project`,
|
|
354
|
+
keywords: ["framework", "laravel"],
|
|
355
|
+
license: "MIT",
|
|
356
|
+
require: laravelDeps,
|
|
357
|
+
"require-dev": {
|
|
358
|
+
"fakerphp/faker": "^1.23",
|
|
359
|
+
"mockery/mockery": "^1.6",
|
|
360
|
+
"nunomaduro/collision": "^8.0",
|
|
361
|
+
"phpunit/phpunit": "^11.0"
|
|
362
|
+
},
|
|
363
|
+
"autoload": {
|
|
364
|
+
"psr-4": {
|
|
365
|
+
"App\\\\": "app/",
|
|
366
|
+
"Database\\\\Factories\\\\": "database/factories/",
|
|
367
|
+
"Database\\\\Seeders\\\\": "database/seeders/"
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
"scripts": {
|
|
371
|
+
"post-autoload-dump": [
|
|
372
|
+
"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump",
|
|
373
|
+
"@php artisan package:discover --ansi"
|
|
374
|
+
]
|
|
375
|
+
},
|
|
376
|
+
"config": {
|
|
377
|
+
"optimize-autoloader": true,
|
|
378
|
+
"preferred-install": "dist",
|
|
379
|
+
"sort-packages": true
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
fs.writeFileSync(path.join(targetDir, 'composer.json'), JSON.stringify(composerJson, null, 2));
|
|
383
|
+
|
|
384
|
+
const vueDeps = await resolveDependencies([
|
|
385
|
+
'vue',
|
|
386
|
+
'@vitejs/plugin-vue',
|
|
387
|
+
'@inertiajs/vue3',
|
|
388
|
+
'laravel-vite-plugin',
|
|
389
|
+
'vite',
|
|
390
|
+
'axios'
|
|
391
|
+
], 'npm');
|
|
392
|
+
|
|
393
|
+
if (styling === 'tailwind') {
|
|
394
|
+
Object.assign(vueDeps, await resolveDependencies(['tailwindcss', 'postcss', 'autoprefixer'], 'npm'));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const pkgJson = {
|
|
398
|
+
private: true,
|
|
399
|
+
type: "module",
|
|
400
|
+
scripts: {
|
|
401
|
+
"dev": "vite",
|
|
402
|
+
"build": "vite build"
|
|
403
|
+
},
|
|
404
|
+
devDependencies: vueDeps
|
|
405
|
+
};
|
|
406
|
+
fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ==========================================
|
|
410
|
+
// 4. NEXT.JS (ANY DATABASE: MongoDB / MSSQL / MySQL + ANY STYLING)
|
|
411
|
+
// ==========================================
|
|
412
|
+
else if (backend === 'nextjs' || backend.includes('nextjs')) {
|
|
413
|
+
const reqPkgs = ['next', 'react', 'react-dom'];
|
|
414
|
+
if (database === 'mongodb') reqPkgs.push('mongoose');
|
|
415
|
+
if (database === 'mysql') reqPkgs.push('mysql2');
|
|
416
|
+
if (database === 'mssql') reqPkgs.push('mssql');
|
|
417
|
+
if (styling === 'tailwind') reqPkgs.push('tailwindcss', 'postcss', 'autoprefixer');
|
|
418
|
+
const chosenNextVer = config.versions?.nextjs || '^14.2.0';
|
|
419
|
+
const isAuth = config.auth ? config.auth.enabled !== false : true;
|
|
420
|
+
|
|
421
|
+
if (isAuth) {
|
|
422
|
+
reqPkgs.push('jsonwebtoken', 'bcryptjs');
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const deps = await resolveDependencies(reqPkgs, 'npm');
|
|
426
|
+
deps['next'] = chosenNextVer;
|
|
427
|
+
if (chosenNextVer.startsWith('^15')) {
|
|
428
|
+
deps['react'] = '^19.0.0';
|
|
429
|
+
deps['react-dom'] = '^19.0.0';
|
|
430
|
+
} else {
|
|
431
|
+
deps['react'] = '^18.3.1';
|
|
432
|
+
deps['react-dom'] = '^18.3.1';
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const pkg = {
|
|
436
|
+
name: projectName,
|
|
437
|
+
version: '1.0.0',
|
|
438
|
+
scripts: {
|
|
439
|
+
dev: `next dev -p ${config.port || 3000}`,
|
|
440
|
+
build: "next build",
|
|
441
|
+
start: `next start -p ${config.port || 3000}`
|
|
442
|
+
},
|
|
443
|
+
dependencies: deps
|
|
444
|
+
};
|
|
445
|
+
fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
446
|
+
|
|
447
|
+
ensureDir(path.join(targetDir, 'src', 'app', 'api', 'health'));
|
|
448
|
+
ensureDir(path.join(targetDir, 'src', 'styles'));
|
|
449
|
+
ensureDir(path.join(targetDir, 'src', 'lib'));
|
|
450
|
+
|
|
451
|
+
const nextTplDir = path.join(__dirname, '..', 'templates', 'nextjs', 'src');
|
|
452
|
+
if (fs.existsSync(nextTplDir)) {
|
|
453
|
+
copyFolderRecursive(nextTplDir, path.join(targetDir, 'src'), replaceMap);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
fs.writeFileSync(path.join(targetDir, 'src', 'styles', 'theme.css'), themeCSS);
|
|
457
|
+
fs.writeFileSync(path.join(targetDir, 'src', 'lib', 'db.js'), getUniversalDbHelper('nextjs', database, projectName));
|
|
458
|
+
|
|
459
|
+
// Next.js Route Handler
|
|
460
|
+
fs.writeFileSync(path.join(targetDir, 'src', 'app', 'api', 'health', 'route.js'), `
|
|
461
|
+
import { NextResponse } from 'next/server';
|
|
462
|
+
export async function GET() {
|
|
463
|
+
return NextResponse.json({
|
|
464
|
+
status: 'online',
|
|
465
|
+
stack: 'Next.js App Router',
|
|
466
|
+
database: '${database.toUpperCase()}',
|
|
467
|
+
styling: '${styling}',
|
|
468
|
+
appName: '${config.appTitle || projectName}',
|
|
469
|
+
author: '${config.author || 'ARG RABBI'}'
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
`);
|
|
473
|
+
|
|
474
|
+
// Next.js Root Layout
|
|
475
|
+
fs.writeFileSync(path.join(targetDir, 'src', 'app', 'layout.jsx'), `
|
|
476
|
+
import '../styles/theme.css';
|
|
477
|
+
${styling === 'bootstrap' ? "import 'bootstrap/dist/css/bootstrap.min.css';" : ''}
|
|
478
|
+
|
|
479
|
+
export const metadata = {
|
|
480
|
+
title: "${config.appTitle || projectName}",
|
|
481
|
+
description: "${config.description || 'Next.js Multi-Stack Application'}"
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
export default function RootLayout({ children }) {
|
|
485
|
+
return (
|
|
486
|
+
<html lang="en" data-theme="${colorMode === 'all' || colorMode === 'device' ? 'dark' : colorMode}">
|
|
487
|
+
<body>{children}</body>
|
|
488
|
+
</html>
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
`);
|
|
492
|
+
|
|
493
|
+
// Next.js Home Page
|
|
494
|
+
fs.writeFileSync(path.join(targetDir, 'src', 'app', 'page.jsx'), `
|
|
495
|
+
export default function Home() {
|
|
496
|
+
return (
|
|
497
|
+
<div style={{ maxWidth: '900px', margin: '3rem auto', padding: '1rem', textAlign: 'center' }}>
|
|
498
|
+
${logoHtml}
|
|
499
|
+
<h1 style={{ fontSize: '2.5rem', fontWeight: 800 }}>${safeAppTitle}</h1>
|
|
500
|
+
<p style={{ color: 'var(--color-text-muted)' }}>${config.description ? safeDescription : 'Next.js + ' + database.toUpperCase() + ' + ' + styling.toUpperCase()}</p>
|
|
501
|
+
|
|
502
|
+
<div style={{ marginTop: '2rem', padding: '1.5rem', background: 'var(--color-surface)', borderRadius: '1rem', textAlign: 'left', border: '1px solid var(--color-border)' }}>
|
|
503
|
+
<h3>🚀 Configured Architecture:</h3>
|
|
504
|
+
<ul>
|
|
505
|
+
<li><strong>Framework:</strong> Next.js (App Router)</li>
|
|
506
|
+
<li><strong>Database Engine:</strong> ${database.toUpperCase()}</li>
|
|
507
|
+
<li><strong>Styling System:</strong> ${styling.toUpperCase()}</li>
|
|
508
|
+
<li><strong>Theme:</strong> ${themeKey}</li>
|
|
509
|
+
</ul>
|
|
510
|
+
<p>Health API: <a href="/api/health" style={{ color: 'var(--color-primary)' }}>/api/health</a></p>
|
|
511
|
+
<hr style={{ borderColor: 'var(--color-border)', margin: '1.5rem 0' }} />
|
|
512
|
+
<h4>Included UI Components:</h4>
|
|
513
|
+
${uiComponents}
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
`);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ==========================================
|
|
522
|
+
// 5. NODE.JS + EXPRESS (ANY DATABASE + ANY STYLING)
|
|
523
|
+
// ==========================================
|
|
524
|
+
else {
|
|
525
|
+
const reqPkgs = ['express', 'dotenv', 'cors'];
|
|
526
|
+
if (database === 'mongodb') reqPkgs.push('mongoose');
|
|
527
|
+
if (database === 'mysql') reqPkgs.push('mysql2');
|
|
528
|
+
if (database === 'mssql') reqPkgs.push('mssql');
|
|
529
|
+
|
|
530
|
+
const chosenExpressVer = config.versions?.nodeExpress || '^5.0.0';
|
|
531
|
+
const isAuth = config.auth ? config.auth.enabled !== false : true;
|
|
532
|
+
|
|
533
|
+
if (isAuth) {
|
|
534
|
+
reqPkgs.push('jsonwebtoken', 'bcryptjs');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const deps = await resolveDependencies(reqPkgs, 'npm');
|
|
538
|
+
deps['express'] = chosenExpressVer;
|
|
539
|
+
|
|
540
|
+
const pkg = {
|
|
541
|
+
name: projectName,
|
|
542
|
+
version: '1.0.0',
|
|
543
|
+
description: config.description,
|
|
544
|
+
author: config.author || 'ARG RABBI',
|
|
545
|
+
main: 'src/server.js',
|
|
546
|
+
scripts: {
|
|
547
|
+
start: 'node src/server.js',
|
|
548
|
+
dev: 'node --watch src/server.js'
|
|
549
|
+
},
|
|
550
|
+
dependencies: deps
|
|
551
|
+
};
|
|
552
|
+
fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
553
|
+
|
|
554
|
+
ensureDir(path.join(targetDir, 'src'));
|
|
555
|
+
ensureDir(path.join(targetDir, 'public'));
|
|
556
|
+
|
|
557
|
+
fs.writeFileSync(path.join(targetDir, 'public', 'theme.css'), themeCSS);
|
|
558
|
+
fs.writeFileSync(path.join(targetDir, 'public', 'theme.js'), themeJS);
|
|
559
|
+
fs.writeFileSync(path.join(targetDir, 'src', 'db.js'), getUniversalDbHelper('node', database, projectName));
|
|
560
|
+
|
|
561
|
+
const nodeTplDir = path.join(__dirname, '..', 'templates', 'node-express', 'src');
|
|
562
|
+
if (fs.existsSync(nodeTplDir)) {
|
|
563
|
+
copyFolderRecursive(nodeTplDir, path.join(targetDir, 'src'), replaceMap);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const srv = `
|
|
567
|
+
const express = require('express');
|
|
568
|
+
const cors = require('cors');
|
|
569
|
+
const path = require('path');
|
|
570
|
+
require('dotenv').config();
|
|
571
|
+
const { connectDB } = require('./db');
|
|
572
|
+
const authRoutes = require('./routes/authRoutes');
|
|
573
|
+
|
|
574
|
+
const app = express();
|
|
575
|
+
const PORT = process.env.PORT || ${config.port || 5000};
|
|
576
|
+
|
|
577
|
+
app.use(cors());
|
|
578
|
+
app.use(express.json());
|
|
579
|
+
app.use(express.static(path.join(__dirname, '../public')));
|
|
580
|
+
|
|
581
|
+
connectDB();
|
|
582
|
+
|
|
583
|
+
// API Endpoints
|
|
584
|
+
app.use('/api/auth', authRoutes);
|
|
585
|
+
|
|
586
|
+
app.get('/api/health', (req, res) => {
|
|
587
|
+
res.json({
|
|
588
|
+
status: 'online',
|
|
589
|
+
app: '${config.appTitle || projectName}',
|
|
590
|
+
database: '${database}',
|
|
591
|
+
styling: '${styling}',
|
|
592
|
+
auth: ${isAuth ? 'true' : 'false'},
|
|
593
|
+
author: '${config.author || 'ARG RABBI'}'
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
app.listen(PORT, () => {
|
|
598
|
+
console.log(\`⚡ Server listening at http://localhost:\${PORT}\`);
|
|
599
|
+
});
|
|
600
|
+
`;
|
|
601
|
+
fs.writeFileSync(path.join(targetDir, 'src', 'server.js'), srv);
|
|
602
|
+
|
|
603
|
+
const indexHtml = `
|
|
604
|
+
<!DOCTYPE html>
|
|
605
|
+
<html lang="en" data-theme="${colorMode}">
|
|
606
|
+
<head>
|
|
607
|
+
<meta charset="UTF-8">
|
|
608
|
+
<title>${safeAppTitle}</title>
|
|
609
|
+
<link rel="stylesheet" href="/theme.css">
|
|
610
|
+
${stylesheetLink}
|
|
611
|
+
</head>
|
|
612
|
+
<body class="p-6">
|
|
613
|
+
<div style="max-width: 850px; margin: 2rem auto; text-align: center;">
|
|
614
|
+
${logoHtml}
|
|
615
|
+
<h1>${safeAppTitle}</h1>
|
|
616
|
+
<p style="color: var(--color-text-muted);">${config.description ? safeDescription : 'Node.js Express + ' + database.toUpperCase() + ' + ' + styling.toUpperCase()}</p>
|
|
617
|
+
|
|
618
|
+
<div style="text-align: left; padding: 2rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-base); margin-top: 2rem;">
|
|
619
|
+
<h3>Active Architecture:</h3>
|
|
620
|
+
<ul>
|
|
621
|
+
<li><strong>Backend:</strong> Node.js Express</li>
|
|
622
|
+
<li><strong>Database:</strong> ${database.toUpperCase()}</li>
|
|
623
|
+
<li><strong>Styling:</strong> ${styling.toUpperCase()}</li>
|
|
624
|
+
</ul>
|
|
625
|
+
<hr style="border: none; border-top: 1px solid var(--color-border); margin: 1.5rem 0;" />
|
|
626
|
+
<h4>Components:</h4>
|
|
627
|
+
${uiComponents}
|
|
628
|
+
</div>
|
|
629
|
+
</div>
|
|
630
|
+
<script src="/theme.js"></script>
|
|
631
|
+
</body>
|
|
632
|
+
</html>
|
|
633
|
+
`;
|
|
634
|
+
fs.writeFileSync(path.join(targetDir, 'public', 'index.html'), indexHtml);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Common environment and documentation
|
|
638
|
+
fs.writeFileSync(path.join(targetDir, '.env.example'), getEnvExample(config, database));
|
|
639
|
+
fs.writeFileSync(path.join(targetDir, '.env'), getEnvLocal(config, database));
|
|
640
|
+
fs.writeFileSync(path.join(targetDir, 'README.md'), generateDynamicStackReadme(config, backend, database, styling, themeKey, colorMode));
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
success: true,
|
|
644
|
+
projectName,
|
|
645
|
+
path: targetDir,
|
|
646
|
+
combination: `${backend} + ${database} + ${styling}`
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// .env.example: placeholder values only, safe to commit, never real secrets.
|
|
651
|
+
function getEnvExample(config, dbType) {
|
|
652
|
+
return `
|
|
653
|
+
PORT=${config.port || 3000}
|
|
654
|
+
APP_NAME="${config.appTitle || config.projectName}"
|
|
655
|
+
APP_ENV=development
|
|
656
|
+
|
|
657
|
+
# Database Settings (${dbType.toUpperCase()})
|
|
658
|
+
${dbType === 'mongodb' ? `MONGODB_URI=mongodb://127.0.0.1:27017/${config.projectName}` : ''}
|
|
659
|
+
${dbType === 'mysql' ? `DB_HOST=127.0.0.1\nDB_PORT=3306\nDB_DATABASE=${config.projectName}\nDB_USERNAME=root\nDB_PASSWORD=change-me` : ''}
|
|
660
|
+
${dbType === 'mssql' ? `DB_HOST=127.0.0.1\nDB_PORT=1433\nDB_DATABASE=${config.projectName}\nDB_USERNAME=sa\nDB_PASSWORD=change-me` : ''}
|
|
661
|
+
`;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// .env: the actual local dev file. Generates a fresh random password/secret per
|
|
665
|
+
// project instead of baking in a shared default credential (e.g. MSSQL's
|
|
666
|
+
// "YourStrong@Password"), which would otherwise ship identically in every
|
|
667
|
+
// scaffolded project.
|
|
668
|
+
function getEnvLocal(config, dbType) {
|
|
669
|
+
const randomSecret = () => crypto.randomBytes(16).toString('hex');
|
|
670
|
+
|
|
671
|
+
return `
|
|
672
|
+
PORT=${config.port || 3000}
|
|
673
|
+
APP_NAME="${config.appTitle || config.projectName}"
|
|
674
|
+
APP_ENV=development
|
|
675
|
+
APP_KEY=${randomSecret()}
|
|
676
|
+
|
|
677
|
+
# Database Settings (${dbType.toUpperCase()})
|
|
678
|
+
${dbType === 'mongodb' ? `MONGODB_URI=mongodb://127.0.0.1:27017/${config.projectName}` : ''}
|
|
679
|
+
${dbType === 'mysql' ? `DB_HOST=127.0.0.1\nDB_PORT=3306\nDB_DATABASE=${config.projectName}\nDB_USERNAME=root\nDB_PASSWORD=` : ''}
|
|
680
|
+
${dbType === 'mssql' ? `DB_HOST=127.0.0.1\nDB_PORT=1433\nDB_DATABASE=${config.projectName}\nDB_USERNAME=sa\nDB_PASSWORD=${randomSecret()}` : ''}
|
|
681
|
+
`;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
module.exports = {
|
|
685
|
+
generateProject
|
|
686
|
+
};
|