File size: 4,983 Bytes
4327358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import { Injectable, NotFoundException } from '@nestjs/common';
import { migrate } from '@waha/apps/app_sdk/migrations';
import { IAppService } from '@waha/apps/app_sdk/services/IAppService';
import { IAppsService } from '@waha/apps/app_sdk/services/IAppsService';
import { ChatWootAppService } from '@waha/apps/chatwoot/services/ChatWootAppService';
import { DataStore } from '@waha/core/abc/DataStore';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { WhatsappSession } from '@waha/core/abc/session.abc';
import { generatePrefixedId } from '@waha/utils/ids';
import { Knex } from 'knex';
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';

import { App, AppName } from '../dto/app.dto';
import { AppRepository } from '../storage/AppRepository';

@Injectable()
export class AppsEnabledService implements IAppsService {
  constructor(
    protected readonly chatwootService: ChatWootAppService,
    @InjectPinoLogger('AppsService')
    protected logger: PinoLogger,
  ) {}

  async list(manager: SessionManager, session: string): Promise<App[]> {
    const knex = manager.store.getWAHADatabase();
    const repo = new AppRepository(knex);
    const apps = await repo.getAllBySession(session);
    apps.forEach((app) => {
      delete app.pk;
    });
    return apps;
  }

  async create(manager: SessionManager, app: App): Promise<App> {
    await this.checkSessionExists(manager, app.session);
    app.id = app.id || generatePrefixedId('app');

    const knex = manager.store.getWAHADatabase();
    const repo = new AppRepository(knex);

    const existingApp = await repo.getById(app.id);
    if (existingApp) {
      throw new Error(`App with ID '${app.id}' already exists.`);
    }

    // Validate only one Chatwoot app per session
    if (app.app === AppName.chatwoot) {
      const existingApps = await repo.getAllBySession(app.session);
      const existingChatwootApp = existingApps.find(
        (existingApp) => existingApp.app === AppName.chatwoot,
      );

      if (existingChatwootApp) {
        throw new Error(
          `Only one Chatwoot app is allowed per session. Session '${app.session}' already has a Chatwoot app with ID '${existingChatwootApp.id}'.`,
        );
      }
    }

    const service = this.getAppService(app);
    await service.beforeCreated(app);

    const result = await repo.save(app);
    await this.restartIfRunning(manager, app.session);
    delete result.pk;
    return result;
  }

  async update(manager: SessionManager, app: App) {
    await this.checkSessionExists(manager, app.session);
    const knex = manager.store.getWAHADatabase();
    const repo = new AppRepository(knex);
    const savedApp = await repo.getById(app.id);
    if (!savedApp) {
      throw new NotFoundException(`App '${app.id}' not found`);
    }
    if (savedApp.app != app.app) {
      throw new Error(
        `Can not change app type. Delete and create a new app. Before type: '${savedApp.app}' After type: '${app.app}'`,
      );
    }
    if (savedApp.session != app.session) {
      throw new Error(
        `Can not change session. Delete and create a new app. Before session: '${savedApp.session}' After session: '${app.session}'`,
      );
    }

    const service = this.getAppService(app);
    await service.beforeUpdated(savedApp, app);

    await repo.update(app.id, app);
    await this.restartIfRunning(manager, app.session);
    return;
  }

  async delete(manager: SessionManager, appId: string) {
    const knex = manager.store.getWAHADatabase();
    const repo = new AppRepository(knex);
    const app = await repo.getById(appId);
    if (!app) {
      throw new NotFoundException(`App '${appId}' not found`);
    }
    const service = this.getAppService(app);
    await service.beforeDeleted(app);
    await repo.delete(app.id);
    await this.restartIfRunning(manager, app.session);
    return;
  }

  async beforeSessionStart(session: WhatsappSession, store: DataStore) {
    const knex = store.getWAHADatabase();
    const repo = new AppRepository(knex);
    const apps = await repo.getAllBySession(session.name);
    for (const app of apps) {
      const service = this.getAppService(app);
      await service.beforeSessionStart(app, session);
    }
  }

  async migrate(knex: Knex): Promise<void> {
    await migrate(knex);
  }

  private restartIfRunning(manager: SessionManager, session: string) {
    const isRunning = manager.isRunning(session);
    if (!isRunning) {
      return;
    }
    return manager.restart(session);
  }

  private getAppService(app: App): IAppService {
    switch (app.app) {
      case AppName.chatwoot:
        return this.chatwootService;
      default:
        throw new Error(`App '${app.app}' not supported`);
    }
  }

  private async checkSessionExists(
    manager: SessionManager,
    sessionName: string,
  ) {
    const session = await manager.exists(sessionName);
    if (session === null) {
      throw new NotFoundException('Session not found');
    }
  }
}