更新 ts-proto 版本

This commit is contained in:
whyour 2025-01-29 23:29:45 +08:00
parent 924dfd6a6f
commit 6df651aa63
11 changed files with 2061 additions and 275 deletions

View File

@ -8,7 +8,7 @@ message EnvItem {
optional string value = 3; optional string value = 3;
optional string remarks = 4; optional string remarks = 4;
optional int32 status = 5; optional int32 status = 5;
optional int32 position = 6; optional int64 position = 6;
} }
message GetEnvsRequest { string searchValue = 1; } message GetEnvsRequest { string searchValue = 1; }
@ -58,6 +58,72 @@ message SystemNotifyRequest {
string content = 2; string content = 2;
} }
message ExtraScheduleItem {
string schedule = 1;
}
message CronItem {
optional int32 id = 1;
optional string command = 2;
optional string schedule = 3;
optional string name = 4;
repeated string labels = 5;
optional int32 sub_id = 6;
repeated ExtraScheduleItem extra_schedules = 7;
optional string task_before = 8;
optional string task_after = 9;
optional int32 status = 10;
optional string log_path = 11;
optional int32 pid = 12;
optional int64 last_running_time = 13;
optional int64 last_execution_time = 14;
}
message CreateCronRequest {
string command = 1;
string schedule = 2;
optional string name = 3;
repeated string labels = 4;
optional int32 sub_id = 5;
repeated ExtraScheduleItem extra_schedules = 6;
optional string task_before = 7;
optional string task_after = 8;
}
message UpdateCronRequest {
int32 id = 1;
string command = 2;
string schedule = 3;
optional string name = 4;
repeated string labels = 5;
optional int32 sub_id = 6;
repeated ExtraScheduleItem extra_schedules = 7;
optional string task_before = 8;
optional string task_after = 9;
}
message DeleteCronsRequest { repeated int32 ids = 1; }
message CronsResponse {
int32 code = 1;
repeated CronItem data = 2;
optional string message = 3;
}
message CronResponse {
int32 code = 1;
CronItem data = 2;
optional string message = 3;
}
message CronDetailRequest { string log_path = 1; }
message CronDetailResponse {
int32 code = 1;
CronItem data = 2;
optional string message = 3;
}
service Api { service Api {
rpc GetEnvs(GetEnvsRequest) returns (EnvsResponse) {} rpc GetEnvs(GetEnvsRequest) returns (EnvsResponse) {}
rpc CreateEnv(CreateEnvRequest) returns (EnvsResponse) {} rpc CreateEnv(CreateEnvRequest) returns (EnvsResponse) {}
@ -69,4 +135,8 @@ service Api {
rpc UpdateEnvNames(UpdateEnvNamesRequest) returns (Response) {} rpc UpdateEnvNames(UpdateEnvNamesRequest) returns (Response) {}
rpc GetEnvById(GetEnvByIdRequest) returns (EnvResponse) {} rpc GetEnvById(GetEnvByIdRequest) returns (EnvResponse) {}
rpc SystemNotify(SystemNotifyRequest) returns (Response) {} rpc SystemNotify(SystemNotifyRequest) returns (Response) {}
rpc GetCronDetail(CronDetailRequest) returns (CronDetailResponse) {}
rpc CreateCron(CreateCronRequest) returns (CronResponse) {}
rpc UpdateCron(UpdateCronRequest) returns (CronResponse) {}
rpc DeleteCrons(DeleteCronsRequest) returns (Response) {}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v1.181.2 // protoc-gen-ts_proto v2.6.1
// protoc v3.17.3 // protoc v3.17.3
// source: back/protos/cron.proto // source: back/protos/cron.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { import {
type CallOptions, type CallOptions,
ChannelCredentials, ChannelCredentials,
@ -17,7 +18,6 @@ import {
type ServiceError, type ServiceError,
type UntypedServiceImplementation, type UntypedServiceImplementation,
} from "@grpc/grpc-js"; } from "@grpc/grpc-js";
import _m0 from "protobufjs/minimal";
export const protobufPackage = "com.ql.cron"; export const protobufPackage = "com.ql.cron";
@ -29,7 +29,7 @@ export interface ICron {
id: string; id: string;
schedule: string; schedule: string;
command: string; command: string;
extraSchedules: ISchedule[]; extra_schedules: ISchedule[];
name: string; name: string;
} }
@ -51,33 +51,34 @@ function createBaseISchedule(): ISchedule {
return { schedule: "" }; return { schedule: "" };
} }
export const ISchedule = { export const ISchedule: MessageFns<ISchedule> = {
encode(message: ISchedule, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { encode(message: ISchedule, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.schedule !== "") { if (message.schedule !== "") {
writer.uint32(10).string(message.schedule); writer.uint32(10).string(message.schedule);
} }
return writer; return writer;
}, },
decode(input: _m0.Reader | Uint8Array, length?: number): ISchedule { decode(input: BinaryReader | Uint8Array, length?: number): ISchedule {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseISchedule(); const message = createBaseISchedule();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
switch (tag >>> 3) { switch (tag >>> 3) {
case 1: case 1: {
if (tag !== 10) { if (tag !== 10) {
break; break;
} }
message.schedule = reader.string(); message.schedule = reader.string();
continue; continue;
}
} }
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
} }
reader.skipType(tag & 7); reader.skip(tag & 7);
} }
return message; return message;
}, },
@ -105,11 +106,11 @@ export const ISchedule = {
}; };
function createBaseICron(): ICron { function createBaseICron(): ICron {
return { id: "", schedule: "", command: "", extraSchedules: [], name: "" }; return { id: "", schedule: "", command: "", extra_schedules: [], name: "" };
} }
export const ICron = { export const ICron: MessageFns<ICron> = {
encode(message: ICron, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { encode(message: ICron, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.id !== "") { if (message.id !== "") {
writer.uint32(10).string(message.id); writer.uint32(10).string(message.id);
} }
@ -119,8 +120,8 @@ export const ICron = {
if (message.command !== "") { if (message.command !== "") {
writer.uint32(26).string(message.command); writer.uint32(26).string(message.command);
} }
for (const v of message.extraSchedules) { for (const v of message.extra_schedules) {
ISchedule.encode(v!, writer.uint32(34).fork()).ldelim(); ISchedule.encode(v!, writer.uint32(34).fork()).join();
} }
if (message.name !== "") { if (message.name !== "") {
writer.uint32(42).string(message.name); writer.uint32(42).string(message.name);
@ -128,53 +129,58 @@ export const ICron = {
return writer; return writer;
}, },
decode(input: _m0.Reader | Uint8Array, length?: number): ICron { decode(input: BinaryReader | Uint8Array, length?: number): ICron {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseICron(); const message = createBaseICron();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
switch (tag >>> 3) { switch (tag >>> 3) {
case 1: case 1: {
if (tag !== 10) { if (tag !== 10) {
break; break;
} }
message.id = reader.string(); message.id = reader.string();
continue; continue;
case 2: }
case 2: {
if (tag !== 18) { if (tag !== 18) {
break; break;
} }
message.schedule = reader.string(); message.schedule = reader.string();
continue; continue;
case 3: }
case 3: {
if (tag !== 26) { if (tag !== 26) {
break; break;
} }
message.command = reader.string(); message.command = reader.string();
continue; continue;
case 4: }
case 4: {
if (tag !== 34) { if (tag !== 34) {
break; break;
} }
message.extraSchedules.push(ISchedule.decode(reader, reader.uint32())); message.extra_schedules.push(ISchedule.decode(reader, reader.uint32()));
continue; continue;
case 5: }
case 5: {
if (tag !== 42) { if (tag !== 42) {
break; break;
} }
message.name = reader.string(); message.name = reader.string();
continue; continue;
}
} }
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
} }
reader.skipType(tag & 7); reader.skip(tag & 7);
} }
return message; return message;
}, },
@ -184,8 +190,8 @@ export const ICron = {
id: isSet(object.id) ? globalThis.String(object.id) : "", id: isSet(object.id) ? globalThis.String(object.id) : "",
schedule: isSet(object.schedule) ? globalThis.String(object.schedule) : "", schedule: isSet(object.schedule) ? globalThis.String(object.schedule) : "",
command: isSet(object.command) ? globalThis.String(object.command) : "", command: isSet(object.command) ? globalThis.String(object.command) : "",
extraSchedules: globalThis.Array.isArray(object?.extraSchedules) extra_schedules: globalThis.Array.isArray(object?.extra_schedules)
? object.extraSchedules.map((e: any) => ISchedule.fromJSON(e)) ? object.extra_schedules.map((e: any) => ISchedule.fromJSON(e))
: [], : [],
name: isSet(object.name) ? globalThis.String(object.name) : "", name: isSet(object.name) ? globalThis.String(object.name) : "",
}; };
@ -202,8 +208,8 @@ export const ICron = {
if (message.command !== "") { if (message.command !== "") {
obj.command = message.command; obj.command = message.command;
} }
if (message.extraSchedules?.length) { if (message.extra_schedules?.length) {
obj.extraSchedules = message.extraSchedules.map((e) => ISchedule.toJSON(e)); obj.extra_schedules = message.extra_schedules.map((e) => ISchedule.toJSON(e));
} }
if (message.name !== "") { if (message.name !== "") {
obj.name = message.name; obj.name = message.name;
@ -219,7 +225,7 @@ export const ICron = {
message.id = object.id ?? ""; message.id = object.id ?? "";
message.schedule = object.schedule ?? ""; message.schedule = object.schedule ?? "";
message.command = object.command ?? ""; message.command = object.command ?? "";
message.extraSchedules = object.extraSchedules?.map((e) => ISchedule.fromPartial(e)) || []; message.extra_schedules = object.extra_schedules?.map((e) => ISchedule.fromPartial(e)) || [];
message.name = object.name ?? ""; message.name = object.name ?? "";
return message; return message;
}, },
@ -229,33 +235,34 @@ function createBaseAddCronRequest(): AddCronRequest {
return { crons: [] }; return { crons: [] };
} }
export const AddCronRequest = { export const AddCronRequest: MessageFns<AddCronRequest> = {
encode(message: AddCronRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { encode(message: AddCronRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
for (const v of message.crons) { for (const v of message.crons) {
ICron.encode(v!, writer.uint32(10).fork()).ldelim(); ICron.encode(v!, writer.uint32(10).fork()).join();
} }
return writer; return writer;
}, },
decode(input: _m0.Reader | Uint8Array, length?: number): AddCronRequest { decode(input: BinaryReader | Uint8Array, length?: number): AddCronRequest {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAddCronRequest(); const message = createBaseAddCronRequest();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
switch (tag >>> 3) { switch (tag >>> 3) {
case 1: case 1: {
if (tag !== 10) { if (tag !== 10) {
break; break;
} }
message.crons.push(ICron.decode(reader, reader.uint32())); message.crons.push(ICron.decode(reader, reader.uint32()));
continue; continue;
}
} }
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
} }
reader.skipType(tag & 7); reader.skip(tag & 7);
} }
return message; return message;
}, },
@ -286,13 +293,13 @@ function createBaseAddCronResponse(): AddCronResponse {
return {}; return {};
} }
export const AddCronResponse = { export const AddCronResponse: MessageFns<AddCronResponse> = {
encode(_: AddCronResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { encode(_: AddCronResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
return writer; return writer;
}, },
decode(input: _m0.Reader | Uint8Array, length?: number): AddCronResponse { decode(input: BinaryReader | Uint8Array, length?: number): AddCronResponse {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAddCronResponse(); const message = createBaseAddCronResponse();
while (reader.pos < end) { while (reader.pos < end) {
@ -302,7 +309,7 @@ export const AddCronResponse = {
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
} }
reader.skipType(tag & 7); reader.skip(tag & 7);
} }
return message; return message;
}, },
@ -329,33 +336,34 @@ function createBaseDeleteCronRequest(): DeleteCronRequest {
return { ids: [] }; return { ids: [] };
} }
export const DeleteCronRequest = { export const DeleteCronRequest: MessageFns<DeleteCronRequest> = {
encode(message: DeleteCronRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { encode(message: DeleteCronRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
for (const v of message.ids) { for (const v of message.ids) {
writer.uint32(10).string(v!); writer.uint32(10).string(v!);
} }
return writer; return writer;
}, },
decode(input: _m0.Reader | Uint8Array, length?: number): DeleteCronRequest { decode(input: BinaryReader | Uint8Array, length?: number): DeleteCronRequest {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseDeleteCronRequest(); const message = createBaseDeleteCronRequest();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
switch (tag >>> 3) { switch (tag >>> 3) {
case 1: case 1: {
if (tag !== 10) { if (tag !== 10) {
break; break;
} }
message.ids.push(reader.string()); message.ids.push(reader.string());
continue; continue;
}
} }
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
} }
reader.skipType(tag & 7); reader.skip(tag & 7);
} }
return message; return message;
}, },
@ -386,13 +394,13 @@ function createBaseDeleteCronResponse(): DeleteCronResponse {
return {}; return {};
} }
export const DeleteCronResponse = { export const DeleteCronResponse: MessageFns<DeleteCronResponse> = {
encode(_: DeleteCronResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { encode(_: DeleteCronResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
return writer; return writer;
}, },
decode(input: _m0.Reader | Uint8Array, length?: number): DeleteCronResponse { decode(input: BinaryReader | Uint8Array, length?: number): DeleteCronResponse {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseDeleteCronResponse(); const message = createBaseDeleteCronResponse();
while (reader.pos < end) { while (reader.pos < end) {
@ -402,7 +410,7 @@ export const DeleteCronResponse = {
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
} }
reader.skipType(tag & 7); reader.skip(tag & 7);
} }
return message; return message;
}, },
@ -506,3 +514,12 @@ export type Exact<P, I extends P> = P extends Builtin ? P
function isSet(value: any): boolean { function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
}

View File

@ -1,25 +1,25 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v1.181.2 // protoc-gen-ts_proto v2.6.1
// protoc v3.17.3 // protoc v3.17.3
// source: back/protos/health.proto // source: back/protos/health.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { import {
type CallOptions, type CallOptions,
ChannelCredentials, ChannelCredentials,
Client, Client,
type ClientOptions, type ClientOptions,
ClientReadableStream, type ClientReadableStream,
type ClientUnaryCall, type ClientUnaryCall,
handleServerStreamingCall, type handleServerStreamingCall,
type handleUnaryCall, type handleUnaryCall,
makeGenericClientConstructor, makeGenericClientConstructor,
Metadata, Metadata,
type ServiceError, type ServiceError,
type UntypedServiceImplementation, type UntypedServiceImplementation,
} from "@grpc/grpc-js"; } from "@grpc/grpc-js";
import _m0 from "protobufjs/minimal";
export const protobufPackage = "com.ql.health"; export const protobufPackage = "com.ql.health";
@ -80,33 +80,34 @@ function createBaseHealthCheckRequest(): HealthCheckRequest {
return { service: "" }; return { service: "" };
} }
export const HealthCheckRequest = { export const HealthCheckRequest: MessageFns<HealthCheckRequest> = {
encode(message: HealthCheckRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { encode(message: HealthCheckRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.service !== "") { if (message.service !== "") {
writer.uint32(10).string(message.service); writer.uint32(10).string(message.service);
} }
return writer; return writer;
}, },
decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckRequest { decode(input: BinaryReader | Uint8Array, length?: number): HealthCheckRequest {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseHealthCheckRequest(); const message = createBaseHealthCheckRequest();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
switch (tag >>> 3) { switch (tag >>> 3) {
case 1: case 1: {
if (tag !== 10) { if (tag !== 10) {
break; break;
} }
message.service = reader.string(); message.service = reader.string();
continue; continue;
}
} }
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
} }
reader.skipType(tag & 7); reader.skip(tag & 7);
} }
return message; return message;
}, },
@ -137,33 +138,34 @@ function createBaseHealthCheckResponse(): HealthCheckResponse {
return { status: 0 }; return { status: 0 };
} }
export const HealthCheckResponse = { export const HealthCheckResponse: MessageFns<HealthCheckResponse> = {
encode(message: HealthCheckResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { encode(message: HealthCheckResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.status !== 0) { if (message.status !== 0) {
writer.uint32(8).int32(message.status); writer.uint32(8).int32(message.status);
} }
return writer; return writer;
}, },
decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckResponse { decode(input: BinaryReader | Uint8Array, length?: number): HealthCheckResponse {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseHealthCheckResponse(); const message = createBaseHealthCheckResponse();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
switch (tag >>> 3) { switch (tag >>> 3) {
case 1: case 1: {
if (tag !== 8) { if (tag !== 8) {
break; break;
} }
message.status = reader.int32() as any; message.status = reader.int32() as any;
continue; continue;
}
} }
if ((tag & 7) === 4 || tag === 0) { if ((tag & 7) === 4 || tag === 0) {
break; break;
} }
reader.skipType(tag & 7); reader.skip(tag & 7);
} }
return message; return message;
}, },
@ -262,3 +264,12 @@ export type Exact<P, I extends P> = P extends Builtin ? P
function isSet(value: any): boolean { function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
}

View File

@ -10,7 +10,7 @@ const addCron = (
callback: sendUnaryData<AddCronResponse>, callback: sendUnaryData<AddCronResponse>,
) => { ) => {
for (const item of call.request.crons) { for (const item of call.request.crons) {
const { id, schedule, command, extraSchedules, name } = item; const { id, schedule, command, extra_schedules, name } = item;
if (scheduleStacks.has(id)) { if (scheduleStacks.has(id)) {
scheduleStacks.get(id)?.forEach((x) => x.cancel()); scheduleStacks.get(id)?.forEach((x) => x.cancel());
} }
@ -23,8 +23,8 @@ const addCron = (
command, command,
); );
if (extraSchedules?.length) { if (extra_schedules?.length) {
extraSchedules.forEach((x) => { extra_schedules.forEach((x) => {
Logger.info( Logger.info(
'[schedule][创建定时任务], 任务ID: %s, 名称: %s, cron: %s, 执行命令: %s', '[schedule][创建定时任务], 任务ID: %s, 名称: %s, cron: %s, 执行命令: %s',
id, id,
@ -40,8 +40,8 @@ const addCron = (
Logger.info(`[schedule][准备运行任务] 命令: ${command}`); Logger.info(`[schedule][准备运行任务] 命令: ${command}`);
runCron(command, item); runCron(command, item);
}), }),
...(extraSchedules?.length ...(extra_schedules?.length
? extraSchedules.map((x) => ? extra_schedules.map((x) =>
nodeSchedule.scheduleJob(id, x.schedule, async () => { nodeSchedule.scheduleJob(id, x.schedule, async () => {
Logger.info(`[schedule][准备运行任务] 命令: ${command}`); Logger.info(`[schedule][准备运行任务] 命令: ${command}`);
runCron(command, item); runCron(command, item);

View File

@ -4,6 +4,7 @@ import EnvService from '../services/env';
import { sendUnaryData, ServerUnaryCall } from '@grpc/grpc-js'; import { sendUnaryData, ServerUnaryCall } from '@grpc/grpc-js';
import { import {
CreateEnvRequest, CreateEnvRequest,
CronItem,
DeleteEnvsRequest, DeleteEnvsRequest,
DisableEnvsRequest, DisableEnvsRequest,
EnableEnvsRequest, EnableEnvsRequest,
@ -21,6 +22,15 @@ import {
import LoggerInstance from '../loaders/logger'; import LoggerInstance from '../loaders/logger';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import SystemService from '../services/system'; import SystemService from '../services/system';
import CronService from '../services/cron';
import {
CronDetailRequest,
CronDetailResponse,
CreateCronRequest,
UpdateCronRequest,
DeleteCronsRequest,
CronResponse,
} from '../protos/api';
Container.set('logger', LoggerInstance); Container.set('logger', LoggerInstance);
@ -171,3 +181,58 @@ export const systemNotify = async (
callback(e); callback(e);
} }
}; };
export const getCronDetail = async (
call: ServerUnaryCall<CronDetailRequest, CronDetailResponse>,
callback: sendUnaryData<CronDetailResponse>,
) => {
try {
const cronService = Container.get(CronService);
const data = (await cronService.find({
log_path: call.request.log_path,
})) as CronItem;
console.log('data', data);
callback(null, { code: 200, data: data || undefined });
} 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 });
} catch (e: any) {
callback(e);
}
};
export const updateCron = async (
call: ServerUnaryCall<UpdateCronRequest, CronResponse>,
callback: sendUnaryData<CronResponse>,
) => {
try {
const cronService = Container.get(CronService);
const data = (await cronService.update(call.request)) as CronItem;
callback(null, { code: 200, 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);
}
};

View File

@ -46,7 +46,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 || [], extra_schedules: doc.extra_schedules || [],
}, },
]); ]);
} }
@ -76,7 +76,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 || [], extra_schedules: newDoc.extra_schedules || [],
}, },
]); ]);
} }
@ -320,10 +320,10 @@ export default class CronService {
log_path, log_path,
}: { }: {
log_path: string; log_path: string;
}): Promise<Crontab | null> { }): Promise<Crontab | undefined> {
try { try {
const result = await CrontabModel.findOne({ where: { log_path } }); const result = await CrontabModel.findOne({ where: { log_path } });
return result; return result?.get({ plain: true });
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -424,7 +424,7 @@ export default class CronService {
name: cron.name, name: cron.name,
command: cron.command, command: cron.command,
schedule: cron.schedule, schedule: cron.schedule,
extraSchedules: cron.extra_schedules, extra_schedules: cron.extra_schedules,
}; };
if (cron.status !== CrontabStatus.queued) { if (cron.status !== CrontabStatus.queued) {
resolve(params); resolve(params);
@ -508,7 +508,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 || [], extra_schedules: doc.extra_schedules || [],
})); }));
await cronClient.addCron(sixCron); await cronClient.addCron(sixCron);
await this.set_crontab(); await this.set_crontab();
@ -653,7 +653,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 || [], extra_schedules: doc.extra_schedules || [],
})); }));
await cronClient.addCron(sixCron); await cronClient.addCron(sixCron);
} }

View File

@ -13,7 +13,7 @@
"schedule": "npm run build:back && node static/build/schedule/index.js", "schedule": "npm run build:back && node static/build/schedule/index.js",
"public": "npm run build:back && node static/build/public.js", "public": "npm run build:back && node static/build/public.js",
"update": "npm run build:back && node static/build/update.js", "update": "npm run build:back && node static/build/update.js",
"gen:proto": "protoc --experimental_allow_proto3_optional --plugin=./node_modules/.bin/protoc-gen-ts_proto ./back/protos/*.proto --ts_proto_out=./ --ts_proto_opt=outputServices=grpc-js,env=node,esModuleInterop=true", "gen:proto": "protoc --experimental_allow_proto3_optional --plugin=./node_modules/.bin/protoc-gen-ts_proto ./back/protos/*.proto --ts_proto_out=./ --ts_proto_opt=outputServices=grpc-js,env=node,esModuleInterop=true,snakeToCamel=false",
"gen:api": "python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./back/protos/api.proto", "gen:api": "python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./back/protos/api.proto",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"postinstall": "max setup 2>/dev/null || true", "postinstall": "max setup 2>/dev/null || true",
@ -88,7 +88,7 @@
"node-schedule": "^2.1.0", "node-schedule": "^2.1.0",
"nodemailer": "^6.9.16", "nodemailer": "^6.9.16",
"p-queue-cjs": "7.3.4", "p-queue-cjs": "7.3.4",
"protobufjs": "^7.4.0", "@bufbuild/protobuf": "^2.2.3",
"pstree.remy": "^1.1.8", "pstree.remy": "^1.1.8",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",
"sequelize": "^6.37.5", "sequelize": "^6.37.5",
@ -171,7 +171,7 @@
"react-split-pane": "^0.1.92", "react-split-pane": "^0.1.92",
"sockjs-client": "^1.6.0", "sockjs-client": "^1.6.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"ts-proto": "^1.146.0", "ts-proto": "^2.6.1",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"typescript": "5.2.2", "typescript": "5.2.2",
"vh-check": "^2.0.5", "vh-check": "^2.0.5",

View File

@ -8,6 +8,9 @@ overrides:
sqlite3: git+https://github.com/whyour/node-sqlite3.git#v1.0.3 sqlite3: git+https://github.com/whyour/node-sqlite3.git#v1.0.3
dependencies: dependencies:
'@bufbuild/protobuf':
specifier: ^2.2.3
version: 2.2.3
'@grpc/grpc-js': '@grpc/grpc-js':
specifier: ^1.12.3 specifier: ^1.12.3
version: 1.12.3 version: 1.12.3
@ -101,9 +104,6 @@ dependencies:
proper-lockfile: proper-lockfile:
specifier: ^4.1.2 specifier: ^4.1.2
version: 4.1.2 version: 4.1.2
protobufjs:
specifier: ^7.4.0
version: 7.4.0
pstree.remy: pstree.remy:
specifier: ^1.1.8 specifier: ^1.1.8
version: 1.1.8 version: 1.1.8
@ -335,8 +335,8 @@ devDependencies:
specifier: ^10.9.2 specifier: ^10.9.2
version: 10.9.2(@types/node@17.0.45)(typescript@5.2.2) version: 10.9.2(@types/node@17.0.45)(typescript@5.2.2)
ts-proto: ts-proto:
specifier: ^1.146.0 specifier: ^2.6.1
version: 1.181.2 version: 2.6.1
tslib: tslib:
specifier: ^2.4.0 specifier: ^2.4.0
version: 2.8.1 version: 2.8.1
@ -1380,6 +1380,9 @@ packages:
resolution: {integrity: sha512-h0OYmPR3A5Dfbetra/GzxBAzQk8sH7LhRkRUTdagX6nrtlUgJGYCTv4bBK33jsTQw9HDd8PE2x1Ma+iRKEDUsw==} resolution: {integrity: sha512-h0OYmPR3A5Dfbetra/GzxBAzQk8sH7LhRkRUTdagX6nrtlUgJGYCTv4bBK33jsTQw9HDd8PE2x1Ma+iRKEDUsw==}
dev: true dev: true
/@bufbuild/protobuf@2.2.3:
resolution: {integrity: sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg==}
/@chenshuai2144/sketch-color@1.0.9(react@18.3.1): /@chenshuai2144/sketch-color@1.0.9(react@18.3.1):
resolution: {integrity: sha512-obzSy26cb7Pm7OprWyVpgMpIlrZpZ0B7vbrU0RMbvRg0YAI890S5Xy02Aj1Nhl4+KTbi1lVYHt6HQP8Hm9s+1w==} resolution: {integrity: sha512-obzSy26cb7Pm7OprWyVpgMpIlrZpZ0B7vbrU0RMbvRg0YAI890S5Xy02Aj1Nhl4+KTbi1lVYHt6HQP8Hm9s+1w==}
peerDependencies: peerDependencies:
@ -3271,36 +3274,46 @@ packages:
/@protobufjs/aspromise@1.1.2: /@protobufjs/aspromise@1.1.2:
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
dev: false
/@protobufjs/base64@1.1.2: /@protobufjs/base64@1.1.2:
resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
dev: false
/@protobufjs/codegen@2.0.4: /@protobufjs/codegen@2.0.4:
resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
dev: false
/@protobufjs/eventemitter@1.1.0: /@protobufjs/eventemitter@1.1.0:
resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
dev: false
/@protobufjs/fetch@1.1.0: /@protobufjs/fetch@1.1.0:
resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
dependencies: dependencies:
'@protobufjs/aspromise': 1.1.2 '@protobufjs/aspromise': 1.1.2
'@protobufjs/inquire': 1.1.0 '@protobufjs/inquire': 1.1.0
dev: false
/@protobufjs/float@1.0.2: /@protobufjs/float@1.0.2:
resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
dev: false
/@protobufjs/inquire@1.1.0: /@protobufjs/inquire@1.1.0:
resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
dev: false
/@protobufjs/path@1.1.2: /@protobufjs/path@1.1.2:
resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
dev: false
/@protobufjs/pool@1.1.0: /@protobufjs/pool@1.1.0:
resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
dev: false
/@protobufjs/utf8@1.1.0: /@protobufjs/utf8@1.1.0:
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
dev: false
/@qixian.cs/path-to-regexp@6.1.0: /@qixian.cs/path-to-regexp@6.1.0:
resolution: {integrity: sha512-2jIiLiVZB1jnY7IIRQKtoV8Gnr7XIhk4mC88ONGunZE3hYt5IHUG4BE/6+JiTBjjEWQLBeWnZB8hGpppkufiVw==} resolution: {integrity: sha512-2jIiLiVZB1jnY7IIRQKtoV8Gnr7XIhk4mC88ONGunZE3hYt5IHUG4BE/6+JiTBjjEWQLBeWnZB8hGpppkufiVw==}
@ -9846,6 +9859,7 @@ packages:
/long@5.2.3: /long@5.2.3:
resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==}
dev: false
/loose-envify@1.4.0: /loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
@ -11749,6 +11763,7 @@ packages:
'@protobufjs/utf8': 1.1.0 '@protobufjs/utf8': 1.1.0
'@types/node': 17.0.45 '@types/node': 17.0.45
long: 5.2.3 long: 5.2.3
dev: false
/proxy-addr@2.0.7: /proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
@ -14528,21 +14543,20 @@ packages:
dprint-node: 1.0.8 dprint-node: 1.0.8
dev: true dev: true
/ts-proto-descriptors@1.16.0: /ts-proto-descriptors@2.0.0:
resolution: {integrity: sha512-3yKuzMLpltdpcyQji1PJZRfoo4OJjNieKTYkQY8pF7xGKsYz/RHe3aEe4KiRxcinoBmnEhmuI+yJTxLb922ULA==} resolution: {integrity: sha512-wHcTH3xIv11jxgkX5OyCSFfw27agpInAd6yh89hKG6zqIXnjW9SYqSER2CVQxdPj4czeOhGagNvZBEbJPy7qkw==}
dependencies: dependencies:
long: 5.2.3 '@bufbuild/protobuf': 2.2.3
protobufjs: 7.4.0
dev: true dev: true
/ts-proto@1.181.2: /ts-proto@2.6.1:
resolution: {integrity: sha512-knJ8dtjn2Pd0c5ZGZG8z9DMiD4PUY8iGI9T9tb8DvGdWRMkLpf0WcPO7G+7cmbZyxvNTAG6ci3fybEaFgMZIvg==} resolution: {integrity: sha512-4LTT99MkwkF1+fIA0b2mZu/58Qlpq3Q1g53TwEMZZgR1w/uX00PoVT4Z8aKJxMw0LeKQD4s9NrJYsF27Clckrg==}
hasBin: true hasBin: true
dependencies: dependencies:
'@bufbuild/protobuf': 2.2.3
case-anything: 2.1.13 case-anything: 2.1.13
protobufjs: 7.4.0
ts-poet: 6.9.0 ts-poet: 6.9.0
ts-proto-descriptors: 1.16.0 ts-proto-descriptors: 2.0.0
dev: true dev: true
/tslib@1.14.1: /tslib@1.14.1:

View File

@ -1,45 +1,134 @@
const grpc = require('@grpc/grpc-js'); const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader'); const protoLoader = require('@grpc/proto-loader');
const { join } = require('path');
const PROTO_PATH = `${process.env.QL_DIR}/back/protos/api.proto`; class GrpcClient {
const options = { static #config = {
keepCase: true, protoPath: join(process.env.QL_DIR, 'back/protos/api.proto'),
longs: String, serverAddress: '0.0.0.0:5500',
enums: String, protoOptions: {
defaults: true, keepCase: true,
}; longs: String,
enums: String,
const packageDefinition = protoLoader.loadSync(PROTO_PATH, options); defaults: true,
const apiProto = grpc.loadPackageDefinition(packageDefinition).com.ql.api; },
grpcOptions: {
const client = new apiProto.Api( 'grpc.enable_http_proxy': 0,
`0.0.0.0:5500`, 'grpc.keepalive_time_ms': 120000,
grpc.credentials.createInsecure(), 'grpc.keepalive_timeout_ms': 20000,
{ 'grpc.enable_http_proxy': 0 }, 'grpc.max_receive_message_length': 100 * 1024 * 1024,
); },
defaultTimeout: 30000,
const promisify = (fn) => {
return (...args) => {
return new Promise((resolve, reject) => {
fn.call(client, ...args, (err, response) => {
if (err) return reject(err);
resolve(response);
});
});
}; };
};
const api = { static #methods = [
getEnvs: promisify(client.GetEnvs), 'getEnvs',
createEnv: promisify(client.CreateEnv), 'createEnv',
updateEnv: promisify(client.UpdateEnv), 'updateEnv',
deleteEnvs: promisify(client.DeleteEnvs), 'deleteEnvs',
moveEnv: promisify(client.MoveEnv), 'moveEnv',
disableEnvs: promisify(client.DisableEnvs), 'disableEnvs',
enableEnvs: promisify(client.EnableEnvs), 'enableEnvs',
updateEnvNames: promisify(client.UpdateEnvNames), 'updateEnvNames',
getEnvById: promisify(client.GetEnvById), 'getEnvById',
systemNotify: promisify(client.SystemNotify), 'systemNotify',
}; 'getCronDetail',
'createCron',
'updateCron',
'deleteCrons'
];
module.exports = api; #client;
#api = {};
constructor() {
this.#initializeClient();
this.#bindMethods();
}
#initializeClient() {
try {
const { protoPath, protoOptions, serverAddress, grpcOptions } =
GrpcClient.#config;
const packageDefinition = protoLoader.loadSync(protoPath, protoOptions);
const apiProto = grpc.loadPackageDefinition(packageDefinition).com.ql.api;
this.#client = new apiProto.Api(
serverAddress,
grpc.credentials.createInsecure(),
grpcOptions,
);
this.#checkConnection();
} catch (error) {
console.error('Failed to initialize gRPC client:', error);
process.exit(1);
}
}
#checkConnection() {
this.#client.waitForReady(Date.now() + 5000, (error) => {
if (error) {
console.error('gRPC client connection failed:', error);
process.exit(1);
}
});
}
#promisifyMethod(methodName) {
const capitalizedMethod =
methodName.charAt(0).toUpperCase() + methodName.slice(1);
const grpcMethod = this.#client[capitalizedMethod].bind(this.#client);
return async (params = {}) => {
return new Promise((resolve, reject) => {
const metadata = new grpc.Metadata();
const deadline = new Date(
Date.now() + GrpcClient.#config.defaultTimeout,
);
grpcMethod(params, metadata, { deadline }, (error, response) => {
if (error) {
return reject(error);
}
resolve(response);
});
});
};
}
#bindMethods() {
GrpcClient.#methods.forEach((method) => {
this.#api[method] = this.#promisifyMethod(method);
});
}
getApi() {
return {
...this.#api,
close: this.close.bind(this),
};
}
close() {
if (this.#client) {
this.#client.close();
this.#client = null;
}
}
}
const grpcClient = new GrpcClient();
process.on('SIGTERM', () => {
grpcClient.close();
process.exit(0);
});
process.on('SIGINT', () => {
grpcClient.close();
process.exit(0);
});
module.exports = grpcClient.getApi();

View File

@ -2,7 +2,7 @@ import subprocess
import json import json
import tempfile import tempfile
import os import os
from typing import Dict, List from typing import Dict, List, TypedDict, Optional
from functools import wraps from functools import wraps
@ -11,16 +11,170 @@ def error_handler(func):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
try: try:
return func(*args, **kwargs) return func(*args, **kwargs)
except json.JSONDecodeError as e: except TypeError as e:
raise Exception(f"parse json error: {str(e)}") if "missing" in str(e):
except subprocess.SubprocessError as e: func_name = func.__name__
raise Exception(f"node process error: {str(e)}") annotations = func.__annotations__
param_type = next(
(t for name, t in annotations.items() if name != "return"), None
)
if param_type and hasattr(param_type, "__annotations__"):
required_fields = {
k: v
for k, v in param_type.__annotations__.items()
if not getattr(param_type, "__total__", True)
or k in getattr(param_type, "__required_keys__", set())
}
fields_str = ", ".join(
f'"{k}": {v.__name__}' for k, v in required_fields.items()
)
raise Exception(
f"unknown error: {func_name}() requires a dictionary with parameters: {{{fields_str}}}"
) from None
raise Exception(f"unknown error: {str(e)}") from None
except Exception as e: except Exception as e:
raise Exception(f"unknown error: {str(e)}") error_msg = str(e)
if "Error:" in error_msg:
error_msg = error_msg.split("Error:")[-1].split("\n")[0].strip()
raise Exception(f"unknown error: {error_msg}") from None
return wrapper return wrapper
class EnvItem(TypedDict, total=False):
id: Optional[int]
name: Optional[str]
value: Optional[str]
remarks: Optional[str]
status: Optional[int]
position: Optional[int]
class GetEnvsParams(TypedDict, total=False):
searchValue: str
class CreateEnvParams(TypedDict):
envs: List[EnvItem]
class UpdateEnvParams(TypedDict):
env: EnvItem
class DeleteEnvsParams(TypedDict):
ids: List[int]
class MoveEnvParams(TypedDict):
id: int
fromIndex: int
toIndex: int
class DisableEnvsParams(TypedDict):
ids: List[int]
class EnableEnvsParams(TypedDict):
ids: List[int]
class UpdateEnvNamesParams(TypedDict):
ids: List[int]
name: str
class GetEnvByIdParams(TypedDict):
id: int
class SystemNotifyParams(TypedDict):
title: str
content: str
class EnvsResponse(TypedDict):
code: int
data: List[EnvItem]
message: Optional[str]
class EnvResponse(TypedDict):
code: int
data: EnvItem
message: Optional[str]
class Response(TypedDict):
code: int
message: Optional[str]
class ExtraScheduleItem(TypedDict, total=False):
schedule: Optional[str]
class CronItem(TypedDict, total=False):
id: Optional[int]
command: Optional[str]
schedule: Optional[str]
name: Optional[str]
labels: List[str]
sub_id: Optional[int]
extra_schedules: List[ExtraScheduleItem]
task_before: Optional[str]
task_after: Optional[str]
status: Optional[int]
log_path: Optional[str]
pid: Optional[int]
last_running_time: Optional[int]
last_execution_time: Optional[int]
class CreateCronParams(TypedDict):
command: str
schedule: str
name: Optional[str]
labels: List[str]
sub_id: Optional[int]
extra_schedules: List[ExtraScheduleItem]
task_before: Optional[str]
task_after: Optional[str]
class UpdateCronParams(TypedDict):
id: int
command: str
schedule: str
name: Optional[str]
labels: List[str]
sub_id: Optional[int]
extra_schedules: List[ExtraScheduleItem]
task_before: Optional[str]
task_after: Optional[str]
class DeleteCronsParams(TypedDict):
ids: List[int]
class CronDetailParams(TypedDict):
log_path: str
class CronsResponse(TypedDict):
code: int
data: List[CronItem]
message: Optional[str]
class CronResponse(TypedDict):
code: int
data: CronItem
message: Optional[str]
class Client: class Client:
def __init__(self): def __init__(self):
self.temp_dir = tempfile.mkdtemp(prefix="node_client_") self.temp_dir = tempfile.mkdtemp(prefix="node_client_")
@ -46,7 +200,8 @@ class Client:
}} catch (error) {{ }} catch (error) {{
console.error(JSON.stringify({{ console.error(JSON.stringify({{
error: error.message, error: error.message,
stack: error.stack stack: error.stack,
name: error.name
}})); }}));
process.exit(1); process.exit(1);
}} }}
@ -56,58 +211,73 @@ class Client:
with open(self.temp_script, "w", encoding="utf-8") as f: with open(self.temp_script, "w", encoding="utf-8") as f:
f.write(node_code) f.write(node_code)
try: result = subprocess.run(
result = subprocess.run( ["node", self.temp_script],
["node", self.temp_script], capture_output=True,
capture_output=True, text=True,
text=True, timeout=30,
timeout=30, )
if result.returncode != 0:
error_data = json.loads(result.stderr)
raise Exception(
f"{error_data.get('name', 'Error')}: {error_data.get('stack')}"
) )
if result.returncode != 0: return json.loads(result.stdout)
error_data = json.loads(result.stderr)
raise Exception(f"{error_data.get('stack')}")
return json.loads(result.stdout)
except subprocess.TimeoutExpired:
raise Exception("node process timeout")
@error_handler @error_handler
def getEnvs(self, params: Dict = None) -> Dict: def getEnvs(self, params: GetEnvsParams = None) -> EnvsResponse:
return self._execute_node("getEnvs", params) return self._execute_node("getEnvs", params)
@error_handler @error_handler
def createEnv(self, data: Dict) -> Dict: def createEnv(self, data: CreateEnvParams) -> EnvsResponse:
return self._execute_node("createEnv", data) return self._execute_node("createEnv", data)
@error_handler @error_handler
def updateEnv(self, data: Dict) -> Dict: def updateEnv(self, data: UpdateEnvParams) -> EnvResponse:
return self._execute_node("updateEnv", data) return self._execute_node("updateEnv", data)
@error_handler @error_handler
def deleteEnvs(self, data: Dict) -> Dict: def deleteEnvs(self, data: DeleteEnvsParams) -> Response:
return self._execute_node("deleteEnvs", data) return self._execute_node("deleteEnvs", data)
@error_handler @error_handler
def moveEnv(self, data: Dict) -> Dict: def moveEnv(self, data: MoveEnvParams) -> EnvResponse:
return self._execute_node("moveEnv", data) return self._execute_node("moveEnv", data)
@error_handler @error_handler
def disableEnvs(self, data: Dict) -> Dict: def disableEnvs(self, data: DisableEnvsParams) -> Response:
return self._execute_node("disableEnvs", data) return self._execute_node("disableEnvs", data)
@error_handler @error_handler
def enableEnvs(self, data: Dict) -> Dict: def enableEnvs(self, data: EnableEnvsParams) -> Response:
return self._execute_node("enableEnvs", data) return self._execute_node("enableEnvs", data)
@error_handler @error_handler
def updateEnvNames(self, data: Dict) -> Dict: def updateEnvNames(self, data: UpdateEnvNamesParams) -> Response:
return self._execute_node("updateEnvNames", data) return self._execute_node("updateEnvNames", data)
@error_handler @error_handler
def getEnvById(self, data: Dict) -> Dict: def getEnvById(self, data: GetEnvByIdParams) -> EnvResponse:
return self._execute_node("getEnvById", data) return self._execute_node("getEnvById", data)
@error_handler @error_handler
def systemNotify(self, data: Dict) -> Dict: def systemNotify(self, data: SystemNotifyParams) -> Response:
return self._execute_node("systemNotify", data) return self._execute_node("systemNotify", data)
@error_handler
def getCronDetail(self, data: CronDetailParams) -> CronResponse:
return self._execute_node("getCronDetail", data)
@error_handler
def createCron(self, data: CreateCronParams) -> CronResponse:
return self._execute_node("createCron", data)
@error_handler
def updateCron(self, data: UpdateCronParams) -> CronResponse:
return self._execute_node("updateCron", data)
@error_handler
def deleteCrons(self, data: DeleteCronsParams) -> Response:
return self._execute_node("deleteCrons", data)