Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added types, services and repository api route #4

Closed
wants to merge 11 commits into from
Closed
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ module.exports = {
parser: "@typescript-eslint/parser",
},
],
};
};
4 changes: 2 additions & 2 deletions app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export const metadata: Metadata = {

export default function LoginPage() {
return (
<div className="container flex h-screen w-screen flex-col items-center justify-center">
<div className="size-screen container flex flex-col items-center justify-center">
<Link
href="/"
className={cn(buttonVariants({ variant: "ghost" }), "absolute left-4 top-4 md:left-8 md:top-8")}>
<>
<ChevronLeft className="mr-2 h-4 w-4" />
<ChevronLeft className="mr-2 size-4" />
Back
</>
</Link>
Expand Down
2 changes: 1 addition & 1 deletion app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const metadata = {

export default function RegisterPage() {
return (
<div className="container grid h-screen w-screen flex-col items-center justify-center lg:max-w-none lg:grid-cols-2 lg:px-0">
<div className="size-screen container grid flex-col items-center justify-center lg:max-w-none lg:grid-cols-2 lg:px-0">
<Link
href="/login"
className={cn(buttonVariants({ variant: "ghost" }), "absolute right-4 top-4 md:right-8 md:top-8")}>
Expand Down
57 changes: 57 additions & 0 deletions app/(dashboard)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { DashboardNav } from "@/components/nav";
import { UserAccountNav } from "@/components/user-account-nav";
import { dashboardConfig } from "@/config/dashboard";
import { getCurrentUser } from "@/lib/session";
import Image from "next/image";
import Link from "next/link";
import { notFound } from "next/navigation";

import OSSGGLogo from "../oss-gg-logo.png";
import ConnectGitHubAppButton from "./client-page";

interface DashboardLayoutProps {
children?: React.ReactNode;
}

export default async function DashboardLayout({ children }: DashboardLayoutProps) {
const user = await getCurrentUser();

if (!user) {
return notFound();
}

return (
<div className="relative flex min-h-screen">
<Link href="/" className="absolute right-12 top-10 z-40">
<Image src={OSSGGLogo} alt="oss gg logo" width={160} />
</Link>
<aside className="hidden w-[250px] flex-col justify-between bg-slate-200 p-8 md:flex">
<div>
<div className="mb-4 ml-4">
<UserAccountNav
user={{
name: user.name,
avatarUrl: user.avatarUrl,
email: user.email,
}}
/>
</div>
<DashboardNav items={dashboardConfig.mainNav} />
</div>
<div>
<div className="mb-2">
<ConnectGitHubAppButton />
</div>
<DashboardNav items={dashboardConfig.bottomNav} />
<p className="mb-3 ml-3 mt-5 text-xs">
<a href="https://formbricks.com/github">Built by Formbricks</a>
</p>
<p className="ml-3 text-xs">
<a href="https://github.com/formbricks/oss.gg">View Source Code</a>
</p>
</div>
</aside>
<main className="flex w-full flex-1 flex-col overflow-auto p-12">{children}</main>
</div>
);
}
17 changes: 17 additions & 0 deletions app/(dashboard)/settings/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use server";

import { createApiKey } from "@/lib/api-key";
import { authOptions } from "@/lib/auth";
import { hasUserAccessToRepository } from "@/lib/repository";
import { TApiKeyCreateInput } from "@/types/apiKey";
import { getServerSession } from "next-auth";

export async function createApiKeyAction(repositoryId: string, apiKeyData: TApiKeyCreateInput) {
const session = await getServerSession(authOptions);
if (!session) return;
const hasUserAccess = await hasUserAccessToRepository(session.user.id, repositoryId);
if (!hasUserAccess) {
throw new Error("You do not have access to this repository");
}
return await createApiKey(repositoryId, apiKeyData);
}
14 changes: 14 additions & 0 deletions app/api/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { getApiKeyFromKey } from "@/lib/api-key";
import { TApiKey } from "@/types/apiKey";

export async function validateApiKey(request: Request): Promise<TApiKey | null> {
const apiKey = request.headers.get("x-api-key");
if (!apiKey) {
return null;
}

const apiKeyData = await getApiKeyFromKey(apiKey);
if (apiKeyData) {
return apiKeyData;
} else return null;
}
53 changes: 53 additions & 0 deletions app/api/repository/[repositoryId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// pages/api/repository.js
import { db } from "@/lib/db";

import { validateApiKey } from "../../auth";

export async function GET(request, { params }) {
const repositoryId = params.repositoryId;
const apiKeyData = await validateApiKey(request);
if (!apiKeyData || repositoryId === apiKeyData.repositoryId) return;

if (!repositoryId) {
return new Response("Repository ID is required", { status: 400 });
}

const repository = await db.repository.findUnique({
where: { id: repositoryId },
});

if (!repository) {
return new Response("Repository not found", { status: 404 });
}

return new Response(JSON.stringify(repository), {
headers: { "Content-Type": "application/json" },
status: 200,
});
}

export async function POST(request) {
const data = await request.json();

try {
const newRepository = await db.repository.create({
data: {
githubId: data.githubId,
name: data.name,
description: data.description,
homepage: data.homepage,
topics: data.topics,
default_branch: data.default_branch,
installationId: data.installationId,
levels: data.levels,
},
});

return new Response(JSON.stringify(newRepository), {
headers: { "Content-Type": "application/json" },
status: 201,
});
} catch (error) {
return new Response(error.message, { status: 500 });
}
}
4 changes: 2 additions & 2 deletions components/empty-placeholder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export function EmptyPlaceholder({ className, children, ...props }: EmptyPlaceho

EmptyPlaceholder.Icon = function EmptyPlaceHolderIcon({ name, className, ...props }) {
return (
<div className="flex h-20 w-20 items-center justify-center rounded-full bg-muted">
<DoorClosed className={cn("h-10 w-10", className)} {...props} />
<div className="flex size-20 items-center justify-center rounded-full bg-muted">
<DoorClosed className={cn("size-10", className)} {...props} />
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion components/main-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function MainNav({ items, children }: MainNavProps) {
key={index}
href={item.disabled ? "#" : item.href}
className={cn(
"flex items-center text-lg font-medium transition-colors hover:text-foreground/80 sm:text-sm",
"hover:text-foreground/80 flex items-center text-lg font-medium transition-colors sm:text-sm",
item.href.startsWith(`/${segment}`)
? "text-foreground"
: "text-foreground/60",
Expand Down
8 changes: 4 additions & 4 deletions components/mode-toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ export function ModeToggle() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="h-8 w-8 px-0">
<Button variant="ghost" size="sm" className="size-8 px-0">
<Sun className="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
<Sun className="mr-2 h-4 w-4" />
<Sun className="mr-2 size-4" />
<span>Light</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
<Moon className="mr-2 h-4 w-4" />
<Moon className="mr-2 size-4" />
<span>Dark</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
<Laptop className="mr-2 h-4 w-4" />
<Laptop className="mr-2 size-4" />
<span>System</span>
</DropdownMenuItem>
</DropdownMenuContent>
Expand Down
2 changes: 1 addition & 1 deletion components/tailwind-indicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export function TailwindIndicator() {
if (process.env.NODE_ENV === "production") return null;

return (
<div className="fixed bottom-1 left-1 z-50 flex h-6 w-6 items-center justify-center rounded-full bg-gray-800 p-3 font-mono text-xs text-white">
<div className="fixed bottom-1 left-1 z-50 flex size-6 items-center justify-center rounded-full bg-gray-800 p-3 font-mono text-xs text-white">
<div className="block sm:hidden">xs</div>
<div className="hidden sm:block md:hidden lg:hidden xl:hidden 2xl:hidden">sm</div>
<div className="hidden md:block lg:hidden xl:hidden 2xl:hidden">md</div>
Expand Down
6 changes: 3 additions & 3 deletions components/ui/dropdown-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
)}
{...props}>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
<ChevronRight className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger>
));
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
Expand Down Expand Up @@ -102,7 +102,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
{...props}>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
<Check className="size-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
Expand All @@ -123,7 +123,7 @@ const DropdownMenuRadioItem = React.forwardRef<
{...props}>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
<Circle className="size-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
Expand Down
14 changes: 7 additions & 7 deletions components/ui/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
type ToastActionElement = React.ReactElement<typeof ToastAction>;

export {
type ToastProps,
type ToastActionElement,
ToastProvider,
ToastViewport,
Toast,
ToastTitle,
ToastDescription,
ToastClose,
ToastAction,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
type ToastActionElement,
type ToastProps,
};
2 changes: 1 addition & 1 deletion components/user-account-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function UserAccountNav({ user }: UserAccountNavProps) {
<DropdownMenuTrigger>
<UserAvatar
user={{ name: user.name || null, avatarUrl: user.avatarUrl || null }}
className="h-8 w-8"
className="size-8"
/>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
Expand Down
4 changes: 2 additions & 2 deletions components/user-auth-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
}}
disabled={isGitHubLoading}>
{isGitHubLoading ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
<Loader2 className="mr-2 size-4 animate-spin" />
) : (
<FaGithub className="mr-2 h-4 w-4" />
<FaGithub className="mr-2 size-4" />
)}
Github
</button>
Expand Down
2 changes: 1 addition & 1 deletion components/user-avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function UserAvatar({ user, ...props }: UserAvatarProps) {
) : (
<AvatarFallback>
<span className="sr-only">{user.name}</span>
<UserIcon className="h-4 w-4" />
<UserIcon className="size-4" />
</AvatarFallback>
)}
</Avatar>
Expand Down
Loading
Loading