CODEBASE: Refactor message handler of Remote API (#2211)

This commit is contained in:
catloversg
2025-06-22 03:51:09 +07:00
committed by GitHub
parent a506554e04
commit 38d033368e

View File

@@ -4,110 +4,170 @@ import { hasScriptExtension } from "../Paths/ScriptFilePath";
import { GetServer, GetAllServers } from "../Server/AllServers";
import {
RFAMessage,
type FileData,
type FileContent,
isFileServer,
isFileLocation,
type FileLocation,
isFileData,
type FileMetadata,
type FileData,
type FileLocation,
type FileServer,
} from "./MessageDefinitions";
import libSource from "../ScriptEditor/NetscriptDefinitions.d.ts?raw";
import { saveObject } from "../SaveObject";
import { Player } from "@player";
import type { BaseServer } from "../Server/BaseServer";
import type { ContentFilePath } from "../Paths/ContentFile";
function error(errorMsg: string, { id }: RFAMessage): RFAMessage {
return new RFAMessage({ error: errorMsg, id: id });
type SuccessResult<T> = { success: true; params: T };
type FailureResult = { success: false; errorResponse: RFAMessage };
export type ValidationResult<T> = SuccessResult<T> | FailureResult;
function getErrorResponse(errorMsg: string, { id }: RFAMessage): RFAMessage {
return new RFAMessage({ error: errorMsg, id });
}
function validateParams<T>(validationFunction: (p: unknown) => p is T, request: RFAMessage): ValidationResult<T> {
if (!request.params) {
return { success: false, errorResponse: getErrorResponse("Missing params", request) };
}
if (!validationFunction(request.params)) {
return {
success: false,
errorResponse: getErrorResponse(`Invalid params: ${JSON.stringify(request.params)}`, request),
};
}
return { success: true, params: request.params };
}
function validateFilePathAndServerParams<T extends FileData | FileLocation>(
validationFunction: (p: unknown) => p is T,
request: RFAMessage,
): { success: true; data: { filePath: ContentFilePath; server: BaseServer; params: T } } | FailureResult {
const validationResult = validateParams(validationFunction, request);
if (!validationResult.success) {
return validationResult;
}
const params = validationResult.params;
const filePath = resolveFilePath(params.filename);
if (!filePath) {
return { success: false, errorResponse: getErrorResponse(`Invalid file path: ${params.filename}`, request) };
}
if (!hasTextExtension(filePath) && !hasScriptExtension(filePath)) {
return {
success: false,
errorResponse: getErrorResponse(`Invalid file extension. Filename: ${params.filename}`, request),
};
}
const server = GetServer(params.server);
if (!server) {
return { success: false, errorResponse: getErrorResponse(`Invalid hostname: ${params.server}`, request) };
}
return { success: true, data: { filePath, server, params } };
}
function validateServerParams(
request: RFAMessage,
): { success: true; data: { server: BaseServer; params: FileServer } } | FailureResult {
const validationResult = validateParams(isFileServer, request);
if (!validationResult.success) {
return validationResult;
}
const fileServer = validationResult.params;
const server = GetServer(fileServer.server);
if (!server) {
return { success: false, errorResponse: getErrorResponse(`Invalid hostname: ${fileServer.server}`, request) };
}
return { success: true, data: { server, params: fileServer } };
}
export const RFARequestHandler: Record<string, (message: RFAMessage) => RFAMessage | Promise<RFAMessage>> = {
pushFile: function (msg: RFAMessage): RFAMessage {
if (!isFileData(msg.params)) return error("Misses parameters", msg);
const fileData: FileData = msg.params;
const filePath = resolveFilePath(fileData.filename);
if (!filePath) return error("Invalid file path", msg);
const server = GetServer(fileData.server);
if (!server) return error("Server hostname invalid", msg);
if (hasTextExtension(filePath) || hasScriptExtension(filePath)) {
server.writeToContentFile(filePath, fileData.content);
return new RFAMessage({ result: "OK", id: msg.id });
const validationResult = validateFilePathAndServerParams(isFileData, msg);
if (!validationResult.success) {
return validationResult.errorResponse;
}
return error("Invalid file extension", msg);
const validationData = validationResult.data;
validationData.server.writeToContentFile(validationData.filePath, validationData.params.content);
return new RFAMessage({ result: "OK", id: msg.id });
},
getFile: function (msg: RFAMessage): RFAMessage {
if (!isFileLocation(msg.params)) return error("Message misses parameters", msg);
const validationResult = validateFilePathAndServerParams(isFileLocation, msg);
if (!validationResult.success) {
return validationResult.errorResponse;
}
const validationData = validationResult.data;
const fileData: FileLocation = msg.params;
const filePath = resolveFilePath(fileData.filename);
if (!filePath) return error("Invalid file path", msg);
const file = validationData.server.getContentFile(validationData.filePath);
if (!file) {
return getErrorResponse(`File does not exist. Filename: ${validationData.params.filename}`, msg);
}
const server = GetServer(fileData.server);
if (!server) return error("Server hostname invalid", msg);
if (!hasTextExtension(filePath) && !hasScriptExtension(filePath)) return error("Invalid file extension", msg);
const file = server.getContentFile(filePath);
if (!file) return error("File doesn't exist", msg);
return new RFAMessage({ result: file.content, id: msg.id });
},
getFileMetadata: function (msg: RFAMessage): RFAMessage {
if (!isFileLocation(msg.params)) return error("Message misses parameters", msg);
const validationResult = validateFilePathAndServerParams(isFileLocation, msg);
if (!validationResult.success) {
return validationResult.errorResponse;
}
const validationData = validationResult.data;
const fileData: FileLocation = msg.params;
const filePath = resolveFilePath(fileData.filename);
if (!filePath) return error("Invalid file path", msg);
const file = validationData.server.getContentFile(validationData.filePath);
if (!file) {
return getErrorResponse(`File does not exist. Filename: ${validationData.params.filename}`, msg);
}
const server = GetServer(fileData.server);
if (!server) return error("Server hostname invalid", msg);
if (!hasTextExtension(filePath) && !hasScriptExtension(filePath)) return error("Invalid file extension", msg);
const file = server.getContentFile(filePath);
if (!file) return error("File doesn't exist", msg);
const result: FileMetadata = {
filename: file.filename,
...file.metadata.plain(),
};
return new RFAMessage({ result: result, id: msg.id });
return new RFAMessage({
result: {
filename: file.filename,
...file.metadata.plain(),
},
id: msg.id,
});
},
deleteFile: function (msg: RFAMessage): RFAMessage {
if (!isFileLocation(msg.params)) return error("Message misses parameters", msg);
const validationResult = validateFilePathAndServerParams(isFileLocation, msg);
if (!validationResult.success) {
return validationResult.errorResponse;
}
const validationData = validationResult.data;
const fileData: FileLocation = msg.params;
const filePath = resolveFilePath(fileData.filename);
if (!filePath) return error("Invalid filename", msg);
const resultOfRemovingFile = validationData.server.removeFile(validationData.filePath);
if (!resultOfRemovingFile.res) {
return getErrorResponse(resultOfRemovingFile.msg ?? "Failed", msg);
}
const server = GetServer(fileData.server);
if (!server) return error("Server hostname invalid", msg);
const result = server.removeFile(filePath);
if (result.res) return new RFAMessage({ result: "OK", id: msg.id });
return error(result.msg ?? "Failed", msg);
return new RFAMessage({ result: "OK", id: msg.id });
},
getFileNames: function (msg: RFAMessage): RFAMessage {
if (!isFileServer(msg.params)) return error("Message misses parameters", msg);
const validationResult = validateServerParams(msg);
if (!validationResult.success) {
return validationResult.errorResponse;
}
const validationData = validationResult.data;
const server = GetServer(msg.params.server);
if (!server) return error("Server hostname invalid", msg);
const fileNameList: string[] = [...server.textFiles.keys(), ...server.scripts.keys()];
const fileNameList = [...validationData.server.textFiles.keys(), ...validationData.server.scripts.keys()];
return new RFAMessage({ result: fileNameList, id: msg.id });
},
getAllFiles: function (msg: RFAMessage): RFAMessage {
if (!isFileServer(msg.params)) return error("Message misses parameters", msg);
const validationResult = validateServerParams(msg);
if (!validationResult.success) {
return validationResult.errorResponse;
}
const validationData = validationResult.data;
const server = GetServer(msg.params.server);
if (!server) return error("Server hostname invalid", msg);
const fileList: FileContent[] = [...server.scripts, ...server.textFiles].map(([filename, file]) => ({
const fileList = [...validationData.server.scripts, ...validationData.server.textFiles].map(([filename, file]) => ({
filename,
content: file.content,
}));
@@ -115,12 +175,13 @@ export const RFARequestHandler: Record<string, (message: RFAMessage) => RFAMessa
},
getAllFileMetadata: function (msg: RFAMessage): RFAMessage {
if (!isFileServer(msg.params)) return error("Message misses parameters", msg);
const validationResult = validateServerParams(msg);
if (!validationResult.success) {
return validationResult.errorResponse;
}
const validationData = validationResult.data;
const server = GetServer(msg.params.server);
if (!server) return error("Server hostname invalid", msg);
const fileList: FileMetadata[] = [...server.scripts, ...server.textFiles].map(([filename, file]) => ({
const fileList = [...validationData.server.scripts, ...validationData.server.textFiles].map(([filename, file]) => ({
filename: filename,
...file.metadata.plain(),
}));
@@ -128,19 +189,30 @@ export const RFARequestHandler: Record<string, (message: RFAMessage) => RFAMessa
},
calculateRam: function (msg: RFAMessage): RFAMessage {
if (!isFileLocation(msg.params)) return error("Message misses parameters", msg);
const fileData: FileLocation = msg.params;
const filePath = resolveFilePath(fileData.filename);
if (!filePath) return error("Invalid filename", msg);
const validationResult = validateFilePathAndServerParams(isFileLocation, msg);
if (!validationResult.success) {
return validationResult.errorResponse;
}
const validationData = validationResult.data;
const server = GetServer(fileData.server);
if (!server) return error("Server hostname invalid", msg);
// Validate filePath again. validateFilePathAndServerParams only checks if filePath is a text file or a script.
if (!hasScriptExtension(validationData.filePath)) {
return getErrorResponse(`File is not a script. Filename: ${validationData.params.filename}`, msg);
}
const script = validationData.server.scripts.get(validationData.filePath);
if (!script) {
return getErrorResponse(`File does not exist. Filename: ${validationData.params.filename}`, msg);
}
const ramUsage = script.getRamUsage(validationData.server.scripts);
if (!ramUsage) {
return getErrorResponse(
`Cannot calculate RAM usage of an invalid script. Filename: ${validationData.params.filename}`,
msg,
);
}
if (!hasScriptExtension(filePath)) return error("Filename isn't a script filename", msg);
const script = server.scripts.get(filePath);
if (!script) return error("File doesn't exist", msg);
const ramUsage = script.getRamUsage(server.scripts);
if (!ramUsage) return error("Ram cost could not be calculated", msg);
return new RFAMessage({ result: ramUsage, id: msg.id });
},