feat: monorepo
This commit is contained in:
50
apps/web/src/pages/_app.tsx
Normal file
50
apps/web/src/pages/_app.tsx
Normal 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);
|
||||
90
apps/web/src/pages/api/auth/confirm.ts
Normal file
90
apps/web/src/pages/api/auth/confirm.ts
Normal 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";
|
||||
26
apps/web/src/pages/api/trpc/[trpc].ts
Normal file
26
apps/web/src/pages/api/trpc/[trpc].ts
Normal 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";
|
||||
31
apps/web/src/pages/api/v1/[...trpc].ts
Normal file
31
apps/web/src/pages/api/v1/[...trpc].ts
Normal 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);
|
||||
}
|
||||
9
apps/web/src/pages/api/v1/openapi.json.ts
Normal file
9
apps/web/src/pages/api/v1/openapi.json.ts
Normal 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;
|
||||
15
apps/web/src/pages/boards/[...boardId]/index.tsx
Normal file
15
apps/web/src/pages/boards/[...boardId]/index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
15
apps/web/src/pages/boards/index.tsx
Normal file
15
apps/web/src/pages/boards/index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
15
apps/web/src/pages/cards/[cardId]/index.tsx
Normal file
15
apps/web/src/pages/cards/[cardId]/index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
5
apps/web/src/pages/index.tsx
Normal file
5
apps/web/src/pages/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import HomeView from "~/views/home";
|
||||
|
||||
export default function Home() {
|
||||
return <HomeView />;
|
||||
}
|
||||
5
apps/web/src/pages/login/index.tsx
Normal file
5
apps/web/src/pages/login/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import LoginView from "~/views/auth/login";
|
||||
|
||||
export default function LoginPage() {
|
||||
return <LoginView />;
|
||||
}
|
||||
15
apps/web/src/pages/members/index.tsx
Normal file
15
apps/web/src/pages/members/index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
15
apps/web/src/pages/settings/index.tsx
Normal file
15
apps/web/src/pages/settings/index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
5
apps/web/src/pages/signup/index.tsx
Normal file
5
apps/web/src/pages/signup/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import SignupView from "~/views/auth/signup";
|
||||
|
||||
export default function SignupPage() {
|
||||
return <SignupView />;
|
||||
}
|
||||
Reference in New Issue
Block a user