/
weylin
/
WtDev
Обзор
Документация
Войти
/
weylin
/
WtDev
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
dev
src/upload.ts
1 264 строки
28 KB
Weylin
Добавлена возможность исключать элементы из пакета по xquery
27 янв 2026, 19:51
27 янв 2026, 19:51
96bca2d
Код
Авторство
О чём код?
const MAIN_CONFIG_FILE = "wtdev.config.json"; const LOCAL_MAIN_CONFIG_FILE = "wtdev.config.local.json"; const REL_WT_UPLOAD_URL = "custom_web_template.html?object_code=wtdev"; let MAIN_CONFIG: WtConfig; import AdmZip from "adm-zip"; import axios from "axios"; import dayjs from "dayjs"; import fs, { promises as fsa } from "fs"; import md5 from "md5"; import * as crypto from "node:crypto"; import * as os from "os"; import path from "path"; import url from "url"; import * as vscode from "vscode"; import { includeFunctionStr } from "./codeFragments"; import { REQUIRED_BACKEND_VERSION, SimpleApiResult, Version, WtServer, XmlElem, globalEventManager, } from "./common"; import { EventName } from "./eventManager"; import { PipelineConfig } from "./pipeline"; import { WtConfigTask, WtTask } from "./tasks"; type PreparedFile = { type: string; url?: string; elem?: string; code?: string; application?: ApplicationConfig; includedWebTemplates?: string[]; outerAlertsToConsole?: boolean; content: string; }; type ParsedFile = { config: FileConfig; content: string; ok: boolean; }; type WtConfig = { servers: WtServer[]; backendFolder: string; projectPrefix: string; fullPackage: PackageScheme; reducedPackages?: PackageScheme[]; events?: WtDevEvent[]; tasks?: WtConfigTask[]; pipelines?: PipelineConfig[]; }; type LocalWtConfig = { servers?: WtServer[]; }; type PackageScheme = { name: string; excludedElems?: PackageSchemeElem[]; }; type PackageSchemeElem = { id?: string; catalog?: string; code?: string; byXQueryWhere?: string; }; type FileConfig = { type: CodeFileType; elem?: CodeFileElemType; code?: string; url?: string; skip?: boolean; encloseInAspTags?: boolean; importFiles?: string[]; includeWebTemplates?: string[]; outerAlertsToConsole?: boolean; funcWrapping?: FuncWrappingConfig; application?: ApplicationConfig; }; type CodeFileType = "elem" | "file"; type CodeFileElemType = | "custom_web_template" | "remote_collection" | "remote_action" | "server_agent"; type FuncWrappingConfig = { enabled: boolean; mode: string; block: string; url: string; }; type ApplicationConfig = { name: string; dropCache: boolean; }; type UploadResult = { ok: boolean; message?: string; updatedXmlElems: XmlElem[]; updatedFilesCnt: number; }; type DownloadXmlElemsResult = { ok: boolean; message?: string; elems: XmlElem[]; }; type UploadXmlElemsResult = { ok: boolean; message?: string; uploadXmlsCount: number; }; type WtDevEvent = { onEvent: EventName; runTask?: string; }; type BackendInfoResult = { ok: boolean; backendVersion: string; message?: string; }; type CheckBackendResult = { ok: boolean; methodExists: boolean; isUpToDate: boolean; hasRequiredVersion: boolean; }; class NoRootFolderError extends Error {} class NoWtConfigError extends Error {} class WtConfigError extends Error {} class NoDefaultServerError extends Error {} class OutdatedBackendError extends Error {} class WrongBackendVersionError extends Error {} async function sendApiRequest<T>(postData: any, server?: WtServer) { server = server ?? (await getDefaultServer()); postData.requiredBackendVersion = REQUIRED_BACKEND_VERSION; const result = await axios.post( url.resolve(server.url, REL_WT_UPLOAD_URL), postData, makeAxiosConfig(server) ); if (result.data?.isBackendVersionWrong) { throw new WrongBackendVersionError(); } return result.data as T; } async function isWtDevFolder() { return (await getMainConfig()) !== null; } function getRootFolder() { const rootFolder = vscode.workspace.workspaceFolders?.[0].uri.fsPath; if (!rootFolder) { throw new NoRootFolderError(); } return rootFolder; } async function getBackendFolder() { const mainConfig = await getMainConfig(); const rootFolder = getRootFolder(); return mainConfig?.backendFolder ? path.resolve(rootFolder, mainConfig.backendFolder) : rootFolder; } async function getXmlFolder() { const backendFolder = await getBackendFolder(); return path.resolve(backendFolder, "xml"); } async function getSrcFolder() { const backendFolder = await getBackendFolder(); return path.resolve(backendFolder, "src"); } async function getPackagesFolder() { const backendFolder = await getBackendFolder(); return path.resolve(backendFolder, "packages"); } async function getBackupFolder() { const backendFolder = await getBackendFolder(); return path.resolve(backendFolder, "backup"); } function getLogsFolder() { const rootFolder = getRootFolder(); return path.resolve(rootFolder, "logs"); } function removeComments(text: string) { try { text = text.replace(/\/\*.*?\*\//gs, ""); text = text.replace(/^\s*\/\/.*$/gm, ""); return text; } catch (e) { console.log(text); throw e; } } function parseFile(str: string) { let str1 = "##wtdev qewr ## wtrrtyrtey"; let fileRegexParts = str.match(/^(\s*)\/\*\#wtdev(.*?)\#\*\/\r?\n(.*)/s); //console.log('fileRegexParts: '+JSON.stringify(fileRegexParts)); let result: ParsedFile = { config: { type: "elem" }, content: "", ok: false, }; if (fileRegexParts && fileRegexParts.length === 4) { //console.log('fileRegexParts[1]', fileRegexParts[1]); result.config = JSON.parse(removeComments(fileRegexParts[2])); result.content = fileRegexParts[1] + fileRegexParts[3]; result.ok = true; } else { result.ok = false; } return result; } function makeFuncWrappers(fileContent: string, mainFileUrl: string) { let clearedFileContent = fileContent.replace(/(\/\*.*?\*\/)/gs, ""); let regexFuncs = Array.from( clearedFileContent.matchAll(/^\s*function\s*(.*)[\s\r\n]*$/gm) ); let funcsWrappers = []; //console.log('regexFuncs', JSON.stringify(Array.from(regexFuncs))); for (const regexFunc of regexFuncs) { let funcDef = regexFunc[1]; funcsWrappers.push( `function ${funcDef}\r\n` + `{\r\n` + ` var lib = OpenCodeLib('${mainFileUrl}');\r\n` + ` return lib.${funcDef};\r\n` + `}` ); } //console.log('funcsWrappers', funcsWrappers); return funcsWrappers.join("\r\n\r\n"); } async function getMainConfig(dir?: string) { if (MAIN_CONFIG) { return MAIN_CONFIG; } const rootFolder = dir ?? getRootFolder(); const mainConfigFilePath = path.resolve(rootFolder, MAIN_CONFIG_FILE); const localConfigFilePath = path.resolve(rootFolder, LOCAL_MAIN_CONFIG_FILE); const mainConfig = await readWtConfig(mainConfigFilePath); const localConfig = await readWtConfig<LocalWtConfig>(localConfigFilePath); if (!mainConfig) { throw new NoWtConfigError(); } if (localConfig) { if (localConfig.servers) { mainConfig.servers = [...mainConfig.servers, ...localConfig.servers]; } } MAIN_CONFIG = mainConfig; if (mainConfig.events) { addConfigEventListeners(mainConfig.events); } return mainConfig; } async function mainConfigExists() { try { await getMainConfig(); return true; } catch (e) { if (e instanceof NoWtConfigError) { return false; } else { throw e; } } } async function readWtConfig<T = WtConfig>(filePath: string) { if (!fs.existsSync(filePath)) { return undefined; } const stat = await fsa.lstat(filePath); if (stat.isFile()) { const dataJSON = await fsa.readFile(filePath); const data: T = JSON.parse(removeComments(dataJSON + "")); return data; } else { return undefined; } } function addConfigEventListeners(events: WtDevEvent[]) { for (const event of events) { if (event.runTask) { globalEventManager.on( event.onEvent, async () => await runTask(event.runTask) ); } } } async function getTasks() { const mainConfig = await getMainConfig(); return mainConfig.tasks; } async function runTask( configTask: WtConfigTask | string | undefined, server?: WtServer ) { if (typeof configTask === "string") { const taskName = configTask; const tasks = await getTasks(); configTask = tasks?.find((t) => t.name === taskName); } if (!configTask) { return false; } const task = WtTask.factory(configTask); if (!task) { return false; } return await task.run(server); } function wrapContentWithInclude(parsedFile: ParsedFile, content?: string) { const includeTo = parsedFile.config.elem === "server_agent" ? { to: "server_agent", code: parsedFile.config.code } : null; const includeCallStr = ` include(${JSON.stringify( parsedFile.config.includeWebTemplates )}${includeTo ? "," : ""} ${includeTo ? JSON.stringify(includeTo) : ""});`; const includeTemplate = `${includeFunctionStr} included = ${includeCallStr} if (included) { %content% } `; return includeTemplate.replace("%content%", content ?? parsedFile.content); } async function prepareFileUpload(filePath: string) { let data = await fsa.readFile(filePath); let parsedFile = parseFile(data.toString()); let preparedFiles: PreparedFile[] = []; let preparedFile: PreparedFile = { type: "", content: "", }; //console.log('parsedFile.ok', parsedFile.ok); if (!parsedFile.ok) { return []; } if (parsedFile.config.skip) { return []; } console.log(filePath); let content = parsedFile.content; content = content.replaceAll("`", '"'); if (parsedFile.config.importFiles) { const folderPath = path.dirname(filePath); let importContents = []; for (const importFile of parsedFile.config.importFiles) { const importFilePath = path.resolve(folderPath, importFile); const importFileContent = await fsa.readFile(importFilePath); importContents.push(importFileContent); } content = importContents.join("\n\n") + "\n\n" + content; } if (parsedFile.config.includeWebTemplates) { preparedFile.includedWebTemplates = parsedFile.config.includeWebTemplates; if (parsedFile.config.type === "elem" && parsedFile.config.elem) { content = wrapContentWithInclude(parsedFile, content); } } if (parsedFile.config.outerAlertsToConsole) { preparedFile.outerAlertsToConsole = parsedFile.config.outerAlertsToConsole; } if (parsedFile.config.encloseInAspTags) { content = "<%\n" + content + "\n%>"; } preparedFile.type = parsedFile.config.type; if (preparedFile.type === "file") { preparedFile.url = parsedFile.config.url; if (!parsedFile.config.url) { throw Error("No URL in config!"); } if (parsedFile.config.funcWrapping?.enabled) { let wrappedFuncsStr = makeFuncWrappers(content, parsedFile.config.url); //console.log('wrappedFuncsStr', wrappedFuncsStr); let auxFile = { type: "file", mode: parsedFile.config.funcWrapping.mode, block: parsedFile.config.funcWrapping.block, url: parsedFile.config.funcWrapping.url, content: wrappedFuncsStr, }; preparedFiles.push(auxFile); } } else if (preparedFile.type === "elem") { preparedFile.elem = parsedFile.config.elem; preparedFile.code = parsedFile.config.code; } if (parsedFile.config.application) { preparedFile.application = parsedFile.config.application; } preparedFile.content = content; preparedFiles.push(preparedFile); //preparedFiles.forEach( (preparedFile) => console.log(preparedFile.url) ); return preparedFiles; } async function prepareFolderUpload(folderPath: string) { let preparedFiles: PreparedFile[] = []; const filenames = await fsa.readdir(folderPath); for (let filename of filenames) { const filePath = path.resolve(folderPath, filename); //console.log('filePath: '+filePath); const stat = await fsa.lstat(filePath); let prepFiles: PreparedFile[]; if (stat.isFile()) { prepFiles = await prepareFileUpload(filePath); } else if (stat.isDirectory()) { prepFiles = await prepareFolderUpload(filePath); } else { prepFiles = []; } //console.log('prepFiles', prepFiles); if (prepFiles) { preparedFiles = [...preparedFiles, ...prepFiles]; } } return preparedFiles; } async function uploadPreparedFiles(files: PreparedFile[], server: WtServer) { const postData = { action: "UploadFiles", files: files, }; const uploadResult = sendApiRequest<UploadResult>(postData, server); return uploadResult; } async function getAvailableServers() { const mainConfig = await getMainConfig(); return mainConfig.servers.filter((s) => s.enabled); } async function getAvailableVisibleServers() { const servers = await getAvailableServers(); return servers.filter((s) => !s.hidden); } async function getAvailablePipelines() { const mainConfig = await getMainConfig(); return mainConfig.pipelines; } async function getLocalPackageSchemes() { const mainConfig = await getMainConfig(); return [mainConfig.fullPackage, ...(mainConfig.reducedPackages ?? [])]; } function makeAxiosConfig(server: WtServer) { const token = Buffer.from( `${server.login}:${server.password}`, "utf8" ).toString("base64"); return { headers: { Authorization: `Basic ${token}`, }, }; } async function getDefaultServer() { const mainConfig = await getMainConfig(); let defaultServer; if (mainConfig.servers.length === 1) { defaultServer = mainConfig.servers[0]; } else if (mainConfig.servers.length > 1) { defaultServer = mainConfig.servers.find((s) => s.default); } if (!defaultServer) { throw new NoDefaultServerError(); } return defaultServer; } async function uploadAllToServer(selectedServer: WtServer) { const srcFolder = await getSrcFolder(); const xmlUploadResult = await uploadAllXmlElems(); await globalEventManager.emit("onDidUploadAll"); const fileUploadResult = await uploadSourceCode(srcFolder, selectedServer); return { xmlUploadResult, fileUploadResult, }; } async function uploadSourceCode(sourcePath: string, server?: WtServer) { const stat = await fsa.lstat(sourcePath); server = server ?? (await getDefaultServer()); if (!server) { return { ok: false, updatedFilesCnt: 0 } as UploadResult; } let file, dir; if (stat.isFile()) { file = sourcePath; dir = path.dirname(file); } else { dir = sourcePath; } if (dir === undefined && file !== undefined) { dir = path.dirname(file); } if (sourcePath === undefined) { throw Error("No path"); } let preparedFiles; if (file) { preparedFiles = await prepareFileUpload(file); } else { preparedFiles = await prepareFolderUpload(dir); } if (preparedFiles.length === 0) { return { ok: true, updatedFilesCnt: 0, message: "Нет файлов для загрузки", } as UploadResult; } const uploadResult = await uploadPreparedFiles(preparedFiles, server); for (const updatedXmlElem of uploadResult.updatedXmlElems) { storeXmlElem(updatedXmlElem); } console.log("Загрузка завершена"); return uploadResult; //await new Promise(resolve => setTimeout(resolve, 5000)); } async function uploadAllXmlElems(server?: WtServer) { server = server ?? (await getDefaultServer()); const xmlElems = await getLocalXmlElems(); const postData = { action: "UploadXmlElems", xmlElems: xmlElems, }; const result = await sendApiRequest<UploadXmlElemsResult>(postData, server); return result; } async function getLocalXmlElemIds(xmlFolderPath?: string) { const xmlElems = await getLocalXmlElems(xmlFolderPath); const elemIds = xmlElems.map((el) => el.id); return elemIds; } async function getLocalXmlElems(xmlFolderPath?: string) { xmlFolderPath = xmlFolderPath ?? (await getXmlFolder()); let xmlElems: XmlElem[] = []; const filenames = await fsa.readdir(xmlFolderPath); for (let filename of filenames) { const fsItemPath = path.resolve(xmlFolderPath, filename); const fsItemStat = await fsa.lstat(fsItemPath); let prepFiles: PreparedFile[]; if (fsItemStat.isFile()) { const xmlElem = await getLocalXmlElem(fsItemPath); if (xmlElem) { xmlElems.push(xmlElem); } } else if (fsItemStat.isDirectory()) { xmlElems = [...xmlElems, ...(await getLocalXmlElems(fsItemPath))]; } } return xmlElems; } async function getLocalXmlElem(xmlFilePath: string) { const filename = path.basename(xmlFilePath); const id = getElemIdFromString(filename); if (!id) { return undefined; } const form = getParentFolderName(xmlFilePath); if (!form) { return undefined; } const data = String(await fsa.readFile(xmlFilePath)); const xmlElemName = getElemNameFromString(filename); const xmlElem: XmlElem = { id: id, name: xmlElemName, form: form, xml: data, hash: md5(data), filepath: xmlFilePath, }; return xmlElem; } async function saveRemoteXmlElemToTempFile(elemId: string, server?: WtServer) { server = server ?? (await getDefaultServer()); const postData = { action: "DownloadXmlElems", elemIds: [elemId], }; const result = await sendApiRequest<DownloadXmlElemsResult>(postData, server); if (!result.ok || result.elems.length === 0) { return undefined; } const remoteXmlElem = result.elems[0]; const tempDir = os.tmpdir(); const filenameRandomPart = crypto.randomBytes(20).toString("hex"); const filename = `wtdev${filenameRandomPart}.xml`; const filepath = path.join(tempDir, filename); await fsa.writeFile(filepath, remoteXmlElem.xml); return filepath; } type GetXmlElemStampsResult = { ok: boolean; stamps: XmlElemStamp[]; }; type XmlElemStamp = { id: string; hash: string; }; async function findOutdatedXmlElems(xmlFolderPath?: string, server?: WtServer) { server = server ?? (await getDefaultServer()); const xmlElems = await getLocalXmlElems(xmlFolderPath); const elemIds = xmlElems.map((el) => el.id); const postData = { action: "GetXmlElemStamps", elemIds, }; const resultData = await sendApiRequest<GetXmlElemStampsResult>( postData, server ); const outdatedXmlElems: XmlElem[] = []; for (const xmlElemStamp of resultData.stamps) { const localXmlElem = xmlElems.find((el) => el.id === xmlElemStamp.id); if (!localXmlElem) { continue; } const localCmpXmlElem = makeComparableXmlElem(localXmlElem); if (localCmpXmlElem.hash !== xmlElemStamp.hash) { outdatedXmlElems.push(localXmlElem); } } return outdatedXmlElems; } function makeComparableXmlElem(xmlElem: XmlElem) { const cmpXml = xmlElem.xml.replace( /(\<file_url\>x-local:\/\/wt_data\/attachments).+\/(.*\<\/file_url\>)/, "$1/something/$2" ); const newXmlElem: XmlElem = { id: xmlElem.id, name: xmlElem.name, form: xmlElem.form, xml: cmpXml, hash: md5(cmpXml), filepath: xmlElem.filepath, }; return newXmlElem; } function getParentFolderName(filePath: string) { const dir = path.dirname(filePath); const dirParts = dir.split(path.sep); return dirParts.length > 0 ? dirParts.reverse()[0] : undefined; } function getElemNameFromString(str: string, id?: string) { if (!id) { id = getElemIdFromString(str); } let elemName = str; if (id) { elemName = elemName.replace(id, ""); } elemName = elemName.replace(/\.[^\.]*$/, ""); return elemName; } function getElemIdFromString(str: string) { const matches = str.match(/\d{18,}/); return matches?.length === 1 ? matches[0] : undefined; } async function downloadXmlElems(elemIds: string[], server?: WtServer) { server = server ?? (await getDefaultServer()); const postData = { action: "DownloadXmlElems", elemIds, }; const result = await sendApiRequest<DownloadXmlElemsResult>(postData, server); if (!result.ok) { return result; } for (const elem of result.elems) { storeXmlElem(elem); } return result; } async function downloadXmlElemsFromPackageScheme( packageSchemeName: string, server?: WtServer ) { server = server ?? (await getDefaultServer()); const postData = { action: "DownloadXmlElemsByPackageScheme", packageSchemeName, }; const result = await sendApiRequest<DownloadXmlElemsResult>(postData, server); if (!result.ok) { return result; } for (const elem of result.elems) { storeXmlElem(elem); } return result; } async function updateLocalXmlElemsFromServer(server?: WtServer) { const elemIds = await getLocalXmlElemIds(); return downloadXmlElems(elemIds, server); } async function storeXmlElem(xmlElem: XmlElem) { const xmlFolderPath = await getXmlFolder(); const xmlElemFolderPath = path.resolve(xmlFolderPath, xmlElem.form); if (!fs.existsSync(xmlElemFolderPath)) { await fsa.mkdir(xmlElemFolderPath, { recursive: true }); } //fsa.access(xmlElemFolderPath, fsa.constants.F_OK); const cleanedElemName = cleanFilename(xmlElem.name); const filename = `${cleanedElemName} ${xmlElem.id}.xml`; const xmlElemPath = path.resolve(xmlElemFolderPath, filename); if (!fs.existsSync(xmlElemPath)) { if (await existXmlFilesWithId(xmlElem.id, xmlElemFolderPath)) { await removeXmlFilesById(xmlElem.id, xmlElemFolderPath); } } await fsa.writeFile(xmlElemPath, xmlElem.xml); return true; } function cleanFilename(filename: string) { return filename.replace(/[\\\/\:\*\?\"\<\>\|]/g, "_"); } async function existXmlFilesWithId(id: string, folderPath: string) { const filenames = await fsa.readdir(folderPath); const matchingFilenames = filenames.filter((f) => f.includes(id)); return matchingFilenames.length > 0; } async function removeXmlFilesById(id: string, folderPath: string) { const filenames = await fsa.readdir(folderPath); const matchingFilenames = filenames.filter((f) => f.includes(id)); for (const matchingFilename of matchingFilenames) { const filePath = path.resolve(folderPath, matchingFilename); await fsa.unlink(filePath); } } type DownloadedPackage = { ok: boolean; message?: string; elemsCount: number; dataB64: string; }; type UploadingPackage = { dataB64: string; }; type PackageDownloadingResult = { ok: boolean; message?: string; filePath: string; }; async function getFullPackageScheme() { const mainConfig = await getMainConfig(); return mainConfig.fullPackage; } async function downloadPackage(params?: { server?: WtServer; scheme?: PackageScheme; packageFileName?: string; targetFolderPath?: string; skipMissingElems?: boolean; }) { const mainConfig = await getMainConfig(); const server = params?.server ?? (await getDefaultServer()); const scheme = params?.scheme ?? (await getFullPackageScheme()); const result: PackageDownloadingResult = { ok: false, message: "", filePath: "", }; if (!server) { result.message = "Не указан сервер"; return result; } if (!scheme) { result.message = "Не указана схема пакета"; return result; } const elemIDs = await getLocalXmlElemIds(); const postData = { action: "CreatePackage", skipMissingElems: params?.skipMissingElems ?? false, elemIDs, excludedElems: scheme.excludedElems ?? [], }; const downloadedPackage = await sendApiRequest<DownloadedPackage>( postData, server ); if (!downloadedPackage) { result.message = "Не удалось создать пакет"; return result; } result.ok = downloadedPackage.ok; result.message = downloadedPackage.message; if (!result.ok) { return result; } const packageFileName = params?.packageFileName ?? `${mainConfig.projectPrefix}_${scheme.name}_${dayjs().format( "YYYYMMDD_HHmm" )}.zip`; const saveResult = await savePackage( downloadedPackage, packageFileName, params?.targetFolderPath ); result.ok = saveResult.ok; result.filePath = saveResult.filePath ?? ""; return result; } async function installPackageToServer( downloadedPackage: PackageDownloadingResult, server: WtServer ): Promise<SimpleApiResult> { if (!downloadedPackage.ok) { return { ok: false, message: downloadedPackage.message }; } const fileData = await fsa.readFile(downloadedPackage.filePath); const postData = { action: "InstallPackage", dataB64: fileData.toString("base64"), }; const packageInstallResult = await sendApiRequest<SimpleApiResult>( postData, server ); console.log("packageInstallResult :>> ", packageInstallResult); return packageInstallResult; } async function savePackage( downloadedPackage: DownloadedPackage, packageFileName: string, folderPath?: string ) { const packagesFolderPath = folderPath ?? (await getPackagesFolder()); if (!packagesFolderPath) { return { ok: false }; } if (!fs.existsSync(packagesFolderPath)) { await fsa.mkdir(packagesFolderPath, { recursive: true }); } const fileData = Buffer.from(String(downloadedPackage.dataB64), "base64"); const filePath = path.resolve(packagesFolderPath, packageFileName); await fsa.writeFile(filePath, fileData); return { ok: true, filePath, }; } async function downloadBackupPackage( server?: WtServer, scheme?: PackageScheme ) { server = server ?? (await getDefaultServer()); scheme = scheme ?? (await getFullPackageScheme()); const packageFileName = `${scheme.name}_BACKUP_${dayjs().format( "YYYYMMDD_HHmmss_SSS" )}.zip`; const targetFolderPath = await getBackupFolder(); await downloadPackage({ server, packageFileName, targetFolderPath, skipMissingElems: true, }); } async function backupLocalData() { const zip = new AdmZip(); const xmlFolderPath = await getXmlFolder(); const srcFolderPath = await getSrcFolder(); zip.addLocalFolder(xmlFolderPath, "xml/"); zip.addLocalFolder(srcFolderPath, "src/"); const backupFolderPath = await getBackupFolder(); const zipFileName = `LocalBackup_${dayjs().format( "YYYYMMDD_HHmmss_SSS" )}.zip`; const zipFilePath = path.resolve(backupFolderPath, zipFileName); return await zip.writeZipPromise(zipFilePath); } async function checkBackend(server?: WtServer) { server = server ?? (await getDefaultServer()); const postData = { action: "GetBackendInfo", }; const backendInfo = await sendApiRequest<BackendInfoResult>(postData, server); const checkResult: CheckBackendResult = { ok: backendInfo.ok, methodExists: backendInfo.message !== "No such api method", isUpToDate: backendInfo.backendVersion === REQUIRED_BACKEND_VERSION, hasRequiredVersion: backendInfo.backendVersion === REQUIRED_BACKEND_VERSION, }; return checkResult; } function parseVersionStr(versionStr: string) { const versionParts = versionStr.split(".").map((p) => parseInt(p)); if (versionParts.length !== 3) { return undefined; } if (versionParts.some((p) => isNaN(p))) { return undefined; } const version: Version = { major: versionParts[0], minor: versionParts[1], patch: versionParts[2], }; } function isVersionActual( currentVersion: string | Version | undefined, requiredVersion: string | Version | undefined ) { if (typeof currentVersion === "string") { currentVersion = parseVersionStr(currentVersion); } if (typeof requiredVersion === "string") { requiredVersion = parseVersionStr(requiredVersion); } if (!currentVersion || !requiredVersion) { return false; } const isActual = false; // TODO return isActual; } function getRequiredBackendUrl() { return `http://wtdev.wprog.ru/backend/wtdev_backend_${REQUIRED_BACKEND_VERSION}.zip`; } export { NoDefaultServerError, NoRootFolderError, NoWtConfigError, OutdatedBackendError, PreparedFile, WrongBackendVersionError, WtConfigError, backupLocalData, checkBackend, downloadBackupPackage, downloadPackage, downloadXmlElems, downloadXmlElemsFromPackageScheme, findOutdatedXmlElems, getAvailablePipelines, getAvailableServers, getAvailableVisibleServers, getBackendFolder, getDefaultServer, getLocalPackageSchemes, getLocalXmlElem, getLogsFolder, getMainConfig, getPackagesFolder, getRequiredBackendUrl, getRootFolder, getSrcFolder, getTasks, installPackageToServer, isWtDevFolder, mainConfigExists, prepareFileUpload, runTask, saveRemoteXmlElemToTempFile, sendApiRequest, storeXmlElem, updateLocalXmlElemsFromServer, uploadAllToServer, uploadAllXmlElems, uploadSourceCode, };