From d04cbe2314239ea7551ad92d247b5579717ecab8 Mon Sep 17 00:00:00 2001 From: Dhruwang Date: Fri, 19 Jan 2024 17:35:41 +0530 Subject: [PATCH 1/8] merged latest changes --- app/(dashboard)/dashboard/settings/actions.ts | 17 ++++ app/api/me/route.ts | 40 +++++++++ components/user-name-form.tsx | 90 ++++++++++++------- config/dashboard.ts | 4 +- lib/apikey.ts | 75 ++++++++++++++++ lib/repository.ts | 65 ++++++++++++++ lib/types/account.ts | 37 ++++++++ lib/types/apiKey.ts | 18 ++++ lib/types/level.ts | 10 +++ lib/types/repository.ts | 31 +++++++ pages/api/apiHelper.ts | 22 +++++ .../20240119111335_add_api_key/migration.sql | 80 +++++++++++++++++ 12 files changed, 456 insertions(+), 33 deletions(-) create mode 100644 app/(dashboard)/dashboard/settings/actions.ts create mode 100644 app/api/me/route.ts create mode 100644 lib/apikey.ts create mode 100644 lib/repository.ts create mode 100644 lib/types/account.ts create mode 100644 lib/types/apiKey.ts create mode 100644 lib/types/level.ts create mode 100644 lib/types/repository.ts create mode 100644 pages/api/apiHelper.ts create mode 100644 prisma/migrations/20240119111335_add_api_key/migration.sql diff --git a/app/(dashboard)/dashboard/settings/actions.ts b/app/(dashboard)/dashboard/settings/actions.ts new file mode 100644 index 0000000..c67e52b --- /dev/null +++ b/app/(dashboard)/dashboard/settings/actions.ts @@ -0,0 +1,17 @@ +"use server" + +import { getServerSession } from "next-auth" + +import { createApiKey } from "@/lib/apikey" +import { authOptions } from "@/lib/auth" +import { TApiKeyCreateInput } from "@/lib/types/apiKey" + +export async function createApiKeyAction( + accountId: string, + apiKeyData: TApiKeyCreateInput +) { + const session = await getServerSession(authOptions) + if (!session) return + + return await createApiKey(accountId, apiKeyData) +} diff --git a/app/api/me/route.ts b/app/api/me/route.ts new file mode 100644 index 0000000..5ab729a --- /dev/null +++ b/app/api/me/route.ts @@ -0,0 +1,40 @@ +import { headers } from "next/headers" +import { NextResponse } from "next/server" + +import { hashApiKey } from "@/lib/apikey" +import { db } from "@/lib/db" + +export async function GET() { + const headersList = headers() + const apiKey = headersList.get("x-api-key") + if (apiKey) { + const apiKeyData = await db.apiKey.findUnique({ + where: { + hashedKey: hashApiKey(apiKey), + }, + select: { + environment: { + select: { + id: true, + createdAt: true, + updatedAt: true, + type: true, + product: { + select: { + id: true, + name: true, + }, + }, + widgetSetupCompleted: true, + }, + }, + }, + }) + if (!apiKeyData) { + return new Response("Not authenticated", { + status: 401, + }) + } + return NextResponse.json(apiKeyData.environment) + } +} diff --git a/components/user-name-form.tsx b/components/user-name-form.tsx index b800b75..42a05fc 100644 --- a/components/user-name-form.tsx +++ b/components/user-name-form.tsx @@ -1,15 +1,16 @@ -"use client"; +"use client" -import * as React from "react"; -import { useRouter } from "next/navigation"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { User } from "@prisma/client"; -import { useForm } from "react-hook-form"; -import * as z from "zod"; +import * as React from "react" +import { useRouter } from "next/navigation" +import { zodResolver } from "@hookform/resolvers/zod" +import { User } from "@prisma/client" +import { Loader2Icon } from "lucide-react" +import { useForm } from "react-hook-form" +import * as z from "zod" -import { cn } from "@/lib/utils"; -import { userNameSchema } from "@/lib/validations/user"; -import { buttonVariants } from "@/components/ui/button"; +import { cn } from "@/lib/utils" +import { userNameSchema } from "@/lib/validations/user" +import { buttonVariants } from "@/components/ui/button" import { Card, CardContent, @@ -17,20 +18,19 @@ import { CardFooter, CardHeader, CardTitle, -} from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { toast } from "@/components/ui/use-toast"; -import { Loader2Icon } from "lucide-react"; +} from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { toast } from "@/components/ui/use-toast" interface UserNameFormProps extends React.HTMLAttributes { - user: Pick; + user: Pick } -type FormData = z.infer; +type FormData = z.infer -export function UserNameForm({ user, className, ...props }: UserNameFormProps) { - const router = useRouter(); +export function UserNameForm({ user, ...props }: UserNameFormProps) { + const router = useRouter() const { handleSubmit, register, @@ -40,30 +40,26 @@ export function UserNameForm({ user, className, ...props }: UserNameFormProps) { defaultValues: { name: user?.name || "", }, - }); - const [isSaving, setIsSaving] = React.useState(false); + }) + const [isSaving, setIsSaving] = React.useState(false) async function onSubmit(data: FormData) { - setIsSaving(true); + setIsSaving(true) // some server action to save the user's name - setIsSaving(false); + setIsSaving(false) return toast({ title: "Something went wrong.", description: "The server action to save the user name is not yet implemented", variant: "destructive", - }); + }) } return ( -
+ Your Name @@ -91,7 +87,7 @@ export function UserNameForm({ user, className, ...props }: UserNameFormProps) { + + + API keys + Add and remobe API keys + + +
+ + + {errors?.name && ( +

{errors.name.message}

+ )} +
+
+ + + +
- ); + ) } diff --git a/config/dashboard.ts b/config/dashboard.ts index 8a7cab5..674d121 100644 --- a/config/dashboard.ts +++ b/config/dashboard.ts @@ -1,4 +1,4 @@ -import { DashboardConfig } from "types"; +import { DashboardConfig } from "types" export const dashboardConfig: DashboardConfig = { mainNav: [ @@ -18,4 +18,4 @@ export const dashboardConfig: DashboardConfig = { href: "/dashboard/settings", }, ], -}; +} diff --git a/lib/apikey.ts b/lib/apikey.ts new file mode 100644 index 0000000..4241503 --- /dev/null +++ b/lib/apikey.ts @@ -0,0 +1,75 @@ +import "server-only" + +import { createHash, randomBytes } from "crypto" + +import { db } from "@/lib/db" + +import { TApiKey, TApiKeyCreateInput } from "./types/apiKey" + +export const getApiKey = async (apiKeyId: string): Promise => { + try { + const apiKeyData = await db.apiKey.findUnique({ + where: { + id: apiKeyId, + }, + }) + + return apiKeyData + } catch (error) { + throw error + } +} +export const getApiKeys = async ( + respositoryId: string, + page?: number +): Promise => { + try { + const apiKeys = await db.apiKey.findMany({ + where: { + respositoryId, + }, + }) + return apiKeys + } catch (error) { + throw error + } +} + +export const hashApiKey = (key: string): string => + createHash("sha256").update(key).digest("hex") + +export async function createApiKey( + respositoryId: string, + apiKeyData: TApiKeyCreateInput +): Promise { + try { + const key = randomBytes(16).toString("hex") + const hashedKey = hashApiKey(key) + + const result = await db.apiKey.create({ + data: { + ...apiKeyData, + hashedKey, + respositoryId: { connect: { id: respositoryId } }, + }, + }) + console.log({ ...result, apiKey: key }) + return { ...result, apiKey: key } + } catch (error) { + throw error + } +} + +export const deleteApiKey = async (id: string): Promise => { + try { + const deletedApiKeyData = await db.apiKey.delete({ + where: { + id: id, + }, + }) + + return deletedApiKeyData + } catch (error) { + throw error + } +} diff --git a/lib/repository.ts b/lib/repository.ts new file mode 100644 index 0000000..2b69c68 --- /dev/null +++ b/lib/repository.ts @@ -0,0 +1,65 @@ +import { db } from "@/lib/db" + +import { TRepository, TRepositoryCreateInput } from "./types/repository" + +export const getRepository = async ( + repositoryId: string +): Promise => { + try { + const repositoryData = await db.repository.findUnique({ + where: { + id: repositoryId, + }, + }) + + return repositoryData + } catch (error) { + throw error + } +} + +export const getRepositories = async ( + ownerId: string +): Promise => { + try { + const repositories = await db.repository.findMany({ + where: { + ownerId, + }, + }) + + return repositories + } catch (error) { + throw error + } +} + +export const createRepository = async ( + repositoryData: TRepositoryCreateInput +): Promise => { + try { + const newRepository = await db.repository.create({ + data: repositoryData, + }) + + return newRepository + } catch (error) { + throw error + } +} + +export const deleteRepository = async ( + repositoryId: string +): Promise => { + try { + const deletedRepository = await db.repository.delete({ + where: { + id: repositoryId, + }, + }) + + return deletedRepository + } catch (error) { + throw error + } +} diff --git a/lib/types/account.ts b/lib/types/account.ts new file mode 100644 index 0000000..e02fdca --- /dev/null +++ b/lib/types/account.ts @@ -0,0 +1,37 @@ +import { z } from "zod" + +import { ZApiKey } from "./apiKey" + +export const ZAccountInput = z.object({ + userId: z.string(), + type: z.string(), + provider: z.string(), + providerAccountId: z.string(), + access_token: z.string().nullish(), + refresh_token: z.string().nullish(), + expires_at: z.number().nullish(), + scope: z.string().nullish(), + token_type: z.string().nullish(), + id_token: z.string().nullish(), +}) + +export type TAccountInput = z.infer + +export const ZAccount = z.object({ + id: z.string(), + createdAt: z.date(), + updatedAt: z.date(), + userId: z.string(), + type: z.string(), + provider: z.string(), + providerAccountId: z.string(), + access_token: z.string().nullable(), + refresh_token: z.string().nullable().optional(), + expires_at: z.number().nullable(), + scope: z.string().nullable(), + token_type: z.string().nullable(), + id_token: z.string().nullable(), + apiKeys: z.array(ZApiKey), +}) + +export type TAccount = z.infer diff --git a/lib/types/apiKey.ts b/lib/types/apiKey.ts new file mode 100644 index 0000000..cad7445 --- /dev/null +++ b/lib/types/apiKey.ts @@ -0,0 +1,18 @@ +import { z } from "zod" + +export const ZApiKey = z.object({ + id: z.string().cuid2(), + createdAt: z.date(), + lastUsedAt: z.date().nullable(), + label: z.string().nullable(), + hashedKey: z.string(), + accountId: z.string().cuid2(), + apiKey: z.string().optional(), +}) + +export type TApiKey = z.infer + +export const ZApiKeyCreateInput = z.object({ + label: z.string(), +}) +export type TApiKeyCreateInput = z.infer diff --git a/lib/types/level.ts b/lib/types/level.ts new file mode 100644 index 0000000..89cc65e --- /dev/null +++ b/lib/types/level.ts @@ -0,0 +1,10 @@ +import { z } from "zod" + +export const ZLevel = z.object({ + id: z.string().cuid2(), + name: z.string(), + pointThreshold: z.string(), // Assuming pointThreshold is stored as a string + repositoryId: z.string().cuid2(), +}) + +export type TLevel = z.infer diff --git a/lib/types/repository.ts b/lib/types/repository.ts new file mode 100644 index 0000000..17a6b98 --- /dev/null +++ b/lib/types/repository.ts @@ -0,0 +1,31 @@ +import { z } from "zod" + +import { ZApiKey } from "./apiKey" + +export const ZRepository = z.object({ + id: z.string().cuid2(), + name: z.string(), + description: z.string().nullable(), + isPrivate: z.boolean(), + createdAt: z.date(), + updatedAt: z.date(), + ownerId: z.string().cuid2(), + ownerType: z.enum(["ORGANIZATION", "USER"]), + userId: z.string().cuid2(), + apiKeys: z.array(ZApiKey), + // Include other fields if needed +}) + +export type TRepository = z.infer + +export const ZRepositoryCreateInput = z.object({ + name: z.string(), + description: z.string().optional(), + isPrivate: z.boolean().optional(), + ownerId: z.string().cuid2(), + ownerType: z.enum(["ORGANIZATION", "USER"]), + userId: z.string().cuid2(), + // Include other fields required for creation +}) + +export type TRepositoryCreateInput = z.infer diff --git a/pages/api/apiHelper.ts b/pages/api/apiHelper.ts new file mode 100644 index 0000000..d9533c7 --- /dev/null +++ b/pages/api/apiHelper.ts @@ -0,0 +1,22 @@ +import { createHash } from "crypto" +import { NextApiRequest, NextApiResponse } from "next" +import type { Session } from "next-auth" +import { getServerSession } from "next-auth" + +import { authOptions } from "@/lib/auth" + +export const hashApiKey = (key: string): string => + createHash("sha256").update(key).digest("hex") +export const getSessionUser = async ( + req?: NextApiRequest, + res?: NextApiResponse +) => { + // check for session (browser usage) + let session: Session | null + if (req && res) { + session = await getServerSession(req, res, authOptions) + } else { + session = await getServerSession(authOptions) + } + if (session && "user" in session) return session.user +} diff --git a/prisma/migrations/20240119111335_add_api_key/migration.sql b/prisma/migrations/20240119111335_add_api_key/migration.sql new file mode 100644 index 0000000..27cc4bb --- /dev/null +++ b/prisma/migrations/20240119111335_add_api_key/migration.sql @@ -0,0 +1,80 @@ +/* + Warnings: + + - A unique constraint covering the columns `[userId]` on the table `accounts` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[name,ownerId]` on the table `repositories` will be added. If there are existing duplicate values, this will fail. + - Added the required column `ownerId` to the `repositories` table without a default value. This is not possible if the table is not empty. + - Added the required column `ownerType` to the `repositories` table without a default value. This is not possible if the table is not empty. + - Added the required column `userId` to the `repositories` table without a default value. This is not possible if the table is not empty. + - Added the required column `accountId` to the `users` table without a default value. This is not possible if the table is not empty. + - Added the required column `organizationId` to the `users` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "OwnerType" AS ENUM ('ORGANIZATION', 'USER'); + +-- DropIndex +DROP INDEX "repositories_name_key"; + +-- AlterTable +ALTER TABLE "repositories" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "description" TEXT, +ADD COLUMN "isPrivate" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "ownerId" TEXT NOT NULL, +ADD COLUMN "ownerType" "OwnerType" NOT NULL, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "userId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "users" ADD COLUMN "accountId" TEXT NOT NULL, +ADD COLUMN "organizationId" TEXT NOT NULL; + +-- CreateTable +CREATE TABLE "organizations" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "organizations_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ApiKey" ( + "id" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "lastUsedAt" TIMESTAMP(3), + "label" TEXT, + "hashedKey" TEXT NOT NULL, + "respositoryId" TEXT NOT NULL, + + CONSTRAINT "ApiKey_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "organizations_name_key" ON "organizations"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "ApiKey_id_key" ON "ApiKey"("id"); + +-- CreateIndex +CREATE INDEX "ApiKey_respositoryId_idx" ON "ApiKey"("respositoryId"); + +-- CreateIndex +CREATE UNIQUE INDEX "accounts_userId_key" ON "accounts"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "repositories_name_ownerId_key" ON "repositories"("name", "ownerId"); + +-- AddForeignKey +ALTER TABLE "repositories" ADD CONSTRAINT "repositories_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "organizations"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "repositories" ADD CONSTRAINT "repositories_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "organizations"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ApiKey" ADD CONSTRAINT "ApiKey_respositoryId_fkey" FOREIGN KEY ("respositoryId") REFERENCES "repositories"("id") ON DELETE CASCADE ON UPDATE CASCADE; From 9e9f937c480a1efe2db39b335120eb03a056c6c8 Mon Sep 17 00:00:00 2001 From: Dhruwang Date: Fri, 19 Jan 2024 18:54:42 +0530 Subject: [PATCH 2/8] added types, services and repository api route --- app/api/auth.ts | 11 +++ app/api/me/route.ts | 40 ---------- app/api/repository/route.ts | 54 +++++++++++++ lib/apikey.ts | 23 +++++- lib/types/installation.ts | 16 ++++ lib/types/level.ts | 2 +- lib/types/membership.ts | 16 ++++ lib/types/pointTransaction.ts | 19 +++++ lib/types/repository.ts | 33 ++++---- lib/types/session.ts | 14 ++++ lib/types/user.ts | 24 ++++++ .../20240119111335_add_api_key/migration.sql | 80 ------------------- 12 files changed, 195 insertions(+), 137 deletions(-) create mode 100644 app/api/auth.ts delete mode 100644 app/api/me/route.ts create mode 100644 app/api/repository/route.ts create mode 100644 lib/types/installation.ts create mode 100644 lib/types/membership.ts create mode 100644 lib/types/pointTransaction.ts create mode 100644 lib/types/session.ts create mode 100644 lib/types/user.ts delete mode 100644 prisma/migrations/20240119111335_add_api_key/migration.sql diff --git a/app/api/auth.ts b/app/api/auth.ts new file mode 100644 index 0000000..6448f8e --- /dev/null +++ b/app/api/auth.ts @@ -0,0 +1,11 @@ +import { getApiKeyFromKey } from "@/lib/apikey" + +export async function isApiKeyValid(request: Request): Promise { + const apiKey = request.headers.get("x-api-key") + if (!apiKey) { + return false + } + + const apiKeyData = await getApiKeyFromKey(apiKey) + return Boolean(apiKeyData) +} diff --git a/app/api/me/route.ts b/app/api/me/route.ts deleted file mode 100644 index 5ab729a..0000000 --- a/app/api/me/route.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { headers } from "next/headers" -import { NextResponse } from "next/server" - -import { hashApiKey } from "@/lib/apikey" -import { db } from "@/lib/db" - -export async function GET() { - const headersList = headers() - const apiKey = headersList.get("x-api-key") - if (apiKey) { - const apiKeyData = await db.apiKey.findUnique({ - where: { - hashedKey: hashApiKey(apiKey), - }, - select: { - environment: { - select: { - id: true, - createdAt: true, - updatedAt: true, - type: true, - product: { - select: { - id: true, - name: true, - }, - }, - widgetSetupCompleted: true, - }, - }, - }, - }) - if (!apiKeyData) { - return new Response("Not authenticated", { - status: 401, - }) - } - return NextResponse.json(apiKeyData.environment) - } -} diff --git a/app/api/repository/route.ts b/app/api/repository/route.ts new file mode 100644 index 0000000..97c03ff --- /dev/null +++ b/app/api/repository/route.ts @@ -0,0 +1,54 @@ +// pages/api/repository.js +import { db } from "@/lib/db" + +import { isApiKeyValid } from "../auth" + +export async function GET(request) { + if (!isApiKeyValid(request)) return + const { searchParams } = new URL(request.url) + const repositoryId = searchParams.get("id") + + 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, + }) +} + +//this route is for teting purpose +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 }) + } +} diff --git a/lib/apikey.ts b/lib/apikey.ts index 4241503..d5fc18d 100644 --- a/lib/apikey.ts +++ b/lib/apikey.ts @@ -6,6 +6,8 @@ import { db } from "@/lib/db" import { TApiKey, TApiKeyCreateInput } from "./types/apiKey" +export const getHash = (key: string): string => + createHash("sha256").update(key).digest("hex") export const getApiKey = async (apiKeyId: string): Promise => { try { const apiKeyData = await db.apiKey.findUnique({ @@ -53,7 +55,6 @@ export async function createApiKey( respositoryId: { connect: { id: respositoryId } }, }, }) - console.log({ ...result, apiKey: key }) return { ...result, apiKey: key } } catch (error) { throw error @@ -73,3 +74,23 @@ export const deleteApiKey = async (id: string): Promise => { throw error } } + +export const getApiKeyFromKey = async ( + apiKey: string +): Promise => { + if (!apiKey) { + throw new Error("API key required") + } + const hashedKey = getHash(apiKey) + try { + const apiKeyData = await db.apiKey.findUnique({ + where: { + hashedKey, + }, + }) + + return apiKeyData + } catch (error) { + throw error + } +} diff --git a/lib/types/installation.ts b/lib/types/installation.ts new file mode 100644 index 0000000..6331daa --- /dev/null +++ b/lib/types/installation.ts @@ -0,0 +1,16 @@ +import { z } from "zod" + +import { ZMembership } from "./membership" +import { ZRepository } from "./repository" + +const InstallationTypeEnum = z.enum(["TYPE1", "TYPE2"]) + +export const ZInstallation = z.object({ + id: z.string().cuid2(), + githubId: z.number().int().nonnegative(), + type: InstallationTypeEnum, + memberships: z.array(ZMembership), + repositories: z.array(ZRepository), +}) + +export type TInstallation = z.infer diff --git a/lib/types/level.ts b/lib/types/level.ts index 89cc65e..5a48607 100644 --- a/lib/types/level.ts +++ b/lib/types/level.ts @@ -3,7 +3,7 @@ import { z } from "zod" export const ZLevel = z.object({ id: z.string().cuid2(), name: z.string(), - pointThreshold: z.string(), // Assuming pointThreshold is stored as a string + pointThreshold: z.string(), repositoryId: z.string().cuid2(), }) diff --git a/lib/types/membership.ts b/lib/types/membership.ts new file mode 100644 index 0000000..3fd69d3 --- /dev/null +++ b/lib/types/membership.ts @@ -0,0 +1,16 @@ +import { z } from "zod" + +import { ZInstallation } from "./installation" +import { ZUser } from "./user" + +const MembershipRoleEnum = z.enum(["owner", "member"]) + +export const ZMembership = z.object({ + installationId: z.string().cuid2(), + userId: z.string().cuid2(), + role: MembershipRoleEnum, + installation: ZInstallation, + user: ZUser, +}) + +export type TMembership = z.infer diff --git a/lib/types/pointTransaction.ts b/lib/types/pointTransaction.ts new file mode 100644 index 0000000..eda2edb --- /dev/null +++ b/lib/types/pointTransaction.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +import { ZRepository } from "./repository" +import { ZUser } from "./user" + +export const ZPointTransaction = z.object({ + id: z.string().cuid2(), + points: z.number().int(), + description: z.string(), + url: z.string().url().optional(), + userId: z.string().cuid2(), + repositoryId: z.string().cuid2(), + createdAt: z.date(), + updatedAt: z.date(), + user: ZUser, + repository: ZRepository, +}) + +export type TPointTransaction = z.infer diff --git a/lib/types/repository.ts b/lib/types/repository.ts index 17a6b98..e82b94e 100644 --- a/lib/types/repository.ts +++ b/lib/types/repository.ts @@ -1,31 +1,34 @@ import { z } from "zod" -import { ZApiKey } from "./apiKey" +import { ZInstallation } from "./installation" +import { ZLevel } from "./level" +import { ZPointTransaction } from "./pointTransaction" export const ZRepository = z.object({ id: z.string().cuid2(), + githubId: z.number().int().nonnegative(), name: z.string(), - description: z.string().nullable(), - isPrivate: z.boolean(), - createdAt: z.date(), - updatedAt: z.date(), - ownerId: z.string().cuid2(), - ownerType: z.enum(["ORGANIZATION", "USER"]), - userId: z.string().cuid2(), - apiKeys: z.array(ZApiKey), - // Include other fields if needed + description: z.string().optional(), + homepage: z.string().url().optional(), + topics: z.array(z.string()), + default_branch: z.string(), + installationId: z.string().cuid2(), + levels: z.array(ZLevel), + pointTransactions: z.array(ZPointTransaction), + installation: ZInstallation, }) export type TRepository = z.infer export const ZRepositoryCreateInput = z.object({ + githubId: z.number().int().nonnegative(), name: z.string(), description: z.string().optional(), - isPrivate: z.boolean().optional(), - ownerId: z.string().cuid2(), - ownerType: z.enum(["ORGANIZATION", "USER"]), - userId: z.string().cuid2(), - // Include other fields required for creation + homepage: z.string().url().optional(), + topics: z.array(z.string()), + default_branch: z.string(), + installationId: z.string().cuid2(), + levels: z.union([z.array(z.unknown()), z.record(z.unknown())]).optional(), }) export type TRepositoryCreateInput = z.infer diff --git a/lib/types/session.ts b/lib/types/session.ts new file mode 100644 index 0000000..b71d077 --- /dev/null +++ b/lib/types/session.ts @@ -0,0 +1,14 @@ +import { z } from "zod" + +import { ZUser } from "./user" + +export const ZSession = z.object({ + id: z.string().cuid2(), + sessionToken: z.string(), + userId: z.string().cuid2(), + expires: z.date(), + + user: ZUser.optional(), +}) + +export type TSession = z.infer diff --git a/lib/types/user.ts b/lib/types/user.ts new file mode 100644 index 0000000..d082d0e --- /dev/null +++ b/lib/types/user.ts @@ -0,0 +1,24 @@ +import { z } from "zod" + +import { ZAccount } from "./account" +import { ZMembership } from "./membership" +import { ZPointTransaction } from "./pointTransaction" +import { ZSession } from "./session" + +export const ZUser = z.object({ + id: z.string().cuid2(), + githubId: z.number().int().nonnegative().optional(), + name: z.string().optional(), + email: z.string().email().optional(), + emailVerified: z.date().optional(), + image: z.string().url().optional(), + address: z.string().optional(), + createdAt: z.date(), + updatedAt: z.date(), + accounts: z.array(ZAccount), + sessions: z.array(ZSession), + pointTransactions: z.array(ZPointTransaction), + memberships: z.array(ZMembership), +}) + +export type TUser = z.infer diff --git a/prisma/migrations/20240119111335_add_api_key/migration.sql b/prisma/migrations/20240119111335_add_api_key/migration.sql deleted file mode 100644 index 27cc4bb..0000000 --- a/prisma/migrations/20240119111335_add_api_key/migration.sql +++ /dev/null @@ -1,80 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[userId]` on the table `accounts` will be added. If there are existing duplicate values, this will fail. - - A unique constraint covering the columns `[name,ownerId]` on the table `repositories` will be added. If there are existing duplicate values, this will fail. - - Added the required column `ownerId` to the `repositories` table without a default value. This is not possible if the table is not empty. - - Added the required column `ownerType` to the `repositories` table without a default value. This is not possible if the table is not empty. - - Added the required column `userId` to the `repositories` table without a default value. This is not possible if the table is not empty. - - Added the required column `accountId` to the `users` table without a default value. This is not possible if the table is not empty. - - Added the required column `organizationId` to the `users` table without a default value. This is not possible if the table is not empty. - -*/ --- CreateEnum -CREATE TYPE "OwnerType" AS ENUM ('ORGANIZATION', 'USER'); - --- DropIndex -DROP INDEX "repositories_name_key"; - --- AlterTable -ALTER TABLE "repositories" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, -ADD COLUMN "description" TEXT, -ADD COLUMN "isPrivate" BOOLEAN NOT NULL DEFAULT false, -ADD COLUMN "ownerId" TEXT NOT NULL, -ADD COLUMN "ownerType" "OwnerType" NOT NULL, -ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, -ADD COLUMN "userId" TEXT NOT NULL; - --- AlterTable -ALTER TABLE "users" ADD COLUMN "accountId" TEXT NOT NULL, -ADD COLUMN "organizationId" TEXT NOT NULL; - --- CreateTable -CREATE TABLE "organizations" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "organizations_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "ApiKey" ( - "id" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "lastUsedAt" TIMESTAMP(3), - "label" TEXT, - "hashedKey" TEXT NOT NULL, - "respositoryId" TEXT NOT NULL, - - CONSTRAINT "ApiKey_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "organizations_name_key" ON "organizations"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "ApiKey_id_key" ON "ApiKey"("id"); - --- CreateIndex -CREATE INDEX "ApiKey_respositoryId_idx" ON "ApiKey"("respositoryId"); - --- CreateIndex -CREATE UNIQUE INDEX "accounts_userId_key" ON "accounts"("userId"); - --- CreateIndex -CREATE UNIQUE INDEX "repositories_name_ownerId_key" ON "repositories"("name", "ownerId"); - --- AddForeignKey -ALTER TABLE "repositories" ADD CONSTRAINT "repositories_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "organizations"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "repositories" ADD CONSTRAINT "repositories_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "users" ADD CONSTRAINT "users_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "organizations"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "ApiKey" ADD CONSTRAINT "ApiKey_respositoryId_fkey" FOREIGN KEY ("respositoryId") REFERENCES "repositories"("id") ON DELETE CASCADE ON UPDATE CASCADE; From da1a8c7e8841d0cd3b596b7e5ef240c3d6979359 Mon Sep 17 00:00:00 2001 From: Dhruwang Date: Fri, 19 Jan 2024 19:49:53 +0530 Subject: [PATCH 3/8] resolved authorization issues --- app/(dashboard)/dashboard/settings/actions.ts | 7 +-- app/api/auth.ts | 9 ++-- .../repository/{ => [repositoryId]}/route.ts | 11 ++--- lib/repository.ts | 46 +++++++++++-------- lib/types/apiKey.ts | 2 +- lib/types/session.ts | 14 ------ lib/types/user.ts | 2 - prisma/schema.prisma | 2 +- 8 files changed, 45 insertions(+), 48 deletions(-) rename app/api/repository/{ => [repositoryId]}/route.ts (81%) delete mode 100644 lib/types/session.ts diff --git a/app/(dashboard)/dashboard/settings/actions.ts b/app/(dashboard)/dashboard/settings/actions.ts index c67e52b..42f4908 100644 --- a/app/(dashboard)/dashboard/settings/actions.ts +++ b/app/(dashboard)/dashboard/settings/actions.ts @@ -4,14 +4,15 @@ import { getServerSession } from "next-auth" import { createApiKey } from "@/lib/apikey" import { authOptions } from "@/lib/auth" +import { userHasAccessToRepository } from "@/lib/repository" import { TApiKeyCreateInput } from "@/lib/types/apiKey" export async function createApiKeyAction( - accountId: string, + repositoryId: string, apiKeyData: TApiKeyCreateInput ) { const session = await getServerSession(authOptions) if (!session) return - - return await createApiKey(accountId, apiKeyData) + if (!userHasAccessToRepository) return + return await createApiKey(repositoryId, apiKeyData) } diff --git a/app/api/auth.ts b/app/api/auth.ts index 6448f8e..de556e8 100644 --- a/app/api/auth.ts +++ b/app/api/auth.ts @@ -1,11 +1,14 @@ import { getApiKeyFromKey } from "@/lib/apikey" +import { TApiKey } from "@/lib/types/apiKey" -export async function isApiKeyValid(request: Request): Promise { +export async function isApiKeyValid(request: Request): Promise { const apiKey = request.headers.get("x-api-key") if (!apiKey) { - return false + return null } const apiKeyData = await getApiKeyFromKey(apiKey) - return Boolean(apiKeyData) + if (apiKeyData) { + return apiKeyData + } else return null } diff --git a/app/api/repository/route.ts b/app/api/repository/[repositoryId]/route.ts similarity index 81% rename from app/api/repository/route.ts rename to app/api/repository/[repositoryId]/route.ts index 97c03ff..5945767 100644 --- a/app/api/repository/route.ts +++ b/app/api/repository/[repositoryId]/route.ts @@ -1,12 +1,12 @@ // pages/api/repository.js import { db } from "@/lib/db" -import { isApiKeyValid } from "../auth" +import { isApiKeyValid } from "../../auth" -export async function GET(request) { - if (!isApiKeyValid(request)) return - const { searchParams } = new URL(request.url) - const repositoryId = searchParams.get("id") +export async function GET(request, { params }) { + const repositoryId = params.respositoryId + const apiKeyData = await isApiKeyValid(request) + if (!apiKeyData || repositoryId === apiKeyData.repositoryId) return if (!repositoryId) { return new Response("Repository ID is required", { status: 400 }) @@ -26,7 +26,6 @@ export async function GET(request) { }) } -//this route is for teting purpose export async function POST(request) { const data = await request.json() diff --git a/lib/repository.ts b/lib/repository.ts index 2b69c68..91c026b 100644 --- a/lib/repository.ts +++ b/lib/repository.ts @@ -3,12 +3,12 @@ import { db } from "@/lib/db" import { TRepository, TRepositoryCreateInput } from "./types/repository" export const getRepository = async ( - repositoryId: string + id: string ): Promise => { try { const repositoryData = await db.repository.findUnique({ where: { - id: repositoryId, + id, }, }) @@ -18,22 +18,6 @@ export const getRepository = async ( } } -export const getRepositories = async ( - ownerId: string -): Promise => { - try { - const repositories = await db.repository.findMany({ - where: { - ownerId, - }, - }) - - return repositories - } catch (error) { - throw error - } -} - export const createRepository = async ( repositoryData: TRepositoryCreateInput ): Promise => { @@ -63,3 +47,29 @@ export const deleteRepository = async ( throw error } } + +export async function userHasAccessToRepository( + userId: string, + repositoryId: string +): Promise { + const repository = await db.repository.findUnique({ + where: { id: repositoryId }, + select: { installation: true }, + }) + + if (!repository || !repository.installation) { + return false // Repository or its installation not found + } + + const installationId = repository.installation.id + + // Check if there is a membership for the user in this installation + const membership = await db.membership.findFirst({ + where: { + userId, + installationId, + }, + }) + + return membership.userId === userId // true if membership exists, false otherwise +} diff --git a/lib/types/apiKey.ts b/lib/types/apiKey.ts index cad7445..36e3b4f 100644 --- a/lib/types/apiKey.ts +++ b/lib/types/apiKey.ts @@ -6,7 +6,7 @@ export const ZApiKey = z.object({ lastUsedAt: z.date().nullable(), label: z.string().nullable(), hashedKey: z.string(), - accountId: z.string().cuid2(), + repositoryId: z.string().cuid2(), apiKey: z.string().optional(), }) diff --git a/lib/types/session.ts b/lib/types/session.ts deleted file mode 100644 index b71d077..0000000 --- a/lib/types/session.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { z } from "zod" - -import { ZUser } from "./user" - -export const ZSession = z.object({ - id: z.string().cuid2(), - sessionToken: z.string(), - userId: z.string().cuid2(), - expires: z.date(), - - user: ZUser.optional(), -}) - -export type TSession = z.infer diff --git a/lib/types/user.ts b/lib/types/user.ts index d082d0e..f209e9e 100644 --- a/lib/types/user.ts +++ b/lib/types/user.ts @@ -3,7 +3,6 @@ import { z } from "zod" import { ZAccount } from "./account" import { ZMembership } from "./membership" import { ZPointTransaction } from "./pointTransaction" -import { ZSession } from "./session" export const ZUser = z.object({ id: z.string().cuid2(), @@ -16,7 +15,6 @@ export const ZUser = z.object({ createdAt: z.date(), updatedAt: z.date(), accounts: z.array(ZAccount), - sessions: z.array(ZSession), pointTransactions: z.array(ZPointTransaction), memberships: z.array(ZMembership), }) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 01f1ac3..cecab28 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -84,7 +84,7 @@ model Repository { pointTransactions PointTransaction[] installation Installation @relation(fields: [installationId], references: [id]) - @@map(name: "repositories") + @@map(name: "repositoresries") } enum MembershipRole { From 3b915b63482b8dec69fb3d114c5212a6bba0c351 Mon Sep 17 00:00:00 2001 From: Matthias Nannt Date: Fri, 19 Jan 2024 17:38:37 +0100 Subject: [PATCH 4/8] various changes --- app/(auth)/login/page.tsx | 26 +- app/(auth)/register/page.tsx | 8 +- app/(dashboard)/dashboard/layout.tsx | 2 +- app/(dashboard)/dashboard/settings/actions.ts | 14 +- app/(marketing)/layout.tsx | 2 +- app/(marketing)/page.tsx | 52 ++-- app/api/auth.ts | 8 +- app/api/repository/[repositoryId]/route.ts | 6 +- app/layout.tsx | 2 +- components/empty-placeholder.tsx | 8 +- components/header.tsx | 2 +- components/main-nav.tsx | 2 +- components/mobile-nav.tsx | 4 +- components/mode-toggle.tsx | 8 +- components/nav.tsx | 2 +- components/tailwind-indicator.tsx | 2 +- components/ui/alert-dialog.tsx | 6 +- components/ui/avatar.tsx | 6 +- components/ui/card.tsx | 4 +- components/ui/dropdown-menu.tsx | 24 +- components/ui/input.tsx | 2 +- components/ui/skeleton.tsx | 2 +- components/ui/toast.tsx | 20 +- components/user-account-nav.tsx | 4 +- components/user-auth-form.tsx | 4 +- components/user-avatar.tsx | 2 +- components/user-name-form.tsx | 4 +- lib/apikey.ts | 96 ------- lib/repository.ts | 6 +- lib/types/account.ts | 37 --- lib/types/apiKey.ts | 18 -- lib/types/installation.ts | 16 -- lib/types/level.ts | 10 - lib/types/membership.ts | 16 -- lib/types/pointTransaction.ts | 19 -- lib/types/repository.ts | 34 --- lib/types/user.ts | 22 -- package.json | 7 +- pnpm-lock.yaml | 248 +++++++++--------- prisma/schema.prisma | 17 +- 40 files changed, 268 insertions(+), 504 deletions(-) delete mode 100644 lib/apikey.ts delete mode 100644 lib/types/account.ts delete mode 100644 lib/types/apiKey.ts delete mode 100644 lib/types/installation.ts delete mode 100644 lib/types/level.ts delete mode 100644 lib/types/membership.ts delete mode 100644 lib/types/pointTransaction.ts delete mode 100644 lib/types/repository.ts delete mode 100644 lib/types/user.ts diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 650e362..6ca6bdd 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -1,20 +1,20 @@ -import { Metadata } from "next"; -import Link from "next/link"; +import { Metadata } from "next" +import Link from "next/link" +import { ChevronLeft } from "lucide-react" -import { cn } from "@/lib/utils"; -import { buttonVariants } from "@/components/ui/button"; -import { UserAuthForm } from "@/components/user-auth-form"; -import { ChevronLeft } from "lucide-react"; -import { Logo } from "@/components/ui/logo"; +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" +import { Logo } from "@/components/ui/logo" +import { UserAuthForm } from "@/components/user-auth-form" export const metadata: Metadata = { title: "Login", description: "Login to your account", -}; +} export default function LoginPage() { return ( -
+
<> - + Back @@ -33,12 +33,12 @@ export default function LoginPage() {

Welcome back

-

+

Use Github to sign in to your account

-

+

- ); + ) } diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx index f5056a5..37984bc 100644 --- a/app/(auth)/register/page.tsx +++ b/app/(auth)/register/page.tsx @@ -12,7 +12,7 @@ export const metadata = { export default function RegisterPage() { return ( -
+
Login -
+
@@ -30,12 +30,12 @@ export default function RegisterPage() {

Create an account

-

+

Use Github to create your account

-

+

By clicking continue, you agree to our{" "} -

+
-
+
- ) + ); } diff --git a/components/repo-selecor.tsx b/components/repo-selecor.tsx index 3bd32ef..0320dda 100644 --- a/components/repo-selecor.tsx +++ b/components/repo-selecor.tsx @@ -1,21 +1,20 @@ -"use client" +"use client"; -import { useToast } from "@/components/ui/use-toast" +import { useToast } from "@/components/ui/use-toast"; export const RepoSelector = ({ repo, selectRepoAction }) => { - const { toast } = useToast() + const { toast } = useToast(); return (
{ - selectRepoAction(repo.id) + selectRepoAction(repo.id); toast({ title: `${repo.name} selected`, description: "Next steps to be built", - }) - }} - > + }); + }}> {repo.name}
- ) -} + ); +}; diff --git a/components/shell.tsx b/components/shell.tsx index ee95821..564a957 100644 --- a/components/shell.tsx +++ b/components/shell.tsx @@ -1,17 +1,12 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; +import * as React from "react"; interface DashboardShellProps extends React.HTMLAttributes {} -export function DashboardShell({ - children, - className, - ...props -}: DashboardShellProps) { +export function DashboardShell({ children, className, ...props }: DashboardShellProps) { return (
{children}
- ) + ); } diff --git a/components/site-footer.tsx b/components/site-footer.tsx index a30a5c2..a890bf9 100644 --- a/components/site-footer.tsx +++ b/components/site-footer.tsx @@ -1,8 +1,7 @@ -import * as React from "react" - -import { siteConfig } from "@/config/site" -import { cn } from "@/lib/utils" -import { ModeToggle } from "@/components/mode-toggle" +import { ModeToggle } from "@/components/mode-toggle"; +import { siteConfig } from "@/config/site"; +import { cn } from "@/lib/utils"; +import * as React from "react"; export function SiteFooter({ className }: React.HTMLAttributes) { return ( @@ -15,8 +14,7 @@ export function SiteFooter({ className }: React.HTMLAttributes) { href={siteConfig.links.twitter} target="_blank" rel="noreferrer" - className="font-medium underline underline-offset-4" - > + className="font-medium underline underline-offset-4"> Formbricks . The source code is available on{" "} @@ -24,8 +22,7 @@ export function SiteFooter({ className }: React.HTMLAttributes) { href={siteConfig.links.github} target="_blank" rel="noreferrer" - className="font-medium underline underline-offset-4" - > + className="font-medium underline underline-offset-4"> GitHub . @@ -34,5 +31,5 @@ export function SiteFooter({ className }: React.HTMLAttributes) {
- ) + ); } diff --git a/components/tailwind-indicator.tsx b/components/tailwind-indicator.tsx index 2251de6..0f6d3bf 100644 --- a/components/tailwind-indicator.tsx +++ b/components/tailwind-indicator.tsx @@ -1,16 +1,14 @@ export function TailwindIndicator() { - if (process.env.NODE_ENV === "production") return null + if (process.env.NODE_ENV === "production") return null; return (
xs
-
- sm -
+
sm
md
lg
xl
2xl
- ) + ); } diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx index b4e8e4f..93d93c0 100644 --- a/components/theme-provider.tsx +++ b/components/theme-provider.tsx @@ -1,8 +1,8 @@ "use client"; -import * as React from "react"; import { ThemeProvider as NextThemesProvider } from "next-themes"; import { ThemeProviderProps } from "next-themes/dist/types"; +import * as React from "react"; export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children}; diff --git a/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx index 1f3a817..510995e 100644 --- a/components/ui/alert-dialog.tsx +++ b/components/ui/alert-dialog.tsx @@ -1,16 +1,15 @@ -"use client" +"use client"; -import * as React from "react" -import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" +import { buttonVariants } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; +import * as React from "react"; -import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" +const AlertDialog = AlertDialogPrimitive.Root; -const AlertDialog = AlertDialogPrimitive.Root +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; -const AlertDialogTrigger = AlertDialogPrimitive.Trigger - -const AlertDialogPortal = AlertDialogPrimitive.Portal +const AlertDialogPortal = AlertDialogPrimitive.Portal; const AlertDialogOverlay = React.forwardRef< React.ElementRef, @@ -18,14 +17,14 @@ const AlertDialogOverlay = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; const AlertDialogContent = React.forwardRef< React.ElementRef, @@ -36,54 +35,35 @@ const AlertDialogContent = React.forwardRef< -)) -AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; -const AlertDialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -AlertDialogHeader.displayName = "AlertDialogHeader" +const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = "AlertDialogHeader"; -const AlertDialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( +const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
-) -AlertDialogFooter.displayName = "AlertDialogFooter" +); +AlertDialogFooter.displayName = "AlertDialogFooter"; const AlertDialogTitle = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; const AlertDialogDescription = React.forwardRef< React.ElementRef, @@ -91,24 +71,19 @@ const AlertDialogDescription = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -AlertDialogDescription.displayName = - AlertDialogPrimitive.Description.displayName +)); +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; const AlertDialogAction = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; const AlertDialogCancel = React.forwardRef< React.ElementRef, @@ -116,15 +91,11 @@ const AlertDialogCancel = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; export { AlertDialog, @@ -138,4 +109,4 @@ export { AlertDialogDescription, AlertDialogAction, AlertDialogCancel, -} +}; diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx index 1cf1283..5b14cb8 100644 --- a/components/ui/avatar.tsx +++ b/components/ui/avatar.tsx @@ -1,9 +1,8 @@ -"use client" +"use client"; -import * as React from "react" -import * as AvatarPrimitive from "@radix-ui/react-avatar" - -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; +import * as React from "react"; const Avatar = React.forwardRef< React.ElementRef, @@ -11,26 +10,19 @@ const Avatar = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -Avatar.displayName = AvatarPrimitive.Root.displayName +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; const AvatarImage = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AvatarImage.displayName = AvatarPrimitive.Image.displayName + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; const AvatarFallback = React.forwardRef< React.ElementRef, @@ -38,13 +30,10 @@ const AvatarFallback = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; -export { Avatar, AvatarImage, AvatarFallback } +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/components/ui/button.tsx b/components/ui/button.tsx index e7c6973..142619d 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -1,8 +1,7 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; +import { Slot } from "@radix-ui/react-slot"; +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; const buttonVariants = cva( "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", @@ -10,12 +9,9 @@ const buttonVariants = cva( variants: { variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: - "border border-input bg-background hover:bg-accent hover:text-accent-foreground", - secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, @@ -31,36 +27,22 @@ const buttonVariants = cva( size: "default", }, } -) +); export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean - href?: string - openInNewTab?: boolean + asChild?: boolean; + href?: string; + openInNewTab?: boolean; } -const Button = React.forwardRef< - HTMLButtonElement | HTMLAnchorElement, - ButtonProps ->( - ( - { - className, - variant, - size, - asChild = false, - href, - openInNewTab = false, - ...props - }, - ref - ) => { - let Comp: React.ElementType = asChild ? Slot : "button" +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, href, openInNewTab = false, ...props }, ref) => { + let Comp: React.ElementType = asChild ? Slot : "button"; if (href) { - Comp = "a" // Use anchor tag if href is provided + Comp = "a"; // Use anchor tag if href is provided } return ( @@ -69,14 +51,12 @@ const Button = React.forwardRef< className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props} - {...(href && openInNewTab - ? { target: "_blank", rel: "noopener noreferrer" } - : {})} + {...(href && openInNewTab ? { target: "_blank", rel: "noopener noreferrer" } : {})} /> - ) + ); } -) +); -Button.displayName = "Button" +Button.displayName = "Button"; -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/components/ui/card.tsx b/components/ui/card.tsx index a26fd5d..6213d88 100644 --- a/components/ui/card.tsx +++ b/components/ui/card.tsx @@ -1,79 +1,52 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -Card.displayName = "Card" - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardTitle.displayName = "CardTitle" - -const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardFooter.displayName = "CardFooter" - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } +import { cn } from "@/lib/utils"; +import * as React from "react"; + +const Card = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( +

+ ) +); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef>( + ({ className, ...props }, ref) => ( +

+ ) +); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef>( + ({ className, ...props }, ref) =>

+); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +); +CardFooter.displayName = "CardFooter"; + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }; diff --git a/components/ui/dropdown-menu.tsx b/components/ui/dropdown-menu.tsx index ca9b16a..62fc185 100644 --- a/components/ui/dropdown-menu.tsx +++ b/components/ui/dropdown-menu.tsx @@ -1,44 +1,41 @@ -"use client" +"use client"; -import * as React from "react" -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" -import { Check, ChevronRight, Circle } from "lucide-react" +import { cn } from "@/lib/utils"; +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { Check, ChevronRight, Circle } from "lucide-react"; +import * as React from "react"; -import { cn } from "@/lib/utils" +const DropdownMenu = DropdownMenuPrimitive.Root; -const DropdownMenu = DropdownMenuPrimitive.Root +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; -const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger +const DropdownMenuGroup = DropdownMenuPrimitive.Group; -const DropdownMenuGroup = DropdownMenuPrimitive.Group +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; -const DropdownMenuPortal = DropdownMenuPrimitive.Portal +const DropdownMenuSub = DropdownMenuPrimitive.Sub; -const DropdownMenuSub = DropdownMenuPrimitive.Sub - -const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; const DropdownMenuSubTrigger = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, children, ...props }, ref) => ( + {...props}> {children} -)) -DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName +)); +DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName; const DropdownMenuSubContent = React.forwardRef< React.ElementRef, @@ -47,14 +44,13 @@ const DropdownMenuSubContent = React.forwardRef< -)) -DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName +)); +DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName; const DropdownMenuContent = React.forwardRef< React.ElementRef, @@ -65,32 +61,32 @@ const DropdownMenuContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md", + "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className )} {...props} /> -)) -DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; const DropdownMenuItem = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, ...props }, ref) => ( -)) -DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; const DropdownMenuCheckboxItem = React.forwardRef< React.ElementRef, @@ -99,12 +95,11 @@ const DropdownMenuCheckboxItem = React.forwardRef< + {...props}> @@ -112,9 +107,8 @@ const DropdownMenuCheckboxItem = React.forwardRef< {children} -)) -DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName +)); +DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName; const DropdownMenuRadioItem = React.forwardRef< React.ElementRef, @@ -123,11 +117,10 @@ const DropdownMenuRadioItem = React.forwardRef< + {...props}> @@ -135,26 +128,22 @@ const DropdownMenuRadioItem = React.forwardRef< {children} -)) -DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName +)); +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; const DropdownMenuLabel = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, ...props }, ref) => ( -)) -DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; const DropdownMenuSeparator = React.forwardRef< React.ElementRef, @@ -162,24 +151,16 @@ const DropdownMenuSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName - -const DropdownMenuShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ) -} -DropdownMenuShortcut.displayName = "DropdownMenuShortcut" +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; + +const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes) => { + return ; +}; +DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; export { DropdownMenu, @@ -197,4 +178,4 @@ export { DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuRadioGroup, -} +}; diff --git a/components/ui/input.tsx b/components/ui/input.tsx index aba4071..fc0d342 100644 --- a/components/ui/input.tsx +++ b/components/ui/input.tsx @@ -1,25 +1,21 @@ -import * as React from "react" +import { cn } from "@/lib/utils"; +import * as React from "react"; -import { cn } from "@/lib/utils" +export interface InputProps extends React.InputHTMLAttributes {} -export interface InputProps - extends React.InputHTMLAttributes {} +const Input = React.forwardRef(({ className, type, ...props }, ref) => { + return ( + + ); +}); +Input.displayName = "Input"; -const Input = React.forwardRef( - ({ className, type, ...props }, ref) => { - return ( - - ) - } -) -Input.displayName = "Input" - -export { Input } +export { Input }; diff --git a/components/ui/label.tsx b/components/ui/label.tsx index 5341821..8b42d4a 100644 --- a/components/ui/label.tsx +++ b/components/ui/label.tsx @@ -1,26 +1,20 @@ -"use client" +"use client"; -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; const labelVariants = cva( "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" -) +); const Label = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps + React.ComponentPropsWithoutRef & VariantProps >(({ className, ...props }, ref) => ( - -)) -Label.displayName = LabelPrimitive.Root.displayName + +)); +Label.displayName = LabelPrimitive.Root.displayName; -export { Label } +export { Label }; diff --git a/components/ui/logo.tsx b/components/ui/logo.tsx index cf62516..63053bd 100644 --- a/components/ui/logo.tsx +++ b/components/ui/logo.tsx @@ -1,15 +1,15 @@ -import Image from "next/image" -import Link from "next/link" +import Image from "next/image"; +import Link from "next/link"; -import OSSGGLogoDark from "../../app/oss-gg-logo-dark.png" -import OSSGGLogoLight from "../../app/oss-gg-logo.png" +import OSSGGLogoDark from "../../app/oss-gg-logo-dark.png"; +import OSSGGLogoLight from "../../app/oss-gg-logo.png"; export const Logo = ({ theme = "light" }) => { - const logoSrc = theme === "dark" ? OSSGGLogoDark : OSSGGLogoLight + const logoSrc = theme === "dark" ? OSSGGLogoDark : OSSGGLogoLight; return ( gamify open source contributions - ) -} + ); +}; diff --git a/components/ui/skeleton.tsx b/components/ui/skeleton.tsx index cb541e0..6690a13 100644 --- a/components/ui/skeleton.tsx +++ b/components/ui/skeleton.tsx @@ -1,15 +1,7 @@ -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; -function Skeleton({ - className, - ...props -}: React.HTMLAttributes) { - return ( -
- ) +function Skeleton({ className, ...props }: React.HTMLAttributes) { + return
; } -export { Skeleton } +export { Skeleton }; diff --git a/components/ui/toast.tsx b/components/ui/toast.tsx index 31397f0..419a24d 100644 --- a/components/ui/toast.tsx +++ b/components/ui/toast.tsx @@ -1,11 +1,10 @@ -import * as React from "react" -import * as ToastPrimitives from "@radix-ui/react-toast" -import { cva, type VariantProps } from "class-variance-authority" -import { X } from "lucide-react" +import { cn } from "@/lib/utils"; +import * as ToastPrimitives from "@radix-ui/react-toast"; +import { type VariantProps, cva } from "class-variance-authority"; +import { X } from "lucide-react"; +import * as React from "react"; -import { cn } from "@/lib/utils" - -const ToastProvider = ToastPrimitives.Provider +const ToastProvider = ToastPrimitives.Provider; const ToastViewport = React.forwardRef< React.ElementRef, @@ -19,8 +18,8 @@ const ToastViewport = React.forwardRef< )} {...props} /> -)) -ToastViewport.displayName = ToastPrimitives.Viewport.displayName +)); +ToastViewport.displayName = ToastPrimitives.Viewport.displayName; const toastVariants = cva( "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", @@ -28,30 +27,22 @@ const toastVariants = cva( variants: { variant: { default: "border bg-background text-foreground", - destructive: - "destructive group border-destructive bg-destructive text-destructive-foreground", + destructive: "destructive group border-destructive bg-destructive text-destructive-foreground", }, }, defaultVariants: { variant: "default", }, } -) +); const Toast = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps + React.ComponentPropsWithoutRef & VariantProps >(({ className, variant, ...props }, ref) => { - return ( - - ) -}) -Toast.displayName = ToastPrimitives.Root.displayName + return ; +}); +Toast.displayName = ToastPrimitives.Root.displayName; const ToastAction = React.forwardRef< React.ElementRef, @@ -60,13 +51,13 @@ const ToastAction = React.forwardRef< -)) -ToastAction.displayName = ToastPrimitives.Action.displayName +)); +ToastAction.displayName = ToastPrimitives.Action.displayName; const ToastClose = React.forwardRef< React.ElementRef, @@ -75,44 +66,35 @@ const ToastClose = React.forwardRef< + {...props}> -)) -ToastClose.displayName = ToastPrimitives.Close.displayName +)); +ToastClose.displayName = ToastPrimitives.Close.displayName; const ToastTitle = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -ToastTitle.displayName = ToastPrimitives.Title.displayName + +)); +ToastTitle.displayName = ToastPrimitives.Title.displayName; const ToastDescription = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -ToastDescription.displayName = ToastPrimitives.Description.displayName + +)); +ToastDescription.displayName = ToastPrimitives.Description.displayName; -type ToastProps = React.ComponentPropsWithoutRef +type ToastProps = React.ComponentPropsWithoutRef; -type ToastActionElement = React.ReactElement +type ToastActionElement = React.ReactElement; export { Toast, @@ -124,4 +106,4 @@ export { ToastViewport, type ToastActionElement, type ToastProps, -} +}; diff --git a/components/ui/toaster.tsx b/components/ui/toaster.tsx index e223385..3aa665d 100644 --- a/components/ui/toaster.tsx +++ b/components/ui/toaster.tsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { Toast, @@ -7,11 +7,11 @@ import { ToastProvider, ToastTitle, ToastViewport, -} from "@/components/ui/toast" -import { useToast } from "@/components/ui/use-toast" +} from "@/components/ui/toast"; +import { useToast } from "@/components/ui/use-toast"; export function Toaster() { - const { toasts } = useToast() + const { toasts } = useToast(); return ( @@ -20,16 +20,14 @@ export function Toaster() {
{title && {title}} - {description && ( - {description} - )} + {description && {description}}
{action}
- ) + ); })}
- ) + ); } diff --git a/components/ui/use-toast.ts b/components/ui/use-toast.ts index 1671307..d606687 100644 --- a/components/ui/use-toast.ts +++ b/components/ui/use-toast.ts @@ -1,76 +1,72 @@ // Inspired by react-hot-toast library -import * as React from "react" +import type { ToastActionElement, ToastProps } from "@/components/ui/toast"; +import * as React from "react"; -import type { - ToastActionElement, - ToastProps, -} from "@/components/ui/toast" - -const TOAST_LIMIT = 1 -const TOAST_REMOVE_DELAY = 1000000 +const TOAST_LIMIT = 1; +const TOAST_REMOVE_DELAY = 1000000; type ToasterToast = ToastProps & { - id: string - title?: React.ReactNode - description?: React.ReactNode - action?: ToastActionElement -} + id: string; + title?: React.ReactNode; + description?: React.ReactNode; + action?: ToastActionElement; +}; const actionTypes = { ADD_TOAST: "ADD_TOAST", UPDATE_TOAST: "UPDATE_TOAST", DISMISS_TOAST: "DISMISS_TOAST", REMOVE_TOAST: "REMOVE_TOAST", -} as const +} as const; -let count = 0 +let count = 0; function genId() { - count = (count + 1) % Number.MAX_SAFE_INTEGER - return count.toString() + count = (count + 1) % Number.MAX_SAFE_INTEGER; + return count.toString(); } -type ActionType = typeof actionTypes +type ActionType = typeof actionTypes; type Action = | { - type: ActionType["ADD_TOAST"] - toast: ToasterToast + type: ActionType["ADD_TOAST"]; + toast: ToasterToast; } | { - type: ActionType["UPDATE_TOAST"] - toast: Partial + type: ActionType["UPDATE_TOAST"]; + toast: Partial; } | { - type: ActionType["DISMISS_TOAST"] - toastId?: ToasterToast["id"] + type: ActionType["DISMISS_TOAST"]; + toastId?: ToasterToast["id"]; } | { - type: ActionType["REMOVE_TOAST"] - toastId?: ToasterToast["id"] - } + type: ActionType["REMOVE_TOAST"]; + toastId?: ToasterToast["id"]; + }; interface State { - toasts: ToasterToast[] + toasts: ToasterToast[]; } -const toastTimeouts = new Map>() +const toastTimeouts = new Map>(); const addToRemoveQueue = (toastId: string) => { if (toastTimeouts.has(toastId)) { - return + return; } const timeout = setTimeout(() => { - toastTimeouts.delete(toastId) + toastTimeouts.delete(toastId); dispatch({ type: "REMOVE_TOAST", toastId: toastId, - }) - }, TOAST_REMOVE_DELAY) + }); + }, TOAST_REMOVE_DELAY); - toastTimeouts.set(toastId, timeout) -} + toastTimeouts.set(toastId, timeout); +}; export const reducer = (state: State, action: Action): State => { switch (action.type) { @@ -78,27 +74,25 @@ export const reducer = (state: State, action: Action): State => { return { ...state, toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), - } + }; case "UPDATE_TOAST": return { ...state, - toasts: state.toasts.map((t) => - t.id === action.toast.id ? { ...t, ...action.toast } : t - ), - } + toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)), + }; case "DISMISS_TOAST": { - const { toastId } = action + const { toastId } = action; // ! Side effects ! - This could be extracted into a dismissToast() action, // but I'll keep it here for simplicity if (toastId) { - addToRemoveQueue(toastId) + addToRemoveQueue(toastId); } else { state.toasts.forEach((toast) => { - addToRemoveQueue(toast.id) - }) + addToRemoveQueue(toast.id); + }); } return { @@ -111,44 +105,44 @@ export const reducer = (state: State, action: Action): State => { } : t ), - } + }; } case "REMOVE_TOAST": if (action.toastId === undefined) { return { ...state, toasts: [], - } + }; } return { ...state, toasts: state.toasts.filter((t) => t.id !== action.toastId), - } + }; } -} +}; -const listeners: Array<(state: State) => void> = [] +const listeners: Array<(state: State) => void> = []; -let memoryState: State = { toasts: [] } +let memoryState: State = { toasts: [] }; function dispatch(action: Action) { - memoryState = reducer(memoryState, action) + memoryState = reducer(memoryState, action); listeners.forEach((listener) => { - listener(memoryState) - }) + listener(memoryState); + }); } -type Toast = Omit +type Toast = Omit; function toast({ ...props }: Toast) { - const id = genId() + const id = genId(); const update = (props: ToasterToast) => dispatch({ type: "UPDATE_TOAST", toast: { ...props, id }, - }) - const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) + }); + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }); dispatch({ type: "ADD_TOAST", @@ -157,36 +151,36 @@ function toast({ ...props }: Toast) { id, open: true, onOpenChange: (open) => { - if (!open) dismiss() + if (!open) dismiss(); }, }, - }) + }); return { id: id, dismiss, update, - } + }; } function useToast() { - const [state, setState] = React.useState(memoryState) + const [state, setState] = React.useState(memoryState); React.useEffect(() => { - listeners.push(setState) + listeners.push(setState); return () => { - const index = listeners.indexOf(setState) + const index = listeners.indexOf(setState); if (index > -1) { - listeners.splice(index, 1) + listeners.splice(index, 1); } - } - }, [state]) + }; + }, [state]); return { ...state, toast, dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), - } + }; } -export { useToast, toast } +export { useToast, toast }; diff --git a/components/user-account-nav.tsx b/components/user-account-nav.tsx index baefcf3..5a28393 100644 --- a/components/user-account-nav.tsx +++ b/components/user-account-nav.tsx @@ -1,22 +1,21 @@ -"use client" - -import { User } from "next-auth" -import { signOut } from "next-auth/react" +"use client"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { UserAvatar } from "@/components/user-avatar" +} from "@/components/ui/dropdown-menu"; +import { UserAvatar } from "@/components/user-avatar"; +import { User } from "next-auth"; +import { signOut } from "next-auth/react"; interface UserAccountNavProps { user: { - name: User["name"] - avatarUrl: User["image"] - email?: User["email"] - } + name: User["name"]; + avatarUrl: User["image"]; + email?: User["email"]; + }; } const topNavigation = [ @@ -24,12 +23,12 @@ const topNavigation = [ { label: "Enroll to play", url: "/enroll" }, { label: "Settings", url: "/settings" }, { label: "Your profile", url: "/", target: "_blank" }, -] +]; const bottomNavigation = [ { label: "What is oss.gg?", url: "https://oss.gg", target: "_blank" }, { label: "Help build oss.gg", url: "/contribute" }, -] +]; export function UserAccountNav({ user }: UserAccountNavProps) { return ( @@ -44,25 +43,20 @@ export function UserAccountNav({ user }: UserAccountNavProps) {
{user.name &&

{user.name}

} - {user.email && ( -

- {user.email} -

- )} + {user.email &&

{user.email}

}
{ - event.preventDefault() + event.preventDefault(); signOut({ callbackUrl: `${window.location.origin}/login`, - }) - }} - > + }); + }}> Sign out - ) + ); } diff --git a/components/user-auth-form.tsx b/components/user-auth-form.tsx index 4827b39..43d5d1b 100644 --- a/components/user-auth-form.tsx +++ b/components/user-auth-form.tsx @@ -1,11 +1,10 @@ "use client"; -import { signIn } from "next-auth/react"; -import * as React from "react"; - import { buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import { Loader2 } from "lucide-react"; +import { signIn } from "next-auth/react"; +import * as React from "react"; import { FaGithub } from "react-icons/fa"; interface UserAuthFormProps extends React.HTMLAttributes {} @@ -22,8 +21,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) { setIsGitHubLoading(true); signIn("github"); }} - disabled={isGitHubLoading} - > + disabled={isGitHubLoading}> {isGitHubLoading ? ( ) : ( diff --git a/components/user-avatar.tsx b/components/user-avatar.tsx index 2770817..d74ae64 100644 --- a/components/user-avatar.tsx +++ b/components/user-avatar.tsx @@ -1,11 +1,10 @@ -import { User } from "@prisma/client" -import { AvatarProps } from "@radix-ui/react-avatar" -import { UserIcon } from "lucide-react" - -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { User } from "@prisma/client"; +import { AvatarProps } from "@radix-ui/react-avatar"; +import { UserIcon } from "lucide-react"; interface UserAvatarProps extends AvatarProps { - user: Pick + user: Pick; } export function UserAvatar({ user, ...props }: UserAvatarProps) { @@ -20,5 +19,5 @@ export function UserAvatar({ user, ...props }: UserAvatarProps) { )} - ) + ); } diff --git a/components/user-name-form.tsx b/components/user-name-form.tsx index a82ba4f..7702b7e 100644 --- a/components/user-name-form.tsx +++ b/components/user-name-form.tsx @@ -1,36 +1,28 @@ -"use client" +"use client"; -import * as React from "react" -import { useRouter } from "next/navigation" -import { zodResolver } from "@hookform/resolvers/zod" -import { User } from "@prisma/client" -import { Loader2Icon } from "lucide-react" -import { useForm } from "react-hook-form" -import * as z from "zod" - -import { cn } from "@/lib/utils" -import { userNameSchema } from "@/lib/validations/user" -import { buttonVariants } from "@/components/ui/button" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { toast } from "@/components/ui/use-toast" +import { buttonVariants } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { toast } from "@/components/ui/use-toast"; +import { cn } from "@/lib/utils"; +import { userNameSchema } from "@/lib/validations/user"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { User } from "@prisma/client"; +import { Loader2Icon } from "lucide-react"; +import { useRouter } from "next/navigation"; +import * as React from "react"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; interface UserNameFormProps extends React.HTMLAttributes { - user: Pick + user: Pick; } -type FormData = z.infer +type FormData = z.infer; export function UserNameForm({ user, ...props }: UserNameFormProps) { - const router = useRouter() + const router = useRouter(); const { handleSubmit, register, @@ -40,22 +32,21 @@ export function UserNameForm({ user, ...props }: UserNameFormProps) { defaultValues: { name: user?.name || "", }, - }) - const [isSaving, setIsSaving] = React.useState(false) + }); + const [isSaving, setIsSaving] = React.useState(false); async function onSubmit(data: FormData) { - setIsSaving(true) + setIsSaving(true); // some server action to save the user's name - setIsSaving(false) + setIsSaving(false); return toast({ title: "Something went wrong.", - description: - "The server action to save the user name is not yet implemented", + description: "The server action to save the user name is not yet implemented", variant: "destructive", - }) + }); } return ( @@ -64,8 +55,7 @@ export function UserNameForm({ user, ...props }: UserNameFormProps) { Your Name - Please enter your full name or a display name you are comfortable - with. + Please enter your full name or a display name you are comfortable with. @@ -73,23 +63,12 @@ export function UserNameForm({ user, ...props }: UserNameFormProps) { - - {errors?.name && ( -

{errors.name.message}

- )} + + {errors?.name &&

{errors.name.message}

}
- @@ -105,28 +84,17 @@ export function UserNameForm({ user, ...props }: UserNameFormProps) { - - {errors?.name && ( -

{errors.name.message}

- )} + + {errors?.name &&

{errors.name.message}

}
- - ) + ); } diff --git a/config/dashboard.ts b/config/dashboard.ts index 37f2cef..eb4c259 100644 --- a/config/dashboard.ts +++ b/config/dashboard.ts @@ -1,4 +1,4 @@ -import { DashboardConfig } from "types" +import { DashboardConfig } from "types"; export const dashboardConfig: DashboardConfig = { mainNav: [ @@ -8,7 +8,7 @@ export const dashboardConfig: DashboardConfig = { { title: "Your profile", href: "/", external: true }, ], bottomNav: [ - { title: "What is oss.gg?", href: "/", external: true}, - { title: "Help build oss.gg", href: "/contribute" }, + { title: "What is oss.gg?", href: "/", external: true }, + { title: "Help build oss.gg", href: "/contribute" }, ], }; diff --git a/config/marketing.ts b/config/marketing.ts index ff341f6..baa4d29 100644 --- a/config/marketing.ts +++ b/config/marketing.ts @@ -1,4 +1,4 @@ -import { MarketingConfig } from "types" +import { MarketingConfig } from "types"; export const marketingConfig: MarketingConfig = { mainNav: [ @@ -19,4 +19,4 @@ export const marketingConfig: MarketingConfig = { href: "/docs", }, ], -} +}; diff --git a/github/constants.ts b/github/constants.ts index 4dd2099..2f89c4a 100644 --- a/github/constants.ts +++ b/github/constants.ts @@ -1,9 +1,9 @@ -import { env } from "@/env.mjs" +import { env } from "@/env.mjs"; -export const LEVEL_LABEL = "level" +export const LEVEL_LABEL = "level"; -export const ASSIGN_IDENTIFIER = "/assign" as const -export const UNASSIGN_IDENTIFIER = "/unassign" as const +export const ASSIGN_IDENTIFIER = "/assign" as const; +export const UNASSIGN_IDENTIFIER = "/unassign" as const; export enum EVENT_TRIGGERS { ISSUE_OPENED = "issues.opened", @@ -11,8 +11,8 @@ export enum EVENT_TRIGGERS { ISSUE_COMMENTED = "issue_comment.created", } -export const ON_NEW_ISSUE = "Thanks for opening an issue! It's live on oss.gg!" +export const ON_NEW_ISSUE = "Thanks for opening an issue! It's live on oss.gg!"; -export const ON_REPO_NOT_REGISTERED = `This repository is not registered with oss.gg. Please register it at https://oss.gg.` +export const ON_REPO_NOT_REGISTERED = `This repository is not registered with oss.gg. Please register it at https://oss.gg.`; -export const GITHUB_APP_APP_ID = env.GITHUB_APP_APP_ID as string +export const GITHUB_APP_APP_ID = env.GITHUB_APP_APP_ID as string; diff --git a/github/hooks/installation.ts b/github/hooks/installation.ts index c7d08aa..3a7d170 100644 --- a/github/hooks/installation.ts +++ b/github/hooks/installation.ts @@ -1,19 +1,14 @@ -import { Webhooks } from "@octokit/webhooks" +import { Webhooks } from "@octokit/webhooks"; -import { EVENT_TRIGGERS } from "../constants" -import { sendInstallationDetails } from "../services/user" +import { EVENT_TRIGGERS } from "../constants"; +import { sendInstallationDetails } from "../services/user"; export const onInstallationCreated = async (webhooks: Webhooks) => { webhooks.on(EVENT_TRIGGERS.INSTALLATION_CREATED, async (context) => { - const installationId = context.payload.installation.id - const appId = context.payload.installation.app_id - const repos = context.payload.repositories + const installationId = context.payload.installation.id; + const appId = context.payload.installation.app_id; + const repos = context.payload.repositories; - await sendInstallationDetails( - installationId, - appId, - repos, - context.payload.installation - ) - }) -} + await sendInstallationDetails(installationId, appId, repos, context.payload.installation); + }); +}; diff --git a/github/hooks/issue.ts b/github/hooks/issue.ts index 85da7ae..bbf2c3a 100644 --- a/github/hooks/issue.ts +++ b/github/hooks/issue.ts @@ -1,18 +1,13 @@ -import { readFileSync } from "fs" -import path from "path" -import { Webhooks } from "@octokit/webhooks" +import { Webhooks } from "@octokit/webhooks"; +import { readFileSync } from "fs"; +import path from "path"; -import { - ASSIGN_IDENTIFIER, - EVENT_TRIGGERS, - LEVEL_LABEL, - UNASSIGN_IDENTIFIER, -} from "../constants" -import { getOctokitInstance } from "../utils" +import { ASSIGN_IDENTIFIER, EVENT_TRIGGERS, LEVEL_LABEL, UNASSIGN_IDENTIFIER } from "../constants"; +import { getOctokitInstance } from "../utils"; export const onIssueOpened = async (webhooks: Webhooks) => { webhooks.on(EVENT_TRIGGERS.ISSUE_OPENED, async (context) => { - const projectId = context.payload.repository.id + const projectId = context.payload.repository.id; // const isProjectRegistered = await getProject(projectId) // if (!isProjectRegistered) { @@ -24,11 +19,11 @@ export const onIssueOpened = async (webhooks: Webhooks) => { // return // } - const labels = context.payload.issue.labels?.map((label) => label.name) - const isLevelLabel = labels?.includes(LEVEL_LABEL) + const labels = context.payload.issue.labels?.map((label) => label.name); + const isLevelLabel = labels?.includes(LEVEL_LABEL); if (!isLevelLabel) { - return + return; } // await sendNewIssue( @@ -42,17 +37,17 @@ export const onIssueOpened = async (webhooks: Webhooks) => { // body: ON_NEW_ISSUE, // }) // ) - }) -} + }); +}; export const onAssignCommented = async (webhooks: Webhooks) => { webhooks.on(EVENT_TRIGGERS.ISSUE_COMMENTED, async (context) => { try { - const octokit = getOctokitInstance(context.payload.installation?.id!) + const octokit = getOctokitInstance(context.payload.installation?.id!); - const issueNumber = context.payload.issue.number - const repo = context.payload.repository.name - const issueCommentBody = context.payload.comment.body + const issueNumber = context.payload.issue.number; + const repo = context.payload.repository.name; + const issueCommentBody = context.payload.comment.body; if (issueCommentBody === ASSIGN_IDENTIFIER) { await octokit.issues.createComment({ @@ -60,22 +55,22 @@ export const onAssignCommented = async (webhooks: Webhooks) => { issue_number: issueNumber, repo, owner: "formbricks", - }) + }); } } catch (err) { - console.error(err) + console.error(err); } - }) -} + }); +}; export const onUnassignCommented = async (webhooks: Webhooks) => { webhooks.on(EVENT_TRIGGERS.ISSUE_COMMENTED, async (context) => { try { - const octokit = getOctokitInstance(context.payload.installation?.id!) + const octokit = getOctokitInstance(context.payload.installation?.id!); - const issueNumber = context.payload.issue.number - const repo = context.payload.repository.name - const issueCommentBody = context.payload.comment.body + const issueNumber = context.payload.issue.number; + const repo = context.payload.repository.name; + const issueCommentBody = context.payload.comment.body; if (issueCommentBody === UNASSIGN_IDENTIFIER) { await octokit.issues.createComment({ @@ -83,10 +78,10 @@ export const onUnassignCommented = async (webhooks: Webhooks) => { issue_number: issueNumber, repo, owner: "formbricks", - }) + }); } } catch (err) { - console.error(err) + console.error(err); } - }) -} + }); +}; diff --git a/github/index.ts b/github/index.ts index 10cc343..4986314 100644 --- a/github/index.ts +++ b/github/index.ts @@ -1,25 +1,20 @@ -import { createNodeMiddleware, Webhooks } from "@octokit/webhooks" +import { env } from "@/env.mjs"; +import { Webhooks, createNodeMiddleware } from "@octokit/webhooks"; -import { env } from "@/env.mjs" - -import { onInstallationCreated } from "./hooks/installation" -import { - onAssignCommented, - onIssueOpened, - onUnassignCommented, -} from "./hooks/issue" +import { onInstallationCreated } from "./hooks/installation"; +import { onAssignCommented, onIssueOpened, onUnassignCommented } from "./hooks/issue"; const webhooks = new Webhooks({ secret: env.GITHUB_WEBHOOK_SECRET as string, -}) +}); export const webhookMiddleware = createNodeMiddleware(webhooks, { path: "/api/github-webhook", -}) +}); export const registerHooks = async () => { - onIssueOpened(webhooks) - onInstallationCreated(webhooks) - onAssignCommented(webhooks) - onUnassignCommented(webhooks) -} + onIssueOpened(webhooks); + onInstallationCreated(webhooks); + onAssignCommented(webhooks); + onUnassignCommented(webhooks); +}; diff --git a/github/services/repository.ts b/github/services/repository.ts index e8102a3..41c6233 100644 --- a/github/services/repository.ts +++ b/github/services/repository.ts @@ -1,4 +1,4 @@ -import { db } from "@/lib/db" +import { db } from "@/lib/db"; export const selectRepository = async (id: string) => { try { @@ -9,12 +9,12 @@ export const selectRepository = async (id: string) => { data: { configured: true, }, - }) - return selectedRepository + }); + return selectedRepository; } catch (error) { - throw new Error(`Failed to select repository: ${error}`) + throw new Error(`Failed to select repository: ${error}`); } -} +}; export const getRepositoriesForUser = async (userId: string) => { try { @@ -22,7 +22,7 @@ export const getRepositoriesForUser = async (userId: string) => { where: { userId, }, - }) + }); const repos = await db.repository.findMany({ where: { @@ -31,10 +31,10 @@ export const getRepositoriesForUser = async (userId: string) => { }, configured: false, }, - }) - repos.sort((a, b) => a.name.localeCompare(b.name)) - return repos + }); + repos.sort((a, b) => a.name.localeCompare(b.name)); + return repos; } catch (error) { - throw new Error(`Failed to get repositories for user: ${error}`) + throw new Error(`Failed to get repositories for user: ${error}`); } -} +}; diff --git a/github/services/user.ts b/github/services/user.ts index 7285e36..65a068e 100644 --- a/github/services/user.ts +++ b/github/services/user.ts @@ -1,24 +1,23 @@ -import { readFileSync } from "fs" -import path from "path" -import { App } from "octokit" +import { env } from "@/env.mjs"; +import { db } from "@/lib/db"; +import { readFileSync } from "fs"; +import { App } from "octokit"; +import path from "path"; -import { env } from "@/env.mjs" -import { db } from "@/lib/db" - -const privateKeyPath = "../../../../key.pem" -const resolvedPath = path.resolve(__dirname, privateKeyPath) -const privateKey = readFileSync(resolvedPath, "utf8") +const privateKeyPath = "../../../../key.pem"; +const resolvedPath = path.resolve(__dirname, privateKeyPath); +const privateKey = readFileSync(resolvedPath, "utf8"); export const sendInstallationDetails = async ( installationId: number, appId: number, repos: | { - id: number - node_id: string - name: string - full_name: string - private: boolean + id: number; + node_id: string; + name: string; + full_name: string; + private: boolean; }[] | undefined, installation: any @@ -30,8 +29,8 @@ export const sendInstallationDetails = async ( webhooks: { secret: env.GITHUB_WEBHOOK_SECRET!, }, - }) - const octokit = await app.getInstallationOctokit(installationId) + }); + const octokit = await app.getInstallationOctokit(installationId); await db.$transaction(async (tx) => { const installationPrisma = await tx.installation.upsert({ @@ -41,14 +40,14 @@ export const sendInstallationDetails = async ( githubId: installationId, type: installation?.account?.type.toLowerCase(), }, - }) + }); - const userType = installation?.account?.type.toLowerCase() + const userType = installation?.account?.type.toLowerCase(); if (userType === "organization") { const membersOfOrg = await octokit.rest.orgs.listMembers({ org: installation?.account?.login, role: "all", - }) + }); await Promise.all( membersOfOrg.data.map(async (member) => { @@ -59,7 +58,7 @@ export const sendInstallationDetails = async ( githubId: member.id, login: member.login, }, - }) + }); await tx.membership.upsert({ where: { @@ -74,11 +73,11 @@ export const sendInstallationDetails = async ( installationId: installationPrisma.id, role: "member", }, - }) + }); }) - ) + ); } else { - const user = installation.account + const user = installation.account; const newUser = await tx.user.upsert({ where: { githubId: user.id }, update: {}, @@ -88,7 +87,7 @@ export const sendInstallationDetails = async ( name: user.name || "", email: user.email || "", }, - }) + }); await tx.membership.upsert({ where: { @@ -103,7 +102,7 @@ export const sendInstallationDetails = async ( installationId: installationPrisma.id, role: "owner", }, - }) + }); } if (repos) { @@ -117,13 +116,13 @@ export const sendInstallationDetails = async ( name: repo.name, installationId: installationPrisma.id, }, - }) + }); }) - ) + ); } - }) + }); } catch (error) { - console.error(`Failed to post installation details: ${error}`) - throw new Error(`Failed to post installation details: ${error}`) + console.error(`Failed to post installation details: ${error}`); + throw new Error(`Failed to post installation details: ${error}`); } -} +}; diff --git a/github/utils.ts b/github/utils.ts index dab42b9..ada2da1 100644 --- a/github/utils.ts +++ b/github/utils.ts @@ -1,17 +1,17 @@ -import { readFileSync } from "fs" -import path from "path" -import { createAppAuth } from "@octokit/auth-app" -import { Octokit } from "@octokit/rest" +import { createAppAuth } from "@octokit/auth-app"; +import { Octokit } from "@octokit/rest"; +import { readFileSync } from "fs"; +import path from "path"; -import { GITHUB_APP_APP_ID } from "./constants" +import { GITHUB_APP_APP_ID } from "./constants"; -const privateKeyPath = "../../../../key.pem" -const resolvedPath = path.resolve(__dirname, privateKeyPath) -const privateKey = readFileSync(resolvedPath, "utf8") +const privateKeyPath = "../../../../key.pem"; +const resolvedPath = path.resolve(__dirname, privateKeyPath); +const privateKey = readFileSync(resolvedPath, "utf8"); export const getOctokitInstance = (installationId: number) => { if (!installationId) { - throw new Error("No installation id provided") + throw new Error("No installation id provided"); } const octokit = new Octokit({ @@ -21,7 +21,7 @@ export const getOctokitInstance = (installationId: number) => { privateKey, installationId, }, - }) + }); - return octokit -} + return octokit; +}; diff --git a/hooks/use-lock-body.ts b/hooks/use-lock-body.ts index 3544a1b..a599069 100644 --- a/hooks/use-lock-body.ts +++ b/hooks/use-lock-body.ts @@ -1,12 +1,10 @@ -import * as React from "react" +import * as React from "react"; // @see https://usehooks.com/useLockBodyScroll. export function useLockBody() { React.useLayoutEffect((): (() => void) => { - const originalStyle: string = window.getComputedStyle( - document.body - ).overflow - document.body.style.overflow = "hidden" - return () => (document.body.style.overflow = originalStyle) - }, []) + const originalStyle: string = window.getComputedStyle(document.body).overflow; + document.body.style.overflow = "hidden"; + return () => (document.body.style.overflow = originalStyle); + }, []); } diff --git a/hooks/use-mounted.ts b/hooks/use-mounted.ts index 3a20c62..57bb851 100644 --- a/hooks/use-mounted.ts +++ b/hooks/use-mounted.ts @@ -1,11 +1,11 @@ -import * as React from "react" +import * as React from "react"; export function useMounted() { - const [mounted, setMounted] = React.useState(false) + const [mounted, setMounted] = React.useState(false); React.useEffect(() => { - setMounted(true) - }, []) + setMounted(true); + }, []); - return mounted + return mounted; } diff --git a/lib/api-key.ts b/lib/api-key.ts index 6d58b17..d5ccf0d 100644 --- a/lib/api-key.ts +++ b/lib/api-key.ts @@ -1,13 +1,11 @@ -import "server-only" +import "server-only"; -import { createHash, randomBytes } from "crypto" +import { db } from "@/lib/db"; +import { createHash, randomBytes } from "crypto"; -import { db } from "@/lib/db" +import { TApiKey, TApiKeyCreateInput } from "../types/apiKey"; -import { TApiKey, TApiKeyCreateInput } from "../types/apiKey" - -export const getHash = (key: string): string => - createHash("sha256").update(key).digest("hex") +export const getHash = (key: string): string => createHash("sha256").update(key).digest("hex"); export const getApiKey = async (apiKeyId: string): Promise => { try { const apiKeyData = await db.apiKey.findUnique({ @@ -21,39 +19,32 @@ export const getApiKey = async (apiKeyId: string): Promise => { where: { id: apiKeyId, }, - }) + }); - return apiKeyData + return apiKeyData; } catch (error) { - throw error + throw error; } -} -export const getApiKeys = async ( - repositoryId: string, - page?: number -): Promise => { +}; +export const getApiKeys = async (repositoryId: string, page?: number): Promise => { try { const apiKeys = await db.apiKey.findMany({ where: { repositoryId, }, - }) - return apiKeys + }); + return apiKeys; } catch (error) { - throw error + throw error; } -} +}; -export const hashApiKey = (key: string): string => - createHash("sha256").update(key).digest("hex") +export const hashApiKey = (key: string): string => createHash("sha256").update(key).digest("hex"); -export async function createApiKey( - repositoryId: string, - apiKeyData: TApiKeyCreateInput -): Promise { +export async function createApiKey(repositoryId: string, apiKeyData: TApiKeyCreateInput): Promise { try { - const key = randomBytes(16).toString("hex") - const hashedKey = hashApiKey(key) + const key = randomBytes(16).toString("hex"); + const hashedKey = hashApiKey(key); const result = await db.apiKey.create({ data: { @@ -61,10 +52,10 @@ export async function createApiKey( hashedKey, repository: { connect: { id: repositoryId } }, }, - }) - return { ...result, apiKey: key } + }); + return { ...result, apiKey: key }; } catch (error) { - throw error + throw error; } } @@ -74,30 +65,28 @@ export const deleteApiKey = async (id: string): Promise => { where: { id: id, }, - }) + }); - return deletedApiKeyData + return deletedApiKeyData; } catch (error) { - throw error + throw error; } -} +}; -export const getApiKeyFromKey = async ( - apiKey: string -): Promise => { +export const getApiKeyFromKey = async (apiKey: string): Promise => { if (!apiKey) { - throw new Error("API key required") + throw new Error("API key required"); } - const hashedKey = getHash(apiKey) + const hashedKey = getHash(apiKey); try { const apiKeyData = await db.apiKey.findUnique({ where: { hashedKey, }, - }) + }); - return apiKeyData + return apiKeyData; } catch (error) { - throw error + throw error; } -} +}; diff --git a/lib/auth.ts b/lib/auth.ts index f6d78b4..7d42a8c 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,8 +1,7 @@ -import { NextAuthOptions } from "next-auth" -import GitHubProvider from "next-auth/providers/github" - -import { env } from "@/env.mjs" -import { db } from "@/lib/db" +import { env } from "@/env.mjs"; +import { db } from "@/lib/db"; +import { NextAuthOptions } from "next-auth"; +import GitHubProvider from "next-auth/providers/github"; export const authOptions: NextAuthOptions = { pages: { @@ -17,7 +16,7 @@ export const authOptions: NextAuthOptions = { callbacks: { async signIn({ user, account, profile }: any) { if (account.type !== "oauth") { - return false + return false; } if (account.provider) { @@ -29,13 +28,13 @@ export const authOptions: NextAuthOptions = { where: { githubId: profile.id, }, - }) + }); if (existingUserWithAccount) { // User with this provider found // check if email still the same if (existingUserWithAccount.email === user.email) { - return true + return true; } // user seemed to change his email within the provider @@ -47,8 +46,8 @@ export const authOptions: NextAuthOptions = { data: { email: user.email, }, - }) - return true + }); + return true; } // create user if it does not exist @@ -65,25 +64,25 @@ export const authOptions: NextAuthOptions = { }, }, }, - }) + }); - return true + return true; } - return false + return false; }, async jwt({ token, user, profile }) { const dbUser = await db.user.findFirst({ where: { email: token.email as string, }, - }) + }); if (!dbUser) { if (user) { - token.id = user?.id + token.id = user?.id; } - return token + return token; } return { @@ -91,17 +90,17 @@ export const authOptions: NextAuthOptions = { name: dbUser.name, email: dbUser.email, avatarUrl: dbUser.avatarUrl, - } + }; }, async session({ token, session }) { if (token) { - session.user.id = token.id - if (token.name) session.user.name = token.name - if (token.email) session.user.email = token.email - if (token.avatarUrl) session.user.avatarUrl = token.avatarUrl as string + session.user.id = token.id; + if (token.name) session.user.name = token.name; + if (token.email) session.user.email = token.email; + if (token.avatarUrl) session.user.avatarUrl = token.avatarUrl as string; } - return session + return session; }, }, -} +}; diff --git a/lib/repository.ts b/lib/repository.ts index 3edd4bf..495b98e 100644 --- a/lib/repository.ts +++ b/lib/repository.ts @@ -1,67 +1,58 @@ -import { db } from "@/lib/db" +import { db } from "@/lib/db"; -import { TRepository, TRepositoryCreateInput } from "../types/repository" +import { TRepository, TRepositoryCreateInput } from "../types/repository"; -export const getRepository = async ( - id: string -): Promise => { +export const getRepository = async (id: string): Promise => { try { const repositoryData = await db.repository.findUnique({ where: { id, }, - }) + }); - return repositoryData + return repositoryData; } catch (error) { - throw error + throw error; } -} +}; -export const createRepository = async ( - repositoryData: TRepositoryCreateInput -): Promise => { +export const createRepository = async (repositoryData: TRepositoryCreateInput): Promise => { try { const newRepository = await db.repository.create({ data: repositoryData, - }) + }); - return newRepository + return newRepository; } catch (error) { - throw error + throw error; } -} +}; -export const deleteRepository = async ( - repositoryId: string -): Promise => { +export const deleteRepository = async (repositoryId: string): Promise => { try { const deletedRepository = await db.repository.delete({ where: { id: repositoryId, }, - }) + }); - return deletedRepository + return deletedRepository; } catch (error) { - throw error + throw error; } -} +}; -export async function hasUserAccessToRepository( - userId: string, - repositoryId: string -): Promise { +export async function hasUserAccessToRepository(userId: string, repositoryId: string): Promise { const repository = await db.repository.findUnique({ where: { id: repositoryId }, select: { installation: true }, - }) + }); if (!repository || !repository.installation) { - return false // Repository or its installation not found + return false; // Repository or its installation not found } - const installationId = repository.installation.id + const installationId = repository.installation.id; // Check if there is a membership for the user in this installation const membership = await db.membership.findFirst({ @@ -69,7 +60,7 @@ export async function hasUserAccessToRepository( userId, installationId, }, - }) + }); - return membership?.userId === userId // true if membership exists, false otherwise + return membership?.userId === userId; // true if membership exists, false otherwise } diff --git a/lib/session.ts b/lib/session.ts index d570582..89c9a89 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -1,9 +1,8 @@ -import { getServerSession } from "next-auth/next" - -import { authOptions } from "@/lib/auth" +import { authOptions } from "@/lib/auth"; +import { getServerSession } from "next-auth/next"; export async function getCurrentUser() { - const session = await getServerSession(authOptions) + const session = await getServerSession(authOptions); - return session?.user + return session?.user; } diff --git a/lib/utils.ts b/lib/utils.ts index 42bc6df..f6fef47 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,5 +1,5 @@ import { env } from "@/env.mjs"; -import { clsx, type ClassValue } from "clsx"; +import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { diff --git a/lib/validations/auth.ts b/lib/validations/auth.ts index b91e85a..dab2901 100644 --- a/lib/validations/auth.ts +++ b/lib/validations/auth.ts @@ -1,5 +1,5 @@ -import * as z from "zod" +import * as z from "zod"; export const userAuthSchema = z.object({ email: z.string().email(), -}) +}); diff --git a/lib/validations/og.ts b/lib/validations/og.ts index bc246f8..a26f14c 100644 --- a/lib/validations/og.ts +++ b/lib/validations/og.ts @@ -1,7 +1,7 @@ -import * as z from "zod" +import * as z from "zod"; export const ogImageSchema = z.object({ heading: z.string(), type: z.string(), mode: z.enum(["light", "dark"]).default("dark"), -}) +}); diff --git a/lib/validations/post.ts b/lib/validations/post.ts index 73ad1c6..783daa3 100644 --- a/lib/validations/post.ts +++ b/lib/validations/post.ts @@ -1,8 +1,8 @@ -import * as z from "zod" +import * as z from "zod"; export const postPatchSchema = z.object({ title: z.string().min(3).max(128).optional(), // TODO: Type this properly from editorjs block types? content: z.any().optional(), -}) +}); diff --git a/lib/validations/user.ts b/lib/validations/user.ts index 035cc5a..3553cd5 100644 --- a/lib/validations/user.ts +++ b/lib/validations/user.ts @@ -1,5 +1,5 @@ -import * as z from "zod" +import * as z from "zod"; export const userNameSchema = z.object({ name: z.string().min(3).max(32), -}) +}); diff --git a/middleware.ts b/middleware.ts index 9f05fd1..017953d 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,21 +1,20 @@ -import { getToken } from "next-auth/jwt" -import { withAuth } from "next-auth/middleware" -import { NextResponse } from "next/server" +import { getToken } from "next-auth/jwt"; +import { withAuth } from "next-auth/middleware"; +import { NextResponse } from "next/server"; export default withAuth( async function middleware(req) { - const token = await getToken({ req }) - const isAuth = !!token + const token = await getToken({ req }); + const isAuth = !!token; const isAuthPage = - req.nextUrl.pathname.startsWith("/login") || - req.nextUrl.pathname.startsWith("/register") + req.nextUrl.pathname.startsWith("/login") || req.nextUrl.pathname.startsWith("/register"); if (isAuthPage) { if (isAuth) { - return NextResponse.redirect(new URL("/dashboard", req.url)) + return NextResponse.redirect(new URL("/dashboard", req.url)); } - return null + return null; } if (!isAuth) { @@ -24,9 +23,7 @@ export default withAuth( from += req.nextUrl.search; } - return NextResponse.redirect( - new URL(`/login?from=${encodeURIComponent(from)}`, req.url) - ); + return NextResponse.redirect(new URL(`/login?from=${encodeURIComponent(from)}`, req.url)); } }, { @@ -35,12 +32,12 @@ export default withAuth( // This is a work-around for handling redirect on auth pages. // We return true here so that the middleware function above // is always called. - return true + return true; }, }, } -) +); export const config = { matcher: ["/dashboard/:path*", "/editor/:path*", "/login", "/register"], -} +}; diff --git a/package.json b/package.json index 870b1da..66f5ee6 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { + "clean": "rimraf .turbo node_modules .next", "db:migrate:deploy": "prisma migrate deploy", "db:migrate:dev": "pnpm prisma migrate dev", "db:up": "docker-compose -f docker-compose.dev.yml up -d", @@ -11,16 +12,17 @@ "db:down": "docker-compose -f docker-compose.dev.yml down", "dev": "pnpm db:start && next dev", "build": "next build", - "start": "next start", - "lint": "next lint" + "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "lint": "next lint", + "start": "next start" }, "dependencies": { "@hookform/resolvers": "^3.3.4", "@next-auth/prisma-adapter": "^1.0.7", "@octokit/auth-app": "^6.0.3", "@octokit/rest": "^20.0.2", - "@octokit/webhooks": "^12.0.11", - "@prisma/client": "5.8.1", + "@octokit/webhooks": "^12.1.0", + "@prisma/client": "5.9.1", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.6", @@ -31,14 +33,14 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "jsonwebtoken": "^9.0.2", - "lucide-react": "^0.316.0", + "lucide-react": "^0.323.0", "next": "14.1.0", "next-auth": "^4.24.5", "next-themes": "^0.2.1", "octokit": "^3.1.2", "react": "^18", "react-dom": "^18", - "react-hook-form": "^7.49.3", + "react-hook-form": "^7.50.1", "react-icons": "^5.0.1", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", @@ -47,7 +49,7 @@ "devDependencies": { "@commitlint/cli": "^18.6.0", "@commitlint/config-conventional": "^18.6.0", - "@ianvs/prettier-plugin-sort-imports": "^4.1.1", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -56,12 +58,13 @@ "eslint-config-next": "14.1.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-tailwindcss": "^3.14.0", - "husky": "^9.0.6", + "eslint-plugin-tailwindcss": "^3.14.2", + "husky": "^9.0.10", "postcss": "^8", - "prettier": "^3.2.4", + "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.11", - "prisma": "^5.8.1", + "prisma": "^5.9.1", + "rimraf": "^5.0.5", "tailwindcss": "^3.4.1", "typescript": "^5" } diff --git a/pages/api/apiHelper.ts b/pages/api/apiHelper.ts index d9533c7..e324148 100644 --- a/pages/api/apiHelper.ts +++ b/pages/api/apiHelper.ts @@ -1,22 +1,17 @@ -import { createHash } from "crypto" -import { NextApiRequest, NextApiResponse } from "next" -import type { Session } from "next-auth" -import { getServerSession } from "next-auth" +import { authOptions } from "@/lib/auth"; +import { createHash } from "crypto"; +import { NextApiRequest, NextApiResponse } from "next"; +import type { Session } from "next-auth"; +import { getServerSession } from "next-auth"; -import { authOptions } from "@/lib/auth" - -export const hashApiKey = (key: string): string => - createHash("sha256").update(key).digest("hex") -export const getSessionUser = async ( - req?: NextApiRequest, - res?: NextApiResponse -) => { +export const hashApiKey = (key: string): string => createHash("sha256").update(key).digest("hex"); +export const getSessionUser = async (req?: NextApiRequest, res?: NextApiResponse) => { // check for session (browser usage) - let session: Session | null + let session: Session | null; if (req && res) { - session = await getServerSession(req, res, authOptions) + session = await getServerSession(req, res, authOptions); } else { - session = await getServerSession(authOptions) + session = await getServerSession(authOptions); } - if (session && "user" in session) return session.user -} + if (session && "user" in session) return session.user; +}; diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts index 2d2d579..7575ea6 100644 --- a/pages/api/auth/[...nextauth].ts +++ b/pages/api/auth/[...nextauth].ts @@ -1,6 +1,5 @@ -import NextAuth from "next-auth" - -import { authOptions } from "@/lib/auth" +import { authOptions } from "@/lib/auth"; +import NextAuth from "next-auth"; // @see ./lib/auth -export default NextAuth(authOptions) +export default NextAuth(authOptions); diff --git a/pages/api/github-webhook.ts b/pages/api/github-webhook.ts index cec305f..ffa8dee 100644 --- a/pages/api/github-webhook.ts +++ b/pages/api/github-webhook.ts @@ -1,20 +1,19 @@ -import { NextApiRequest, NextApiResponse } from "next" -import { registerHooks, webhookMiddleware } from "@/github" - -import "@/github/hooks/issue" +import { registerHooks, webhookMiddleware } from "@/github"; +import "@/github/hooks/issue"; +import { NextApiRequest, NextApiResponse } from "next"; export const config = { api: { bodyParser: false, }, -} +}; export default function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === "POST") { - registerHooks() - webhookMiddleware(req, res, () => res.status(200).end()) + registerHooks(); + webhookMiddleware(req, res, () => res.status(200).end()); } else { - res.setHeader("Allow", "POST") - res.status(405).end("Method Not Allowed") + res.setHeader("Allow", "POST"); + res.status(405).end("Method Not Allowed"); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02bec90..881afb2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,10 +7,10 @@ settings: dependencies: '@hookform/resolvers': specifier: ^3.3.4 - version: 3.3.4(react-hook-form@7.49.3) + version: 3.3.4(react-hook-form@7.50.1) '@next-auth/prisma-adapter': specifier: ^1.0.7 - version: 1.0.7(@prisma/client@5.8.1)(next-auth@4.24.5) + version: 1.0.7(@prisma/client@5.9.1)(next-auth@4.24.5) '@octokit/auth-app': specifier: ^6.0.3 version: 6.0.3 @@ -18,11 +18,11 @@ dependencies: specifier: ^20.0.2 version: 20.0.2 '@octokit/webhooks': - specifier: ^12.0.11 - version: 12.0.11 + specifier: ^12.1.0 + version: 12.1.0 '@prisma/client': - specifier: 5.8.1 - version: 5.8.1(prisma@5.8.1) + specifier: 5.9.1 + version: 5.9.1(prisma@5.9.1) '@radix-ui/react-alert-dialog': specifier: ^1.0.5 version: 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) @@ -54,11 +54,11 @@ dependencies: specifier: ^9.0.2 version: 9.0.2 lucide-react: - specifier: ^0.316.0 - version: 0.316.0(react@18.2.0) + specifier: ^0.323.0 + version: 0.323.0(react@18.2.0) next: specifier: 14.1.0 - version: 14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) + version: 14.1.0(react-dom@18.2.0)(react@18.2.0) next-auth: specifier: ^4.24.5 version: 4.24.5(next@14.1.0)(react-dom@18.2.0)(react@18.2.0) @@ -75,8 +75,8 @@ dependencies: specifier: ^18 version: 18.2.0(react@18.2.0) react-hook-form: - specifier: ^7.49.3 - version: 7.49.3(react@18.2.0) + specifier: ^7.50.1 + version: 7.50.1(react@18.2.0) react-icons: specifier: ^5.0.1 version: 5.0.1(react@18.2.0) @@ -97,9 +97,9 @@ devDependencies: '@commitlint/config-conventional': specifier: ^18.6.0 version: 18.6.0 - '@ianvs/prettier-plugin-sort-imports': - specifier: ^4.1.1 - version: 4.1.1(prettier@3.2.4) + '@trivago/prettier-plugin-sort-imports': + specifier: ^4.3.0 + version: 4.3.0(prettier@3.2.5) '@types/node': specifier: ^20 version: 20.11.5 @@ -125,23 +125,26 @@ devDependencies: specifier: ^7.33.2 version: 7.33.2(eslint@8.56.0) eslint-plugin-tailwindcss: - specifier: ^3.14.0 + specifier: ^3.14.2 version: 3.14.2(tailwindcss@3.4.1) husky: - specifier: ^9.0.6 - version: 9.0.6 + specifier: ^9.0.10 + version: 9.0.10 postcss: specifier: ^8 version: 8.4.33 prettier: - specifier: ^3.2.4 - version: 3.2.4 + specifier: ^3.2.5 + version: 3.2.5 prettier-plugin-tailwindcss: specifier: ^0.5.11 - version: 0.5.11(@ianvs/prettier-plugin-sort-imports@4.1.1)(prettier@3.2.4) + version: 0.5.11(@trivago/prettier-plugin-sort-imports@4.3.0)(prettier@3.2.5) prisma: - specifier: ^5.8.1 - version: 5.8.1 + specifier: ^5.9.1 + version: 5.9.1 + rimraf: + specifier: ^5.0.5 + version: 5.0.5 tailwindcss: specifier: ^3.4.1 version: 3.4.1 @@ -160,45 +163,22 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - /@ampproject/remapping@2.2.1: - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.21 - /@babel/code-frame@7.23.5: resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.23.4 chalk: 2.4.2 + dev: true - /@babel/compat-data@7.23.5: - resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} - engines: {node: '>=6.9.0'} - - /@babel/core@7.23.7: - resolution: {integrity: sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==} + /@babel/generator@7.17.7: + resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7) - '@babel/helpers': 7.23.8 - '@babel/parser': 7.23.6 - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.7 '@babel/types': 7.23.6 - convert-source-map: 2.0.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + jsesc: 2.5.2 + source-map: 0.5.7 + dev: true /@babel/generator@7.23.6: resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} @@ -208,20 +188,12 @@ packages: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.21 jsesc: 2.5.2 - - /@babel/helper-compilation-targets@7.23.6: - resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.23.5 - '@babel/helper-validator-option': 7.23.5 - browserslist: 4.22.2 - lru-cache: 5.1.1 - semver: 6.3.1 + dev: true /@babel/helper-environment-visitor@7.22.20: resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} + dev: true /@babel/helper-function-name@7.23.0: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} @@ -229,65 +201,31 @@ packages: dependencies: '@babel/template': 7.22.15 '@babel/types': 7.23.6 + dev: true /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.23.6 - - /@babel/helper-module-imports@7.22.15: - resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.6 - - /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.7): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.7 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - - /@babel/helper-simple-access@7.22.5: - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.6 + dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.23.6 + dev: true /@babel/helper-string-parser@7.23.4: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} + dev: true /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - - /@babel/helper-validator-option@7.23.5: - resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} - engines: {node: '>=6.9.0'} - - /@babel/helpers@7.23.8: - resolution: {integrity: sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.7 - '@babel/types': 7.23.6 - transitivePeerDependencies: - - supports-color + dev: true /@babel/highlight@7.23.4: resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} @@ -296,6 +234,7 @@ packages: '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 + dev: true /@babel/parser@7.23.6: resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} @@ -303,6 +242,7 @@ packages: hasBin: true dependencies: '@babel/types': 7.23.6 + dev: true /@babel/runtime@7.23.8: resolution: {integrity: sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==} @@ -317,9 +257,10 @@ packages: '@babel/code-frame': 7.23.5 '@babel/parser': 7.23.6 '@babel/types': 7.23.6 + dev: true - /@babel/traverse@7.23.7: - resolution: {integrity: sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==} + /@babel/traverse@7.23.2: + resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.23.5 @@ -334,6 +275,15 @@ packages: globals: 11.12.0 transitivePeerDependencies: - supports-color + dev: true + + /@babel/types@7.17.0: + resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true /@babel/types@7.23.6: resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} @@ -342,6 +292,7 @@ packages: '@babel/helper-string-parser': 7.23.4 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 + dev: true /@commitlint/cli@18.6.0(@types/node@20.11.5)(typescript@5.3.3): resolution: {integrity: sha512-FiH23cr9QG8VdfbmvJJZmdfHGVMCouOOAzoXZ3Cd7czGC52RbycwNt8YCI7SA69pAl+t30vh8LMaO/N+kcel6w==} @@ -572,12 +523,12 @@ packages: resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} dev: false - /@hookform/resolvers@3.3.4(react-hook-form@7.49.3): + /@hookform/resolvers@3.3.4(react-hook-form@7.50.1): resolution: {integrity: sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==} peerDependencies: react-hook-form: ^7.0.0 dependencies: - react-hook-form: 7.49.3(react@18.2.0) + react-hook-form: 7.50.1(react@18.2.0) dev: false /@humanwhocodes/config-array@0.11.14: @@ -600,26 +551,6 @@ packages: resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} dev: true - /@ianvs/prettier-plugin-sort-imports@4.1.1(prettier@3.2.4): - resolution: {integrity: sha512-kJhXq63ngpTQ2dxgf5GasbPJWsJA3LgoOdd7WGhpUSzLgLgI4IsIzYkbJf9kmpOHe7Vdm/o3PcRA3jmizXUuAQ==} - peerDependencies: - '@vue/compiler-sfc': '>=3.0.0' - prettier: 2 || 3 - peerDependenciesMeta: - '@vue/compiler-sfc': - optional: true - dependencies: - '@babel/core': 7.23.7 - '@babel/generator': 7.23.6 - '@babel/parser': 7.23.6 - '@babel/traverse': 7.23.7 - '@babel/types': 7.23.6 - prettier: 3.2.4 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - dev: true - /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -656,13 +587,13 @@ packages: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 - /@next-auth/prisma-adapter@1.0.7(@prisma/client@5.8.1)(next-auth@4.24.5): + /@next-auth/prisma-adapter@1.0.7(@prisma/client@5.9.1)(next-auth@4.24.5): resolution: {integrity: sha512-Cdko4KfcmKjsyHFrWwZ//lfLUbcLqlyFqjd/nYE2m3aZ7tjMNUjpks47iw7NTCnXf+5UWz5Ypyt1dSs1EP5QJw==} peerDependencies: '@prisma/client': '>=2.26.0 || >=3' next-auth: ^4 dependencies: - '@prisma/client': 5.8.1(prisma@5.8.1) + '@prisma/client': 5.9.1(prisma@5.9.1) next-auth: 4.24.5(next@14.1.0)(react-dom@18.2.0)(react@18.2.0) dev: false @@ -785,7 +716,7 @@ packages: '@octokit/oauth-app': 6.0.0 '@octokit/plugin-paginate-rest': 9.1.5(@octokit/core@5.0.2) '@octokit/types': 12.4.0 - '@octokit/webhooks': 12.0.11 + '@octokit/webhooks': 12.1.0 dev: false /@octokit/auth-app@6.0.3: @@ -1016,17 +947,17 @@ packages: engines: {node: '>= 18'} dev: false - /@octokit/webhooks-types@7.1.0: - resolution: {integrity: sha512-y92CpG4kFFtBBjni8LHoV12IegJ+KFxLgKRengrVjKmGE5XMeCuGvlfRe75lTRrgXaG6XIWJlFpIDTlkoJsU8w==} + /@octokit/webhooks-types@7.3.2: + resolution: {integrity: sha512-JWOoOgtWTFnTSAamPXXyjTY5/apttvNxF+vPBnwdSu5cj5snrd7FO0fyw4+wTXy8fHduq626JjhO+TwCyyA6vA==} dev: false - /@octokit/webhooks@12.0.11: - resolution: {integrity: sha512-YEQOb7v0TZ662nh5jsbY1CMgJyMajCEagKrHWC30LTCwCtnuIrLtEpE20vq4AtH0SuZI90+PtV66/Bnnw0jkvg==} + /@octokit/webhooks@12.1.0: + resolution: {integrity: sha512-ppqZ1DyHhZklpeuxnx7WRn5S5WRxjHYt/fQlr33JNvbK+Dpaz6XFD5Zw/AFri62J4NH3jKreHeQFQkLouMqdog==} engines: {node: '>= 18'} dependencies: '@octokit/request-error': 5.0.1 '@octokit/webhooks-methods': 4.0.0 - '@octokit/webhooks-types': 7.1.0 + '@octokit/webhooks-types': 7.3.2 aggregate-error: 3.1.0 dev: false @@ -1040,8 +971,8 @@ packages: requiresBuild: true optional: true - /@prisma/client@5.8.1(prisma@5.8.1): - resolution: {integrity: sha512-xQtMPfbIwLlbm0VVIVQY2yqQVOxPwRQhvIp7Z3m2900g1bu/zRHKhYZJQWELqmjl6d8YwBy0K2NvMqh47v1ubw==} + /@prisma/client@5.9.1(prisma@5.9.1): + resolution: {integrity: sha512-caSOnG4kxcSkhqC/2ShV7rEoWwd3XrftokxJqOCMVvia4NYV/TPtJlS9C2os3Igxw/Qyxumj9GBQzcStzECvtQ==} engines: {node: '>=16.13'} requiresBuild: true peerDependencies: @@ -1050,35 +981,35 @@ packages: prisma: optional: true dependencies: - prisma: 5.8.1 + prisma: 5.9.1 dev: false - /@prisma/debug@5.8.1: - resolution: {integrity: sha512-tjuw7eA0Us3T42jx9AmAgL58rzwzpFGYc3R7Y4Ip75EBYrKMBA1YihuWMcBC92ILmjlQ/u3p8VxcIE0hr+fZfg==} + /@prisma/debug@5.9.1: + resolution: {integrity: sha512-yAHFSFCg8KVoL0oRUno3m60GAjsUKYUDkQ+9BA2X2JfVR3kRVSJFc/GpQ2fSORi4pSHZR9orfM4UC9OVXIFFTA==} - /@prisma/engines-version@5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2: - resolution: {integrity: sha512-f5C3JM3l9yhGr3cr4FMqWloFaSCpNpMi58Om22rjD2DOz3owci2mFdFXMgnAGazFPKrCbbEhcxdsRfspEYRoFQ==} + /@prisma/engines-version@5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64: + resolution: {integrity: sha512-HFl7275yF0FWbdcNvcSRbbu9JCBSLMcurYwvWc8WGDnpu7APxQo2ONtZrUggU3WxLxUJ2uBX+0GOFIcJeVeOOQ==} - /@prisma/engines@5.8.1: - resolution: {integrity: sha512-TJgYLRrZr56uhqcXO4GmP5be+zjCIHtLDK20Cnfg+o9d905hsN065QOL+3Z0zQAy6YD31Ol4u2kzSfRmbJv/uA==} + /@prisma/engines@5.9.1: + resolution: {integrity: sha512-gkdXmjxQ5jktxWNdDA5aZZ6R8rH74JkoKq6LD5mACSvxd2vbqWeWIOV0Py5wFC8vofOYShbt6XUeCIUmrOzOnQ==} requiresBuild: true dependencies: - '@prisma/debug': 5.8.1 - '@prisma/engines-version': 5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2 - '@prisma/fetch-engine': 5.8.1 - '@prisma/get-platform': 5.8.1 + '@prisma/debug': 5.9.1 + '@prisma/engines-version': 5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64 + '@prisma/fetch-engine': 5.9.1 + '@prisma/get-platform': 5.9.1 - /@prisma/fetch-engine@5.8.1: - resolution: {integrity: sha512-+bgjjoSFa6uYEbAPlklfoVSStOEfcpheOjoBoNsNNSQdSzcwE2nM4Q0prun0+P8/0sCHo18JZ9xqa8gObvgOUw==} + /@prisma/fetch-engine@5.9.1: + resolution: {integrity: sha512-l0goQOMcNVOJs1kAcwqpKq3ylvkD9F04Ioe1oJoCqmz05mw22bNAKKGWuDd3zTUoUZr97va0c/UfLNru+PDmNA==} dependencies: - '@prisma/debug': 5.8.1 - '@prisma/engines-version': 5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2 - '@prisma/get-platform': 5.8.1 + '@prisma/debug': 5.9.1 + '@prisma/engines-version': 5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64 + '@prisma/get-platform': 5.9.1 - /@prisma/get-platform@5.8.1: - resolution: {integrity: sha512-wnA+6HTFcY+tkykMokix9GiAkaauPC5W/gg0O5JB0J8tCTNWrqpnQ7AsaGRfkYUbeOIioh6woDjQrGTTRf1Zag==} + /@prisma/get-platform@5.9.1: + resolution: {integrity: sha512-6OQsNxTyhvG+T2Ksr8FPFpuPeL4r9u0JF0OZHUBI/Uy9SS43sPyAIutt4ZEAyqWQt104ERh70EZedkHZKsnNbg==} dependencies: - '@prisma/debug': 5.8.1 + '@prisma/debug': 5.9.1 /@radix-ui/primitive@1.0.1: resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} @@ -1742,6 +1673,26 @@ packages: zod: 3.22.4 dev: false + /@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.2.5): + resolution: {integrity: sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==} + peerDependencies: + '@vue/compiler-sfc': 3.x + prettier: 2.x - 3.x + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + dependencies: + '@babel/generator': 7.17.7 + '@babel/parser': 7.23.6 + '@babel/traverse': 7.23.2 + '@babel/types': 7.17.0 + javascript-natural-sort: 0.7.1 + lodash: 4.17.21 + prettier: 3.2.5 + transitivePeerDependencies: + - supports-color + dev: true + /@types/aws-lambda@8.10.131: resolution: {integrity: sha512-IWmFpqnVDvskYWnNSiu/qlRn80XlIOU0Gy5rKCl/NjhnI95pV8qIHs6L5b+bpHhyzuOSzjLgBcwgFSXrC1nZWA==} dev: false @@ -1920,6 +1871,7 @@ packages: engines: {node: '>=4'} dependencies: color-convert: 1.9.3 + dev: true /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -2131,6 +2083,7 @@ packages: electron-to-chromium: 1.4.637 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) + dev: true /btoa-lite@1.0.0: resolution: {integrity: sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==} @@ -2188,6 +2141,7 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 + dev: true /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -2249,6 +2203,7 @@ packages: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 + dev: true /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -2258,6 +2213,7 @@ packages: /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -2302,9 +2258,6 @@ packages: split2: 4.2.0 dev: true - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - /cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} @@ -2386,6 +2339,7 @@ packages: optional: true dependencies: ms: 2.1.2 + dev: true /decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} @@ -2480,6 +2434,7 @@ packages: /electron-to-chromium@1.4.637: resolution: {integrity: sha512-G7j3UCOukFtxVO1vWrPQUoDk3kL70mtvjc/DC/k2o7lE0wAdq+Vwp1ipagOow+BH0uVztFysLWbkM/RTIrbK3w==} + dev: true /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2592,10 +2547,12 @@ packages: /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} + dev: true /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} + dev: true /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -3026,10 +2983,6 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -3124,6 +3077,7 @@ packages: /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} + dev: true /globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} @@ -3176,6 +3130,7 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} + dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -3227,8 +3182,8 @@ packages: engines: {node: '>=10.17.0'} dev: true - /husky@9.0.6: - resolution: {integrity: sha512-EEuw/rfTiMjOfuL7pGO/i9otg1u36TXxqjIA6D9qxVjd/UXoDOsLor/BSFf5hTK50shwzCU3aVVwdXDp/lp7RA==} + /husky@9.0.10: + resolution: {integrity: sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==} engines: {node: '>=18'} hasBin: true dev: true @@ -3496,6 +3451,10 @@ packages: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + /javascript-natural-sort@0.7.1: + resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} + dev: true + /jiti@1.21.0: resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} hasBin: true @@ -3518,6 +3477,7 @@ packages: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} hasBin: true + dev: true /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -3546,11 +3506,6 @@ packages: minimist: 1.2.8 dev: true - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - /jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} @@ -3729,19 +3684,14 @@ packages: resolution: {integrity: sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==} engines: {node: 14 || >=16.14} - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} dependencies: yallist: 4.0.0 - /lucide-react@0.316.0(react@18.2.0): - resolution: {integrity: sha512-dTmYX1H4IXsRfVcj/KUxworV6814ApTl7iXaS21AimK2RUEl4j4AfOmqD3VR8phe5V91m4vEJ8tCK4uT1jE5nA==} + /lucide-react@0.323.0(react@18.2.0): + resolution: {integrity: sha512-rTXZFILl2Y4d1SG9p1Mdcf17AcPvPvpc/egFIzUrp7IUy60MUQo3Oi1mu8LGYXUVwuRZYsSMt3csHRW5mAovJg==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 dependencies: @@ -3836,6 +3786,7 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -3871,7 +3822,7 @@ packages: '@panva/hkdf': 1.1.1 cookie: 0.5.0 jose: 4.15.4 - next: 14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) + next: 14.1.0(react-dom@18.2.0)(react@18.2.0) oauth: 0.9.15 openid-client: 5.6.4 preact: 10.19.3 @@ -3888,12 +3839,12 @@ packages: react: '*' react-dom: '*' dependencies: - next: 14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) + next: 14.1.0(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /next@14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0): + /next@14.1.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==} engines: {node: '>=18.17.0'} hasBin: true @@ -3916,7 +3867,7 @@ packages: postcss: 8.4.31 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(@babel/core@7.23.7)(react@18.2.0) + styled-jsx: 5.1.1(react@18.2.0) optionalDependencies: '@next/swc-darwin-arm64': 14.1.0 '@next/swc-darwin-x64': 14.1.0 @@ -3934,6 +3885,7 @@ packages: /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: true /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -4287,7 +4239,7 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss@0.5.11(@ianvs/prettier-plugin-sort-imports@4.1.1)(prettier@3.2.4): + /prettier-plugin-tailwindcss@0.5.11(@trivago/prettier-plugin-sort-imports@4.3.0)(prettier@3.2.5): resolution: {integrity: sha512-AvI/DNyMctyyxGOjyePgi/gqj5hJYClZ1avtQvLlqMT3uDZkRbi4HhGUpok3DRzv9z7Lti85Kdj3s3/1CeNI0w==} engines: {node: '>=14.21.3'} peerDependencies: @@ -4336,12 +4288,12 @@ packages: prettier-plugin-twig-melody: optional: true dependencies: - '@ianvs/prettier-plugin-sort-imports': 4.1.1(prettier@3.2.4) - prettier: 3.2.4 + '@trivago/prettier-plugin-sort-imports': 4.3.0(prettier@3.2.5) + prettier: 3.2.5 dev: true - /prettier@3.2.4: - resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==} + /prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} hasBin: true dev: true @@ -4350,13 +4302,13 @@ packages: resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} dev: false - /prisma@5.8.1: - resolution: {integrity: sha512-N6CpjzECnUHZ5beeYpDzkt2rYpEdAeqXX2dweu6BoQaeYkNZrC/WJHM+5MO/uidFHTak8QhkPKBWck1o/4MD4A==} + /prisma@5.9.1: + resolution: {integrity: sha512-Hy/8KJZz0ELtkw4FnG9MS9rNWlXcJhf98Z2QMqi0QiVMoS8PzsBkpla0/Y5hTlob8F3HeECYphBjqmBxrluUrQ==} engines: {node: '>=16.13'} hasBin: true requiresBuild: true dependencies: - '@prisma/engines': 5.8.1 + '@prisma/engines': 5.9.1 /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -4389,9 +4341,9 @@ packages: scheduler: 0.23.0 dev: false - /react-hook-form@7.49.3(react@18.2.0): - resolution: {integrity: sha512-foD6r3juidAT1cOZzpmD/gOKt7fRsDhXXZ0y28+Al1CHgX+AY1qIN9VSIIItXRq1dN68QrRwl1ORFlwjBaAqeQ==} - engines: {node: '>=18', pnpm: '8'} + /react-hook-form@7.50.1(react@18.2.0): + resolution: {integrity: sha512-3PCY82oE0WgeOgUtIr3nYNNtNvqtJ7BZjsbxh6TnYNbXButaD5WpjOmTjdxZfheuHKR68qfeFnEDVYoSSFPMTQ==} + engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 dependencies: @@ -4599,6 +4551,14 @@ packages: glob: 7.2.3 dev: true + /rimraf@5.0.5: + resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 10.3.10 + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -4640,6 +4600,7 @@ packages: /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + dev: true /semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} @@ -4703,6 +4664,11 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + dev: true + /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: @@ -4836,7 +4802,7 @@ packages: engines: {node: '>=8'} dev: true - /styled-jsx@5.1.1(@babel/core@7.23.7)(react@18.2.0): + /styled-jsx@5.1.1(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} peerDependencies: @@ -4849,7 +4815,6 @@ packages: babel-plugin-macros: optional: true dependencies: - '@babel/core': 7.23.7 client-only: 0.0.1 react: 18.2.0 dev: false @@ -4872,6 +4837,7 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 + dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -4966,6 +4932,7 @@ packages: /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} + dev: true /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -5105,6 +5072,7 @@ packages: browserslist: 4.22.2 escalade: 3.1.1 picocolors: 1.0.0 + dev: true /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -5237,9 +5205,6 @@ packages: engines: {node: '>=10'} dev: true - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} diff --git a/prettier.config.js b/prettier.config.js deleted file mode 100644 index c72df58..0000000 --- a/prettier.config.js +++ /dev/null @@ -1,33 +0,0 @@ -/** @type {import('prettier').Config} */ -module.exports = { - endOfLine: "lf", - semi: false, - singleQuote: false, - tabWidth: 2, - trailingComma: "es5", - importOrder: [ - "^(react/(.*)$)|^(react$)", - "^(next/(.*)$)|^(next$)", - "", - "", - "^types$", - "^@/env(.*)$", - "^@/types/(.*)$", - "^@/config/(.*)$", - "^@/lib/(.*)$", - "^@/hooks/(.*)$", - "^@/components/ui/(.*)$", - "^@/components/(.*)$", - "^@/styles/(.*)$", - "^@/app/(.*)$", - "", - "^[./]", - ], - importOrderSeparation: false, - importOrderSortSpecifiers: true, - importOrderBuiltinModulesToTop: true, - importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], - importOrderMergeDuplicateImports: true, - importOrderCombineTypeAndValueImports: true, - plugins: ["@ianvs/prettier-plugin-sort-imports"], -} diff --git a/tailwind.config.ts b/tailwind.config.ts index a2cd264..37ff386 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,4 +1,5 @@ import type { Config } from "tailwindcss"; + const { fontFamily } = require("tailwindcss/defaultTheme"); const config = { @@ -20,7 +21,7 @@ const config = { }, extend: { scale: { - '102': '1.02', + "102": "1.02", }, fontFamily: { sans: ["var(--font-sans)", ...fontFamily.sans], diff --git a/types/account.ts b/types/account.ts index e02fdca..10b998f 100644 --- a/types/account.ts +++ b/types/account.ts @@ -1,6 +1,6 @@ -import { z } from "zod" +import { z } from "zod"; -import { ZApiKey } from "./apiKey" +import { ZApiKey } from "./apiKey"; export const ZAccountInput = z.object({ userId: z.string(), @@ -13,9 +13,9 @@ export const ZAccountInput = z.object({ scope: z.string().nullish(), token_type: z.string().nullish(), id_token: z.string().nullish(), -}) +}); -export type TAccountInput = z.infer +export type TAccountInput = z.infer; export const ZAccount = z.object({ id: z.string(), @@ -32,6 +32,6 @@ export const ZAccount = z.object({ token_type: z.string().nullable(), id_token: z.string().nullable(), apiKeys: z.array(ZApiKey), -}) +}); -export type TAccount = z.infer +export type TAccount = z.infer; diff --git a/types/apiKey.ts b/types/apiKey.ts index 5326bdb..4fc78f7 100644 --- a/types/apiKey.ts +++ b/types/apiKey.ts @@ -1,4 +1,4 @@ -import { z } from "zod" +import { z } from "zod"; export const ZApiKey = z.object({ id: z.string().cuid2(), @@ -7,11 +7,11 @@ export const ZApiKey = z.object({ hashedKey: z.string(), repositoryId: z.string().cuid2(), apiKey: z.string().optional(), -}) +}); -export type TApiKey = z.infer +export type TApiKey = z.infer; export const ZApiKeyCreateInput = z.object({ label: z.string(), -}) -export type TApiKeyCreateInput = z.infer +}); +export type TApiKeyCreateInput = z.infer; diff --git a/types/installation.ts b/types/installation.ts index 6331daa..da57372 100644 --- a/types/installation.ts +++ b/types/installation.ts @@ -1,9 +1,9 @@ -import { z } from "zod" +import { z } from "zod"; -import { ZMembership } from "./membership" -import { ZRepository } from "./repository" +import { ZMembership } from "./membership"; +import { ZRepository } from "./repository"; -const InstallationTypeEnum = z.enum(["TYPE1", "TYPE2"]) +const InstallationTypeEnum = z.enum(["TYPE1", "TYPE2"]); export const ZInstallation = z.object({ id: z.string().cuid2(), @@ -11,6 +11,6 @@ export const ZInstallation = z.object({ type: InstallationTypeEnum, memberships: z.array(ZMembership), repositories: z.array(ZRepository), -}) +}); -export type TInstallation = z.infer +export type TInstallation = z.infer; diff --git a/types/level.ts b/types/level.ts index 39e4985..65c8436 100644 --- a/types/level.ts +++ b/types/level.ts @@ -1,8 +1,8 @@ -import { z } from "zod" +import { z } from "zod"; export const ZLevel = z.object({ name: z.string(), pointThreshold: z.string(), -}) +}); -export type TLevel = z.infer +export type TLevel = z.infer; diff --git a/types/membership.ts b/types/membership.ts index 3fd69d3..05f2c2b 100644 --- a/types/membership.ts +++ b/types/membership.ts @@ -1,9 +1,9 @@ -import { z } from "zod" +import { z } from "zod"; -import { ZInstallation } from "./installation" -import { ZUser } from "./user" +import { ZInstallation } from "./installation"; +import { ZUser } from "./user"; -const MembershipRoleEnum = z.enum(["owner", "member"]) +const MembershipRoleEnum = z.enum(["owner", "member"]); export const ZMembership = z.object({ installationId: z.string().cuid2(), @@ -11,6 +11,6 @@ export const ZMembership = z.object({ role: MembershipRoleEnum, installation: ZInstallation, user: ZUser, -}) +}); -export type TMembership = z.infer +export type TMembership = z.infer; diff --git a/types/next-auth.d.ts b/types/next-auth.d.ts index ab96afd..cbe08c3 100644 --- a/types/next-auth.d.ts +++ b/types/next-auth.d.ts @@ -1,21 +1,21 @@ -import { User } from "next-auth" -import { JWT } from "next-auth/jwt" +import { User } from "next-auth"; +import { JWT } from "next-auth/jwt"; -type UserId = string +type UserId = string; declare module "next-auth/jwt" { interface JWT { - id: UserId + id: UserId; } } declare module "next-auth" { interface Session { user: { - id: string - name: string - email: string - avatarUrl: string - } + id: string; + name: string; + email: string; + avatarUrl: string; + }; } } diff --git a/types/pointTransaction.ts b/types/pointTransaction.ts index eda2edb..bbcedc6 100644 --- a/types/pointTransaction.ts +++ b/types/pointTransaction.ts @@ -1,7 +1,7 @@ -import { z } from "zod" +import { z } from "zod"; -import { ZRepository } from "./repository" -import { ZUser } from "./user" +import { ZRepository } from "./repository"; +import { ZUser } from "./user"; export const ZPointTransaction = z.object({ id: z.string().cuid2(), @@ -14,6 +14,6 @@ export const ZPointTransaction = z.object({ updatedAt: z.date(), user: ZUser, repository: ZRepository, -}) +}); -export type TPointTransaction = z.infer +export type TPointTransaction = z.infer; diff --git a/types/repository.ts b/types/repository.ts index 6dd9e17..6260a35 100644 --- a/types/repository.ts +++ b/types/repository.ts @@ -1,8 +1,8 @@ -import { z } from "zod" +import { z } from "zod"; -import { ZInstallation } from "./installation" -import { ZLevel } from "./level" -import { ZPointTransaction } from "./pointTransaction" +import { ZInstallation } from "./installation"; +import { ZLevel } from "./level"; +import { ZPointTransaction } from "./pointTransaction"; export const ZRepository = z.object({ id: z.string().cuid2(), @@ -14,9 +14,9 @@ export const ZRepository = z.object({ default_branch: z.string(), installationId: z.string().cuid2(), levels: z.array(ZLevel), -}) +}); -export type TRepository = z.infer +export type TRepository = z.infer; export const ZRepositoryCreateInput = z.object({ githubId: z.string(), @@ -27,6 +27,6 @@ export const ZRepositoryCreateInput = z.object({ default_branch: z.string(), installationId: z.string().cuid2(), levels: z.array(ZLevel).optional(), -}) +}); -export type TRepositoryCreateInput = z.infer +export type TRepositoryCreateInput = z.infer; diff --git a/types/user.ts b/types/user.ts index f209e9e..9aaa526 100644 --- a/types/user.ts +++ b/types/user.ts @@ -1,8 +1,8 @@ -import { z } from "zod" +import { z } from "zod"; -import { ZAccount } from "./account" -import { ZMembership } from "./membership" -import { ZPointTransaction } from "./pointTransaction" +import { ZAccount } from "./account"; +import { ZMembership } from "./membership"; +import { ZPointTransaction } from "./pointTransaction"; export const ZUser = z.object({ id: z.string().cuid2(), @@ -17,6 +17,6 @@ export const ZUser = z.object({ accounts: z.array(ZAccount), pointTransactions: z.array(ZPointTransaction), memberships: z.array(ZMembership), -}) +}); -export type TUser = z.infer +export type TUser = z.infer; From 229ec3d935c6fc6075734b9b50368120cf653a92 Mon Sep 17 00:00:00 2001 From: Matthias Nannt Date: Wed, 7 Feb 2024 15:37:17 +0100 Subject: [PATCH 7/8] update lint config & fix errors --- .eslintrc.json | 31 ----- app/(marketing)/page.tsx | 116 +++++++++-------- app/[profile]/page.tsx | 2 +- package.json | 2 +- pnpm-lock.yaml | 273 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 320 insertions(+), 104 deletions(-) delete mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 0ba73ae..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": [ - "next/core-web-vitals", - "prettier", - "plugin:tailwindcss/recommended" - ], - "plugins": ["tailwindcss"], - "rules": { - "@next/next/no-html-link-for-pages": "off", - "react/jsx-key": "off", - "tailwindcss/no-custom-classname": "off", - "tailwindcss/classnames-order": "error" - }, - "settings": { - "tailwindcss": { - "callees": ["cn"], - "config": "tailwind.config.js" - }, - "next": { - "rootDir": true - } - }, - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parser": "@typescript-eslint/parser" - } - ] -} diff --git a/app/(marketing)/page.tsx b/app/(marketing)/page.tsx index f15de3b..aefe13b 100644 --- a/app/(marketing)/page.tsx +++ b/app/(marketing)/page.tsx @@ -72,15 +72,15 @@ export default async function IndexPage() {

- What’s oss.gg, why we need it and how you can become a part of it 🕹️ + What's oss.gg, why we need it and how you can become a part of it 🕹️

The concept of oss.gg emerged from a common challenge faced by many open source founders and maintainers: the struggle to balance community contributions while fully focusing on developing - the core product. In this write-up, we'll walk you through the most common problems around + the core product. In this write-up, we'll walk you through the most common problems around community contributions, what has been tried to solve them, what worked well and how oss.gg - tries to innovate in this space. Let's dive in! + tries to innovate in this space. Let's dive in!

@@ -91,9 +91,9 @@ export default async function IndexPage() { community.

- Our experience isn’t unique. As we are building Formnbricks, we got to know many more open source - founders and all of them made a similar experience. These are 8 problems we all face, in some - variation: + Our experience isn't unique. As we are building Formnbricks, we got to know many more open + source founders and all of them made a similar experience. These are 8 problems we all face, in + some variation:

Problems

@@ -110,7 +110,7 @@ export default async function IndexPage() {

- Before we dive in, let’s have a look at what you can expect from the rest of this article: + Before we dive in, let's have a look at what you can expect from the rest of this article:

  1. @@ -127,7 +127,7 @@ export default async function IndexPage() {
  2. - What's been tried (and what we’ve learned) + What's been tried (and what we've learned)
  3. @@ -148,15 +148,15 @@ export default async function IndexPage() {

- What’s the purpose of this blog post? + What's the purpose of this blog post?

We want to catalyse the conversation.

There is something in the air right now, lots of people work on different ideas to one or several - of these problems. Most do it as a side project, which likely won’t work out, because community - stuff is hard. The idea is to bring everyone onto the same page so we can move forward with a - joint understanding and build something that works together. So everything laid out in this - article is up for discussion, it’s supposed to get you involved 😉 + of these problems. Most do it as a side project, which likely won't work out, because + community stuff is hard. The idea is to bring everyone onto the same page so we can move forward + with a joint understanding and build something that works together. So everything laid out in this + article is up for discussion, it's supposed to get you involved 😉

When the community changes, a new set of issues arises. These issues call for a new set of tools, - one of which is oss.gg - but let’s look at what we’re solving first: + one of which is oss.gg - but let's look at what we're solving first:

The problem(s) @@ -193,28 +193,30 @@ export default async function IndexPage() { Handholding required: Many of the engineers wanting to contribute have little experience in software engineering or web development. They ask a lot of questions, get stuck, need help. While maintainers are - usually happy to help the first set of contributors, it quickly gets out of hand. We’ve referred - juniors to ChatGPT a lot, but some things need to be explained. While it’s fun to teach, we have - to make sure the efforts are not in vain because the contributor does not stick around. + usually happy to help the first set of contributors, it quickly gets out of hand. We've + referred juniors to ChatGPT a lot, but some things need to be explained. While it's fun to + teach, we have to make sure the efforts are not in vain because the contributor does not stick + around.
  • Seniors leaving: - Every now and then, a more senior engineer gets excited by a project and contributes. That’s - obviously really cool and valuable. The issue? They usually don’t stick around. While it’s not - an issue that a company with a commercialisation interest maintains a project (vs. a completely - free and community-run project) these seniors make 1, 2, 3 contributions and then ask - themselves, why they are spending their evenings and weekends creating value which will be - harvest by someone else. Curiosity and altruism only go so far. So why not pay them?{" "} + Every now and then, a more senior engineer gets excited by a project and contributes. + That's obviously really cool and valuable. The issue? They usually don't stick around. + While it's not an issue that a company with a commercialisation interest maintains a + project (vs. a completely free and community-run project) these seniors make 1, 2, 3 + contributions and then ask themselves, why they are spending their evenings and weekends + creating value which will be harvest by someone else. Curiosity and altruism only go so far. So + why not pay them?{" "}
  • Incentive dilemma: Incentives are a funny thing because they can shift the mental model for looking at something. There is lots of research proving that a financial incentive can lower the motivation for a specific task, because it takes the aspects of reciprocity and signalling out of the picture: If - I receive money for it, I won’t get a favour back nor can I tell myself I worked for the greater - good and feel good about it. This, and the short-term thinking attached to doing the minimum to - get the financial reward make it difficult to just hang a price tag to an issue and wait for - someone to fix it properly. In most cases, it’s a quick and dirty patch. + I receive money for it, I won't get a favour back nor can I tell myself I worked for the + greater good and feel good about it. This, and the short-term thinking attached to doing the + minimum to get the financial reward make it difficult to just hang a price tag to an issue and + wait for someone to fix it properly. In most cases, it's a quick and dirty patch.
  • Review overhead: @@ -229,13 +231,13 @@ export default async function IndexPage() { People keep coming back when they feel recognized. It not only feels good to be appreciated for the time invested but is also an important signal, for both side: Contributors can showcase their work publicly (and share it) and projects show that their project is actively contributed - to. Win win. The only issue? It’s time-consuming to keep track, write content and find the + to. Win win. The only issue? It's time-consuming to keep track, write content and find the social handles of contributors to tag them. This can be semi-automated.
  • Hard to flex: Along with the projects effort to recognize contributors, these contributors have a hard time - flexing with their contributions. There isn’t really a good plaform to do that. Important + flexing with their contributions. There isn't really a good plaform to do that. Important contributions on GitHub profiles get lost in other activity. CVs are outdated. Contributors want a publicly accessible page where they can collect contributions like stickers.
  • @@ -243,15 +245,15 @@ export default async function IndexPage() { Assignment overhead & redundant work: Keeping track of who works on which issue can get messy with an active community. We ran a hackathon with a Macbook prize and for every issue we released we had 5 people who wanted to - work on it. First come, first assigned works well, until people claim issues and don’t deliver. - This can be solved by automating the enforcement of rules such as “If you don’t open a draft PR - within 48h, we will assign the issue to the next developer in line”. + work on it. First come, first assigned works well, until people claim issues and don't + deliver. This can be solved by automating the enforcement of rules such as “If you don't + open a draft PR within 48h, we will assign the issue to the next developer in line”.
  • Unfinished PRs Along the lines of the previous problem: If contributors drop out, maintainers have to notice. - To be able to do that we have to monitor all issues manually. It’d be much easier if a GitHub - app would do that for us. + To be able to do that we have to monitor all issues manually. It'd be much easier if a + GitHub app would do that for us.
  • @@ -288,7 +290,7 @@ export default async function IndexPage() {
  • There is enough excitement for it to be worth building a system
  • - Novu’s Hero Directory + Novu's Hero Directory

    Novu built a pretty cool directory of their{" "} @@ -302,32 +304,32 @@ export default async function IndexPage() { done 😎

    - Algora’s Bounty System + Algora's Bounty System

    The Algora team did a great job building a well-functioning GitHub app as well as attracting a community of bounty-hungry contributors. However, tying back to what I wrote above about - incentives, I don’t think it solve the right problem. In our experience (and what I’ve heard from - other projects) it’s not hard to find people who want to contribute. It’s much harder to handle - the contributions and keep people familiar with the code base engaged over longer periods of time. - To some extent, bounties make the situation worse by sending engineers our way who are only - looking for a quick buck. They tend to be more spammy and push for a quick review of their - half-baked code. This might sound harsh but it has been exactly our experience. + incentives, I don't think it solve the right problem. In our experience (and what I've + heard from other projects) it's not hard to find people who want to contribute. It's + much harder to handle the contributions and keep people familiar with the code base engaged over + longer periods of time. To some extent, bounties make the situation worse by sending engineers our + way who are only looking for a quick buck. They tend to be more spammy and push for a quick review + of their half-baked code. This might sound harsh but it has been exactly our experience.

    We strongly believe in bounties and they are an essential element of oss.gg but they have to be wrapped in a bigger picture (see below).

    - Please note: I’m fully aware that many projects are happy with Algora and their - success justifies their approach :) + Please note: I'm fully aware that many projects are happy with Algora and + their success justifies their approach :)

    What is oss.gg?

    - So far we looked at the problems and what has been tried to solve some of them. Let’s now have a - closer look at how we imagine oss.gg solve for all of the problems above. + So far we looked at the problems and what has been tried to solve some of them. Let's now + have a closer look at how we imagine oss.gg solve for all of the problems above.

    oss.gg - the concept @@ -350,14 +352,14 @@ export default async function IndexPage() { to do what. Here is an overview:

    so much fun -

    How does this help us? Let’s look at the problems:

    +

    How does this help us? Let's look at the problems:

    • Handholding: By limiting who can get assigned to e.g. issues for end-to-end features, we limit the requests for handholding significantly. We know that every “Deploy Deputy” already went through a set of DevRel related tasks so is somewhat familiar with the code base and tech stack. Secondly, we can filter out a lot of the “I can quickly ship this” - engineers. Additionally, we as a team don’t have to help dozens of engineers set up their + engineers. Additionally, we as a team don't have to help dozens of engineers set up their development environment by limiting support to GitPod when contributors are getting started.
    • @@ -417,7 +419,7 @@ export default async function IndexPage() { oss.gg - the tech

    - Now that we have an understanding of what we want to build and why, let’s talk about the how. + Now that we have an understanding of what we want to build and why, let's talk about the how. oss.gg consists of 5 different parts:

      @@ -427,7 +429,7 @@ export default async function IndexPage() {
    1. A bounty system +
    2. An automations interface
    -

    Let’s go through them one by one:

    +

    Let's go through them one by one:

    1. A Web App

    @@ -477,7 +479,7 @@ export default async function IndexPage() { Assign first contributor to issue, if eligible (has enough points / level required for this issue) -
  • Unassign and reassign if contributor doesn’t ship
  • +
  • Unassign and reassign if contributor doesn't ship
  • Follows up with dormant PRs
  • Makes awarding points easy “/award 500 points”
  • Makes tipping easy “/tip 10$”
  • @@ -487,9 +489,9 @@ export default async function IndexPage() { 4. A Bounty System

    - Shoutout to Bailey who built a low-fee bounty system for the OSSHack in NYC. It’s a very - convenient and inexpensive way to pay out bounties worldwide. We’ll leverage that for anyone who - wants to open a bounty for an issue. + Shoutout to Bailey who built a low-fee bounty system for the OSSHack in NYC. It's a very + convenient and inexpensive way to pay out bounties worldwide. We'll leverage that for anyone + who wants to open a bounty for an issue.

    5. An Automations Interface (later on) @@ -519,8 +521,8 @@ export default async function IndexPage() { What is the status quo?

    - We’ve shipped the core for this launch so that we as a community can pick up the fun stuff right - away. Here is a broad plan forward: + We've shipped the core for this launch so that we as a community can pick up the fun stuff + right away. Here is a broad plan forward:

    Phase 1: To keep iteration cycles short, we limit the number of projects who can @@ -558,7 +560,7 @@ export default async function IndexPage() {

    🕹️

    - Let's play + Let's play
    diff --git a/app/[profile]/page.tsx b/app/[profile]/page.tsx index c367251..56ed7de 100644 --- a/app/[profile]/page.tsx +++ b/app/[profile]/page.tsx @@ -30,7 +30,7 @@ export default async function ProfilePage({ params }) {
    diff --git a/package.json b/package.json index 66f5ee6..b004b64 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "react-dom": "^18", "react-hook-form": "^7.50.1", "react-icons": "^5.0.1", + "sharp": "^0.33.2", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", "zod": "^3.22.4" @@ -58,7 +59,6 @@ "eslint-config-next": "14.1.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-tailwindcss": "^3.14.2", "husky": "^9.0.10", "postcss": "^8", "prettier": "^3.2.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 881afb2..92c3573 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,6 +80,9 @@ dependencies: react-icons: specifier: ^5.0.1 version: 5.0.1(react@18.2.0) + sharp: + specifier: ^0.33.2 + version: 0.33.2 tailwind-merge: specifier: ^2.2.1 version: 2.2.1 @@ -124,9 +127,6 @@ devDependencies: eslint-plugin-react: specifier: ^7.33.2 version: 7.33.2(eslint@8.56.0) - eslint-plugin-tailwindcss: - specifier: ^3.14.2 - version: 3.14.2(tailwindcss@3.4.1) husky: specifier: ^9.0.10 version: 9.0.10 @@ -458,6 +458,14 @@ packages: chalk: 4.1.2 dev: true + /@emnapi/runtime@0.45.0: + resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -551,6 +559,194 @@ packages: resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} dev: true + /@img/sharp-darwin-arm64@0.33.2: + resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-darwin-x64@0.33.2: + resolution: {integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-libvips-darwin-arm64@1.0.1: + resolution: {integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==} + engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-darwin-x64@1.0.1: + resolution: {integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==} + engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm64@1.0.1: + resolution: {integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm@1.0.1: + resolution: {integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-s390x@1.0.1: + resolution: {integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-x64@1.0.1: + resolution: {integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-arm64@1.0.1: + resolution: {integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-x64@1.0.1: + resolution: {integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-linux-arm64@0.33.2: + resolution: {integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-arm@0.33.2: + resolution: {integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-s390x@0.33.2: + resolution: {integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-x64@0.33.2: + resolution: {integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linuxmusl-arm64@0.33.2: + resolution: {integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linuxmusl-x64@0.33.2: + resolution: {integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-wasm32@0.33.2: + resolution: {integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@emnapi/runtime': 0.45.0 + dev: false + optional: true + + /@img/sharp-win32-ia32@0.33.2: + resolution: {integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-win32-x64@0.33.2: + resolution: {integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2218,6 +2414,21 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: false + /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -2385,6 +2596,11 @@ packages: engines: {node: '>=6'} dev: true + /detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + dev: false + /detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false @@ -2750,17 +2966,6 @@ packages: string.prototype.matchall: 4.0.10 dev: true - /eslint-plugin-tailwindcss@3.14.2(tailwindcss@3.4.1): - resolution: {integrity: sha512-fNzdf4poZP2yQC0xC2prQxMuArMSb5mnellLQvwb9HC3NcLzxs+0IVKWIg1BqUqyui0c+bbjMmhWcKUWK67SLQ==} - engines: {node: '>=12.13.0'} - peerDependencies: - tailwindcss: ^3.4.0 - dependencies: - fast-glob: 3.3.2 - postcss: 8.4.33 - tailwindcss: 3.4.1 - dev: true - /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3252,6 +3457,10 @@ packages: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + /is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} @@ -4629,6 +4838,36 @@ packages: has-property-descriptors: 1.0.1 dev: true + /sharp@0.33.2: + resolution: {integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==} + engines: {libvips: '>=8.15.1', node: ^18.17.0 || ^20.3.0 || >=21.0.0} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.2 + semver: 7.5.4 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.2 + '@img/sharp-darwin-x64': 0.33.2 + '@img/sharp-libvips-darwin-arm64': 1.0.1 + '@img/sharp-libvips-darwin-x64': 1.0.1 + '@img/sharp-libvips-linux-arm': 1.0.1 + '@img/sharp-libvips-linux-arm64': 1.0.1 + '@img/sharp-libvips-linux-s390x': 1.0.1 + '@img/sharp-libvips-linux-x64': 1.0.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + '@img/sharp-linux-arm': 0.33.2 + '@img/sharp-linux-arm64': 0.33.2 + '@img/sharp-linux-s390x': 0.33.2 + '@img/sharp-linux-x64': 0.33.2 + '@img/sharp-linuxmusl-arm64': 0.33.2 + '@img/sharp-linuxmusl-x64': 0.33.2 + '@img/sharp-wasm32': 0.33.2 + '@img/sharp-win32-ia32': 0.33.2 + '@img/sharp-win32-x64': 0.33.2 + dev: false + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -4655,6 +4894,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} From 05c6a8d3e017674120c33ffd8137d054c541ac59 Mon Sep 17 00:00:00 2001 From: Matthias Nannt Date: Wed, 7 Feb 2024 15:45:16 +0100 Subject: [PATCH 8/8] fix migration --- .eslintrc.js | 18 ++++++ app/(dashboard)/dashboard/layout.tsx | 56 ++++++++++++------- .../migration.sql | 6 -- 3 files changed, 53 insertions(+), 27 deletions(-) create mode 100644 .eslintrc.js rename prisma/migrations/{20240119162927_add_api_keys => 20240207144141_add_api_keys}/migration.sql (81%) diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..5cd7940 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,18 @@ +module.exports = { + extends: ["next", "prettier"], + rules: { + "@next/next/no-html-link-for-pages": "off", + "react/jsx-key": "off", + }, + settings: { + next: { + rootDir: true, + }, + }, + overrides: [ + { + files: ["*.ts", "*.tsx"], + parser: "@typescript-eslint/parser", + }, + ], +}; diff --git a/app/(dashboard)/dashboard/layout.tsx b/app/(dashboard)/dashboard/layout.tsx index 688103f..d120610 100644 --- a/app/(dashboard)/dashboard/layout.tsx +++ b/app/(dashboard)/dashboard/layout.tsx @@ -1,11 +1,14 @@ -import { MainNav } from "@/components/main-nav"; import { DashboardNav } from "@/components/nav"; -import { SiteFooter } from "@/components/site-footer"; 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; } @@ -18,26 +21,37 @@ export default async function DashboardLayout({ children }: DashboardLayoutProps } return ( -
    -
    -
    - - +
    + + oss gg logo + +
    -
    - -
    {children}
    -
    - + +
    {children}
    ); } diff --git a/prisma/migrations/20240119162927_add_api_keys/migration.sql b/prisma/migrations/20240207144141_add_api_keys/migration.sql similarity index 81% rename from prisma/migrations/20240119162927_add_api_keys/migration.sql rename to prisma/migrations/20240207144141_add_api_keys/migration.sql index b3a10bb..0d8235f 100644 --- a/prisma/migrations/20240119162927_add_api_keys/migration.sql +++ b/prisma/migrations/20240207144141_add_api_keys/migration.sql @@ -1,9 +1,3 @@ --- AlterTable -ALTER TABLE "users" ADD COLUMN "city" TEXT, -ADD COLUMN "country" TEXT, -ADD COLUMN "postalCode" TEXT, -ADD COLUMN "state" TEXT; - -- CreateTable CREATE TABLE "api_keys" ( "id" TEXT NOT NULL,