path correction
This commit is contained in:
Vendored
+8
-11
@@ -1,25 +1,22 @@
|
|||||||
# Deployed under /citpl_website/ on Apache.
|
# Deployed under /citpl_website/ — Apache must proxy API to the Node server.
|
||||||
# Requires: mod_rewrite, mod_headers, PHP curl
|
# Requires: mod_rewrite, mod_proxy, mod_proxy_http, mod_headers
|
||||||
# API: PHP proxy to Node on 127.0.0.1:3001 (start with: npm run server / pm2)
|
# Start Node on the host: npm run server (or pm2 start server/index.js)
|
||||||
|
|
||||||
<IfModule mod_rewrite.c>
|
<IfModule mod_rewrite.c>
|
||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
RewriteBase /citpl_website/
|
RewriteBase /citpl_website/
|
||||||
|
|
||||||
# API -> PHP proxy (works without mod_proxy)
|
# Proxy API + uploads to Express (Node) on port 3001
|
||||||
RewriteRule ^api(?:/.*)?$ api/index.php [QSA,L]
|
RewriteRule ^api/(.*)$ http://127.0.0.1:3001/api/$1 [P,L]
|
||||||
|
RewriteRule ^uploads/(.*)$ http://127.0.0.1:3001/uploads/$1 [P,L]
|
||||||
# Uploads -> PHP proxy
|
|
||||||
RewriteRule ^uploads/(.*)$ uploads-proxy.php?file=$1 [QSA,L]
|
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
# Host security headers use frame-ancestors 'none' + X-Frame-Options DENY.
|
# Allow admin login preview iframe (same origin)
|
||||||
# Edit them so admin can embed preview.html (same origin).
|
|
||||||
<IfModule mod_headers.c>
|
<IfModule mod_headers.c>
|
||||||
Header always edit Content-Security-Policy "frame-ancestors 'none'" "frame-ancestors 'self'"
|
Header always edit Content-Security-Policy "frame-ancestors 'none'" "frame-ancestors 'self'"
|
||||||
Header always edit X-Frame-Options "DENY" "SAMEORIGIN"
|
Header always edit X-Frame-Options "DENY" "SAMEORIGIN"
|
||||||
|
|
||||||
<FilesMatch "^(preview\.html|preview\.php|index\.html)$">
|
<FilesMatch "^(preview\.html|index\.html)$">
|
||||||
Header unset X-Frame-Options
|
Header unset X-Frame-Options
|
||||||
Header always unset X-Frame-Options
|
Header always unset X-Frame-Options
|
||||||
Header unset Content-Security-Policy
|
Header unset Content-Security-Policy
|
||||||
|
|||||||
Vendored
-119
@@ -1,119 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Forward /citpl_website/api/* → Node (Express) without requiring Apache mod_proxy.
|
|
||||||
* Start the API on the server: cd /path/to/app && npm run server
|
|
||||||
* Or: pm2 start server/index.js --name citpl-api
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
$apiOrigin = getenv('CITPL_API_ORIGIN') ?: 'http://127.0.0.1:3001';
|
|
||||||
|
|
||||||
$requestUri = $_SERVER['REQUEST_URI'] ?? '/api';
|
|
||||||
$path = parse_url($requestUri, PHP_URL_PATH) ?: '/api';
|
|
||||||
|
|
||||||
// Keep everything from "/api" onward (works under /citpl_website/api/...)
|
|
||||||
if (preg_match('#(/api(?:/.*)?)$#', $path, $m)) {
|
|
||||||
$forwardPath = $m[1];
|
|
||||||
} else {
|
|
||||||
$forwardPath = '/api';
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = $_SERVER['QUERY_STRING'] ?? '';
|
|
||||||
$url = rtrim($apiOrigin, '/') . $forwardPath . ($query !== '' ? '?' . $query : '');
|
|
||||||
|
|
||||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
|
||||||
$body = file_get_contents('php://input');
|
|
||||||
if ($body === false) {
|
|
||||||
$body = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$headers = [];
|
|
||||||
if (function_exists('getallheaders')) {
|
|
||||||
foreach (getallheaders() as $name => $value) {
|
|
||||||
$lower = strtolower((string) $name);
|
|
||||||
if ($lower === 'host' || $lower === 'content-length') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$headers[] = $name . ': ' . $value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foreach ($_SERVER as $key => $value) {
|
|
||||||
if (strpos($key, 'HTTP_') !== 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
|
|
||||||
if (strtolower($name) === 'host') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$headers[] = $name . ': ' . $value;
|
|
||||||
}
|
|
||||||
if (!empty($_SERVER['CONTENT_TYPE'])) {
|
|
||||||
$headers[] = 'Content-Type: ' . $_SERVER['CONTENT_TYPE'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('curl_init')) {
|
|
||||||
http_response_code(500);
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode([
|
|
||||||
'error' => 'PHP curl extension is required for the API proxy',
|
|
||||||
'code' => 'PROXY_MISCONFIGURED',
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt_array($ch, [
|
|
||||||
CURLOPT_CUSTOMREQUEST => $method,
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_HEADER => true,
|
|
||||||
CURLOPT_FOLLOWLOCATION => false,
|
|
||||||
CURLOPT_CONNECTTIMEOUT => 5,
|
|
||||||
CURLOPT_TIMEOUT => 60,
|
|
||||||
CURLOPT_HTTPHEADER => $headers,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($method !== 'GET' && $method !== 'HEAD') {
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
if ($response === false) {
|
|
||||||
$err = curl_error($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
http_response_code(502);
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode([
|
|
||||||
'error' => 'API server unreachable. On the host run: npm run server (port 3001)',
|
|
||||||
'detail' => $err,
|
|
||||||
'code' => 'BAD_GATEWAY',
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$headerSize = (int) curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
$rawHeaders = substr($response, 0, $headerSize);
|
|
||||||
$responseBody = substr($response, $headerSize);
|
|
||||||
|
|
||||||
http_response_code($status > 0 ? $status : 502);
|
|
||||||
|
|
||||||
$skip = ['transfer-encoding', 'connection', 'keep-alive', 'content-length'];
|
|
||||||
foreach (explode("\r\n", $rawHeaders) as $line) {
|
|
||||||
if ($line === '' || stripos($line, 'HTTP/') === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$parts = explode(':', $line, 2);
|
|
||||||
if (count($parts) < 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = trim($parts[0]);
|
|
||||||
if (in_array(strtolower($name), $skip, true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
header($name . ':' . $parts[1], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $responseBody;
|
|
||||||
Vendored
-66
@@ -1,66 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Forward /citpl_website/uploads/* → Node static uploads (no mod_proxy required).
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
$apiOrigin = getenv('CITPL_API_ORIGIN') ?: 'http://127.0.0.1:3001';
|
|
||||||
$file = $_GET['file'] ?? '';
|
|
||||||
$file = str_replace(['..', '\\'], '', $file);
|
|
||||||
$file = ltrim($file, '/');
|
|
||||||
|
|
||||||
if ($file === '') {
|
|
||||||
http_response_code(400);
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode(['error' => 'Missing file', 'code' => 'VALIDATION']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$url = rtrim($apiOrigin, '/') . '/uploads/' . str_replace(' ', '%20', $file);
|
|
||||||
|
|
||||||
if (!function_exists('curl_init')) {
|
|
||||||
http_response_code(500);
|
|
||||||
exit('curl required');
|
|
||||||
}
|
|
||||||
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt_array($ch, [
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_HEADER => true,
|
|
||||||
CURLOPT_FOLLOWLOCATION => false,
|
|
||||||
CURLOPT_CONNECTTIMEOUT => 5,
|
|
||||||
CURLOPT_TIMEOUT => 60,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
if ($response === false) {
|
|
||||||
curl_close($ch);
|
|
||||||
http_response_code(502);
|
|
||||||
exit('Upload server unreachable');
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$headerSize = (int) curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
http_response_code($status > 0 ? $status : 502);
|
|
||||||
$rawHeaders = substr($response, 0, $headerSize);
|
|
||||||
$body = substr($response, $headerSize);
|
|
||||||
|
|
||||||
$skip = ['transfer-encoding', 'connection', 'keep-alive'];
|
|
||||||
foreach (explode("\r\n", $rawHeaders) as $line) {
|
|
||||||
if ($line === '' || stripos($line, 'HTTP/') === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$parts = explode(':', $line, 2);
|
|
||||||
if (count($parts) < 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = trim($parts[0]);
|
|
||||||
if (in_array(strtolower($name), $skip, true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
header($name . ':' . $parts[1], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $body;
|
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
"dev": "concurrently \"npm run server\" \"vite\"",
|
"dev": "concurrently \"npm run server\" \"vite\"",
|
||||||
"dev:client": "vite",
|
"dev:client": "vite",
|
||||||
"server": "node server/index.js",
|
"server": "node server/index.js",
|
||||||
|
"start": "node server/index.js",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
|
|||||||
+8
-11
@@ -1,25 +1,22 @@
|
|||||||
# Deployed under /citpl_website/ on Apache.
|
# Deployed under /citpl_website/ — Apache must proxy API to the Node server.
|
||||||
# Requires: mod_rewrite, mod_headers, PHP curl
|
# Requires: mod_rewrite, mod_proxy, mod_proxy_http, mod_headers
|
||||||
# API: PHP proxy to Node on 127.0.0.1:3001 (start with: npm run server / pm2)
|
# Start Node on the host: npm run server (or pm2 start server/index.js)
|
||||||
|
|
||||||
<IfModule mod_rewrite.c>
|
<IfModule mod_rewrite.c>
|
||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
RewriteBase /citpl_website/
|
RewriteBase /citpl_website/
|
||||||
|
|
||||||
# API -> PHP proxy (works without mod_proxy)
|
# Proxy API + uploads to Express (Node) on port 3001
|
||||||
RewriteRule ^api(?:/.*)?$ api/index.php [QSA,L]
|
RewriteRule ^api/(.*)$ http://127.0.0.1:3001/api/$1 [P,L]
|
||||||
|
RewriteRule ^uploads/(.*)$ http://127.0.0.1:3001/uploads/$1 [P,L]
|
||||||
# Uploads -> PHP proxy
|
|
||||||
RewriteRule ^uploads/(.*)$ uploads-proxy.php?file=$1 [QSA,L]
|
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
# Host security headers use frame-ancestors 'none' + X-Frame-Options DENY.
|
# Allow admin login preview iframe (same origin)
|
||||||
# Edit them so admin can embed preview.html (same origin).
|
|
||||||
<IfModule mod_headers.c>
|
<IfModule mod_headers.c>
|
||||||
Header always edit Content-Security-Policy "frame-ancestors 'none'" "frame-ancestors 'self'"
|
Header always edit Content-Security-Policy "frame-ancestors 'none'" "frame-ancestors 'self'"
|
||||||
Header always edit X-Frame-Options "DENY" "SAMEORIGIN"
|
Header always edit X-Frame-Options "DENY" "SAMEORIGIN"
|
||||||
|
|
||||||
<FilesMatch "^(preview\.html|preview\.php|index\.html)$">
|
<FilesMatch "^(preview\.html|index\.html)$">
|
||||||
Header unset X-Frame-Options
|
Header unset X-Frame-Options
|
||||||
Header always unset X-Frame-Options
|
Header always unset X-Frame-Options
|
||||||
Header unset Content-Security-Policy
|
Header unset Content-Security-Policy
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Forward /citpl_website/api/* → Node (Express) without requiring Apache mod_proxy.
|
|
||||||
* Start the API on the server: cd /path/to/app && npm run server
|
|
||||||
* Or: pm2 start server/index.js --name citpl-api
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
$apiOrigin = getenv('CITPL_API_ORIGIN') ?: 'http://127.0.0.1:3001';
|
|
||||||
|
|
||||||
$requestUri = $_SERVER['REQUEST_URI'] ?? '/api';
|
|
||||||
$path = parse_url($requestUri, PHP_URL_PATH) ?: '/api';
|
|
||||||
|
|
||||||
// Keep everything from "/api" onward (works under /citpl_website/api/...)
|
|
||||||
if (preg_match('#(/api(?:/.*)?)$#', $path, $m)) {
|
|
||||||
$forwardPath = $m[1];
|
|
||||||
} else {
|
|
||||||
$forwardPath = '/api';
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = $_SERVER['QUERY_STRING'] ?? '';
|
|
||||||
$url = rtrim($apiOrigin, '/') . $forwardPath . ($query !== '' ? '?' . $query : '');
|
|
||||||
|
|
||||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
|
||||||
$body = file_get_contents('php://input');
|
|
||||||
if ($body === false) {
|
|
||||||
$body = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$headers = [];
|
|
||||||
if (function_exists('getallheaders')) {
|
|
||||||
foreach (getallheaders() as $name => $value) {
|
|
||||||
$lower = strtolower((string) $name);
|
|
||||||
if ($lower === 'host' || $lower === 'content-length') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$headers[] = $name . ': ' . $value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foreach ($_SERVER as $key => $value) {
|
|
||||||
if (strpos($key, 'HTTP_') !== 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
|
|
||||||
if (strtolower($name) === 'host') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$headers[] = $name . ': ' . $value;
|
|
||||||
}
|
|
||||||
if (!empty($_SERVER['CONTENT_TYPE'])) {
|
|
||||||
$headers[] = 'Content-Type: ' . $_SERVER['CONTENT_TYPE'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('curl_init')) {
|
|
||||||
http_response_code(500);
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode([
|
|
||||||
'error' => 'PHP curl extension is required for the API proxy',
|
|
||||||
'code' => 'PROXY_MISCONFIGURED',
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt_array($ch, [
|
|
||||||
CURLOPT_CUSTOMREQUEST => $method,
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_HEADER => true,
|
|
||||||
CURLOPT_FOLLOWLOCATION => false,
|
|
||||||
CURLOPT_CONNECTTIMEOUT => 5,
|
|
||||||
CURLOPT_TIMEOUT => 60,
|
|
||||||
CURLOPT_HTTPHEADER => $headers,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($method !== 'GET' && $method !== 'HEAD') {
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
if ($response === false) {
|
|
||||||
$err = curl_error($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
http_response_code(502);
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode([
|
|
||||||
'error' => 'API server unreachable. On the host run: npm run server (port 3001)',
|
|
||||||
'detail' => $err,
|
|
||||||
'code' => 'BAD_GATEWAY',
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$headerSize = (int) curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
$rawHeaders = substr($response, 0, $headerSize);
|
|
||||||
$responseBody = substr($response, $headerSize);
|
|
||||||
|
|
||||||
http_response_code($status > 0 ? $status : 502);
|
|
||||||
|
|
||||||
$skip = ['transfer-encoding', 'connection', 'keep-alive', 'content-length'];
|
|
||||||
foreach (explode("\r\n", $rawHeaders) as $line) {
|
|
||||||
if ($line === '' || stripos($line, 'HTTP/') === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$parts = explode(':', $line, 2);
|
|
||||||
if (count($parts) < 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = trim($parts[0]);
|
|
||||||
if (in_array(strtolower($name), $skip, true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
header($name . ':' . $parts[1], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $responseBody;
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Forward /citpl_website/uploads/* → Node static uploads (no mod_proxy required).
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
$apiOrigin = getenv('CITPL_API_ORIGIN') ?: 'http://127.0.0.1:3001';
|
|
||||||
$file = $_GET['file'] ?? '';
|
|
||||||
$file = str_replace(['..', '\\'], '', $file);
|
|
||||||
$file = ltrim($file, '/');
|
|
||||||
|
|
||||||
if ($file === '') {
|
|
||||||
http_response_code(400);
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode(['error' => 'Missing file', 'code' => 'VALIDATION']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$url = rtrim($apiOrigin, '/') . '/uploads/' . str_replace(' ', '%20', $file);
|
|
||||||
|
|
||||||
if (!function_exists('curl_init')) {
|
|
||||||
http_response_code(500);
|
|
||||||
exit('curl required');
|
|
||||||
}
|
|
||||||
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt_array($ch, [
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_HEADER => true,
|
|
||||||
CURLOPT_FOLLOWLOCATION => false,
|
|
||||||
CURLOPT_CONNECTTIMEOUT => 5,
|
|
||||||
CURLOPT_TIMEOUT => 60,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
if ($response === false) {
|
|
||||||
curl_close($ch);
|
|
||||||
http_response_code(502);
|
|
||||||
exit('Upload server unreachable');
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$headerSize = (int) curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
http_response_code($status > 0 ? $status : 502);
|
|
||||||
$rawHeaders = substr($response, 0, $headerSize);
|
|
||||||
$body = substr($response, $headerSize);
|
|
||||||
|
|
||||||
$skip = ['transfer-encoding', 'connection', 'keep-alive'];
|
|
||||||
foreach (explode("\r\n", $rawHeaders) as $line) {
|
|
||||||
if ($line === '' || stripos($line, 'HTTP/') === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$parts = explode(':', $line, 2);
|
|
||||||
if (count($parts) < 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = trim($parts[0]);
|
|
||||||
if (in_array(strtolower($name), $skip, true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
header($name . ':' . $parts[1], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $body;
|
|
||||||
+7
-6
@@ -34,14 +34,15 @@ export function initDb() {
|
|||||||
console.log('Seeded content database from default-content.json');
|
console.log('Seeded content database from default-content.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(ADMIN_FILE)) {
|
// Keep login credentials in sync with .env (ADMIN_USERNAME / ADMIN_PASSWORD)
|
||||||
const password = process.env.ADMIN_PASSWORD || 'admin123';
|
const username = process.env.ADMIN_USERNAME || 'admin';
|
||||||
const hash = bcrypt.hashSync(password, 12);
|
const password = process.env.ADMIN_PASSWORD || 'admin123';
|
||||||
|
if (process.env.ADMIN_USERNAME || process.env.ADMIN_PASSWORD || !fs.existsSync(ADMIN_FILE)) {
|
||||||
writeJson(ADMIN_FILE, {
|
writeJson(ADMIN_FILE, {
|
||||||
username: process.env.ADMIN_USERNAME || 'admin',
|
username,
|
||||||
passwordHash: hash,
|
passwordHash: bcrypt.hashSync(password, 12),
|
||||||
});
|
});
|
||||||
console.log(`Admin user created (username: ${process.env.ADMIN_USERNAME || 'admin'})`);
|
console.log(`Admin credentials loaded from .env (username: ${username})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-14
@@ -2,6 +2,7 @@ import 'dotenv/config';
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { initDb } from './db.js';
|
import { initDb } from './db.js';
|
||||||
import authRoutes from './routes/auth.js';
|
import authRoutes from './routes/auth.js';
|
||||||
@@ -12,6 +13,13 @@ import multer from 'multer';
|
|||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const PORT = process.env.PORT || 3001;
|
const PORT = process.env.PORT || 3001;
|
||||||
const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, 'uploads');
|
const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, 'uploads');
|
||||||
|
// Public URL prefix on Apache (e.g. /citpl_website). Leave empty if site is at domain root.
|
||||||
|
const SITE_BASE = (process.env.SITE_BASE || '/citpl_website').replace(/\/$/, '');
|
||||||
|
const distDir = path.join(__dirname, '..', 'dist');
|
||||||
|
const serveStatic =
|
||||||
|
process.env.SERVE_STATIC === '1' ||
|
||||||
|
process.env.SERVE_STATIC === 'true' ||
|
||||||
|
(process.env.SERVE_STATIC !== '0' && fs.existsSync(distDir));
|
||||||
|
|
||||||
initDb();
|
initDb();
|
||||||
|
|
||||||
@@ -23,22 +31,30 @@ app.use(cors({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
app.use(express.json({ limit: '10mb' }));
|
app.use(express.json({ limit: '10mb' }));
|
||||||
app.use('/uploads', express.static(UPLOAD_DIR));
|
|
||||||
|
|
||||||
app.get('/api/health', (_req, res) => {
|
function mountApp(base) {
|
||||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
const prefix = base || '';
|
||||||
});
|
|
||||||
|
|
||||||
app.use('/api/auth', authRoutes);
|
app.use(`${prefix}/uploads`, express.static(UPLOAD_DIR));
|
||||||
app.use('/api/content', contentRoutes);
|
|
||||||
app.use('/api/upload', uploadRoutes);
|
|
||||||
|
|
||||||
// Optional: serve Vite dist from Node (set SERVE_STATIC=1). Useful when Apache
|
app.get(`${prefix}/api/health`, (_req, res) => {
|
||||||
// proxies the whole /citpl_website path to this process.
|
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||||
if (process.env.SERVE_STATIC === '1') {
|
});
|
||||||
const distDir = path.join(__dirname, '..', 'dist');
|
|
||||||
app.use(express.static(distDir));
|
app.use(`${prefix}/api/auth`, authRoutes);
|
||||||
app.use('/citpl_website', express.static(distDir));
|
app.use(`${prefix}/api/content`, contentRoutes);
|
||||||
|
app.use(`${prefix}/api/upload`, uploadRoutes);
|
||||||
|
|
||||||
|
if (serveStatic) {
|
||||||
|
app.use(prefix || '/', express.static(distDir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root paths — used when Apache proxies /citpl_website/api → http://127.0.0.1:3001/api
|
||||||
|
mountApp('');
|
||||||
|
// Prefixed paths — used when Apache proxies /citpl_website → http://127.0.0.1:3001/citpl_website
|
||||||
|
if (SITE_BASE) {
|
||||||
|
mountApp(SITE_BASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use((err, _req, res, _next) => {
|
app.use((err, _req, res, _next) => {
|
||||||
@@ -50,5 +66,10 @@ app.use((err, _req, res, _next) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`API server running on http://localhost:${PORT}`);
|
console.log(`CITPL Node server on http://localhost:${PORT}`);
|
||||||
|
console.log(` API: /api/* and ${SITE_BASE || ''}/api/*`);
|
||||||
|
console.log(` Auth: ADMIN_USERNAME from .env (${process.env.ADMIN_USERNAME || 'admin'})`);
|
||||||
|
if (serveStatic) {
|
||||||
|
console.log(` Static: ${distDir}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user