diff --git a/back/api/config.ts b/back/api/config.ts index 889f17ff3d2..d98e3c14ace 100644 --- a/back/api/config.ts +++ b/back/api/config.ts @@ -3,7 +3,7 @@ import { Router, Request, Response, NextFunction } from 'express'; import { Container } from 'typedi'; import { Logger } from 'winston'; import config from '../config'; -import * as fs from 'fs'; +import * as fs from 'fs/promises'; import { celebrate, Joi } from 'celebrate'; import { join } from 'path'; const route = Router(); @@ -16,7 +16,7 @@ export default (app: Router) => { async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const fileList = fs.readdirSync(config.configPath, 'utf-8'); + const fileList = await fs.readdir(config.configPath, 'utf-8'); res.send({ code: 200, data: fileList @@ -41,11 +41,11 @@ export default (app: Router) => { res.send({ code: 403, message: '文件无法访问' }); } if (req.params.file.includes('sample')) { - content = getFileContentByName( + content = await getFileContentByName( join(config.samplePath, req.params.file), ); } else { - content = getFileContentByName( + content = await getFileContentByName( join(config.configPath, req.params.file), ); } @@ -72,7 +72,7 @@ export default (app: Router) => { res.send({ code: 403, message: '文件无法访问' }); } const path = join(config.configPath, name); - fs.writeFileSync(path, content); + await fs.writeFile(path, content); res.send({ code: 200, message: '保存成功' }); } catch (e) { return next(e); diff --git a/back/api/log.ts b/back/api/log.ts index 521c089ce90..562671a44b2 100644 --- a/back/api/log.ts +++ b/back/api/log.ts @@ -3,7 +3,7 @@ import { Container } from 'typedi'; import { Logger } from 'winston'; import * as fs from 'fs'; import config from '../config'; -import { emptyDir, getFileContentByName, readDirs } from '../config/util'; +import { getFileContentByName, readDirs, rmPath } from '../config/util'; import { join } from 'path'; import { celebrate, Joi } from 'celebrate'; const route = Router(); @@ -39,7 +39,7 @@ export default (app: Router) => { (req.query.path || '') as string, req.params.file, ); - const content = getFileContentByName(filePath); + const content = await getFileContentByName(filePath); res.send({ code: 200, data: content }); } catch (e) { return next(e); @@ -64,11 +64,7 @@ export default (app: Router) => { type: string; }; const filePath = join(config.logPath, path, filename); - if (type === 'directory') { - await emptyDir(filePath); - } else { - fs.unlinkSync(filePath); - } + await rmPath(filePath); res.send({ code: 200 }); } catch (e) { return next(e); diff --git a/back/api/script.ts b/back/api/script.ts index 8cee70958fb..ab2c5177d1a 100644 --- a/back/api/script.ts +++ b/back/api/script.ts @@ -4,13 +4,13 @@ import { readDirs, getLastModifyFilePath, readDir, - emptyDir, + rmPath, } from '../config/util'; import { Router, Request, Response, NextFunction } from 'express'; import { Container } from 'typedi'; import { Logger } from 'winston'; import config from '../config'; -import * as fs from 'fs'; +import * as fs from 'fs/promises'; import { celebrate, Joi } from 'celebrate'; import path, { join, parse } from 'path'; import ScriptService from '../services/script'; @@ -40,9 +40,13 @@ export default (app: Router) => { config.scriptPath, req.query.path as string, ); - result = readDir(targetPath, config.scriptPath, blacklist); + result = await readDir(targetPath, config.scriptPath, blacklist); } else { - result = readDirs(config.scriptPath, config.scriptPath, blacklist); + result = await readDirs( + config.scriptPath, + config.scriptPath, + blacklist, + ); } res.send({ code: 200, @@ -64,7 +68,7 @@ export default (app: Router) => { req.query.path as string, req.params.file, ); - const content = getFileContentByName(filePath); + const content = await getFileContentByName(filePath); res.send({ code: 200, data: content }); } catch (e) { return next(e); @@ -104,12 +108,12 @@ export default (app: Router) => { } if (req.file) { - fs.renameSync(req.file.path, join(path, req.file.filename)); + await fs.rename(req.file.path, join(path, req.file.filename)); return res.send({ code: 200 }); } if (directory) { - fs.mkdirSync(join(path, directory), { recursive: true }); + await fs.mkdir(join(path, directory), { recursive: true }); return res.send({ code: 200 }); } @@ -121,16 +125,17 @@ export default (app: Router) => { `${originFilename.replace(/\//g, '')}`, ); const filePath = join(path, `${filename.replace(/\//g, '')}`); - if (fs.existsSync(originFilePath)) { - fs.copyFileSync( + const fileExists = await fileExist(filePath); + if (fileExists) { + await fs.copyFile( originFilePath, join(config.bakPath, originFilename.replace(/\//g, '')), ); if (filename !== originFilename) { - fs.unlinkSync(originFilePath); + await rmPath(originFilePath); } } - fs.writeFileSync(filePath, content); + await fs.writeFile(filePath, content); return res.send({ code: 200 }); } catch (e) { return next(e); @@ -156,7 +161,7 @@ export default (app: Router) => { path: string; }; const filePath = join(config.scriptPath, path, filename); - fs.writeFileSync(filePath, content); + await fs.writeFile(filePath, content); return res.send({ code: 200 }); } catch (e) { return next(e); @@ -182,11 +187,7 @@ export default (app: Router) => { type: string; }; const filePath = join(config.scriptPath, path, filename); - if (type === 'directory') { - await emptyDir(filePath); - } else { - fs.unlinkSync(filePath); - } + await rmPath(filePath); res.send({ code: 200 }); } catch (e) { return next(e); @@ -239,7 +240,7 @@ export default (app: Router) => { let { filename, content, path } = req.body; const { name, ext } = parse(filename); const filePath = join(config.scriptPath, path, `${name}.swap${ext}`); - fs.writeFileSync(filePath, content || '', { encoding: 'utf8' }); + await fs.writeFile(filePath, content || '', { encoding: 'utf8' }); const scriptService = Container.get(ScriptService); const result = await scriptService.runScript(filePath); @@ -269,7 +270,7 @@ export default (app: Router) => { const scriptService = Container.get(ScriptService); const result = await scriptService.stopScript(filePath, pid); setTimeout(() => { - emptyDir(logPath); + rmPath(logPath); }, 3000); res.send(result); } catch (e) { @@ -297,7 +298,7 @@ export default (app: Router) => { }; const filePath = join(config.scriptPath, path, filename); const newPath = join(config.scriptPath, path, newFilename); - fs.renameSync(filePath, newPath); + await fs.rename(filePath, newPath); res.send({ code: 200 }); } catch (e) { return next(e); diff --git a/back/api/system.ts b/back/api/system.ts index 5925ef16239..e2e714cf3e8 100644 --- a/back/api/system.ts +++ b/back/api/system.ts @@ -1,7 +1,7 @@ import { Router, Request, Response, NextFunction } from 'express'; import { Container } from 'typedi'; import { Logger } from 'winston'; -import * as fs from 'fs'; +import * as fs from 'fs/promises'; import config from '../config'; import SystemService from '../services/system'; import { celebrate, Joi } from 'celebrate'; @@ -174,7 +174,7 @@ export default (app: Router) => { async (req: Request, res: Response, next: NextFunction) => { try { const systemService = Container.get(SystemService); - const command = req.body.command + const command = req.body.command; const idStr = `cat ${config.crontabFile} | grep -E "${command}" | perl -pe "s|.*ID=(.*) ${command}.*|\\1|" | head -1 | awk -F " " '{print $1}' | xargs echo -n`; let id = await promiseExec(idStr); const uniqPath = await getUniqPath(command, id); @@ -193,12 +193,12 @@ export default (app: Router) => { onError: async (message: string) => { res.write(`\n${message}`); const absolutePath = await handleLogPath(logPath); - fs.appendFileSync(absolutePath, `\n${message}`); + await fs.appendFile(absolutePath, `\n${message}`); }, onLog: async (message: string) => { res.write(`\n${message}`); const absolutePath = await handleLogPath(logPath); - fs.appendFileSync(absolutePath, `\n${message}`); + await fs.appendFile(absolutePath, `\n${message}`); }, }, ); @@ -253,16 +253,12 @@ export default (app: Router) => { }, ); - route.get( - '/log', - async (req: Request, res: Response, next: NextFunction) => { - try { - const systemService = Container.get(SystemService); - await systemService.getSystemLog(res); - } catch (e) { - return next(e); - } - }, - ); - + route.get('/log', async (req: Request, res: Response, next: NextFunction) => { + try { + const systemService = Container.get(SystemService); + await systemService.getSystemLog(res); + } catch (e) { + return next(e); + } + }); }; diff --git a/back/config/util.ts b/back/config/util.ts index e4cab590a49..708ad360384 100644 --- a/back/config/util.ts +++ b/back/config/util.ts @@ -1,4 +1,4 @@ -import * as fs from 'fs'; +import * as fs from 'fs/promises'; import * as path from 'path'; import got from 'got'; import iconv from 'iconv-lite'; @@ -13,22 +13,24 @@ import Logger from '../loaders/logger'; export * from './share'; -export function getFileContentByName(fileName: string) { - if (fs.existsSync(fileName)) { - return fs.readFileSync(fileName, 'utf8'); +export async function getFileContentByName(fileName: string) { + const _exsit = await fileExist(fileName); + if (_exsit) { + return await fs.readFile(fileName, 'utf8'); } return ''; } -export function getLastModifyFilePath(dir: string) { +export async function getLastModifyFilePath(dir: string) { let filePath = ''; - if (fs.existsSync(dir)) { - const arr = fs.readdirSync(dir); + const _exsit = await fileExist(dir); + if (_exsit) { + const arr = await fs.readdir(dir); - arr.forEach((item) => { + arr.forEach(async (item) => { const fullpath = path.join(dir, item); - const stats = fs.statSync(fullpath); + const stats = await fs.stat(fullpath); if (stats.isFile()) { if (stats.mtimeMs >= 0) { filePath = fullpath; @@ -152,22 +154,17 @@ export function getPlatform(userAgent: string): 'mobile' | 'desktop' { } export async function fileExist(file: any) { - return new Promise((resolve) => { - try { - fs.accessSync(file); - resolve(true); - } catch (error) { - resolve(false); - } - }); + try { + await fs.access(file); + return true; + } catch (error) { + return false; + } } export async function createFile(file: string, data: string = '') { - return new Promise((resolve) => { - fs.mkdirSync(path.dirname(file), { recursive: true }); - fs.writeFileSync(file, data); - resolve(true); - }); + await fs.mkdir(path.dirname(file), { recursive: true }); + await fs.writeFile(file, data); } export async function handleLogPath( @@ -244,18 +241,18 @@ export function dirSort(a: IFile, b: IFile): number { } } -export function readDirs( +export async function readDirs( dir: string, baseDir: string = '', blacklist: string[] = [], -): IFile[] { +): Promise { const relativePath = path.relative(baseDir, dir); - const files = fs.readdirSync(dir); - const result: IFile[] = files + const files = await fs.readdir(dir); + const result: IFile[] = await Promise.all(files .filter((x) => !blacklist.includes(x)) - .map((file: string) => { + .map(async (file: string) => { const subPath = path.join(dir, file); - const stats = fs.statSync(subPath); + const stats = await fs.stat(subPath); const key = path.join(relativePath, file); if (stats.isDirectory()) { return { @@ -264,7 +261,7 @@ export function readDirs( type: 'directory', parent: relativePath, mtime: stats.mtime.getTime(), - children: readDirs(subPath, baseDir).sort(dirSort), + children: (await readDirs(subPath, baseDir)).sort(dirSort), }; } return { @@ -276,22 +273,22 @@ export function readDirs( size: stats.size, mtime: stats.mtime.getTime(), }; - }); + })); return result.sort(dirSort); } -export function readDir( +export async function readDir( dir: string, baseDir: string = '', blacklist: string[] = [], ) { const relativePath = path.relative(baseDir, dir); - const files = fs.readdirSync(dir); + const files = await fs.readdir(dir); const result: any = files .filter((x) => !blacklist.includes(x)) - .map((file: string) => { + .map(async (file: string) => { const subPath = path.join(dir, file); - const stats = fs.statSync(subPath); + const stats = await fs.stat(subPath); const key = path.join(relativePath, file); return { title: file, @@ -303,24 +300,6 @@ export function readDir( return result; } -export async function emptyDir(path: string) { - const pathExist = await fileExist(path); - if (!pathExist) { - return; - } - const files = fs.readdirSync(path); - for (const file of files) { - const filePath = `${path}/${file}`; - const stats = fs.statSync(filePath); - if (stats.isDirectory()) { - await emptyDir(filePath); - } else { - fs.unlinkSync(filePath); - } - } - fs.rmdirSync(path); -} - export async function promiseExec(command: string): Promise { try { const { stderr, stdout } = await promisify(exec)(command, { maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' }); @@ -449,7 +428,7 @@ interface IVersion { } export async function parseVersion(path: string): Promise { - return load(await promisify(fs.readFile)(path, 'utf8')) as IVersion; + return load(await fs.readFile(path, 'utf8')) as IVersion; } export async function parseContentVersion(content: string): Promise { @@ -501,4 +480,15 @@ export function safeJSONParse(value?: string) { Logger.error('[JSON.parse失败]', error) return {}; } +} + +export async function rmPath(path: string) { + try { + const _exsit = await fileExist(path); + if (_exsit) { + await fs.rm(path, { force: true, recursive: true, maxRetries: 5 }); + } + } catch (error) { + Logger.error('[rmPath失败]', error) + } } \ No newline at end of file diff --git a/back/loaders/deps.ts b/back/loaders/deps.ts index 26f450a8980..b62194c7e1a 100644 --- a/back/loaders/deps.ts +++ b/back/loaders/deps.ts @@ -1,20 +1,17 @@ import path from 'path'; -import fs from 'fs'; +import fs from 'fs/promises'; import chokidar from 'chokidar'; import config from '../config/index'; -import { promiseExec } from '../config/util'; +import { fileExist, promiseExec, rmPath } from '../config/util'; -function linkToNodeModule(src: string, dst?: string) { +async function linkToNodeModule(src: string, dst?: string) { const target = path.join(config.rootPath, 'node_modules', dst || src); const source = path.join(config.rootPath, src); - fs.lstat(target, (err, stat) => { - if (!stat) { - fs.symlink(source, target, 'dir', (err) => { - if (err) throw err; - }); - } - }); + const _exist = await fileExist(target); + if (!_exist) { + await fs.symlink(source, target, 'dir'); + } } async function linkCommand() { @@ -34,16 +31,14 @@ async function linkCommand() { for (const link of linkShell) { const source = path.join(config.rootPath, 'shell', link.src); const target = path.join(commandDir, link.dest); - if (fs.existsSync(target)) { - fs.unlinkSync(target); - } - fs.symlink(source, target, (err) => { }); + await rmPath(target); + await fs.symlink(source, target); } } export default async (src: string = 'deps') => { await linkCommand(); - linkToNodeModule(src); + await linkToNodeModule(src); const source = path.join(config.rootPath, src); const watcher = chokidar.watch(source, { diff --git a/back/loaders/express.ts b/back/loaders/express.ts index 1de92cd896a..45fd64dc2fb 100644 --- a/back/loaders/express.ts +++ b/back/loaders/express.ts @@ -4,7 +4,7 @@ import cors from 'cors'; import routes from '../api'; import config from '../config'; import jwt, { UnauthorizedError } from 'express-jwt'; -import fs from 'fs'; +import fs from 'fs/promises'; import { getPlatform, getToken, safeJSONParse } from '../config/util'; import Container from 'typedi'; import OpenService from '../services/open'; @@ -29,7 +29,7 @@ export default ({ app }: { app: Application }) => { target: `http://0.0.0.0:${config.publicPort}/api`, changeOrigin: true, pathRewrite: { '/api/public': '' }, - logProvider: () => Logger + logProvider: () => Logger, }), ); @@ -83,7 +83,7 @@ export default ({ app }: { app: Application }) => { return next(); } - const data = fs.readFileSync(config.authConfigFile, 'utf8'); + const data = await fs.readFile(config.authConfigFile, 'utf8'); if (data && headerToken) { const { token = '', tokens = {} } = safeJSONParse(data); if (headerToken === token || tokens[req.platform] === headerToken) { diff --git a/back/loaders/initFile.ts b/back/loaders/initFile.ts index 3d9b7441ad1..861395c608a 100644 --- a/back/loaders/initFile.ts +++ b/back/loaders/initFile.ts @@ -1,7 +1,6 @@ -import fs from 'fs'; +import fs from 'fs/promises'; import path from 'path'; import os from 'os'; -import dotenv from 'dotenv'; import Logger from './logger'; import { fileExist } from '../config/util'; @@ -48,64 +47,70 @@ export default async () => { const TaskAfterFileExist = await fileExist(TaskAfterFile); if (!configDirExist) { - fs.mkdirSync(configPath); + await fs.mkdir(configPath); } if (!scriptDirExist) { - fs.mkdirSync(scriptPath); + await fs.mkdir(scriptPath); } if (!logDirExist) { - fs.mkdirSync(logPath); + await fs.mkdir(logPath); } if (!tmpDirExist) { - fs.mkdirSync(tmpPath); + await fs.mkdir(tmpPath); } if (!uploadDirExist) { - fs.mkdirSync(uploadPath); + await fs.mkdir(uploadPath); } if (!sshDirExist) { - fs.mkdirSync(sshPath); + await fs.mkdir(sshPath); } if (!bakDirExist) { - fs.mkdirSync(bakPath); + await fs.mkdir(bakPath); } if (!sshdDirExist) { - fs.mkdirSync(sshdPath); + await fs.mkdir(sshdPath); } if (!systemLogDirExist) { - fs.mkdirSync(systemLogPath); + await fs.mkdir(systemLogPath); } // 初始化文件 if (!authFileExist) { - fs.writeFileSync(authConfigFile, fs.readFileSync(sampleAuthFile)); + await fs.writeFile(authConfigFile, await fs.readFile(sampleAuthFile)); } if (!confFileExist) { - fs.writeFileSync(confFile, fs.readFileSync(sampleConfigFile)); + await fs.writeFile(confFile, await fs.readFile(sampleConfigFile)); } if (!scriptNotifyJsFileExist) { - fs.writeFileSync(scriptNotifyJsFile, fs.readFileSync(sampleNotifyJsFile)); + await fs.writeFile( + scriptNotifyJsFile, + await fs.readFile(sampleNotifyJsFile), + ); } if (!scriptNotifyPyFileExist) { - fs.writeFileSync(scriptNotifyPyFile, fs.readFileSync(sampleNotifyPyFile)); + await fs.writeFile( + scriptNotifyPyFile, + await fs.readFile(sampleNotifyPyFile), + ); } if (!TaskBeforeFileExist) { - fs.writeFileSync(TaskBeforeFile, fs.readFileSync(sampleTaskShellFile)); + await fs.writeFile(TaskBeforeFile, await fs.readFile(sampleTaskShellFile)); } if (!TaskAfterFileExist) { - fs.writeFileSync(TaskAfterFile, fs.readFileSync(sampleTaskShellFile)); + await fs.writeFile(TaskAfterFile, await fs.readFile(sampleTaskShellFile)); } Logger.info('✌️ Init file down'); diff --git a/back/loaders/sock.ts b/back/loaders/sock.ts index 47feeee52b6..a116f936bc3 100644 --- a/back/loaders/sock.ts +++ b/back/loaders/sock.ts @@ -1,22 +1,21 @@ import sockJs from 'sockjs'; import { Server } from 'http'; -import Logger from './logger'; import { Container } from 'typedi'; import SockService from '../services/sock'; import config from '../config/index'; -import fs from 'fs'; +import fs from 'fs/promises'; import { getPlatform, safeJSONParse } from '../config/util'; export default async ({ server }: { server: Server }) => { const echo = sockJs.createServer({ prefix: '/api/ws', log: () => {} }); const sockService = Container.get(SockService); - echo.on('connection', (conn) => { + echo.on('connection', async (conn) => { if (!conn.headers || !conn.url || !conn.pathname) { conn.close('404'); } - const data = fs.readFileSync(config.authConfigFile, 'utf8'); + const data = await fs.readFile(config.authConfigFile, 'utf8'); const platform = getPlatform(conn.headers['user-agent'] || '') || 'desktop'; const headerToken = conn.url.replace(`${conn.pathname}?token=`, ''); if (data) { diff --git a/back/services/cron.ts b/back/services/cron.ts index 8a7e7cc8d7d..a1b36079996 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -3,10 +3,15 @@ import winston from 'winston'; import config from '../config'; import { Crontab, CrontabModel, CrontabStatus } from '../data/cron'; import { exec, execSync } from 'child_process'; -import fs from 'fs'; +import fs from 'fs/promises'; import cron_parser from 'cron-parser'; -import { getFileContentByName, fileExist, killTask, getUniqPath, safeJSONParse } from '../config/util'; -import { promises, existsSync } from 'fs'; +import { + getFileContentByName, + fileExist, + killTask, + getUniqPath, + safeJSONParse, +} from '../config/util'; import { Op, where, col as colFn, FindOptions, fn } from 'sequelize'; import path from 'path'; import { TASK_PREFIX, QL_PREFIX } from '../config/const'; @@ -19,7 +24,7 @@ import omit from 'lodash/omit'; @Service() export default class CronService { - constructor(@Inject('logger') private logger: winston.Logger) { } + constructor(@Inject('logger') private logger: winston.Logger) {} private isSixCron(cron: Crontab) { const { schedule } = cron; @@ -35,7 +40,13 @@ export default class CronService { const doc = await this.insert(tab); if (this.isSixCron(doc) || doc.extra_schedules?.length) { await cronClient.addCron([ - { name: doc.name || '', id: String(doc.id), schedule: doc.schedule!, command: this.makeCommand(doc), extraSchedules: doc.extra_schedules || [] }, + { + name: doc.name || '', + id: String(doc.id), + schedule: doc.schedule!, + command: this.makeCommand(doc), + extraSchedules: doc.extra_schedules || [], + }, ]); } await this.set_crontab(); @@ -64,7 +75,7 @@ export default class CronService { id: String(newDoc.id), schedule: newDoc.schedule!, command: this.makeCommand(newDoc), - extraSchedules: newDoc.extra_schedules || [] + extraSchedules: newDoc.extra_schedules || [], }, ]); } @@ -107,7 +118,10 @@ export default class CronService { if (status === CrontabStatus.idle && log_path !== cron.log_path) { options = omit(options, ['status', 'log_path', 'pid']); } - await CrontabModel.update({ ...pickBy(options, (v) => v === 0 || !!v) }, { where: { id } }); + await CrontabModel.update( + { ...pickBy(options, (v) => v === 0 || !!v) }, + { where: { id } }, + ); } } @@ -396,35 +410,45 @@ export default class CronService { return taskLimit.runWithCronLimit(() => { return new Promise(async (resolve: any) => { const cron = await this.getDb({ id: cronId }); - const params = { name: cron.name, command: cron.command, schedule: cron.schedule, extraSchedules: cron.extra_schedules }; + const params = { + name: cron.name, + command: cron.command, + schedule: cron.schedule, + extraSchedules: cron.extra_schedules, + }; if (cron.status !== CrontabStatus.queued) { resolve(params); return; } - this.logger.info(`[panel][开始执行任务] 参数 ${JSON.stringify(params)}`); + this.logger.info( + `[panel][开始执行任务] 参数 ${JSON.stringify(params)}`, + ); let { id, command, log_path } = cron; const uniqPath = await getUniqPath(command, `${id}`); const logTime = dayjs().format('YYYY-MM-DD-HH-mm-ss-SSS'); const logDirPath = path.resolve(config.logPath, `${uniqPath}`); - if (log_path?.split('/')?.every(x => x !== uniqPath)) { - fs.mkdirSync(logDirPath, { recursive: true }); + if (log_path?.split('/')?.every((x) => x !== uniqPath)) { + await fs.mkdir(logDirPath, { recursive: true }); } const logPath = `${uniqPath}/${logTime}.log`; const absolutePath = path.resolve(config.logPath, `${logPath}`); - const cp = spawn(`real_log_path=${logPath} no_delay=true ${this.makeCommand(cron)}`, { shell: '/bin/bash' }); + const cp = spawn( + `real_log_path=${logPath} no_delay=true ${this.makeCommand(cron)}`, + { shell: '/bin/bash' }, + ); await CrontabModel.update( { status: CrontabStatus.running, pid: cp.pid, log_path: logPath }, { where: { id } }, ); - cp.stderr.on('data', (data) => { - fs.appendFileSync(`${absolutePath}`, `${data.toString()}`); + cp.stderr.on('data', async (data) => { + await fs.appendFile(`${absolutePath}`, `${data.toString()}`); }); - cp.on('error', (err) => { - fs.appendFileSync(`${absolutePath}`, `${JSON.stringify(err)}`); + cp.on('error', async (err) => { + await fs.appendFile(`${absolutePath}`, `${JSON.stringify(err)}`); }); cp.on('exit', async (code) => { @@ -454,7 +478,7 @@ export default class CronService { id: String(doc.id), schedule: doc.schedule!, command: this.makeCommand(doc), - extraSchedules: doc.extra_schedules || [] + extraSchedules: doc.extra_schedules || [], })); await cronClient.addCron(sixCron); await this.set_crontab(); @@ -469,7 +493,7 @@ export default class CronService { const absolutePath = path.resolve(config.logPath, `${doc.log_path}`); const logFileExist = doc.log_path && (await fileExist(absolutePath)); if (logFileExist) { - return getFileContentByName(`${absolutePath}`); + return await getFileContentByName(`${absolutePath}`); } else { return '任务未运行'; } @@ -483,15 +507,18 @@ export default class CronService { const relativeDir = path.dirname(`${doc.log_path}`); const dir = path.resolve(config.logPath, relativeDir); - if (existsSync(dir)) { - let files = await promises.readdir(dir); - return files - .map((x) => ({ - filename: x, - directory: relativeDir.replace(config.logPath, ''), - time: fs.statSync(`${dir}/${x}`).mtime.getTime(), - })) - .sort((a, b) => b.time - a.time); + const dirExist = await fileExist(dir); + if (dirExist) { + let files = await fs.readdir(dir); + return ( + await Promise.all( + files.map(async (x) => ({ + filename: x, + directory: relativeDir.replace(config.logPath, ''), + time: (await fs.stat(`${dir}/${x}`)).mtime.getTime(), + })), + ) + ).sort((a, b) => b.time - a.time); } else { return []; } @@ -502,13 +529,15 @@ export default class CronService { if (!command.startsWith(TASK_PREFIX) && !command.startsWith(QL_PREFIX)) { command = `${TASK_PREFIX}${tab.command}`; } - let commandVariable = `no_tee=true ID=${tab.id} ` + let commandVariable = `no_tee=true ID=${tab.id} `; if (tab.task_before) { - commandVariable += `task_before='${tab.task_before.replace(/'/g, "'\\''") + commandVariable += `task_before='${tab.task_before + .replace(/'/g, "'\\''") .trim()}' `; } if (tab.task_after) { - commandVariable += `task_after='${tab.task_after.replace(/'/g, "'\\''") + commandVariable += `task_after='${tab.task_after + .replace(/'/g, "'\\''") .trim()}' `; } @@ -521,7 +550,11 @@ export default class CronService { var crontab_string = ''; tabs.data.forEach((tab) => { const _schedule = tab.schedule && tab.schedule.split(/ +/); - if (tab.isDisabled === 1 || _schedule!.length !== 5 || tab.extra_schedules?.length) { + if ( + tab.isDisabled === 1 || + _schedule!.length !== 5 || + tab.extra_schedules?.length + ) { crontab_string += '# '; crontab_string += tab.schedule; crontab_string += ' '; @@ -535,7 +568,7 @@ export default class CronService { } }); - fs.writeFileSync(config.crontabFile, crontab_string); + await fs.writeFile(config.crontabFile, crontab_string); execSync(`crontab ${config.crontabFile}`); await CrontabModel.update({ saved: true }, { where: {} }); @@ -586,7 +619,7 @@ export default class CronService { id: String(doc.id), schedule: doc.schedule!, command: this.makeCommand(doc), - extraSchedules: doc.extra_schedules || [] + extraSchedules: doc.extra_schedules || [], })); await cronClient.addCron(sixCron); } diff --git a/back/services/env.ts b/back/services/env.ts index 3ceb3cbc73d..7b33f3314b9 100644 --- a/back/services/env.ts +++ b/back/services/env.ts @@ -1,7 +1,7 @@ import { Service, Inject } from 'typedi'; import winston from 'winston'; import config from '../config'; -import * as fs from 'fs'; +import * as fs from 'fs/promises'; import { Env, EnvModel, @@ -208,6 +208,6 @@ export default class EnvService { } } } - fs.writeFileSync(config.envFile, env_string); + await fs.writeFile(config.envFile, env_string); } } diff --git a/back/services/script.ts b/back/services/script.ts index 16bc0e7b7aa..10b19e720ce 100644 --- a/back/services/script.ts +++ b/back/services/script.ts @@ -1,13 +1,12 @@ import { Service, Inject } from 'typedi'; import winston from 'winston'; -import fs from 'fs'; import path from 'path'; import SockService from './sock'; import CronService from './cron'; import ScheduleService, { TaskCallbacks } from './schedule'; import config from '../config'; import { TASK_COMMAND } from '../config/const'; -import { getPid, killTask } from '../config/util'; +import { getPid, killTask, rmPath } from '../config/util'; @Service() export default class ScriptService { @@ -16,14 +15,12 @@ export default class ScriptService { private sockService: SockService, private cronService: CronService, private scheduleService: ScheduleService, - ) { } + ) {} private taskCallbacks(filePath: string): TaskCallbacks { return { onEnd: async (cp, endTime, diff) => { - try { - fs.unlinkSync(filePath); - } catch (error) { } + await rmPath(filePath); }, onError: async (message: string) => { this.sockService.sendMessage({ @@ -56,11 +53,11 @@ export default class ScriptService { public async stopScript(filePath: string, pid: number) { if (!pid) { const relativePath = path.relative(config.scriptPath, filePath); - pid = await getPid(`${TASK_COMMAND} ${relativePath} now`) as number; + pid = (await getPid(`${TASK_COMMAND} ${relativePath} now`)) as number; } try { await killTask(pid); - } catch (error) { } + } catch (error) {} return { code: 200 }; } diff --git a/back/services/sshKey.ts b/back/services/sshKey.ts index 0dee5ad36c6..62244d2bd4c 100644 --- a/back/services/sshKey.ts +++ b/back/services/sshKey.ts @@ -1,11 +1,12 @@ import { Service, Inject } from 'typedi'; import winston from 'winston'; -import fs, { existsSync } from 'fs'; +import fs from 'fs/promises'; import os from 'os'; import path from 'path'; import { Subscription } from '../data/subscription'; import { formatUrl } from '../config/subscription'; import config from '../config'; +import { fileExist, rmPath } from '../config/util'; @Service() export default class SshKeyService { @@ -18,15 +19,16 @@ export default class SshKeyService { this.initSshConfigFile(); } - private initSshConfigFile() { + private async initSshConfigFile() { let config = ''; - if (existsSync(this.sshConfigFilePath)) { - config = fs.readFileSync(this.sshConfigFilePath, { encoding: 'utf-8' }); + const _exist = await fileExist(this.sshConfigFilePath); + if (_exist) { + config = await fs.readFile(this.sshConfigFilePath, { encoding: 'utf-8' }); } else { - fs.writeFileSync(this.sshConfigFilePath, ''); + await fs.writeFile(this.sshConfigFilePath, ''); } if (!config.includes(this.sshConfigHeader)) { - fs.writeFileSync( + await fs.writeFile( this.sshConfigFilePath, `${this.sshConfigHeader}\n\n${config}`, { encoding: 'utf-8' }, @@ -34,9 +36,12 @@ export default class SshKeyService { } } - private generatePrivateKeyFile(alias: string, key: string): void { + private async generatePrivateKeyFile( + alias: string, + key: string, + ): Promise { try { - fs.writeFileSync(path.join(this.sshPath, alias), `${key}${os.EOL}`, { + await fs.writeFile(path.join(this.sshPath, alias), `${key}${os.EOL}`, { encoding: 'utf8', mode: '400', }); @@ -45,18 +50,20 @@ export default class SshKeyService { } } - private removePrivateKeyFile(alias: string): void { + private async removePrivateKeyFile(alias: string): Promise { try { const filePath = path.join(this.sshPath, alias); - if (existsSync(filePath)) { - fs.unlinkSync(filePath); - } + await rmPath(filePath); } catch (error) { this.logger.error('删除私钥文件失败', error); } } - private generateSingleSshConfig(alias: string, host: string, proxy?: string) { + private async generateSingleSshConfig( + alias: string, + host: string, + proxy?: string, + ) { if (host === 'github.com') { host = `ssh.github.com\n Port 443\n HostkeyAlgorithms +ssh-rsa`; } @@ -67,49 +74,51 @@ export default class SshKeyService { this.sshPath, alias, )}\n StrictHostKeyChecking no\n${proxyStr}`; - fs.writeFileSync(`${path.join(this.sshPath, `${alias}.config`)}`, config, { - encoding: 'utf8', - }); + await fs.writeFile( + `${path.join(this.sshPath, `${alias}.config`)}`, + config, + { + encoding: 'utf8', + }, + ); } - private removeSshConfig(alias: string) { + private async removeSshConfig(alias: string) { try { const filePath = path.join(this.sshPath, `${alias}.config`); - if (existsSync(filePath)) { - fs.unlinkSync(filePath); - } + await rmPath(filePath); } catch (error) { this.logger.error(`删除ssh配置文件${alias}失败`, error); } } - public addSSHKey( + public async addSSHKey( key: string, alias: string, host: string, proxy?: string, - ): void { - this.generatePrivateKeyFile(alias, key); - this.generateSingleSshConfig(alias, host, proxy); + ): Promise { + await this.generatePrivateKeyFile(alias, key); + await this.generateSingleSshConfig(alias, host, proxy); } - public removeSSHKey(alias: string, host: string, proxy?: string): void { - this.removePrivateKeyFile(alias); - this.removeSshConfig(alias); + public async removeSSHKey(alias: string, host: string, proxy?: string): Promise { + await this.removePrivateKeyFile(alias); + await this.removeSshConfig(alias); } - public setSshConfig(docs: Subscription[]) { + public async setSshConfig(docs: Subscription[]) { for (const doc of docs) { if (doc.type === 'private-repo' && doc.pull_type === 'ssh-key') { const { alias, proxy } = doc; const { host } = formatUrl(doc); - this.removePrivateKeyFile(alias); - this.removeSshConfig(alias); - this.generatePrivateKeyFile( + await this.removePrivateKeyFile(alias); + await this.removeSshConfig(alias); + await this.generatePrivateKeyFile( alias, (doc.pull_option as any).private_key, ); - this.generateSingleSshConfig(alias, host, proxy); + await this.generateSingleSshConfig(alias, host, proxy); } } } diff --git a/back/services/subscription.ts b/back/services/subscription.ts index 14b20ad1dcd..0e8dd3a1476 100644 --- a/back/services/subscription.ts +++ b/back/services/subscription.ts @@ -7,7 +7,6 @@ import { SubscriptionStatus, } from '../data/subscription'; import { ChildProcessWithoutNullStreams } from 'child_process'; -import fs from 'fs'; import { getFileContentByName, concurrentRun, @@ -16,9 +15,9 @@ import { killTask, handleLogPath, promiseExec, - emptyDir, + rmPath, } from '../config/util'; -import { promises, existsSync } from 'fs'; +import fs from 'fs/promises'; import { FindOptions, Op } from 'sequelize'; import path, { join } from 'path'; import ScheduleService, { TaskCallbacks } from './schedule'; @@ -39,7 +38,7 @@ export default class SubscriptionService { private sockService: SockService, private sshKeyService: SshKeyService, private crontabService: CrontabService, - ) { } + ) {} public async list(searchText?: string): Promise { let query = {}; @@ -107,7 +106,7 @@ export default class SubscriptionService { public async setSshConfig() { const docs = await SubscriptionModel.findAll(); - this.sshKeyService.setSshConfig(docs); + await this.sshKeyService.setSshConfig(docs); } private taskCallbacks(doc: Subscription): TaskCallbacks { @@ -131,7 +130,7 @@ export default class SubscriptionService { let beforeStr = ''; try { if (doc.sub_before) { - fs.appendFileSync(absolutePath, `\n## 执行before命令...\n\n`); + await fs.appendFile(absolutePath, `\n## 执行before命令...\n\n`); beforeStr = await promiseExec(doc.sub_before); } } catch (error: any) { @@ -139,7 +138,7 @@ export default class SubscriptionService { (error.stderr && error.stderr.toString()) || JSON.stringify(error); } if (beforeStr) { - fs.appendFileSync(absolutePath, `${beforeStr}\n`); + await fs.appendFile(absolutePath, `${beforeStr}\n`); } }, onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => { @@ -158,7 +157,7 @@ export default class SubscriptionService { let afterStr = ''; try { if (sub.sub_after) { - fs.appendFileSync(absolutePath, `\n\n## 执行after命令...\n\n`); + await fs.appendFile(absolutePath, `\n\n## 执行after命令...\n\n`); afterStr = await promiseExec(sub.sub_after); } } catch (error: any) { @@ -166,10 +165,10 @@ export default class SubscriptionService { (error.stderr && error.stderr.toString()) || JSON.stringify(error); } if (afterStr) { - fs.appendFileSync(absolutePath, `${afterStr}\n`); + await fs.appendFile(absolutePath, `${afterStr}\n`); } - fs.appendFileSync( + await fs.appendFile( absolutePath, `\n## 执行结束... ${endTime.format( 'YYYY-MM-DD HH:mm:ss', @@ -190,12 +189,12 @@ export default class SubscriptionService { onError: async (message: string) => { const sub = await this.getDb({ id: doc.id }); const absolutePath = await handleLogPath(sub.log_path as string); - fs.appendFileSync(absolutePath, `\n${message}`); + await fs.appendFile(absolutePath, `\n${message}`); }, onLog: async (message: string) => { const sub = await this.getDb({ id: doc.id }); const absolutePath = await handleLogPath(sub.log_path as string); - fs.appendFileSync(absolutePath, `\n${message}`); + await fs.appendFile(absolutePath, `\n${message}`); }, }; } @@ -268,11 +267,11 @@ export default class SubscriptionService { if (query?.force === true) { const crons = await CrontabModel.findAll({ where: { sub_id: ids } }); if (crons?.length) { - await this.crontabService.remove(crons.map(x => x.id!)) + await this.crontabService.remove(crons.map((x) => x.id!)); } for (const doc of docs) { const filePath = join(config.scriptPath, doc.alias); - emptyDir(filePath); + await rmPath(filePath); } } } @@ -323,7 +322,7 @@ export default class SubscriptionService { this.scheduleService.runTask(command, this.taskCallbacks(subscription), { name: subscription.name, schedule: subscription.schedule, - command + command, }); } @@ -352,7 +351,7 @@ export default class SubscriptionService { } const absolutePath = await handleLogPath(doc.log_path as string); - return getFileContentByName(absolutePath); + return await getFileContentByName(absolutePath); } public async logs(id: number) { @@ -364,15 +363,18 @@ export default class SubscriptionService { if (doc.log_path) { const relativeDir = path.dirname(`${doc.log_path}`); const dir = path.resolve(config.logPath, relativeDir); - if (existsSync(dir)) { - let files = await promises.readdir(dir); - return files - .map((x) => ({ - filename: x, - directory: relativeDir.replace(config.logPath, ''), - time: fs.statSync(`${dir}/${x}`).mtime.getTime(), - })) - .sort((a, b) => b.time - a.time); + const _exist = await fileExist(dir); + if (_exist) { + let files = await fs.readdir(dir); + return ( + await Promise.all( + files.map(async (x) => ({ + filename: x, + directory: relativeDir.replace(config.logPath, ''), + time: (await fs.stat(`${dir}/${x}`)).mtime.getTime(), + })), + ) + ).sort((a, b) => b.time - a.time); } } } diff --git a/back/services/system.ts b/back/services/system.ts index c2f67ae7e6e..84012cb19e4 100644 --- a/back/services/system.ts +++ b/back/services/system.ts @@ -39,7 +39,7 @@ export default class SystemService { @Inject('logger') private logger: winston.Logger, private scheduleService: ScheduleService, private sockService: SockService, - ) { } + ) {} public async getSystemConfig() { const doc = await this.getDb({ type: AuthDataType.systemConfig }); @@ -114,7 +114,7 @@ export default class SystemService { }, ); lastVersionContent = await parseContentVersion(result.body); - } catch (error) { } + } catch (error) {} if (!lastVersionContent) { lastVersionContent = currentVersionContent; @@ -234,7 +234,7 @@ export default class SystemService { callback, { command, - } + }, ); } @@ -283,7 +283,7 @@ export default class SystemService { } public async getSystemLog(res: Response) { - const result = readDirs(config.systemLogPath, config.systemLogPath); + const result = await readDirs(config.systemLogPath, config.systemLogPath); const logs = result.reverse().filter((x) => x.title.endsWith('.log')); res.set({ 'Content-Length': sum(logs.map((x) => x.size)), diff --git a/back/services/user.ts b/back/services/user.ts index 607c778e9a6..3ed9bd8537b 100644 --- a/back/services/user.ts +++ b/back/services/user.ts @@ -8,7 +8,7 @@ import { safeJSONParse, } from '../config/util'; import config from '../config'; -import * as fs from 'fs'; +import * as fs from 'fs/promises'; import jwt from 'jsonwebtoken'; import { authenticator } from '@otplib/preset-default'; import { @@ -44,12 +44,13 @@ export default class UserService { req: Request, needTwoFactor = true, ): Promise { - if (!fs.existsSync(config.authConfigFile)) { + const _exist = await fileExist(config.authConfigFile); + if (!_exist) { return this.initAuthInfo(); } let { username, password } = payloads; - const content = this.getAuthInfo(); + const content = await this.getAuthInfo(); const timestamp = Date.now(); if (content) { let { @@ -187,7 +188,7 @@ export default class UserService { } public async logout(platform: string): Promise { - const authInfo = this.getAuthInfo(); + const authInfo = await this.getAuthInfo(); this.updateAuthInfo(authInfo, { token: '', tokens: { ...authInfo.tokens, [platform]: '' }, @@ -217,8 +218,8 @@ export default class UserService { return doc; } - private initAuthInfo() { - fs.writeFileSync( + private async initAuthInfo() { + await fs.writeFile( config.authConfigFile, JSON.stringify({ username: 'admin', @@ -255,7 +256,7 @@ export default class UserService { public async getUserInfo(): Promise { const authFileExist = await fileExist(config.authConfigFile); if (!authFileExist) { - fs.writeFileSync( + await fs.writeFile( config.authConfigFile, JSON.stringify({ username: 'admin', @@ -266,16 +267,16 @@ export default class UserService { return this.getAuthInfo(); } - public initTwoFactor() { + public async initTwoFactor() { const secret = authenticator.generateSecret(); - const authInfo = this.getAuthInfo(); + const authInfo = await this.getAuthInfo(); const otpauth = authenticator.keyuri(authInfo.username, 'qinglong', secret); this.updateAuthInfo(authInfo, { twoFactorSecret: secret }); return { secret, url: otpauth }; } - public activeTwoFactor(code: string) { - const authInfo = this.getAuthInfo(); + public async activeTwoFactor(code: string) { + const authInfo = await this.getAuthInfo(); const isValid = authenticator.verify({ token: code, secret: authInfo.twoFactorSecret, @@ -294,7 +295,7 @@ export default class UserService { }: { username: string; password: string; code: string }, req: any, ) { - const authInfo = this.getAuthInfo(); + const authInfo = await this.getAuthInfo(); const { isTwoFactorChecking, twoFactorSecret } = authInfo; if (!isTwoFactorChecking) { return { code: 450, message: '未知错误' }; @@ -326,13 +327,13 @@ export default class UserService { return true; } - private getAuthInfo() { - const content = fs.readFileSync(config.authConfigFile, 'utf8'); + private async getAuthInfo() { + const content = await fs.readFile(config.authConfigFile, 'utf8'); return safeJSONParse(content); } - private updateAuthInfo(authInfo: any, info: any) { - fs.writeFileSync( + private async updateAuthInfo(authInfo: any, info: any) { + await fs.writeFile( config.authConfigFile, JSON.stringify({ ...authInfo, ...info }), ); diff --git a/shell/env.sh b/shell/env.sh old mode 100644 new mode 100755