Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a64820048c | ||
|
|
4ff4a5a893 | ||
|
|
5ea5681d2b | ||
|
|
2bb327b747 | ||
|
|
4dcdf60309 | ||
|
|
009fd5b2ec | ||
|
|
091e7e5bc1 | ||
|
|
ea1202bb65 | ||
|
|
cb413febaa | ||
|
|
364e55d477 | ||
|
|
680a35a26d | ||
|
|
08ff59e463 | ||
|
|
9db4c8c333 | ||
|
|
2489c2da46 |
No files matched your search
@@ -126,7 +126,16 @@ jobs:
|
||||
working-directory: tests/smoke
|
||||
timeout-minutes: 2
|
||||
run: |
|
||||
docker run -d --name smoke --rm -p3000:3000 ${{ vars.DOCKER_IMAGE }}:${{ matrix.tag }}
|
||||
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 }}
|
||||
|
||||
sleep 3
|
||||
docker logs smoke
|
||||
goss validate --retry-timeout 30s --sleep 1s
|
||||
|
||||
@@ -39,6 +39,7 @@ repos:
|
||||
(?x)^(
|
||||
docs/.*|
|
||||
README.md|
|
||||
tests/smoke/goss.yaml|
|
||||
)$
|
||||
- repo: https://github.com/Lucas-C/pre-commit-hooks-nodejs
|
||||
rev: v1.1.2
|
||||
|
||||
+6
-3
@@ -147,7 +147,6 @@ RUN if [ "$USE_BROWSER" = "chromium" ] || [ "$USE_BROWSER" = "chrome" ]; then \
|
||||
libgtk-3-0 \
|
||||
libdrm2 \
|
||||
ca-certificates \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
@@ -172,6 +171,11 @@ RUN if [ "$USE_BROWSER" = "chrome" ]; then \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# curl
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# GOWS requirements
|
||||
# libc6
|
||||
RUN apt-get update \
|
||||
@@ -196,10 +200,9 @@ ENV WAHA_GOWS_SOCKET /tmp/gows.sock
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Add healthcheck
|
||||
# Add healthcheck script
|
||||
COPY ping-check.sh /ping-check.sh
|
||||
RUN chmod +x /ping-check.sh
|
||||
HEALTHCHECK --interval=60s --timeout=10s --retries=3 --start-period=30s CMD ["/bin/bash", "/ping-check.sh"]
|
||||
|
||||
# Chokidar options to monitor file changes
|
||||
ENV CHOKIDAR_USEPOLLING=1
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
build:
|
||||
docker build . -t devlikeapro/waha
|
||||
|
||||
build-all: build-plus build-chrome build-gows build-noweb
|
||||
|
||||
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
|
||||
|
||||
build-plus-gows:
|
||||
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
|
||||
|
||||
@@ -7,6 +7,12 @@ 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
|
||||
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Default port is 3000
|
||||
PORT=${WHATSAPP_API_PORT:-3000}
|
||||
# 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
|
||||
@@ -11,4 +11,4 @@ else
|
||||
fi
|
||||
|
||||
# Execute curl command with SSL verification disabled
|
||||
curl -f -s -k "${PROTOCOL}://127.0.0.1:${PORT}/ping" || exit 1
|
||||
curl -f -s -k "${PROTOCOL}://127.0.0.1:${PORT}/ping" || exit 1
|
||||
@@ -682,10 +682,17 @@ export abstract class WhatsappSession {
|
||||
}
|
||||
|
||||
protected async refreshProfilePicture(id: string) {
|
||||
let fn: Promise<string>;
|
||||
if (isJidBroadcast(id)) {
|
||||
this.logger.debug(`Refreshing profile picture for id "${id}"...`);
|
||||
// Have no pictures
|
||||
if (id === '0@c.us' || id === '0@s.whatsapp.net') {
|
||||
return null;
|
||||
} else if (isJidNewsletter(id)) {
|
||||
} else if (isJidBroadcast(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the right method
|
||||
let fn: Promise<string>;
|
||||
if (isJidNewsletter(id)) {
|
||||
fn = this.channelsGetChannel(id).then(
|
||||
(channel: Channel) => channel.picture || channel.preview,
|
||||
);
|
||||
|
||||
@@ -1805,6 +1805,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
const messagesUpsert$ = fromEvent(this.sock.ev, 'messages.upsert').pipe(
|
||||
map((event: BaileysEventMap['messages.upsert']) => event.messages),
|
||||
mergeAll(),
|
||||
share(),
|
||||
);
|
||||
let [messagesFromMe$, messagesFromOthers$] = partition(
|
||||
messagesUpsert$,
|
||||
@@ -1888,6 +1889,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
).pipe(
|
||||
// @ts-ignore
|
||||
mergeAll(),
|
||||
share(),
|
||||
);
|
||||
const messageAckDirect$ = messageUpdates$.pipe(
|
||||
filter(isMine), // ack comes only for MY messages
|
||||
@@ -1898,6 +1900,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
fromEvent(this.sock.ev, 'message-receipt.update').pipe(
|
||||
// @ts-ignore
|
||||
mergeAll(),
|
||||
share(),
|
||||
);
|
||||
|
||||
const messageAckGroups$ = messageReceiptUpdate$.pipe(
|
||||
@@ -1914,7 +1917,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
//
|
||||
this.events2
|
||||
.get(WAHAEvents.STATE_CHANGE)
|
||||
.switch(fromEvent(this.sock.ev, 'connection.update'));
|
||||
.switch(fromEvent(this.sock.ev, 'connection.update').pipe(share()));
|
||||
|
||||
const groupsUpsert$: Observable<GroupMetadata> = fromEvent(
|
||||
this.sock.ev,
|
||||
@@ -1922,6 +1925,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
).pipe(
|
||||
// @ts-ignore
|
||||
mergeAll(),
|
||||
share(),
|
||||
);
|
||||
const groupsUpdate$: Observable<Partial<GroupMetadata>> = fromEvent(
|
||||
this.sock.ev,
|
||||
@@ -1929,11 +1933,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
).pipe(
|
||||
// @ts-ignore
|
||||
mergeAll(),
|
||||
share(),
|
||||
);
|
||||
const groupsParticipantsUpdate$: Observable<any> = fromEvent(
|
||||
this.sock.ev,
|
||||
'group-participants.update',
|
||||
);
|
||||
).pipe(share());
|
||||
|
||||
this.events2.get(WAHAEvents.GROUP_JOIN).switch(groupsUpsert$);
|
||||
|
||||
@@ -1960,13 +1965,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
);
|
||||
this.events2.get(WAHAEvents.GROUP_V2_LEAVE).switch(groupV2Leave$);
|
||||
|
||||
this.events2
|
||||
.get(WAHAEvents.PRESENCE_UPDATE)
|
||||
.switch(
|
||||
fromEvent(this.sock.ev, 'presence.update').pipe(
|
||||
map((data: any) => this.toWahaPresences(data.id, data.presences)),
|
||||
),
|
||||
);
|
||||
this.events2.get(WAHAEvents.PRESENCE_UPDATE).switch(
|
||||
fromEvent(this.sock.ev, 'presence.update').pipe(
|
||||
map((data: any) => this.toWahaPresences(data.id, data.presences)),
|
||||
share(),
|
||||
),
|
||||
);
|
||||
|
||||
//
|
||||
// Poll votes
|
||||
@@ -1993,7 +1997,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
//
|
||||
// @ts-ignore
|
||||
const calls$: Observable<WACallEvent[]> = fromEvent(this.sock.ev, 'call');
|
||||
const call$ = calls$.pipe(mergeMap(identity));
|
||||
const call$ = calls$.pipe(mergeMap(identity), share());
|
||||
this.events2.get(WAHAEvents.CALL_RECEIVED).switch(
|
||||
call$.pipe(
|
||||
filter((call: WACallEvent) => call.status === 'offer'),
|
||||
@@ -2026,7 +2030,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
const labelsEdit$: Observable<NOWEBLabel> = fromEvent(
|
||||
this.sock.ev,
|
||||
'labels.edit',
|
||||
);
|
||||
).pipe(share());
|
||||
this.events2.get(WAHAEvents.LABEL_UPSERT).switch(
|
||||
labelsEdit$.pipe(
|
||||
exclude((data: NOWEBLabel) => data.deleted),
|
||||
@@ -2039,7 +2043,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
map(this.toLabel.bind(this)),
|
||||
),
|
||||
);
|
||||
const labelsAssociation$ = fromEvent(this.sock.ev, 'labels.association');
|
||||
const labelsAssociation$ = fromEvent(
|
||||
this.sock.ev,
|
||||
'labels.association',
|
||||
).pipe(share());
|
||||
const labelsAssociationAdd$: Observable<ChatLabelAssociation> =
|
||||
labelsAssociation$.pipe(
|
||||
filter(({ type }: any) => type === 'add'),
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { Schema } from '@waha/core/storage/Schema';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const Database = require('better-sqlite3');
|
||||
import Knex from 'knex';
|
||||
|
||||
export class Sqlite3SchemaValidation {
|
||||
constructor(
|
||||
private table: Schema,
|
||||
private db,
|
||||
private knex: Knex.Knex,
|
||||
) {}
|
||||
|
||||
validate() {
|
||||
async validate() {
|
||||
const table = this.table;
|
||||
|
||||
// Check table has the columns
|
||||
const columns = this.db.prepare(`PRAGMA table_info(${table.name})`).all();
|
||||
// Check exact number of columns
|
||||
const columns = await this.knex.raw(`PRAGMA table_info(${table.name})`);
|
||||
// Check the exact number of columns
|
||||
if (columns.length !== table.columns.length) {
|
||||
throw new Error(
|
||||
`Table '${table.name}' does not have expected number of columns. Expected ${table.columns.length}, got ${columns.length}`,
|
||||
@@ -37,7 +35,7 @@ export class Sqlite3SchemaValidation {
|
||||
}
|
||||
|
||||
// Check table has expected indexes
|
||||
const indexes = this.db.prepare(`PRAGMA index_list(${table.name})`).all();
|
||||
const indexes = await this.knex.raw(`PRAGMA index_list(${table.name})`);
|
||||
const indexNames = indexes.map((index) => index.name);
|
||||
for (const index of table.indexes) {
|
||||
if (!indexNames.includes(index.name)) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Sqlite3LabelAssociationsRepository } from '@waha/core/engines/noweb/sto
|
||||
import { Sqlite3LabelsRepository } from '@waha/core/engines/noweb/store/sqlite3/Sqlite3LabelsRepository';
|
||||
import { Sqlite3LidPNRepository } from '@waha/core/engines/noweb/store/sqlite3/Sqlite3LidPNRepository';
|
||||
import { Schema } from '@waha/core/storage/Schema';
|
||||
import Knex from 'knex';
|
||||
|
||||
import { INowebStorage } from '../INowebStorage';
|
||||
import { Migrations, NOWEB_STORE_SCHEMA } from '../schemas';
|
||||
@@ -14,70 +15,71 @@ import { Sqlite3ContactRepository } from './Sqlite3ContactRepository';
|
||||
import { Sqlite3MessagesRepository } from './Sqlite3MessagesRepository';
|
||||
import { Sqlite3SchemaValidation } from './Sqlite3SchemaValidation';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const Database = require('better-sqlite3');
|
||||
|
||||
export class Sqlite3Storage extends INowebStorage {
|
||||
private readonly db: any;
|
||||
private readonly tables: Schema[];
|
||||
private readonly knex: Knex.Knex;
|
||||
|
||||
constructor(filePath: string) {
|
||||
super();
|
||||
this.db = new Database(filePath);
|
||||
this.knex = Knex({
|
||||
client: 'better-sqlite3',
|
||||
connection: { filename: filePath },
|
||||
useNullAsDefault: true,
|
||||
});
|
||||
this.tables = NOWEB_STORE_SCHEMA;
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.db.pragma('journal_mode = WAL;');
|
||||
this.migrate();
|
||||
this.validateSchema();
|
||||
await this.knex.raw('PRAGMA journal_mode = WAL;');
|
||||
await this.migrate();
|
||||
await this.validateSchema();
|
||||
}
|
||||
|
||||
private migrate() {
|
||||
this.migration0001init();
|
||||
private async migrate() {
|
||||
await this.migration0001init();
|
||||
}
|
||||
|
||||
private validateSchema() {
|
||||
private async validateSchema() {
|
||||
for (const table of this.tables) {
|
||||
new Sqlite3SchemaValidation(table, this.db).validate();
|
||||
await new Sqlite3SchemaValidation(table, this.knex).validate();
|
||||
}
|
||||
}
|
||||
|
||||
private migration0001init() {
|
||||
private async migration0001init() {
|
||||
for (const migration of Migrations) {
|
||||
this.db.exec(migration);
|
||||
await this.knex.raw(migration);
|
||||
}
|
||||
}
|
||||
|
||||
async close() {
|
||||
return this.db.close();
|
||||
return this.knex.destroy();
|
||||
}
|
||||
|
||||
getContactsRepository() {
|
||||
return new Sqlite3ContactRepository(this.db);
|
||||
return new Sqlite3ContactRepository(this.knex);
|
||||
}
|
||||
|
||||
getChatRepository() {
|
||||
return new Sqlite3ChatRepository(this.db);
|
||||
return new Sqlite3ChatRepository(this.knex);
|
||||
}
|
||||
|
||||
getGroupRepository() {
|
||||
return new Sqlite3GroupRepository(this.db);
|
||||
return new Sqlite3GroupRepository(this.knex);
|
||||
}
|
||||
|
||||
getLabelsRepository(): ILabelsRepository {
|
||||
return new Sqlite3LabelsRepository(this.db);
|
||||
return new Sqlite3LabelsRepository(this.knex);
|
||||
}
|
||||
|
||||
getLabelAssociationRepository(): ILabelAssociationRepository {
|
||||
return new Sqlite3LabelAssociationsRepository(this.db);
|
||||
return new Sqlite3LabelAssociationsRepository(this.knex);
|
||||
}
|
||||
|
||||
getMessagesRepository() {
|
||||
return new Sqlite3MessagesRepository(this.db);
|
||||
return new Sqlite3MessagesRepository(this.knex);
|
||||
}
|
||||
|
||||
getLidPNRepository(): INowebLidPNRepository {
|
||||
return new Sqlite3LidPNRepository(this.db);
|
||||
return new Sqlite3LidPNRepository(this.knex);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import Knex from 'knex';
|
||||
|
||||
import { DataStore } from '../abc/DataStore';
|
||||
|
||||
export abstract class LocalStore extends DataStore {
|
||||
@@ -23,5 +25,5 @@ export abstract class LocalStore extends DataStore {
|
||||
*/
|
||||
abstract getFilePath(session: string, file: string): string;
|
||||
|
||||
abstract getWAHADatabase(): any;
|
||||
abstract getWAHADatabase(): Knex.Knex;
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
import { safeJoin } from '@waha/utils/files';
|
||||
import * as fs from 'fs/promises';
|
||||
import Knex from 'knex';
|
||||
import * as path from 'path';
|
||||
|
||||
import { LocalStore } from './LocalStore';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const Database = require('better-sqlite3');
|
||||
|
||||
export class LocalStoreCore extends LocalStore {
|
||||
protected readonly baseDirectory: string =
|
||||
process.env.WAHA_LOCAL_STORE_BASE_DIR || './.sessions';
|
||||
|
||||
private readonly engine: string;
|
||||
private db: any;
|
||||
private knex: Knex.Knex;
|
||||
|
||||
constructor(engine: string) {
|
||||
super();
|
||||
@@ -21,6 +19,11 @@ export class LocalStoreCore extends LocalStore {
|
||||
|
||||
async init(sessionName?: string) {
|
||||
await fs.mkdir(this.getEngineDirectory(), { recursive: true });
|
||||
if (!this.knex) {
|
||||
this.knex = this.buildKnex();
|
||||
await this.knex.raw('PRAGMA journal_mode = WAL;');
|
||||
await this.knex.raw('PRAGMA foreign_keys = ON;');
|
||||
}
|
||||
if (sessionName) {
|
||||
await fs.mkdir(this.getSessionDirectory(sessionName), {
|
||||
recursive: true,
|
||||
@@ -54,17 +57,24 @@ export class LocalStoreCore extends LocalStore {
|
||||
return safeJoin(this.getEngineDirectory(), name);
|
||||
}
|
||||
|
||||
getWAHADatabase(): any {
|
||||
if (!this.db) {
|
||||
const engineDir = this.getEngineDirectory();
|
||||
const database = safeJoin(engineDir, 'waha.sqlite3');
|
||||
this.db = new Database(database);
|
||||
this.db.pragma('journal_mode = WAL;');
|
||||
getWAHADatabase(): Knex.Knex {
|
||||
if (!this.knex) {
|
||||
throw new Error('Knex is not initialized, call LocalStore.init() first');
|
||||
}
|
||||
return this.db;
|
||||
return this.knex;
|
||||
}
|
||||
|
||||
buildKnex(): Knex.Knex {
|
||||
const engineDir = this.getEngineDirectory();
|
||||
const database = path.join(engineDir, 'waha.sqlite3');
|
||||
return Knex({
|
||||
client: 'better-sqlite3',
|
||||
connection: { filename: database },
|
||||
useNullAsDefault: true,
|
||||
});
|
||||
}
|
||||
|
||||
async close() {
|
||||
this.db?.close();
|
||||
await this.knex.destroy();
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import Knex from 'knex';
|
||||
|
||||
export interface ISQLEngine {
|
||||
/**
|
||||
* Run a query that does not return any data
|
||||
*/
|
||||
run(query: Knex.QueryBuilder): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get a single row from the database
|
||||
*/
|
||||
get(query: Knex.QueryBuilder): Promise<any | null>;
|
||||
|
||||
/**
|
||||
* Get all rows from the database
|
||||
*/
|
||||
all(query: Knex.QueryBuilder): Promise<any[]>;
|
||||
|
||||
/**
|
||||
* Run a raw SQL query with bindings
|
||||
*/
|
||||
raw(sql: string, bindings: any[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Execute a raw SQL query
|
||||
*/
|
||||
exec(sql: string): Promise<void>;
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Field, Schema } from '@waha/core/storage/Schema';
|
||||
import { IJsonQuery } from '@waha/core/storage/sql/IJsonQuery';
|
||||
import { ISQLEngine } from '@waha/core/storage/sql/ISQLEngine';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { KnexPaginator } from '@waha/utils/Paginator';
|
||||
import Knex from 'knex';
|
||||
import { Knex } from 'knex';
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
export type Migration = string;
|
||||
@@ -26,10 +25,7 @@ export class SqlKVRepository<Entity> {
|
||||
return [];
|
||||
}
|
||||
|
||||
constructor(
|
||||
private engine: ISQLEngine,
|
||||
protected knex: Knex.Knex,
|
||||
) {}
|
||||
constructor(protected knex: Knex) {}
|
||||
|
||||
get columns(): Field[] {
|
||||
return this.schema.columns;
|
||||
@@ -49,7 +45,7 @@ export class SqlKVRepository<Entity> {
|
||||
|
||||
protected async applyMigrations() {
|
||||
for (const migration of this.migrations) {
|
||||
await this.engine.exec(migration);
|
||||
await this.knex.raw(migration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +115,7 @@ export class SqlKVRepository<Entity> {
|
||||
|
||||
async getCount(): Promise<number> {
|
||||
const query = this.select().count({ count: 'id' });
|
||||
const row = await this.engine.get(query);
|
||||
const row = await query.first();
|
||||
if (!row) {
|
||||
return 0;
|
||||
}
|
||||
@@ -138,7 +134,7 @@ export class SqlKVRepository<Entity> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
const rows = await this.engine.all(this.select().whereIn('id', ids));
|
||||
const rows = await this.select().whereIn('id', ids);
|
||||
const entitiesMap = new Map<string, Entity | null>();
|
||||
|
||||
// Initialize a map with null values for all requested IDs
|
||||
@@ -191,15 +187,15 @@ export class SqlKVRepository<Entity> {
|
||||
* SQL Implementation details
|
||||
*/
|
||||
public async raw(sql: string, bindings: any[]): Promise<void> {
|
||||
await this.engine.raw(sql, bindings);
|
||||
await this.knex.raw(sql, bindings);
|
||||
}
|
||||
|
||||
protected async run(query: Knex.QueryBuilder): Promise<void> {
|
||||
await this.engine.run(query);
|
||||
await query;
|
||||
}
|
||||
|
||||
protected async get(query: Knex.QueryBuilder): Promise<Entity | null> {
|
||||
const row = await this.engine.get(query);
|
||||
const row = await query.first();
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
@@ -207,7 +203,7 @@ export class SqlKVRepository<Entity> {
|
||||
}
|
||||
|
||||
public async all(query: Knex.QueryBuilder): Promise<Entity[]> {
|
||||
const rows = await this.engine.all(query);
|
||||
const rows = await query;
|
||||
return rows.map((row) => this.parse(row));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { ISQLEngine } from '@waha/core/storage/sql/ISQLEngine';
|
||||
import { Database } from 'better-sqlite3';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export class Sqlite3Engine implements ISQLEngine {
|
||||
constructor(protected db: Database) {}
|
||||
|
||||
async all(query: Knex.QueryBuilder): Promise<any[]> {
|
||||
const sql = query.toSQL().sql;
|
||||
const bind = query.toSQL().bindings;
|
||||
return this.db.prepare(sql).all(bind);
|
||||
}
|
||||
|
||||
async get(query: Knex.QueryBuilder): Promise<any | null> {
|
||||
const sql = query.toSQL().sql;
|
||||
const bind = query.toSQL().bindings;
|
||||
const row: any = this.db.prepare(sql).get(bind);
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
async run(query: Knex.QueryBuilder): Promise<void> {
|
||||
const sql = query.toSQL().sql;
|
||||
const bind = query.toSQL().bindings;
|
||||
this.db.prepare(sql).run(bind);
|
||||
return;
|
||||
}
|
||||
|
||||
async raw(sql: string, bindings: any[]) {
|
||||
this.db.prepare(sql).run(bindings);
|
||||
}
|
||||
|
||||
async exec(sql): Promise<void> {
|
||||
this.db.exec(sql);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,17 @@
|
||||
import { SqlKVRepository } from '@waha/core/storage/sql/SqlKVRepository';
|
||||
import { Sqlite3Engine } from '@waha/core/storage/sqlite3/Sqlite3Engine';
|
||||
import { Sqlite3JsonQuery } from '@waha/core/storage/sqlite3/Sqlite3JsonQuery';
|
||||
import { sleep } from '@waha/utils/promiseTimeout';
|
||||
import { Database } from 'better-sqlite3';
|
||||
import Knex from 'knex';
|
||||
|
||||
/**
|
||||
* Key value repository with extra metadata
|
||||
*/
|
||||
export class Sqlite3KVRepository<Entity> extends SqlKVRepository<Entity> {
|
||||
protected db: Database;
|
||||
protected knex: Knex.Knex;
|
||||
protected jsonQuery = new Sqlite3JsonQuery();
|
||||
|
||||
constructor(db: Database) {
|
||||
// sqlite does not support inserting default values. Set the `useNullAsDefault` flag to hide this warning. (see docs https://knexjs.org/guide/query-builder.html#insert).
|
||||
const knex = Knex({ client: 'better-sqlite3', useNullAsDefault: true });
|
||||
const engine = new Sqlite3Engine(db);
|
||||
super(engine, knex);
|
||||
this.db = db;
|
||||
constructor(knex: Knex.Knex) {
|
||||
super(knex);
|
||||
}
|
||||
|
||||
protected async upsertBatch(entities: Entity[]): Promise<void> {
|
||||
|
||||
@@ -23,8 +23,8 @@ export class Sqlite3SessionMeRepository
|
||||
}
|
||||
|
||||
constructor(store: LocalStore) {
|
||||
const db = store.getWAHADatabase();
|
||||
super(db);
|
||||
const knex = store.getWAHADatabase();
|
||||
super(knex);
|
||||
}
|
||||
|
||||
upsertMe(sessionName: string, me: MeInfo): Promise<void> {
|
||||
@@ -41,7 +41,7 @@ export class Sqlite3SessionMeRepository
|
||||
}
|
||||
|
||||
protected async validateSchema() {
|
||||
const validation = new Sqlite3SchemaValidation(this.schema, this.db);
|
||||
validation.validate();
|
||||
const validation = new Sqlite3SchemaValidation(this.schema, this.knex);
|
||||
await validation.validate();
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ export class Sqlite3SessionWorkerRepository
|
||||
}
|
||||
|
||||
constructor(store: LocalStore) {
|
||||
const db = store.getWAHADatabase();
|
||||
super(db);
|
||||
const knex = store.getWAHADatabase();
|
||||
super(knex);
|
||||
}
|
||||
|
||||
assign(session: string, worker: string): Promise<void> {
|
||||
@@ -45,7 +45,7 @@ export class Sqlite3SessionWorkerRepository
|
||||
}
|
||||
|
||||
protected async validateSchema() {
|
||||
const validation = new Sqlite3SchemaValidation(this.schema, this.db);
|
||||
validation.validate();
|
||||
const validation = new Sqlite3SchemaValidation(this.schema, this.knex);
|
||||
await validation.validate();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -33,7 +33,7 @@ export function getEngineName(): string {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2025.6.6',
|
||||
version: '2025.7.1',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser:
|
||||
|
||||
@@ -5,3 +5,11 @@ http:
|
||||
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
|
||||
Reference in new issue
Block a user