fs 文件操作替换为 fs.promise

This commit is contained in:
whyour 2023-11-01 16:44:34 +08:00
parent 66a2769e7c
commit 20f615eadf
17 changed files with 284 additions and 260 deletions

View File

@ -3,7 +3,7 @@ import { Router, Request, Response, NextFunction } from 'express';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { Logger } from 'winston'; import { Logger } from 'winston';
import config from '../config'; import config from '../config';
import * as fs from 'fs'; import * as fs from 'fs/promises';
import { celebrate, Joi } from 'celebrate'; import { celebrate, Joi } from 'celebrate';
import { join } from 'path'; import { join } from 'path';
const route = Router(); const route = Router();
@ -16,7 +16,7 @@ export default (app: Router) => {
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
const fileList = fs.readdirSync(config.configPath, 'utf-8'); const fileList = await fs.readdir(config.configPath, 'utf-8');
res.send({ res.send({
code: 200, code: 200,
data: fileList data: fileList
@ -41,11 +41,11 @@ export default (app: Router) => {
res.send({ code: 403, message: '文件无法访问' }); res.send({ code: 403, message: '文件无法访问' });
} }
if (req.params.file.includes('sample')) { if (req.params.file.includes('sample')) {
content = getFileContentByName( content = await getFileContentByName(
join(config.samplePath, req.params.file), join(config.samplePath, req.params.file),
); );
} else { } else {
content = getFileContentByName( content = await getFileContentByName(
join(config.configPath, req.params.file), join(config.configPath, req.params.file),
); );
} }
@ -72,7 +72,7 @@ export default (app: Router) => {
res.send({ code: 403, message: '文件无法访问' }); res.send({ code: 403, message: '文件无法访问' });
} }
const path = join(config.configPath, name); const path = join(config.configPath, name);
fs.writeFileSync(path, content); await fs.writeFile(path, content);
res.send({ code: 200, message: '保存成功' }); res.send({ code: 200, message: '保存成功' });
} catch (e) { } catch (e) {
return next(e); return next(e);

View File

@ -3,7 +3,7 @@ import { Container } from 'typedi';
import { Logger } from 'winston'; import { Logger } from 'winston';
import * as fs from 'fs'; import * as fs from 'fs';
import config from '../config'; import config from '../config';
import { emptyDir, getFileContentByName, readDirs } from '../config/util'; import { getFileContentByName, readDirs, rmPath } from '../config/util';
import { join } from 'path'; import { join } from 'path';
import { celebrate, Joi } from 'celebrate'; import { celebrate, Joi } from 'celebrate';
const route = Router(); const route = Router();
@ -39,7 +39,7 @@ export default (app: Router) => {
(req.query.path || '') as string, (req.query.path || '') as string,
req.params.file, req.params.file,
); );
const content = getFileContentByName(filePath); const content = await getFileContentByName(filePath);
res.send({ code: 200, data: content }); res.send({ code: 200, data: content });
} catch (e) { } catch (e) {
return next(e); return next(e);
@ -64,11 +64,7 @@ export default (app: Router) => {
type: string; type: string;
}; };
const filePath = join(config.logPath, path, filename); const filePath = join(config.logPath, path, filename);
if (type === 'directory') { await rmPath(filePath);
await emptyDir(filePath);
} else {
fs.unlinkSync(filePath);
}
res.send({ code: 200 }); res.send({ code: 200 });
} catch (e) { } catch (e) {
return next(e); return next(e);

View File

@ -4,13 +4,13 @@ import {
readDirs, readDirs,
getLastModifyFilePath, getLastModifyFilePath,
readDir, readDir,
emptyDir, rmPath,
} from '../config/util'; } from '../config/util';
import { Router, Request, Response, NextFunction } from 'express'; import { Router, Request, Response, NextFunction } from 'express';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { Logger } from 'winston'; import { Logger } from 'winston';
import config from '../config'; import config from '../config';
import * as fs from 'fs'; import * as fs from 'fs/promises';
import { celebrate, Joi } from 'celebrate'; import { celebrate, Joi } from 'celebrate';
import path, { join, parse } from 'path'; import path, { join, parse } from 'path';
import ScriptService from '../services/script'; import ScriptService from '../services/script';
@ -40,9 +40,13 @@ export default (app: Router) => {
config.scriptPath, config.scriptPath,
req.query.path as string, req.query.path as string,
); );
result = readDir(targetPath, config.scriptPath, blacklist); result = await readDir(targetPath, config.scriptPath, blacklist);
} else { } else {
result = readDirs(config.scriptPath, config.scriptPath, blacklist); result = await readDirs(
config.scriptPath,
config.scriptPath,
blacklist,
);
} }
res.send({ res.send({
code: 200, code: 200,
@ -64,7 +68,7 @@ export default (app: Router) => {
req.query.path as string, req.query.path as string,
req.params.file, req.params.file,
); );
const content = getFileContentByName(filePath); const content = await getFileContentByName(filePath);
res.send({ code: 200, data: content }); res.send({ code: 200, data: content });
} catch (e) { } catch (e) {
return next(e); return next(e);
@ -104,12 +108,12 @@ export default (app: Router) => {
} }
if (req.file) { 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 }); return res.send({ code: 200 });
} }
if (directory) { if (directory) {
fs.mkdirSync(join(path, directory), { recursive: true }); await fs.mkdir(join(path, directory), { recursive: true });
return res.send({ code: 200 }); return res.send({ code: 200 });
} }
@ -121,16 +125,17 @@ export default (app: Router) => {
`${originFilename.replace(/\//g, '')}`, `${originFilename.replace(/\//g, '')}`,
); );
const filePath = join(path, `${filename.replace(/\//g, '')}`); const filePath = join(path, `${filename.replace(/\//g, '')}`);
if (fs.existsSync(originFilePath)) { const fileExists = await fileExist(filePath);
fs.copyFileSync( if (fileExists) {
await fs.copyFile(
originFilePath, originFilePath,
join(config.bakPath, originFilename.replace(/\//g, '')), join(config.bakPath, originFilename.replace(/\//g, '')),
); );
if (filename !== originFilename) { if (filename !== originFilename) {
fs.unlinkSync(originFilePath); await rmPath(originFilePath);
} }
} }
fs.writeFileSync(filePath, content); await fs.writeFile(filePath, content);
return res.send({ code: 200 }); return res.send({ code: 200 });
} catch (e) { } catch (e) {
return next(e); return next(e);
@ -156,7 +161,7 @@ export default (app: Router) => {
path: string; path: string;
}; };
const filePath = join(config.scriptPath, path, filename); const filePath = join(config.scriptPath, path, filename);
fs.writeFileSync(filePath, content); await fs.writeFile(filePath, content);
return res.send({ code: 200 }); return res.send({ code: 200 });
} catch (e) { } catch (e) {
return next(e); return next(e);
@ -182,11 +187,7 @@ export default (app: Router) => {
type: string; type: string;
}; };
const filePath = join(config.scriptPath, path, filename); const filePath = join(config.scriptPath, path, filename);
if (type === 'directory') { await rmPath(filePath);
await emptyDir(filePath);
} else {
fs.unlinkSync(filePath);
}
res.send({ code: 200 }); res.send({ code: 200 });
} catch (e) { } catch (e) {
return next(e); return next(e);
@ -239,7 +240,7 @@ export default (app: Router) => {
let { filename, content, path } = req.body; let { filename, content, path } = req.body;
const { name, ext } = parse(filename); const { name, ext } = parse(filename);
const filePath = join(config.scriptPath, path, `${name}.swap${ext}`); 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 scriptService = Container.get(ScriptService);
const result = await scriptService.runScript(filePath); const result = await scriptService.runScript(filePath);
@ -269,7 +270,7 @@ export default (app: Router) => {
const scriptService = Container.get(ScriptService); const scriptService = Container.get(ScriptService);
const result = await scriptService.stopScript(filePath, pid); const result = await scriptService.stopScript(filePath, pid);
setTimeout(() => { setTimeout(() => {
emptyDir(logPath); rmPath(logPath);
}, 3000); }, 3000);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
@ -297,7 +298,7 @@ export default (app: Router) => {
}; };
const filePath = join(config.scriptPath, path, filename); const filePath = join(config.scriptPath, path, filename);
const newPath = join(config.scriptPath, path, newFilename); const newPath = join(config.scriptPath, path, newFilename);
fs.renameSync(filePath, newPath); await fs.rename(filePath, newPath);
res.send({ code: 200 }); res.send({ code: 200 });
} catch (e) { } catch (e) {
return next(e); return next(e);

View File

@ -1,7 +1,7 @@
import { Router, Request, Response, NextFunction } from 'express'; import { Router, Request, Response, NextFunction } from 'express';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { Logger } from 'winston'; import { Logger } from 'winston';
import * as fs from 'fs'; import * as fs from 'fs/promises';
import config from '../config'; import config from '../config';
import SystemService from '../services/system'; import SystemService from '../services/system';
import { celebrate, Joi } from 'celebrate'; import { celebrate, Joi } from 'celebrate';
@ -174,7 +174,7 @@ export default (app: Router) => {
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
try { try {
const systemService = Container.get(SystemService); 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`; 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); let id = await promiseExec(idStr);
const uniqPath = await getUniqPath(command, id); const uniqPath = await getUniqPath(command, id);
@ -193,12 +193,12 @@ export default (app: Router) => {
onError: async (message: string) => { onError: async (message: string) => {
res.write(`\n${message}`); res.write(`\n${message}`);
const absolutePath = await handleLogPath(logPath); const absolutePath = await handleLogPath(logPath);
fs.appendFileSync(absolutePath, `\n${message}`); await fs.appendFile(absolutePath, `\n${message}`);
}, },
onLog: async (message: string) => { onLog: async (message: string) => {
res.write(`\n${message}`); res.write(`\n${message}`);
const absolutePath = await handleLogPath(logPath); 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( route.get('/log', async (req: Request, res: Response, next: NextFunction) => {
'/log',
async (req: Request, res: Response, next: NextFunction) => {
try { try {
const systemService = Container.get(SystemService); const systemService = Container.get(SystemService);
await systemService.getSystemLog(res); await systemService.getSystemLog(res);
} catch (e) { } catch (e) {
return next(e); return next(e);
} }
}, });
);
}; };

View File

@ -1,4 +1,4 @@
import * as fs from 'fs'; import * as fs from 'fs/promises';
import * as path from 'path'; import * as path from 'path';
import got from 'got'; import got from 'got';
import iconv from 'iconv-lite'; import iconv from 'iconv-lite';
@ -13,22 +13,24 @@ import Logger from '../loaders/logger';
export * from './share'; export * from './share';
export function getFileContentByName(fileName: string) { export async function getFileContentByName(fileName: string) {
if (fs.existsSync(fileName)) { const _exsit = await fileExist(fileName);
return fs.readFileSync(fileName, 'utf8'); if (_exsit) {
return await fs.readFile(fileName, 'utf8');
} }
return ''; return '';
} }
export function getLastModifyFilePath(dir: string) { export async function getLastModifyFilePath(dir: string) {
let filePath = ''; let filePath = '';
if (fs.existsSync(dir)) { const _exsit = await fileExist(dir);
const arr = fs.readdirSync(dir); if (_exsit) {
const arr = await fs.readdir(dir);
arr.forEach((item) => { arr.forEach(async (item) => {
const fullpath = path.join(dir, item); const fullpath = path.join(dir, item);
const stats = fs.statSync(fullpath); const stats = await fs.stat(fullpath);
if (stats.isFile()) { if (stats.isFile()) {
if (stats.mtimeMs >= 0) { if (stats.mtimeMs >= 0) {
filePath = fullpath; filePath = fullpath;
@ -152,22 +154,17 @@ export function getPlatform(userAgent: string): 'mobile' | 'desktop' {
} }
export async function fileExist(file: any) { export async function fileExist(file: any) {
return new Promise((resolve) => {
try { try {
fs.accessSync(file); await fs.access(file);
resolve(true); return true;
} catch (error) { } catch (error) {
resolve(false); return false;
} }
});
} }
export async function createFile(file: string, data: string = '') { export async function createFile(file: string, data: string = '') {
return new Promise((resolve) => { await fs.mkdir(path.dirname(file), { recursive: true });
fs.mkdirSync(path.dirname(file), { recursive: true }); await fs.writeFile(file, data);
fs.writeFileSync(file, data);
resolve(true);
});
} }
export async function handleLogPath( 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, dir: string,
baseDir: string = '', baseDir: string = '',
blacklist: string[] = [], blacklist: string[] = [],
): IFile[] { ): Promise<IFile[]> {
const relativePath = path.relative(baseDir, dir); const relativePath = path.relative(baseDir, dir);
const files = fs.readdirSync(dir); const files = await fs.readdir(dir);
const result: IFile[] = files const result: IFile[] = await Promise.all(files
.filter((x) => !blacklist.includes(x)) .filter((x) => !blacklist.includes(x))
.map((file: string) => { .map(async (file: string) => {
const subPath = path.join(dir, file); const subPath = path.join(dir, file);
const stats = fs.statSync(subPath); const stats = await fs.stat(subPath);
const key = path.join(relativePath, file); const key = path.join(relativePath, file);
if (stats.isDirectory()) { if (stats.isDirectory()) {
return { return {
@ -264,7 +261,7 @@ export function readDirs(
type: 'directory', type: 'directory',
parent: relativePath, parent: relativePath,
mtime: stats.mtime.getTime(), mtime: stats.mtime.getTime(),
children: readDirs(subPath, baseDir).sort(dirSort), children: (await readDirs(subPath, baseDir)).sort(dirSort),
}; };
} }
return { return {
@ -276,22 +273,22 @@ export function readDirs(
size: stats.size, size: stats.size,
mtime: stats.mtime.getTime(), mtime: stats.mtime.getTime(),
}; };
}); }));
return result.sort(dirSort); return result.sort(dirSort);
} }
export function readDir( export async function readDir(
dir: string, dir: string,
baseDir: string = '', baseDir: string = '',
blacklist: string[] = [], blacklist: string[] = [],
) { ) {
const relativePath = path.relative(baseDir, dir); const relativePath = path.relative(baseDir, dir);
const files = fs.readdirSync(dir); const files = await fs.readdir(dir);
const result: any = files const result: any = files
.filter((x) => !blacklist.includes(x)) .filter((x) => !blacklist.includes(x))
.map((file: string) => { .map(async (file: string) => {
const subPath = path.join(dir, file); const subPath = path.join(dir, file);
const stats = fs.statSync(subPath); const stats = await fs.stat(subPath);
const key = path.join(relativePath, file); const key = path.join(relativePath, file);
return { return {
title: file, title: file,
@ -303,24 +300,6 @@ export function readDir(
return result; 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<string> { export async function promiseExec(command: string): Promise<string> {
try { try {
const { stderr, stdout } = await promisify(exec)(command, { maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' }); 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<IVersion> { export async function parseVersion(path: string): Promise<IVersion> {
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<IVersion> { export async function parseContentVersion(content: string): Promise<IVersion> {
@ -502,3 +481,14 @@ export function safeJSONParse(value?: string) {
return {}; 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)
}
}

View File

@ -1,20 +1,17 @@
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs/promises';
import chokidar from 'chokidar'; import chokidar from 'chokidar';
import config from '../config/index'; 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 target = path.join(config.rootPath, 'node_modules', dst || src);
const source = path.join(config.rootPath, src); const source = path.join(config.rootPath, src);
fs.lstat(target, (err, stat) => { const _exist = await fileExist(target);
if (!stat) { if (!_exist) {
fs.symlink(source, target, 'dir', (err) => { await fs.symlink(source, target, 'dir');
if (err) throw err;
});
} }
});
} }
async function linkCommand() { async function linkCommand() {
@ -34,16 +31,14 @@ async function linkCommand() {
for (const link of linkShell) { for (const link of linkShell) {
const source = path.join(config.rootPath, 'shell', link.src); const source = path.join(config.rootPath, 'shell', link.src);
const target = path.join(commandDir, link.dest); const target = path.join(commandDir, link.dest);
if (fs.existsSync(target)) { await rmPath(target);
fs.unlinkSync(target); await fs.symlink(source, target);
}
fs.symlink(source, target, (err) => { });
} }
} }
export default async (src: string = 'deps') => { export default async (src: string = 'deps') => {
await linkCommand(); await linkCommand();
linkToNodeModule(src); await linkToNodeModule(src);
const source = path.join(config.rootPath, src); const source = path.join(config.rootPath, src);
const watcher = chokidar.watch(source, { const watcher = chokidar.watch(source, {

View File

@ -4,7 +4,7 @@ import cors from 'cors';
import routes from '../api'; import routes from '../api';
import config from '../config'; import config from '../config';
import jwt, { UnauthorizedError } from 'express-jwt'; import jwt, { UnauthorizedError } from 'express-jwt';
import fs from 'fs'; import fs from 'fs/promises';
import { getPlatform, getToken, safeJSONParse } from '../config/util'; import { getPlatform, getToken, safeJSONParse } from '../config/util';
import Container from 'typedi'; import Container from 'typedi';
import OpenService from '../services/open'; import OpenService from '../services/open';
@ -29,7 +29,7 @@ export default ({ app }: { app: Application }) => {
target: `http://0.0.0.0:${config.publicPort}/api`, target: `http://0.0.0.0:${config.publicPort}/api`,
changeOrigin: true, changeOrigin: true,
pathRewrite: { '/api/public': '' }, pathRewrite: { '/api/public': '' },
logProvider: () => Logger logProvider: () => Logger,
}), }),
); );
@ -83,7 +83,7 @@ export default ({ app }: { app: Application }) => {
return next(); return next();
} }
const data = fs.readFileSync(config.authConfigFile, 'utf8'); const data = await fs.readFile(config.authConfigFile, 'utf8');
if (data && headerToken) { if (data && headerToken) {
const { token = '', tokens = {} } = safeJSONParse(data); const { token = '', tokens = {} } = safeJSONParse(data);
if (headerToken === token || tokens[req.platform] === headerToken) { if (headerToken === token || tokens[req.platform] === headerToken) {

View File

@ -1,7 +1,6 @@
import fs from 'fs'; import fs from 'fs/promises';
import path from 'path'; import path from 'path';
import os from 'os'; import os from 'os';
import dotenv from 'dotenv';
import Logger from './logger'; import Logger from './logger';
import { fileExist } from '../config/util'; import { fileExist } from '../config/util';
@ -48,64 +47,70 @@ export default async () => {
const TaskAfterFileExist = await fileExist(TaskAfterFile); const TaskAfterFileExist = await fileExist(TaskAfterFile);
if (!configDirExist) { if (!configDirExist) {
fs.mkdirSync(configPath); await fs.mkdir(configPath);
} }
if (!scriptDirExist) { if (!scriptDirExist) {
fs.mkdirSync(scriptPath); await fs.mkdir(scriptPath);
} }
if (!logDirExist) { if (!logDirExist) {
fs.mkdirSync(logPath); await fs.mkdir(logPath);
} }
if (!tmpDirExist) { if (!tmpDirExist) {
fs.mkdirSync(tmpPath); await fs.mkdir(tmpPath);
} }
if (!uploadDirExist) { if (!uploadDirExist) {
fs.mkdirSync(uploadPath); await fs.mkdir(uploadPath);
} }
if (!sshDirExist) { if (!sshDirExist) {
fs.mkdirSync(sshPath); await fs.mkdir(sshPath);
} }
if (!bakDirExist) { if (!bakDirExist) {
fs.mkdirSync(bakPath); await fs.mkdir(bakPath);
} }
if (!sshdDirExist) { if (!sshdDirExist) {
fs.mkdirSync(sshdPath); await fs.mkdir(sshdPath);
} }
if (!systemLogDirExist) { if (!systemLogDirExist) {
fs.mkdirSync(systemLogPath); await fs.mkdir(systemLogPath);
} }
// 初始化文件 // 初始化文件
if (!authFileExist) { if (!authFileExist) {
fs.writeFileSync(authConfigFile, fs.readFileSync(sampleAuthFile)); await fs.writeFile(authConfigFile, await fs.readFile(sampleAuthFile));
} }
if (!confFileExist) { if (!confFileExist) {
fs.writeFileSync(confFile, fs.readFileSync(sampleConfigFile)); await fs.writeFile(confFile, await fs.readFile(sampleConfigFile));
} }
if (!scriptNotifyJsFileExist) { if (!scriptNotifyJsFileExist) {
fs.writeFileSync(scriptNotifyJsFile, fs.readFileSync(sampleNotifyJsFile)); await fs.writeFile(
scriptNotifyJsFile,
await fs.readFile(sampleNotifyJsFile),
);
} }
if (!scriptNotifyPyFileExist) { if (!scriptNotifyPyFileExist) {
fs.writeFileSync(scriptNotifyPyFile, fs.readFileSync(sampleNotifyPyFile)); await fs.writeFile(
scriptNotifyPyFile,
await fs.readFile(sampleNotifyPyFile),
);
} }
if (!TaskBeforeFileExist) { if (!TaskBeforeFileExist) {
fs.writeFileSync(TaskBeforeFile, fs.readFileSync(sampleTaskShellFile)); await fs.writeFile(TaskBeforeFile, await fs.readFile(sampleTaskShellFile));
} }
if (!TaskAfterFileExist) { if (!TaskAfterFileExist) {
fs.writeFileSync(TaskAfterFile, fs.readFileSync(sampleTaskShellFile)); await fs.writeFile(TaskAfterFile, await fs.readFile(sampleTaskShellFile));
} }
Logger.info('✌️ Init file down'); Logger.info('✌️ Init file down');

View File

@ -1,22 +1,21 @@
import sockJs from 'sockjs'; import sockJs from 'sockjs';
import { Server } from 'http'; import { Server } from 'http';
import Logger from './logger';
import { Container } from 'typedi'; import { Container } from 'typedi';
import SockService from '../services/sock'; import SockService from '../services/sock';
import config from '../config/index'; import config from '../config/index';
import fs from 'fs'; import fs from 'fs/promises';
import { getPlatform, safeJSONParse } from '../config/util'; import { getPlatform, safeJSONParse } from '../config/util';
export default async ({ server }: { server: Server }) => { export default async ({ server }: { server: Server }) => {
const echo = sockJs.createServer({ prefix: '/api/ws', log: () => {} }); const echo = sockJs.createServer({ prefix: '/api/ws', log: () => {} });
const sockService = Container.get(SockService); const sockService = Container.get(SockService);
echo.on('connection', (conn) => { echo.on('connection', async (conn) => {
if (!conn.headers || !conn.url || !conn.pathname) { if (!conn.headers || !conn.url || !conn.pathname) {
conn.close('404'); 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 platform = getPlatform(conn.headers['user-agent'] || '') || 'desktop';
const headerToken = conn.url.replace(`${conn.pathname}?token=`, ''); const headerToken = conn.url.replace(`${conn.pathname}?token=`, '');
if (data) { if (data) {

View File

@ -3,10 +3,15 @@ import winston from 'winston';
import config from '../config'; import config from '../config';
import { Crontab, CrontabModel, CrontabStatus } from '../data/cron'; import { Crontab, CrontabModel, CrontabStatus } from '../data/cron';
import { exec, execSync } from 'child_process'; import { exec, execSync } from 'child_process';
import fs from 'fs'; import fs from 'fs/promises';
import cron_parser from 'cron-parser'; import cron_parser from 'cron-parser';
import { getFileContentByName, fileExist, killTask, getUniqPath, safeJSONParse } from '../config/util'; import {
import { promises, existsSync } from 'fs'; getFileContentByName,
fileExist,
killTask,
getUniqPath,
safeJSONParse,
} from '../config/util';
import { Op, where, col as colFn, FindOptions, fn } from 'sequelize'; import { Op, where, col as colFn, FindOptions, fn } from 'sequelize';
import path from 'path'; import path from 'path';
import { TASK_PREFIX, QL_PREFIX } from '../config/const'; import { TASK_PREFIX, QL_PREFIX } from '../config/const';
@ -35,7 +40,13 @@ export default class CronService {
const doc = await this.insert(tab); const doc = await this.insert(tab);
if (this.isSixCron(doc) || doc.extra_schedules?.length) { if (this.isSixCron(doc) || doc.extra_schedules?.length) {
await cronClient.addCron([ 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(); await this.set_crontab();
@ -64,7 +75,7 @@ export default class CronService {
id: String(newDoc.id), id: String(newDoc.id),
schedule: newDoc.schedule!, schedule: newDoc.schedule!,
command: this.makeCommand(newDoc), 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) { if (status === CrontabStatus.idle && log_path !== cron.log_path) {
options = omit(options, ['status', 'log_path', 'pid']); 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 taskLimit.runWithCronLimit(() => {
return new Promise(async (resolve: any) => { return new Promise(async (resolve: any) => {
const cron = await this.getDb({ id: cronId }); 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) { if (cron.status !== CrontabStatus.queued) {
resolve(params); resolve(params);
return; return;
} }
this.logger.info(`[panel][开始执行任务] 参数 ${JSON.stringify(params)}`); this.logger.info(
`[panel][开始执行任务] 参数 ${JSON.stringify(params)}`,
);
let { id, command, log_path } = cron; let { id, command, log_path } = cron;
const uniqPath = await getUniqPath(command, `${id}`); const uniqPath = await getUniqPath(command, `${id}`);
const logTime = dayjs().format('YYYY-MM-DD-HH-mm-ss-SSS'); const logTime = dayjs().format('YYYY-MM-DD-HH-mm-ss-SSS');
const logDirPath = path.resolve(config.logPath, `${uniqPath}`); const logDirPath = path.resolve(config.logPath, `${uniqPath}`);
if (log_path?.split('/')?.every(x => x !== uniqPath)) { if (log_path?.split('/')?.every((x) => x !== uniqPath)) {
fs.mkdirSync(logDirPath, { recursive: true }); await fs.mkdir(logDirPath, { recursive: true });
} }
const logPath = `${uniqPath}/${logTime}.log`; const logPath = `${uniqPath}/${logTime}.log`;
const absolutePath = path.resolve(config.logPath, `${logPath}`); 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( await CrontabModel.update(
{ status: CrontabStatus.running, pid: cp.pid, log_path: logPath }, { status: CrontabStatus.running, pid: cp.pid, log_path: logPath },
{ where: { id } }, { where: { id } },
); );
cp.stderr.on('data', (data) => { cp.stderr.on('data', async (data) => {
fs.appendFileSync(`${absolutePath}`, `${data.toString()}`); await fs.appendFile(`${absolutePath}`, `${data.toString()}`);
}); });
cp.on('error', (err) => { cp.on('error', async (err) => {
fs.appendFileSync(`${absolutePath}`, `${JSON.stringify(err)}`); await fs.appendFile(`${absolutePath}`, `${JSON.stringify(err)}`);
}); });
cp.on('exit', async (code) => { cp.on('exit', async (code) => {
@ -454,7 +478,7 @@ export default class CronService {
id: String(doc.id), id: String(doc.id),
schedule: doc.schedule!, schedule: doc.schedule!,
command: this.makeCommand(doc), command: this.makeCommand(doc),
extraSchedules: doc.extra_schedules || [] extraSchedules: doc.extra_schedules || [],
})); }));
await cronClient.addCron(sixCron); await cronClient.addCron(sixCron);
await this.set_crontab(); await this.set_crontab();
@ -469,7 +493,7 @@ export default class CronService {
const absolutePath = path.resolve(config.logPath, `${doc.log_path}`); const absolutePath = path.resolve(config.logPath, `${doc.log_path}`);
const logFileExist = doc.log_path && (await fileExist(absolutePath)); const logFileExist = doc.log_path && (await fileExist(absolutePath));
if (logFileExist) { if (logFileExist) {
return getFileContentByName(`${absolutePath}`); return await getFileContentByName(`${absolutePath}`);
} else { } else {
return '任务未运行'; return '任务未运行';
} }
@ -483,15 +507,18 @@ export default class CronService {
const relativeDir = path.dirname(`${doc.log_path}`); const relativeDir = path.dirname(`${doc.log_path}`);
const dir = path.resolve(config.logPath, relativeDir); const dir = path.resolve(config.logPath, relativeDir);
if (existsSync(dir)) { const dirExist = await fileExist(dir);
let files = await promises.readdir(dir); if (dirExist) {
return files let files = await fs.readdir(dir);
.map((x) => ({ return (
await Promise.all(
files.map(async (x) => ({
filename: x, filename: x,
directory: relativeDir.replace(config.logPath, ''), directory: relativeDir.replace(config.logPath, ''),
time: fs.statSync(`${dir}/${x}`).mtime.getTime(), time: (await fs.stat(`${dir}/${x}`)).mtime.getTime(),
})) })),
.sort((a, b) => b.time - a.time); )
).sort((a, b) => b.time - a.time);
} else { } else {
return []; return [];
} }
@ -502,13 +529,15 @@ export default class CronService {
if (!command.startsWith(TASK_PREFIX) && !command.startsWith(QL_PREFIX)) { if (!command.startsWith(TASK_PREFIX) && !command.startsWith(QL_PREFIX)) {
command = `${TASK_PREFIX}${tab.command}`; 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) { if (tab.task_before) {
commandVariable += `task_before='${tab.task_before.replace(/'/g, "'\\''") commandVariable += `task_before='${tab.task_before
.replace(/'/g, "'\\''")
.trim()}' `; .trim()}' `;
} }
if (tab.task_after) { if (tab.task_after) {
commandVariable += `task_after='${tab.task_after.replace(/'/g, "'\\''") commandVariable += `task_after='${tab.task_after
.replace(/'/g, "'\\''")
.trim()}' `; .trim()}' `;
} }
@ -521,7 +550,11 @@ export default class CronService {
var crontab_string = ''; var crontab_string = '';
tabs.data.forEach((tab) => { tabs.data.forEach((tab) => {
const _schedule = tab.schedule && tab.schedule.split(/ +/); 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 += '# ';
crontab_string += tab.schedule; crontab_string += tab.schedule;
crontab_string += ' '; 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}`); execSync(`crontab ${config.crontabFile}`);
await CrontabModel.update({ saved: true }, { where: {} }); await CrontabModel.update({ saved: true }, { where: {} });
@ -586,7 +619,7 @@ export default class CronService {
id: String(doc.id), id: String(doc.id),
schedule: doc.schedule!, schedule: doc.schedule!,
command: this.makeCommand(doc), command: this.makeCommand(doc),
extraSchedules: doc.extra_schedules || [] extraSchedules: doc.extra_schedules || [],
})); }));
await cronClient.addCron(sixCron); await cronClient.addCron(sixCron);
} }

View File

@ -1,7 +1,7 @@
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import winston from 'winston'; import winston from 'winston';
import config from '../config'; import config from '../config';
import * as fs from 'fs'; import * as fs from 'fs/promises';
import { import {
Env, Env,
EnvModel, EnvModel,
@ -208,6 +208,6 @@ export default class EnvService {
} }
} }
} }
fs.writeFileSync(config.envFile, env_string); await fs.writeFile(config.envFile, env_string);
} }
} }

View File

@ -1,13 +1,12 @@
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import winston from 'winston'; import winston from 'winston';
import fs from 'fs';
import path from 'path'; import path from 'path';
import SockService from './sock'; import SockService from './sock';
import CronService from './cron'; import CronService from './cron';
import ScheduleService, { TaskCallbacks } from './schedule'; import ScheduleService, { TaskCallbacks } from './schedule';
import config from '../config'; import config from '../config';
import { TASK_COMMAND } from '../config/const'; import { TASK_COMMAND } from '../config/const';
import { getPid, killTask } from '../config/util'; import { getPid, killTask, rmPath } from '../config/util';
@Service() @Service()
export default class ScriptService { export default class ScriptService {
@ -21,9 +20,7 @@ export default class ScriptService {
private taskCallbacks(filePath: string): TaskCallbacks { private taskCallbacks(filePath: string): TaskCallbacks {
return { return {
onEnd: async (cp, endTime, diff) => { onEnd: async (cp, endTime, diff) => {
try { await rmPath(filePath);
fs.unlinkSync(filePath);
} catch (error) { }
}, },
onError: async (message: string) => { onError: async (message: string) => {
this.sockService.sendMessage({ this.sockService.sendMessage({
@ -56,7 +53,7 @@ export default class ScriptService {
public async stopScript(filePath: string, pid: number) { public async stopScript(filePath: string, pid: number) {
if (!pid) { if (!pid) {
const relativePath = path.relative(config.scriptPath, filePath); 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 { try {
await killTask(pid); await killTask(pid);

View File

@ -1,11 +1,12 @@
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import winston from 'winston'; import winston from 'winston';
import fs, { existsSync } from 'fs'; import fs from 'fs/promises';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import { Subscription } from '../data/subscription'; import { Subscription } from '../data/subscription';
import { formatUrl } from '../config/subscription'; import { formatUrl } from '../config/subscription';
import config from '../config'; import config from '../config';
import { fileExist, rmPath } from '../config/util';
@Service() @Service()
export default class SshKeyService { export default class SshKeyService {
@ -18,15 +19,16 @@ export default class SshKeyService {
this.initSshConfigFile(); this.initSshConfigFile();
} }
private initSshConfigFile() { private async initSshConfigFile() {
let config = ''; let config = '';
if (existsSync(this.sshConfigFilePath)) { const _exist = await fileExist(this.sshConfigFilePath);
config = fs.readFileSync(this.sshConfigFilePath, { encoding: 'utf-8' }); if (_exist) {
config = await fs.readFile(this.sshConfigFilePath, { encoding: 'utf-8' });
} else { } else {
fs.writeFileSync(this.sshConfigFilePath, ''); await fs.writeFile(this.sshConfigFilePath, '');
} }
if (!config.includes(this.sshConfigHeader)) { if (!config.includes(this.sshConfigHeader)) {
fs.writeFileSync( await fs.writeFile(
this.sshConfigFilePath, this.sshConfigFilePath,
`${this.sshConfigHeader}\n\n${config}`, `${this.sshConfigHeader}\n\n${config}`,
{ encoding: 'utf-8' }, { 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<void> {
try { try {
fs.writeFileSync(path.join(this.sshPath, alias), `${key}${os.EOL}`, { await fs.writeFile(path.join(this.sshPath, alias), `${key}${os.EOL}`, {
encoding: 'utf8', encoding: 'utf8',
mode: '400', mode: '400',
}); });
@ -45,18 +50,20 @@ export default class SshKeyService {
} }
} }
private removePrivateKeyFile(alias: string): void { private async removePrivateKeyFile(alias: string): Promise<void> {
try { try {
const filePath = path.join(this.sshPath, alias); const filePath = path.join(this.sshPath, alias);
if (existsSync(filePath)) { await rmPath(filePath);
fs.unlinkSync(filePath);
}
} catch (error) { } catch (error) {
this.logger.error('删除私钥文件失败', 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') { if (host === 'github.com') {
host = `ssh.github.com\n Port 443\n HostkeyAlgorithms +ssh-rsa`; host = `ssh.github.com\n Port 443\n HostkeyAlgorithms +ssh-rsa`;
} }
@ -67,49 +74,51 @@ export default class SshKeyService {
this.sshPath, this.sshPath,
alias, alias,
)}\n StrictHostKeyChecking no\n${proxyStr}`; )}\n StrictHostKeyChecking no\n${proxyStr}`;
fs.writeFileSync(`${path.join(this.sshPath, `${alias}.config`)}`, config, { await fs.writeFile(
`${path.join(this.sshPath, `${alias}.config`)}`,
config,
{
encoding: 'utf8', encoding: 'utf8',
}); },
);
} }
private removeSshConfig(alias: string) { private async removeSshConfig(alias: string) {
try { try {
const filePath = path.join(this.sshPath, `${alias}.config`); const filePath = path.join(this.sshPath, `${alias}.config`);
if (existsSync(filePath)) { await rmPath(filePath);
fs.unlinkSync(filePath);
}
} catch (error) { } catch (error) {
this.logger.error(`删除ssh配置文件${alias}失败`, error); this.logger.error(`删除ssh配置文件${alias}失败`, error);
} }
} }
public addSSHKey( public async addSSHKey(
key: string, key: string,
alias: string, alias: string,
host: string, host: string,
proxy?: string, proxy?: string,
): void { ): Promise<void> {
this.generatePrivateKeyFile(alias, key); await this.generatePrivateKeyFile(alias, key);
this.generateSingleSshConfig(alias, host, proxy); await this.generateSingleSshConfig(alias, host, proxy);
} }
public removeSSHKey(alias: string, host: string, proxy?: string): void { public async removeSSHKey(alias: string, host: string, proxy?: string): Promise<void> {
this.removePrivateKeyFile(alias); await this.removePrivateKeyFile(alias);
this.removeSshConfig(alias); await this.removeSshConfig(alias);
} }
public setSshConfig(docs: Subscription[]) { public async setSshConfig(docs: Subscription[]) {
for (const doc of docs) { for (const doc of docs) {
if (doc.type === 'private-repo' && doc.pull_type === 'ssh-key') { if (doc.type === 'private-repo' && doc.pull_type === 'ssh-key') {
const { alias, proxy } = doc; const { alias, proxy } = doc;
const { host } = formatUrl(doc); const { host } = formatUrl(doc);
this.removePrivateKeyFile(alias); await this.removePrivateKeyFile(alias);
this.removeSshConfig(alias); await this.removeSshConfig(alias);
this.generatePrivateKeyFile( await this.generatePrivateKeyFile(
alias, alias,
(doc.pull_option as any).private_key, (doc.pull_option as any).private_key,
); );
this.generateSingleSshConfig(alias, host, proxy); await this.generateSingleSshConfig(alias, host, proxy);
} }
} }
} }

View File

@ -7,7 +7,6 @@ import {
SubscriptionStatus, SubscriptionStatus,
} from '../data/subscription'; } from '../data/subscription';
import { ChildProcessWithoutNullStreams } from 'child_process'; import { ChildProcessWithoutNullStreams } from 'child_process';
import fs from 'fs';
import { import {
getFileContentByName, getFileContentByName,
concurrentRun, concurrentRun,
@ -16,9 +15,9 @@ import {
killTask, killTask,
handleLogPath, handleLogPath,
promiseExec, promiseExec,
emptyDir, rmPath,
} from '../config/util'; } from '../config/util';
import { promises, existsSync } from 'fs'; import fs from 'fs/promises';
import { FindOptions, Op } from 'sequelize'; import { FindOptions, Op } from 'sequelize';
import path, { join } from 'path'; import path, { join } from 'path';
import ScheduleService, { TaskCallbacks } from './schedule'; import ScheduleService, { TaskCallbacks } from './schedule';
@ -107,7 +106,7 @@ export default class SubscriptionService {
public async setSshConfig() { public async setSshConfig() {
const docs = await SubscriptionModel.findAll(); const docs = await SubscriptionModel.findAll();
this.sshKeyService.setSshConfig(docs); await this.sshKeyService.setSshConfig(docs);
} }
private taskCallbacks(doc: Subscription): TaskCallbacks { private taskCallbacks(doc: Subscription): TaskCallbacks {
@ -131,7 +130,7 @@ export default class SubscriptionService {
let beforeStr = ''; let beforeStr = '';
try { try {
if (doc.sub_before) { 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); beforeStr = await promiseExec(doc.sub_before);
} }
} catch (error: any) { } catch (error: any) {
@ -139,7 +138,7 @@ export default class SubscriptionService {
(error.stderr && error.stderr.toString()) || JSON.stringify(error); (error.stderr && error.stderr.toString()) || JSON.stringify(error);
} }
if (beforeStr) { if (beforeStr) {
fs.appendFileSync(absolutePath, `${beforeStr}\n`); await fs.appendFile(absolutePath, `${beforeStr}\n`);
} }
}, },
onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => { onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => {
@ -158,7 +157,7 @@ export default class SubscriptionService {
let afterStr = ''; let afterStr = '';
try { try {
if (sub.sub_after) { 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); afterStr = await promiseExec(sub.sub_after);
} }
} catch (error: any) { } catch (error: any) {
@ -166,10 +165,10 @@ export default class SubscriptionService {
(error.stderr && error.stderr.toString()) || JSON.stringify(error); (error.stderr && error.stderr.toString()) || JSON.stringify(error);
} }
if (afterStr) { if (afterStr) {
fs.appendFileSync(absolutePath, `${afterStr}\n`); await fs.appendFile(absolutePath, `${afterStr}\n`);
} }
fs.appendFileSync( await fs.appendFile(
absolutePath, absolutePath,
`\n## 执行结束... ${endTime.format( `\n## 执行结束... ${endTime.format(
'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss',
@ -190,12 +189,12 @@ export default class SubscriptionService {
onError: async (message: string) => { onError: async (message: string) => {
const sub = await this.getDb({ id: doc.id }); const sub = await this.getDb({ id: doc.id });
const absolutePath = await handleLogPath(sub.log_path as string); const absolutePath = await handleLogPath(sub.log_path as string);
fs.appendFileSync(absolutePath, `\n${message}`); await fs.appendFile(absolutePath, `\n${message}`);
}, },
onLog: async (message: string) => { onLog: async (message: string) => {
const sub = await this.getDb({ id: doc.id }); const sub = await this.getDb({ id: doc.id });
const absolutePath = await handleLogPath(sub.log_path as string); 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) { if (query?.force === true) {
const crons = await CrontabModel.findAll({ where: { sub_id: ids } }); const crons = await CrontabModel.findAll({ where: { sub_id: ids } });
if (crons?.length) { 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) { for (const doc of docs) {
const filePath = join(config.scriptPath, doc.alias); 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), { this.scheduleService.runTask(command, this.taskCallbacks(subscription), {
name: subscription.name, name: subscription.name,
schedule: subscription.schedule, schedule: subscription.schedule,
command command,
}); });
} }
@ -352,7 +351,7 @@ export default class SubscriptionService {
} }
const absolutePath = await handleLogPath(doc.log_path as string); const absolutePath = await handleLogPath(doc.log_path as string);
return getFileContentByName(absolutePath); return await getFileContentByName(absolutePath);
} }
public async logs(id: number) { public async logs(id: number) {
@ -364,15 +363,18 @@ export default class SubscriptionService {
if (doc.log_path) { if (doc.log_path) {
const relativeDir = path.dirname(`${doc.log_path}`); const relativeDir = path.dirname(`${doc.log_path}`);
const dir = path.resolve(config.logPath, relativeDir); const dir = path.resolve(config.logPath, relativeDir);
if (existsSync(dir)) { const _exist = await fileExist(dir);
let files = await promises.readdir(dir); if (_exist) {
return files let files = await fs.readdir(dir);
.map((x) => ({ return (
await Promise.all(
files.map(async (x) => ({
filename: x, filename: x,
directory: relativeDir.replace(config.logPath, ''), directory: relativeDir.replace(config.logPath, ''),
time: fs.statSync(`${dir}/${x}`).mtime.getTime(), time: (await fs.stat(`${dir}/${x}`)).mtime.getTime(),
})) })),
.sort((a, b) => b.time - a.time); )
).sort((a, b) => b.time - a.time);
} }
} }
} }

View File

@ -234,7 +234,7 @@ export default class SystemService {
callback, callback,
{ {
command, command,
} },
); );
} }
@ -283,7 +283,7 @@ export default class SystemService {
} }
public async getSystemLog(res: Response) { 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')); const logs = result.reverse().filter((x) => x.title.endsWith('.log'));
res.set({ res.set({
'Content-Length': sum(logs.map((x) => x.size)), 'Content-Length': sum(logs.map((x) => x.size)),

View File

@ -8,7 +8,7 @@ import {
safeJSONParse, safeJSONParse,
} from '../config/util'; } from '../config/util';
import config from '../config'; import config from '../config';
import * as fs from 'fs'; import * as fs from 'fs/promises';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { authenticator } from '@otplib/preset-default'; import { authenticator } from '@otplib/preset-default';
import { import {
@ -44,12 +44,13 @@ export default class UserService {
req: Request, req: Request,
needTwoFactor = true, needTwoFactor = true,
): Promise<any> { ): Promise<any> {
if (!fs.existsSync(config.authConfigFile)) { const _exist = await fileExist(config.authConfigFile);
if (!_exist) {
return this.initAuthInfo(); return this.initAuthInfo();
} }
let { username, password } = payloads; let { username, password } = payloads;
const content = this.getAuthInfo(); const content = await this.getAuthInfo();
const timestamp = Date.now(); const timestamp = Date.now();
if (content) { if (content) {
let { let {
@ -187,7 +188,7 @@ export default class UserService {
} }
public async logout(platform: string): Promise<any> { public async logout(platform: string): Promise<any> {
const authInfo = this.getAuthInfo(); const authInfo = await this.getAuthInfo();
this.updateAuthInfo(authInfo, { this.updateAuthInfo(authInfo, {
token: '', token: '',
tokens: { ...authInfo.tokens, [platform]: '' }, tokens: { ...authInfo.tokens, [platform]: '' },
@ -217,8 +218,8 @@ export default class UserService {
return doc; return doc;
} }
private initAuthInfo() { private async initAuthInfo() {
fs.writeFileSync( await fs.writeFile(
config.authConfigFile, config.authConfigFile,
JSON.stringify({ JSON.stringify({
username: 'admin', username: 'admin',
@ -255,7 +256,7 @@ export default class UserService {
public async getUserInfo(): Promise<any> { public async getUserInfo(): Promise<any> {
const authFileExist = await fileExist(config.authConfigFile); const authFileExist = await fileExist(config.authConfigFile);
if (!authFileExist) { if (!authFileExist) {
fs.writeFileSync( await fs.writeFile(
config.authConfigFile, config.authConfigFile,
JSON.stringify({ JSON.stringify({
username: 'admin', username: 'admin',
@ -266,16 +267,16 @@ export default class UserService {
return this.getAuthInfo(); return this.getAuthInfo();
} }
public initTwoFactor() { public async initTwoFactor() {
const secret = authenticator.generateSecret(); const secret = authenticator.generateSecret();
const authInfo = this.getAuthInfo(); const authInfo = await this.getAuthInfo();
const otpauth = authenticator.keyuri(authInfo.username, 'qinglong', secret); const otpauth = authenticator.keyuri(authInfo.username, 'qinglong', secret);
this.updateAuthInfo(authInfo, { twoFactorSecret: secret }); this.updateAuthInfo(authInfo, { twoFactorSecret: secret });
return { secret, url: otpauth }; return { secret, url: otpauth };
} }
public activeTwoFactor(code: string) { public async activeTwoFactor(code: string) {
const authInfo = this.getAuthInfo(); const authInfo = await this.getAuthInfo();
const isValid = authenticator.verify({ const isValid = authenticator.verify({
token: code, token: code,
secret: authInfo.twoFactorSecret, secret: authInfo.twoFactorSecret,
@ -294,7 +295,7 @@ export default class UserService {
}: { username: string; password: string; code: string }, }: { username: string; password: string; code: string },
req: any, req: any,
) { ) {
const authInfo = this.getAuthInfo(); const authInfo = await this.getAuthInfo();
const { isTwoFactorChecking, twoFactorSecret } = authInfo; const { isTwoFactorChecking, twoFactorSecret } = authInfo;
if (!isTwoFactorChecking) { if (!isTwoFactorChecking) {
return { code: 450, message: '未知错误' }; return { code: 450, message: '未知错误' };
@ -326,13 +327,13 @@ export default class UserService {
return true; return true;
} }
private getAuthInfo() { private async getAuthInfo() {
const content = fs.readFileSync(config.authConfigFile, 'utf8'); const content = await fs.readFile(config.authConfigFile, 'utf8');
return safeJSONParse(content); return safeJSONParse(content);
} }
private updateAuthInfo(authInfo: any, info: any) { private async updateAuthInfo(authInfo: any, info: any) {
fs.writeFileSync( await fs.writeFile(
config.authConfigFile, config.authConfigFile,
JSON.stringify({ ...authInfo, ...info }), JSON.stringify({ ...authInfo, ...info }),
); );

0
shell/env.sh Normal file → Executable file
View File