Skip to content

Commit

Permalink
refractor Planetscale to Neon (#234)
Browse files Browse the repository at this point in the history
* refractor Planetscale to Neon

* Seed working, next is router TRPC.

* fix runtime error with cannot find DB

* remove references to mysql

* create a new customer when the onboarding is complete

* change onDuplicateKeyUpdate to onConflictDoUpdate

* change clerkUserId to userId

* remove warnings for key not provided

* fix type changes and allow assets and accounts to be added

* fix real estate add

* fixed build issue

---------

Co-authored-by: Shouryan Nikam <dev.shouryan@gmail.com>
  • Loading branch information
Codehagen and shouryan01 authored May 11, 2024
1 parent 3613edb commit d41a151
Show file tree
Hide file tree
Showing 32 changed files with 1,108 additions and 978 deletions.
7 changes: 2 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/dashboard"
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/dashboard"

# -----------------------------------------------------------------------------
# Database (MySQL - PlanetScale)
# Database (Neon - PostgreSQL)
# -----------------------------------------------------------------------------
DATABASE_USERNAME=
DATABASE_PASSWORD=pscale_pw_
DATABASE_HOST=eu-west.connect.psdb.
DATABASE_NAME=YOUR_DB_NAME
NEXT_PUBLIC_DATABASE_URL=

# -----------------------------------------------------------------------------
# Stripe
Expand Down
13 changes: 2 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,10 @@ To set up Stripe locally with environment variables:

## Database

This project uses MySQL database on PlanetScale. To setup a DB for your local dev:
This project uses Postgres database on Neon. To setup a DB for your local dev:

1. Create a free account and a [new Database](https://planetscale.com/docs/tutorials/planetscale-quick-start-guide#create-a-database)
2. From the dashboard, create a branch and click "Connect" button.
3. Hit `Create password` and select `Drizzle` in `Connect with` dropdown
4. Copy the entire list of params to `.env.local` file. Make sure to change the params under the section "Database (MySQL - PlanetScale)"
5. run `pnpm run db:push`
Create a free account and a [new Database](https://neon.tech/)

You can also use `docker-compose` to have a Mysql database locally, instead of relying on PlanetScale:

1. Enter `MYSQL_ROOT_PASSWORD`, `MYSQL_DATABASE`, `MYSQL_USER` and `MYSQL_PASSWORD` values in `.env.local`.
2. run `docker-compose --env-file .env.local up` to start the DB.
3. run `pnpm run db:push`.

## Roadmap

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function SidebarNav({ isCollapsed }: { isCollapsed: boolean }) {

{sideNavItems.map((group) => {
return (
<>
<div key={group.group}>
<Separator />
<div className="flex flex-col gap-1 p-2" key={group.group}>
{group.items.map((link, idx) => {
Expand All @@ -116,7 +116,7 @@ export function SidebarNav({ isCollapsed }: { isCollapsed: boolean }) {
);
})}
</div>
</>
</div>
);
})}
</nav>
Expand Down
9 changes: 3 additions & 6 deletions apps/www/app/(dashboard)/_components/hamburger-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"

import { AlignJustify } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { sideNavItems, siteConfig } from "@/app/config";
import { Icons } from "@/components/shared/icons";
import { ExpandedItem } from "../(workspaceId)/_components/sidebar-nav";
Expand Down Expand Up @@ -43,9 +40,9 @@ export default function HamburgerMenu() {

{sideNavItems.map((group) => {
return (
<>
<div key={group.group}>
<Separator className="mb-2 mt-2" />
<div className="flex flex-col gap-1 p-2" key={group.group}>
<div className="flex flex-col gap-1 p-2">
{group.items.map((link, idx) => {
return (
<ExpandedItem
Expand All @@ -56,7 +53,7 @@ export default function HamburgerMenu() {
)
})}
</div>
</>
</div>
);
})}
</SheetContent>
Expand Down
32 changes: 0 additions & 32 deletions apps/www/app/(docs)/docs/installation/planet-scale/page.mdx

This file was deleted.

185 changes: 95 additions & 90 deletions apps/www/app/api/cron/update-bank-account-data/route.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,109 @@
import { NextRequest, NextResponse } from "next/server";
// import { NextRequest, NextResponse } from "next/server";

import { db, schema, sql } from "@projectx/db";
// import { db, schema, sql } from "@projectx/db";

import { env } from "@/env";
import { BankingData, connectorFacade, toConnectorEnv } from "@/lib/connector";
// import { env } from "@/env";
// import { BankingData, connectorFacade, toConnectorEnv } from "@/lib/connector";

export async function POST(req: NextRequest) {
// get the bearer token from the header
const authToken = (req.headers.get("authorization") || "")
.split("Bearer ")
.at(1);
// export async function POST(req: NextRequest) {
// // get the bearer token from the header
// const authToken = (req.headers.get("authorization") || "")
// .split("Bearer ")
// .at(1);

// if not found OR the bearer token does NOT equal the CRON_SECRET
// TODO: Later we'll add the 2nd part of condition authToken !== env.CRON_SECRET
if (!authToken) {
return NextResponse.json(
{ error: "Unauthorized" },
{
status: 401,
},
);
}
// // if not found OR the bearer token does NOT equal the CRON_SECRET
// // TODO: Later we'll add the 2nd part of condition authToken !== env.CRON_SECRET
// if (!authToken) {
// return NextResponse.json(
// { error: "Unauthorized" },
// {
// status: 401,
// },
// );
// }

try {
console.log("○ [openbanking]: Handling cron bank-account-data");
// try {
// console.log("○ [openbanking]: Handling cron bank-account-data");

await handleEvent();
// await handleEvent();

console.log("✓ [openbanking]: Handled cron bank-account-data");
return NextResponse.json({ received: true }, { status: 200 });
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";
console.log(`❌ [openbanking] Error when handling cron: ${message}`);
return NextResponse.json({ error: message }, { status: 400 });
}
}
// console.log("✓ [openbanking]: Handled cron bank-account-data");
// return NextResponse.json({ received: true }, { status: 200 });
// } catch (error) {
// const message = error instanceof Error ? error.message : "Unknown error";
// console.log(`❌ [openbanking] Error when handling cron: ${message}`);
// return NextResponse.json({ error: message }, { status: 400 });
// }
// }

const handleEvent = async () => {
const facade = await connectorFacade(toConnectorEnv(env.NODE_ENV));
const resourceList = await facade.listResourcesFromDB();
console.debug(`○ [openbanking] ${resourceList.length} resources`);
// const handleEvent = async () => {
// const facade = await connectorFacade(toConnectorEnv(env.NODE_ENV));
// const resourceList = await facade.listResourcesFromDB();
// console.debug(`○ [openbanking] ${resourceList.length} resources`);

const promises = resourceList.map(({ integration, ...resource }) => {
return facade.listBankingAccountData(resource, integration.connectorId);
});
// const promises = resourceList.map(({ integration, ...resource }) => {
// return facade.listBankingAccountData(resource, integration.connectorId);
// });

const results = await Promise.allSettled(promises);
// TODO: log errors
const errors = results.filter((r) => r.status === "rejected");
const successes = results.filter(
(r): r is PromiseFulfilledResult<BankingData[]> => r.status === "fulfilled",
);
// const results = await Promise.allSettled(promises);
// // TODO: log errors
// const errors = results.filter((r) => r.status === "rejected");
// const successes = results.filter(
// (r): r is PromiseFulfilledResult<BankingData[]> => r.status === "fulfilled",
// );

const dbTransactionPromiseList = successes
.map((success) => success.value.map(upsertBankAccountData))
.flat();
// const dbTransactionPromiseList = successes
// .map((success) => success.value.map(upsertBankAccountData))
// .flat();

// TODO: use Promise.allSettled to better log
await Promise.all(dbTransactionPromiseList);
};
// // TODO: use Promise.allSettled to better log
// await Promise.all(dbTransactionPromiseList);
// };

const upsertBankAccountData = async (data: BankingData) => {
await db.transaction(async (tx) => {
const accountQuery = await tx
.insert(schema.account)
.values(data.account)
.onDuplicateKeyUpdate({ set: { name: sql`name` } });
// const upsertBankAccountData = async (data: BankingData) => {
// await db.transaction(async (tx) => {
// const accountQuery = await tx
// .insert(schema.account)
// .values(data.account)
// .onConflictDoUpdate({
// target: schema.account.id,
// set: { name: sql`name` }
// });

await tx
.insert(schema.balance)
.values(
data.balances.map((balance) => {
return {
...balance,
accountId: BigInt(accountQuery.insertId),
};
}),
)
.onDuplicateKeyUpdate({
set: {
amount: sql`amount`,
date: sql`date`,
},
});
await tx
.insert(schema.transaction)
.values(
data.transactions.map((transaction) => {
return {
...transaction,
accountId: BigInt(accountQuery.insertId),
};
}),
)
.onDuplicateKeyUpdate({
set: {
amount: sql`amount`,
date: sql`date`,
description: sql`description`,
},
});
});
};
// await tx
// .insert(schema.balance)
// .values(
// data.balances.map((balance) => {
// return {
// ...balance,
// accountId: BigInt(accountQuery.insertId),
// };
// }
// )
// .onConflictDoUpdate({
// target: schema.balance.id,
// set: {
// amount: sql`amount`,
// date: sql`date`,
// },
// });
// await tx
// .insert(schema.transaction)
// .values(
// data.transactions.map((transaction) => {
// return {
// ...transaction,
// accountId: BigInt(accountQuery.insertId),
// };
// }),
// )
// .onConflictDoUpdate({
// target: schema.transaction.id,
// set: {
// amount: sql`amount`,
// date: sql`date`,
// description: sql`description`,
// },
// });
// });
// };
9 changes: 5 additions & 4 deletions apps/www/app/api/cron/update-integrations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ const handleEvent = async () => {
await db
.insert(schema.integration)
.values(integrationList)
.onDuplicateKeyUpdate({
.onConflictDoUpdate({
target: schema.integration.id,
set: {
connectorProviderId: sql`connector_provider_id`,
connectorId: sql`connector_id`,
name: sql`values(name)`,
logoUrl: sql`values(logo_url)`,
updatedAt: sql`values(updated_at)`,
name: sql`name`,
logoUrl: sql`logo_url`,
updatedAt: sql`updated_at`,
},
});
};
20 changes: 18 additions & 2 deletions apps/www/app/onboarding/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,29 @@ import { Toaster } from "@/components/ui/toaster";
import ConnectAccount from "@/components/onboarding/connect-account";
import FinancialGoals from "@/components/onboarding/financial-goals";
import Welcome from "@/components/onboarding/welcome";
import { db } from "@projectx/db";
import { customer } from "../../../../packages/db/src/schema/customer";
import { useUser } from "@clerk/nextjs";
import { nanoid } from 'nanoid'

export default function Intro() {
const router = useRouter();
const searchParams = useSearchParams();

const user = useUser();
const step = searchParams.get("step");

async function createUser() {
await db.insert(customer).values({
id: `prefix_${nanoid(16)}`,
createdAt: new Date(),
updatedAt: new Date(),
stripeId: "stripe_id",
userId: user.user?.id || "error",
});

return { message: "user created" };
}

return (
<div className="dark mx-auto flex h-screen w-screen flex-col items-center justify-center overflow-x-hidden bg-zinc-950 text-white">
<Toaster />
Expand All @@ -37,7 +53,7 @@ export default function Intro() {
<h1 className="font-display max-w-md text-3xl font-semibold transition-colors sm:text-4xl">
Done!
</h1>
<Link href="/dashboard" className="rounded-2xl">
<Link href="/dashboard" className="rounded-2xl" onClick={createUser}>
Go to Dashboard
</Link>
</div>
Expand Down
Loading

0 comments on commit d41a151

Please sign in to comment.