Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5df21e165 | ||
|
|
792029b13f | ||
|
|
0ef49d7116 | ||
|
|
600d3291eb | ||
|
|
b78413190c | ||
|
|
c9729af139 | ||
|
|
697f267fc3 | ||
|
|
ac49ce3cc5 | ||
|
|
555daf6888 | ||
|
|
8f0c2994a6 | ||
|
|
873e66db19 | ||
|
|
38b542def4 | ||
|
|
a646e5efc0 | ||
|
|
31590c9229 | ||
|
|
98c2172908 | ||
|
|
314b02daf1 | ||
|
|
d2ab78b65c | ||
|
|
db2c352fd1 | ||
|
|
6e79ad2793 | ||
|
|
77eaa287fe |
No files matched your search
@@ -128,11 +128,6 @@ jobs:
|
||||
run: |
|
||||
docker run -d \
|
||||
--name smoke \
|
||||
--health-cmd "/bin/bash /ping-check.sh" \
|
||||
--health-interval 1s \
|
||||
--health-retries 60 \
|
||||
--health-timeout 2s \
|
||||
--health-start-period 2s \
|
||||
--rm -p3000:3000 \
|
||||
${{ vars.DOCKER_IMAGE }}:${{ matrix.tag }}
|
||||
|
||||
|
||||
@@ -200,10 +200,6 @@ ENV WAHA_GOWS_SOCKET /tmp/gows.sock
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Add healthcheck script
|
||||
COPY ping-check.sh /ping-check.sh
|
||||
RUN chmod +x /ping-check.sh
|
||||
|
||||
# Chokidar options to monitor file changes
|
||||
ENV CHOKIDAR_USEPOLLING=1
|
||||
ENV CHOKIDAR_INTERVAL=5000
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
build-all: build-plus build-chrome build-gows build-noweb
|
||||
|
||||
build:
|
||||
docker build . -t devlikeapro/waha
|
||||
|
||||
build-all: build-plus build-chrome build-gows build-noweb
|
||||
build-plus:
|
||||
docker build . -t devlikeapro/waha-plus
|
||||
|
||||
build-chrome:
|
||||
docker build . -t devlikeapro/waha-plus:chrome --build-arg USE_BROWSER=chrome
|
||||
|
||||
build-noweb:
|
||||
docker build . -t devlikeapro/waha:noweb --build-arg USE_BROWSER=none --build-arg WHATSAPP_DEFAULT_ENGINE=NOWEB
|
||||
docker build . -t devlikeapro/waha-plus:noweb --build-arg USE_BROWSER=none --build-arg WHATSAPP_DEFAULT_ENGINE=NOWEB
|
||||
|
||||
build-gows:
|
||||
docker build . -t devlikeapro/waha-plus:gows --build-arg USE_BROWSER=none --build-arg WHATSAPP_DEFAULT_ENGINE=GOWS
|
||||
|
||||
build-all: build build-chrome build-noweb
|
||||
|
||||
build-plus:
|
||||
docker build . -t devlikeapro/waha-plus
|
||||
|
||||
build-ssh:
|
||||
# check IMAGE provided
|
||||
@[ "${IMAGE}" ] || ( echo "Add APP: make build-ssh image=devlikeapro/waha"; exit 1 );
|
||||
|
||||
+5
-6
@@ -7,12 +7,11 @@ services:
|
||||
image: devlikeapro/waha-plus
|
||||
# WAHA Core
|
||||
# image: devlikeapro/waha:latest
|
||||
healthcheck:
|
||||
test: ['CMD', '/bin/bash', '/ping-check.sh']
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
# Add "dns" if you have a problem with resolving "web.whatsapp.com"
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Try PORT first, then WHATSAPP_API_PORT, then default to 3000
|
||||
PORT=${PORT:-${WHATSAPP_API_PORT:-3000}}
|
||||
|
||||
# Check if HTTPS is enabled
|
||||
if [[ "${WAHA_HTTPS_ENABLED}" == "true" || "${WAHA_HTTPS_ENABLED}" == "1" ]]; then
|
||||
PROTOCOL="https"
|
||||
else
|
||||
PROTOCOL="http"
|
||||
fi
|
||||
|
||||
# Execute curl command with SSL verification disabled
|
||||
curl -f -s -k "${PROTOCOL}://127.0.0.1:${PORT}/ping" || exit 1
|
||||
@@ -1,15 +1,12 @@
|
||||
import * as process from 'node:process';
|
||||
import * as v8 from 'node:v8';
|
||||
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
Post,
|
||||
Query,
|
||||
StreamableFile,
|
||||
UsePipes,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
@@ -112,34 +109,3 @@ export class ServerController {
|
||||
return { stopping: true };
|
||||
}
|
||||
}
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/server/debug')
|
||||
@ApiTags('🔍 Observability')
|
||||
export class ServerDebugController {
|
||||
private logger: Logger;
|
||||
private readonly enabled: boolean;
|
||||
|
||||
constructor(protected config: WhatsappConfigService) {
|
||||
this.logger = new Logger('ServerDebugController');
|
||||
this.enabled = this.config.debugModeEnabled;
|
||||
}
|
||||
|
||||
@Get('heapsnapshot')
|
||||
@ApiOperation({
|
||||
summary: 'Return a heapsnapshot',
|
||||
description: "Return a heapsnapshot of the server's memory",
|
||||
})
|
||||
async heapsnapshot() {
|
||||
if (!this.enabled) {
|
||||
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
|
||||
}
|
||||
this.logger.log('Creating a heap snapshot...');
|
||||
const heap = v8.getHeapSnapshot();
|
||||
const fileName = `${Date.now()}.heapsnapshot`;
|
||||
return new StreamableFile(heap, {
|
||||
type: 'application/octet-stream',
|
||||
disposition: `attachment; filename=${fileName}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import * as v8 from 'node:v8';
|
||||
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
Query,
|
||||
StreamableFile,
|
||||
UsePipes,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { WhatsappConfigService } from '@waha/config.service';
|
||||
import { SessionManager } from '@waha/core/abc/manager.abc';
|
||||
import { WhatsappSession } from '@waha/core/abc/session.abc';
|
||||
import {
|
||||
SessionApiParam,
|
||||
WorkingSessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
|
||||
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
|
||||
import { createReadStream } from 'fs';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/server/debug')
|
||||
@ApiTags('🔍 Observability')
|
||||
export class ServerDebugController {
|
||||
private logger: Logger;
|
||||
private readonly enabled: boolean;
|
||||
|
||||
constructor(
|
||||
private config: WhatsappConfigService,
|
||||
private manager: SessionManager,
|
||||
) {
|
||||
this.logger = new Logger('ServerDebugController');
|
||||
this.enabled = this.config.debugModeEnabled;
|
||||
}
|
||||
|
||||
@Get('heapsnapshot')
|
||||
@ApiOperation({
|
||||
summary: 'Return a heapsnapshot for the current nodejs process',
|
||||
description: "Return a heapsnapshot of the server's memory",
|
||||
})
|
||||
async heapsnapshot() {
|
||||
if (!this.enabled) {
|
||||
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
|
||||
}
|
||||
this.logger.log('Creating a heap snapshot...');
|
||||
const heap = v8.getHeapSnapshot();
|
||||
const fileName = `${Date.now()}.heapsnapshot`;
|
||||
return new StreamableFile(heap, {
|
||||
type: 'application/octet-stream',
|
||||
disposition: `attachment; filename=${fileName}`,
|
||||
});
|
||||
}
|
||||
|
||||
@Get('browser/trace/:session')
|
||||
@ApiOperation({
|
||||
summary: 'Collect and get a trace.json for Chrome DevTools ',
|
||||
description: 'Uses https://pptr.dev/api/puppeteer.tracing',
|
||||
})
|
||||
@SessionApiParam
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async browserTrace(
|
||||
@WorkingSessionParam session: WhatsappSession,
|
||||
@Query() query: BrowserTraceQuery,
|
||||
) {
|
||||
if (!this.enabled) {
|
||||
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
|
||||
}
|
||||
const filepath = await session.browserTrace(query);
|
||||
const stream = createReadStream(filepath);
|
||||
const filename = `trace - ${session.name} - ${new Date()}.json`;
|
||||
return new StreamableFile(stream, {
|
||||
type: 'application/octet-stream',
|
||||
disposition: `attachment; filename=${filename}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import { Label, LabelDTO, LabelID } from '@waha/structures/labels.dto';
|
||||
import { LidToPhoneNumber } from '@waha/structures/lids.dto';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { MessageSource, WAMessage } from '@waha/structures/responses.dto';
|
||||
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
|
||||
import { DefaultMap } from '@waha/utils/DefaultMap';
|
||||
import { generatePrefixedId } from '@waha/utils/ids';
|
||||
import { LoggerBuilder } from '@waha/utils/logging';
|
||||
@@ -285,7 +286,6 @@ export abstract class WhatsappSession {
|
||||
'--aggressive-cache-discard',
|
||||
'--disable-accelerated-2d-canvas',
|
||||
'--disable-application-cache',
|
||||
// '--disable-background-networking', // COMMENTED to test WEBJS stability
|
||||
// DO NOT disable software rasterizer, it will break the video
|
||||
// https://github.com/devlikeapro/waha/issues/629
|
||||
// '--disable-software-rasterizer',
|
||||
@@ -296,7 +296,7 @@ export abstract class WhatsappSession {
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-extensions',
|
||||
// '--disable-features=site-per-process', // COMMENTED to test WEBJS stability
|
||||
//'--disable-gpu', // COMMENTED to test WEBJS stability
|
||||
'--disable-gpu', // COMMENTED to test WEBJS stability
|
||||
'--disable-offer-store-unmasked-wallet-cards',
|
||||
'--disable-offline-load-stale-cache',
|
||||
'--disable-popup-blocking',
|
||||
@@ -320,9 +320,8 @@ export abstract class WhatsappSession {
|
||||
'--no-sandbox',
|
||||
'--no-zygote',
|
||||
'--password-store=basic',
|
||||
// '--renderer-process-limit=2', // COMMENTED to test WEBJS stability
|
||||
'--renderer-process-limit=1',
|
||||
'--safebrowsing-disable-auto-update',
|
||||
// '--single-process',
|
||||
'--use-mock-keychain',
|
||||
'--window-size=1280,720',
|
||||
];
|
||||
@@ -351,6 +350,10 @@ export abstract class WhatsappSession {
|
||||
* START - Methods for API
|
||||
*/
|
||||
|
||||
public browserTrace(query: BrowserTraceQuery): Promise<string> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth methods
|
||||
*/
|
||||
|
||||
@@ -10,10 +10,8 @@ import { TerminusModule } from '@nestjs/terminus';
|
||||
import { ChannelsController } from '@waha/api/channels.controller';
|
||||
import { LidsController } from '@waha/api/lids.controller';
|
||||
import { ProfileController } from '@waha/api/profile.controller';
|
||||
import {
|
||||
ServerController,
|
||||
ServerDebugController,
|
||||
} from '@waha/api/server.controller';
|
||||
import { ServerController } from '@waha/api/server.controller';
|
||||
import { ServerDebugController } from '@waha/api/server.debug.controller';
|
||||
import { WebsocketGatewayCore } from '@waha/api/websocket.gateway.core';
|
||||
import { ApiKeyStrategy } from '@waha/core/auth/apiKey.strategy';
|
||||
import { IApiKeyAuth } from '@waha/core/auth/auth';
|
||||
|
||||
@@ -7031,6 +7031,8 @@ export namespace messages {
|
||||
constructor(data?: any[] | {
|
||||
session?: Session;
|
||||
message?: string;
|
||||
jid?: string;
|
||||
messageId?: string;
|
||||
}) {
|
||||
super();
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
|
||||
@@ -7041,6 +7043,12 @@ export namespace messages {
|
||||
if ("message" in data && data.message != undefined) {
|
||||
this.message = data.message;
|
||||
}
|
||||
if ("jid" in data && data.jid != undefined) {
|
||||
this.jid = data.jid;
|
||||
}
|
||||
if ("messageId" in data && data.messageId != undefined) {
|
||||
this.messageId = data.messageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
get session() {
|
||||
@@ -7058,9 +7066,23 @@ export namespace messages {
|
||||
set message(value: string) {
|
||||
pb_1.Message.setField(this, 2, value);
|
||||
}
|
||||
get jid() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 3, "") as string;
|
||||
}
|
||||
set jid(value: string) {
|
||||
pb_1.Message.setField(this, 3, value);
|
||||
}
|
||||
get messageId() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 4, "") as string;
|
||||
}
|
||||
set messageId(value: string) {
|
||||
pb_1.Message.setField(this, 4, value);
|
||||
}
|
||||
static fromObject(data: {
|
||||
session?: ReturnType<typeof Session.prototype.toObject>;
|
||||
message?: string;
|
||||
jid?: string;
|
||||
messageId?: string;
|
||||
}): DownloadMediaRequest {
|
||||
const message = new DownloadMediaRequest({});
|
||||
if (data.session != null) {
|
||||
@@ -7069,12 +7091,20 @@ export namespace messages {
|
||||
if (data.message != null) {
|
||||
message.message = data.message;
|
||||
}
|
||||
if (data.jid != null) {
|
||||
message.jid = data.jid;
|
||||
}
|
||||
if (data.messageId != null) {
|
||||
message.messageId = data.messageId;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
const data: {
|
||||
session?: ReturnType<typeof Session.prototype.toObject>;
|
||||
message?: string;
|
||||
jid?: string;
|
||||
messageId?: string;
|
||||
} = {};
|
||||
if (this.session != null) {
|
||||
data.session = this.session.toObject();
|
||||
@@ -7082,6 +7112,12 @@ export namespace messages {
|
||||
if (this.message != null) {
|
||||
data.message = this.message;
|
||||
}
|
||||
if (this.jid != null) {
|
||||
data.jid = this.jid;
|
||||
}
|
||||
if (this.messageId != null) {
|
||||
data.messageId = this.messageId;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
@@ -7092,6 +7128,10 @@ export namespace messages {
|
||||
writer.writeMessage(1, this.session, () => this.session.serialize(writer));
|
||||
if (this.message.length)
|
||||
writer.writeString(2, this.message);
|
||||
if (this.jid.length)
|
||||
writer.writeString(3, this.jid);
|
||||
if (this.messageId.length)
|
||||
writer.writeString(4, this.messageId);
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
@@ -7107,6 +7147,12 @@ export namespace messages {
|
||||
case 2:
|
||||
message.message = reader.readString();
|
||||
break;
|
||||
case 3:
|
||||
message.jid = reader.readString();
|
||||
break;
|
||||
case 4:
|
||||
message.messageId = reader.readString();
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14889,7 +14889,9 @@ proto.messages.DownloadMediaRequest.prototype.toObject = function(opt_includeIns
|
||||
proto.messages.DownloadMediaRequest.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
session: (f = msg.getSession()) && proto.messages.Session.toObject(includeInstance, f),
|
||||
message: jspb.Message.getFieldWithDefault(msg, 2, "")
|
||||
message: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
jid: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
messageid: jspb.Message.getFieldWithDefault(msg, 4, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -14935,6 +14937,14 @@ proto.messages.DownloadMediaRequest.deserializeBinaryFromReader = function(msg,
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setMessage(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setJid(value);
|
||||
break;
|
||||
case 4:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setMessageid(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -14979,6 +14989,20 @@ proto.messages.DownloadMediaRequest.serializeBinaryToWriter = function(message,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getJid();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getMessageid();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
4,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -15037,6 +15061,42 @@ proto.messages.DownloadMediaRequest.prototype.setMessage = function(value) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string jid = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.messages.DownloadMediaRequest.prototype.getJid = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.messages.DownloadMediaRequest} returns this
|
||||
*/
|
||||
proto.messages.DownloadMediaRequest.prototype.setJid = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string messageId = 4;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.messages.DownloadMediaRequest.prototype.getMessageid = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.messages.DownloadMediaRequest} returns this
|
||||
*/
|
||||
proto.messages.DownloadMediaRequest.prototype.setMessageid = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2152,6 +2152,8 @@ export class GOWSEngineMediaProcessor implements IMediaEngineProcessor<any> {
|
||||
// double "session" it's not a mistake here
|
||||
session: this.session.session,
|
||||
message: data,
|
||||
jid: message.Info.Chat,
|
||||
messageId: message.Info.ID,
|
||||
});
|
||||
const response = await promisify(this.session.client.DownloadMedia)(
|
||||
request,
|
||||
|
||||
@@ -119,4 +119,8 @@ export class WPage extends EventEmitter {
|
||||
this.page.on(event, listener);
|
||||
return this.page as any;
|
||||
}
|
||||
|
||||
get tracing() {
|
||||
return this.page.tracing;
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,7 @@ import {
|
||||
WAHAPresenceData,
|
||||
} from '@waha/structures/presence.dto';
|
||||
import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
|
||||
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
|
||||
import { MeInfo } from '@waha/structures/sessions.dto';
|
||||
import { StatusRequest, TextStatus } from '@waha/structures/status.dto';
|
||||
import {
|
||||
@@ -117,7 +118,9 @@ import {
|
||||
import { PaginatorInMemory } from '@waha/utils/Paginator';
|
||||
import { sleep, waitUntil } from '@waha/utils/promiseTimeout';
|
||||
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
|
||||
import { TmpDir } from '@waha/utils/tmpdir';
|
||||
import * as lodash from 'lodash';
|
||||
import * as path from 'path';
|
||||
import { ProtocolError } from 'puppeteer';
|
||||
import { filter, fromEvent, merge, mergeMap, Observable, share } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
@@ -194,13 +197,19 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
if (cacheType === 'local') {
|
||||
this.logger.info(`Using web version: '${webVersion}'`);
|
||||
}
|
||||
const args = this.getBrowserArgsForPuppeteer();
|
||||
// add at the start
|
||||
args.unshift(`--a-waha-timestamp=${new Date()}`);
|
||||
args.unshift(`--a-waha-session=${this.name}`);
|
||||
return {
|
||||
puppeteer: {
|
||||
headless: true,
|
||||
executablePath: this.getBrowserExecutablePath(),
|
||||
args: this.getBrowserArgsForPuppeteer(),
|
||||
args: args,
|
||||
dumpio: this.isDebugEnabled(),
|
||||
},
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
|
||||
webVersion: webVersion,
|
||||
webVersionCache: {
|
||||
type: cacheType,
|
||||
@@ -513,6 +522,23 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
public async browserTrace(query: BrowserTraceQuery): Promise<string> {
|
||||
const tmpdir = new TmpDir(
|
||||
this.logger,
|
||||
`waha-browser-trace-${this.name}-`,
|
||||
(10 * query.seconds + 120) * 1000,
|
||||
);
|
||||
const page = this.whatsapp.pupPage;
|
||||
return await tmpdir.use(async (dir) => {
|
||||
this.logger.info({ query }, `Starting browser tracing...`);
|
||||
const filepath = path.join(dir, 'trace.json');
|
||||
await page.tracing.start({ path: filepath });
|
||||
await sleep(query.seconds * 1000);
|
||||
await page.tracing.stop();
|
||||
this.logger.info(`Browser tracing finished, saved to ${filepath}`);
|
||||
return filepath;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth methods
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class BrowserTraceQuery {
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@ApiProperty({
|
||||
description: 'How many seconds to trace',
|
||||
example: 30,
|
||||
required: true,
|
||||
})
|
||||
seconds: number = 30;
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@Transform(({ value }) => (Array.isArray(value) ? value : [value]))
|
||||
@IsOptional()
|
||||
@ApiProperty({
|
||||
description: 'Categories to trace (all by default)',
|
||||
example: ['*'],
|
||||
required: true,
|
||||
})
|
||||
categories: string[] = ['*'];
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { Logger } from 'pino';
|
||||
|
||||
function sanitize(input: string) {
|
||||
return input.replace(/[^a-zA-Z0-9-_]/g, '_');
|
||||
}
|
||||
|
||||
export class TmpDir {
|
||||
private readonly prefix: string;
|
||||
constructor(
|
||||
private logger: Logger,
|
||||
prefix: string,
|
||||
private cleanupTimeout = 10_000,
|
||||
) {
|
||||
this.prefix = sanitize(prefix);
|
||||
if (this.prefix.length > 249) {
|
||||
this.prefix = this.prefix.slice(0, 249);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary directory and passes it to the callback.
|
||||
* The directory is automatically cleaned up after the callback returns.
|
||||
* @param callback Function that receives the temporary directory path
|
||||
* @returns The result of the callback
|
||||
*/
|
||||
public async use<T>(callback: (dir: string) => Promise<T>): Promise<T> {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), this.prefix));
|
||||
try {
|
||||
return await callback(dir);
|
||||
} finally {
|
||||
setTimeout(async () => {
|
||||
fs.rm(dir, { recursive: true, force: true })
|
||||
.then(() => {
|
||||
this.logger.trace(`Cleaned up temporary directory '${dir}'`);
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.warn(
|
||||
`Failed to clean up temporary directory '${dir}': ${err.message}`,
|
||||
);
|
||||
});
|
||||
}, this.cleanupTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -33,7 +33,7 @@ export function getEngineName(): string {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2025.7.1',
|
||||
version: '2025.7.3',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser:
|
||||
|
||||
@@ -4,12 +4,4 @@ http:
|
||||
allow-insecure: true
|
||||
no-follow-redirects: true
|
||||
timeout: 5000
|
||||
body: ['version']
|
||||
|
||||
command:
|
||||
docker smoke health:
|
||||
exec: "docker inspect --format '{{`{{if .State.Health}}{{.State.Health.Status}}{{else}}no healthcheck{{end}}`}}' smoke"
|
||||
exit-status: 0
|
||||
stdout:
|
||||
- 'healthy'
|
||||
timeout: 5000
|
||||
body: ['version']
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"waha": {
|
||||
"gows": {
|
||||
"repo": "devlikeapro/gows",
|
||||
"ref": "v1.0.8"
|
||||
"ref": "v1.0.9"
|
||||
},
|
||||
"dashboard": {
|
||||
"repo": "devlikeapro/dashboard",
|
||||
|
||||
@@ -13399,7 +13399,7 @@ __metadata:
|
||||
|
||||
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-06-09":
|
||||
version: 1.30.1-alpha.2
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=48fcc59575b46386747673d1b01b8f89e8c49c2d"
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=c133ae4a347ba7b21b647c8c6811f58c55b21069"
|
||||
dependencies:
|
||||
"@pedroslopez/moduleraid": ^5.0.2
|
||||
archiver: ^5.3.1
|
||||
@@ -13417,7 +13417,7 @@ __metadata:
|
||||
optional: true
|
||||
unzipper:
|
||||
optional: true
|
||||
checksum: 7d867a4180321e85477e57b15f9ea85d2e26b6d1cf875f7e40131e4a047aaae1ad5e8b977c1ff9b3a64538276271fed06202ac6eb9168c7c5dd01eb155402f7f
|
||||
checksum: e99e0c5172e4045098ef0cd027304b1f5d37b6ce1ae00f1b50f0ca59e5a0894faeca9972e7db88f1d0e6aebb3ca67c3d3cac333f594ab31ffeb166414e4eeb19
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in new issue
Block a user