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,73 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
namespace App\Controllers;
|
|
3
|
+
|
|
4
|
+
use App\Models\User;
|
|
5
|
+
use App\Config\Auth;
|
|
6
|
+
|
|
7
|
+
class AuthController {
|
|
8
|
+
public function showLogin(): void {
|
|
9
|
+
require __DIR__ . '/../Views/auth/login.php';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public function login(): void {
|
|
13
|
+
header('Content-Type: application/json');
|
|
14
|
+
$raw = file_get_contents('php://input');
|
|
15
|
+
$data = json_decode($raw, true) ?: $_POST;
|
|
16
|
+
|
|
17
|
+
$email = trim($data['email'] ?? '');
|
|
18
|
+
$password = $data['password'] ?? '';
|
|
19
|
+
|
|
20
|
+
$userModel = new User();
|
|
21
|
+
$user = $userModel->findByEmail($email);
|
|
22
|
+
|
|
23
|
+
if ($user && password_verify($password, $user['password'])) {
|
|
24
|
+
Auth::login($user);
|
|
25
|
+
echo json_encode(['success' => true, 'redirect' => '/dashboard']);
|
|
26
|
+
} else {
|
|
27
|
+
http_response_code(401);
|
|
28
|
+
echo json_encode(['success' => false, 'error' => 'Invalid email or password']);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public function showRegister(): void {
|
|
33
|
+
require __DIR__ . '/../Views/auth/register.php';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public function register(): void {
|
|
37
|
+
header('Content-Type: application/json');
|
|
38
|
+
$raw = file_get_contents('php://input');
|
|
39
|
+
$data = json_decode($raw, true) ?: $_POST;
|
|
40
|
+
|
|
41
|
+
$name = trim($data['name'] ?? '');
|
|
42
|
+
$email = trim($data['email'] ?? '');
|
|
43
|
+
$password = $data['password'] ?? '';
|
|
44
|
+
$role = $data['role'] ?? 'user';
|
|
45
|
+
|
|
46
|
+
if (empty($name) || empty($email) || strlen($password) < 6) {
|
|
47
|
+
http_response_code(422);
|
|
48
|
+
echo json_encode(['success' => false, 'error' => 'Name, valid email and min 6 char password required.']);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$userModel = new User();
|
|
53
|
+
if ($userModel->findByEmail($email)) {
|
|
54
|
+
http_response_code(409);
|
|
55
|
+
echo json_encode(['success' => false, 'error' => 'Email already registered.']);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
$hashed = password_hash($password, PASSWORD_BCRYPT);
|
|
60
|
+
$userModel->createWithPassword($name, $email, $hashed, $role);
|
|
61
|
+
|
|
62
|
+
$newUser = $userModel->findByEmail($email);
|
|
63
|
+
Auth::login($newUser);
|
|
64
|
+
|
|
65
|
+
echo json_encode(['success' => true, 'redirect' => '/dashboard']);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public function logout(): void {
|
|
69
|
+
Auth::logout();
|
|
70
|
+
header('Location: /login');
|
|
71
|
+
exit;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
namespace App\Controllers;
|
|
3
|
+
|
|
4
|
+
use App\Models\User;
|
|
5
|
+
|
|
6
|
+
class HomeController {
|
|
7
|
+
public function index(): void {
|
|
8
|
+
$appName = "{{APP_TITLE}}";
|
|
9
|
+
$appDesc = "{{APP_DESC}}";
|
|
10
|
+
$author = "{{AUTHOR}}";
|
|
11
|
+
|
|
12
|
+
$userModel = new User();
|
|
13
|
+
$users = $userModel->all();
|
|
14
|
+
|
|
15
|
+
require __DIR__ . '/../Views/home.php';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* AJAX Endpoint for User Registration
|
|
20
|
+
*/
|
|
21
|
+
public function apiCreateUser(): void {
|
|
22
|
+
header('Content-Type: application/json');
|
|
23
|
+
$raw = file_get_contents('php://input');
|
|
24
|
+
$data = json_decode($raw, true) ?: $_POST;
|
|
25
|
+
|
|
26
|
+
$name = trim($data['name'] ?? '');
|
|
27
|
+
$email = trim($data['email'] ?? '');
|
|
28
|
+
|
|
29
|
+
if (empty($name) || empty($email)) {
|
|
30
|
+
http_response_code(422);
|
|
31
|
+
echo json_encode(['success' => false, 'error' => 'Name and Email are required.']);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
$userModel = new User();
|
|
37
|
+
$userModel->create($name, $email);
|
|
38
|
+
echo json_encode(['success' => true, 'message' => 'User created via AJAX successfully!']);
|
|
39
|
+
} catch (\Exception $e) {
|
|
40
|
+
http_response_code(500);
|
|
41
|
+
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* AJAX Endpoint for fetching Users list
|
|
47
|
+
*/
|
|
48
|
+
public function apiGetUsers(): void {
|
|
49
|
+
header('Content-Type: application/json');
|
|
50
|
+
$userModel = new User();
|
|
51
|
+
$users = $userModel->all();
|
|
52
|
+
echo json_encode(['success' => true, 'data' => $users]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public function apiHealth(): void {
|
|
56
|
+
header('Content-Type: application/json');
|
|
57
|
+
echo json_encode([
|
|
58
|
+
'status' => 'online',
|
|
59
|
+
'platform' => 'Raw PHP MVC',
|
|
60
|
+
'database' => 'MySQL PDO',
|
|
61
|
+
'ajax_support' => true,
|
|
62
|
+
'timestamp' => time()
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
namespace App\Models;
|
|
3
|
+
|
|
4
|
+
use App\Config\QueryBuilder;
|
|
5
|
+
use App\Config\Database;
|
|
6
|
+
|
|
7
|
+
class User {
|
|
8
|
+
protected string $table = 'users';
|
|
9
|
+
|
|
10
|
+
public function __construct() {
|
|
11
|
+
$this->initSchema();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private function initSchema(): void {
|
|
15
|
+
$sql = "CREATE TABLE IF NOT EXISTS {$this->table} (
|
|
16
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
17
|
+
name VARCHAR(100) NOT NULL,
|
|
18
|
+
email VARCHAR(150) UNIQUE NOT NULL,
|
|
19
|
+
password VARCHAR(255) NULL,
|
|
20
|
+
role VARCHAR(50) DEFAULT 'user',
|
|
21
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
22
|
+
) ENGINE=INNODB;";
|
|
23
|
+
Database::execute($sql);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public function all(): array {
|
|
27
|
+
return QueryBuilder::table($this->table)
|
|
28
|
+
->orderBy('id', 'DESC')
|
|
29
|
+
->limit(20)
|
|
30
|
+
->get();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public function find(int $id): ?array {
|
|
34
|
+
return QueryBuilder::table($this->table)
|
|
35
|
+
->where('id', $id)
|
|
36
|
+
->first();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public function findByEmail(string $email): ?array {
|
|
40
|
+
return QueryBuilder::table($this->table)
|
|
41
|
+
->where('email', $email)
|
|
42
|
+
->first();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public function createWithPassword(string $name, string $email, string $passwordHash, string $role = 'user'): bool {
|
|
46
|
+
return QueryBuilder::table($this->table)->insert([
|
|
47
|
+
'name' => $name,
|
|
48
|
+
'email' => $email,
|
|
49
|
+
'password' => $passwordHash,
|
|
50
|
+
'role' => $role
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public function create(string $name, string $email): bool {
|
|
55
|
+
return QueryBuilder::table($this->table)->insert([
|
|
56
|
+
'name' => $name,
|
|
57
|
+
'email' => $email
|
|
58
|
+
]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>404 Not Found</title>
|
|
6
|
+
<link rel="stylesheet" href="/css/theme.css">
|
|
7
|
+
</head>
|
|
8
|
+
<body style="display: flex; height: 100vh; align-items: center; justify-content: center; text-align: center;">
|
|
9
|
+
<div>
|
|
10
|
+
<h1 style="font-size: 4rem; color: var(--color-primary);">404</h1>
|
|
11
|
+
<p style="color: var(--color-text-muted);">Page Not Found</p>
|
|
12
|
+
<a href="/" style="color: var(--color-primary); text-decoration: underline; margin-top: 1rem; display: inline-block;">Return Home</a>
|
|
13
|
+
</div>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Login — Access Account</title>
|
|
7
|
+
<link rel="stylesheet" href="/css/theme.css">
|
|
8
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
9
|
+
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
10
|
+
</head>
|
|
11
|
+
<body class="min-h-screen flex items-center justify-center p-6 bg-[var(--color-bg)] text-[var(--color-text)]">
|
|
12
|
+
<div class="max-w-md w-full p-8 rounded-2xl bg-[var(--color-surface)] border border-[var(--color-border)] shadow-2xl">
|
|
13
|
+
<h2 class="text-2xl font-bold mb-1">Welcome Back</h2>
|
|
14
|
+
<p class="text-sm text-[var(--color-text-muted)] mb-6">Sign in to your authenticated account</p>
|
|
15
|
+
|
|
16
|
+
<form id="loginForm" class="space-y-4">
|
|
17
|
+
<div>
|
|
18
|
+
<label class="block text-xs font-semibold mb-1">Email Address</label>
|
|
19
|
+
<input type="email" id="email" required class="w-full px-4 py-2.5 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] text-[var(--color-text)] outline-none focus:border-[var(--color-primary)]">
|
|
20
|
+
</div>
|
|
21
|
+
<div>
|
|
22
|
+
<label class="block text-xs font-semibold mb-1">Password</label>
|
|
23
|
+
<input type="password" id="password" required class="w-full px-4 py-2.5 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] text-[var(--color-text)] outline-none focus:border-[var(--color-primary)]">
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div id="loginMsg" class="text-xs font-semibold"></div>
|
|
27
|
+
|
|
28
|
+
<button type="submit" id="btnLogin" class="w-full py-3 rounded-xl bg-[var(--color-primary)] text-white font-bold hover:opacity-95 transition shadow-lg shadow-[var(--color-primary)]/25">
|
|
29
|
+
Sign In
|
|
30
|
+
</button>
|
|
31
|
+
</form>
|
|
32
|
+
|
|
33
|
+
<p class="text-xs text-center mt-6 text-[var(--color-text-muted)]">
|
|
34
|
+
Don't have an account? <a href="/register" class="text-[var(--color-primary)] font-bold">Register</a>
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<script>
|
|
39
|
+
$('#loginForm').on('submit', function(e) {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
const btn = $('#btnLogin');
|
|
42
|
+
const msg = $('#loginMsg');
|
|
43
|
+
btn.prop('disabled', true).text('Verifying...');
|
|
44
|
+
|
|
45
|
+
$.ajax({
|
|
46
|
+
url: '/api/login',
|
|
47
|
+
type: 'POST',
|
|
48
|
+
contentType: 'application/json',
|
|
49
|
+
data: JSON.stringify({
|
|
50
|
+
email: $('#email').val(),
|
|
51
|
+
password: $('#password').val()
|
|
52
|
+
}),
|
|
53
|
+
success: function(res) {
|
|
54
|
+
msg.html('<span class="text-emerald-500">✓ Login successful! Redirecting...</span>');
|
|
55
|
+
window.location.href = res.redirect || '/dashboard';
|
|
56
|
+
},
|
|
57
|
+
error: function(xhr) {
|
|
58
|
+
const err = xhr.responseJSON ? xhr.responseJSON.error : 'Invalid credentials';
|
|
59
|
+
msg.html('<span class="text-rose-500">✗ ' + err + '</span>');
|
|
60
|
+
btn.prop('disabled', false).text('Sign In');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
</body>
|
|
66
|
+
</html>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Register — Create Account</title>
|
|
7
|
+
<link rel="stylesheet" href="/css/theme.css">
|
|
8
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
9
|
+
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
10
|
+
</head>
|
|
11
|
+
<body class="min-h-screen flex items-center justify-center p-6 bg-[var(--color-bg)] text-[var(--color-text)]">
|
|
12
|
+
<div class="max-w-md w-full p-8 rounded-2xl bg-[var(--color-surface)] border border-[var(--color-border)] shadow-2xl">
|
|
13
|
+
<h2 class="text-2xl font-bold mb-1">Create Account</h2>
|
|
14
|
+
<p class="text-sm text-[var(--color-text-muted)] mb-6">Register a new authenticated profile</p>
|
|
15
|
+
|
|
16
|
+
<form id="regForm" class="space-y-4">
|
|
17
|
+
<div>
|
|
18
|
+
<label class="block text-xs font-semibold mb-1">Full Name</label>
|
|
19
|
+
<input type="text" id="name" required class="w-full px-4 py-2.5 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] text-[var(--color-text)] outline-none focus:border-[var(--color-primary)]">
|
|
20
|
+
</div>
|
|
21
|
+
<div>
|
|
22
|
+
<label class="block text-xs font-semibold mb-1">Email Address</label>
|
|
23
|
+
<input type="email" id="email" required class="w-full px-4 py-2.5 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] text-[var(--color-text)] outline-none focus:border-[var(--color-primary)]">
|
|
24
|
+
</div>
|
|
25
|
+
<div>
|
|
26
|
+
<label class="block text-xs font-semibold mb-1">Password</label>
|
|
27
|
+
<input type="password" id="password" minlength="6" required class="w-full px-4 py-2.5 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] text-[var(--color-text)] outline-none focus:border-[var(--color-primary)]">
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div id="regMsg" class="text-xs font-semibold"></div>
|
|
31
|
+
|
|
32
|
+
<button type="submit" id="btnReg" class="w-full py-3 rounded-xl bg-[var(--color-primary)] text-white font-bold hover:opacity-95 transition shadow-lg shadow-[var(--color-primary)]/25">
|
|
33
|
+
Register Now
|
|
34
|
+
</button>
|
|
35
|
+
</form>
|
|
36
|
+
|
|
37
|
+
<p class="text-xs text-center mt-6 text-[var(--color-text-muted)]">
|
|
38
|
+
Already registered? <a href="/login" class="text-[var(--color-primary)] font-bold">Login</a>
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<script>
|
|
43
|
+
$('#regForm').on('submit', function(e) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
const btn = $('#btnReg');
|
|
46
|
+
const msg = $('#regMsg');
|
|
47
|
+
btn.prop('disabled', true).text('Creating Account...');
|
|
48
|
+
|
|
49
|
+
$.ajax({
|
|
50
|
+
url: '/api/register',
|
|
51
|
+
type: 'POST',
|
|
52
|
+
contentType: 'application/json',
|
|
53
|
+
data: JSON.stringify({
|
|
54
|
+
name: $('#name').val(),
|
|
55
|
+
email: $('#email').val(),
|
|
56
|
+
password: $('#password').val()
|
|
57
|
+
}),
|
|
58
|
+
success: function(res) {
|
|
59
|
+
msg.html('<span class="text-emerald-500">✓ Account created! Redirecting...</span>');
|
|
60
|
+
window.location.href = res.redirect || '/dashboard';
|
|
61
|
+
},
|
|
62
|
+
error: function(xhr) {
|
|
63
|
+
const err = xhr.responseJSON ? xhr.responseJSON.error : 'Registration failed';
|
|
64
|
+
msg.html('<span class="text-rose-500">✗ ' + err + '</span>');
|
|
65
|
+
btn.prop('disabled', false).text('Register Now');
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
70
|
+
</body>
|
|
71
|
+
</html>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
use App\Config\Auth;
|
|
3
|
+
$currentUser = Auth::user();
|
|
4
|
+
?>
|
|
5
|
+
<!DOCTYPE html>
|
|
6
|
+
<html lang="en">
|
|
7
|
+
<head>
|
|
8
|
+
<meta charset="UTF-8">
|
|
9
|
+
<title>Dashboard — Authenticated Area</title>
|
|
10
|
+
<link rel="stylesheet" href="/css/theme.css">
|
|
11
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
12
|
+
</head>
|
|
13
|
+
<body class="p-8 bg-[var(--color-bg)] text-[var(--color-text)] min-h-screen">
|
|
14
|
+
<div class="max-w-4xl mx-auto">
|
|
15
|
+
<div class="flex justify-between items-center pb-6 border-b border-[var(--color-border)] mb-8">
|
|
16
|
+
<div>
|
|
17
|
+
<h1 class="text-3xl font-extrabold">Protected Dashboard</h1>
|
|
18
|
+
<p class="text-sm text-[var(--color-text-muted)]">Session Authentication & Role Authorization Active</p>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="flex items-center gap-4">
|
|
21
|
+
<span class="px-3 py-1 text-xs rounded-full bg-emerald-500/10 text-emerald-400 font-bold border border-emerald-500/20">
|
|
22
|
+
Role: <?= htmlspecialchars($currentUser['role']) ?>
|
|
23
|
+
</span>
|
|
24
|
+
<a href="/logout" class="px-4 py-2 rounded-xl border border-[var(--color-border)] text-sm font-semibold hover:bg-rose-500 hover:text-white transition">
|
|
25
|
+
Logout
|
|
26
|
+
</a>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="p-6 rounded-2xl bg-[var(--color-surface)] border border-[var(--color-border)] shadow-xl mb-6">
|
|
31
|
+
<h3 class="font-bold text-lg mb-2">Welcome, <?= htmlspecialchars($currentUser['name']) ?>!</h3>
|
|
32
|
+
<p class="text-sm text-[var(--color-text-muted)] mb-4">You are authorized to view this page. Your email is <strong><?= htmlspecialchars($currentUser['email']) ?></strong>.</p>
|
|
33
|
+
|
|
34
|
+
<div class="p-4 rounded-xl bg-[var(--color-bg)] border border-[var(--color-border)] font-mono text-xs text-emerald-400">
|
|
35
|
+
// Session State<br>
|
|
36
|
+
User ID: <?= $currentUser['id'] ?><br>
|
|
37
|
+
Role: <?= $currentUser['role'] ?><br>
|
|
38
|
+
Guard: Middleware::auth() Passed
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<?php if ($currentUser['role'] === 'admin'): ?>
|
|
43
|
+
<div class="p-6 rounded-2xl bg-amber-500/10 border border-amber-500/30 text-amber-200">
|
|
44
|
+
<h4 class="font-bold mb-1">👑 Admin Only Area</h4>
|
|
45
|
+
<p class="text-xs">This section is strictly rendered because your account has the 'admin' permission.</p>
|
|
46
|
+
</div>
|
|
47
|
+
<?php endif; ?>
|
|
48
|
+
</div>
|
|
49
|
+
</body>
|
|
50
|
+
</html>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="{{COLOR_MODE}}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title><?= htmlspecialchars($appName) ?></title>
|
|
7
|
+
<link rel="stylesheet" href="/css/theme.css">
|
|
8
|
+
{{STYLESHEET_LINK}}
|
|
9
|
+
<!-- jQuery Support CDN -->
|
|
10
|
+
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
11
|
+
</head>
|
|
12
|
+
<body class="p-6">
|
|
13
|
+
<div style="max-width: 900px; margin: 2rem auto; text-align: center;">
|
|
14
|
+
{{LOGO_HTML}}
|
|
15
|
+
<h1 style="font-size: 2.5rem; font-weight: 800; margin-bottom: 0.5rem;"><?= htmlspecialchars($appName) ?></h1>
|
|
16
|
+
<p style="color: var(--color-text-muted); font-size: 1.1rem;"><?= htmlspecialchars($appDesc) ?></p>
|
|
17
|
+
<p style="font-size: 0.85rem; color: var(--color-primary); font-weight: 700; margin-top: 4px;">Crafted by <?= htmlspecialchars($author) ?></p>
|
|
18
|
+
|
|
19
|
+
<!-- AJAX Live Form Demo (jQuery / Vanilla Fetch) -->
|
|
20
|
+
<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);">
|
|
21
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
|
22
|
+
<h3 style="margin: 0;">⚡ Live AJAX + jQuery Demo (No Page Reload)</h3>
|
|
23
|
+
<span style="font-size: 0.75rem; padding: 4px 8px; border-radius: 6px; background: rgba(16, 185, 129, 0.2); color: #10b981; font-weight: bold;">AJAX Ready</span>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<p style="color: var(--color-text-muted); font-size: 0.9rem;">
|
|
27
|
+
এই Raw PHP আর্কিটেকচারে আপনি নির্দ্বিধায় <strong>jQuery, Axios, Fetch API, Tailwind CSS</strong> বা <strong>Bootstrap 5</strong> যা ইচ্ছা ব্যবহার করতে পারবেন।
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<form id="ajaxUserForm" style="display: grid; grid-template-columns: 1fr 1fr auto; gap: 10px; margin-top: 1rem;">
|
|
31
|
+
<input type="text" id="ajaxName" placeholder="Full Name" required style="padding: 10px; border-radius: 8px; border: 1px solid var(--color-border); background: var(--color-bg); color: var(--color-text);">
|
|
32
|
+
<input type="email" id="ajaxEmail" placeholder="Email Address" required style="padding: 10px; border-radius: 8px; border: 1px solid var(--color-border); background: var(--color-bg); color: var(--color-text);">
|
|
33
|
+
<button type="submit" id="btnSubmit" style="background: var(--color-primary); color: white; border: none; padding: 10px 18px; border-radius: 8px; font-weight: bold; cursor: pointer;">
|
|
34
|
+
Send via AJAX
|
|
35
|
+
</button>
|
|
36
|
+
</form>
|
|
37
|
+
<div id="ajaxResponse" style="margin-top: 10px; font-size: 0.85rem; font-weight: 600;"></div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Dynamic UI Components Section -->
|
|
41
|
+
<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;">
|
|
42
|
+
<h4>Included Interactive UI Components:</h4>
|
|
43
|
+
{{UI_COMPONENTS}}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<!-- Live jQuery AJAX Handling Script -->
|
|
48
|
+
<script>
|
|
49
|
+
$(document).ready(function() {
|
|
50
|
+
$('#ajaxUserForm').on('submit', function(e) {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
const btn = $('#btnSubmit');
|
|
53
|
+
const resp = $('#ajaxResponse');
|
|
54
|
+
|
|
55
|
+
btn.prop('disabled', true).text('Saving...');
|
|
56
|
+
resp.html('<span style="color: var(--color-primary);">Connecting with server...</span>');
|
|
57
|
+
|
|
58
|
+
$.ajax({
|
|
59
|
+
url: '/api/users',
|
|
60
|
+
type: 'POST',
|
|
61
|
+
contentType: 'application/json',
|
|
62
|
+
data: JSON.stringify({
|
|
63
|
+
name: $('#ajaxName').val(),
|
|
64
|
+
email: $('#ajaxEmail').val()
|
|
65
|
+
}),
|
|
66
|
+
success: function(response) {
|
|
67
|
+
resp.html('<span style="color: #10b981;">✓ ' + response.message + '</span>');
|
|
68
|
+
$('#ajaxUserForm')[0].reset();
|
|
69
|
+
},
|
|
70
|
+
error: function(xhr) {
|
|
71
|
+
const err = xhr.responseJSON ? xhr.responseJSON.error : 'Submission failed';
|
|
72
|
+
resp.html('<span style="color: #ef4444;">✗ ' + err + '</span>');
|
|
73
|
+
},
|
|
74
|
+
complete: function() {
|
|
75
|
+
btn.prop('disabled', false).text('Send via AJAX');
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
</script>
|
|
81
|
+
<script src="/js/theme.js"></script>
|
|
82
|
+
</body>
|
|
83
|
+
</html>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
namespace App\Config;
|
|
3
|
+
|
|
4
|
+
class Auth {
|
|
5
|
+
public static function init(): void {
|
|
6
|
+
if (session_status() === PHP_SESSION_NONE) {
|
|
7
|
+
session_start();
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public static function login(array $user): void {
|
|
12
|
+
self::init();
|
|
13
|
+
$_SESSION['user_id'] = $user['id'];
|
|
14
|
+
$_SESSION['user_role'] = $user['role'] ?? 'user';
|
|
15
|
+
$_SESSION['user_name'] = $user['name'];
|
|
16
|
+
$_SESSION['user_email'] = $user['email'];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static function check(): bool {
|
|
20
|
+
self::init();
|
|
21
|
+
return isset($_SESSION['user_id']);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public static function user(): ?array {
|
|
25
|
+
self::init();
|
|
26
|
+
if (!self::check()) return null;
|
|
27
|
+
return [
|
|
28
|
+
'id' => $_SESSION['user_id'],
|
|
29
|
+
'name' => $_SESSION['user_name'],
|
|
30
|
+
'email' => $_SESSION['user_email'],
|
|
31
|
+
'role' => $_SESSION['user_role']
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public static function id(): ?int {
|
|
36
|
+
self::init();
|
|
37
|
+
return $_SESSION['user_id'] ?? null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public static function hasRole(string|array $roles): bool {
|
|
41
|
+
self::init();
|
|
42
|
+
if (!self::check()) return false;
|
|
43
|
+
$userRole = $_SESSION['user_role'] ?? 'user';
|
|
44
|
+
if (is_array($roles)) {
|
|
45
|
+
return in_array($userRole, $roles);
|
|
46
|
+
}
|
|
47
|
+
return $userRole === $roles;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public static function logout(): void {
|
|
51
|
+
self::init();
|
|
52
|
+
$_SESSION = [];
|
|
53
|
+
if (ini_get("session.use_cookies")) {
|
|
54
|
+
$params = session_get_cookie_params();
|
|
55
|
+
setcookie(session_name(), '', time() - 42000,
|
|
56
|
+
$params["path"], $params["domain"],
|
|
57
|
+
$params["secure"], $params["httponly"]
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
session_destroy();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
namespace App\Config;
|
|
3
|
+
|
|
4
|
+
use PDO;
|
|
5
|
+
use PDOException;
|
|
6
|
+
|
|
7
|
+
class Database {
|
|
8
|
+
private static ?PDO $instance = null;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get Singleton PDO connection with multi-driver support:
|
|
12
|
+
* - MySQL / MariaDB (utf8mb4)
|
|
13
|
+
* - MSSQL (sqlsrv / dblib)
|
|
14
|
+
* - PostgreSQL (pgsql)
|
|
15
|
+
* - SQLite (zero-config local file)
|
|
16
|
+
*/
|
|
17
|
+
public static function getConnection(): PDO {
|
|
18
|
+
if (self::$instance === null) {
|
|
19
|
+
$dbType = strtolower(getenv('DB_CONNECTION') ?: '{{DB_TYPE}}');
|
|
20
|
+
$host = getenv('DB_HOST') ?: '127.0.0.1';
|
|
21
|
+
$dbname = getenv('DB_NAME') ?: getenv('DB_DATABASE') ?: '{{DB_NAME}}';
|
|
22
|
+
$user = getenv('DB_USER') ?: getenv('DB_USERNAME') ?: 'root';
|
|
23
|
+
$pass = getenv('DB_PASS') ?: getenv('DB_PASSWORD') ?: '';
|
|
24
|
+
|
|
25
|
+
$commonOptions = [
|
|
26
|
+
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
27
|
+
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
28
|
+
PDO::ATTR_EMULATE_PREPARES => false,
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
switch ($dbType) {
|
|
33
|
+
case 'mssql':
|
|
34
|
+
case 'sqlsrv':
|
|
35
|
+
$port = getenv('DB_PORT') ?: '1433';
|
|
36
|
+
// Checks if sqlsrv driver exists, else fallback to dblib
|
|
37
|
+
if (in_array('sqlsrv', PDO::getAvailableDrivers())) {
|
|
38
|
+
$dsn = "sqlsrv:Server={$host},{$port};Database={$dbname};TrustServerCertificate=true";
|
|
39
|
+
} else {
|
|
40
|
+
$dsn = "dblib:host={$host}:{$port};dbname={$dbname}";
|
|
41
|
+
}
|
|
42
|
+
self::$instance = new PDO($dsn, $user, $pass, $commonOptions);
|
|
43
|
+
break;
|
|
44
|
+
|
|
45
|
+
case 'pgsql':
|
|
46
|
+
case 'postgres':
|
|
47
|
+
$port = getenv('DB_PORT') ?: '5432';
|
|
48
|
+
$dsn = "pgsql:host={$host};port={$port};dbname={$dbname}";
|
|
49
|
+
self::$instance = new PDO($dsn, $user, $pass, $commonOptions);
|
|
50
|
+
break;
|
|
51
|
+
|
|
52
|
+
case 'sqlite':
|
|
53
|
+
$sqlitePath = getenv('DB_PATH') ?: __DIR__ . '/../../database.sqlite';
|
|
54
|
+
if (!file_exists($sqlitePath)) {
|
|
55
|
+
touch($sqlitePath);
|
|
56
|
+
}
|
|
57
|
+
$dsn = "sqlite:{$sqlitePath}";
|
|
58
|
+
self::$instance = new PDO($dsn, null, null, $commonOptions);
|
|
59
|
+
break;
|
|
60
|
+
|
|
61
|
+
case 'mysql':
|
|
62
|
+
case 'mariadb':
|
|
63
|
+
default:
|
|
64
|
+
$port = getenv('DB_PORT') ?: '3306';
|
|
65
|
+
$charset = 'utf8mb4';
|
|
66
|
+
$dsn = "mysql:host={$host};port={$port};dbname={$dbname};charset={$charset}";
|
|
67
|
+
self::$instance = new PDO($dsn, $user, $pass, $commonOptions);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
} catch (PDOException $e) {
|
|
71
|
+
http_response_code(500);
|
|
72
|
+
header('Content-Type: application/json');
|
|
73
|
+
echo json_encode([
|
|
74
|
+
'success' => false,
|
|
75
|
+
'error' => "Database Connection Failed [Engine: {$dbType}]",
|
|
76
|
+
'message' => $e->getMessage()
|
|
77
|
+
]);
|
|
78
|
+
exit;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return self::$instance;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Helper to run parameterized queries safely
|
|
86
|
+
*/
|
|
87
|
+
public static function query(string $sql, array $params = []): array {
|
|
88
|
+
$stmt = self::getConnection()->prepare($sql);
|
|
89
|
+
$stmt->execute($params);
|
|
90
|
+
return $stmt->fetchAll();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Helper for INSERT / UPDATE / DELETE
|
|
95
|
+
*/
|
|
96
|
+
public static function execute(string $sql, array $params = []): bool {
|
|
97
|
+
$stmt = self::getConnection()->prepare($sql);
|
|
98
|
+
return $stmt->execute($params);
|
|
99
|
+
}
|
|
100
|
+
}
|