Skip to content

Commit

Permalink
Platform (#62)
Browse files Browse the repository at this point in the history
* Update dependencies

* wip

* wip

* wip

* wip

* wip

* wip
  • Loading branch information
pontusab authored Jan 16, 2025
1 parent 59c3e18 commit cf1731c
Show file tree
Hide file tree
Showing 86 changed files with 622 additions and 4,131 deletions.
104 changes: 52 additions & 52 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,56 +1,56 @@
name: 📦 Release Packages
# name: 📦 Release Packages

on:
push:
branches:
- main
paths-ignore:
- 'apps/**'
# on:
# push:
# branches:
# - main
# paths-ignore:
# - 'apps/**'

jobs:
test:
name: 🧪 Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: 📦 Install dependencies
run: bun install
- name: 🔍 Run type checking
run: bun run typecheck
- name: 🔬 Run linting
run: bun run lint
- name: 🧪 Run unit tests
run: bun run test
# jobs:
# test:
# name: 🧪 Run tests
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: oven-sh/setup-bun@v1
# with:
# bun-version: latest
# - name: 📦 Install dependencies
# run: bun install
# - name: 🔍 Run type checking
# run: bun run typecheck
# - name: 🔬 Run linting
# run: bun run lint
# - name: 🧪 Run unit tests
# run: bun run test

create-release:
name: 📦 Create Release
needs: [test]
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: 📦 Install dependencies
run: bun install
- name: 🏗️ Build packages
run: bun turbo build --filter=languine --filter=@languine/react-email
- name: 🔖 Create and publish versions
uses: changesets/action@master
with:
version: bun run changeset version
commit: "chore: update versions"
title: "chore: update versions"
publish: bun run changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# create-release:
# name: 📦 Create Release
# needs: [test]
# runs-on: ubuntu-latest
# permissions:
# contents: write
# pull-requests: write
# steps:
# - uses: actions/checkout@v4
# with:
# fetch-depth: 0
# - uses: oven-sh/setup-bun@v1
# with:
# bun-version: latest
# - name: 📦 Install dependencies
# run: bun install
# - name: 🏗️ Build packages
# run: bun turbo build --filter=languine --filter=@languine/react-email
# - name: 🔖 Create and publish versions
# uses: changesets/action@master
# with:
# version: bun run changeset version
# commit: "chore: update versions"
# title: "chore: update versions"
# publish: bun run changeset publish
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ dist/

# deps
node_modules/
.wrangler

# env
.env
Expand Down
20 changes: 10 additions & 10 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@
"@react-email/components": "0.0.32",
"@react-email/font": "^0.0.9",
"@tanstack/react-query": "^5.64.1",
"@trpc/client": "^11.0.0-rc.688",
"@trpc/react-query": "^11.0.0-rc.688",
"@trpc/server": "^11.0.0-rc.688",
"@trpc/client": "11.0.0-rc.700",
"@trpc/react-query": "11.0.0-rc.700",
"@trpc/server": "11.0.0-rc.700",
"@upstash/ratelimit": "^2.0.5",
"@upstash/redis": "^1.34.3",
"@vercel/functions": "^1.5.2",
"ai": "^4.0.34",
"better-auth": "^1.1.11",
"ai": "^4.0.38",
"better-auth": "^1.1.13",
"class-variance-authority": "^0.7.1",
"client-only": "^0.0.1",
"clsx": "^2.1.1",
Expand All @@ -53,29 +53,29 @@
"drizzle-orm": "^0.38.3",
"input-otp": "^1.4.2",
"lucide-react": "^0.471.1",
"motion": "^11.17.1",
"motion": "^11.18.0",
"next": "15.1.4",
"next-international": "^1.3.1",
"next-themes": "^0.4.4",
"nuqs": "^2.3.0",
"nuqs": "^2.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"react-icons": "^5.4.0",
"recharts": "^2.15.0",
"resend": "^4.0.1",
"resend": "^4.1.1",
"server-only": "^0.0.1",
"sonner": "^1.7.1",
"sonner": "^1.7.2",
"superjson": "^2.2.2",
"tailwind-merge": "^2.6.0",
"zod": "^3.24.1"
},
"devDependencies": {
"languine": "link:languine",
"@types/node": "^22",
"@types/react": "^19",
"react-email": "3.0.6",
"@types/react-dom": "^19",
"languine": "^1.0.2",
"postcss": "^8",
"tailwindcss": "^3.4.17",
"typescript": "^5"
Expand Down
22 changes: 22 additions & 0 deletions apps/web/src/app/[locale]/(dashboard)/cli/success/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getSession } from "@/lib/session";
import { getI18n } from "@/locales/server";
import { redirect } from "next/navigation";

export default async function Page() {
const t = await getI18n();
const session = await getSession();

if (!session.data) {
redirect("/login");
}

return (
<div className="w-full h-screen flex flex-col items-center justify-center">
<h1 className="text-xl font-medium mb-4">{t("cli.success.title")}</h1>
<p className="text-center mb-2 text-sm text-secondary">
{t("cli.success.description")} <span>{session.data.user.email}</span>
</p>
<p className="text-sm text-secondary">{t("cli.success.description_2")}</p>
</div>
);
}
2 changes: 1 addition & 1 deletion apps/web/src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default async function RootLayout({
params,
}: Readonly<{
children: React.ReactNode;
params: { locale: string };
params: Promise<{ locale: string }>;
}>) {
const { locale } = await params;

Expand Down
52 changes: 52 additions & 0 deletions apps/web/src/app/api/auth/cli/[token]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { CLI_TOKEN_NAME, saveCLISession } from "@/lib/auth/cli";
import { getSession } from "@/lib/session";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { NextResponse } from "next/server";

const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(5, "60s"),
});

export async function GET(
request: Request,
{ params }: { params: Promise<{ token: string }> },
) {
const ip = request.headers.get("x-forwarded-for") ?? "127.0.0.1";
const { success } = await ratelimit.limit(ip);

if (!success) {
return NextResponse.json({ error: "Too many requests" }, { status: 429 });
}

const { token } = await params;

const session = await getSession();

if (!session?.data) {
const response = NextResponse.redirect(new URL("/login", request.url), {
status: 302,
});

if (token) {
response.cookies.set(CLI_TOKEN_NAME, token, {
maxAge: 5 * 60,
});
}

return response;
}

if (session?.data?.session) {
await saveCLISession(session.data.session, token);
}

const response = NextResponse.redirect(new URL("/cli/success", request.url), {
status: 302,
});

response.cookies.delete(CLI_TOKEN_NAME);

return response;
}
39 changes: 39 additions & 0 deletions apps/web/src/app/api/auth/cli/[token]/verify/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { db } from "@/db";
import { users } from "@/db/schema";
import { getCLISession } from "@/lib/auth/cli";
import { eq } from "drizzle-orm";
import { NextResponse } from "next/server";

export async function GET(
request: Request,
{ params }: { params: Promise<{ token: string }> },
) {
const { token } = await params;

const cliSession = await getCLISession(token);

if (!cliSession) {
return NextResponse.json(
{
success: false,
},
{ status: 404 },
);
}

const user = await db
.select({
id: users.id,
name: users.name,
email: users.email,
apiKey: users.apiKey,
})
.from(users)
.where(eq(users.id, cliSession.userId))
.get();

return NextResponse.json({
success: true,
user,
});
}
4 changes: 2 additions & 2 deletions apps/web/src/components/onboarding-steps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export function OnboardingSteps() {
{t("onboarding.steps.1.description")}
</p>
<CopyInput
value={`npx languine@latest --p=${project}`}
value={`npx languine@latest init --p=${project}`}
onCopy={() => setStep(2)}
className="border-dashed text-xs"
className="border-dashed"
/>
</CardContent>
</Card>
Expand Down
17 changes: 17 additions & 0 deletions apps/web/src/lib/auth/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { kv } from "@/lib/kv";
import type { Session } from "better-auth";
import { cookies } from "next/headers";

export const CLI_TOKEN_NAME = "cli-token";

export async function saveCLISession(session: Session, token: string) {
await kv.set(`${CLI_TOKEN_NAME}:${token}`, session, {
ex: 5 * 60,
});
}

export async function getCLISession(token: string) {
const data = await kv.get<Session>(`${CLI_TOKEN_NAME}:${token}`);

return data;
}
57 changes: 57 additions & 0 deletions apps/web/src/lib/auth/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
createDefaultOrganization,
getDefaultOrganization,
} from "@/db/queries/organization";
import WelcomeEmail from "@/emails/templates/welcome";
import { resend } from "@/lib/resend";
import { waitUntil } from "@vercel/functions";
import type { Session, User } from "better-auth";
import { cookies } from "next/headers";
import { CLI_TOKEN_NAME, saveCLISession } from "./cli";

export const databaseHooks = {
user: {
create: {
after: async (user: User) => {
await createDefaultOrganization(user);

// Send welcome email to new user
try {
waitUntil(
resend.emails.send({
from: "Languine <[email protected]>",
to: user.email,
subject: "Welcome to Languine",
react: WelcomeEmail({ name: user.name }),
}),
);
} catch (error) {
console.error("Error sending welcome email", error);
}
},
},
},
session: {
create: {
before: async (session: Session) => {
const org = await getDefaultOrganization(session.userId);

const cookieStore = await cookies();
const token = cookieStore.get(CLI_TOKEN_NAME);

if (token?.value) {
await saveCLISession(session, token.value);

cookieStore.delete(CLI_TOKEN_NAME);
}

return {
data: {
...session,
activeOrganizationId: org?.organizations?.id,
},
};
},
},
},
};
Loading

0 comments on commit cf1731c

Please sign in to comment.