mirror of
https://github.com/whyour/qinglong.git
synced 2026-11-05 21:01:11 +08:00
Use MikroORM's official Node SQLite dialect with bounded database isolation, preserve legacy schema and data for rollback, and require Node 22.17 or newer. Update Debian runtimes and retain ARMv6 musl compatibility without a SQLite addon. Complete short archive writes, stop orphaned SQL workers after owner death, preserve buffered startup requests, and prestart the archived backend on every platform. Cover real package installation, migration, failure recovery, and legacy database compatibility. Keep authentication storage failures distinct from expired sessions with a pinned Keyv runtime, propagated cache errors, and retryable HTTP 503 responses. Preserve genuine 401 failures and verify legacy cache bytes, initialization safety, and session recovery.
434 lines
12 KiB
TypeScript
434 lines
12 KiB
TypeScript
import 'reflect-metadata';
|
|
import { Container } from 'typedi';
|
|
import EnvService from '../services/env';
|
|
import { sendUnaryData, ServerUnaryCall } from '@grpc/grpc-js';
|
|
import {
|
|
CreateEnvRequest,
|
|
CronItem,
|
|
DeleteEnvsRequest,
|
|
DisableEnvsRequest,
|
|
EnableEnvsRequest,
|
|
EnvItem,
|
|
EnvResponse,
|
|
EnvsResponse,
|
|
GetEnvByIdRequest,
|
|
GetEnvsRequest,
|
|
MoveEnvRequest,
|
|
Response,
|
|
SystemNotifyRequest,
|
|
RecordNotifyLogRequest,
|
|
UpdateEnvNamesRequest,
|
|
UpdateEnvRequest,
|
|
} from '../protos/api';
|
|
import LoggerInstance from '../loaders/logger';
|
|
import pick from 'lodash/pick';
|
|
import SystemService from '../services/system';
|
|
import CronService from '../services/cron';
|
|
import {
|
|
CronDetailRequest,
|
|
CronDetailResponse,
|
|
CreateCronRequest,
|
|
UpdateCronRequest,
|
|
DeleteCronsRequest,
|
|
CronResponse,
|
|
GetCronsRequest,
|
|
CronsResponse,
|
|
GetCronByIdRequest,
|
|
EnableCronsRequest,
|
|
DisableCronsRequest,
|
|
RunCronsRequest,
|
|
} from '../protos/api';
|
|
import { NotificationInfo } from '../data/notify';
|
|
Container.set('logger', LoggerInstance);
|
|
export const getEnvs = async (
|
|
call: ServerUnaryCall<GetEnvsRequest, EnvsResponse>,
|
|
callback: sendUnaryData<EnvsResponse>,
|
|
) => {
|
|
try {
|
|
const envService = Container.get(EnvService);
|
|
const data = await envService.envs(call.request.searchValue);
|
|
callback(null, {
|
|
code: 200,
|
|
data: data.map((x) => ({ ...x, remarks: x.remarks || '' })),
|
|
});
|
|
} catch (e: any) {
|
|
callback(null, {
|
|
code: 500,
|
|
data: [],
|
|
message: e.message,
|
|
});
|
|
}
|
|
};
|
|
export const createEnv = async (
|
|
call: ServerUnaryCall<CreateEnvRequest, EnvsResponse>,
|
|
callback: sendUnaryData<EnvsResponse>,
|
|
) => {
|
|
try {
|
|
const envService = Container.get(EnvService);
|
|
const data = await envService.create(call.request.envs);
|
|
callback(null, { code: 200, data });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const updateEnv = async (
|
|
call: ServerUnaryCall<UpdateEnvRequest, EnvResponse>,
|
|
callback: sendUnaryData<EnvResponse>,
|
|
) => {
|
|
try {
|
|
if (!call.request.env?.id) {
|
|
return callback(null, {
|
|
code: 400,
|
|
data: undefined,
|
|
message: 'id parameter is required',
|
|
});
|
|
}
|
|
const envService = Container.get(EnvService);
|
|
const data = await envService.update(
|
|
pick(call.request.env, ['id', 'name', 'value', 'remarks']) as EnvItem,
|
|
);
|
|
callback(null, { code: 200, data });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const deleteEnvs = async (
|
|
call: ServerUnaryCall<DeleteEnvsRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
if (!call.request.ids || call.request.ids.length === 0) {
|
|
return callback(null, {
|
|
code: 400,
|
|
message: 'ids parameter is required',
|
|
});
|
|
}
|
|
const envService = Container.get(EnvService);
|
|
await envService.remove(call.request.ids);
|
|
callback(null, { code: 200 });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const moveEnv = async (
|
|
call: ServerUnaryCall<MoveEnvRequest, EnvResponse>,
|
|
callback: sendUnaryData<EnvResponse>,
|
|
) => {
|
|
try {
|
|
if (!call.request.id) {
|
|
return callback(null, {
|
|
code: 400,
|
|
data: undefined,
|
|
message: 'id parameter is required',
|
|
});
|
|
}
|
|
const envService = Container.get(EnvService);
|
|
const data = await envService.move(call.request.id, {
|
|
fromIndex: call.request.fromIndex,
|
|
toIndex: call.request.toIndex,
|
|
});
|
|
callback(null, { code: 200, data });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const disableEnvs = async (
|
|
call: ServerUnaryCall<DisableEnvsRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
if (!call.request.ids || call.request.ids.length === 0) {
|
|
return callback(null, {
|
|
code: 400,
|
|
message: 'ids parameter is required',
|
|
});
|
|
}
|
|
const envService = Container.get(EnvService);
|
|
await envService.disabled(call.request.ids);
|
|
callback(null, { code: 200 });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const enableEnvs = async (
|
|
call: ServerUnaryCall<EnableEnvsRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
if (!call.request.ids || call.request.ids.length === 0) {
|
|
return callback(null, {
|
|
code: 400,
|
|
message: 'ids parameter is required',
|
|
});
|
|
}
|
|
const envService = Container.get(EnvService);
|
|
await envService.enabled(call.request.ids);
|
|
callback(null, { code: 200 });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const updateEnvNames = async (
|
|
call: ServerUnaryCall<UpdateEnvNamesRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
if (!call.request.ids || call.request.ids.length === 0) {
|
|
return callback(null, {
|
|
code: 400,
|
|
message: 'ids parameter is required',
|
|
});
|
|
}
|
|
const envService = Container.get(EnvService);
|
|
await envService.updateNames({
|
|
ids: call.request.ids,
|
|
name: call.request.name,
|
|
});
|
|
callback(null, { code: 200 });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const getEnvById = async (
|
|
call: ServerUnaryCall<GetEnvByIdRequest, EnvResponse>,
|
|
callback: sendUnaryData<EnvResponse>,
|
|
) => {
|
|
try {
|
|
if (!call.request.id) {
|
|
return callback(null, {
|
|
code: 400,
|
|
data: undefined,
|
|
message: 'id parameter is required',
|
|
});
|
|
}
|
|
const envService = Container.get(EnvService);
|
|
const data = await envService.getDb({ id: call.request.id });
|
|
callback(null, {
|
|
code: 200,
|
|
data: { ...data, remarks: data.remarks || '' },
|
|
});
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const systemNotify = async (
|
|
call: ServerUnaryCall<SystemNotifyRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
const systemService = Container.get(SystemService);
|
|
const data = await systemService.notify({
|
|
title: call.request.title,
|
|
content: call.request.content,
|
|
notificationInfo: call.request
|
|
.notificationInfo as unknown as NotificationInfo,
|
|
});
|
|
callback(null, data);
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const recordNotifyLog = async (
|
|
call: ServerUnaryCall<RecordNotifyLogRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
await Container.get(SystemService).recordNotifyLog({
|
|
title: call.request.title,
|
|
content: call.request.content,
|
|
status: call.request.success ? 0 : 1,
|
|
notifyType: call.request.notifyType,
|
|
source: 'script',
|
|
scriptName: call.request.scriptName,
|
|
taskId: call.request.taskId,
|
|
});
|
|
callback(null, { code: 200 });
|
|
} catch (error: any) {
|
|
callback(error);
|
|
}
|
|
};
|
|
const normalizeCronData = (data: CronItem | null): CronItem | undefined => {
|
|
if (!data) return undefined;
|
|
const cron = data;
|
|
return {
|
|
...cron,
|
|
labels: cron.labels ?? [],
|
|
sub_id: cron.sub_id ?? undefined,
|
|
extra_schedules: cron.extra_schedules ?? [],
|
|
pid: cron.pid ?? undefined,
|
|
task_before: cron.task_before ?? undefined,
|
|
task_after: cron.task_after ?? undefined,
|
|
};
|
|
};
|
|
export const getCronDetail = async (
|
|
call: ServerUnaryCall<CronDetailRequest, CronDetailResponse>,
|
|
callback: sendUnaryData<CronDetailResponse>,
|
|
) => {
|
|
try {
|
|
if (!call.request.log_path) {
|
|
return callback(null, {
|
|
code: 400,
|
|
data: undefined,
|
|
message: 'log_path is required',
|
|
});
|
|
}
|
|
const cronService = Container.get(CronService);
|
|
const data = (await cronService.find({
|
|
log_path: call.request.log_path,
|
|
})) as CronItem;
|
|
callback(null, { code: 200, data: normalizeCronData(data) });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const createCron = async (
|
|
call: ServerUnaryCall<CreateCronRequest, CronResponse>,
|
|
callback: sendUnaryData<CronResponse>,
|
|
) => {
|
|
try {
|
|
const cronService = Container.get(CronService);
|
|
const data = (await cronService.create(call.request)) as CronItem;
|
|
callback(null, { code: 200, data: normalizeCronData(data) });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const updateCron = async (
|
|
call: ServerUnaryCall<UpdateCronRequest, CronResponse>,
|
|
callback: sendUnaryData<CronResponse>,
|
|
) => {
|
|
try {
|
|
const cronService = Container.get(CronService);
|
|
const { id, ...fields } = call.request;
|
|
const updateRequest = {
|
|
id,
|
|
...Object.entries(fields).reduce((acc: any, [key, value]) => {
|
|
if (value !== undefined) {
|
|
acc[key] = value;
|
|
}
|
|
return acc;
|
|
}, {}),
|
|
} as UpdateCronRequest;
|
|
const data = (await cronService.update(updateRequest)) as CronItem;
|
|
callback(null, { code: 200, data: normalizeCronData(data) });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const deleteCrons = async (
|
|
call: ServerUnaryCall<DeleteCronsRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
const cronService = Container.get(CronService);
|
|
await cronService.remove(call.request.ids);
|
|
callback(null, { code: 200 });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const getCrons = async (
|
|
call: ServerUnaryCall<GetCronsRequest, CronsResponse>,
|
|
callback: sendUnaryData<CronsResponse>,
|
|
) => {
|
|
try {
|
|
const cronService = Container.get(CronService);
|
|
const result = await cronService.crontabs({
|
|
searchValue: call.request.searchValue || '',
|
|
page: '0',
|
|
size: '0',
|
|
sorter: '',
|
|
filters: '',
|
|
queryString: '',
|
|
});
|
|
const data = result.data.map((x) => normalizeCronData(x as CronItem));
|
|
callback(null, {
|
|
code: 200,
|
|
data: data.filter((x): x is CronItem => x !== undefined),
|
|
});
|
|
} catch (e: any) {
|
|
callback(null, {
|
|
code: 500,
|
|
data: [],
|
|
message: e.message,
|
|
});
|
|
}
|
|
};
|
|
export const getCronById = async (
|
|
call: ServerUnaryCall<GetCronByIdRequest, CronResponse>,
|
|
callback: sendUnaryData<CronResponse>,
|
|
) => {
|
|
try {
|
|
if (!call.request.id) {
|
|
return callback(null, {
|
|
code: 400,
|
|
data: undefined,
|
|
message: 'id parameter is required',
|
|
});
|
|
}
|
|
const cronService = Container.get(CronService);
|
|
const data = (await cronService.getDb({ id: call.request.id })) as CronItem;
|
|
callback(null, { code: 200, data: normalizeCronData(data) });
|
|
} catch (e: any) {
|
|
callback(null, {
|
|
code: 404,
|
|
data: undefined,
|
|
message: e.message,
|
|
});
|
|
}
|
|
};
|
|
export const enableCrons = async (
|
|
call: ServerUnaryCall<EnableCronsRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
if (!call.request.ids || call.request.ids.length === 0) {
|
|
return callback(null, {
|
|
code: 400,
|
|
message: 'ids parameter is required',
|
|
});
|
|
}
|
|
const cronService = Container.get(CronService);
|
|
await cronService.enabled(call.request.ids);
|
|
callback(null, { code: 200 });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const disableCrons = async (
|
|
call: ServerUnaryCall<DisableCronsRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
if (!call.request.ids || call.request.ids.length === 0) {
|
|
return callback(null, {
|
|
code: 400,
|
|
message: 'ids parameter is required',
|
|
});
|
|
}
|
|
const cronService = Container.get(CronService);
|
|
await cronService.disabled(call.request.ids);
|
|
callback(null, { code: 200 });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|
|
export const runCrons = async (
|
|
call: ServerUnaryCall<RunCronsRequest, Response>,
|
|
callback: sendUnaryData<Response>,
|
|
) => {
|
|
try {
|
|
if (!call.request.ids || call.request.ids.length === 0) {
|
|
return callback(null, {
|
|
code: 400,
|
|
message: 'ids parameter is required',
|
|
});
|
|
}
|
|
const cronService = Container.get(CronService);
|
|
await cronService.run(call.request.ids);
|
|
callback(null, { code: 200 });
|
|
} catch (e: any) {
|
|
callback(e);
|
|
}
|
|
};
|