Compare commits
3 Commits
702803d982
...
thehe
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d3d15e8e5 | |||
| cfa72be700 | |||
| 0ee598fbdd |
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
+15
-4
@@ -1,11 +1,22 @@
|
||||
FROM node:22-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY assets/ ./assets/
|
||||
COPY templates/ ./templates/
|
||||
|
||||
# JS bauen
|
||||
RUN ./node_modules/.bin/esbuild ./assets/javascript/test.js --bundle --outfile=./public/build/app.js --minify
|
||||
|
||||
# CSS bauen
|
||||
RUN ./node_modules/.bin/tailwindcss -i ./assets/css/app.css -o ./public/build/app.css --minify
|
||||
|
||||
FROM php:8.3-apache
|
||||
|
||||
RUN a2enmod rewrite
|
||||
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
COPY . /var/www/html
|
||||
|
||||
COPY --from=builder /app/public/build /var/www/html/public/build
|
||||
RUN sed -i 's|/var/www/html|/var/www/html/public|g' \
|
||||
/etc/apache2/sites-available/000-default.conf
|
||||
|
||||
RUN sed -i 's|AllowOverride None|AllowOverride All|g' \
|
||||
/etc/apache2/apache2.conf
|
||||
@@ -0,0 +1 @@
|
||||
@import "tailwindcss";
|
||||
@@ -0,0 +1,11 @@
|
||||
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7', '#dda0dd'];
|
||||
let current = 0;
|
||||
|
||||
setInterval(() => {
|
||||
current = (current + 1) % colors.length;
|
||||
document.body.style.backgroundColor = colors[current];
|
||||
}, 1000);
|
||||
|
||||
window.test = function(world) {
|
||||
console.log('Hello, ' + world + '!');
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: pawra-app
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: 3306
|
||||
DB_NAME: pawra
|
||||
DB_USER: pawra
|
||||
DB_PASS: pawra_pw
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
db:
|
||||
image: mysql:8.4
|
||||
container_name: pawra-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root_pw
|
||||
MYSQL_DATABASE: pawra
|
||||
MYSQL_USER: pawra
|
||||
MYSQL_PASSWORD: pawra_pw
|
||||
ports:
|
||||
- "3306:3306"
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
- ./migrations/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -proot_pw || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
use src\Controller\HomeController;
|
||||
|
||||
$router->get('/', [new HomeController(), 'index']);
|
||||
$router->get('/', [HomeController::class, 'index']);
|
||||
@@ -0,0 +1,23 @@
|
||||
CREATE DATABASE IF NOT EXISTS auth_system
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE auth_system;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(30) NOT NULL UNIQUE,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login DATETIME NULL,
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS remember_tokens (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
token_hash VARCHAR(255) NOT NULL,
|
||||
expires_at DATETIME NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
Generated
+1540
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "pawra",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@git.pawly.dev:cheeseburger/pawra.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"devDependencies": {
|
||||
"@tailwindcss/cli": "^4.2.4",
|
||||
"esbuild": "0.28.0",
|
||||
"tailwindcss": "^4.2.4"
|
||||
}
|
||||
}
|
||||
+21
-3
@@ -1,13 +1,31 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../src/Autoloader.php';
|
||||
|
||||
use src\Router;
|
||||
|
||||
$autoloader = new Autoloader();
|
||||
$autoloader->addNamespace('src', __DIR__ . '/../src');
|
||||
$autoloader->register();
|
||||
|
||||
$router = new Router();
|
||||
use src\Container;
|
||||
use src\Router;
|
||||
use src\Middleware\TestMiddleware;
|
||||
use src\Database;
|
||||
|
||||
$container = new Container();
|
||||
|
||||
$container->bind(Database::class, function() {
|
||||
return new Database(
|
||||
$_ENV['DB_HOST'] ?? '127.0.0.1',
|
||||
$_ENV['DB_PORT'] ?? '3306',
|
||||
$_ENV['DB_NAME'] ?? 'pawra',
|
||||
$_ENV['DB_USER'] ?? 'root',
|
||||
$_ENV['DB_PASS'] ?? ''
|
||||
);
|
||||
});
|
||||
|
||||
$router = new Router($container);
|
||||
|
||||
$router->addGlobalMiddleware(TestMiddleware::class);
|
||||
|
||||
require_once __DIR__ . '/../config/routes.php';
|
||||
|
||||
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace src;
|
||||
|
||||
class Container
|
||||
{
|
||||
private array $bindings = [];
|
||||
|
||||
public function bind(string $abstract, callable $factory): void
|
||||
{
|
||||
$this->bindings[$abstract] = $factory;
|
||||
}
|
||||
|
||||
public function make(string $class): object
|
||||
{
|
||||
if (isset($this->bindings[$class])) {
|
||||
return ($this->bindings[$class])($this);
|
||||
}
|
||||
|
||||
return $this->resolve($class);
|
||||
}
|
||||
|
||||
private function resolve(string $class): object
|
||||
{
|
||||
$reflector = new \ReflectionClass($class);
|
||||
$constructor = $reflector->getConstructor();
|
||||
|
||||
if ($constructor === null) {
|
||||
return new $class();
|
||||
}
|
||||
|
||||
$dependencies = array_map(function($param) {
|
||||
$type = $param->getType();
|
||||
|
||||
if ($type === null) {
|
||||
throw new \Exception("Cannot resolve parameter \${$param->getName()} — no type hint.");
|
||||
}
|
||||
|
||||
if ($type->isBuiltin()) {
|
||||
if ($param->isDefaultValueAvailable()) {
|
||||
return $param->getDefaultValue();
|
||||
}
|
||||
throw new \Exception("Cannot resolve primitive parameter \${$param->getName()} — no default value.");
|
||||
}
|
||||
|
||||
return $this->make($type->getName());
|
||||
}, $constructor->getParameters());
|
||||
|
||||
return $reflector->newInstanceArgs($dependencies);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,23 @@
|
||||
<?php
|
||||
namespace src\Controller;
|
||||
|
||||
use src\Database;
|
||||
|
||||
class HomeController extends BaseController
|
||||
{
|
||||
public function __construct(private Database $db)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function index(): void
|
||||
{
|
||||
$stmt = $this->db->pdo()->query('SELECT NOW() as now');
|
||||
$row = $stmt->fetch();
|
||||
|
||||
$this->render('home', [
|
||||
'title' => 'meow :3'
|
||||
'title' => 'meow :3',
|
||||
'now' => $row['now'] ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace src;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
class Database
|
||||
{
|
||||
private PDO $pdo;
|
||||
|
||||
public function __construct(
|
||||
string $host,
|
||||
string $port,
|
||||
string $name,
|
||||
string $user,
|
||||
string $password
|
||||
) {
|
||||
$dsn = "mysql:host={$host};port={$port};dbname={$name};charset=utf8mb4";
|
||||
|
||||
$this->pdo = new PDO($dsn, $user, $password, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
}
|
||||
|
||||
public function pdo(): PDO
|
||||
{
|
||||
return $this->pdo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace src\Middleware;
|
||||
|
||||
interface MiddlewareInterface
|
||||
{
|
||||
public function handle(callable $next): void;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace src\Middleware;
|
||||
|
||||
class TestMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function handle(callable $next): void
|
||||
{
|
||||
error_log('TestMiddleware: Handling request...');
|
||||
$next();
|
||||
}
|
||||
}
|
||||
+49
-9
@@ -4,28 +4,68 @@ namespace src;
|
||||
class Router
|
||||
{
|
||||
private array $routes = [];
|
||||
private array $globalMiddleware = [];
|
||||
private Container $container;
|
||||
|
||||
public function get(string $path, callable $handler): void
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->routes['GET'][$path] = $handler;
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function post(string $path, callable $handler): void
|
||||
public function get(string $path, array $handler, array $middleware = []): void
|
||||
{
|
||||
$this->routes['POST'][$path] = $handler;
|
||||
$this->routes['GET'][$path] = compact('handler', 'middleware');
|
||||
}
|
||||
|
||||
public function post(string $path, array $handler, array $middleware = []): void
|
||||
{
|
||||
$this->routes['POST'][$path] = compact('handler', 'middleware');
|
||||
}
|
||||
|
||||
public function addGlobalMiddleware(string $middleware): void
|
||||
{
|
||||
$this->globalMiddleware[] = $middleware;
|
||||
}
|
||||
|
||||
public function dispatch(string $method, string $uri): void
|
||||
{
|
||||
$path = parse_url($uri, PHP_URL_PATH) ?? '/';
|
||||
$handler = $this->routes[$method][$path] ?? null;
|
||||
$route = $this->routes[$method][$uri] ?? null;
|
||||
|
||||
if ($handler === null) {
|
||||
if ($route === null) {
|
||||
http_response_code(404);
|
||||
echo '404 Not Found';
|
||||
require __DIR__ . '/../views/404.php';
|
||||
return;
|
||||
}
|
||||
|
||||
call_user_func($handler);
|
||||
[$controllerClass, $action] = $route['handler'];
|
||||
|
||||
$final = function() use ($controllerClass, $action) {
|
||||
$controller = $this->container->make($controllerClass);
|
||||
$controller->$action();
|
||||
};
|
||||
|
||||
$middleware = array_merge($this->globalMiddleware, $route['middleware']);
|
||||
$this->run($middleware, $final);
|
||||
}
|
||||
|
||||
private function run(array $middleware, callable $final): void
|
||||
{
|
||||
if (empty($middleware)) {
|
||||
$final();
|
||||
return;
|
||||
}
|
||||
|
||||
$chain = array_reduce(
|
||||
array_reverse($middleware),
|
||||
function(callable $carry, string $middlewareClass) {
|
||||
return function() use ($carry, $middlewareClass) {
|
||||
$instance = $this->container->make($middlewareClass);
|
||||
$instance->handle($carry);
|
||||
};
|
||||
},
|
||||
$final
|
||||
);
|
||||
|
||||
$chain();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,12 @@ ob_start();
|
||||
<h1><?= htmlspecialchars($title) ?></h1>
|
||||
<p>Willkommen auf meiner Seite!</p>
|
||||
|
||||
<p class="text-red-500">I am red.</p>
|
||||
|
||||
<button onclick="test('World')">Test</button>
|
||||
|
||||
<p><?= htmlspecialchars($now) ?></p>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
require __DIR__ . '/layout.php';
|
||||
@@ -3,6 +3,8 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?= htmlspecialchars($title) ?></title>
|
||||
<link rel="stylesheet" href="/build/app.css">
|
||||
<script src="/build/app.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<?= $content ?>
|
||||
|
||||
Reference in New Issue
Block a user