create-berna-stencil 1.0.8 → 1.0.9

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.
@@ -0,0 +1,16 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ require_once __DIR__ . '/../../modules/Response.php';
6
+
7
+ if ($method !== 'GET') {
8
+ Response::error('Method not allowed', 405);
9
+ }
10
+
11
+ Response::success([
12
+ 'message' => 'Protected endpoint is working',
13
+ 'endpoint' => 'secret',
14
+ 'visibility' => 'protected',
15
+ 'params' => $requestParams,
16
+ ]);
@@ -0,0 +1,75 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ use PHPMailer\PHPMailer\PHPMailer;
6
+ use PHPMailer\PHPMailer\Exception;
7
+
8
+ /**
9
+ * NOTA: Non serve require 'vendor/autoload.php' o 'init.php'
10
+ * perché questo file viene incluso da index.php che ha già caricato tutto.
11
+ */
12
+
13
+ // 1. Controllo Metodo (Vogliamo solo POST)
14
+ if ($method !== 'POST') {
15
+ Response::error('Method not allowed', 405);
16
+ }
17
+
18
+ // 2. Funzioni di Sanitizzazione (Locali o spostabili in un helper)
19
+ $clean = fn($v) => htmlspecialchars(trim((string)($v ?? '')), ENT_QUOTES, 'UTF-8');
20
+ $safeNum = fn($v) => filter_var($v ?? '', FILTER_SANITIZE_NUMBER_INT);
21
+
22
+ // 3. Recupero Dati (supporta sia $_POST standard che JSON)
23
+ $input = $_POST;
24
+ if (empty($input)) {
25
+ $input = json_decode(file_get_contents('php://input'), true) ?? [];
26
+ }
27
+
28
+ $formType = $clean($input['formType'] ?? 'Contatto Generico');
29
+ $name = $clean($input['name'] ?? '');
30
+ $phoneNumber = $safeNum($input['phoneNumber'] ?? '');
31
+
32
+ // Validazione minima
33
+ if (empty($name)) {
34
+ Response::error('Il campo nome è obbligatorio');
35
+ }
36
+
37
+ // 4. Configurazione PHPMailer
38
+ $mail = new PHPMailer(true);
39
+
40
+ try {
41
+ // Usiamo le variabili d'ambiente caricate da init.php
42
+ $mail->isSMTP();
43
+ $mail->Host = $_ENV['MAIL_HOST'];
44
+ $mail->SMTPAuth = true;
45
+ $mail->Username = $_ENV['MAIL_USERNAME'];
46
+ $mail->Password = $_ENV['MAIL_PASSWORD'];
47
+ $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
48
+ $mail->Port = (int)$_ENV['MAIL_PORT'];
49
+ $mail->CharSet = 'UTF-8';
50
+
51
+ $mail->setFrom($_ENV['MAIL_USERNAME'], $_ENV['MAIL_FROM_NAME'] ?? 'API Robot');
52
+ $mail->addAddress($_ENV['MAIL_TO_ADDRESS'], $_ENV['MAIL_TO_NAME'] ?? 'Admin');
53
+
54
+ $mail->isHTML(true);
55
+ $mail->Subject = "Nuovo invio modulo: {$formType}";
56
+
57
+ // Costruzione Body
58
+ $htmlBody = "<h2>Dettagli Richiesta</h2>";
59
+ $htmlBody .= "<p><strong>Nome:</strong> {$name}</p>";
60
+ if (!empty($phoneNumber)) {
61
+ $htmlBody .= "<p><strong>Telefono:</strong> {$phoneNumber}</p>";
62
+ }
63
+
64
+ $mail->Body = $htmlBody;
65
+ $mail->AltBody = strip_tags(str_replace(['<br>', '</p>'], ["\n", "\n\n"], $htmlBody));
66
+
67
+ $mail->send();
68
+
69
+ // Risposta JSON di successo
70
+ Response::success(['message' => 'Email inviata con successo']);
71
+
72
+ } catch (Exception $e) {
73
+ // Risposta JSON di errore
74
+ Response::error("Errore nell'invio della mail: {$mail->ErrorInfo}", 500);
75
+ }
@@ -0,0 +1,16 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ require_once __DIR__ . '/../../modules/Response.php';
6
+
7
+ if ($method !== 'GET') {
8
+ Response::error('Method not allowed', 405);
9
+ }
10
+
11
+ Response::success([
12
+ 'message' => 'Public endpoint is working',
13
+ 'endpoint' => 'ping',
14
+ 'visibility' => 'public',
15
+ 'params' => $requestParams,
16
+ ]);
@@ -0,0 +1,95 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ /**
6
+ * Caricamento delle dipendenze e configurazione iniziale.
7
+ */
8
+ require_once __DIR__ . '/init.php';
9
+ require_once __DIR__ . '/modules/Response.php';
10
+
11
+ // =====================================================
12
+ // 1. ANALISI DELLA RICHIESTA (REQUEST PARSING)
13
+ // =====================================================
14
+
15
+ $method = $_SERVER['REQUEST_METHOD'];
16
+ $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
17
+
18
+ // Pulizia URI: rimuoviamo /api e eventuali slash finali
19
+ $uri = rtrim(preg_replace('#^/api#', '', $uri), '/') ?: '/';
20
+ $parts = array_values(array_filter(explode('/', $uri)));
21
+
22
+ $resource = $parts[0] ?? null;
23
+
24
+ // =====================================================
25
+ // 2. RISOLUZIONE ENDPOINT (ROUTING)
26
+ // =====================================================
27
+
28
+ $publicPath = __DIR__ . '/endpoints/public/' . $resource . '.php';
29
+ $protectedPath = __DIR__ . '/endpoints/protected/' . $resource . '.php';
30
+
31
+ $isPublic = $resource !== null && file_exists($publicPath);
32
+ $isProtected = $resource !== null && file_exists($protectedPath);
33
+
34
+ /**
35
+ * SE L'ENDPOINT NON ESISTE (RITORNO HTML 404)
36
+ */
37
+ if (!$isPublic && !$isProtected) {
38
+ http_response_code(404);
39
+
40
+ // Cerchiamo il file 404.html generato da Eleventy nella root di Laragon
41
+ $errorPage = $_SERVER['DOCUMENT_ROOT'] . '/404.html';
42
+
43
+ if (file_exists($errorPage)) {
44
+ header('Content-Type: text/html; charset=UTF-8');
45
+ echo file_get_contents($errorPage);
46
+ } else {
47
+ echo "<h1>404 Not Found</h1>";
48
+ echo "The requested URL was not found on this server.";
49
+ }
50
+ exit;
51
+ }
52
+
53
+ // =====================================================
54
+ // 3. HEADERS E CORS (Solo se l'endpoint esiste)
55
+ // =====================================================
56
+
57
+ header('Content-Type: application/json; charset=UTF-8');
58
+ header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS');
59
+ header('Access-Control-Allow-Headers: Content-Type, X-Api-Key');
60
+
61
+ $allowedOrigins = array_filter(array_map('trim', explode(',', $_ENV['CORS_ALLOWED_ORIGINS'] ?? '')));
62
+ $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
63
+
64
+ if (in_array($origin, $allowedOrigins, true) || in_array('*', $allowedOrigins, true)) {
65
+ header("Access-Control-Allow-Origin: $origin");
66
+ } else {
67
+ header("Access-Control-Allow-Origin: " . ($allowedOrigins[0] ?? ''));
68
+ }
69
+
70
+ if ($method === 'OPTIONS') {
71
+ http_response_code(204);
72
+ exit;
73
+ }
74
+
75
+ // =====================================================
76
+ // 4. GUARDIA DI AUTENTICAZIONE
77
+ // =====================================================
78
+
79
+ if ($isProtected) {
80
+ $apiKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
81
+ $validKey = $_ENV['API_KEY'] ?? '';
82
+
83
+ if ($validKey === '' || $apiKey !== $validKey) {
84
+ Response::error('Unauthorized', 401);
85
+ }
86
+ }
87
+
88
+ // =====================================================
89
+ // 5. ESECUZIONE (DISPATCH)
90
+ // =====================================================
91
+
92
+ $requestParams = array_slice($parts, 1);
93
+
94
+ // Carica il file dell'endpoint richiesto
95
+ require $isProtected ? $protectedPath : $publicPath;
@@ -0,0 +1,43 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ require_once __DIR__ . '/vendor/autoload.php';
6
+ require_once __DIR__ . '/modules/Response.php';
7
+
8
+ // --- GESTORE GLOBALE ERRORI E ECCEZIONI ---
9
+ // Trasforma ogni errore PHP in una risposta JSON pulita
10
+ set_exception_handler(function ($exception) {
11
+ Response::error(
12
+ $exception->getMessage(),
13
+ 500,
14
+ ['file' => $exception->getFile(), 'line' => $exception->getLine()]
15
+ );
16
+ });
17
+
18
+ set_error_handler(function ($severity, $message, $file, $line) {
19
+ if (!(error_reporting() & $severity)) return;
20
+ throw new ErrorException($message, 0, $severity, $file, $line);
21
+ });
22
+
23
+ // --- CARICAMENTO DOTENV ---
24
+ // dirname(__DIR__, 1) sale di un livello (da api/ a Berna-Stencil-out/)
25
+ try {
26
+ $dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__, 1));
27
+ $dotenv->load();
28
+ } catch (Exception $e) {
29
+ Response::error("Impossibile caricare il file .env. Assicurati che esista nella root e si chiami esattamente .env", 500);
30
+ }
31
+
32
+ $dotenv->required([
33
+ 'API_KEY',
34
+ 'CORS_ALLOWED_ORIGINS',
35
+ ]);
36
+
37
+ if (($_ENV['APP_ENV'] ?? 'production') === 'production') {
38
+ ini_set('display_errors', '0');
39
+ error_reporting(0);
40
+ } else {
41
+ ini_set('display_errors', '1');
42
+ error_reporting(E_ALL);
43
+ }
@@ -0,0 +1,37 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ class Response
6
+ {
7
+ public static function success(mixed $data = null, int $code = 200): never
8
+ {
9
+ http_response_code($code);
10
+ echo json_encode([
11
+ 'status' => 'success',
12
+ 'data' => $data,
13
+ ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
14
+ exit;
15
+ }
16
+
17
+ public static function error(string $message, int $code = 400, mixed $details = null): never
18
+ {
19
+ http_response_code($code);
20
+ $body = [
21
+ 'status' => 'error',
22
+ 'message' => $message,
23
+ 'code' => $code,
24
+ ];
25
+ if ($details !== null) {
26
+ $body['details'] = $details;
27
+ }
28
+ echo json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
29
+ exit;
30
+ }
31
+
32
+ public static function noContent(): never
33
+ {
34
+ http_response_code(204);
35
+ exit;
36
+ }
37
+ }
@@ -0,0 +1,22 @@
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ if (PHP_VERSION_ID < 50600) {
6
+ if (!headers_sent()) {
7
+ header('HTTP/1.1 500 Internal Server Error');
8
+ }
9
+ $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
10
+ if (!ini_get('display_errors')) {
11
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
12
+ fwrite(STDERR, $err);
13
+ } elseif (!headers_sent()) {
14
+ echo $err;
15
+ }
16
+ }
17
+ throw new RuntimeException($err);
18
+ }
19
+
20
+ require_once __DIR__ . '/composer/autoload_real.php';
21
+
22
+ return ComposerAutoloaderInite875ae8441d070d7dda5f4b47a2117aa::getLoader();