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,76 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
defineProps({
|
|
5
|
+
appName: String,
|
|
6
|
+
description: String,
|
|
7
|
+
author: String,
|
|
8
|
+
framework: String,
|
|
9
|
+
frontend: String,
|
|
10
|
+
database: String
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const isModalOpen = ref(false);
|
|
14
|
+
const isDrawerOpen = ref(false);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div class="min-h-screen bg-slate-50 text-slate-900 p-8">
|
|
19
|
+
<div class="max-w-4xl mx-auto text-center">
|
|
20
|
+
<div class="w-16 h-16 rounded-2xl bg-sky-600 text-white text-3xl font-extrabold flex items-center justify-center mx-auto mb-4 shadow-md">
|
|
21
|
+
{{ appName ? appName.charAt(0) : 'L' }}
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<h1 class="text-4xl font-extrabold mb-2 tracking-tight text-slate-900">{{ appName }}</h1>
|
|
25
|
+
<p class="text-lg text-slate-600 mb-6">{{ description }}</p>
|
|
26
|
+
|
|
27
|
+
<div class="inline-flex items-center gap-3 px-4 py-2 rounded-full bg-white border border-slate-200 text-sm font-semibold mb-8 shadow-sm">
|
|
28
|
+
<span class="text-red-500 font-bold">🔴 {{ framework }}</span>
|
|
29
|
+
<span>•</span>
|
|
30
|
+
<span class="text-emerald-600 font-bold">🟢 {{ frontend }}</span>
|
|
31
|
+
<span>•</span>
|
|
32
|
+
<span class="text-sky-600 font-bold">🗄️ {{ database.toUpperCase() }}</span>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<!-- Action Buttons -->
|
|
36
|
+
<div class="flex gap-4 justify-center mb-8">
|
|
37
|
+
<button @click="isModalOpen = true" class="px-5 py-2.5 rounded-lg bg-sky-600 text-white font-semibold hover:bg-sky-700 transition">
|
|
38
|
+
Open Demo Modal
|
|
39
|
+
</button>
|
|
40
|
+
<button @click="isDrawerOpen = true" class="px-5 py-2.5 rounded-lg border border-slate-300 bg-white font-semibold hover:bg-slate-100 transition">
|
|
41
|
+
Open Offcanvas Drawer
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<!-- System Architecture Details -->
|
|
46
|
+
<div class="p-6 rounded-xl bg-white border border-slate-200 text-left shadow-sm">
|
|
47
|
+
<h3 class="text-lg font-bold mb-3">Included Architecture:</h3>
|
|
48
|
+
<ul class="space-y-2 text-sm text-slate-600">
|
|
49
|
+
<li class="flex items-center gap-2">✓ <strong>Artisan CLI:</strong> Full <code>php artisan</code> support included.</li>
|
|
50
|
+
<li class="flex items-center gap-2">✓ <strong>Database:</strong> Native Eloquent ORM & Migrations configured for {{ database.toUpperCase() }}.</li>
|
|
51
|
+
<li class="flex items-center gap-2">✓ <strong>Frontend:</strong> Vue 3 Composition API with Inertia.js & Vite HMR.</li>
|
|
52
|
+
<li class="flex items-center gap-2">✓ <strong>Maintainer:</strong> Built with BoilerCraft by {{ author }}.</li>
|
|
53
|
+
</ul>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- Modal Dialog -->
|
|
58
|
+
<div v-if="isModalOpen" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-xs">
|
|
59
|
+
<div class="bg-white border border-slate-200 p-6 rounded-xl max-w-md w-full shadow-2xl">
|
|
60
|
+
<h3 class="text-lg font-bold mb-2">Action Modal</h3>
|
|
61
|
+
<p class="text-sm text-slate-600 mb-4">This Vue component is pre-wired and ready for your forms.</p>
|
|
62
|
+
<button @click="isModalOpen = false" class="w-full py-2 bg-sky-600 text-white rounded-lg font-semibold hover:bg-sky-700">Close</button>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<!-- Offcanvas Drawer -->
|
|
67
|
+
<div v-if="isDrawerOpen" class="fixed inset-0 z-50 bg-black/30" @click="isDrawerOpen = false"></div>
|
|
68
|
+
<div :class="['fixed top-0 left-0 bottom-0 w-80 bg-white border-r border-slate-200 p-6 z-50 transition-transform duration-300', isDrawerOpen ? 'translate-x-0' : '-translate-x-full']">
|
|
69
|
+
<div class="flex justify-between items-center mb-6">
|
|
70
|
+
<h4 class="font-bold">Navigation Drawer</h4>
|
|
71
|
+
<button @click="isDrawerOpen = false" class="text-xl">×</button>
|
|
72
|
+
</div>
|
|
73
|
+
<p class="text-sm text-slate-600">Vue 3 offcanvas drawer with clean responsive layout.</p>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import './bootstrap';
|
|
2
|
+
import '../css/app.css';
|
|
3
|
+
|
|
4
|
+
import { createApp, h } from 'vue';
|
|
5
|
+
import { createInertiaApp } from '@inertiajs/vue3';
|
|
6
|
+
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
|
|
7
|
+
|
|
8
|
+
const appName = import.meta.env.VITE_APP_NAME || '{{APP_TITLE}}';
|
|
9
|
+
|
|
10
|
+
createInertiaApp({
|
|
11
|
+
title: (title) => `${title} - ${appName}`,
|
|
12
|
+
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
|
|
13
|
+
setup({ el, App, props, plugin }) {
|
|
14
|
+
return createApp({ render: () => h(App, props) })
|
|
15
|
+
.use(plugin)
|
|
16
|
+
.mount(el);
|
|
17
|
+
},
|
|
18
|
+
progress: {
|
|
19
|
+
color: '#0284c7',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title inertia>{{ config('app.name', 'Laravel') }}</title>
|
|
7
|
+
|
|
8
|
+
<!-- Scripts -->
|
|
9
|
+
@vite(['resources/js/app.js', 'resources/css/app.css'])
|
|
10
|
+
@inertiaHead
|
|
11
|
+
</head>
|
|
12
|
+
<body class="font-sans antialiased bg-slate-50 text-slate-900">
|
|
13
|
+
@inertia
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Support\Facades\Route;
|
|
4
|
+
use Inertia\Inertia;
|
|
5
|
+
use App\Http\Controllers\HomeController;
|
|
6
|
+
use App\Http\Controllers\AuthController;
|
|
7
|
+
|
|
8
|
+
Route::get('/', [HomeController::class, 'index']);
|
|
9
|
+
|
|
10
|
+
// Authentication Routes
|
|
11
|
+
Route::post('/login', [AuthController::class, 'login'])->name('login');
|
|
12
|
+
Route::post('/register', [AuthController::class, 'register'])->name('register');
|
|
13
|
+
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
|
|
14
|
+
|
|
15
|
+
Route::get('/health', function () {
|
|
16
|
+
return response()->json([
|
|
17
|
+
'status' => 'online',
|
|
18
|
+
'app' => '{{APP_TITLE}}',
|
|
19
|
+
'framework' => 'Laravel',
|
|
20
|
+
'php' => PHP_VERSION,
|
|
21
|
+
'timestamp' => now()
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import laravel from 'laravel-vite-plugin';
|
|
3
|
+
import vue from '@vitejs/plugin-vue';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [
|
|
7
|
+
laravel({
|
|
8
|
+
input: ['resources/css/app.css', 'resources/js/app.js'],
|
|
9
|
+
refresh: true,
|
|
10
|
+
}),
|
|
11
|
+
vue({
|
|
12
|
+
template: {
|
|
13
|
+
transformAssetUrls: {
|
|
14
|
+
base: null,
|
|
15
|
+
includeAbsolute: false,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
19
|
+
],
|
|
20
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
namespace App\Http\Controllers\Auth;
|
|
3
|
+
|
|
4
|
+
use App\Http\Controllers\Controller;
|
|
5
|
+
use Illuminate\Http\Request;
|
|
6
|
+
use Illuminate\Support\Facades\Auth;
|
|
7
|
+
use Inertia\Inertia;
|
|
8
|
+
use Inertia\Response;
|
|
9
|
+
|
|
10
|
+
class AuthenticatedSessionController extends Controller
|
|
11
|
+
{
|
|
12
|
+
public function create(): Response
|
|
13
|
+
{
|
|
14
|
+
return Inertia::render('Auth/Login', [
|
|
15
|
+
'status' => session('status'),
|
|
16
|
+
]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public function store(Request $request)
|
|
20
|
+
{
|
|
21
|
+
$credentials = $request->validate([
|
|
22
|
+
'email' => ['required', 'email'],
|
|
23
|
+
'password' => ['required'],
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
if (Auth::attempt($credentials)) {
|
|
27
|
+
$request->session()->regenerate();
|
|
28
|
+
return redirect()->intended('/dashboard');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return back()->withErrors([
|
|
32
|
+
'email' => 'The provided credentials do not match our records.',
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public function destroy(Request $request)
|
|
37
|
+
{
|
|
38
|
+
Auth::guard('web')->logout();
|
|
39
|
+
$request->session()->invalidate();
|
|
40
|
+
$request->session()->regenerateToken();
|
|
41
|
+
return redirect('/');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
namespace App\Http\Controllers;
|
|
3
|
+
|
|
4
|
+
use Illuminate\Http\Request;
|
|
5
|
+
use Inertia\Inertia;
|
|
6
|
+
use Inertia\Response;
|
|
7
|
+
|
|
8
|
+
class HomeController extends Controller
|
|
9
|
+
{
|
|
10
|
+
public function index(): Response
|
|
11
|
+
{
|
|
12
|
+
return Inertia::render('Home', [
|
|
13
|
+
'appName' => '{{APP_TITLE}}',
|
|
14
|
+
'description' => '{{APP_DESC}}',
|
|
15
|
+
'author' => '{{AUTHOR}}',
|
|
16
|
+
'database' => 'MySQL',
|
|
17
|
+
'features' => [
|
|
18
|
+
'Laravel 11 Enterprise Framework',
|
|
19
|
+
'Vue 3 Composition API & Vite',
|
|
20
|
+
'Tailwind CSS & Theme Switcher',
|
|
21
|
+
'MySQL Eloquent ORM'
|
|
22
|
+
]
|
|
23
|
+
]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
defineProps({
|
|
5
|
+
appName: String,
|
|
6
|
+
description: String,
|
|
7
|
+
author: String,
|
|
8
|
+
features: Array
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const isModalOpen = ref(false);
|
|
12
|
+
const isDrawerOpen = ref(false);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div class="min-h-screen bg-[var(--color-bg)] text-[var(--color-text)] p-8">
|
|
17
|
+
<div class="max-w-4xl mx-auto text-center">
|
|
18
|
+
<div class="w-16 h-16 rounded-2xl bg-[var(--color-primary)] text-white text-3xl font-extrabold flex items-center justify-center mx-auto mb-4 shadow-lg shadow-[var(--color-primary)]/20">
|
|
19
|
+
{{ appName ? appName.charAt(0) : 'L' }}
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<h1 class="text-4xl font-extrabold mb-2 tracking-tight">{{ appName }}</h1>
|
|
23
|
+
<p class="text-lg text-[var(--color-text-muted)] mb-6">{{ description }}</p>
|
|
24
|
+
|
|
25
|
+
<div class="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-[var(--color-surface)] border border-[var(--color-border)] text-sm font-semibold mb-8">
|
|
26
|
+
<span>Laravel 11 + Vue 3 + MySQL</span>
|
|
27
|
+
<span class="text-[var(--color-primary)] font-bold">● Active</span>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- Action Buttons for Modal and Drawer -->
|
|
31
|
+
<div class="flex gap-4 justify-center mb-8">
|
|
32
|
+
<button @click="isModalOpen = true" class="px-5 py-2.5 rounded-xl bg-[var(--color-primary)] text-white font-semibold hover:opacity-90 transition">
|
|
33
|
+
Open Demo Modal
|
|
34
|
+
</button>
|
|
35
|
+
<button @click="isDrawerOpen = true" class="px-5 py-2.5 rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] font-semibold hover:bg-[var(--color-surface-hover)] transition">
|
|
36
|
+
Open Offcanvas Drawer
|
|
37
|
+
</button>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Feature Card -->
|
|
41
|
+
<div class="p-6 rounded-2xl bg-[var(--color-surface)] border border-[var(--color-border)] text-left shadow-xl">
|
|
42
|
+
<h3 class="text-lg font-bold mb-4">Included Architectures:</h3>
|
|
43
|
+
<ul class="space-y-2">
|
|
44
|
+
<li v-for="(feat, idx) in features" :key="idx" class="flex items-center gap-2 text-sm text-[var(--color-text-muted)]">
|
|
45
|
+
<span class="text-emerald-500 font-bold">✓</span> {{ feat }}
|
|
46
|
+
</li>
|
|
47
|
+
</ul>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- Modal Component -->
|
|
52
|
+
<div v-if="isModalOpen" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
|
53
|
+
<div class="bg-[var(--color-surface)] border border-[var(--color-border)] p-6 rounded-2xl max-w-md w-full shadow-2xl">
|
|
54
|
+
<h3 class="text-lg font-bold mb-2">Enterprise Modal</h3>
|
|
55
|
+
<p class="text-sm text-[var(--color-text-muted)] mb-4">This Vue modal component is pre-wired to BoilerCraft design tokens.</p>
|
|
56
|
+
<button @click="isModalOpen = false" class="w-full py-2 bg-[var(--color-primary)] text-white rounded-lg font-semibold">Close</button>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<!-- Drawer Component -->
|
|
61
|
+
<div v-if="isDrawerOpen" class="fixed inset-0 z-50 bg-black/40" @click="isDrawerOpen = false"></div>
|
|
62
|
+
<div :class="['fixed top-0 left-0 bottom-0 w-80 bg-[var(--color-surface)] border-r border-[var(--color-border)] p-6 z-50 transition-transform duration-300', isDrawerOpen ? 'translate-x-0' : '-translate-x-full']">
|
|
63
|
+
<div class="flex justify-between items-center mb-6">
|
|
64
|
+
<h4 class="font-bold">Navigation Drawer</h4>
|
|
65
|
+
<button @click="isDrawerOpen = false" class="text-xl">×</button>
|
|
66
|
+
</div>
|
|
67
|
+
<p class="text-sm text-[var(--color-text-muted)]">Vue 3 composition offcanvas drawer ready for your menu links.</p>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
|
|
3
|
+
export async function POST(request) {
|
|
4
|
+
try {
|
|
5
|
+
const body = await request.json();
|
|
6
|
+
const { email, password } = body;
|
|
7
|
+
|
|
8
|
+
if (email === 'admin@example.com' && password === 'admin123') {
|
|
9
|
+
return NextResponse.json({
|
|
10
|
+
success: true,
|
|
11
|
+
user: { id: 1, name: 'Admin User', email, role: 'admin' },
|
|
12
|
+
token: 'mock_jwt_token_admin'
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (email && password) {
|
|
17
|
+
return NextResponse.json({
|
|
18
|
+
success: true,
|
|
19
|
+
user: { id: 2, name: 'Regular User', email, role: 'user' },
|
|
20
|
+
token: 'mock_jwt_token_user'
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return NextResponse.json({ success: false, error: 'Invalid credentials' }, { status: 401 });
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return NextResponse.json({ success: false, error: error.message }, { status: 500 });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { connectToDatabase } from '@/lib/mongodb';
|
|
3
|
+
|
|
4
|
+
export async function GET() {
|
|
5
|
+
try {
|
|
6
|
+
await connectToDatabase();
|
|
7
|
+
return NextResponse.json({
|
|
8
|
+
status: 'online',
|
|
9
|
+
stack: 'Next.js App Router',
|
|
10
|
+
database: 'MongoDB (Mongoose)',
|
|
11
|
+
appName: '{{APP_TITLE}}',
|
|
12
|
+
author: '{{AUTHOR}}'
|
|
13
|
+
});
|
|
14
|
+
} catch (error) {
|
|
15
|
+
return NextResponse.json({ success: false, error: error.message }, { status: 500 });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
let isConnected = false;
|
|
4
|
+
|
|
5
|
+
export async function connectToDatabase() {
|
|
6
|
+
if (isConnected) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const uri = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/{{PROJECT_NAME}}';
|
|
10
|
+
try {
|
|
11
|
+
const db = await mongoose.connect(uri);
|
|
12
|
+
isConnected = db.connections[0].readyState;
|
|
13
|
+
console.log('⚡ Connected to MongoDB (Next.js App Router)');
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.error('MongoDB connection error:', err);
|
|
16
|
+
throw err;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getMssqlPool } from '@/lib/mssql';
|
|
3
|
+
|
|
4
|
+
export async function GET() {
|
|
5
|
+
try {
|
|
6
|
+
const pool = await getMssqlPool();
|
|
7
|
+
const result = await pool.request().query('SELECT @@VERSION as version, GETDATE() as currentTime');
|
|
8
|
+
|
|
9
|
+
return NextResponse.json({
|
|
10
|
+
status: 'online',
|
|
11
|
+
stack: 'Next.js App Router',
|
|
12
|
+
database: 'Microsoft SQL Server (MSSQL)',
|
|
13
|
+
appName: '{{APP_TITLE}}',
|
|
14
|
+
author: '{{AUTHOR}}',
|
|
15
|
+
dbInfo: result.recordset[0]
|
|
16
|
+
});
|
|
17
|
+
} catch (error) {
|
|
18
|
+
return NextResponse.json({
|
|
19
|
+
status: 'database_unreachable_or_offline',
|
|
20
|
+
stack: 'Next.js App Router + MSSQL',
|
|
21
|
+
message: error.message
|
|
22
|
+
}, { status: 500 });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import sql from 'mssql';
|
|
2
|
+
|
|
3
|
+
const sqlConfig = {
|
|
4
|
+
user: process.env.DB_USER || 'sa',
|
|
5
|
+
password: process.env.DB_PASSWORD || 'YourStrong@Password',
|
|
6
|
+
database: process.env.DB_NAME || '{{PROJECT_NAME}}',
|
|
7
|
+
server: process.env.DB_HOST || 'localhost',
|
|
8
|
+
port: parseInt(process.env.DB_PORT || '1433'),
|
|
9
|
+
pool: {
|
|
10
|
+
max: 10,
|
|
11
|
+
min: 0,
|
|
12
|
+
idleTimeoutMillis: 30000
|
|
13
|
+
},
|
|
14
|
+
options: {
|
|
15
|
+
encrypt: false, // Set to true for Azure
|
|
16
|
+
trustServerCertificate: true
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let poolPromise = null;
|
|
21
|
+
|
|
22
|
+
export async function getMssqlPool() {
|
|
23
|
+
if (!poolPromise) {
|
|
24
|
+
poolPromise = sql.connect(sqlConfig)
|
|
25
|
+
.then(pool => {
|
|
26
|
+
console.log('⚡ Connected to MSSQL Server (Next.js App Router)');
|
|
27
|
+
return pool;
|
|
28
|
+
})
|
|
29
|
+
.catch(err => {
|
|
30
|
+
poolPromise = null;
|
|
31
|
+
console.error('Database connection failed:', err);
|
|
32
|
+
throw err;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return poolPromise;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { sql };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const bcrypt = require('bcryptjs');
|
|
2
|
+
const jwt = require('jsonwebtoken');
|
|
3
|
+
const { JWT_SECRET } = require('../middlewares/auth');
|
|
4
|
+
|
|
5
|
+
// In-memory or dynamic model fallback
|
|
6
|
+
let usersTable = [];
|
|
7
|
+
|
|
8
|
+
exports.register = async (req, res) => {
|
|
9
|
+
const { name, email, password, role = 'user' } = req.body;
|
|
10
|
+
if (!name || !email || !password) {
|
|
11
|
+
return res.status(400).json({ success: false, error: 'Name, email, and password required.' });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const existing = usersTable.find(u => u.email === email);
|
|
15
|
+
if (existing) {
|
|
16
|
+
return res.status(409).json({ success: false, error: 'User already exists.' });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const hashedPassword = await bcrypt.hash(password, 10);
|
|
20
|
+
const newUser = { id: usersTable.length + 1, name, email, password: hashedPassword, role };
|
|
21
|
+
usersTable.push(newUser);
|
|
22
|
+
|
|
23
|
+
const token = jwt.sign({ id: newUser.id, name: newUser.name, email: newUser.email, role: newUser.role }, JWT_SECRET, { expiresIn: '7d' });
|
|
24
|
+
|
|
25
|
+
res.status(201).json({
|
|
26
|
+
success: true,
|
|
27
|
+
message: 'User registered successfully',
|
|
28
|
+
token,
|
|
29
|
+
user: { id: newUser.id, name: newUser.name, email: newUser.email, role: newUser.role }
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
exports.login = async (req, res) => {
|
|
34
|
+
const { email, password } = req.body;
|
|
35
|
+
const user = usersTable.find(u => u.email === email);
|
|
36
|
+
|
|
37
|
+
if (!user || !(await bcrypt.compare(password, user.password))) {
|
|
38
|
+
return res.status(401).json({ success: false, error: 'Invalid credentials.' });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const token = jwt.sign({ id: user.id, name: user.name, email: user.email, role: user.role }, JWT_SECRET, { expiresIn: '7d' });
|
|
42
|
+
|
|
43
|
+
res.json({
|
|
44
|
+
success: true,
|
|
45
|
+
message: 'Login successful',
|
|
46
|
+
token,
|
|
47
|
+
user: { id: user.id, name: user.name, email: user.email, role: user.role }
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
exports.getProfile = (req, res) => {
|
|
52
|
+
res.json({ success: true, user: req.user });
|
|
53
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const jwt = require('jsonwebtoken');
|
|
2
|
+
|
|
3
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'super_secret_jwt_key_boiler_craft_2026';
|
|
4
|
+
|
|
5
|
+
function auth(req, res, next) {
|
|
6
|
+
const authHeader = req.headers['authorization'];
|
|
7
|
+
const token = authHeader && authHeader.split(' ')[1];
|
|
8
|
+
|
|
9
|
+
if (!token) {
|
|
10
|
+
return res.status(401).json({ success: false, error: 'Access denied. No token provided.' });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const decoded = jwt.verify(token, JWT_SECRET);
|
|
15
|
+
req.user = decoded;
|
|
16
|
+
next();
|
|
17
|
+
} catch (err) {
|
|
18
|
+
return res.status(403).json({ success: false, error: 'Invalid or expired token.' });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function role(allowedRoles = []) {
|
|
23
|
+
if (typeof allowedRoles === 'string') allowedRoles = [allowedRoles];
|
|
24
|
+
|
|
25
|
+
return (req, res, next) => {
|
|
26
|
+
if (!req.user) {
|
|
27
|
+
return res.status(401).json({ success: false, error: 'Unauthenticated.' });
|
|
28
|
+
}
|
|
29
|
+
if (!allowedRoles.includes(req.user.role)) {
|
|
30
|
+
return res.status(403).json({ success: false, error: 'Forbidden. Insufficient permissions.' });
|
|
31
|
+
}
|
|
32
|
+
next();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = { auth, role, JWT_SECRET };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { register, login, getProfile } = require('../controllers/authController');
|
|
4
|
+
const { auth, role } = require('../middlewares/auth');
|
|
5
|
+
|
|
6
|
+
// Public
|
|
7
|
+
router.post('/register', register);
|
|
8
|
+
router.post('/login', login);
|
|
9
|
+
|
|
10
|
+
// Protected Routes
|
|
11
|
+
router.get('/profile', auth, getProfile);
|
|
12
|
+
router.get('/admin', auth, role('admin'), (req, res) => {
|
|
13
|
+
res.json({ success: true, message: 'Welcome to Admin Authorized Area!', user: req.user });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
module.exports = router;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const User = require('../models/User');
|
|
2
|
+
|
|
3
|
+
exports.getHealth = async (req, res) => {
|
|
4
|
+
res.json({
|
|
5
|
+
status: 'online',
|
|
6
|
+
app: '{{APP_TITLE}}',
|
|
7
|
+
description: '{{APP_DESC}}',
|
|
8
|
+
author: '{{AUTHOR}}',
|
|
9
|
+
database: 'MongoDB connected via Mongoose',
|
|
10
|
+
timestamp: new Date().toISOString()
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
exports.getUsers = async (req, res, next) => {
|
|
15
|
+
try {
|
|
16
|
+
const users = await User.find().sort({ createdAt: -1 }).limit(20);
|
|
17
|
+
res.json({ success: true, count: users.length, data: users });
|
|
18
|
+
} catch (err) {
|
|
19
|
+
next(err);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
exports.createUser = async (req, res, next) => {
|
|
24
|
+
try {
|
|
25
|
+
const user = await User.create(req.body);
|
|
26
|
+
res.status(201).json({ success: true, data: user });
|
|
27
|
+
} catch (err) {
|
|
28
|
+
next(err);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const UserSchema = new mongoose.Schema({
|
|
4
|
+
name: {
|
|
5
|
+
type: String,
|
|
6
|
+
required: [true, 'Name is required'],
|
|
7
|
+
trim: true
|
|
8
|
+
},
|
|
9
|
+
email: {
|
|
10
|
+
type: String,
|
|
11
|
+
required: [true, 'Email is required'],
|
|
12
|
+
unique: true,
|
|
13
|
+
lowercase: true,
|
|
14
|
+
trim: true
|
|
15
|
+
},
|
|
16
|
+
role: {
|
|
17
|
+
type: String,
|
|
18
|
+
enum: ['user', 'admin'],
|
|
19
|
+
default: 'user'
|
|
20
|
+
}
|
|
21
|
+
}, { timestamps: true });
|
|
22
|
+
|
|
23
|
+
module.exports = mongoose.model('User', UserSchema);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { getHealth, getUsers, createUser } = require('../controllers/userController');
|
|
4
|
+
|
|
5
|
+
router.get('/health', getHealth);
|
|
6
|
+
router.get('/users', getUsers);
|
|
7
|
+
router.post('/users', createUser);
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|