Refactoring

This commit is contained in:
Darren Clarke 2024-04-29 17:27:25 +02:00
parent 39cfada3e8
commit dd14dfe72e
41 changed files with 866 additions and 742 deletions

View file

@ -4,6 +4,7 @@ import {
createAction,
updateAction,
deleteAction,
selectAllAction,
} from "@/app/_actions/service";
type GenerateCreateActionArgs = {
@ -18,6 +19,8 @@ export function generateCreateAction({
fields,
}: GenerateCreateActionArgs) {
return async (currentState: any, formData: FormData) => {
console.log({ entity, table, fields });
console.log({ currentState, formData });
return createAction({
entity,
table,
@ -63,3 +66,9 @@ export function generateDeleteAction({
return deleteAction({ entity, table, id });
};
}
export function generateSelectAllAction(table: keyof Database) {
return async () => {
return selectAllAction(table);
};
}

View file

@ -1,16 +0,0 @@
import type { ServiceConfig } from "./service";
import { facebookConfig as facebook } from "./facebook";
import { signalConfig as signal } from "./signal";
import { whatsappConfig as whatsapp } from "./whatsapp";
import { voiceConfig as voice } from "./voice";
import { webhooksConfig as webhooks } from "./webhooks";
import { usersConfig as users } from "./users";
export const serviceConfig: Record<string, ServiceConfig> = {
facebook,
signal,
whatsapp,
voice,
webhooks,
users,
};

View file

@ -115,6 +115,11 @@ export interface Database {
id: GeneratedAlways<string>;
name: string;
description: string;
backendType: string;
backendId: string;
endpointUrl: string;
httpMethod: "post" | "put";
headers: Record<string, any>;
createdBy: string;
createdAt: Date;
updatedAt: Date;

View file

@ -1,138 +1,18 @@
import { NextRequest, NextResponse } from "next/server";
import { Service, ServiceConfig } from "./service";
import { makeWorkerUtils, WorkerUtils } from "graphile-worker";
import { Service } from "./service";
import { db } from "./database";
export const facebookConfig: ServiceConfig = {
entity: "facebook",
table: "FacebookBot",
displayName: "Facebook Connection",
createFields: [
{
name: "name",
label: "Name",
required: true,
size: 12,
},
{
name: "description",
label: "Description",
size: 12,
lines: 3,
},
{ name: "appId", label: "App ID", required: true },
{ name: "appSecret", label: "App Secret", required: true },
{ name: "pageId", label: "Page ID", required: true },
{
name: "pageAccessToken",
label: "Page Access Token",
required: true,
},
{
name: "token",
label: "Token",
hidden: true,
required: true,
autogenerated: "token",
},
{
name: "verifyToken",
label: "Verify Token",
hidden: true,
required: true,
autogenerated: "token",
},
],
updateFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
size: 12,
lines: 3,
},
{
name: "token",
label: "Token",
disabled: true,
refreshable: true,
},
{
name: "verifyToken",
label: "Verify Token",
disabled: true,
refreshable: true,
},
{ name: "appId", label: "App ID", required: true },
{ name: "appSecret", label: "App Secret", required: true },
{ name: "pageId", label: "Page ID", required: true },
{
name: "pageAccessToken",
label: "Page Access Token",
required: true,
},
],
displayFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
required: true,
size: 12,
},
{
name: "token",
label: "Token",
copyable: true,
},
{
name: "verifyToken",
label: "Verify Token",
copyable: true,
},
let workerUtils: WorkerUtils;
{ name: "appId", label: "App ID", required: true },
{ name: "appSecret", label: "App Secret", required: true },
{
name: "pageId",
label: "Page ID",
required: true,
copyable: true,
},
{
name: "pageAccessToken",
label: "Page Access Token",
required: true,
},
],
listColumns: [
{
field: "name",
headerName: "Name",
flex: 1,
},
{
field: "description",
headerName: "Description",
flex: 2,
},
{
field: "updatedAt",
headerName: "Updated At",
valueGetter: (value: any) => new Date(value).toLocaleString(),
flex: 1,
},
],
};
const getWorkerUtils = async () => {
if (!workerUtils) {
workerUtils = await makeWorkerUtils({
connectionString: process.env.DATABASE_URL,
});
}
const getAllBots = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const getOneBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
return workerUtils;
};
const sendMessage = async (req: NextRequest) => {
@ -147,80 +27,75 @@ const receiveMessages = async (req: NextRequest) => {
return NextResponse.json({ response: "ok" });
};
const registerBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const resetBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const requestCode = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const unverifyBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const refreshBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const createBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const deleteBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const handleWebhook = async (req: NextRequest) => {
console.log({ req });
const { searchParams } = req.nextUrl;
const token = searchParams.get("hub.verify_token");
const submittedToken = searchParams.get("hub.verify_token");
if (token !== process.env.FB_VERIFY_TOKEN) {
// return NextResponse.error("Invalid token", { status: 403 });
if (submittedToken) {
console.log({ submittedToken });
const row = await db
.selectFrom("FacebookBot")
.selectAll()
.where("verifyToken", "=", submittedToken)
.executeTakeFirst();
console.log({ row });
if (!row) {
return NextResponse.error();
}
if (searchParams.get("hub.mode") === "subscribe") {
const challenge = searchParams.get("hub.challenge");
console.log(submittedToken);
console.log(challenge);
return NextResponse.json(challenge) as any;
}
}
if (searchParams.get("hub.mode") === "subscribe") {
const challenge = searchParams.get("hub.challenge");
console.log(token);
console.log(challenge);
const message = await req.json();
console.log({ message });
const entry = message.entry[0];
console.log({ entry });
const messaging = entry?.messaging[0];
const pageId = messaging?.recipient?.id;
console.log({ pageId });
const row = await db
.selectFrom("FacebookBot")
.selectAll()
.where("pageId", "=", pageId)
.executeTakeFirst();
return new Response(challenge, { status: 200 }) as NextResponse;
}
console.log({ row });
const endpoint = `https://graph.facebook.com/v19.0/${pageId}/messages`;
const inMessage = messaging?.message?.text;
const outgoingMessage = {
recipient: { id: messaging?.sender?.id },
message: { text: `"${inMessage}", right back at you!` },
messaging_type: "RESPONSE",
access_token: row?.pageAccessToken,
};
console.log({ outgoingMessage });
const response = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(outgoingMessage),
});
console.log({ response });
console.log(message);
const wu = await getWorkerUtils();
await wu.addJob("receive_facebook_message", message);
return NextResponse.json({ response: "ok" });
};
export const Facebook: Service = {
getAllBots,
getOneBot,
sendMessage,
receiveMessages,
registerBot,
resetBot,
requestCode,
unverifyBot,
refreshBot,
createBot,
deleteBot,
handleWebhook,
};

View file

@ -1,50 +1,17 @@
import { NextRequest, NextResponse } from "next/server";
import { Service } from "./service";
import { Facebook } from "./facebook";
import { getService } from "./utils";
const services: Record<string, Service> = {
facebook: Facebook,
none: NextResponse.error() as any,
};
const getService = (req: NextRequest): Service => {
const service = req.nextUrl.searchParams.get("service") ?? "none";
return services[service];
};
export const getAllBots = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.getAllBots(req);
const notFound = () => new NextResponse(null, { status: 404 });
export const getOneBot = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.getOneBot(req);
notFound();
export const sendMessage = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.sendMessage(req);
getService(req)?.sendMessage(req) ?? notFound();
export const receiveMessages = async (
req: NextRequest,
): Promise<NextResponse> => getService(req)?.receiveMessages(req);
export const registerBot = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.registerBot(req);
export const resetBot = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.resetBot(req);
export const requestCode = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.requestCode(req);
export const unverifyBot = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.unverifyBot(req);
export const refreshBot = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.refreshBot(req);
export const createBot = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.createBot(req);
export const deleteBot = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.deleteBot(req);
): Promise<NextResponse> => getService(req)?.receiveMessages(req) ?? notFound();
export const handleWebhook = async (req: NextRequest): Promise<NextResponse> =>
getService(req)?.handleWebhook(req);
getService(req)?.handleWebhook(req) ?? notFound();

View file

@ -13,9 +13,16 @@ const entities = [
export type Entity = (typeof entities)[number];
export type SelectOption = {
value: string;
label: string;
};
export type FieldDescription = {
name: string;
label: string;
kind?: "text" | "phone" | "select" | "multi";
getOptions?: (formState: any) => Promise<SelectOption[]>;
autogenerated?: "token";
hidden?: boolean;
type?: string;
@ -39,17 +46,18 @@ export type ServiceConfig = {
listColumns: GridColDef[];
};
export type Service = {
getAllBots: (req: NextRequest) => Promise<NextResponse>;
getOneBot: (req: NextRequest) => Promise<NextResponse>;
sendMessage: (req: NextRequest) => Promise<NextResponse>;
receiveMessages: (req: NextRequest) => Promise<NextResponse>;
registerBot: (req: NextRequest) => Promise<NextResponse>;
resetBot: (req: NextRequest) => Promise<NextResponse>;
requestCode: (req: NextRequest) => Promise<NextResponse>;
unverifyBot: (req: NextRequest) => Promise<NextResponse>;
refreshBot: (req: NextRequest) => Promise<NextResponse>;
createBot: (req: NextRequest) => Promise<NextResponse>;
deleteBot: (req: NextRequest) => Promise<NextResponse>;
handleWebhook: (req: NextRequest) => Promise<NextResponse>;
};
export class Service {
sendMessage: (req: NextRequest) => Promise<NextResponse> = async (req) => {
return NextResponse.json({ ok: "nice" });
};
receiveMessages: (req: NextRequest) => Promise<NextResponse> = async (
req,
) => {
return NextResponse.json({ ok: "nice" });
};
handleWebhook: (req: NextRequest) => Promise<NextResponse> = async (req) => {
return NextResponse.json({ ok: "nice" });
};
}

View file

@ -1,102 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { Service, ServiceConfig } from "./service";
export const signalConfig: ServiceConfig = {
entity: "signal",
table: "SignalBot",
displayName: "Signal Connection",
createFields: [
{
name: "name",
label: "Name",
required: true,
size: 12,
},
{
name: "description",
label: "Description",
size: 12,
lines: 3,
},
{
name: "phoneNumber",
label: "phoneNumber",
required: true,
},
{
name: "token",
label: "Token",
hidden: true,
required: true,
autogenerated: "token",
},
],
updateFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
size: 12,
},
{
name: "phoneNumber",
label: "phoneNumber",
required: true,
},
],
displayFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
size: 12,
},
{
name: "phoneNumber",
label: "phoneNumber",
},
{
name: "token",
label: "Token",
copyable: true,
},
],
listColumns: [
{
field: "name",
headerName: "Name",
flex: 1,
},
{
field: "phoneNumber",
headerName: "Phone Number",
flex: 1,
},
{
field: "description",
headerName: "Description",
flex: 2,
},
{
field: "updatedAt",
headerName: "Updated At",
valueGetter: (value: any) => new Date(value).toLocaleString(),
flex: 1,
},
],
};
const getAllBots = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const getOneBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
import { Service } from "./service";
const sendMessage = async (req: NextRequest) => {
console.log({ req });
@ -110,48 +13,6 @@ const receiveMessages = async (req: NextRequest) => {
return NextResponse.json({ response: "ok" });
};
const registerBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const resetBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const requestCode = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const unverifyBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const refreshBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const createBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const deleteBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const handleWebhook = async (req: NextRequest) => {
console.log({ req });
@ -159,16 +20,7 @@ const handleWebhook = async (req: NextRequest) => {
};
export const Signal: Service = {
getAllBots,
getOneBot,
sendMessage,
receiveMessages,
registerBot,
resetBot,
requestCode,
unverifyBot,
refreshBot,
createBot,
deleteBot,
handleWebhook,
};

View file

@ -1,56 +0,0 @@
import { ServiceConfig } from "./service";
export const usersConfig: ServiceConfig = {
entity: "users",
table: "User",
displayName: "User",
createFields: [
{
name: "name",
label: "Name",
required: true,
size: 12,
},
{
name: "email",
label: "Email",
required: true,
size: 12,
},
],
updateFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "email",
label: "Email",
required: true,
size: 12,
},
],
displayFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "email",
label: "Email",
size: 12,
},
],
listColumns: [
{
field: "name",
headerName: "Name",
flex: 1,
},
{
field: "email",
headerName: "Email",
flex: 2,
},
{
field: "updatedAt",
headerName: "Updated At",
valueGetter: (value: any) => new Date(value).toLocaleString(),
flex: 1,
},
],
};

View file

@ -0,0 +1,13 @@
import { NextRequest } from "next/server";
import { Service } from "./service";
import { Facebook } from "./facebook";
const services: Record<string, Service> = {
facebook: Facebook,
};
export const getService = (req: NextRequest): Service => {
const service = req.nextUrl.pathname.split("/")?.[2] ?? "none";
return services[service];
};

View file

@ -1,93 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { Service, ServiceConfig } from "./service";
export const voiceConfig: ServiceConfig = {
entity: "voice",
table: "VoiceLine",
displayName: "Voice Line",
createFields: [
{
name: "name",
label: "Name",
required: true,
size: 12,
},
{
name: "description",
label: "Description",
size: 12,
lines: 3,
},
{
name: "phoneNumber",
label: "phoneNumber",
required: true,
},
],
updateFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
required: true,
size: 12,
},
{
name: "phoneNumber",
label: "Phone Number",
required: true,
},
],
displayFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
required: true,
size: 12,
},
{
name: "phoneNumber",
label: "Phone Number",
required: true,
},
],
listColumns: [
{
field: "name",
headerName: "Name",
flex: 1,
},
{
field: "phoneNumber",
headerName: "Phone Number",
flex: 1,
},
{
field: "description",
headerName: "Description",
flex: 2,
},
{
field: "updatedAt",
headerName: "Updated At",
valueGetter: (value: any) => new Date(value).toLocaleString(),
flex: 1,
},
],
};
const getAllBots = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const getOneBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
import { Service } from "./service";
const sendMessage = async (req: NextRequest) => {
console.log({ req });
@ -101,48 +13,6 @@ const receiveMessages = async (req: NextRequest) => {
return NextResponse.json({ response: "ok" });
};
const registerBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const resetBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const requestCode = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const unverifyBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const refreshBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const createBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const deleteBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const handleWebhook = async (req: NextRequest) => {
console.log({ req });
@ -150,16 +20,7 @@ const handleWebhook = async (req: NextRequest) => {
};
export const Voice: Service = {
getAllBots,
getOneBot,
sendMessage,
receiveMessages,
registerBot,
resetBot,
requestCode,
unverifyBot,
refreshBot,
createBot,
deleteBot,
handleWebhook,
};

View file

@ -1,57 +0,0 @@
import { ServiceConfig } from "./service";
export const webhooksConfig: ServiceConfig = {
entity: "webhooks",
table: "Webhook",
displayName: "Webhook",
createFields: [
{
name: "name",
label: "Name",
required: true,
size: 12,
},
{
name: "description",
label: "Description",
size: 12,
lines: 3,
},
],
updateFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
required: true,
size: 12,
},
],
displayFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
required: true,
size: 12,
},
],
listColumns: [
{
field: "name",
headerName: "Name",
flex: 1,
},
{
field: "description",
headerName: "Description",
flex: 2,
},
{
field: "updatedAt",
headerName: "Updated At",
valueGetter: (value: any) => new Date(value).toLocaleString(),
flex: 1,
},
],
};

View file

@ -1,102 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { Service, ServiceConfig } from "./service";
export const whatsappConfig: ServiceConfig = {
entity: "whatsapp",
table: "WhatsappBot",
displayName: "WhatsApp Connection",
createFields: [
{
name: "name",
label: "Name",
required: true,
size: 12,
},
{
name: "description",
label: "Description",
size: 12,
lines: 3,
},
{
name: "phoneNumber",
label: "Phone Number",
required: true,
},
{
name: "token",
label: "Token",
hidden: true,
required: true,
autogenerated: "token",
},
],
updateFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
size: 12,
},
{
name: "phoneNumber",
label: "Phone Number",
required: true,
},
],
displayFields: [
{ name: "name", label: "Name", required: true, size: 12 },
{
name: "description",
label: "Description",
size: 12,
},
{
name: "phoneNumber",
label: "Phone Number",
},
{
name: "token",
label: "Token",
copyable: true,
},
],
listColumns: [
{
field: "name",
headerName: "Name",
flex: 1,
},
{
field: "phoneNumber",
headerName: "Phone Number",
flex: 1,
},
{
field: "description",
headerName: "Description",
flex: 2,
},
{
field: "updatedAt",
headerName: "Updated At",
valueGetter: (value: any) => new Date(value).toLocaleString(),
flex: 1,
},
],
};
const getAllBots = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const getOneBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
import { Service } from "./service";
const sendMessage = async (req: NextRequest) => {
console.log({ req });
@ -110,48 +13,6 @@ const receiveMessages = async (req: NextRequest) => {
return NextResponse.json({ response: "ok" });
};
const registerBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const resetBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const requestCode = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const unverifyBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const refreshBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const createBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const deleteBot = async (req: NextRequest) => {
console.log({ req });
return NextResponse.json({ response: "ok" });
};
const handleWebhook = async (req: NextRequest) => {
console.log({ req });
@ -159,16 +20,7 @@ const handleWebhook = async (req: NextRequest) => {
};
export const Whatsapp: Service = {
getAllBots,
getOneBot,
sendMessage,
receiveMessages,
registerBot,
resetBot,
requestCode,
unverifyBot,
refreshBot,
createBot,
deleteBot,
handleWebhook,
};