Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions apps/dashboard/app/(app)/apis/create-api-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,28 @@ import { Button } from "@unkey/ui";
import { Plus } from "lucide-react";
import { useRouter } from "next/navigation";
import type React from "react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";

const formSchema = z.object({
name: z.string().trim().min(3, "Name must be at least 3 characters long").max(50),
});

export const CreateApiButton = ({ ...rest }: React.ButtonHTMLAttributes<HTMLButtonElement>) => {
type Props = {
defaultOpen?: boolean;
};

export const CreateApiButton = ({
defaultOpen,
...rest
}: React.ButtonHTMLAttributes<HTMLButtonElement> & Props) => {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});

const [open, setOpen] = useState(defaultOpen ?? false);

const create = trpc.api.create.useMutation({
async onSuccess(res) {
toast.success("Your API has been created");
Expand All @@ -49,7 +59,7 @@ export const CreateApiButton = ({ ...rest }: React.ButtonHTMLAttributes<HTMLButt

return (
<>
<Dialog>
<Dialog open={open} onOpenChange={(o) => setOpen(o)}>
<DialogTrigger asChild>
<Button variant="primary" {...rest}>
<Plus />
Expand Down
11 changes: 9 additions & 2 deletions apps/dashboard/app/(app)/apis/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import { ApiList } from "./client";
export const dynamic = "force-dynamic";
export const runtime = "edge";

export default async function ApisOverviewPage() {
type Props = {
searchParams: { new?: boolean };
};

export default async function ApisOverviewPage(props: Props) {
const tenantId = getTenantId();
const workspace = await db.query.workspaces.findFirst({
where: (table, { and, eq, isNull }) =>
Expand Down Expand Up @@ -48,7 +52,10 @@ export default async function ApisOverviewPage() {
<Navbar.Breadcrumbs.Link href="/apis">APIs</Navbar.Breadcrumbs.Link>
</Navbar.Breadcrumbs>
<Navbar.Actions>
<CreateApiButton key="createApi" />
<CreateApiButton
key="createApi"
defaultOpen={apis.length === 0 || props.searchParams.new}
/>
</Navbar.Actions>
</Navbar>
<PageContent>
Expand Down
35 changes: 35 additions & 0 deletions apps/dashboard/app/(app)/gateway-new/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* This route creates a shortcut for onboarding
*
* We need this for compliance of our gateway.new domain.
* 1. A user will enter "gateway.new" in the browser
* 2. Vercel will detect the host and rewrite the request to this page
* 3. A workspace is upserted
* 4. The user is redirected to create their API
*/

import { getTenantId } from "@/lib/auth";
import { db, schema } from "@/lib/db";
import { newId } from "@unkey/id";
import { redirect } from "next/navigation";

export default async function Page() {
const tenantId = getTenantId();

const ws = await db.query.workspaces.findFirst({
where: (table, { eq, isNull, and }) =>
and(eq(table.tenantId, tenantId), isNull(table.deletedAt)),
});

if (!ws) {
await db.insert(schema.workspaces).values({
id: newId("workspace"),
name: "Personal Workspace",
tenantId,
betaFeatures: {},
features: {},
});
}

return redirect("/apis?new=true");
}
1 change: 1 addition & 0 deletions apps/dashboard/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const config = {
"/success",
"/success/(.*)",
"/auth/(.*)",
"/gateway-new",
"/(api|trpc)(.*)",
"/((?!.+\\.[\\w]+$|_next).*)",
"/((?!_next/static|_next/image|images|favicon.ico|$).*)",
Expand Down
12 changes: 11 additions & 1 deletion apps/dashboard/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** @type {import('next').NextConfig} */

const securityHeaders = [
{
key: "X-Frame-Options",
Expand Down Expand Up @@ -51,6 +50,17 @@ const nextConfig = {
source: "/engineering/:match*",
destination: "https://unkey-engineering.mintlify.dev/engineering/:match*",
},
// Powers our gateway.new compliant domain
{
source: "/:path",
has: [
{
type: "host",
value: "gateway.new",
},
],
destination: "/gateway-new",
},
],
};

Expand Down