d
This commit is contained in:
parent
c3a6f5ff37
commit
4c4e767ec6
73 changed files with 3502 additions and 741 deletions
62
server/apache-bridge.js
Normal file
62
server/apache-bridge.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const BEGIN_MARKER = '# BEGIN LINKFIELD MANAGED PROXY';
|
||||
const END_MARKER = '# END LINKFIELD MANAGED PROXY';
|
||||
|
||||
function normalizePort(value) {
|
||||
const port = Number(value);
|
||||
if (!Number.isSafeInteger(port) || port < 1 || port > 65535) throw new Error(`Invalid Apache bridge port: ${value}`);
|
||||
return port;
|
||||
}
|
||||
|
||||
function renderApacheBridge(portValue) {
|
||||
const port = normalizePort(portValue);
|
||||
return `${BEGIN_MARKER}\n` +
|
||||
`<IfModule mod_rewrite.c>\n` +
|
||||
` RewriteEngine On\n` +
|
||||
`\n` +
|
||||
` # Prefer a native Apache proxy when the host permits it.\n` +
|
||||
` <IfModule mod_proxy.c>\n` +
|
||||
` <IfModule mod_proxy_wstunnel.c>\n` +
|
||||
` RewriteCond %{HTTP:Upgrade} =websocket [NC]\n` +
|
||||
` RewriteRule ^api/realtime/?$ ws://127.0.0.1:${port}/api/realtime [P,L]\n` +
|
||||
` </IfModule>\n` +
|
||||
` RewriteRule ^api/(.*)$ http://127.0.0.1:${port}/api/$1 [P,L]\n` +
|
||||
` </IfModule>\n` +
|
||||
`\n` +
|
||||
` # Shared hosts often disable mod_proxy. Route ordinary API requests\n` +
|
||||
` # through the bundled PHP bridge instead.\n` +
|
||||
` RewriteCond %{REQUEST_FILENAME} !-f\n` +
|
||||
` RewriteRule ^api/(.*)$ api-bridge.php?path=/api/$1 [QSA,L]\n` +
|
||||
`</IfModule>\n` +
|
||||
`<Files ".linkfield-port">\n` +
|
||||
` Require all denied\n` +
|
||||
`</Files>\n` +
|
||||
`${END_MARKER}\n`;
|
||||
}
|
||||
|
||||
function replaceManagedBlock(existingValue, managedBlock) {
|
||||
const existing = String(existingValue || '').replace(/\r\n?/g, '\n');
|
||||
const pattern = new RegExp(`(?:^|\\n)${BEGIN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?:\\n|$)`, 'g');
|
||||
const preserved = existing.replace(pattern, '\n').replace(/^\n+|\n+$/g, '');
|
||||
return preserved ? `${preserved}\n\n${managedBlock}` : managedBlock;
|
||||
}
|
||||
|
||||
async function installApacheBridge({fsp, root, port, enabled = true} = {}) {
|
||||
if (!enabled) return {enabled:false, written:false, file:null};
|
||||
if (!fsp || typeof fsp.readFile !== 'function' || typeof fsp.writeFile !== 'function') throw new Error('A promises-compatible filesystem is required');
|
||||
const file = path.join(root, '.htaccess');
|
||||
let existing = '';
|
||||
try { existing = await fsp.readFile(file, 'utf8'); }
|
||||
catch (error) { if (error?.code !== 'ENOENT') throw error; }
|
||||
const next = replaceManagedBlock(existing, renderApacheBridge(port));
|
||||
if (next === existing.replace(/\r\n?/g, '\n')) return {enabled:true, written:false, file};
|
||||
const temporary = `${file}.linkfield-${process.pid}-${Date.now()}.tmp`;
|
||||
await fsp.writeFile(temporary, next, {encoding:'utf8', mode:0o644});
|
||||
await fsp.rename(temporary, file);
|
||||
return {enabled:true, written:true, file};
|
||||
}
|
||||
|
||||
module.exports = Object.freeze({BEGIN_MARKER, END_MARKER, renderApacheBridge, replaceManagedBlock, installApacheBridge});
|
||||
Loading…
Add table
Add a link
Reference in a new issue