feat: Complete Woles Framework v1.0 with enterprise-grade UI

- Add comprehensive error handling system with custom error pages
- Implement professional enterprise-style design with Tailwind CSS
- Create modular HMVC architecture with clean separation of concerns
- Add security features: CSRF protection, XSS filtering, Argon2ID hashing
- Include CLI tools for development workflow
- Add error reporting dashboard with system monitoring
- Implement responsive design with consistent slate color scheme
- Replace all emoji icons with professional SVG icons
- Add comprehensive test suite with PHPUnit
- Include database migrations and seeders
- Add proper exception handling with fallback pages
- Implement template engine with custom syntax support
- Add helper functions and facades for clean code
- Include proper logging and debugging capabilities
This commit is contained in:
mwpn
2025-10-11 07:08:23 +07:00
commit 0b42271bfe
90 changed files with 8315 additions and 0 deletions

View File

@@ -0,0 +1,249 @@
<?php
namespace App\Core\Database;
/**
* NovaCore Database Migrator
* Handle database migrations
*/
class Migrator
{
private Connection $connection;
private string $migrationsPath;
public function __construct()
{
$this->connection = $this->getConnection();
$this->migrationsPath = __DIR__ . '/../../database/migrations';
}
/**
* Get database connection
*/
protected function getConnection(): Connection
{
$config = include __DIR__ . '/../../Config/database.php';
$connectionConfig = $config['connections'][$config['default']];
return new Connection($connectionConfig);
}
/**
* Run all pending migrations
*/
public function run(): void
{
$this->createMigrationsTable();
$migrations = $this->getPendingMigrations();
foreach ($migrations as $migration) {
$this->runMigration($migration);
}
echo "Migrations completed successfully!\n";
}
/**
* Create migrations table
*/
private function createMigrationsTable(): void
{
$sql = "CREATE TABLE IF NOT EXISTS migrations (
id INT AUTO_INCREMENT PRIMARY KEY,
migration VARCHAR(255) NOT NULL,
batch INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)";
$this->connection->execute($sql);
}
/**
* Get pending migrations
*/
private function getPendingMigrations(): array
{
$migrationFiles = glob($this->migrationsPath . '/*.php');
$migratedFiles = $this->getMigratedFiles();
$pending = [];
foreach ($migrationFiles as $file) {
$filename = basename($file);
if (!in_array($filename, $migratedFiles)) {
$pending[] = $file;
}
}
sort($pending);
return $pending;
}
/**
* Get already migrated files
*/
private function getMigratedFiles(): array
{
$sql = "SELECT migration FROM migrations ORDER BY id";
$results = $this->connection->fetchAll($sql);
return array_column($results, 'migration');
}
/**
* Run single migration
*/
private function runMigration(string $file): void
{
$filename = basename($file);
$className = $this->getMigrationClassName($filename);
require_once $file;
if (!class_exists($className)) {
throw new \Exception("Migration class {$className} not found in {$file}");
}
$migration = new $className();
$migration->up();
// Record migration
$this->recordMigration($filename);
echo "{$filename}\n";
}
/**
* Get migration class name
*/
private function getMigrationClassName(string $filename): string
{
$name = pathinfo($filename, PATHINFO_FILENAME);
$parts = explode('_', $name);
// Remove timestamp
array_shift($parts);
// Convert to PascalCase
$className = '';
foreach ($parts as $part) {
$className .= ucfirst($part);
}
return $className;
}
/**
* Record migration
*/
private function recordMigration(string $filename): void
{
$batch = $this->getNextBatchNumber();
$sql = "INSERT INTO migrations (migration, batch) VALUES (?, ?)";
$this->connection->execute($sql, [$filename, $batch]);
}
/**
* Get next batch number
*/
private function getNextBatchNumber(): int
{
$sql = "SELECT MAX(batch) as max_batch FROM migrations";
$result = $this->connection->fetch($sql);
return ($result['max_batch'] ?? 0) + 1;
}
/**
* Rollback last batch
*/
public function rollback(): void
{
$lastBatch = $this->getLastBatchNumber();
if (!$lastBatch) {
echo "No migrations to rollback.\n";
return;
}
$migrations = $this->getMigrationsByBatch($lastBatch);
foreach (array_reverse($migrations) as $migration) {
$this->rollbackMigration($migration);
}
echo "Rollback completed successfully!\n";
}
/**
* Get last batch number
*/
private function getLastBatchNumber(): ?int
{
$sql = "SELECT MAX(batch) as max_batch FROM migrations";
$result = $this->connection->fetch($sql);
return $result['max_batch'] ?? null;
}
/**
* Get migrations by batch
*/
private function getMigrationsByBatch(int $batch): array
{
$sql = "SELECT migration FROM migrations WHERE batch = ? ORDER BY id DESC";
$results = $this->connection->fetchAll($sql, [$batch]);
return array_column($results, 'migration');
}
/**
* Rollback single migration
*/
private function rollbackMigration(string $filename): void
{
$file = $this->migrationsPath . '/' . $filename;
if (!file_exists($file)) {
echo "Warning: Migration file {$filename} not found.\n";
return;
}
$className = $this->getMigrationClassName($filename);
require_once $file;
if (!class_exists($className)) {
echo "Warning: Migration class {$className} not found.\n";
return;
}
$migration = new $className();
$migration->down();
// Remove migration record
$sql = "DELETE FROM migrations WHERE migration = ?";
$this->connection->execute($sql, [$filename]);
echo "✓ Rolled back {$filename}\n";
}
/**
* Get migration status
*/
public function status(): void
{
$migrationFiles = glob($this->migrationsPath . '/*.php');
$migratedFiles = $this->getMigratedFiles();
echo "Migration Status:\n";
echo "================\n";
foreach ($migrationFiles as $file) {
$filename = basename($file);
$status = in_array($filename, $migratedFiles) ? '✓' : '✗';
echo "{$status} {$filename}\n";
}
}
}