Skip to content

Commit

Permalink
added exact account worker
Browse files Browse the repository at this point in the history
  • Loading branch information
RafidMuhymin committed Jan 15, 2024
1 parent f26e936 commit 4a3c724
Show file tree
Hide file tree
Showing 15 changed files with 528 additions and 0 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
},
"homepage": "https://github.com/dilmaheu/dilmahtea.me-workers#readme",
"dependencies": {
"@lucia-auth/adapter-sqlite": "^2.0.1",
"fast-xml-parser": "^4.2.7",
"google-libphonenumber": "^3.2.34",
"lucia": "^2.7.6",
"prettier": "^3.0.3",
"stripe": "^12.18.0",
"wrangler": "^3.7.0",
Expand Down
39 changes: 39 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions src/utils/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { lucia } from "lucia";
import { web } from "lucia/middleware";
import { d1 } from "@lucia-auth/adapter-sqlite";

export const initializeLucia = (db: D1Database) => {
const auth = lucia({
env: "PROD",
middleware: web(),
sessionCookie: {
expires: false,
},
adapter: d1(db, {
user: "user",
key: "user_key",
session: "user_session",
}),
getUserAttributes: (databaseUser) => databaseUser,
});

return auth;
};

export type Auth = ReturnType<typeof initializeLucia>;
12 changes: 12 additions & 0 deletions src/utils/formatNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import phoneUtil from "google-libphonenumber";

const phoneUtilInstance = phoneUtil.PhoneNumberUtil.getInstance(),
{ E164 } = phoneUtil.PhoneNumberFormat;

export default function formatNumber(number: string, country: string): string {
number = phoneUtilInstance.parse(number, country);

number = phoneUtilInstance.format(number, E164);

return number;
}
6 changes: 6 additions & 0 deletions src/utils/getCustomerFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const getCustomerFilter = (contact: string, isEmail: boolean): string =>
isEmail
? `Email eq '${contact}'`
: `substringof('${contact.slice(1)}', Phone)`;

export default getCustomerFilter;
9 changes: 9 additions & 0 deletions src/workers/dilmahtea-me-exact-account/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"private": true,
"name": "dilmahtea-me-exact-signup",
"version": "1.0.0",
"description": "A template for kick starting a Cloudflare Workers project",
"type": "module",
"main": "src/index.ts",
"license": "MIT"
}
5 changes: 5 additions & 0 deletions src/workers/dilmahtea-me-exact-account/src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { ENV } from "./types";

import env from "../../../utils/env";

export default env as unknown as ENV;
69 changes: 69 additions & 0 deletions src/workers/dilmahtea-me-exact-account/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ENV } from "./types";

import fetchExactAPI from "../../../utils/fetchExactAPI";
import getCustomerFilter from "../../../utils/getCustomerFilter";
import createModuleWorker, { reply } from "../../../utils/createModuleWorker";
import updateCustomer from "./utils/updateCustomer";
import createCustomer from "./utils/createCustomer";

declare interface Body {
userId?: string;
Email?: string;
Phone?: string;
FirstName: string;
LastName: string;
Language: string;
Address?: {
[key: string]: string;
AddressLine1: string;
City: string;
Country: string;
PostCode: string;
};
}

async function handlePOST(request: Request, env: ENV) {
const { userId, Email, Phone, FirstName, LastName, Language, Address } =
await request.json<Body>();

const Name = `${FirstName} ${LastName}`,
CustomerData = {
userId,
Name,
Email,
Phone,
FirstName,
LastName,
Language,
Address,
};

const CustomerFilter = getCustomerFilter(Email || Phone, !!Email);

let Customer = await fetchExactAPI(
"GET",
`/CRM/Accounts?$filter=${getCustomerFilter(
Email || Phone,
!!Email,
)}&$select=ID,Name,Language,Email,Phone,Country,LeadSource,Classification1`,
).feed.entry;

if (Customer) {
console.log("Exact: Customer exists");

await updateCustomer(CustomerData, Customer, CustomerFilter, userId);
} else {
Customer = await createCustomer(CustomerData);

console.log("Exact: Customer created successfully");
}

return reply({ success: true, Customer }, 200);
}

handlePOST.retry = true;

export default createModuleWorker({
pathname: "*",
methods: { POST: handlePOST },
});
14 changes: 14 additions & 0 deletions src/workers/dilmahtea-me-exact-account/src/types/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface ENV {
// D1
USERS: D1Database;

// KV
EXACT_TOKENS: KVNamespace;
EXACT_GUID_COLLECTION: KVNamespace;

// EXACT
EXACT_API_ENDPOINT: string;

// DKIM MAILCHANNELS
DKIM_PRIVATE_KEY: string;
}
1 change: 1 addition & 0 deletions src/workers/dilmahtea-me-exact-account/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { ENV } from "./env";
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const AddressTypes = {
Visit: 1,
Invoice: 3,
Delivery: 4,
};

export default AddressTypes;
73 changes: 73 additions & 0 deletions src/workers/dilmahtea-me-exact-account/src/utils/createCustomer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import env from "../env";
import AddressTypes from "./AddressTypes";
import fetchExactAPI from "../../../../utils/fetchExactAPI";

const NonVisitAddressTypes = structuredClone(AddressTypes);

// @ts-ignore
delete NonVisitAddressTypes.Visit;

export default async function createCustomer({
Name,
Email,
Phone,
FirstName,
LastName,
Language,
Address = {},
}) {
const contact = Email || Phone,
contactType = Email ? "email" : "phone";

const Customer = await fetchExactAPI("POST", "/CRM/Accounts", {
[contactType]: contact,
Name,
Language,
Status: "C",
...Address,
LeadSource: await env.EXACT_GUID_COLLECTION.get("WEBSHOP_LEAD_SOURCE"),
Classification1: await env.EXACT_GUID_COLLECTION.get(
"B2C_CUSTOMER_SEGMENT",
),
}).entry.content["m:properties"];

const CustomerID = Customer["d:ID"];

const Contact = await fetchExactAPI("POST", "/CRM/Contacts", {
[contactType]: contact,
Account: CustomerID,
FirstName,
LastName,
});

const ContactID = Contact.entry.content["m:properties"]["d:ID"];

if (Object.keys(Address).length) {
// link Visit Address created during Customer creation to the created Contact
await fetchExactAPI(
"GET",
`/CRM/Addresses?$filter=Account eq guid'${CustomerID}'&$select=ID`,
)
.then(({ feed }) => feed.entry.content["m:properties"]["d:ID"])
.then((addressID) =>
fetchExactAPI("PUT", `/CRM/Addresses(guid'${addressID}')`, {
Contact: ContactID,
}),
);

// create new Invoice & Delivery Addresses
await Promise.all(
Object.values(NonVisitAddressTypes).map((Type) =>
fetchExactAPI("POST", "/CRM/Addresses", {
Type,
Main: true,
Contact: ContactID,
Account: CustomerID,
...Address,
}),
),
);
}

return Customer;
}
Loading

0 comments on commit 4a3c724

Please sign in to comment.