Added the possibility to serve UI files for the client

This commit is contained in:
WolverinDEV 2019-10-29 22:18:36 +01:00
parent 68e9ed504c
commit 6ae6c6f0cd
4 changed files with 116 additions and 56 deletions

2
.gitignore vendored
View file

@ -16,3 +16,5 @@ TeaSpeakUI.tar.gz
TeaWeb-*.zip TeaWeb-*.zip
todo.txt todo.txt
tmp/

View file

@ -10,11 +10,6 @@
$UI_RAW_BASE_PATH = "ui-files/raw/"; $UI_RAW_BASE_PATH = "ui-files/raw/";
$CLIENT_BASE_PATH = "files/"; $CLIENT_BASE_PATH = "files/";
if(!isset($_SERVER['REQUEST_METHOD'])) {
error_log("This is a web only script!");
exit(1);
}
function list_dir($base_dir, &$results = array(), $dir = "") { function list_dir($base_dir, &$results = array(), $dir = "") {
$files = scandir($base_dir . $dir); $files = scandir($base_dir . $dir);

117
file.ts
View file

@ -17,6 +17,7 @@ type ProjectResource = {
"web-only"?: boolean; "web-only"?: boolean;
"client-only"?: boolean; "client-only"?: boolean;
"serve-only"?: boolean;
"search-pattern": RegExp; "search-pattern": RegExp;
"search-exclude"?: RegExp; "search-exclude"?: RegExp;
@ -223,7 +224,7 @@ const APP_FILE_LIST_CLIENT_SOURCE: ProjectResource[] = [
"path": "js/", "path": "js/",
"local-path": "./shared/generated/" "local-path": "./shared/generated/"
} },
]; ];
const APP_FILE_LIST_WEB_SOURCE: ProjectResource[] = [ const APP_FILE_LIST_WEB_SOURCE: ProjectResource[] = [
@ -451,6 +452,8 @@ namespace generator {
target: "client" | "web"; target: "client" | "web";
mode: "rel" | "dev"; mode: "rel" | "dev";
serving: boolean;
source_path: string; source_path: string;
parameter: string[]; parameter: string[];
}; };
@ -510,6 +513,8 @@ namespace generator {
continue; continue;
if(typeof file["client-only"] === "boolean" && file["client-only"] && options.target !== "client") if(typeof file["client-only"] === "boolean" && file["client-only"] && options.target !== "client")
continue; continue;
if(typeof file["serve-only"] === "boolean" && file["serve-only"] && !options.serving)
continue;
if(!file["build-target"].split("|").find(e => e === options.mode)) if(!file["build-target"].split("|").find(e => e === options.mode))
continue; continue;
if(Array.isArray(file["req-parm"]) && file["req-parm"].find(e => !options.parameter.find(p => p.toLowerCase() === e.toLowerCase()))) if(Array.isArray(file["req-parm"]) && file["req-parm"].find(e => !options.parameter.find(p => p.toLowerCase() === e.toLowerCase())))
@ -550,6 +555,7 @@ namespace server {
const exists = util.promisify(fs.exists); const exists = util.promisify(fs.exists);
const stat = util.promisify(fs.stat); const stat = util.promisify(fs.stat);
const unlink = util.promisify(fs.unlink);
const exec: (command: string) => Promise<{ stdout: string, stderr: string }> = util.promisify(cp.exec); const exec: (command: string) => Promise<{ stdout: string, stderr: string }> = util.promisify(cp.exec);
let files: (generator.Entry & { http_path: string; })[] = []; let files: (generator.Entry & { http_path: string; })[] = [];
@ -586,6 +592,7 @@ namespace server {
http_path: "/" + e.target_path.replace(/\\/g, "/") http_path: "/" + e.target_path.replace(/\\/g, "/")
} }
}); });
console.log(files.filter(e => e.target_path.endsWith(".php")));
} }
export async function shutdown() { export async function shutdown() {
@ -595,31 +602,17 @@ namespace server {
} }
} }
function handle_request(request: http.IncomingMessage, response: http.ServerResponse) { function serve_php(file: string, query: any, response: http.ServerResponse) {
let url: url_utils.UrlWithParsedQuery; if(!fs.existsSync("tmp"))
try { fs.mkdirSync("tmp");
url = url_utils.parse(request.url, true); let tmp_script_name = path.join("tmp", Math.random().toFixed(32).substr(2));
} catch(error) { let script = "<?php\n";
response.writeHead(500); script += "$params = json_decode(urldecode(\"" + encodeURIComponent(JSON.stringify(query)) + "\")); \n";
response.write("invalid url:\n"); script += "foreach($params as $key => $value) $_GET[$key] = $value;\n";
response.write(error.toString()); script += "chdir(urldecode(\"" + encodeURIComponent(path.dirname(file)) + "\"));";
response.end(); script += "?>";
return; fs.writeFileSync(tmp_script_name, script, {flag: 'w'});
} exec(php + " -d auto_prepend_file=" + tmp_script_name + " " + file).then(result => {
const file = files.find(e => e.http_path === url.pathname);
if(!file) {
console.log("[SERVER] Client requested unknown file %s (%s)", url.pathname, request.url);
response.writeHead(404);
response.write("Missing file: " + url.path);
response.end();
return;
}
let type = mt.lookup(path.extname(file.local_path)) || "text/html";
console.log("[SERVER] Serving file %s (%s) (%s)", file.target_path, type, file.local_path);
if(path.extname(file.local_path) === ".php") {
exec(php + " -d WEB_CLIENT=1 " + file.local_path).then(result => {
if(result.stderr) { if(result.stderr) {
response.writeHead(500); response.writeHead(500);
response.write("Encountered error while interpreting PHP script:\n"); response.write("Encountered error while interpreting PHP script:\n");
@ -638,7 +631,25 @@ namespace server {
response.write("Received an exception while interpreting PHP script:\n"); response.write("Received an exception while interpreting PHP script:\n");
response.write(error.toString()); response.write(error.toString());
response.end(); response.end();
}).then(() => unlink(tmp_script_name)).catch(error => {
console.error("[SERVER] Failed to delete tmp PHP prepend file: %o", error);
}); });
}
function serve_file(pathname: string, query: any, response: http.ServerResponse) {
const file = files.find(e => e.http_path === pathname);
if(!file) {
console.log("[SERVER] Client requested unknown file %s", pathname);
response.writeHead(404);
response.write("Missing file: " + pathname);
response.end();
return;
}
let type = mt.lookup(path.extname(file.local_path)) || "text/html";
console.log("[SERVER] Serving file %s (%s) (%s)", file.target_path, type, file.local_path);
if(path.extname(file.local_path) === ".php") {
serve_php(file.local_path, query, response);
return; return;
} }
const fis = fs.createReadStream(file.local_path); const fis = fs.createReadStream(file.local_path);
@ -653,6 +664,56 @@ namespace server {
}); });
fis.on("data", data => response.write(data)); fis.on("data", data => response.write(data));
} }
function handle_api_request(request: http.IncomingMessage, response: http.ServerResponse, url: url_utils.UrlWithParsedQuery) {
if(url.query["type"] === "files") {
response.writeHead(200, { "info-version": 1 });
response.write("type\thash\tpath\tname\n");
for(const file of files)
if(file.http_path.endsWith(".php"))
response.write(file.type + "\t" + file.hash + "\t" + path.dirname(file.http_path) + "\t" + path.basename(file.http_path, ".php") + ".html" + "\n");
else
response.write(file.type + "\t" + file.hash + "\t" + path.dirname(file.http_path) + "\t" + path.basename(file.http_path) + "\n");
response.end();
return;
} else if(url.query["type"] === "file") {
let p = path.join(url.query["path"] as string, url.query["name"] as string).replace(/\\/g, "/");
if(p.endsWith(".html")) {
const np = p.substr(0, p.length - 5) + ".php";
if(files.find(e => e.http_path == np) && !files.find(e => e.http_path == p))
p = np;
}
serve_file(p, url.query, response);
return;
}
response.writeHead(404);
response.write(JSON.stringify({
success: 0,
message: "Unknown command"
}));
response.end();
}
function handle_request(request: http.IncomingMessage, response: http.ServerResponse) {
let url: url_utils.UrlWithParsedQuery;
try {
url = url_utils.parse(request.url, true);
} catch(error) {
response.writeHead(500);
response.write("invalid url:\n");
response.write(error.toString());
response.end();
return;
}
if(url.pathname === "/api.php") {
//Client API
handle_api_request(request, response, url);
return;
}
serve_file(url.pathname, url.query, response);
}
} }
function php_exe() : string { function php_exe() : string {
@ -668,7 +729,8 @@ async function main_serve(target: "client" | "web", mode: "rel" | "dev", port: n
source_path: __dirname, source_path: __dirname,
parameter: [], parameter: [],
target: target, target: target,
mode: mode mode: mode,
serving: true
}); });
await server.launch(files, { await server.launch(files, {
@ -752,6 +814,7 @@ async function main(args: string[]) {
console.log(" | Windows: C:\\php\\php.exe"); console.log(" | Windows: C:\\php\\php.exe");
console.log(" | Linux: /bin/php"); console.log(" | Linux: /bin/php");
} }
main(process.argv.slice(2)).then(ignore_exit => { main(process.argv.slice(2)).then(ignore_exit => {
if(typeof(ignore_exit) === "boolean" && !<any>ignore_exit) return; if(typeof(ignore_exit) === "boolean" && !<any>ignore_exit) return;
process.exit(); process.exit();

View file

@ -3,7 +3,7 @@
ini_set('display_startup_errors', 1); ini_set('display_startup_errors', 1);
error_reporting(E_ALL); error_reporting(E_ALL);
$WEB_CLIENT = (!isset($CLIENT) || !$CLIENT) && http_response_code() !== false || WEB_CLIENT; $WEB_CLIENT = (!isset($CLIENT) || !$CLIENT) && http_response_code() !== false || (defined("TEA_SERVE") && TEA_SERVE);
$localhost = false; $localhost = false;
if(gethostname() == "WolverinDEV") if(gethostname() == "WolverinDEV")
$localhost = true; $localhost = true;
@ -49,7 +49,7 @@
spawn_property('connect_default_host', $localhost ? "localhost" : "ts.TeaSpeak.de"); spawn_property('connect_default_host', $localhost ? "localhost" : "ts.TeaSpeak.de");
spawn_property('localhost_debug', $localhost ? "true" : "false"); spawn_property('localhost_debug', $localhost ? "true" : "false");
if(WEB_CLIENT) { if(defined("TEA_SERVE") && TEA_SERVE) {
$version = "0000000"; $version = "0000000";
} else { } else {
$version = file_get_contents("./version"); $version = file_get_contents("./version");