From d11d6d0c18219edd876d2dd60836f1f3fbca9d75 Mon Sep 17 00:00:00 2001 From: whyour Date: Tue, 2 May 2023 22:11:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AE=B9=E5=99=A8=E5=81=A5?= =?UTF-8?q?=E5=BA=B7=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .umirc.ts | 1 + back/protos/cron.proto | 8 +- back/protos/cron.ts | 58 +++---- back/protos/health.proto | 22 +++ back/protos/health.ts | 317 ++++++++++++++++++++++++++++++++++++ back/public.ts | 16 +- back/schedule/client.ts | 4 +- back/schedule/health.ts | 30 ++++ back/schedule/index.ts | 7 +- docker/Dockerfile | 105 ++++++------ docker/docker-compose.yml | 5 + docker/docker-entrypoint.sh | 1 + docker/front.conf | 2 +- package.json | 2 +- shell/check.sh | 4 +- shell/share.sh | 1 + src/pages/error/index.less | 40 +++++ src/pages/error/index.tsx | 68 +++----- 18 files changed, 553 insertions(+), 138 deletions(-) create mode 100644 back/protos/health.proto create mode 100644 back/protos/health.ts create mode 100644 back/schedule/health.ts diff --git a/.umirc.ts b/.umirc.ts index 9845e263..13a2acc2 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -15,6 +15,7 @@ export default defineConfig({ '/api/public': { target: 'http://127.0.0.1:5400/', changeOrigin: true, + pathRewrite: { '^/api/public': '/api/' }, }, '/api': { target: 'http://127.0.0.1:5600/', diff --git a/back/protos/cron.proto b/back/protos/cron.proto index c269c0da..c1b0be46 100644 --- a/back/protos/cron.proto +++ b/back/protos/cron.proto @@ -2,21 +2,21 @@ syntax = "proto3"; package com.ql.cron; -service CronService { +service Cron { rpc addCron(AddCronRequest) returns (AddCronResponse); rpc delCron(DeleteCronRequest) returns (DeleteCronResponse); } -message Cron { +message ICron { string id = 1; string schedule = 2; string command = 3; } -message AddCronRequest { repeated Cron crons = 1; } +message AddCronRequest { repeated ICron crons = 1; } message AddCronResponse {} message DeleteCronRequest { repeated string ids = 1; } -message DeleteCronResponse {} \ No newline at end of file +message DeleteCronResponse {} diff --git a/back/protos/cron.ts b/back/protos/cron.ts index 5f312a95..cbbc2038 100644 --- a/back/protos/cron.ts +++ b/back/protos/cron.ts @@ -15,14 +15,14 @@ import _m0 from 'protobufjs/minimal'; export const protobufPackage = 'com.ql.cron'; -export interface Cron { +export interface ICron { id: string; schedule: string; command: string; } export interface AddCronRequest { - crons: Cron[]; + crons: ICron[]; } export interface AddCronResponse {} @@ -33,12 +33,12 @@ export interface DeleteCronRequest { export interface DeleteCronResponse {} -function createBaseCron(): Cron { +function createBaseICron(): ICron { return { id: '', schedule: '', command: '' }; } -export const Cron = { - encode(message: Cron, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { +export const ICron = { + encode(message: ICron, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { if (message.id !== '') { writer.uint32(10).string(message.id); } @@ -51,11 +51,11 @@ export const Cron = { return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): Cron { + decode(input: _m0.Reader | Uint8Array, length?: number): ICron { const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseCron(); + const message = createBaseICron(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -89,7 +89,7 @@ export const Cron = { return message; }, - fromJSON(object: any): Cron { + fromJSON(object: any): ICron { return { id: isSet(object.id) ? String(object.id) : '', schedule: isSet(object.schedule) ? String(object.schedule) : '', @@ -97,7 +97,7 @@ export const Cron = { }; }, - toJSON(message: Cron): unknown { + toJSON(message: ICron): unknown { const obj: any = {}; message.id !== undefined && (obj.id = message.id); message.schedule !== undefined && (obj.schedule = message.schedule); @@ -105,12 +105,12 @@ export const Cron = { return obj; }, - create, I>>(base?: I): Cron { - return Cron.fromPartial(base ?? {}); + create, I>>(base?: I): ICron { + return ICron.fromPartial(base ?? {}); }, - fromPartial, I>>(object: I): Cron { - const message = createBaseCron(); + fromPartial, I>>(object: I): ICron { + const message = createBaseICron(); message.id = object.id ?? ''; message.schedule = object.schedule ?? ''; message.command = object.command ?? ''; @@ -128,7 +128,7 @@ export const AddCronRequest = { writer: _m0.Writer = _m0.Writer.create(), ): _m0.Writer { for (const v of message.crons) { - Cron.encode(v!, writer.uint32(10).fork()).ldelim(); + ICron.encode(v!, writer.uint32(10).fork()).ldelim(); } return writer; }, @@ -146,7 +146,7 @@ export const AddCronRequest = { break; } - message.crons.push(Cron.decode(reader, reader.uint32())); + message.crons.push(ICron.decode(reader, reader.uint32())); continue; } if ((tag & 7) == 4 || tag == 0) { @@ -160,7 +160,7 @@ export const AddCronRequest = { fromJSON(object: any): AddCronRequest { return { crons: Array.isArray(object?.crons) - ? object.crons.map((e: any) => Cron.fromJSON(e)) + ? object.crons.map((e: any) => ICron.fromJSON(e)) : [], }; }, @@ -168,7 +168,7 @@ export const AddCronRequest = { toJSON(message: AddCronRequest): unknown { const obj: any = {}; if (message.crons) { - obj.crons = message.crons.map((e) => (e ? Cron.toJSON(e) : undefined)); + obj.crons = message.crons.map((e) => (e ? ICron.toJSON(e) : undefined)); } else { obj.crons = []; } @@ -185,7 +185,7 @@ export const AddCronRequest = { object: I, ): AddCronRequest { const message = createBaseAddCronRequest(); - message.crons = object.crons?.map((e) => Cron.fromPartial(e)) || []; + message.crons = object.crons?.map((e) => ICron.fromPartial(e)) || []; return message; }, }; @@ -366,10 +366,10 @@ export const DeleteCronResponse = { }, }; -export type CronServiceService = typeof CronServiceService; -export const CronServiceService = { +export type CronService = typeof CronService; +export const CronService = { addCron: { - path: '/com.ql.cron.CronService/addCron', + path: '/com.ql.cron.Cron/addCron', requestStream: false, responseStream: false, requestSerialize: (value: AddCronRequest) => @@ -380,7 +380,7 @@ export const CronServiceService = { responseDeserialize: (value: Buffer) => AddCronResponse.decode(value), }, delCron: { - path: '/com.ql.cron.CronService/delCron', + path: '/com.ql.cron.Cron/delCron', requestStream: false, responseStream: false, requestSerialize: (value: DeleteCronRequest) => @@ -392,12 +392,12 @@ export const CronServiceService = { }, } as const; -export interface CronServiceServer extends UntypedServiceImplementation { +export interface CronServer extends UntypedServiceImplementation { addCron: handleUnaryCall; delCron: handleUnaryCall; } -export interface CronServiceClient extends Client { +export interface CronClient extends Client { addCron( request: AddCronRequest, callback: (error: ServiceError | null, response: AddCronResponse) => void, @@ -439,16 +439,16 @@ export interface CronServiceClient extends Client { ): ClientUnaryCall; } -export const CronServiceClient = makeGenericClientConstructor( - CronServiceService, - 'com.ql.cron.CronService', +export const CronClient = makeGenericClientConstructor( + CronService, + 'com.ql.cron.Cron', ) as unknown as { new ( address: string, credentials: ChannelCredentials, options?: Partial, - ): CronServiceClient; - service: typeof CronServiceService; + ): CronClient; + service: typeof CronService; }; type Builtin = diff --git a/back/protos/health.proto b/back/protos/health.proto new file mode 100644 index 00000000..d85c4749 --- /dev/null +++ b/back/protos/health.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package com.ql.health; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + SERVICE_UNKNOWN = 3; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); + rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); +} diff --git a/back/protos/health.ts b/back/protos/health.ts new file mode 100644 index 00000000..b85e020e --- /dev/null +++ b/back/protos/health.ts @@ -0,0 +1,317 @@ +/* eslint-disable */ +import { + CallOptions, + ChannelCredentials, + Client, + ClientOptions, + ClientReadableStream, + ClientUnaryCall, + handleServerStreamingCall, + handleUnaryCall, + makeGenericClientConstructor, + Metadata, + ServiceError, + UntypedServiceImplementation, +} from '@grpc/grpc-js'; +import _m0 from 'protobufjs/minimal'; + +export const protobufPackage = 'com.ql.health'; + +export interface HealthCheckRequest { + service: string; +} + +export interface HealthCheckResponse { + status: HealthCheckResponse_ServingStatus; +} + +export enum HealthCheckResponse_ServingStatus { + UNKNOWN = 0, + SERVING = 1, + NOT_SERVING = 2, + SERVICE_UNKNOWN = 3, + UNRECOGNIZED = -1, +} + +export function healthCheckResponse_ServingStatusFromJSON( + object: any, +): HealthCheckResponse_ServingStatus { + switch (object) { + case 0: + case 'UNKNOWN': + return HealthCheckResponse_ServingStatus.UNKNOWN; + case 1: + case 'SERVING': + return HealthCheckResponse_ServingStatus.SERVING; + case 2: + case 'NOT_SERVING': + return HealthCheckResponse_ServingStatus.NOT_SERVING; + case 3: + case 'SERVICE_UNKNOWN': + return HealthCheckResponse_ServingStatus.SERVICE_UNKNOWN; + case -1: + case 'UNRECOGNIZED': + default: + return HealthCheckResponse_ServingStatus.UNRECOGNIZED; + } +} + +export function healthCheckResponse_ServingStatusToJSON( + object: HealthCheckResponse_ServingStatus, +): string { + switch (object) { + case HealthCheckResponse_ServingStatus.UNKNOWN: + return 'UNKNOWN'; + case HealthCheckResponse_ServingStatus.SERVING: + return 'SERVING'; + case HealthCheckResponse_ServingStatus.NOT_SERVING: + return 'NOT_SERVING'; + case HealthCheckResponse_ServingStatus.SERVICE_UNKNOWN: + return 'SERVICE_UNKNOWN'; + case HealthCheckResponse_ServingStatus.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +function createBaseHealthCheckRequest(): HealthCheckRequest { + return { service: '' }; +} + +export const HealthCheckRequest = { + encode( + message: HealthCheckRequest, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.service !== '') { + writer.uint32(10).string(message.service); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckRequest { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHealthCheckRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag != 10) { + break; + } + + message.service = reader.string(); + continue; + } + if ((tag & 7) == 4 || tag == 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): HealthCheckRequest { + return { service: isSet(object.service) ? String(object.service) : '' }; + }, + + toJSON(message: HealthCheckRequest): unknown { + const obj: any = {}; + message.service !== undefined && (obj.service = message.service); + return obj; + }, + + create, I>>( + base?: I, + ): HealthCheckRequest { + return HealthCheckRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): HealthCheckRequest { + const message = createBaseHealthCheckRequest(); + message.service = object.service ?? ''; + return message; + }, +}; + +function createBaseHealthCheckResponse(): HealthCheckResponse { + return { status: 0 }; +} + +export const HealthCheckResponse = { + encode( + message: HealthCheckResponse, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.status !== 0) { + writer.uint32(8).int32(message.status); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckResponse { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHealthCheckResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag != 8) { + break; + } + + message.status = reader.int32() as any; + continue; + } + if ((tag & 7) == 4 || tag == 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): HealthCheckResponse { + return { + status: isSet(object.status) + ? healthCheckResponse_ServingStatusFromJSON(object.status) + : 0, + }; + }, + + toJSON(message: HealthCheckResponse): unknown { + const obj: any = {}; + message.status !== undefined && + (obj.status = healthCheckResponse_ServingStatusToJSON(message.status)); + return obj; + }, + + create, I>>( + base?: I, + ): HealthCheckResponse { + return HealthCheckResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): HealthCheckResponse { + const message = createBaseHealthCheckResponse(); + message.status = object.status ?? 0; + return message; + }, +}; + +export type HealthService = typeof HealthService; +export const HealthService = { + check: { + path: '/com.ql.health.Health/Check', + requestStream: false, + responseStream: false, + requestSerialize: (value: HealthCheckRequest) => + Buffer.from(HealthCheckRequest.encode(value).finish()), + requestDeserialize: (value: Buffer) => HealthCheckRequest.decode(value), + responseSerialize: (value: HealthCheckResponse) => + Buffer.from(HealthCheckResponse.encode(value).finish()), + responseDeserialize: (value: Buffer) => HealthCheckResponse.decode(value), + }, + watch: { + path: '/com.ql.health.Health/Watch', + requestStream: false, + responseStream: true, + requestSerialize: (value: HealthCheckRequest) => + Buffer.from(HealthCheckRequest.encode(value).finish()), + requestDeserialize: (value: Buffer) => HealthCheckRequest.decode(value), + responseSerialize: (value: HealthCheckResponse) => + Buffer.from(HealthCheckResponse.encode(value).finish()), + responseDeserialize: (value: Buffer) => HealthCheckResponse.decode(value), + }, +} as const; + +export interface HealthServer extends UntypedServiceImplementation { + check: handleUnaryCall; + watch: handleServerStreamingCall; +} + +export interface HealthClient extends Client { + check( + request: HealthCheckRequest, + callback: ( + error: ServiceError | null, + response: HealthCheckResponse, + ) => void, + ): ClientUnaryCall; + check( + request: HealthCheckRequest, + metadata: Metadata, + callback: ( + error: ServiceError | null, + response: HealthCheckResponse, + ) => void, + ): ClientUnaryCall; + check( + request: HealthCheckRequest, + metadata: Metadata, + options: Partial, + callback: ( + error: ServiceError | null, + response: HealthCheckResponse, + ) => void, + ): ClientUnaryCall; + watch( + request: HealthCheckRequest, + options?: Partial, + ): ClientReadableStream; + watch( + request: HealthCheckRequest, + metadata?: Metadata, + options?: Partial, + ): ClientReadableStream; +} + +export const HealthClient = makeGenericClientConstructor( + HealthService, + 'com.ql.health.Health', +) as unknown as { + new ( + address: string, + credentials: ChannelCredentials, + options?: Partial, + ): HealthClient; + service: typeof HealthService; +}; + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & { + [K in Exclude>]: never; + }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/back/public.ts b/back/public.ts index 3197ccfe..056f748c 100644 --- a/back/public.ts +++ b/back/public.ts @@ -2,15 +2,21 @@ import express from 'express'; import { exec } from 'child_process'; import Logger from './loaders/logger'; import config from './config'; +import { HealthClient } from './protos/health'; +import { credentials } from '@grpc/grpc-js'; const app = express(); +const client = new HealthClient( + `localhost:${config.cronPort}`, + credentials.createInsecure(), +); -app.get('/api/public/panel/log', (req, res) => { - exec('tail -n 300 ~/.pm2/logs/panel-error.log', (err, stdout, stderr) => { - if (err || stderr) { - return res.send({ code: 400, message: (err && err.message) || stderr }); +app.get('/api/health', (req, res) => { + client.check({ service: 'cron' }, (err, response) => { + if (err) { + return res.status(500).send({ error: err }); } - return res.send({ code: 200, data: stdout }); + return res.status(200).send({ data: response }); }); }); diff --git a/back/schedule/client.ts b/back/schedule/client.ts index 2360ffc7..e9d04b77 100644 --- a/back/schedule/client.ts +++ b/back/schedule/client.ts @@ -2,14 +2,14 @@ import { credentials } from '@grpc/grpc-js'; import { AddCronRequest, AddCronResponse, - CronServiceClient, + CronClient, DeleteCronRequest, DeleteCronResponse, } from '../protos/cron'; import config from '../config'; class Client { - private client = new CronServiceClient( + private client = new CronClient( `localhost:${config.cronPort}`, credentials.createInsecure(), ); diff --git a/back/schedule/health.ts b/back/schedule/health.ts new file mode 100644 index 00000000..f9b3731e --- /dev/null +++ b/back/schedule/health.ts @@ -0,0 +1,30 @@ +import { ServerUnaryCall, sendUnaryData } from '@grpc/grpc-js'; +import { HealthCheckRequest, HealthCheckResponse } from '../protos/health'; +import { exec } from 'child_process'; +import config from '../config'; +import { promiseExec } from '../config/util'; + +const check = async ( + call: ServerUnaryCall, + callback: sendUnaryData, +) => { + switch (call.request.service) { + case 'cron': + const res = await promiseExec( + `curl -sf http://localhost:${config.port}/api/system`, + ); + console.log(res); + if (res.includes('200')) { + return callback(null, { status: 1 }); + } + const errLog = await promiseExec( + `tail -n 300 ~/.pm2/logs/panel-error.log`, + ); + return callback(new Error(errLog)); + + default: + return callback(null, { status: 1 }); + } +}; + +export { check }; diff --git a/back/schedule/index.ts b/back/schedule/index.ts index fb1c6797..cb4e4e82 100644 --- a/back/schedule/index.ts +++ b/back/schedule/index.ts @@ -1,12 +1,15 @@ import { Server, ServerCredentials } from '@grpc/grpc-js'; -import { CronServiceService } from '../protos/cron'; +import { CronService } from '../protos/cron'; import { addCron } from './addCron'; import { delCron } from './delCron'; +import { HealthService } from '../protos/health'; +import { check } from './health'; import config from '../config'; import Logger from '../loaders/logger'; const server = new Server(); -server.addService(CronServiceService, { addCron, delCron }); +server.addService(HealthService, { check }); +server.addService(CronService, { addCron, delCron }); server.bindAsync( `localhost:${config.cronPort}`, ServerCredentials.createInsecure(), diff --git a/docker/Dockerfile b/docker/Dockerfile index f5015fd8..624faae1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,11 +1,11 @@ FROM python:3.10-alpine as builder COPY package.json .npmrc pnpm-lock.yaml /tmp/build/ RUN set -x \ - && apk update \ - && apk add nodejs npm git \ - && npm i -g pnpm \ - && cd /tmp/build \ - && pnpm install --prod + && apk update \ + && apk add nodejs npm git \ + && npm i -g pnpm \ + && cd /tmp/build \ + && pnpm install --prod FROM python:3.10-alpine @@ -15,59 +15,62 @@ ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git ARG QL_BRANCH=develop ENV PNPM_HOME=/root/.local/share/pnpm \ - PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/share/pnpm:/root/.local/share/pnpm/global/5/node_modules:$PNPM_HOME \ - NODE_PATH=/usr/local/bin:/usr/local/pnpm-global/5/node_modules:/usr/local/lib/node_modules:/root/.local/share/pnpm/global/5/node_modules \ - LANG=C.UTF-8 \ - SHELL=/bin/bash \ - PS1="\u@\h:\w \$ " \ - QL_DIR=/ql \ - QL_BRANCH=${QL_BRANCH} + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/share/pnpm:/root/.local/share/pnpm/global/5/node_modules:$PNPM_HOME \ + NODE_PATH=/usr/local/bin:/usr/local/pnpm-global/5/node_modules:/usr/local/lib/node_modules:/root/.local/share/pnpm/global/5/node_modules \ + LANG=C.UTF-8 \ + SHELL=/bin/bash \ + PS1="\u@\h:\w \$ " \ + QL_DIR=/ql \ + QL_BRANCH=${QL_BRANCH} RUN set -x \ - && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \ - && apk update -f \ - && apk upgrade \ - && apk --no-cache add -f bash \ - coreutils \ - moreutils \ - git \ - curl \ - wget \ - tzdata \ - perl \ - openssl \ - nginx \ - nodejs \ - jq \ - openssh \ - npm \ - && rm -rf /var/cache/apk/* \ - && apk update \ - && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ - && echo "Asia/Shanghai" > /etc/timezone \ - && git config --global user.email "qinglong@@users.noreply.github.com" \ - && git config --global user.name "qinglong" \ - && git config --global http.postBuffer 524288000 \ - && npm install -g pnpm \ - && pnpm add -g pm2 tsx \ - && rm -rf /root/.pnpm-store \ - && rm -rf /root/.local/share/pnpm/store \ - && rm -rf /root/.cache \ - && rm -rf /root/.npm + && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \ + && apk update -f \ + && apk upgrade \ + && apk --no-cache add -f bash \ + coreutils \ + moreutils \ + git \ + curl \ + wget \ + tzdata \ + perl \ + openssl \ + nginx \ + nodejs \ + jq \ + openssh \ + npm \ + && rm -rf /var/cache/apk/* \ + && apk update \ + && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone \ + && git config --global user.email "qinglong@@users.noreply.github.com" \ + && git config --global user.name "qinglong" \ + && git config --global http.postBuffer 524288000 \ + && npm install -g pnpm \ + && pnpm add -g pm2 tsx \ + && rm -rf /root/.pnpm-store \ + && rm -rf /root/.local/share/pnpm/store \ + && rm -rf /root/.cache \ + && rm -rf /root/.npm ARG SOURCE_COMMIT RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \ - && cd ${QL_DIR} \ - && cp -f .env.example .env \ - && chmod 777 ${QL_DIR}/shell/*.sh \ - && chmod 777 ${QL_DIR}/docker/*.sh \ - && git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \ - && mkdir -p ${QL_DIR}/static \ - && cp -rf /static/* ${QL_DIR}/static \ - && rm -rf /static + && cd ${QL_DIR} \ + && cp -f .env.example .env \ + && chmod 777 ${QL_DIR}/shell/*.sh \ + && chmod 777 ${QL_DIR}/docker/*.sh \ + && git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \ + && mkdir -p ${QL_DIR}/static \ + && cp -rf /static/* ${QL_DIR}/static \ + && rm -rf /static COPY --from=builder /tmp/build/node_modules/. /ql/node_modules/ WORKDIR ${QL_DIR} - + +HEALTHCHECK --interval=5s --timeout=2s --retries=10 \ + CMD curl -sf http://127.0.0.1:5400/api/health || exit 1 + ENTRYPOINT ["./docker/docker-entrypoint.sh"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8e2b08a6..ba8bfd78 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -7,3 +7,8 @@ services: ports: - "0.0.0.0:5700:5700" restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-sf", "http://127.0.0.1:5400/api/health", "||", "exit", "1"] + interval: 2m + timeout: 10s + retries: 3 diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 53699883..4b65b490 100755 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -12,6 +12,7 @@ make_dir /run/nginx init_nginx pm2 l &>/dev/null +pm2 flush &>/dev/null echo -e "======================2. 安装依赖========================\n" patch_version diff --git a/docker/front.conf b/docker/front.conf index 5d64a2de..dddcbeef 100644 --- a/docker/front.conf +++ b/docker/front.conf @@ -21,7 +21,7 @@ server { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass http://publicApi/api/public/; + proxy_pass http://publicApi/api/; } location QL_BASE_URL/api/ { diff --git a/package.json b/package.json index 45e78614..9560b3ee 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "panel": "npm run build:back && node static/build/app.js", "schedule": "npm run build:back && node static/build/schedule/index.js", "public": "npm run build:back && node static/build/public.js", - "gen:proto": "protoc --experimental_allow_proto3_optional --plugin=./node_modules/.bin/protoc-gen-ts_proto ./back/protos/cron.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", "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", "postinstall": "max setup 2>/dev/null || true", "test": "umi-test", diff --git a/shell/check.sh b/shell/check.sh index ba3c194f..97fe1312 100755 --- a/shell/check.sh +++ b/shell/check.sh @@ -33,8 +33,8 @@ pm2_log() { echo -e "---> pm2日志" local panelOut="/root/.pm2/logs/panel-out.log" local panelError="/root/.pm2/logs/panel-error.log" - tail -n 100 "$panelOut" - tail -n 100 "$panelError" + tail -n 300 "$panelOut" + tail -n 300 "$panelError" } check_nginx() { diff --git a/shell/share.sh b/shell/share.sh index bc990aca..2455036e 100755 --- a/shell/share.sh +++ b/shell/share.sh @@ -382,6 +382,7 @@ random_range() { reload_pm2() { pm2 l &>/dev/null + pm2 flush &>/dev/null echo -e "启动面板服务\n" pm2 delete panel --source-map-support --time &>/dev/null diff --git a/src/pages/error/index.less b/src/pages/error/index.less index 23c2b0a3..398773f6 100644 --- a/src/pages/error/index.less +++ b/src/pages/error/index.less @@ -9,4 +9,44 @@ height: calc(100vh - 80px); overflow-y: auto; } + + .code-box { + position: relative; + display: inline-block; + width: 80%; + margin: 16px; + background-color: #ffffff; + border: 1px solid rgba(5, 5, 5, 0.06); + border-radius: 6px; + -webkit-transition: all 0.2s; + transition: all 0.2s; + border-radius: 6px 6px 0 0; + color: rgba(0, 0, 0, 0.88); + border-bottom: 1px solid rgba(5, 5, 5, 0.06); + + .browser-markup { + position: relative; + border-top: 2em solid rgba(230, 230, 230, 0.7); + border-radius: 3px 3px 0 0; + + &::before { + position: absolute; + top: -1.25em; + left: 1em; + display: block; + width: 0.5em; + height: 0.5em; + background-color: #f44; + border-radius: 50%; + box-shadow: 0 0 0 2px #f44, 1.5em 0 0 2px #9b3, 3em 0 0 2px #fb5; + content: ''; + } + } + + .log { + height: calc(100vh - 150px); + overflow-y: auto; + padding: 12px; + } + } } diff --git a/src/pages/error/index.tsx b/src/pages/error/index.tsx index bf539796..396b01c3 100644 --- a/src/pages/error/index.tsx +++ b/src/pages/error/index.tsx @@ -1,43 +1,37 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import config from '@/utils/config'; import { request } from '@/utils/http'; -import Terminal, { ColorMode, LineType } from '../../components/terminal'; import { PageLoading } from '@ant-design/pro-layout'; import { history, useOutletContext } from '@umijs/max'; -import Ansi from 'ansi-to-react'; import './index.less'; import { SharedContext } from '@/layouts'; +import { Alert, Typography } from 'antd'; const Error = () => { const { user, theme, reloadUser } = useOutletContext(); const [loading, setLoading] = useState(false); const [data, setData] = useState('暂无日志'); + const retryTimes = useRef(1); - const getTimes = () => { - return parseInt(localStorage.getItem('error_retry_times') || '0', 10); - }; - - let times = getTimes(); + console.log(retryTimes.current); const getLog = (needLoading: boolean = true) => { needLoading && setLoading(true); request - .get(`${config.apiPrefix}public/panel/log`) - .then(({ code, data }) => { - if (code === 200) { - setData(data); - if (!data) { - times = getTimes(); - if (times > 5) { - return; - } - localStorage.setItem('error_retry_times', `${times + 1}`); - setTimeout(() => { - reloadUser(); - getLog(false); - }, 3000); - } + .get(`${config.apiPrefix}public/health`) + .then(({ status, error }) => { + if (status === 1) { + return reloadUser(); } + if (retryTimes.current > 3) { + return; + } + setData(error.details); + retryTimes.current += 1; + setTimeout(() => { + reloadUser(); + getLog(false); + }, 3000); }) .finally(() => needLoading && setLoading(false)); }; @@ -56,24 +50,16 @@ const Error = () => {
{loading ? ( - ) : data ? ( - - {data} - - ), - }, - ]} - /> - ) : times > 5 ? ( - <>服务启动超时,请手动进入容器执行 ql -l check 后刷新再试 + ) : retryTimes.current < 3 ? ( +
+
+ + {data} +
) : ( )}