frankenphp
This commit is contained in:
6
Caddyfile
Normal file
6
Caddyfile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
:80 {
|
||||||
|
root * /var/www/public
|
||||||
|
php_server {
|
||||||
|
index index.php
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Dockerfile
10
Dockerfile
@@ -1,7 +1,9 @@
|
|||||||
FROM php:8.5-cli-alpine
|
FROM dunglas/frankenphp:php8.5-alpine
|
||||||
RUN apk add --no-cache libzip-dev zip \
|
|
||||||
&& docker-php-ext-install zip
|
RUN install-php-extensions zip
|
||||||
|
|
||||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
WORKDIR /var/www
|
WORKDIR /var/www
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
CMD ["php", "-S", "0.0.0.0:8001", "-t", "public"]
|
|
||||||
@@ -4,11 +4,24 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: cloud
|
container_name: cloud
|
||||||
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8001:8001"
|
- "127.0.0.1:8001:80"
|
||||||
volumes:
|
volumes:
|
||||||
- .:/var/www
|
- .:/var/www
|
||||||
- ./db:/var/db
|
- ./db:/var/db
|
||||||
- ./php/php.ini:/usr/local/etc/php/conf.d/uploads.ini
|
- ./php/php.ini:/usr/local/etc/php/conf.d/uploads.ini
|
||||||
|
- ./Caddyfile:/etc/frankenphp/Caddyfile
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
tty: false
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "curl", "-kf", "https://localhost/" ]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
volumes:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
@@ -15,8 +15,6 @@ use Din9xtrCloud\Router;
|
|||||||
use Din9xtrCloud\Storage\Drivers\LocalStorageDriver;
|
use Din9xtrCloud\Storage\Drivers\LocalStorageDriver;
|
||||||
use Din9xtrCloud\Storage\Drivers\StorageDriverInterface;
|
use Din9xtrCloud\Storage\Drivers\StorageDriverInterface;
|
||||||
use Din9xtrCloud\Storage\UserStorageInitializer;
|
use Din9xtrCloud\Storage\UserStorageInitializer;
|
||||||
use Din9xtrCloud\ViewModels\BaseViewModel;
|
|
||||||
use Din9xtrCloud\ViewModels\LayoutConfig;
|
|
||||||
use FastRoute\RouteCollector;
|
use FastRoute\RouteCollector;
|
||||||
use Monolog\Handler\StreamHandler;
|
use Monolog\Handler\StreamHandler;
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
@@ -138,6 +136,8 @@ try {
|
|||||||
$container->beginRequest();
|
$container->beginRequest();
|
||||||
$app->dispatch();
|
$app->dispatch();
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
$container->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'Internal Server Error';
|
echo 'Internal Server Error';
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ $page = $viewModel->page;
|
|||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||||
<link rel="manifest" href="/site.webmanifest">
|
<link rel="manifest" href="/site.webmanifest">
|
||||||
<link rel="stylesheet" href=/assets/cloud.css>
|
<link rel="stylesheet" href="/assets/cloud.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ final readonly class AuthController
|
|||||||
$username = (string)($data['username'] ?? '');
|
$username = (string)($data['username'] ?? '');
|
||||||
$password = (string)($data['password'] ?? '');
|
$password = (string)($data['password'] ?? '');
|
||||||
|
|
||||||
$ip = $request->getServerParams()['REMOTE_ADDR'] ?? null;
|
$ip = getClientIp();
|
||||||
$ua = $request->getHeaderLine('User-Agent') ?: null;
|
$ua = $request->getHeaderLine('User-Agent') ?: null;
|
||||||
|
|
||||||
$this->logger->info('Login submitted', [
|
$this->logger->info('Login submitted', [
|
||||||
|
|||||||
@@ -21,3 +21,76 @@ if (!function_exists('getStoragePercent')) {
|
|||||||
return $totalBytes > 0 ? ($categoryBytes / $totalBytes * 100) : 0;
|
return $totalBytes > 0 ? ($categoryBytes / $totalBytes * 100) : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!function_exists('getClientIp')) {
|
||||||
|
function getClientIp(): string
|
||||||
|
{
|
||||||
|
$server = $_SERVER;
|
||||||
|
|
||||||
|
$candidates = [
|
||||||
|
$server['HTTP_CF_CONNECTING_IP'] ?? null,
|
||||||
|
|
||||||
|
extractForwardedIp($server['HTTP_FORWARDED'] ?? null),
|
||||||
|
extractForwardedIp($server['HTTP_X_FORWARDED_FOR'] ?? null),
|
||||||
|
extractForwardedIp($server['HTTP_FORWARDED_FOR'] ?? null),
|
||||||
|
|
||||||
|
$server['HTTP_X_REAL_IP'] ?? null,
|
||||||
|
$server['HTTP_CLIENT_IP'] ?? null,
|
||||||
|
$server['REMOTE_ADDR'] ?? null,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($candidates as $ip) {
|
||||||
|
if ($ip === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valid = validateIp($ip)) {
|
||||||
|
return $valid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('extractForwardedIp')) {
|
||||||
|
function extractForwardedIp(?string $value): ?string
|
||||||
|
{
|
||||||
|
if (!$value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_contains($value, 'for=')) {
|
||||||
|
if (preg_match('/for="?([^";, ]+)/i', $value, $m)) {
|
||||||
|
return $m[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = explode(',', $value);
|
||||||
|
|
||||||
|
return trim($parts[0]) ?: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('validateIp')) {
|
||||||
|
function validateIp(string $ip): ?string
|
||||||
|
{
|
||||||
|
$ip = trim($ip);
|
||||||
|
|
||||||
|
if ($ip === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
str_contains($ip, ':') &&
|
||||||
|
!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
|
||||||
|
) {
|
||||||
|
$ip = explode(':', $ip, 2)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$flags = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
|
||||||
|
|
||||||
|
return filter_var($ip, FILTER_VALIDATE_IP, $flags)
|
||||||
|
? $ip
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use Psr\Http\Message\ResponseInterface;
|
|||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Server\MiddlewareInterface;
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
use Psr\Http\Server\RequestHandlerInterface;
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
final class AuthMiddleware implements MiddlewareInterface
|
final class AuthMiddleware implements MiddlewareInterface
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,7 @@ final class AuthMiddleware implements MiddlewareInterface
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly SessionRepository $sessions,
|
private readonly SessionRepository $sessions,
|
||||||
private readonly UserRepository $users,
|
private readonly UserRepository $users,
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -35,6 +37,8 @@ final class AuthMiddleware implements MiddlewareInterface
|
|||||||
RequestHandlerInterface $handler
|
RequestHandlerInterface $handler
|
||||||
): ResponseInterface
|
): ResponseInterface
|
||||||
{
|
{
|
||||||
|
$this->logger->debug('getClientIp: ' . getClientIp());
|
||||||
|
|
||||||
$path = $request->getUri()->getPath();
|
$path = $request->getUri()->getPath();
|
||||||
|
|
||||||
$token = $request->getCookieParams()['auth_token'] ?? null;
|
$token = $request->getCookieParams()['auth_token'] ?? null;
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ final class ThrottleMiddleware implements MiddlewareInterface
|
|||||||
|
|
||||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
{
|
{
|
||||||
$ip = $request->getHeaderLine('X-Forwarded-For') ?: $request->getServerParams()['REMOTE_ADDR'] ?? 'unknown';
|
$ip = getClientIp();
|
||||||
$ip = explode(',', $ip)[0];
|
|
||||||
|
|
||||||
$stmt = $this->db->prepare("SELECT * FROM login_throttle WHERE ip = :ip ORDER BY id DESC LIMIT 1");
|
$stmt = $this->db->prepare("SELECT * FROM login_throttle WHERE ip = :ip ORDER BY id DESC LIMIT 1");
|
||||||
$stmt->execute(['ip' => $ip]);
|
$stmt->execute(['ip' => $ip]);
|
||||||
|
|||||||
Reference in New Issue
Block a user