bizlin-chat 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -46
- package/backend/package.json +40 -10
- package/bin/bizlin-chat.js +2 -1
- package/frontend/package.json +49 -4
- package/frontend/tsconfig.tsbuildinfo +1 -0
- package/package.json +50 -4
- package/backend/README.md +0 -54
- package/backend/src/db.js +0 -185
- package/backend/src/index.js +0 -262
- package/backend/src/whatsapp.js +0 -65
- package/frontend/README.md +0 -11
- package/frontend/pages/index.js +0 -172
package/README.md
CHANGED
|
@@ -1,61 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Bizlin Chat - WhatsApp Cloud API Team Inbox
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Open-source WhatsApp Cloud API Team Inbox with Express.js, Next.js, and TypeScript.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Backend: Express API, Socket.IO, message persistence (SQLite/Postgres), WhatsApp Cloud API integration, setup endpoints for admin and WhatsApp account.
|
|
7
|
-
- Frontend: Next.js app with message list, send text/media/template, setup wizard for initial admin and WhatsApp account.
|
|
8
|
-
- CLI: `bizlin-chat` command to run the platform, with background daemon support and log files.
|
|
9
|
-
- CI: GitHub Actions workflow for npm publishing with provenance.
|
|
10
|
-
|
|
11
|
-
Quick start (local, foreground)
|
|
12
|
-
|
|
13
|
-
1. Install dependencies:
|
|
5
|
+
## Global Install
|
|
14
6
|
|
|
15
7
|
```bash
|
|
16
|
-
|
|
17
|
-
npm install
|
|
18
|
-
cd ../frontend
|
|
19
|
-
npm install
|
|
8
|
+
npm install -g bizlin-chat
|
|
20
9
|
```
|
|
21
10
|
|
|
22
|
-
|
|
11
|
+
## Usage
|
|
23
12
|
|
|
24
13
|
```bash
|
|
25
|
-
|
|
14
|
+
bizlin-chat start # Start application (ports 4000-4001)
|
|
15
|
+
bizlin-chat stop # Stop application
|
|
16
|
+
bizlin-chat restart # Restart application
|
|
17
|
+
bizlin-chat logs # View logs
|
|
18
|
+
|
|
19
|
+
# Options
|
|
20
|
+
bizlin-chat start --port 5000 # Custom port
|
|
21
|
+
bizlin-chat start --db postgres # Use PostgreSQL
|
|
26
22
|
```
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Daemon mode (background)
|
|
31
|
-
|
|
32
|
-
- Start background: `bizlin-chat start --port 1994 --db sqlite --dbname bizlin_chat`
|
|
33
|
-
- Stop: `bizlin-chat stop`
|
|
34
|
-
- Restart: `bizlin-chat restart --port 1994`
|
|
35
|
-
- View logs (last ~200 lines): `bizlin-chat logs`
|
|
24
|
+
Access at: http://localhost:4001
|
|
36
25
|
|
|
37
|
-
|
|
26
|
+
## Environment Setup
|
|
38
27
|
|
|
39
|
-
|
|
28
|
+
Create `.env` file (optional):
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- Default: SQLite file `data/<dbname>.sqlite` (default dbname `bizlin_chat`).
|
|
49
|
-
- To use Postgres: `--db postgres --dbname my_db` and ensure `DATABASE_URL` is set or the default postgres URL is reachable.
|
|
30
|
+
```env
|
|
31
|
+
PORT=4000
|
|
32
|
+
DB_CLIENT=sqlite
|
|
33
|
+
WHATSAPP_PHONE_NUMBER_ID=your_phone_id
|
|
34
|
+
WHATSAPP_ACCESS_TOKEN=your_access_token
|
|
35
|
+
WHATSAPP_VERIFY_TOKEN=your_verify_token
|
|
36
|
+
```
|
|
50
37
|
|
|
51
|
-
|
|
52
|
-
- `GET /api/setup/status` — check if admin exists
|
|
53
|
-
- `POST /api/setup/create-admin` — create admin
|
|
54
|
-
- `POST /api/setup/test-whatsapp` — test WhatsApp credentials
|
|
55
|
-
- `POST /api/setup/whatsapp` — save WhatsApp account
|
|
56
|
-
- `GET /api/messages`, `POST /api/messages` — messages
|
|
57
|
-
- `POST /api/send`, `POST /api/sendMedia`, `POST /api/sendTemplate` — send via WhatsApp (uses saved account)
|
|
38
|
+
## License
|
|
58
39
|
|
|
59
|
-
|
|
60
|
-
- Credentials are stored in the database; passwords are hashed.
|
|
61
|
-
- The CLI writes PID to `.bizlin-chat.pid` for background control.
|
|
40
|
+
MIT
|
package/backend/package.json
CHANGED
|
@@ -1,21 +1,51 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "backend",
|
|
2
|
+
"name": "@bizlin/backend",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"private":
|
|
5
|
-
"main": "
|
|
4
|
+
"private": false,
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"description": "WhatsApp Team Inbox Backend API",
|
|
8
|
+
"keywords": ["whatsapp", "api", "express", "typescript"],
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/yourusername/zchat.git",
|
|
12
|
+
"directory": "backend"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"tsconfig.json"
|
|
18
|
+
],
|
|
6
19
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"start": "node dist/index.js",
|
|
22
|
+
"dev": "ts-node --transpile-only src/index.ts",
|
|
23
|
+
"dev:watch": "nodemon --exec ts-node --transpile-only src/index.ts"
|
|
9
24
|
},
|
|
10
25
|
"dependencies": {
|
|
26
|
+
"express": "4.18.2",
|
|
11
27
|
"axios": "1.5.0",
|
|
28
|
+
"bcryptjs": "2.4.3",
|
|
12
29
|
"cors": "2.8.5",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"uuid": "9.0.0",
|
|
30
|
+
"form-data": "^4.0.5",
|
|
31
|
+
"multer": "^2.1.1",
|
|
16
32
|
"pg": "8.11.0",
|
|
33
|
+
"socket.io": "4.7.2",
|
|
17
34
|
"sqlite3": "5.1.6",
|
|
18
|
-
"
|
|
35
|
+
"uuid": "9.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"typescript": "^5.1.3",
|
|
39
|
+
"ts-node": "^10.9.1",
|
|
40
|
+
"nodemon": "^3.0.1",
|
|
41
|
+
"@types/express": "^4.17.17",
|
|
42
|
+
"@types/node": "^20.3.1",
|
|
43
|
+
"@types/bcryptjs": "^2.4.2",
|
|
44
|
+
"@types/multer": "^2.1.0",
|
|
45
|
+
"@types/uuid": "^9.0.2",
|
|
46
|
+
"@types/pg": "^8.10.9"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=14"
|
|
19
50
|
}
|
|
20
|
-
|
|
21
51
|
}
|
package/bin/bizlin-chat.js
CHANGED
|
@@ -21,13 +21,14 @@ function start(name, cmd, args, env) {
|
|
|
21
21
|
|
|
22
22
|
function parseArgs() {
|
|
23
23
|
const args = process.argv.slice(2)
|
|
24
|
-
const out = { port: 1994, db: 'sqlite' }
|
|
24
|
+
const out = { port: 1994, db: 'sqlite', extra: [] }
|
|
25
25
|
for (let i = 0; i < args.length; i++) {
|
|
26
26
|
const a = args[i]
|
|
27
27
|
if (a === '--port' && args[i+1]) { out.port = parseInt(args[i+1], 10); i++ }
|
|
28
28
|
else if (a === '--db' && args[i+1]) { out.db = args[i+1]; i++ }
|
|
29
29
|
else if (a === '--dbname' && args[i+1]) { out.dbname = args[i+1]; i++ }
|
|
30
30
|
else if (a === '--help' || a === '-h') { out.action = 'help' }
|
|
31
|
+
else if (['start','stop','restart','run','logs','help'].includes(a)) { out.action = a }
|
|
31
32
|
else out.extra.push(a)
|
|
32
33
|
}
|
|
33
34
|
return out
|
package/frontend/package.json
CHANGED
|
@@ -1,16 +1,61 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "frontend",
|
|
2
|
+
"name": "@bizlin/frontend",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"private":
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "WhatsApp Team Inbox Frontend Application",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"whatsapp",
|
|
8
|
+
"next.js",
|
|
9
|
+
"react",
|
|
10
|
+
"typescript"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/yourusername/zchat.git",
|
|
15
|
+
"directory": "frontend"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
".next",
|
|
19
|
+
"public",
|
|
20
|
+
"app",
|
|
21
|
+
"components",
|
|
22
|
+
"context",
|
|
23
|
+
"hooks",
|
|
24
|
+
"lib",
|
|
25
|
+
"styles",
|
|
26
|
+
"pages",
|
|
27
|
+
"package.json",
|
|
28
|
+
"next.config.js",
|
|
29
|
+
"tsconfig.json",
|
|
30
|
+
"tailwind.config.js",
|
|
31
|
+
"postcss.config.js"
|
|
32
|
+
],
|
|
5
33
|
"scripts": {
|
|
6
34
|
"dev": "next dev",
|
|
7
35
|
"build": "next build",
|
|
8
|
-
"start": "next start -p 3000"
|
|
36
|
+
"start": "next start -p 3000",
|
|
37
|
+
"lint": "next lint"
|
|
9
38
|
},
|
|
10
39
|
"dependencies": {
|
|
11
40
|
"next": "13.4.7",
|
|
12
41
|
"react": "18.2.0",
|
|
13
42
|
"react-dom": "18.2.0",
|
|
14
|
-
"
|
|
43
|
+
"react-icons": "^4.12.0",
|
|
44
|
+
"socket.io-client": "4.7.2",
|
|
45
|
+
"axios": "1.5.0",
|
|
46
|
+
"utf-8-validate": "^6.0.0",
|
|
47
|
+
"bufferutil": "^4.0.8"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"typescript": "^5.1.3",
|
|
51
|
+
"@types/react": "^18.2.12",
|
|
52
|
+
"@types/react-dom": "^18.2.5",
|
|
53
|
+
"@types/node": "^20.3.1",
|
|
54
|
+
"autoprefixer": "^10.4.0",
|
|
55
|
+
"postcss": "^8.4.0",
|
|
56
|
+
"tailwindcss": "^3.4.8"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=14"
|
|
15
60
|
}
|
|
16
61
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/prop-types/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/next/amp.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/future/route-kind.d.ts","./node_modules/next/dist/server/future/route-definitions/route-definition.d.ts","./node_modules/next/dist/server/future/route-matches/route-match.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/node-polyfill-fetch.d.ts","./node_modules/next/dist/server/node-polyfill-form.d.ts","./node_modules/next/dist/server/node-polyfill-web-streams.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/next/dist/server/future/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/future/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/future/helpers/i18n-provider.d.ts","./node_modules/next/dist/server/future/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/router.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/send-payload/revalidate-headers.d.ts","./node_modules/next/dist/server/send-payload/index.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/server/font-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/next/dist/server/future/helpers/module-loader/module-loader.d.ts","./node_modules/next/dist/server/future/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/client/components/static-generation-async-storage.d.ts","./node_modules/next/dist/server/lib/patch-fetch.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/server/future/route-modules/route-module.d.ts","./node_modules/next/dist/server/async-storage/async-storage-wrapper.d.ts","./node_modules/next/dist/server/async-storage/static-generation-async-storage-wrapper.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/client/components/request-async-storage.d.ts","./node_modules/next/dist/client/components/draft-mode.d.ts","./node_modules/next/dist/client/components/headers.d.ts","./node_modules/next/dist/client/components/static-generation-bailout.d.ts","./node_modules/next/dist/client/components/action-async-storage.d.ts","./node_modules/next/dist/server/future/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/future/route-matches/app-route-route-match.d.ts","./node_modules/next/dist/server/future/route-handler-managers/route-handler-manager.d.ts","./node_modules/next/dist/server/future/normalizers/normalizer.d.ts","./node_modules/next/dist/server/future/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/future/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/future/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/future/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/lib/cpu-profile.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/future/route-matcher-managers/default-route-matcher-manager.d.ts","./node_modules/next/dist/server/future/route-matcher-managers/dev-route-matcher-manager.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/types/index.d.ts","./node_modules/next/dist/shared/lib/html-context.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate-path.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/shared/lib/runtime-config.d.ts","./node_modules/next/config.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/client/image.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/dist/compiled/@vercel/og/index.node.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/zod/lib/helpers/typealiases.d.ts","./node_modules/zod/lib/helpers/util.d.ts","./node_modules/zod/lib/zoderror.d.ts","./node_modules/zod/lib/locales/en.d.ts","./node_modules/zod/lib/errors.d.ts","./node_modules/zod/lib/helpers/parseutil.d.ts","./node_modules/zod/lib/helpers/enumutil.d.ts","./node_modules/zod/lib/helpers/errorutil.d.ts","./node_modules/zod/lib/helpers/partialutil.d.ts","./node_modules/zod/lib/types.d.ts","./node_modules/zod/lib/external.d.ts","./node_modules/zod/lib/index.d.ts","./node_modules/zod/index.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/shared/lib/app-router-context.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/navigation-types/compat/navigation.d.ts","./next-env.d.ts","./node_modules/@socket.io/component-emitter/lib/cjs/index.d.ts","./node_modules/engine.io-parser/build/esm/commons.d.ts","./node_modules/engine.io-parser/build/esm/encodepacket.d.ts","./node_modules/engine.io-parser/build/esm/decodepacket.d.ts","./node_modules/engine.io-parser/build/esm/index.d.ts","./node_modules/engine.io-client/build/esm/transport.d.ts","./node_modules/engine.io-client/build/esm/socket.d.ts","./node_modules/engine.io-client/build/esm/transports/polling.d.ts","./node_modules/engine.io-client/build/esm/transports/websocket.d.ts","./node_modules/engine.io-client/build/esm/transports/webtransport.d.ts","./node_modules/engine.io-client/build/esm/transports/index.d.ts","./node_modules/engine.io-client/build/esm/util.d.ts","./node_modules/engine.io-client/build/esm/contrib/parseuri.d.ts","./node_modules/engine.io-client/build/esm/transports/websocket-constructor.d.ts","./node_modules/engine.io-client/build/esm/index.d.ts","./node_modules/socket.io-parser/build/esm/index.d.ts","./node_modules/socket.io-client/build/esm/socket.d.ts","./node_modules/socket.io-client/build/esm/manager.d.ts","./node_modules/socket.io-client/build/esm/index.d.ts","./app/chatclient.tsx","./context/authcontext.tsx","./components/header.tsx","./components/sidebar.tsx","./app/layout.tsx","./app/page.tsx","./app/analytics/page.tsx","./node_modules/react-icons/lib/iconsmanifest.d.ts","./node_modules/react-icons/lib/iconbase.d.ts","./node_modules/react-icons/lib/iconcontext.d.ts","./node_modules/react-icons/lib/index.d.ts","./node_modules/react-icons/md/index.d.ts","./components/messagerenderer.tsx","./app/chat/page.tsx","./app/contacts/page.tsx","./app/settings/waba/page.tsx","./app/setup/layout.tsx","./app/setup/page.tsx","./pages/_document.tsx","./.next/types/app/layout.ts","./.next/types/app/page.ts","./.next/types/app/analytics/page.ts","./.next/types/app/chat/page.ts","./.next/types/app/contacts/page.ts","./.next/types/app/settings/waba/page.ts","../node_modules/@types/bcryptjs/index.d.ts","../node_modules/@types/connect/index.d.ts","../node_modules/@types/body-parser/index.d.ts","../node_modules/@types/cookie/index.d.ts","../node_modules/@types/cors/index.d.ts","../node_modules/@types/send/index.d.ts","../node_modules/@types/qs/index.d.ts","../node_modules/@types/range-parser/index.d.ts","../node_modules/@types/express-serve-static-core/index.d.ts","../node_modules/@types/http-errors/index.d.ts","../node_modules/@types/mime/index.d.ts","../node_modules/@types/serve-static/node_modules/@types/send/index.d.ts","../node_modules/@types/serve-static/index.d.ts","../node_modules/@types/express/index.d.ts","../node_modules/pg-types/index.d.ts","../node_modules/pg-protocol/dist/messages.d.ts","../node_modules/pg-protocol/dist/serializer.d.ts","../node_modules/pg-protocol/dist/parser.d.ts","../node_modules/pg-protocol/dist/index.d.ts","../node_modules/@types/pg/lib/type-overrides.d.ts","../node_modules/@types/pg/index.d.ts","../node_modules/@types/uuid/index.d.ts"],"fileIdsList":[[64,110,263,360],[64,110,263,367],[64,110,263,368],[64,110,263,358],[64,110,263,359],[64,110,263,369],[52,64,110,355],[52,64,110,353,355,365,366],[52,64,110,353],[52,64,110,332,333,355,356,357],[52,64,110,296,355],[52,64,110,332,333,355],[52,64,110],[52,64,110,294,365],[52,64,110,296,332,333],[64,110,309,310,333],[64,110],[64,107,110],[64,109,110],[110],[64,110,115,143],[64,110,111,116,121,129,140,151],[64,110,111,112,121,129],[59,60,61,64,110],[64,110,113,152],[64,110,114,115,122,130],[64,110,115,140,148],[64,110,116,118,121,129],[64,109,110,117],[64,110,118,119],[64,110,120,121],[64,109,110,121],[64,110,121,122,123,140,151],[64,110,121,122,123,136,140,143],[64,110,118,121,124,129,140,151],[64,110,121,122,124,125,129,140,148,151],[64,110,124,126,140,148,151],[62,63,64,65,66,67,68,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,152,153,154,155,156,157],[64,110,121,127],[64,110,128,151,156],[64,110,118,121,129,140],[64,110,130],[64,110,131],[64,109,110,132],[64,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,152,153,154,155,156,157],[64,110,134],[64,110,135],[64,110,121,136,137],[64,110,136,138,152,154],[64,110,121,140,141,143],[64,110,142,143],[64,110,140,141],[64,110,143],[64,110,144],[64,107,110,140,145],[64,110,121,146,147],[64,110,146,147],[64,110,115,129,140,148],[64,110,149],[64,110,129,150],[64,110,124,135,151],[64,110,115,152],[64,110,140,153],[64,110,128,154],[64,110,155],[64,105,110],[64,105,110,121,123,132,140,143,151,154,156],[64,110,140,157],[52,64,110,162,163,164],[52,64,110,162,163],[52,56,64,110,161,264,304],[52,56,64,110,160,264,304],[49,50,51,64,110],[64,110,340,341,345,346,347,348],[64,110,335,339,340],[64,110,335,339,341],[64,110,342,343,344],[64,110,340],[64,110,339,340],[64,110,336],[64,110,336,337,338],[57,64,110],[64,110,277],[64,110,279,280,281],[64,110,283],[64,110,167,176,193,264],[64,110,178],[64,110,135,158,159,167,176,180,194,220,221,222,225,264],[64,110,165,192],[64,110,165],[64,110,165,192,193],[64,109,110,158],[64,110,232],[64,110,231,233,235],[64,110,311,312,313,330],[64,110,198],[64,109,110,158,198,231,232,233],[64,110,327,329],[64,110,327,328,330],[64,109,110,158,222,223],[52,64,110,168],[52,64,110,151,158],[52,64,110,192,270],[52,64,110,192],[64,110,268,273],[52,64,110,269,276],[52,64,110,140,158,304],[52,56,64,110,124,158,160,161,264,302,303],[64,110,166],[64,110,257,258,259,260,261,262],[64,110,259],[52,64,110,265,276],[52,64,110,276],[64,110,124,158,177,276],[52,64,110,210,211,212,222,264,326],[64,110,124,158,177,178,198,199,231],[64,110,222,224,228],[64,110,124,158,176,178],[64,110,124,140,158,175,177,178,264],[64,110,124,135,151,158,166,167,168,175,176,177,178,186,189,190,191,192,195,204,205,207,209,210,211,212,213,215,217,222,241,243,264],[64,110,124,140,158],[64,110,165,167,168,169,175,276],[64,110,176],[64,110,135,151,158,167,173,175,176,177,186,189,190,191,202,205,208,211,214,222,241,244,251,253,254],[64,110,176,180,222],[64,110,175,176],[64,110,189,242],[64,110,171,172],[64,110,171,245],[64,110,171],[64,110,177,218,239,240],[64,110,171,172,173,187,188,190],[64,110,171,172,173,187,190,252],[64,110,173,188,189],[64,110,187],[64,110,172,173],[64,110,173,219],[64,110,173,246],[64,110,172],[64,110,176,199,204,219,223,224,226,227,229,230,234,236,237,238],[64,110,172,199],[64,110,124,151,158,168,175,176,217],[64,110,204,217,276],[64,110,223,224],[64,110,180],[64,110,159,210,264,276],[64,110,124,135,151,158,167,173,175,177,180,186,191,194,195,202,204,205,207,208,209,213,214,217,222,244,247,249,250,276],[64,110,124,158,175,176,180,251,255],[52,64,110,124,135,158,166,168,175,178,195,209,210,211,212,264],[64,110,124,135,151,158,170,173,174,177],[64,110,216],[64,110,124,158,195],[64,110,135,158,166,167,175,177,186,189,190],[64,110,124,158,195,206],[64,110,124,158,177,207],[64,110,124,158,176,189],[64,110,124,158],[64,110,124,158,177,198],[64,110,197],[64,110,199],[64,110,305],[64,110,176,196,198,202],[64,110,176,196,198],[64,110,124,158,170,176,199,200,201],[52,64,110,327,328,329],[64,110,274],[52,64,110,159,209,212,264,276],[52,64,110,135,151,158,166,267,269,271,272,276],[64,110,177,186,192],[64,110,135,158],[64,110,185],[52,64,110,124,135,158,166,264,265,266,273,275],[48,52,53,54,55,64,110,160,161,264,304],[64,110,285],[64,110,287],[64,110,289],[64,110,291],[64,110,293],[56,58,64,110,264,278,282,284,286,288,290,292,294,296,297,299,307,308],[64,110,295],[64,110,332,333],[64,110,331],[64,110,269],[64,110,298],[64,109,110,199,200,201,202,300,301,304,306],[64,110,158],[52,56,64,110,124,135,158,160,161,162,164,166,178,256,263,276,304],[64,110,361,362,363],[64,110,364],[64,110,350,351,352],[64,110,335,349,350,351],[64,110,335,350,352],[64,110,335],[64,77,81,110,151],[64,77,110,140,151],[64,72,110],[64,74,77,110,148,151],[64,110,129,148],[64,72,110,158],[64,74,77,110,129,151],[64,69,70,73,76,110,121,140,151],[64,77,84,110],[64,69,75,110],[64,77,98,99,110],[64,73,77,110,143,151,158],[64,98,110,158],[64,71,72,110,158],[64,77,110],[64,71,72,73,74,75,76,77,78,79,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,99,100,101,102,103,104,110],[64,77,92,110],[64,77,84,85,110],[64,75,77,85,86,110],[64,76,110],[64,69,72,77,110],[64,77,81,85,86,110],[64,81,110],[64,75,77,80,110,151],[64,69,74,77,84,110],[64,110,140],[64,72,77,98,110,156,158],[64,110,325],[64,110,316,317],[64,110,314,315,316,318,319,323],[64,110,315,316],[64,110,324],[64,110,316],[64,110,314,315,316,319,320,321,322],[64,110,314,315,325],[64,110,286],[64,110,124,158,380],[64,110,121,124,158,384,385,386],[64,110,381,385,387,391],[64,110,121,140,148,158,393,394,397,398,399],[64,110,399],[64,110,122,140,158],[64,110,124,158,388,390],[64,110,122,140,158,389],[64,110,158,394,395,396],[64,110,140,158,394]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"0990a7576222f248f0a3b888adcb7389f957928ce2afb1cd5128169086ff4d29","impliedFormat":1},{"version":"eb5b19b86227ace1d29ea4cf81387279d04bb34051e944bc53df69f58914b788","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"87d9d29dbc745f182683f63187bf3d53fd8673e5fca38ad5eaab69798ed29fbc","impliedFormat":1},{"version":"035312d4945d13efa134ae482f6dc56a1a9346f7ac3be7ccbad5741058ce87f3","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc69795d9954ee4ad57545b10c7bf1a7260d990231b1685c147ea71a6faa265c","impliedFormat":1},{"version":"8bc6c94ff4f2af1f4023b7bb2379b08d3d7dd80c698c9f0b07431ea16101f05f","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"57194e1f007f3f2cbef26fa299d4c6b21f4623a2eddc63dfeef79e38e187a36e","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"ba481bca06f37d3f2c137ce343c7d5937029b2468f8e26111f3c9d9963d6568d","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d9ef24f9a22a88e3e9b3b3d8c40ab1ddb0853f1bfbd5c843c37800138437b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2677634fe27e87348825bb041651e22d50a613e2fdf6a4a3ade971d71bac37e","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"8c0bcd6c6b67b4b503c11e91a1fb91522ed585900eab2ab1f61bba7d7caa9d6f","impliedFormat":1},{"version":"8cd19276b6590b3ebbeeb030ac271871b9ed0afc3074ac88a94ed2449174b776","affectsGlobalScope":true,"impliedFormat":1},{"version":"696eb8d28f5949b87d894b26dc97318ef944c794a9a4e4f62360cd1d1958014b","impliedFormat":1},{"version":"3f8fa3061bd7402970b399300880d55257953ee6d3cd408722cb9ac20126460c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"68bd56c92c2bd7d2339457eb84d63e7de3bd56a69b25f3576e1568d21a162398","affectsGlobalScope":true,"impliedFormat":1},{"version":"3e93b123f7c2944969d291b35fed2af79a6e9e27fdd5faa99748a51c07c02d28","impliedFormat":1},{"version":"9d19808c8c291a9010a6c788e8532a2da70f811adb431c97520803e0ec649991","impliedFormat":1},{"version":"87aad3dd9752067dc875cfaa466fc44246451c0c560b820796bdd528e29bef40","impliedFormat":1},{"version":"4aacb0dd020eeaef65426153686cc639a78ec2885dc72ad220be1d25f1a439df","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"8db0ae9cb14d9955b14c214f34dae1b9ef2baee2fe4ce794a4cd3ac2531e3255","affectsGlobalScope":true,"impliedFormat":1},{"version":"15fc6f7512c86810273af28f224251a5a879e4261b4d4c7e532abfbfc3983134","impliedFormat":1},{"version":"58adba1a8ab2d10b54dc1dced4e41f4e7c9772cbbac40939c0dc8ce2cdb1d442","impliedFormat":1},{"version":"641942a78f9063caa5d6b777c99304b7d1dc7328076038c6d94d8a0b81fc95c1","impliedFormat":1},{"version":"2de7636e6fbb9e54a61a38bcb7505b94ec5be61197fe9e840cff629ca920c4bc","impliedFormat":1},{"version":"855cd5f7eb396f5f1ab1bc0f8580339bff77b68a770f84c6b254e319bbfd1ac7","impliedFormat":1},{"version":"5650cf3dace09e7c25d384e3e6b818b938f68f4e8de96f52d9c5a1b3db068e86","impliedFormat":1},{"version":"1354ca5c38bd3fd3836a68e0f7c9f91f172582ba30ab15bb8c075891b91502b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e20d899c28ca26a2a7afc98beaa69e63ff7fba0a8bc47b4e3bf3ede5e09e424","impliedFormat":1},{"version":"2d2fcaab481b31a5882065c7951255703ddbe1c0e507af56ea42d79ac3911201","impliedFormat":1},{"version":"a192fe8ec33f75edbc8d8f3ed79f768dfae11ff5735e7fe52bfa69956e46d78d","impliedFormat":1},{"version":"ca867399f7db82df981d6915bcbb2d81131d7d1ef683bc782b59f71dda59bc85","affectsGlobalScope":true,"impliedFormat":1},{"version":"372413016d17d804e1d139418aca0c68e47a83fb6669490857f4b318de8cccb3","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"b4f70ec656a11d570e1a9edce07d118cd58d9760239e2ece99306ee9dfe61d02","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"6e70e9570e98aae2b825b533aa6292b6abd542e8d9f6e9475e88e1d7ba17c866","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"085f552d005479e2e6a7311cdbbe5d8c55c497b4d19274285df161ee9684cd9c","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"007faacc9268357caa21d24169f3f3f2497af3e9241308df2d89f6e6d9bb3f2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"74cf591a0f63db318651e0e04cb55f8791385f86e987a67fd4d2eaab8191f730","impliedFormat":1},{"version":"5eab9b3dc9b34f185417342436ec3f106898da5f4801992d8ff38ab3aff346b5","impliedFormat":1},{"version":"12ed4559eba17cd977aa0db658d25c4047067444b51acfdcbf38470630642b23","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3ffabc95802521e1e4bcba4c88d8615176dc6e09111d920c7a213bdda6e1d65","impliedFormat":1},{"version":"809821b8a065e3234a55b3a9d7846231ed18d66dd749f2494c66288d890daf7f","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"c3b41e74b9a84b88b1dca61ec39eee25c0dbc8e7d519ba11bb070918cfacf656","affectsGlobalScope":true,"impliedFormat":1},{"version":"4737a9dc24d0e68b734e6cfbcea0c15a2cfafeb493485e27905f7856988c6b29","affectsGlobalScope":true,"impliedFormat":1},{"version":"36d8d3e7506b631c9582c251a2c0b8a28855af3f76719b12b534c6edf952748d","impliedFormat":1},{"version":"1ca69210cc42729e7ca97d3a9ad48f2e9cb0042bada4075b588ae5387debd318","impliedFormat":1},{"version":"f5ebe66baaf7c552cfa59d75f2bfba679f329204847db3cec385acda245e574e","impliedFormat":1},{"version":"ed59add13139f84da271cafd32e2171876b0a0af2f798d0c663e8eeb867732cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7c5e2ea4a9749097c347454805e933844ed207b6eefec6b7cfd418b5f5f7b28","impliedFormat":1},{"version":"b1810689b76fd473bd12cc9ee219f8e62f54a7d08019a235d07424afbf074d25","impliedFormat":1},{"version":"8caa5c86be1b793cd5f599e27ecb34252c41e011980f7d61ae4989a149ff6ccc","impliedFormat":1},{"version":"f9fd93190acb1ffe0bc0fb395df979452f8d625071e9ffc8636e4dfb86ab2508","impliedFormat":1},{"version":"5f41fd8732a89e940c58ce22206e3df85745feb8983e2b4c6257fb8cbb118493","impliedFormat":1},{"version":"17ed71200119e86ccef2d96b73b02ce8854b76ad6bd21b5021d4269bec527b5f","impliedFormat":1},{"version":"1cfa8647d7d71cb03847d616bd79320abfc01ddea082a49569fda71ac5ece66b","impliedFormat":1},{"version":"bb7a61dd55dc4b9422d13da3a6bb9cc5e89be888ef23bbcf6558aa9726b89a1c","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"4059592a5c2f2d5f75aac6f555702e3e64be45d1f4e54ba2444b92ce5ac84b1f","impliedFormat":1},{"version":"cbea99888785d49bb630dcbb1613c73727f2b5a2cf02e1abcaab7bcf8d6bf3c5","impliedFormat":1},{"version":"a3f1220f5331589384d77ed650001719baac21fcbed91e36b9abc5485b06335a","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"6ceac05c32f579adbed2f1a9c98cd297de3c00a3caaffc423385d00e82bce4ce","impliedFormat":1},{"version":"fa5bbc7ab4130dd8cdc55ea294ec39f76f2bc507a0f75f4f873e38631a836ca7","impliedFormat":1},{"version":"f7c024ce0f73f3a0e56f35826bed34dd9743ad7daa19068acca653dd7d45f010","impliedFormat":1},{"version":"d10ed89f8eebbf41788ab0a301fe75e3e4bc81133a8acc8fb9f77a800de85308","impliedFormat":1},{"version":"596d057adf5da16026fde7dc76c88c6690ebf16e46c230492a926ea34a88513e","impliedFormat":1},{"version":"5511a5ce5ca0f35a59ec5bbd7fbb7293966177694051a056de7220d4f28be242","impliedFormat":1},{"version":"098c6d4a39f4b57de9b20e611a1ebdc7d86ceaa5a34203b51320469951cf549e","impliedFormat":1},{"version":"653060b69b4c62825fca79d91259a5f42736f56dba428322b36cfae593ee8359","impliedFormat":1},{"version":"d38c7510cee97b30fe3fee6f4729580d29fca94c7115cac0f1197da6af575bfc","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"a9226d6aaddf108b737bc227ba7a5944f787f2c125eb1183047cbe1a04fc4269","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8a8bf772f83e9546b61720cf3b9add9aa4c2058479ad0d8db0d7c9fd948c4eaf","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"973b59a17aaa817eb205baf6c132b83475a5c0a44e8294a472af7793b1817e89","impliedFormat":1},{"version":"ada39cbb2748ab2873b7835c90c8d4620723aedf323550e8489f08220e477c7f","impliedFormat":1},{"version":"1364f64d2fb03bbb514edc42224abd576c064f89be6a990136774ecdd881a1da","impliedFormat":1},{"version":"741c438ec079a077b08d37d9c0466924b68e98ed47224e83fcb125c5863eb355","impliedFormat":1},{"version":"e8da637cbd6ed1cf6c36e9424f6bcee4515ca2c677534d4006cbd9a05f930f0c","impliedFormat":1},{"version":"fa34a00e044e9a3a6044abdb52d38bc7877ff1d6348aa79be99774e413c2568a","impliedFormat":1},{"version":"424b42ca9bf3d0aa599795b3b7cdebdd40d27c876e77664ec9526f24304b3947","impliedFormat":1},{"version":"97db28013378bc815528290a76a884276aa7a2470728fd212592d1b257d4e4ec","impliedFormat":1},{"version":"a41278669baac3dc42d95e762b810aa9371a613792d330766a7661f03df6fae0","impliedFormat":1},{"version":"4c91cc1ab59b55d880877ccf1999ded0bb2ebc8e3a597c622962d65bf0e76be8","impliedFormat":1},{"version":"5569bf3e094f521e461c0d58981d1381cdb085de1d91b35dcaefc6ed1248fe45","impliedFormat":1},{"version":"ca1b882a105a1972f82cc58e3be491e7d750a1eb074ffd13b198269f57ed9e1b","impliedFormat":1},{"version":"b714a2744382be1f2c8bf6e5376b0e4cc5546c2a0ed7585c8da82a9a3d9675ee","impliedFormat":1},{"version":"3867ca0e9757cc41e04248574f4f07b8f9e3c0c2a796a5eb091c65bfd2fc8bdb","impliedFormat":1},{"version":"fc7214ff37161bf1e89c52b11fc7dddceccab809e36ea0ee26353c7502b0b27b","impliedFormat":1},{"version":"58902668adae2e5eb67efbccb4048afa02308fa684f1a4e4c7d47668ecf58c1b","impliedFormat":1},{"version":"ef2d1bd01d144d426b72db3744e7a6b6bb518a639d5c9c8d86438fb75a3b1934","impliedFormat":1},{"version":"ca3ae117555ae4ddc5d0b38ace215fe645e12cbf36a473290f4804741cff4cdb","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"a38faf2579a5a094409d00e463aa142e8408e065b692742f4305dcda9affc4ec","impliedFormat":1},{"version":"476c48dfa7aef1b279542a1d90018f67912b3c970e147b77c2d8063c40c06b24","impliedFormat":1},{"version":"17937316a2f7f362dd6375251a9ce9e4960cfdc0aa7ba6cbd00656f7ab92334b","impliedFormat":1},{"version":"be2d91ce0cef98ac6a467d0b48813d78ae0a54d5f1a994afb16018a6b45f711d","impliedFormat":1},{"version":"a7a92f071d6891b2fa6542e343bdebc819492e6e14db37563bb71b8bd7e9b83f","impliedFormat":1},{"version":"99ace27cc2c78ef0fe3f92f11164eca7494b9f98a49ee0a19ede0a4c82a6a800","impliedFormat":1},{"version":"7545c9288b771b046c041357be55281f81e916fd978fba8d1ab80f7a19c9872c","impliedFormat":1},{"version":"fafaad58e8fec794bbc0b8471c83a032a6234231e65111d3a123902d085e0929","impliedFormat":1},{"version":"34f13a87ce0b5d9f3177909ea2fefd5f288df9fd1d3b02211d4623a60a4537ec","impliedFormat":1},{"version":"6ef06a3c167e93229e013b3be0eacb869f338262801ff160e840a64504583239","impliedFormat":1},{"version":"6e5f5cee603d67ee1ba6120815497909b73399842254fc1e77a0d5cdc51d8c9c","impliedFormat":1},{"version":"f6404e7837b96da3ea4d38c4f1a3812c96c9dcdf264e93d5bdb199f983a3ef4b","impliedFormat":1},{"version":"53e8716b56477507dcd80b45a516e87e04fddc2815ad1c4a110c577bd6f2a56d","impliedFormat":1},{"version":"3cd0346fc79e262233785d9fe2cbad08fc3fe6339af3419791687152ddfe5596","impliedFormat":1},{"version":"b1645ede06e14485c0cbcae199c5d9075f116fe34d9df7f55609511798e51878","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"ba7f9c1882b80478c7fe2268f12e06bb02445212ae104c92b03e4f774e605ae5","impliedFormat":1},{"version":"d23518a5f155f1a3e07214baf0295687507122ae2e6e9bd5e772551ebd4b3157","impliedFormat":1},{"version":"a984957da359a346925863242a2e096ce444d7952a6bb683289e1243628bc89d","impliedFormat":1},{"version":"a966ea6e57d54a96fd90453fb40ef482b61cec8e0797e6b3074925226643c7c6","impliedFormat":1},{"version":"700408b4faf9170f6424678e11255fa908467e223e84ed7c10b128243414264b","impliedFormat":1},{"version":"74424c988b16077ad1b318c303b7c1e65ab991cec09710603d39cdd52354bde0","impliedFormat":1},{"version":"88961917ec908f9fde57b86b8038b8b30766ba14cfdcc214c7c5498d7c9f7924","impliedFormat":1},{"version":"369db4c180c005815c807c5a2952d44c38db8572afa1c52565602f44dbdd77b1","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"056903d58aab97535445ec11984be0533769b4a36a422ac6eba01c2e2571dbd7","impliedFormat":1},{"version":"b47bb50ee9d8ac4f69ff2f616b556905e7cb486c554fcc7e04f31c21dfd5e919","impliedFormat":1},{"version":"48e8af7fdb2677a44522fd185d8c87deff4d36ee701ea003c6c780b1407a1397","impliedFormat":1},{"version":"606e6f841ba9667de5d83ca458449f0ed8c511ba635f753eaa731e532dea98c7","impliedFormat":1},{"version":"3026abd48e5e312f2328629ede6e0f770d21c3cd32cee705c450e589d015ee09","impliedFormat":1},{"version":"4a8bae6576783c910147d19ec6bef24fd2a24e83acbbb2043a60eec7134738e6","impliedFormat":1},{"version":"c325e0ae43fb49d82042ad9d8740fb7ae5aa10900697bcc253286bb3b25b5442","impliedFormat":1},{"version":"ad444a874f011d3a797f1a41579dbfcc6b246623f49c20009f60e211dbd5315e","impliedFormat":1},{"version":"368b2100f2635daf950e800f2731b093c5880e276f3773c9b9157ff0aeac0f6e","impliedFormat":1},{"version":"1d07d33162aa6e67fadac57f94278f1993c2cf812235343f0497a052313eddf4","impliedFormat":1},{"version":"d1a8da005ce5b2974b6b4bca95327d6aa230df23a7446e609c7e25ea84d7cf13","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"1822b69406252b606dc1aec3231a7104ac1d456cfa2c0a9041e61061895ae348","impliedFormat":1},{"version":"f7737bc5cdd9e20e64537ace1c892a4868fed47dc87611b8d43f7bd7d4361ccf","impliedFormat":1},{"version":"c1ac179620434b59c1569f2964a5c7354037ac91a212a1fb281673589965c893","impliedFormat":1},{"version":"9f891dc96f3e9343c4e823ba28195fd77e59c84199696a8bdfe7b67925732409","impliedFormat":1},{"version":"27efe8aa87d6699088ba2bb78a2101d51054f6273e0827f24e9caef82647ca5c","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"aa54626cd7dcd44f2cc9fd62c6e7e09aedfcd1372864526f9dd2d5eca16133f6","impliedFormat":1},{"version":"eb9c80fd1c0ea0eff7b0830d8e5eba8af21bb73578aa6c4cb78f7b6e93bc3e28","impliedFormat":1},{"version":"b21962d604522cdec8b13760091c7c83e691062077a5283c5213212bb7ea2db4","impliedFormat":1},{"version":"2c6f043430f24bde409ed1e70d197b3ef70607cd656817bfd6cf02e630bb7a39","impliedFormat":1},{"version":"495a5da35b04cd142d1301921ce8776c3bd8eab85bbf0ea694e631bc5cd35338","impliedFormat":1},{"version":"46ceb528c649c7c2c6d1c46e774c9f049f3e4f15766c5efaf6b510e0b5fd1434","impliedFormat":1},{"version":"9a99ae1436fe41e98bf61280e29e012c3b070d05fba49db37e49a10dffb29715","impliedFormat":1},{"version":"a2251459e8bdd8e9c9883bd9f1b5f18af247fee2b774e0e90d988749ca702c8b","impliedFormat":1},{"version":"55e528b0903b1572cae79a52445d21a66de54001aa4041663b17dcbca78faaa1","impliedFormat":1},{"version":"a805c88b28da817123a9e4c45ceb642ef0154c8ea41ea3dde0e64a70dde7ac5f","impliedFormat":1},{"version":"3182b3720ea5d895d14ecf8c4243c62f91278ea041df16fee6e1e7c820197548","impliedFormat":1},{"version":"32531dfbb0cdc4525296648f53b2b5c39b64282791e2a8c765712e49e6461046","impliedFormat":1},{"version":"3bfd88431ff03e76f98193aff3baea01493c918670dc42e1cedec9ed67cf0141","impliedFormat":1},{"version":"e489985388e2c71d3542612685b4a7db326922b57ac880f299da7026a4e8a117","impliedFormat":1},{"version":"d7f7b8dbdbe51042f63f0d671fc60c22465cb48e18c5dcb4c36baa83d773f5e8","impliedFormat":1},{"version":"19dcdc086f0c02968d5d774a8199dca62e5817797828e8d228658b75dcc1f616","affectsGlobalScope":true,"impliedFormat":1},{"version":"1efc1478edc8047b1cdb25f5138780b117c6502a62aa8b7ee928f269cd2cb1b3","impliedFormat":1},{"version":"991ce7d6e9a68aea60e1f136e9e18bf626857d0b1ad09f070ef2f0666e4f61e5","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1dc574e42493e8bf9bb37be44d9e38c5bd7bbc04f884e5e58b4d69636cb192b3","impliedFormat":1},{"version":"6bf9cdef836ed04a0b23414d26a134dbee65d8fa50153bbe5bdadc9bd55dd2df","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"6b8e8c0331a0c2e9fb53b8b0d346e44a8db8c788dae727a2c52f4cf3bd857f0d","impliedFormat":1},{"version":"0aa0f0184c0f9635dd1b95c178223aa262bb01ec8ac7b39c911ef2bd32b8f65b","affectsGlobalScope":true,"impliedFormat":1},{"version":"ec29be0737d39268696edcec4f5e97ce26f449fa9b7afc2f0f99a86def34a418","impliedFormat":1},{"version":"d94111e36b98d3cee902610bfcc7f83a473a5790a6ce32975009c7acc127fc4e","impliedFormat":1},{"version":"7ce21be1af9f34e76481d5030aa8b1fa23ff0f10848222fb488668b10dc33052","impliedFormat":1},{"version":"edaa27d57d30467edc63e9da7e7196acd315b02071f2c7ecd8475085a5cab9a2","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"ec9fd890d681789cb0aa9efbc50b1e0afe76fbf3c49c3ac50ff80e90e29c6bcb","impliedFormat":1},{"version":"167e0ad8d357a1c1a7d68be49914c7a446560c9c4a35d65c6970635c604e8602","impliedFormat":1},{"version":"9eac5a6beea91cfb119688bf44a5688b129b804ede186e5e2413572a534c21bb","impliedFormat":1},{"version":"6c292de17d4e8763406421cb91f545d1634c81486d8e14fceae65955c119584e","impliedFormat":1},{"version":"b7fff2d004c5879cae335db8f954eb1d61242d9f2d28515e67902032723caeab","impliedFormat":1},{"version":"8303df69e9d100e3df8f2d67ec77348cb6494dc406356fdd9b56e61aa7c3c758","impliedFormat":1},{"version":"3dd1c86894c90acf4f31a9e61de8e7a67158adb8334524930cec92fea73341c1","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"15959543f93f27e8e2b1a012fe28e14b682034757e2d7a6c1f02f87107fc731e","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"4e828bf688597c32905215785730cbdb603b54e284d472a23fc0195c6d4aeee8","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"4da80db9ed5a1a20fd5bfce863dd178b8928bcaf4a3d75e8657bcae32e572ede","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"abe1cd817246c003c25874b191b592aa202b80b763802f1f3cfe1955dff25714","impliedFormat":1},{"version":"42c686ce08bf5576ed178f4a6a62d1b580d941334fb53bdff7054e0980f2dc75","impliedFormat":1},{"version":"7531bb445f08a05e9a83c0a366c67cb5131c9816eb1f00c6362b31792639fc37","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"e2b097a887dcd8442eaf3668862b6219f078c9c56f1fd0015d8d009bfea57043","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"c0eeaaa67c85c3bb6c52b629ebbfd3b2292dc67e8c0ffda2fc6cd2f78dc471e6","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"cb048c7e28bdc3fc12766cc0203cc1da6c19ecb6d9614c7fc05d9df0908598db","impliedFormat":99},{"version":"c2008605e78208cfa9cd70bd29856b72dda7ad89df5dc895920f8e10bcb9cd0a","impliedFormat":99},{"version":"ec61ebac4d71c4698318673efbb5c481a6c4d374da8d285f6557541a5bd318d0","impliedFormat":99},{"version":"10ec84e648ffc7654868ca02c21a851bc211c8e4d50fd68131c1afa9afd96a33","impliedFormat":99},{"version":"b5934ca186f498c83e9a663d3df019d82290303fd86b1658cf27cf892b50aaf9","impliedFormat":1},{"version":"16fd66ae997b2f01c972531239da90fbf8ab4022bb145b9587ef746f6cecde5a","affectsGlobalScope":true,"impliedFormat":1},{"version":"0bcce57ab91bed60fc66440a053df9254f526ee98a812a35f91604734e21009c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d7a1155bc29ed4f608bad12f17d1eadccfc4a5ca55f0c483255089ab5c30855","impliedFormat":1},{"version":"79d056984a8964d3917c7587067447d7565d9da696fcf6ecaa5e8437a214f04e","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"8d8546e8dac9cf56a7853e46d297b560980455f0c99a35c7ac5ebc89e73f649d","impliedFormat":1},{"version":"cd0c5af42811a4a56a0f77856cfa6c170278e9522888db715b11f176df3ff1f2","impliedFormat":1},{"version":"5487b97cfa28b26b4a9ef0770f872bdbebd4c46124858de00f242c3eed7519f4","impliedFormat":1},{"version":"7a01f546ace66019156e4232a1bee2fabc2f8eabeb052473d926ee1693956265","impliedFormat":1},{"version":"fb53b1c6a6c799b7e3cc2de3fb5c9a1c04a1c60d4380a37792d84c5f8b33933b","impliedFormat":1},{"version":"8485b6da53ec35637d072e516631d25dae53984500de70a6989058f24354666f","impliedFormat":1},{"version":"ebe80346928736532e4a822154eb77f57ef3389dbe2b3ba4e571366a15448ef2","impliedFormat":1},{"version":"c2cb3c8ff388781258ea9ddbcd8a947f751bddd6886e1d3b3ea09ddaa895df80","impliedFormat":1},{"version":"f672c876c1a04a223cf2023b3d91e8a52bb1544c576b81bf64a8fec82be9969c","impliedFormat":1},{"version":"98a9cc18f661d28e6bd31c436e1984f3980f35e0f0aa9cf795c54f8ccb667ffe","impliedFormat":1},{"version":"c76b0c5727302341d0bdfa2cc2cee4b19ff185b554edb6e8543f0661d8487116","impliedFormat":1},{"version":"19903057d0249e45c579bef2b771c37609e4853a8b88adbb0b6b63f9e1d1f372","impliedFormat":1},{"version":"f5ef066942e4f0bd98200aa6a6694b831e73200c9b3ade77ad0aa2409e8fe1b1","impliedFormat":1},{"version":"b9e99cd94f4166a245f5158f7286c05406e2a4c694619bceb7a4f3519d1d768e","impliedFormat":1},{"version":"5568d7c32e5cf5f35e092649f4e5e168c3114c800b1d7545b7ae5e0415704802","impliedFormat":1},{"version":"57365ae017e0c541f600938a4deeb228a0e481ade617894dca41437c95dddaa9","impliedFormat":1},{"version":"094220a45928be7bbb5f749fbc5fc9f8183bbd25d0c529dbd09300179d6a39f6","impliedFormat":1},{"version":"20dc11f7647499cd153504af14fd405becdeab2d40dbb40fda66c0f993e0aed9","impliedFormat":1},{"version":"92d4846268140b00e6b116b003bb53112de7914aab0f30e70582cc2cf18e1cb7","impliedFormat":1},{"version":"c52227ccc6a7c340147c3e3e1478af80bfec3210167fd899d6dd08c8f3f24d58","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"ae75542ca40444773c29335014e2114a131b2ccac7bb96fe94fa0f8236cacd1a","impliedFormat":1},"7a1f284364e4e2053860dd4571a8d871b5b4b4b833f49056c7ce83384b5e732d",{"version":"14ecfc29e0c44ad4c5e50f9b597492cd8f45a2a635db8b5fe911a5da83e26cf8","impliedFormat":1},{"version":"569e762cf47aafdad508360a443c6c757e56c61db3b652b65458a7d168d139c4","impliedFormat":99},{"version":"02ed2766d79a00719ac3cc77851d54bd7197c1b12085ea12126bc2a65068223e","impliedFormat":99},{"version":"4b84373e192b7e0f8569b65eb16857098a6ee279b75d49223db2a751fdd7efde","impliedFormat":99},{"version":"5aeea312cd1d3cc5d72fc8a9c964439d771bdf41d9cce46667471b896b997473","impliedFormat":99},{"version":"7c37569e586a3276ae7cbc405c9f1c51efdf3f603cc8b76dd2196d1b01c2f9d0","impliedFormat":99},{"version":"bef359bd6998f4ce186f7450ec3fbd9cf47ee3097d75c26828b6d985c843a48d","impliedFormat":99},{"version":"696a04758e6c58966e18bb99a64292017fac57f0ba5482e1bc0b617be850b12e","impliedFormat":99},{"version":"46fe6faf3d6907bb675b5c37df15b6021b9ca1e4f91b25ccc422630b4c1568e3","impliedFormat":99},{"version":"c45a995ea8fd0d701c3e013dcf7a106809d9b1517c7115ade3f58d4196bd350c","impliedFormat":99},{"version":"0332891c6714ceea22d919677ba76e7875f4be1104dc1c2a19b9359b7a2e08e4","impliedFormat":99},{"version":"38a217719a1e847d3b56f0e513075f07740ea536a838c332d02b2ce4288f23be","impliedFormat":99},{"version":"d51809d133c78da34a13a1b4267e29afb0d979f50acbeb4321e10d74380beeea","impliedFormat":99},{"version":"68745f37d24b1b5800c45d0c5c00abfcbb031f9be0bcecdafd29405667397abe","impliedFormat":99},{"version":"fccc4725f7937821ed7744c796c090963929da13a497a05a58ba478d1c1442ef","impliedFormat":99},{"version":"ba0badf6398010ef0ba77947d4b52078fc84cb4f209f6b70194b3b2cfb1823d8","impliedFormat":99},{"version":"c3e72657ff6de287781e29531f2cceff9d01403f92fbf350dd3aaf3c8e44eced","impliedFormat":99},{"version":"60fbce4fe62a585d67228f8c1e43e7f1e5493519ac7f3d0fb383d95c1f690a1b","impliedFormat":99},{"version":"944248b9c52a2c6dfe7b9a7b0b0c6e42b0bf3469d8dbb05625fd564ee35cb3b0","impliedFormat":99},"42933f697965034ebfbfc920121ec429970bb4a5d68521108ac00153767e5c4b","00068dbbc088601743ab323162720eb0c135be056da394f4a3bcf82b66c5d593","2229b0ab0fc028633876e84ccfc94486413c371c9bda558f7d976c66c3649f02","7100ece4f7e7702f0f27db0d628b55c743ef6c4fd7051064803d7820109339cb","2de88fa8d0c9780a1b6486c3e86f786ce4838d054d21f95630909fcf7a858164","f7d52e8ad99e21071a55228ba506e0d6d52a348cafac27609bc6f4988219ef0c","7e520c3667d81cba10ed744e4ed92409949eb39c3c010a81bcf70638a152f0ff",{"version":"d04f947114fa00a20ee3c3182bb2863c30869df93293cc673f200defadbd69d9","impliedFormat":1},{"version":"c3f3190aa2cdbb8d997ed914f1e7b42848156ba5b47ae570cf7a8753a1811e12","impliedFormat":1},{"version":"785926dee839d0b3f5e479615d5653d77f6a9ef8aa4eea5bbdce2703c860b254","impliedFormat":1},{"version":"66d5c68894bb2975727cd550b53cd6f9d99f7cb77cb0cbecdd4af1c9332b01dd","impliedFormat":1},{"version":"dcd3f12a4f679467c7625ea8ff91c98368e367e922674be3f4a62fef705fe54f","impliedFormat":1},{"version":"212f5fc4d47c00231d36971460a5912f3c802356ef029ecdb178c427916a9a6b","signature":"241ddcd8b5b8196e7fbc5f7046a5f43a32799fc9167cb1a8d8549314a3bca67a"},{"version":"ceaab64372ca1dfa47f1a4b6b951e624b70080befbb28e3e750171033b372667","signature":"52c85d5fc439c63bca3a080904fe064adff41f29318f2332385589f6cb6c69c7"},"fb16d6161557071ae03e8188a6879adf224293b8b96eb45affc464067ec7b228","aaffdbe9d6e43320baec029bf7a236fc15feca90811f20994fe651046b478c2c","10220d5c81afdde6a33b2dae07075363457d46ad68f4ebdea22c4705209ff6a3","d3c48d0bfe53a3409497e2bca4d8197de35f3e88130aae79556f3f834aa0a9f8","35dc94b4942f94b8b09a1788d67612589b606a24b77950f12ae4e8527781627b","bc15ab59c7c9e580236ea31a5263813e61f92e0a7463e1c26effc11981c8ae2b","373e95e60216b7be0d1ad2cb153e46e3df1c31409952ac5992dff05f7251ad83","9dffd08fe87876a0444d3c969598493d87311259028b452149e40468cacf65a6","593c19a95e81604725a80858b7de42b2d5addf352bbc8913f6a02563fd90c7d9","cb974632c06887a38f6b8cbc14dcb724dff88d03cda3806308cadd2f761e3945","3e63babe1884fd22a33ae3372349dd1a50e9eb818c18d6c79d557db4eaf96aca",{"version":"a3d3f704c5339a36da3ca8c62b29072f87e86c783b8452d235992142ec71aa2d","impliedFormat":1},{"version":"104c67f0da1bdf0d94865419247e20eded83ce7f9911a1aa75fc675c077ca66e","impliedFormat":1},{"version":"cc0d0b339f31ce0ab3b7a5b714d8e578ce698f1e13d7f8c60bfb766baeb1d35c","impliedFormat":1},{"version":"117ffeecf6c55e25b6446f449ad079029b5e7317399b0a693858faaaea5ca73e","impliedFormat":1},{"version":"25be1eb939c9c63242c7a45446edb20c40541da967f43f1aa6a00ed53c0552db","impliedFormat":1},{"version":"d34aa8df2d0b18fb56b1d772ff9b3c7aea7256cf0d692f969be6e1d27b74d660","impliedFormat":1},{"version":"93a3b8e57c68e348fc4054b245bd7cf4893225f56c991028844b693c2fa8c03c","impliedFormat":1},{"version":"2f5747b1508ccf83fad0c251ba1e5da2f5a30b78b09ffa1cfaf633045160afed","impliedFormat":1},{"version":"90407bbaa24977b8a6a90861148ac98d8652afe69992a90d823f29e9807fe2d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b71c603a539078a5e3a039b20f2b0a0d1708967530cf97dec8850a9ca45baa2b","impliedFormat":1},{"version":"d3f2d715f57df3f04bf7b16dde01dec10366f64fce44503c92b8f78f614c1769","impliedFormat":1},{"version":"cb90077223cc1365fa21ef0911a1f9b8f2f878943523d97350dc557973ca3823","impliedFormat":1},{"version":"18f1541b81b80d806120a3489af683edfb811deb91aeca19735d9bb2613e6311","impliedFormat":1},{"version":"232f118ae64ab84dcd26ddb60eaed5a6e44302d36249abf05e9e3fc2cbb701a2","impliedFormat":1},{"version":"f60e3e3060207ac982da13363181fd7ee4beecc19a7c569f0d6bb034331066c2","impliedFormat":1},{"version":"17230b34bb564a3a2e36f9d3985372ccab4ad1722df2c43f7c5c2b553f68e5db","impliedFormat":1},{"version":"dcb41cc4b671f5f85d3754e68dfde7824686a8adbb4c690cb259c1501cb868e1","impliedFormat":1},{"version":"21ac4cf3f8d8c6e1201cb31f600be708c9a37867fc5c73b7ccf80560fae591c8","impliedFormat":1},{"version":"0dfe35191a04e8f9dc7caeb9f52f2ee07402736563d12cbccd15fb5f31ac877f","impliedFormat":1},{"version":"798367363a3274220cbed839b883fe2f52ba7197b25e8cb2ac59c1e1fd8af6b7","impliedFormat":1},{"version":"2636a309ed87d6876728d9aca846a76b372cf2a21a4fdf9940a82a2dd86687d0","impliedFormat":1},{"version":"7d2b7fe4adb76d8253f20e4dbdce044f1cdfab4902ec33c3604585f553883f7d","impliedFormat":1}],"root":[334,[354,360],[366,378]],"options":{"allowJs":true,"alwaysStrict":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"jsx":1,"module":99,"noFallthroughCasesInSwitch":true,"noImplicitAny":true,"noImplicitReturns":true,"noImplicitThis":true,"noUnusedLocals":false,"noUnusedParameters":false,"skipLibCheck":true,"sourceMap":true,"strict":true,"strictBindCallApply":true,"strictFunctionTypes":true,"strictNullChecks":true,"strictPropertyInitialization":true,"target":7},"referencedMap":[[375,1],[376,2],[377,3],[373,4],[374,5],[378,6],[360,7],[367,8],[354,9],[368,7],[358,10],[359,11],[369,12],[370,13],[371,12],[356,13],[366,14],[357,15],[355,13],[334,16],[266,17],[335,17],[107,18],[108,18],[109,19],[64,20],[110,21],[111,22],[112,23],[59,17],[62,24],[60,17],[61,17],[113,25],[114,26],[115,27],[116,28],[117,29],[118,30],[119,30],[120,31],[121,32],[122,33],[123,34],[65,17],[63,17],[124,35],[125,36],[126,37],[158,38],[127,39],[128,40],[129,41],[130,42],[131,43],[132,44],[133,45],[134,46],[135,47],[136,48],[137,48],[138,49],[139,17],[140,50],[142,51],[141,52],[143,53],[144,54],[145,55],[146,56],[147,57],[148,58],[149,59],[150,60],[151,61],[152,62],[153,63],[154,64],[155,65],[66,17],[67,17],[68,17],[106,66],[156,67],[157,68],[51,17],[163,69],[164,70],[162,13],[160,71],[161,72],[49,17],[52,73],[50,17],[347,17],[349,74],[341,75],[340,76],[345,77],[342,75],[348,17],[343,78],[344,79],[346,17],[336,17],[338,80],[337,80],[339,81],[58,82],[278,83],[282,84],[284,85],[192,86],[204,87],[226,88],[193,89],[220,17],[210,90],[194,91],[212,90],[205,90],[169,90],[238,92],[174,17],[235,93],[236,94],[223,17],[331,95],[313,17],[312,96],[234,97],[328,98],[329,99],[224,100],[237,17],[293,101],[295,102],[271,103],[270,104],[269,105],[298,13],[268,106],[197,17],[301,17],[303,17],[305,107],[302,13],[304,108],[165,17],[221,17],[167,109],[257,17],[258,17],[260,17],[263,110],[259,17],[261,111],[262,111],[203,17],[277,106],[285,112],[289,113],[178,114],[327,115],[228,92],[232,116],[229,117],[177,118],[208,119],[244,120],[170,121],[176,122],[166,123],[255,124],[254,125],[209,17],[189,126],[218,17],[243,127],[242,17],[219,128],[245,128],[246,129],[172,130],[241,131],[171,17],[252,132],[253,133],[190,134],[188,135],[187,136],[240,137],[247,138],[173,139],[239,140],[227,141],[159,17],[250,142],[248,17],[222,143],[225,144],[249,145],[211,146],[251,147],[256,148],[179,17],[184,17],[181,17],[182,17],[183,17],[195,121],[213,149],[175,150],[180,17],[217,151],[216,152],[191,153],[207,154],[206,155],[230,17],[196,156],[233,157],[231,158],[198,159],[200,160],[306,161],[199,162],[201,163],[280,17],[281,17],[279,17],[300,17],[202,164],[57,17],[330,165],[272,17],[275,166],[287,13],[291,13],[265,167],[168,17],[267,17],[274,17],[273,168],[215,169],[214,170],[186,171],[185,17],[283,17],[311,13],[276,172],[48,17],[56,173],[53,13],[54,17],[55,17],[286,174],[288,175],[290,176],[292,177],[310,178],[294,178],[309,179],[296,180],[333,181],[332,182],[297,183],[299,184],[307,185],[308,186],[264,187],[362,13],[363,13],[361,17],[364,188],[365,189],[353,190],[352,191],[351,192],[350,193],[46,17],[47,17],[8,17],[9,17],[11,17],[10,17],[2,17],[12,17],[13,17],[14,17],[15,17],[16,17],[17,17],[18,17],[19,17],[3,17],[20,17],[21,17],[4,17],[22,17],[26,17],[23,17],[24,17],[25,17],[27,17],[28,17],[29,17],[5,17],[30,17],[31,17],[32,17],[33,17],[6,17],[37,17],[34,17],[35,17],[36,17],[38,17],[7,17],[39,17],[44,17],[45,17],[40,17],[41,17],[42,17],[43,17],[1,17],[84,194],[94,195],[83,194],[104,196],[75,197],[74,198],[103,186],[97,199],[102,200],[77,201],[91,202],[76,203],[100,204],[72,205],[71,186],[101,206],[73,207],[78,208],[79,17],[82,208],[69,17],[105,209],[95,210],[86,211],[87,212],[89,213],[85,214],[88,215],[98,186],[80,216],[81,217],[90,218],[70,219],[93,210],[92,208],[96,17],[99,220],[326,221],[318,222],[324,223],[320,17],[321,17],[319,224],[322,221],[314,17],[315,17],[325,225],[317,226],[323,227],[316,228],[372,229],[379,17],[381,230],[380,157],[382,17],[383,157],[387,231],[392,232],[388,17],[389,17],[399,233],[398,234],[385,17],[386,17],[384,235],[391,236],[390,237],[400,17],[397,238],[394,186],[396,239],[395,186],[393,17]],"affectedFilesPendingEmit":[[375,51],[376,51],[377,51],[373,51],[374,51],[378,51],[360,51],[367,51],[354,51],[368,51],[358,51],[359,51],[369,51],[370,51],[371,51],[356,51],[366,51],[357,51],[355,51],[372,51]],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bizlin-chat",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Open-source WhatsApp Cloud API Team Inbox by Bizlin Technologies",
|
|
5
|
-
"main": "backend/index.
|
|
5
|
+
"main": "backend/src/index.ts",
|
|
6
6
|
"bin": {
|
|
7
7
|
"bizlin-chat": "bin/bizlin-chat.js"
|
|
8
8
|
},
|
|
@@ -11,8 +11,18 @@
|
|
|
11
11
|
"frontend"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
14
|
+
"dev:backend": "ts-node --transpile-only backend/src/index.ts",
|
|
15
|
+
"dev:frontend": "cd frontend && next dev",
|
|
16
|
+
"dev": "concurrently \"npm:dev:backend\" \"npm:dev:frontend\"",
|
|
17
|
+
"build:backend": "tsc -p backend/tsconfig.json",
|
|
18
|
+
"build:frontend": "cd frontend && next build",
|
|
19
|
+
"build": "npm run build:backend && npm run build:frontend",
|
|
20
|
+
"start:backend": "node backend/dist/index.js",
|
|
21
|
+
"start:frontend": "cd frontend && next start -p 3000",
|
|
22
|
+
"start": "concurrently \"npm:start:backend\" \"npm:start:frontend\"",
|
|
23
|
+
"deploy": "npm run build && npm run deploy:docker",
|
|
24
|
+
"deploy:docker": "docker build -t bizlin-chat:latest . && docker push bizlin-chat:latest",
|
|
25
|
+
"deploy:npm": "npm run build && npm publish",
|
|
16
26
|
"test": "echo \"No tests specified\" && exit 0"
|
|
17
27
|
},
|
|
18
28
|
"keywords": [
|
|
@@ -32,5 +42,41 @@
|
|
|
32
42
|
"license": "MIT",
|
|
33
43
|
"engines": {
|
|
34
44
|
"node": ">=14"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"axios": "1.5.0",
|
|
48
|
+
"bcryptjs": "2.4.3",
|
|
49
|
+
"bufferutil": "^4.0.8",
|
|
50
|
+
"cors": "2.8.5",
|
|
51
|
+
"express": "4.18.2",
|
|
52
|
+
"form-data": "^4.0.5",
|
|
53
|
+
"multer": "^2.1.1",
|
|
54
|
+
"next": "13.4.7",
|
|
55
|
+
"pg": "8.11.0",
|
|
56
|
+
"react": "18.2.0",
|
|
57
|
+
"react-dom": "18.2.0",
|
|
58
|
+
"react-icons": "^4.12.0",
|
|
59
|
+
"socket.io": "4.7.2",
|
|
60
|
+
"socket.io-client": "4.7.2",
|
|
61
|
+
"sqlite3": "5.1.6",
|
|
62
|
+
"utf-8-validate": "^6.0.0",
|
|
63
|
+
"uuid": "9.0.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/bcryptjs": "^2.4.2",
|
|
67
|
+
"@types/express": "^4.17.17",
|
|
68
|
+
"@types/multer": "^2.1.0",
|
|
69
|
+
"@types/node": "^20.3.1",
|
|
70
|
+
"@types/pg": "^8.10.9",
|
|
71
|
+
"@types/react": "^18.2.12",
|
|
72
|
+
"@types/react-dom": "^18.2.5",
|
|
73
|
+
"@types/uuid": "^9.0.2",
|
|
74
|
+
"autoprefixer": "^10.4.0",
|
|
75
|
+
"concurrently": "^8.0.1",
|
|
76
|
+
"nodemon": "^3.0.1",
|
|
77
|
+
"postcss": "^8.4.0",
|
|
78
|
+
"tailwindcss": "^3.4.8",
|
|
79
|
+
"ts-node": "^10.9.1",
|
|
80
|
+
"typescript": "^5.1.3"
|
|
35
81
|
}
|
|
36
82
|
}
|
package/backend/README.md
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# Backend (Node/Express)
|
|
2
|
-
|
|
3
|
-
Run locally:
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
cd backend
|
|
7
|
-
npm install
|
|
8
|
-
npm start
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
API endpoints:
|
|
12
|
-
|
|
13
|
-
- `GET /api/messages` — list messages
|
|
14
|
-
- `POST /api/messages` — send message, body `{ text, sender? }`
|
|
15
|
-
|
|
16
|
-
Realtime: Socket.IO server on the same port (emits `message` events).
|
|
17
|
-
|
|
18
|
-
Example: http://localhost:4000/api/hello
|
|
19
|
-
|
|
20
|
-
Database (PostgreSQL)
|
|
21
|
-
|
|
22
|
-
The backend supports PostgreSQL. By default it connects to the `DATABASE_URL` env var or `postgresql://postgres:postgres@localhost:5432/bizchat`.
|
|
23
|
-
|
|
24
|
-
Quick start with Docker Compose:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
docker compose up -d
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
This starts a Postgres instance on port 5432. Then run the backend as usual and it will auto-create the `messages` table.
|
|
31
|
-
|
|
32
|
-
WhatsApp integration
|
|
33
|
-
|
|
34
|
-
Set these environment variables for WhatsApp Cloud API integration:
|
|
35
|
-
|
|
36
|
-
- `WHATSAPP_PHONE_NUMBER_ID` — your phone number ID from the Meta App Dashboard
|
|
37
|
-
- `WHATSAPP_TOKEN` — system user permanent access token (keep secret)
|
|
38
|
-
- `WHATSAPP_VERIFY_TOKEN` — a token you set to verify webhook subscription
|
|
39
|
-
- `WHATSAPP_API_VERSION` — optional, defaults to `v15.0`
|
|
40
|
-
|
|
41
|
-
Endpoints:
|
|
42
|
-
|
|
43
|
-
- `GET /webhook` — verification endpoint used when subscribing to webhooks (uses `WHATSAPP_VERIFY_TOKEN`).
|
|
44
|
-
- `POST /webhook` — receives webhook events (incoming messages and statuses).
|
|
45
|
-
- `POST /api/send` — send a text message via WhatsApp Cloud API. Body: `{ "to": "<whatsapp_number>", "text": "Hello" }`.
|
|
46
|
-
- `POST /api/send` — send a text message via WhatsApp Cloud API. Body: `{ "to": "<whatsapp_number>", "text": "Hello" }`.
|
|
47
|
-
- `POST /api/sendMedia` — send media by URL. Body: `{ "to": "<whatsapp_number>", "mediaUrl": "https://...", "mediaType": "image|audio|video|document" }`.
|
|
48
|
-
- `POST /api/sendTemplate` — send a template message. Body: `{ "to": "<whatsapp_number>", "templateName": "template_name", "language": "en_US", "components": [...] }`.
|
|
49
|
-
|
|
50
|
-
The backend persists incoming messages to the `messages` table and emits realtime `message` events via Socket.IO.
|
|
51
|
-
|
|
52
|
-
Security
|
|
53
|
-
- Webhook requests are verified using `X-Hub-Signature-256` when `WHATSAPP_APP_SECRET` is set.
|
|
54
|
-
|
package/backend/src/db.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
|
|
4
|
-
const DB_CLIENT = (process.env.DB_CLIENT || '').toLowerCase()
|
|
5
|
-
|
|
6
|
-
if (DB_CLIENT === 'postgres' || (process.env.DATABASE_URL || '').startsWith('postgres')) {
|
|
7
|
-
const { Pool } = require('pg')
|
|
8
|
-
const DATABASE_URL = process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/bizchat'
|
|
9
|
-
const pool = new Pool({ connectionString: DATABASE_URL })
|
|
10
|
-
|
|
11
|
-
async function init() {
|
|
12
|
-
const sql = `
|
|
13
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
14
|
-
id TEXT PRIMARY KEY,
|
|
15
|
-
text TEXT NOT NULL,
|
|
16
|
-
sender TEXT,
|
|
17
|
-
ts BIGINT NOT NULL,
|
|
18
|
-
wa_id TEXT,
|
|
19
|
-
direction TEXT,
|
|
20
|
-
type TEXT,
|
|
21
|
-
media_url TEXT,
|
|
22
|
-
status TEXT
|
|
23
|
-
);
|
|
24
|
-
`
|
|
25
|
-
await pool.query(sql)
|
|
26
|
-
// accounts table
|
|
27
|
-
await pool.query(`
|
|
28
|
-
CREATE TABLE IF NOT EXISTS accounts (
|
|
29
|
-
id SERIAL PRIMARY KEY,
|
|
30
|
-
username TEXT UNIQUE NOT NULL,
|
|
31
|
-
password_hash TEXT NOT NULL,
|
|
32
|
-
name TEXT,
|
|
33
|
-
email TEXT,
|
|
34
|
-
role TEXT DEFAULT 'admin'
|
|
35
|
-
);
|
|
36
|
-
`)
|
|
37
|
-
// whatsapp accounts
|
|
38
|
-
await pool.query(`
|
|
39
|
-
CREATE TABLE IF NOT EXISTS whatsapp_accounts (
|
|
40
|
-
id SERIAL PRIMARY KEY,
|
|
41
|
-
phone_id TEXT,
|
|
42
|
-
token TEXT,
|
|
43
|
-
api_version TEXT
|
|
44
|
-
);
|
|
45
|
-
`)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function getMessages() {
|
|
49
|
-
const res = await pool.query('SELECT id, text, sender, ts, wa_id, direction, type, media_url FROM messages ORDER BY ts ASC')
|
|
50
|
-
return res.rows
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function addMessage(message) {
|
|
54
|
-
const { id, text, sender, ts, wa_id, direction, type, media_url, status } = message
|
|
55
|
-
const sql = 'INSERT INTO messages(id, text, sender, ts, wa_id, direction, type, media_url, status) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9)'
|
|
56
|
-
await pool.query(sql, [id, text, sender, ts, wa_id || null, direction || null, type || null, media_url || null, status || null])
|
|
57
|
-
return message
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function updateMessageStatus(id, status) {
|
|
61
|
-
const sql = 'UPDATE messages SET status = $1 WHERE id = $2'
|
|
62
|
-
await pool.query(sql, [status, id])
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function getAdminCount() {
|
|
66
|
-
const res = await pool.query('SELECT COUNT(*)::int AS cnt FROM accounts')
|
|
67
|
-
return res.rows[0] && res.rows[0].cnt
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function createAdmin({ username, password_hash, name, email }) {
|
|
71
|
-
const sql = 'INSERT INTO accounts(username, password_hash, name, email, role) VALUES($1,$2,$3,$4,$5)'
|
|
72
|
-
await pool.query(sql, [username, password_hash, name || null, email || null, 'admin'])
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function saveWhatsAppAccount({ phone_id, token, api_version }) {
|
|
76
|
-
await pool.query('DELETE FROM whatsapp_accounts')
|
|
77
|
-
const sql = 'INSERT INTO whatsapp_accounts(phone_id, token, api_version) VALUES($1,$2,$3)'
|
|
78
|
-
await pool.query(sql, [phone_id || null, token || null, api_version || null])
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function getWhatsAppAccount() {
|
|
82
|
-
const res = await pool.query('SELECT phone_id, token, api_version FROM whatsapp_accounts LIMIT 1')
|
|
83
|
-
return res.rows[0]
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
module.exports = { init, getMessages, addMessage, updateMessageStatus, getAdminCount, createAdmin, saveWhatsAppAccount, getWhatsAppAccount, pool }
|
|
87
|
-
|
|
88
|
-
} else {
|
|
89
|
-
// Default to sqlite
|
|
90
|
-
const sqlite3 = require('sqlite3')
|
|
91
|
-
const dbPath = (process.env.DATABASE_URL && process.env.DATABASE_URL.startsWith('sqlite:'))
|
|
92
|
-
? process.env.DATABASE_URL.replace(/^sqlite:/, '')
|
|
93
|
-
: path.join(process.cwd(), 'data', 'bizchat.sqlite')
|
|
94
|
-
|
|
95
|
-
const dir = path.dirname(dbPath)
|
|
96
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
97
|
-
|
|
98
|
-
const db = new sqlite3.Database(dbPath)
|
|
99
|
-
|
|
100
|
-
function run(sql, params=[]) {
|
|
101
|
-
return new Promise((resolve, reject) => db.run(sql, params, function(err) {
|
|
102
|
-
if (err) reject(err); else resolve(this)
|
|
103
|
-
}))
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function all(sql, params=[]) {
|
|
107
|
-
return new Promise((resolve, reject) => db.all(sql, params, (err, rows) => err ? reject(err) : resolve(rows)))
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function init() {
|
|
111
|
-
const sql = `
|
|
112
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
113
|
-
id TEXT PRIMARY KEY,
|
|
114
|
-
text TEXT NOT NULL,
|
|
115
|
-
sender TEXT,
|
|
116
|
-
ts BIGINT NOT NULL,
|
|
117
|
-
wa_id TEXT,
|
|
118
|
-
direction TEXT,
|
|
119
|
-
type TEXT,
|
|
120
|
-
media_url TEXT,
|
|
121
|
-
status TEXT
|
|
122
|
-
);
|
|
123
|
-
`
|
|
124
|
-
await run(sql)
|
|
125
|
-
// accounts table
|
|
126
|
-
await run(`
|
|
127
|
-
CREATE TABLE IF NOT EXISTS accounts (
|
|
128
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
129
|
-
username TEXT UNIQUE NOT NULL,
|
|
130
|
-
password_hash TEXT NOT NULL,
|
|
131
|
-
name TEXT,
|
|
132
|
-
email TEXT,
|
|
133
|
-
role TEXT DEFAULT 'admin'
|
|
134
|
-
);
|
|
135
|
-
`)
|
|
136
|
-
await run(`
|
|
137
|
-
CREATE TABLE IF NOT EXISTS whatsapp_accounts (
|
|
138
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
139
|
-
phone_id TEXT,
|
|
140
|
-
token TEXT,
|
|
141
|
-
api_version TEXT
|
|
142
|
-
);
|
|
143
|
-
`)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function getMessages() {
|
|
147
|
-
const rows = await all('SELECT id, text, sender, ts, wa_id, direction, type, media_url FROM messages ORDER BY ts ASC')
|
|
148
|
-
return rows
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async function addMessage(message) {
|
|
152
|
-
const { id, text, sender, ts, wa_id, direction, type, media_url, status } = message
|
|
153
|
-
const sql = 'INSERT OR REPLACE INTO messages(id, text, sender, ts, wa_id, direction, type, media_url, status) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
|
154
|
-
await run(sql, [id, text, sender, ts, wa_id || null, direction || null, type || null, media_url || null, status || null])
|
|
155
|
-
return message
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async function updateMessageStatus(id, status) {
|
|
159
|
-
const sql = 'UPDATE messages SET status = ? WHERE id = ?'
|
|
160
|
-
await run(sql, [status, id])
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async function getAdminCount() {
|
|
164
|
-
const rows = await all('SELECT COUNT(*) as cnt FROM accounts')
|
|
165
|
-
return rows && rows[0] && rows[0].cnt
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async function createAdmin({ username, password_hash, name, email }) {
|
|
169
|
-
const sql = 'INSERT INTO accounts(username, password_hash, name, email, role) VALUES(?, ?, ?, ?, ?)'
|
|
170
|
-
await run(sql, [username, password_hash, name || null, email || null, 'admin'])
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async function saveWhatsAppAccount({ phone_id, token, api_version }) {
|
|
174
|
-
await run('DELETE FROM whatsapp_accounts')
|
|
175
|
-
const sql = 'INSERT INTO whatsapp_accounts(phone_id, token, api_version) VALUES(?, ?, ?)'
|
|
176
|
-
await run(sql, [phone_id || null, token || null, api_version || null])
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async function getWhatsAppAccount() {
|
|
180
|
-
const rows = await all('SELECT phone_id, token, api_version FROM whatsapp_accounts LIMIT 1')
|
|
181
|
-
return rows && rows[0]
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
module.exports = { init, getMessages, addMessage, updateMessageStatus, getAdminCount, createAdmin, saveWhatsAppAccount, getWhatsAppAccount, db }
|
|
185
|
-
}
|
package/backend/src/index.js
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
const express = require('express')
|
|
2
|
-
const cors = require('cors')
|
|
3
|
-
const http = require('http')
|
|
4
|
-
const { Server } = require('socket.io')
|
|
5
|
-
const { v4: uuidv4 } = require('uuid')
|
|
6
|
-
const db = require('./db')
|
|
7
|
-
const whatsapp = require('./whatsapp')
|
|
8
|
-
const crypto = require('crypto')
|
|
9
|
-
|
|
10
|
-
const app = express()
|
|
11
|
-
app.use(cors())
|
|
12
|
-
app.use(express.json())
|
|
13
|
-
const bcrypt = require('bcryptjs')
|
|
14
|
-
|
|
15
|
-
app.get('/api/hello', (req, res) => {
|
|
16
|
-
res.json({ message: 'Hello from backend' })
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
app.get('/api/messages', async (req, res) => {
|
|
20
|
-
try {
|
|
21
|
-
const rows = await db.getMessages()
|
|
22
|
-
res.json(rows)
|
|
23
|
-
} catch (err) {
|
|
24
|
-
console.error(err)
|
|
25
|
-
res.status(500).json({ error: 'failed to load messages' })
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
// Setup endpoints
|
|
30
|
-
app.get('/api/setup/status', async (req, res) => {
|
|
31
|
-
try {
|
|
32
|
-
const cnt = await db.getAdminCount()
|
|
33
|
-
res.json({ setup: !!cnt })
|
|
34
|
-
} catch (err) {
|
|
35
|
-
console.error('setup status error', err)
|
|
36
|
-
res.status(500).json({ error: 'failed to check setup status' })
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
app.post('/api/setup/create-admin', async (req, res) => {
|
|
41
|
-
try {
|
|
42
|
-
const { username, password, name, email } = req.body
|
|
43
|
-
if (!username || !password) return res.status(400).json({ error: 'username and password required' })
|
|
44
|
-
const hash = bcrypt.hashSync(password, 10)
|
|
45
|
-
await db.createAdmin({ username, password_hash: hash, name, email })
|
|
46
|
-
res.status(201).json({ ok: true })
|
|
47
|
-
} catch (err) {
|
|
48
|
-
console.error('create admin error', err)
|
|
49
|
-
res.status(500).json({ error: 'failed to create admin' })
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
app.post('/api/setup/whatsapp', async (req, res) => {
|
|
54
|
-
try {
|
|
55
|
-
const { phone_id, token, api_version } = req.body
|
|
56
|
-
if (!phone_id || !token) return res.status(400).json({ error: 'phone_id and token required' })
|
|
57
|
-
await db.saveWhatsAppAccount({ phone_id, token, api_version })
|
|
58
|
-
res.status(201).json({ ok: true })
|
|
59
|
-
} catch (err) {
|
|
60
|
-
console.error('save whatsapp account error', err)
|
|
61
|
-
res.status(500).json({ error: 'failed to save whatsapp account' })
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
app.post('/api/setup/test-whatsapp', async (req, res) => {
|
|
66
|
-
try {
|
|
67
|
-
const { phone_id, token, api_version } = req.body
|
|
68
|
-
if (!phone_id || !token) return res.status(400).json({ error: 'phone_id and token required' })
|
|
69
|
-
const result = await whatsapp.testAccount({ phone_id, token, api_version })
|
|
70
|
-
if (result.ok) return res.json({ ok: true, info: result.body })
|
|
71
|
-
return res.status(400).json({ ok: false, error: result.error })
|
|
72
|
-
} catch (err) {
|
|
73
|
-
console.error('test whatsapp error', err)
|
|
74
|
-
res.status(500).json({ error: 'failed to test whatsapp account' })
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
app.post('/api/messages', async (req, res) => {
|
|
79
|
-
try {
|
|
80
|
-
const { text, sender } = req.body
|
|
81
|
-
if (!text) return res.status(400).json({ error: 'text is required' })
|
|
82
|
-
const message = { id: uuidv4(), text, sender: sender || 'agent', ts: Date.now() }
|
|
83
|
-
await db.addMessage(message)
|
|
84
|
-
io.emit('message', message)
|
|
85
|
-
res.status(201).json(message)
|
|
86
|
-
} catch (err) {
|
|
87
|
-
console.error(err)
|
|
88
|
-
res.status(500).json({ error: 'failed to save message' })
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
// Send outbound message via WhatsApp Cloud API
|
|
93
|
-
app.post('/api/send', async (req, res) => {
|
|
94
|
-
const { to, text } = req.body
|
|
95
|
-
if (!to || !text) return res.status(400).json({ error: 'to and text are required' })
|
|
96
|
-
try {
|
|
97
|
-
const account = await db.getWhatsAppAccount()
|
|
98
|
-
const resp = await whatsapp.sendText(account, to, text)
|
|
99
|
-
const waMessageId = resp && resp.messages && resp.messages[0] && resp.messages[0].id
|
|
100
|
-
const message = { id: waMessageId || uuidv4(), text, sender: 'us', ts: Date.now(), wa_id: to, direction: 'outbound', type: 'text' }
|
|
101
|
-
await db.addMessage(message)
|
|
102
|
-
io.emit('message', message)
|
|
103
|
-
res.status(200).json(message)
|
|
104
|
-
} catch (err) {
|
|
105
|
-
console.error('send failed', err?.response?.data || err.message || err)
|
|
106
|
-
res.status(500).json({ error: 'failed to send message' })
|
|
107
|
-
}
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
app.post('/api/sendMedia', async (req, res) => {
|
|
111
|
-
const { to, mediaUrl, mediaType } = req.body
|
|
112
|
-
if (!to || !mediaUrl) return res.status(400).json({ error: 'to and mediaUrl are required' })
|
|
113
|
-
try {
|
|
114
|
-
const account = await db.getWhatsAppAccount()
|
|
115
|
-
const resp = await whatsapp.sendMedia(account, to, mediaUrl, mediaType || 'image')
|
|
116
|
-
const waMessageId = resp && resp.messages && resp.messages[0] && resp.messages[0].id
|
|
117
|
-
const message = { id: waMessageId || uuidv4(), text: mediaUrl, sender: 'us', ts: Date.now(), wa_id: to, direction: 'outbound', type: mediaType || 'image', media_url: mediaUrl }
|
|
118
|
-
await db.addMessage(message)
|
|
119
|
-
io.emit('message', message)
|
|
120
|
-
res.status(200).json(message)
|
|
121
|
-
} catch (err) {
|
|
122
|
-
console.error('sendMedia failed', err?.response?.data || err.message || err)
|
|
123
|
-
res.status(500).json({ error: 'failed to send media' })
|
|
124
|
-
}
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
app.post('/api/sendTemplate', async (req, res) => {
|
|
128
|
-
const { to, templateName, language, components } = req.body
|
|
129
|
-
if (!to || !templateName) return res.status(400).json({ error: 'to and templateName are required' })
|
|
130
|
-
try {
|
|
131
|
-
const account = await db.getWhatsAppAccount()
|
|
132
|
-
const resp = await whatsapp.sendTemplate(account, to, templateName, language || 'en_US', components || [])
|
|
133
|
-
const waMessageId = resp && resp.messages && resp.messages[0] && resp.messages[0].id
|
|
134
|
-
const message = { id: waMessageId || uuidv4(), text: `[template:${templateName}]`, sender: 'us', ts: Date.now(), wa_id: to, direction: 'outbound', type: 'template' }
|
|
135
|
-
await db.addMessage(message)
|
|
136
|
-
io.emit('message', message)
|
|
137
|
-
res.status(200).json(message)
|
|
138
|
-
} catch (err) {
|
|
139
|
-
console.error('sendTemplate failed', err?.response?.data || err.message || err)
|
|
140
|
-
res.status(500).json({ error: 'failed to send template' })
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
// Webhook verification endpoint (GET)
|
|
145
|
-
app.get('/webhook', (req, res) => {
|
|
146
|
-
const mode = req.query['hub.mode']
|
|
147
|
-
const token = req.query['hub.verify_token']
|
|
148
|
-
const challenge = req.query['hub.challenge']
|
|
149
|
-
if (mode && token) {
|
|
150
|
-
if (mode === 'subscribe' && token === process.env.WHATSAPP_VERIFY_TOKEN) {
|
|
151
|
-
console.log('WEBHOOK_VERIFIED')
|
|
152
|
-
res.status(200).send(challenge)
|
|
153
|
-
} else {
|
|
154
|
-
res.sendStatus(403)
|
|
155
|
-
}
|
|
156
|
-
} else {
|
|
157
|
-
res.sendStatus(400)
|
|
158
|
-
}
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
// Webhook receiver (POST)
|
|
162
|
-
// Use raw body parsing for webhook to allow signature verification
|
|
163
|
-
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
164
|
-
const raw = req.body
|
|
165
|
-
const sig = req.headers['x-hub-signature-256']
|
|
166
|
-
const appSecret = process.env.WHATSAPP_APP_SECRET
|
|
167
|
-
if (appSecret && sig) {
|
|
168
|
-
const hmac = crypto.createHmac('sha256', appSecret).update(raw).digest('hex')
|
|
169
|
-
const expected = `sha256=${hmac}`
|
|
170
|
-
if (sig !== expected) {
|
|
171
|
-
console.warn('Invalid webhook signature')
|
|
172
|
-
return res.sendStatus(403)
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
let body
|
|
176
|
-
try {
|
|
177
|
-
body = JSON.parse(raw.toString())
|
|
178
|
-
} catch (err) {
|
|
179
|
-
console.error('invalid json', err)
|
|
180
|
-
return res.sendStatus(400)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (body.object === 'whatsapp_business_account') {
|
|
184
|
-
const account = await db.getWhatsAppAccount()
|
|
185
|
-
try {
|
|
186
|
-
for (const entry of body.entry || []) {
|
|
187
|
-
for (const change of entry.changes || []) {
|
|
188
|
-
const value = change.value || {}
|
|
189
|
-
// Incoming messages
|
|
190
|
-
if (value.messages) {
|
|
191
|
-
for (const msg of value.messages) {
|
|
192
|
-
const wa_id = msg.from || (value.contacts && value.contacts[0] && value.contacts[0].wa_id)
|
|
193
|
-
const senderName = (value.contacts && value.contacts[0] && value.contacts[0].profile && value.contacts[0].profile.name) || wa_id
|
|
194
|
-
let text = ''
|
|
195
|
-
let media_url = null
|
|
196
|
-
if (msg.type === 'text') text = msg.text && msg.text.body
|
|
197
|
-
else if (['image', 'audio', 'video', 'document'].includes(msg.type)) {
|
|
198
|
-
const media = msg[msg.type]
|
|
199
|
-
// media.id is present — get downloadable url
|
|
200
|
-
if (media && media.id) {
|
|
201
|
-
try {
|
|
202
|
-
const url = await whatsapp.getMediaUrl(account, media.id)
|
|
203
|
-
media_url = url
|
|
204
|
-
} catch (e) {
|
|
205
|
-
console.warn('failed to fetch media url', e?.message || e)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
} else if (msg.type === 'interactive') {
|
|
209
|
-
// buttons or list replies
|
|
210
|
-
if (msg.interactive && msg.interactive.type === 'button_reply') {
|
|
211
|
-
text = msg.interactive.button_reply && msg.interactive.button_reply.title
|
|
212
|
-
} else if (msg.interactive && msg.interactive.type === 'list_reply') {
|
|
213
|
-
text = msg.interactive.list_reply && msg.interactive.list_reply.title
|
|
214
|
-
}
|
|
215
|
-
} else if (msg.type === 'contacts') {
|
|
216
|
-
text = JSON.stringify(msg.contacts)
|
|
217
|
-
} else if (msg.type === 'location') {
|
|
218
|
-
text = JSON.stringify(msg.location)
|
|
219
|
-
}
|
|
220
|
-
const message = { id: msg.id || uuidv4(), text, sender: senderName, ts: Date.now(), wa_id, direction: 'inbound', type: msg.type, media_url }
|
|
221
|
-
await db.addMessage(message)
|
|
222
|
-
io.emit('message', message)
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// Status updates
|
|
226
|
-
if (value.statuses) {
|
|
227
|
-
for (const st of value.statuses) {
|
|
228
|
-
const msgId = st.id
|
|
229
|
-
const status = st.status
|
|
230
|
-
if (msgId) await db.updateMessageStatus(msgId, status)
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
res.sendStatus(200)
|
|
236
|
-
} catch (err) {
|
|
237
|
-
console.error('webhook processing error', err)
|
|
238
|
-
res.sendStatus(500)
|
|
239
|
-
}
|
|
240
|
-
} else {
|
|
241
|
-
res.sendStatus(404)
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
const port = process.env.PORT || 4000
|
|
246
|
-
const server = http.createServer(app)
|
|
247
|
-
const io = new Server(server, { cors: { origin: '*' } })
|
|
248
|
-
|
|
249
|
-
io.on('connection', (socket) => {
|
|
250
|
-
console.log('socket connected', socket.id)
|
|
251
|
-
socket.on('disconnect', () => console.log('socket disconnected', socket.id))
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
;(async () => {
|
|
255
|
-
try {
|
|
256
|
-
await db.init()
|
|
257
|
-
server.listen(port, () => console.log(`Backend listening on ${port}`))
|
|
258
|
-
} catch (err) {
|
|
259
|
-
console.error('Failed to initialize database', err)
|
|
260
|
-
process.exit(1)
|
|
261
|
-
}
|
|
262
|
-
})()
|
package/backend/src/whatsapp.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const axios = require('axios')
|
|
2
|
-
|
|
3
|
-
const API_DEFAULT = 'v15.0'
|
|
4
|
-
|
|
5
|
-
function makeUrl(apiVersion, phoneId) {
|
|
6
|
-
return `https://graph.facebook.com/${apiVersion || API_DEFAULT}/${phoneId}/messages`
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
async function sendText(account, to, text) {
|
|
10
|
-
const phoneId = account && account.phone_id
|
|
11
|
-
const token = account && account.token
|
|
12
|
-
const apiVersion = (account && account.api_version) || API_DEFAULT
|
|
13
|
-
if (!phoneId || !token) throw new Error('WhatsApp account not configured')
|
|
14
|
-
const url = makeUrl(apiVersion, phoneId)
|
|
15
|
-
const body = { messaging_product: 'whatsapp', to, type: 'text', text: { body: text } }
|
|
16
|
-
const res = await axios.post(url, body, { headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' } })
|
|
17
|
-
return res.data
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function sendMedia(account, to, mediaUrl, mediaType = 'image') {
|
|
21
|
-
const phoneId = account && account.phone_id
|
|
22
|
-
const token = account && account.token
|
|
23
|
-
const apiVersion = (account && account.api_version) || API_DEFAULT
|
|
24
|
-
if (!phoneId || !token) throw new Error('WhatsApp account not configured')
|
|
25
|
-
const url = makeUrl(apiVersion, phoneId)
|
|
26
|
-
const body = { messaging_product: 'whatsapp', to, type: mediaType, [mediaType]: { link: mediaUrl } }
|
|
27
|
-
const res = await axios.post(url, body, { headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' } })
|
|
28
|
-
return res.data
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function sendTemplate(account, to, templateName, language = 'en_US', components = []) {
|
|
32
|
-
const phoneId = account && account.phone_id
|
|
33
|
-
const token = account && account.token
|
|
34
|
-
const apiVersion = (account && account.api_version) || API_DEFAULT
|
|
35
|
-
if (!phoneId || !token) throw new Error('WhatsApp account not configured')
|
|
36
|
-
const url = makeUrl(apiVersion, phoneId)
|
|
37
|
-
const body = { messaging_product: 'whatsapp', to, type: 'template', template: { name: templateName, language: { code: language }, components } }
|
|
38
|
-
const res = await axios.post(url, body, { headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' } })
|
|
39
|
-
return res.data
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function getMediaUrl(account, mediaId) {
|
|
43
|
-
const token = account && account.token
|
|
44
|
-
const apiVersion = (account && account.api_version) || API_DEFAULT
|
|
45
|
-
if (!token) throw new Error('WhatsApp token not configured')
|
|
46
|
-
const url = `https://graph.facebook.com/${apiVersion}/${mediaId}`
|
|
47
|
-
const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` } })
|
|
48
|
-
return res.data && res.data.url
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = { sendText, sendMedia, sendTemplate, getMediaUrl }
|
|
52
|
-
|
|
53
|
-
async function testAccount({ phone_id, token, api_version }) {
|
|
54
|
-
const apiVersion = api_version || API_DEFAULT
|
|
55
|
-
if (!phone_id || !token) throw new Error('phone_id and token required')
|
|
56
|
-
const url = `https://graph.facebook.com/${apiVersion}/${phone_id}`
|
|
57
|
-
try {
|
|
58
|
-
const res = await axios.get(url, { params: { access_token: token } })
|
|
59
|
-
return { ok: true, body: res.data }
|
|
60
|
-
} catch (err) {
|
|
61
|
-
return { ok: false, error: err?.response?.data || err.message }
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = { sendText, sendMedia, sendTemplate, getMediaUrl, testAccount }
|
package/frontend/README.md
DELETED
package/frontend/pages/index.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState, useRef } from 'react'
|
|
2
|
-
import { io } from 'socket.io-client'
|
|
3
|
-
|
|
4
|
-
export default function Home() {
|
|
5
|
-
const [messages, setMessages] = useState([])
|
|
6
|
-
const [text, setText] = useState('')
|
|
7
|
-
const [to, setTo] = useState('')
|
|
8
|
-
const [mediaUrl, setMediaUrl] = useState('')
|
|
9
|
-
const [templateName, setTemplateName] = useState('')
|
|
10
|
-
const [templateLang, setTemplateLang] = useState('en_US')
|
|
11
|
-
const socketRef = useRef(null)
|
|
12
|
-
const [setupNeeded, setSetupNeeded] = useState(false)
|
|
13
|
-
const [adminForm, setAdminForm] = useState({ username: '', password: '', name: '', email: '' })
|
|
14
|
-
const [waForm, setWaForm] = useState({ phone_id: '', token: '', api_version: 'v15.0' })
|
|
15
|
-
const [setupStep, setSetupStep] = useState(0)
|
|
16
|
-
|
|
17
|
-
const BACKEND = (process.env.NEXT_PUBLIC_BACKEND_URL) || 'http://localhost:4000'
|
|
18
|
-
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
// check setup status first
|
|
21
|
-
fetch(BACKEND + '/api/setup/status')
|
|
22
|
-
.then((r) => r.json())
|
|
23
|
-
.then((d) => {
|
|
24
|
-
if (d && d.setup) {
|
|
25
|
-
setSetupNeeded(false)
|
|
26
|
-
// load messages and connect socket
|
|
27
|
-
fetch(BACKEND + '/api/messages')
|
|
28
|
-
.then((r) => r.json())
|
|
29
|
-
.then((msgs) => setMessages(msgs)).catch(() => setMessages([]))
|
|
30
|
-
socketRef.current = io(BACKEND)
|
|
31
|
-
socketRef.current.on('message', (msg) => setMessages((m) => [...m, msg]))
|
|
32
|
-
} else {
|
|
33
|
-
setSetupNeeded(true)
|
|
34
|
-
setSetupStep(0)
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
return () => {
|
|
39
|
-
if (socketRef.current) socketRef.current.disconnect()
|
|
40
|
-
}
|
|
41
|
-
}, [])
|
|
42
|
-
|
|
43
|
-
const send = async () => {
|
|
44
|
-
if (!text) return
|
|
45
|
-
// If `to` is provided, send via WhatsApp Cloud API through backend
|
|
46
|
-
if (to) {
|
|
47
|
-
await fetch('http://localhost:4000/api/send', {
|
|
48
|
-
method: 'POST',
|
|
49
|
-
headers: { 'Content-Type': 'application/json' },
|
|
50
|
-
body: JSON.stringify({ to, text })
|
|
51
|
-
})
|
|
52
|
-
} else {
|
|
53
|
-
await fetch('http://localhost:4000/api/messages', {
|
|
54
|
-
method: 'POST',
|
|
55
|
-
headers: { 'Content-Type': 'application/json' },
|
|
56
|
-
body: JSON.stringify({ text, sender: 'web' })
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
setText('')
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const createAdmin = async () => {
|
|
63
|
-
const resp = await fetch('/api/setup/create-admin', {
|
|
64
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(adminForm)
|
|
65
|
-
})
|
|
66
|
-
if (resp.ok) {
|
|
67
|
-
setSetupStep(1)
|
|
68
|
-
} else {
|
|
69
|
-
alert('Failed to create admin')
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const saveWa = async () => {
|
|
74
|
-
const resp = await fetch('/api/setup/whatsapp', {
|
|
75
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(waForm)
|
|
76
|
-
})
|
|
77
|
-
if (resp.ok) {
|
|
78
|
-
// reload page to initialize socket/messages
|
|
79
|
-
window.location.reload()
|
|
80
|
-
} else {
|
|
81
|
-
alert('Failed to save WhatsApp account')
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const sendMedia = async () => {
|
|
86
|
-
if (!to || !mediaUrl) return
|
|
87
|
-
await fetch('http://localhost:4000/api/sendMedia', {
|
|
88
|
-
method: 'POST',
|
|
89
|
-
headers: { 'Content-Type': 'application/json' },
|
|
90
|
-
body: JSON.stringify({ to, mediaUrl, mediaType: 'image' })
|
|
91
|
-
})
|
|
92
|
-
setMediaUrl('')
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const sendTemplate = async () => {
|
|
96
|
-
if (!to || !templateName) return
|
|
97
|
-
await fetch('http://localhost:4000/api/sendTemplate', {
|
|
98
|
-
method: 'POST',
|
|
99
|
-
headers: { 'Content-Type': 'application/json' },
|
|
100
|
-
body: JSON.stringify({ to, templateName, language: templateLang, components: [] })
|
|
101
|
-
})
|
|
102
|
-
setTemplateName('')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<main style={{ fontFamily: 'system-ui, sans-serif', padding: 24 }}>
|
|
107
|
-
<h1>WhatsApp Team Inbox (Frontend)</h1>
|
|
108
|
-
<div style={{ maxWidth: 720 }}>
|
|
109
|
-
{setupNeeded ? (
|
|
110
|
-
<div>
|
|
111
|
-
{setupStep === 0 ? (
|
|
112
|
-
<div>
|
|
113
|
-
<h2>Initial setup — create admin</h2>
|
|
114
|
-
<input placeholder="username" value={adminForm.username} onChange={(e)=>setAdminForm({...adminForm, username: e.target.value})} style={{padding:8, display:'block', marginBottom:8}} />
|
|
115
|
-
<input placeholder="password" type="password" value={adminForm.password} onChange={(e)=>setAdminForm({...adminForm, password: e.target.value})} style={{padding:8, display:'block', marginBottom:8}} />
|
|
116
|
-
<input placeholder="name" value={adminForm.name} onChange={(e)=>setAdminForm({...adminForm, name: e.target.value})} style={{padding:8, display:'block', marginBottom:8}} />
|
|
117
|
-
<input placeholder="email" value={adminForm.email} onChange={(e)=>setAdminForm({...adminForm, email: e.target.value})} style={{padding:8, display:'block', marginBottom:8}} />
|
|
118
|
-
<button onClick={createAdmin} style={{padding:'8px 12px'}}>Create Admin</button>
|
|
119
|
-
</div>
|
|
120
|
-
) : (
|
|
121
|
-
<div>
|
|
122
|
-
<h2>Configure WhatsApp Cloud API</h2>
|
|
123
|
-
<input placeholder="phone_id" value={waForm.phone_id} onChange={(e)=>setWaForm({...waForm, phone_id: e.target.value})} style={{padding:8, display:'block', marginBottom:8}} />
|
|
124
|
-
<input placeholder="token" value={waForm.token} onChange={(e)=>setWaForm({...waForm, token: e.target.value})} style={{padding:8, display:'block', marginBottom:8}} />
|
|
125
|
-
<input placeholder="api_version" value={waForm.api_version} onChange={(e)=>setWaForm({...waForm, api_version: e.target.value})} style={{padding:8, display:'block', marginBottom:8}} />
|
|
126
|
-
<div style={{display:'flex', gap:8}}>
|
|
127
|
-
<button onClick={async()=>{
|
|
128
|
-
// test credentials
|
|
129
|
-
try {
|
|
130
|
-
const r = await fetch(BACKEND + '/api/setup/test-whatsapp', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(waForm) })
|
|
131
|
-
const j = await r.json()
|
|
132
|
-
if (r.ok) alert('Credentials valid')
|
|
133
|
-
else alert('Invalid credentials: ' + (j.error?.message || JSON.stringify(j.error)))
|
|
134
|
-
} catch (e) { alert('Test failed: '+e.message) }
|
|
135
|
-
}} style={{padding:'8px 12px'}}>Test</button>
|
|
136
|
-
<button onClick={saveWa} style={{padding:'8px 12px'}}>Save WhatsApp Account</button>
|
|
137
|
-
</div>
|
|
138
|
-
</div>
|
|
139
|
-
)}
|
|
140
|
-
</div>
|
|
141
|
-
) : (
|
|
142
|
-
|
|
143
|
-
<div>
|
|
144
|
-
<div style={{ border: '1px solid #ddd', padding: 12, minHeight: 200 }}>
|
|
145
|
-
{messages.map((m) => (
|
|
146
|
-
<div key={m.id} style={{ padding: 8, borderBottom: '1px solid #eee' }}>
|
|
147
|
-
<div style={{ fontSize: 12, color: '#666' }}>{m.sender} • {new Date(m.ts).toLocaleTimeString()}</div>
|
|
148
|
-
<div>{m.text}</div>
|
|
149
|
-
</div>
|
|
150
|
-
))}
|
|
151
|
-
</div>
|
|
152
|
-
|
|
153
|
-
<div style={{ display: 'flex', marginTop: 12, gap: 8 }}>
|
|
154
|
-
<input placeholder="To (WhatsApp number, optional)" value={to} onChange={(e) => setTo(e.target.value)} style={{ width: 220, padding: 8 }} />
|
|
155
|
-
<input value={text} onChange={(e) => setText(e.target.value)} style={{ flex: 1, padding: 8 }} />
|
|
156
|
-
<button onClick={send} style={{ padding: '8px 12px' }}>Send</button>
|
|
157
|
-
</div>
|
|
158
|
-
<div style={{ display: 'flex', marginTop: 8, gap: 8 }}>
|
|
159
|
-
<input placeholder="Media URL (optional)" value={mediaUrl} onChange={(e) => setMediaUrl(e.target.value)} style={{ flex: 1, padding: 8 }} />
|
|
160
|
-
<button onClick={sendMedia} style={{ padding: '8px 12px' }}>Send Media</button>
|
|
161
|
-
</div>
|
|
162
|
-
<div style={{ display: 'flex', marginTop: 8, gap: 8 }}>
|
|
163
|
-
<input placeholder="Template name" value={templateName} onChange={(e) => setTemplateName(e.target.value)} style={{ width: 240, padding: 8 }} />
|
|
164
|
-
<input placeholder="lang" value={templateLang} onChange={(e) => setTemplateLang(e.target.value)} style={{ width: 100, padding: 8 }} />
|
|
165
|
-
<button onClick={sendTemplate} style={{ padding: '8px 12px' }}>Send Template</button>
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
)}
|
|
169
|
-
</div>
|
|
170
|
-
</main>
|
|
171
|
-
)
|
|
172
|
-
}
|