feat: monorepo

This commit is contained in:
Henry
2024-12-12 14:34:10 +00:00
parent b8eed7a90c
commit 0c8d17dce5
370 changed files with 10280 additions and 39805 deletions

View File

@@ -0,0 +1,50 @@
import "~/styles/globals.css";
import { type AppType } from "next/app";
import { Plus_Jakarta_Sans } from "next/font/google";
import { ModalProvider } from "~/providers/modal";
import { BoardProvider } from "~/providers/board";
import { PopupProvider } from "~/providers/popup";
import { ThemeProvider } from "~/providers/theme";
import { api } from "~/utils/api";
const jakarta = Plus_Jakarta_Sans({
subsets: ["latin"],
display: "swap",
});
export const metadata = {
title: "Kan",
description: "The open source Trello alternative",
icons: [{ rel: "icon", url: "/favicon.ico" }],
};
const MyApp: AppType = ({ Component, pageProps }) => {
return (
<>
<style jsx global>{`
html {
font-family: ${jakarta.style.fontFamily};
}
body {
position: relative;
}
`}</style>
<main className="font-sans">
<ThemeProvider>
<ModalProvider>
<PopupProvider>
<BoardProvider>
<Component {...pageProps} />
</BoardProvider>
</PopupProvider>
</ModalProvider>
</ThemeProvider>
</main>
</>
);
};
export default api.withTRPC(MyApp);

View File

@@ -0,0 +1,90 @@
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { type EmailOtpType } from "@supabase/supabase-js";
import * as memberRepo from "@kan/db/repository/member.repo";
import * as userRepo from "@kan/db/repository/user.repo";
import { createNextClient } from "@kan/supabase/clients";
export default async function handler(req: NextRequest) {
if (req.method !== "GET") {
return new NextResponse(null, {
status: 405,
headers: { Allow: "GET" },
});
}
if (!req.url) {
return new NextResponse(null, {
status: 400,
});
}
const url = new URL(req.url);
const queryParams = Object.fromEntries(url.searchParams.entries());
const tokenHash = queryParams.token_hash;
const type = queryParams.type;
const code = queryParams.code;
const memberPublicId = queryParams.memberPublicId;
let next = "/error";
let authRes;
const response = NextResponse.next();
if ((tokenHash && type) ?? code) {
const db = createNextClient(req, response);
if (tokenHash && type) {
authRes = await db.auth.verifyOtp({
type: type as EmailOtpType,
token_hash: tokenHash,
});
}
if (code) {
authRes = await db.auth.exchangeCodeForSession(code);
}
const user = authRes?.data.user;
if (user?.id && user.email) {
const existingUser = await userRepo.getById(db, user.id);
if (!existingUser) {
await userRepo.create(db, {
id: user.id,
email: user.email,
});
}
}
if (memberPublicId) {
const member = await memberRepo.getByPublicId(db, memberPublicId);
if (member?.id) {
await memberRepo.acceptInvite(db, member.id);
}
}
if (authRes?.error) {
console.error(authRes.error);
} else {
next = queryParams.next ?? "/";
}
}
const redirectResponse = NextResponse.redirect(new URL(next, req.url));
response.headers.getSetCookie().forEach((cookie) => {
redirectResponse.headers.append("Set-Cookie", cookie);
});
return redirectResponse;
}
export const runtime = "edge";
export const preferredRegion = "lhr1";
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,26 @@
import { type NextRequest } from "next/server";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@kan/api/root";
import { createTRPCContext } from "@kan/api/trpc";
export default async function handler(req: NextRequest) {
return fetchRequestHandler({
endpoint: "/api/trpc",
router: appRouter,
req,
createContext: createTRPCContext,
onError:
process.env.NODE_ENV === "development"
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
);
}
: undefined,
});
}
export const runtime = "edge";
export const preferredRegion = "lhr1";
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,31 @@
import { type NextApiRequest, type NextApiResponse } from "next";
import cors from "nextjs-cors";
import { createOpenApiNextHandler } from "trpc-to-openapi";
import { appRouter } from "@kan/api";
import { createRESTContext } from "@kan/api/trpc";
import { env } from "~/env";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
await cors(req, res);
const openApiHandler = createOpenApiNextHandler({
router: appRouter,
createContext: createRESTContext,
responseMeta: () => ({ headers: {} }),
onError:
env.NODE_ENV === "development"
? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
);
}
: undefined,
});
return await openApiHandler(req, res);
}

View File

@@ -0,0 +1,9 @@
import { type NextApiRequest, type NextApiResponse } from "next";
import { openApiDocument } from "@kan/api/openapi";
const handler = (req: NextApiRequest, res: NextApiResponse) => {
res.status(200).send(openApiDocument);
};
export default handler;

View File

@@ -0,0 +1,15 @@
import { WorkspaceProvider } from "~/providers/workspace";
import Dashboard from "~/components/Dashboard";
import Popup from "~/components/Popup";
import BoardView from "~/views/board";
export default function BoardPage() {
return (
<WorkspaceProvider>
<Dashboard>
<BoardView />
</Dashboard>
<Popup />
</WorkspaceProvider>
);
}

View File

@@ -0,0 +1,15 @@
import { WorkspaceProvider } from "~/providers/workspace";
import Dashboard from "~/components/Dashboard";
import Popup from "~/components/Popup";
import BoardsView from "~/views/boards";
export default function BoardsPage() {
return (
<WorkspaceProvider>
<Dashboard>
<BoardsView />
</Dashboard>
<Popup />
</WorkspaceProvider>
);
}

View File

@@ -0,0 +1,15 @@
import { WorkspaceProvider } from "~/providers/workspace";
import Dashboard from "~/components/Dashboard";
import Popup from "~/components/Popup";
import CardView from "~/views/card";
export default function CardPage() {
return (
<WorkspaceProvider>
<Dashboard>
<CardView />
</Dashboard>
<Popup />
</WorkspaceProvider>
);
}

View File

@@ -0,0 +1,5 @@
import HomeView from "~/views/home";
export default function Home() {
return <HomeView />;
}

View File

@@ -0,0 +1,5 @@
import LoginView from "~/views/auth/login";
export default function LoginPage() {
return <LoginView />;
}

View File

@@ -0,0 +1,15 @@
import { WorkspaceProvider } from "~/providers/workspace";
import Dashboard from "~/components/Dashboard";
import Popup from "~/components/Popup";
import MembersView from "~/views/members";
export default function MembersPage() {
return (
<WorkspaceProvider>
<Dashboard>
<MembersView />
</Dashboard>
<Popup />
</WorkspaceProvider>
);
}

View File

@@ -0,0 +1,15 @@
import { WorkspaceProvider } from "~/providers/workspace";
import Dashboard from "~/components/Dashboard";
import SettingsView from "~/views/settings";
import Popup from "~/components/Popup";
export default function SettingsPage() {
return (
<WorkspaceProvider>
<Dashboard>
<SettingsView />
</Dashboard>
<Popup />
</WorkspaceProvider>
);
}

View File

@@ -0,0 +1,5 @@
import SignupView from "~/views/auth/signup";
export default function SignupPage() {
return <SignupView />;
}