Итерация 1: каркас Slim 4 приложения

- Slim 4 + php-di контейнер с маршрутизацией
- Docker Compose: app (PHP 8.3-FPM) + web (nginx) + db (MariaDB)
- Phinx миграция: таблица users (uuid, username, password_hash)
- Auth: login/logout с сессиями
- Setup: создание первого пользователя
- Dashboard с layout (Bootstrap 5.3)
- CLI: bin/console, bin/run-scan-worker.php
- Smoke test: scripts/check.sh
- README.md, PLAN.md
This commit is contained in:
2026-05-26 07:26:55 +08:00
commit e7732f5cee
31 changed files with 8790 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
/* Domovoy custom styles */
body {
background-color: #f8f9fa;
}
#sidebar-wrapper .list-group-item:hover {
background-color: #343a40 !important;
color: #fff !important;
}
#sidebar-wrapper .list-group-item.active {
background-color: #0d6efd !important;
border-color: #0d6efd !important;
}
.card {
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}
.navbar-brand {
font-weight: bold;
}
+99
View File
@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
session_start();
require dirname(__DIR__) . '/vendor/autoload.php';
use DI\ContainerBuilder;
use Slim\Factory\AppFactory;
$dotenv = \Dotenv\Dotenv::createImmutable(dirname(__DIR__));
$dotenv->safeLoad();
// Build container
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
'settings' => [
'db' => [
'host' => getenv('DB_HOST') ?: 'db',
'port' => (int)(getenv('DB_PORT') ?: 3306),
'database' => getenv('DB_DATABASE') ?: 'domovoy',
'username' => getenv('DB_USERNAME') ?: 'domovoy',
'password' => getenv('DB_PASSWORD') ?: 'domovoy',
],
'app' => [
'env' => getenv('APP_ENV') ?: 'development',
'secret' => getenv('APP_SECRET') ?: 'change-me',
'encryption_key' => getenv('ENCRYPTION_KEY') ?: '',
],
'ssh' => [
'connect_timeout' => (int)(getenv('SSH_CONNECT_TIMEOUT_SECONDS') ?: 5),
'auth_timeout' => (int)(getenv('SSH_AUTH_TIMEOUT_SECONDS') ?: 10),
'command_timeout' => (int)(getenv('SSH_COMMAND_TIMEOUT_SECONDS') ?: 8),
'total_scan_timeout' => (int)(getenv('SSH_TOTAL_SCAN_TIMEOUT_SECONDS') ?: 60),
'retry_count' => (int)(getenv('SSH_RETRY_COUNT') ?: 0),
],
],
'logger' => function () {
$logger = new \Monolog\Logger('domovoy');
$logger->pushHandler(new \Monolog\Handler\StreamHandler(
dirname(__DIR__) . '/storage/logs/app.log',
\Monolog\Level::Debug
));
return $logger;
},
PDO::class => function ($c) {
$db = $c->get('settings')['db'];
$dsn = sprintf(
'mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4',
$db['host'],
$db['port'],
$db['database']
);
return new PDO($dsn, $db['username'], $db['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
},
\Domovoy\Repositories\UserRepository::class => function ($c) {
return new \Domovoy\Repositories\UserRepository($c->get(PDO::class));
},
\Domovoy\Services\AuthService::class => function ($c) {
return new \Domovoy\Services\AuthService($c->get(\Domovoy\Repositories\UserRepository::class));
},
\Domovoy\Controllers\AuthController::class => function ($c) {
return new \Domovoy\Controllers\AuthController($c->get(\Domovoy\Services\AuthService::class));
},
\Domovoy\Controllers\SetupController::class => function ($c) {
return new \Domovoy\Controllers\SetupController($c->get(\Domovoy\Services\AuthService::class));
},
\Domovoy\Controllers\DashboardController::class => function () {
return new \Domovoy\Controllers\DashboardController();
},
]);
$container = $containerBuilder->build();
// Build Slim app
AppFactory::setContainer($container);
$app = AppFactory::create();
$app->addRoutingMiddleware();
$app->addBodyParsingMiddleware();
$app->add(new \Domovoy\Middleware\AuthMiddleware());
$app->addErrorMiddleware(true, true, true);
// Routes
$app->get('/login', [\Domovoy\Controllers\AuthController::class, 'loginForm'])->setName('login');
$app->post('/login', [\Domovoy\Controllers\AuthController::class, 'login'])->setName('login.post');
$app->get('/setup', [\Domovoy\Controllers\SetupController::class, 'form'])->setName('setup');
$app->post('/setup', [\Domovoy\Controllers\SetupController::class, 'create'])->setName('setup.post');
$app->group('', function (\Slim\Routing\RouteCollectorProxy $group) {
$group->get('/dashboard', [\Domovoy\Controllers\DashboardController::class, 'index'])->setName('dashboard');
$group->post('/logout', [\Domovoy\Controllers\AuthController::class, 'logout'])->setName('logout');
});
$app->run();