Prepare server deployment
This commit is contained in:
32
README.md
32
README.md
@ -25,3 +25,35 @@ cp .env.example .env
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Deploy test server
|
||||
Target deploy:
|
||||
- domain `wa.iptek.co`
|
||||
- app port `127.0.0.1:3007`
|
||||
- reverse proxy `nginx`
|
||||
|
||||
Contoh langkah di server:
|
||||
```bash
|
||||
cd /var/www
|
||||
git clone https://git.iptek.co/wirabasalamah/webhook-test.git wa-test-nextjs
|
||||
cd wa-test-nextjs
|
||||
cp .env.example .env
|
||||
npm ci
|
||||
npm run build
|
||||
npx pm2 start ecosystem.config.cjs
|
||||
npx pm2 save
|
||||
```
|
||||
|
||||
Set `.env` minimal:
|
||||
```bash
|
||||
NEXT_PUBLIC_APP_URL=https://wa.iptek.co
|
||||
WHATSAPP_VERIFY_TOKEN=...
|
||||
WHATSAPP_ACCESS_TOKEN=...
|
||||
WHATSAPP_PHONE_NUMBER_ID=...
|
||||
WA_TEST_NUMBER=...
|
||||
WA_TEST_LOGIN_USERNAME=...
|
||||
WA_TEST_LOGIN_PASSWORD=...
|
||||
SESSION_SECRET=...
|
||||
```
|
||||
|
||||
Contoh config nginx ada di `deploy/nginx/wa.iptek.co.conf`.
|
||||
|
||||
19
deploy/nginx/wa.iptek.co.conf
Normal file
19
deploy/nginx/wa.iptek.co.conf
Normal file
@ -0,0 +1,19 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name wa.iptek.co;
|
||||
|
||||
client_max_body_size 10m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3007;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
}
|
||||
13
ecosystem.config.cjs
Normal file
13
ecosystem.config.cjs
Normal file
@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'wa-test-nextjs',
|
||||
script: 'npm',
|
||||
args: 'run start:3007',
|
||||
cwd: '/var/www/wa-test-nextjs',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
4
next-env.d.ts
vendored
4
next-env.d.ts
vendored
@ -1,4 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// This file is auto-generated by Next.js
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
11
package.json
11
package.json
@ -6,12 +6,13 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
"start:3007": "next start -p 3007 -H 127.0.0.1",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "16.0.0",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0"
|
||||
"next": "16.2.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
@ -19,6 +20,6 @@
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.0"
|
||||
"eslint-config-next": "16.2.6"
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ export default async function TestPage({ searchParams }: { searchParams: Promise
|
||||
redirect('/login')
|
||||
}
|
||||
|
||||
const logs = readJsonFile<any[]>('webhook-logs.json', [])
|
||||
const logs = readJsonFile<unknown[]>('webhook-logs.json', [])
|
||||
const state = readJsonFile<Record<string, unknown>>('flow-state.json', {})
|
||||
const params = await searchParams
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ export default async function WebhookLogsPage() {
|
||||
redirect('/login')
|
||||
}
|
||||
|
||||
const logs = readJsonFile<any[]>('webhook-logs.json', [])
|
||||
const logs = readJsonFile<unknown[]>('webhook-logs.json', [])
|
||||
|
||||
return (
|
||||
<div className="wrap">
|
||||
|
||||
@ -1,6 +1,25 @@
|
||||
import { appendLog, readJsonFile, writeJsonFile } from './storage'
|
||||
|
||||
type FlowState = Record<string, { step: string; name?: string }>
|
||||
type WebhookValue = {
|
||||
messages?: IncomingMessage[]
|
||||
statuses?: unknown[]
|
||||
}
|
||||
type WebhookChange = {
|
||||
value?: WebhookValue
|
||||
}
|
||||
type WebhookEntry = {
|
||||
changes?: WebhookChange[]
|
||||
}
|
||||
type WebhookPayload = {
|
||||
entry?: WebhookEntry[]
|
||||
}
|
||||
type IncomingMessage = {
|
||||
from?: string
|
||||
text?: {
|
||||
body?: string
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeNumber(number?: string | null) {
|
||||
return (number || '').replace(/\D+/g, '')
|
||||
@ -37,7 +56,7 @@ export async function sendTextMessage(to: string, message: string) {
|
||||
return payload
|
||||
}
|
||||
|
||||
export async function handleWebhook(payload: any) {
|
||||
export async function handleWebhook(payload: WebhookPayload) {
|
||||
appendLog({ type: 'webhook_received', payload, created_at: new Date().toISOString() })
|
||||
|
||||
for (const entry of payload.entry || []) {
|
||||
@ -55,7 +74,7 @@ export async function handleWebhook(payload: any) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleIncomingMessage(message: any) {
|
||||
async function handleIncomingMessage(message: IncomingMessage) {
|
||||
const from = message.from || ''
|
||||
appendLog({ type: 'incoming_message', from, message, created_at: new Date().toISOString() })
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@ -11,13 +15,27 @@
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [{ "name": "next" }],
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user