[core] API to restart server (actually it's /stop, but docker will handle restart)
This commit is contained in:
1 parent
fb774f2caa
commit
9253e54958
5 files changed
+130
-15
No files matched your search
@@ -1,20 +1,36 @@
|
||||
import * as process from 'node:process';
|
||||
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Logger,
|
||||
Post,
|
||||
Query,
|
||||
UsePipes,
|
||||
} from '@nestjs/common';
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { parseBool } from '@waha/helpers';
|
||||
import { getApp, getAppModuleInstance } from '@waha/main';
|
||||
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
|
||||
import { WAHAEnvironment } from '@waha/structures/environment.dto';
|
||||
import { EnvironmentQuery } from '@waha/structures/server.dto';
|
||||
import {
|
||||
EnvironmentQuery,
|
||||
ServerStatusResponse,
|
||||
StopRequest,
|
||||
} from '@waha/structures/server.dto';
|
||||
import { VERSION } from '@waha/version';
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
interface EnvironmentVariables {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
@Controller('api/server')
|
||||
@ApiTags('other')
|
||||
export class ServerController {
|
||||
private logger: Logger;
|
||||
|
||||
constructor() {
|
||||
this.logger = new Logger('ServerController');
|
||||
}
|
||||
|
||||
@Get('version')
|
||||
@ApiOperation({ summary: 'Get the version of the server' })
|
||||
get(): WAHAEnvironment {
|
||||
@@ -23,11 +39,12 @@ export class ServerController {
|
||||
|
||||
@Get('environment')
|
||||
@ApiOperation({ summary: 'Return the server environment' })
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
environment(@Query() query: EnvironmentQuery): object {
|
||||
environment(
|
||||
@Query(new WAHAValidationPipe()) query: EnvironmentQuery,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
): object {
|
||||
let result = process.env;
|
||||
const all = parseBool(query.all);
|
||||
if (!all) {
|
||||
if (!query.all) {
|
||||
result = lodash.pickBy(result, (value, key) => {
|
||||
return (
|
||||
key.startsWith('WAHA_') ||
|
||||
@@ -45,4 +62,44 @@ export class ServerController {
|
||||
});
|
||||
return Object.fromEntries(map);
|
||||
}
|
||||
|
||||
@Get('status')
|
||||
@ApiOperation({ summary: 'The server status' })
|
||||
async status(): Promise<ServerStatusResponse> {
|
||||
const appModule = await getAppModuleInstance();
|
||||
if (!appModule) {
|
||||
throw new UnprocessableEntityException('AppModule not found');
|
||||
}
|
||||
const now = Date.now();
|
||||
const startTimestamp = appModule.startTimestamp;
|
||||
return {
|
||||
startTimestamp: startTimestamp,
|
||||
uptime: now - startTimestamp,
|
||||
};
|
||||
}
|
||||
|
||||
@Post('stop')
|
||||
@ApiOperation({ summary: 'Stop the server' })
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async stop(@Body() request: StopRequest) {
|
||||
const timeout = 1_000;
|
||||
if (request.force) {
|
||||
this.logger.log(`Force stopping the server in ${timeout}ms`);
|
||||
setTimeout(() => {
|
||||
this.logger.log('Force stopping the server');
|
||||
process.kill(process.pid, 'SIGKILL');
|
||||
}, timeout);
|
||||
} else {
|
||||
this.logger.log(`Gracefully stopping the server in ${timeout}ms`);
|
||||
setTimeout(async () => {
|
||||
this.logger.log('Gracefully closing the application...');
|
||||
const app = getApp();
|
||||
if (app) {
|
||||
await app.close();
|
||||
}
|
||||
this.logger.log('Application closed');
|
||||
process.exit(0);
|
||||
}, timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ export class WebsocketGatewayCore
|
||||
this.logger.log('Shutting down websocket server');
|
||||
// Allow pending messages to be sent, it can be even 1ms, just to release the event loop
|
||||
await sleep(100);
|
||||
this.server.clients.forEach((options, client) => {
|
||||
this.server?.clients.forEach((options, client) => {
|
||||
client.close(1001, 'Server is shutting down');
|
||||
});
|
||||
// Do not turn off heartbeat service here,
|
||||
|
||||
@@ -140,7 +140,11 @@ const PROVIDERS = [
|
||||
providers: PROVIDERS,
|
||||
})
|
||||
export class AppModuleCore {
|
||||
constructor(protected config: WhatsappConfigService) {}
|
||||
public startTimestamp: number;
|
||||
|
||||
constructor(protected config: WhatsappConfigService) {
|
||||
this.startTimestamp = Date.now();
|
||||
}
|
||||
|
||||
static getHttpsOptions(logger: Logger) {
|
||||
return undefined;
|
||||
|
||||
+23
-2
@@ -34,7 +34,6 @@ async function loadModules(): Promise<
|
||||
[typeof AppModuleCore, typeof SwaggerConfiguratorCore]
|
||||
> {
|
||||
const version = getWAHAVersion();
|
||||
logger.info(`WAHA (WhatsApp HTTP API) - Running ${version} version...`);
|
||||
|
||||
if (version === WAHAVersion.CORE) {
|
||||
const { AppModuleCore } = await import('./core/app.module.core');
|
||||
@@ -52,10 +51,32 @@ async function loadModules(): Promise<
|
||||
return [AppModulePlus, SwaggerConfiguratorPlus];
|
||||
}
|
||||
|
||||
let app = undefined;
|
||||
|
||||
export function getApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AppModule instance (depends on the version)
|
||||
*/
|
||||
export async function getAppModuleInstance(): Promise<
|
||||
AppModuleCore | undefined
|
||||
> {
|
||||
const [AppModule, _] = await loadModules();
|
||||
const app = getApp();
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
return app.get(AppModule);
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
const version = getWAHAVersion();
|
||||
logger.info(`WAHA (WhatsApp HTTP API) - Running ${version} version...`);
|
||||
const [AppModule, SwaggerModule] = await loadModules();
|
||||
const httpsOptions = AppModule.getHttpsOptions(logger);
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
app = await NestFactory.create(AppModule, {
|
||||
logger: getNestJSLogLevels(),
|
||||
httpsOptions: httpsOptions,
|
||||
bufferLogs: true,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { BooleanString } from '@waha/nestjs/validation/BooleanString';
|
||||
import { Transform } from 'class-transformer';
|
||||
import { IsBoolean, IsOptional } from 'class-validator';
|
||||
|
||||
export class EnvironmentQuery {
|
||||
@ApiProperty({
|
||||
@@ -6,5 +9,35 @@ export class EnvironmentQuery {
|
||||
required: false,
|
||||
description: 'Include all environment variables',
|
||||
})
|
||||
@Transform(BooleanString)
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
all: boolean = false;
|
||||
}
|
||||
|
||||
export class StopRequest {
|
||||
@ApiProperty({
|
||||
example: false,
|
||||
required: false,
|
||||
description:
|
||||
'By default, it gracefully stops the server, ' +
|
||||
'but you can force it to terminate immediately.',
|
||||
})
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
force: boolean = false;
|
||||
}
|
||||
|
||||
export class ServerStatusResponse {
|
||||
@ApiProperty({
|
||||
example: 1723788847247,
|
||||
description: 'The timestamp when the server started (milliseconds).',
|
||||
})
|
||||
startTimestamp: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: 3600000,
|
||||
description: 'The uptime of the server in milliseconds.',
|
||||
})
|
||||
uptime: number;
|
||||
}
|
||||
Reference in new issue
Block a user