-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
276 additions
and
60 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
app/api/webhook/twilio/authenticateAndRecordTwilioRequest.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Copyright 2024 Peter Beverloo & AnimeCon. All rights reserved. | ||
// Use of this source code is governed by a MIT license that can be found in the LICENSE file. | ||
|
||
import type { NextRequest } from 'next/server'; | ||
import { notFound } from 'next/navigation'; | ||
|
||
import type { TwilioWebhookEndpoint } from '@lib/database/Types'; | ||
import { readSetting } from '@lib/Settings'; | ||
import db, { tTwilioWebhookCalls} from '@lib/database'; | ||
|
||
/** | ||
* Authenticates that the given `request` indeed was issued by Twilio, based on the signature that | ||
* they (hopefully) included as an HTTP header. When the authentication was successful, the request | ||
* will be logged, and the request's body will be returned as a string. | ||
*/ | ||
export async function authenticateAndRecordTwilioRequest( | ||
request: NextRequest, endpoint: TwilioWebhookEndpoint) | ||
{ | ||
const authToken = await readSetting('integration-twilio-account-auth-token'); | ||
if (!authToken) | ||
notFound(); // Twilio is not enabled for this instance | ||
|
||
const requestBody = await request.text(); | ||
|
||
const dbInstance = db; | ||
await dbInstance.insertInto(tTwilioWebhookCalls) | ||
.set({ | ||
webhookCallDate: dbInstance.currentZonedDateTime(), | ||
webhookCallEndpoint: endpoint, | ||
webhookRequestSource: request.ip ?? request.headers.get('x-forwarded-for'), | ||
webhookRequestMethod: request.method, | ||
webhookRequestUrl: request.url, | ||
webhookRequestHeaders: JSON.stringify([ ...request.headers.entries() ]), | ||
webhookRequestBody: requestBody, | ||
}) | ||
.executeInsert(); | ||
|
||
// TODO: Actually authenticate and validate the `requestBody`. | ||
return requestBody; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright 2024 Peter Beverloo & AnimeCon. All rights reserved. | ||
// Use of this source code is governed by a MIT license that can be found in the LICENSE file. | ||
|
||
import type { NextRequest } from 'next/server'; | ||
|
||
import twilio from 'twilio'; | ||
|
||
import { TwilioWebhookEndpoint } from '@lib/database/Types'; | ||
import { authenticateAndRecordTwilioRequest } from '../authenticateAndRecordTwilioRequest'; | ||
|
||
/** | ||
* Webhook invoked when an inbound message has been received over one of our communication channels, | ||
* generally either SMS or WhatsApp. An immediate response is expected. | ||
*/ | ||
export async function GET(request: NextRequest) { | ||
const body = await authenticateAndRecordTwilioRequest(request, TwilioWebhookEndpoint.Inbound); | ||
// TODO: Do something with the `body` | ||
|
||
const response = new twilio.twiml.MessagingResponse(); | ||
return new Response(response.toString(), { | ||
headers: [ | ||
[ 'Content-Type', 'text/xml' ], | ||
], | ||
status: /* HTTP OK= */ 200, | ||
}); | ||
} | ||
|
||
export const dynamic = 'force-dynamic'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Copyright 2024 Peter Beverloo & AnimeCon. All rights reserved. | ||
// Use of this source code is governed by a MIT license that can be found in the LICENSE file. | ||
|
||
import type { NextRequest } from 'next/server'; | ||
|
||
import { TwilioWebhookEndpoint } from '@lib/database/Types'; | ||
import { authenticateAndRecordTwilioRequest } from '../authenticateAndRecordTwilioRequest'; | ||
|
||
/** | ||
* Webhook invoked when the status of an outbound message has been updated. This may happen minutes, | ||
* sometimes even hours after sending the message, so we need to keep our stored state in sync. | ||
*/ | ||
export async function POST(request: NextRequest) { | ||
const body = await authenticateAndRecordTwilioRequest(request, TwilioWebhookEndpoint.Outbound); | ||
// TODO: Do something with the `body` | ||
|
||
return new Response(undefined, { | ||
status: /* HTTP OK= */ 200, | ||
}); | ||
} | ||
|
||
export const dynamic = 'force-dynamic'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// @ts-nocheck | ||
/* eslint-disable quotes, max-len */ | ||
/** | ||
* DO NOT EDIT: | ||
* | ||
* This file has been auto-generated from database schema using ts-sql-codegen. | ||
* Any changes will be overwritten. | ||
*/ | ||
import { Table } from "ts-sql-query/Table"; | ||
import type { DBConnection } from "../Connection"; | ||
import { | ||
TemporalTypeAdapter, | ||
} from "../TemporalTypeAdapter"; | ||
import { | ||
ZonedDateTime, | ||
} from "../../Temporal"; | ||
import { | ||
TwilioWebhookEndpoint, | ||
} from "../Types"; | ||
|
||
export class TwilioWebhookCallsTable extends Table<DBConnection, 'TwilioWebhookCallsTable'> { | ||
webhookCallId = this.autogeneratedPrimaryKey('webhook_call_id', 'int'); | ||
webhookCallDate = this.column<ZonedDateTime>('webhook_call_date', 'customLocalDateTime', 'dateTime', TemporalTypeAdapter); | ||
webhookCallEndpoint = this.column<TwilioWebhookEndpoint>('webhook_call_endpoint', 'enum', 'TwilioWebhookEndpoint'); | ||
webhookRequestSource = this.optionalColumnWithDefaultValue('webhook_request_source', 'string'); | ||
webhookRequestMethod = this.column('webhook_request_method', 'string'); | ||
webhookRequestUrl = this.column('webhook_request_url', 'string'); | ||
webhookRequestHeaders = this.column('webhook_request_headers', 'string'); | ||
webhookRequestBody = this.column('webhook_request_body', 'string'); | ||
|
||
constructor() { | ||
super('twilio_webhook_calls'); | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters