Skip to content

Commit

Permalink
📈 server: setup segment
Browse files Browse the repository at this point in the history
co-authored-by: danilo neves cruz <[email protected]>
  • Loading branch information
nfmelendez and cruzdanilo committed Oct 30, 2024
1 parent 5b88698 commit 695dbf3
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 4 deletions.
2 changes: 2 additions & 0 deletions server/api/auth/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import authSecret from "../../utils/authSecret";
import decodePublicKey from "../../utils/decodePublicKey";
import deriveAddress from "../../utils/deriveAddress";
import redis from "../../utils/redis";
import { identify } from "../../utils/segment";

if (!process.env.ALCHEMY_ACTIVITY_ID) throw new Error("missing alchemy activity id");
const webhookId = process.env.ALCHEMY_ACTIVITY_ID;
Expand Down Expand Up @@ -143,6 +144,7 @@ export default app
})
.catch((error: unknown) => captureException(error)),
]);
identify({ userId: account });

return c.json(
{ credentialId: credential.id, factory: exaAccountFactoryAddress, x, y, auth: expires.getTime() },
Expand Down
5 changes: 4 additions & 1 deletion server/api/card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import database, { cards, credentials } from "../database";
import auth from "../middleware/auth";
import { createCard, getPAN } from "../utils/cryptomate";
import { getInquiry } from "../utils/persona";
import { track } from "../utils/segment";

const mutexes = new Map<string, Mutex>();
function createMutex(credentialId: string) {
Expand Down Expand Up @@ -118,7 +119,8 @@ export default app
},
});
if (!credential) return c.json("credential not found", 401);
setUser({ id: parse(Address, credential.account) });
const account = parse(Address, credential.account);
setUser({ id: account });
if (credential.cards.length === 0 || !credential.cards[0]) return c.json("no card found", 404);
const card = credential.cards[0];
switch (patch.type) {
Expand All @@ -132,6 +134,7 @@ export default app
const { status } = patch;
if (card.status === status) return c.json(`card is already ${status.toLowerCase()}`, 400);
await database.update(cards).set({ status }).where(eq(cards.id, card.id));
track({ userId: account, event: status === "FROZEN" ? "CardFrozen" : "CardUnfrozen" });
return c.json({ status }, 200);
}
}
Expand Down
6 changes: 6 additions & 0 deletions server/hooks/cryptomate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { auditorAbi, issuerCheckerAbi, issuerCheckerAddress, marketAbi } from ".
import COLLECTOR from "../utils/COLLECTOR";
import keeper from "../utils/keeper";
import publicClient from "../utils/publicClient";
import { track } from "../utils/segment";
import traceClient, { type CallFrame } from "../utils/traceClient";
import transactionOptions from "../utils/transactionOptions";

Expand Down Expand Up @@ -202,6 +203,11 @@ export default new Hono().post(
captureException(new Error("bad collection"), { level: "warning", contexts: { tx: { call, trace } } });
return c.json({ response_code: "51" });
}
track({
userId: account,
event: "TransactionAuthorized",
properties: { usdAmount: payload.data.bill_amount },
});
return c.json({ response_code: "00" });
} catch (error: unknown) {
captureException(error, { contexts: { tx: { call } } });
Expand Down
7 changes: 6 additions & 1 deletion server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Base64URL } from "@exactly/common/validation";
import { serve } from "@hono/node-server";
import { serveStatic } from "@hono/node-server/serve-static";
import { sentry } from "@hono/sentry";
import { captureException } from "@sentry/node";
import { captureException, withScope } from "@sentry/node";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { trimTrailingSlash } from "hono/trailing-slash";
Expand All @@ -18,6 +18,7 @@ import block from "./hooks/block";
import cryptomate from "./hooks/cryptomate";
import androidFingerprint from "./utils/android/fingerprint";
import appOrigin from "./utils/appOrigin";
import { closeAndFlush } from "./utils/segment";

const app = new Hono();
app.use(sentry());
Expand Down Expand Up @@ -58,6 +59,10 @@ app.onError((error, c) => {

serve(app);

["SIGINT", "SIGTERM"].map((code) =>
process.on(code, () => closeAndFlush().catch((error: unknown) => captureException(error, { level: "error" }))),
);

declare module "hono" {
interface ContextVariableMap {
credentialId: Base64URL;
Expand Down
8 changes: 6 additions & 2 deletions server/utils/cryptomate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { Address } from "@exactly/common/validation";
import removeAccents from "remove-accents";
import * as v from "valibot";

import { track } from "./segment";

if (!process.env.CRYPTOMATE_URL || !process.env.CRYPTOMATE_API_KEY) throw new Error("missing cryptomate vars");
const baseURL = process.env.CRYPTOMATE_URL;
const apiKey = process.env.CRYPTOMATE_API_KEY;

export function createCard({
export async function createCard({
account,
email,
name,
Expand All @@ -21,7 +23,7 @@ export function createCard({
}) {
let cardholder = [name.first, name.middle, name.last].filter(Boolean).join(" ");
if (cardholder.length > 26 && name.middle) cardholder = `${name.first} ${name.last}`;
return request(
const card = await request(
CreateCardResponse,
"/cards/virtual-cards/create",
v.parse(CreateCardRequest, {
Expand All @@ -36,6 +38,8 @@ export function createCard({
metadata: { account },
}),
);
track({ event: "CardIssued", userId: account });
return card;
}

export async function getPAN(cardId: string) {
Expand Down
34 changes: 34 additions & 0 deletions server/utils/segment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Address } from "@exactly/common/validation";
import { Analytics } from "@segment/analytics-node";
import { captureException } from "@sentry/node";

if (!process.env.SEGMENT_WRITE_KEY) throw new Error("missing segment write key");

const analytics = new Analytics({ writeKey: process.env.SEGMENT_WRITE_KEY });

export function identify(
user: Prettify<{ userId: Address } & Omit<Parameters<typeof analytics.identify>[0], "userId">>,
) {
analytics.identify(user);
}

export function track(
action: Id<
| { event: "CardIssued" }
| { event: "CardFrozen" }
| { event: "CardUnfrozen" }
| { event: "TransactionAuthorized"; properties: { usdAmount: number } }
>,
) {
analytics.track(action);
}

export function closeAndFlush() {
return analytics.closeAndFlush();
}

analytics.on("error", (error) => captureException(error, { level: "error" }));

type Id<T> = Prettify<{ userId: Address } & T>;

type Prettify<T> = { [K in keyof T]: T[K] } & unknown;
1 change: 1 addition & 0 deletions server/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default defineConfig({
KEEPER_PRIVATE_KEY: padHex("0x69"),
POSTGRES_URL: "postgres",
REDIS_URL: "redis",
SEGMENT_WRITE_KEY: "segment",
},
coverage: { enabled: true, reporter: ["lcov"] },
},
Expand Down

0 comments on commit 695dbf3

Please sign in to comment.