qinglong/back/services/dependence.ts

287 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Service, Inject } from 'typedi';
import winston from 'winston';
import config from '../config';
import DataStore from 'nedb';
import {
Dependence,
InstallDependenceCommandTypes,
DependenceStatus,
DependenceTypes,
unInstallDependenceCommandTypes,
} from '../data/dependence';
import _ from 'lodash';
import { spawn } from 'child_process';
import SockService from './sock';
import { dbs } from '../loaders/db';
@Service()
export default class DependenceService {
private dependenceDb = dbs.dependenceDb;
constructor(
@Inject('logger') private logger: winston.Logger,
private sockService: SockService,
) {}
public async create(payloads: Dependence[]): Promise<Dependence[]> {
const tabs = payloads.map((x) => {
const tab = new Dependence({ ...x, status: DependenceStatus.installing });
return tab;
});
const docs = await this.insert(tabs);
this.installOrUninstallDependencies(docs);
return docs;
}
public async insert(payloads: Dependence[]): Promise<Dependence[]> {
return new Promise((resolve) => {
this.dependenceDb.insert(payloads, (err, docs) => {
if (err) {
this.logger.error(err);
} else {
resolve(docs);
}
});
});
}
public async update(
payload: Dependence & { _id: string },
): Promise<Dependence> {
const { _id, ...other } = payload;
const doc = await this.get(_id);
const tab = new Dependence({
...doc,
...other,
status: DependenceStatus.installing,
});
const newDoc = await this.updateDb(tab);
this.installOrUninstallDependencies([newDoc]);
return newDoc;
}
private async updateDb(payload: Dependence): Promise<Dependence> {
return new Promise((resolve) => {
this.dependenceDb.update(
{ _id: payload._id },
payload,
{ returnUpdatedDocs: true },
(err, num, doc) => {
if (err) {
this.logger.error(err);
} else {
resolve(doc as Dependence);
}
},
);
});
}
public async remove(ids: string[]) {
return new Promise((resolve: any) => {
this.dependenceDb.update(
{ _id: { $in: ids } },
{ $set: { status: DependenceStatus.removing, log: [] } },
{ multi: true, returnUpdatedDocs: true },
async (err, num, docs: Dependence[]) => {
this.installOrUninstallDependencies(docs, false);
resolve(docs);
},
);
});
}
public async removeDb(ids: string[]) {
return new Promise((resolve: any) => {
this.dependenceDb.remove(
{ _id: { $in: ids } },
{ multi: true },
async (err) => {
resolve();
},
);
});
}
public async dependencies(
{ searchValue, type }: { searchValue: string; type: string },
sort: any = { position: -1 },
query: any = {},
): Promise<Dependence[]> {
let condition = { ...query, type: DependenceTypes[type as any] };
if (searchValue) {
const reg = new RegExp(searchValue);
condition = {
...condition,
$or: [
{
name: reg,
},
],
};
}
const newDocs = await this.find(condition, sort);
return newDocs;
}
public async reInstall(ids: string[]): Promise<Dependence[]> {
return new Promise((resolve: any) => {
this.dependenceDb.update(
{ _id: { $in: ids } },
{ $set: { status: DependenceStatus.installing, log: [] } },
{ multi: true, returnUpdatedDocs: true },
async (err, num, docs: Dependence[]) => {
await this.installOrUninstallDependencies(docs);
resolve(docs);
},
);
});
}
private async find(query: any, sort: any): Promise<Dependence[]> {
return new Promise((resolve) => {
this.dependenceDb
.find(query)
.sort({ ...sort })
.exec((err, docs) => {
resolve(docs);
});
});
}
public async get(_id: string): Promise<Dependence> {
return new Promise((resolve) => {
this.dependenceDb.find({ _id }).exec((err, docs) => {
resolve(docs[0]);
});
});
}
private async updateLog(ids: string[], log: string): Promise<void> {
return new Promise((resolve) => {
this.dependenceDb.update(
{ _id: { $in: ids } },
{ $push: { log } },
{ multi: true },
(err, num, doc) => {
if (err) {
this.logger.error(err);
} else {
resolve();
}
},
);
});
}
public installOrUninstallDependencies(
dependencies: Dependence[],
isInstall: boolean = true,
) {
return new Promise((resolve) => {
if (dependencies.length === 0) {
resolve(null);
return;
}
const depNames = dependencies.map((x) => x.name).join(' ');
const depRunCommand = (
isInstall
? InstallDependenceCommandTypes
: unInstallDependenceCommandTypes
)[dependencies[0].type as any];
const actionText = isInstall ? '安装' : '删除';
const depIds = dependencies.map((x) => x._id) as string[];
const cp = spawn(`${depRunCommand} ${depNames}`, { shell: '/bin/bash' });
const startTime = Date.now();
this.sockService.sendMessage({
type: 'installDependence',
message: `开始${actionText}依赖 ${depNames},开始时间 ${new Date(
startTime,
).toLocaleString()}`,
references: depIds,
});
this.updateLog(
depIds,
`开始${actionText}依赖 ${depNames},开始时间 ${new Date(
startTime,
).toLocaleString()}\n`,
);
cp.stdout.on('data', (data) => {
this.sockService.sendMessage({
type: 'installDependence',
message: data.toString(),
references: depIds,
});
this.updateLog(depIds, data.toString());
});
cp.stderr.on('data', (data) => {
this.sockService.sendMessage({
type: 'installDependence',
message: data.toString(),
references: depIds,
});
this.updateLog(depIds, data.toString());
});
cp.on('error', (err) => {
this.sockService.sendMessage({
type: 'installDependence',
message: JSON.stringify(err),
references: depIds,
});
this.updateLog(depIds, JSON.stringify(err));
resolve(null);
});
cp.on('close', (code) => {
const endTime = Date.now();
const isSucceed = code === 0;
const resultText = isSucceed ? '成功' : '失败';
this.sockService.sendMessage({
type: 'installDependence',
message: `依赖${actionText}${resultText},结束时间 ${new Date(
endTime,
).toLocaleString()},耗时 ${(endTime - startTime) / 1000}`,
references: depIds,
});
this.updateLog(
depIds,
`依赖${actionText}${resultText},结束时间 ${new Date(
endTime,
).toLocaleString()},耗时 ${(endTime - startTime) / 1000}`,
);
let status = null;
if (isSucceed) {
status = isInstall
? DependenceStatus.installed
: DependenceStatus.removed;
} else {
status = isInstall
? DependenceStatus.installFailed
: DependenceStatus.removeFailed;
}
this.dependenceDb.update(
{ _id: { $in: depIds } },
{
$set: { status },
$unset: { pid: true },
},
{ multi: true },
);
// 如果删除依赖成功3秒后删除数据库记录
if (isSucceed && !isInstall) {
setTimeout(() => {
this.removeDb(depIds);
}, 5000);
}
resolve(null);
});
});
}
}