App router almost done
This commit is contained in:
parent
b6564b326d
commit
52b3444d4d
27 changed files with 192 additions and 345 deletions
|
@ -1,5 +1,7 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import { GiTechnoHeart } from "react-icons/gi";
|
import { GiTechnoHeart } from "react-icons/gi";
|
||||||
import packagejson from "../../package.json";
|
import packagejson from "../../../package.json";
|
||||||
|
|
||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
return (
|
return (
|
|
@ -1,7 +1,9 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import { UserButton, useUser } from "@clerk/nextjs";
|
import { UserButton, useUser } from "@clerk/nextjs";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter, usePathname } from "next/navigation";
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
|
|
||||||
interface NavbarProps {
|
interface NavbarProps {
|
||||||
|
@ -11,9 +13,10 @@ interface NavbarProps {
|
||||||
const Navbar = ({ title }: NavbarProps) => {
|
const Navbar = ({ title }: NavbarProps) => {
|
||||||
const { isLoaded, isSignedIn } = useUser();
|
const { isLoaded, isSignedIn } = useUser();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
const navigationMenu = () => {
|
const navigationMenu = () => {
|
||||||
if (router.pathname !== "/dashboard" && isSignedIn) {
|
if (pathname !== "/dashboard" && isSignedIn) {
|
||||||
return (
|
return (
|
||||||
<Link className="btn btn-secondary btn-outline mx-2" href="/dashboard">
|
<Link className="btn btn-secondary btn-outline mx-2" href="/dashboard">
|
||||||
Dashboard
|
Dashboard
|
|
@ -1,10 +1,12 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { configureAbly, useChannel } from "@ably-labs/react-hooks";
|
import { configureAbly, useChannel } from "@ably-labs/react-hooks";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { IoEnterOutline, IoTrashBinOutline } from "react-icons/io5";
|
import { IoEnterOutline, IoTrashBinOutline } from "react-icons/io5";
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
import { api } from "~/utils/api";
|
|
||||||
import { useUser } from "@clerk/nextjs";
|
import { useUser } from "@clerk/nextjs";
|
||||||
|
import { trpc } from "../_trpc/client";
|
||||||
|
|
||||||
const RoomList = () => {
|
const RoomList = () => {
|
||||||
const { isSignedIn, user } = useUser();
|
const { isSignedIn, user } = useUser();
|
||||||
|
@ -25,11 +27,11 @@ const RoomList = () => {
|
||||||
const [roomName, setRoomName] = useState<string>("");
|
const [roomName, setRoomName] = useState<string>("");
|
||||||
|
|
||||||
const { data: roomsFromDb, refetch: refetchRoomsFromDb } =
|
const { data: roomsFromDb, refetch: refetchRoomsFromDb } =
|
||||||
api.room.getAll.useQuery(undefined, {
|
trpc.room.getAll.useQuery(undefined, {
|
||||||
enabled: isSignedIn,
|
enabled: isSignedIn,
|
||||||
});
|
});
|
||||||
|
|
||||||
const createRoom = api.room.create.useMutation({});
|
const createRoom = trpc.room.create.useMutation({});
|
||||||
|
|
||||||
const createRoomHandler = () => {
|
const createRoomHandler = () => {
|
||||||
createRoom.mutate({ name: roomName });
|
createRoom.mutate({ name: roomName });
|
||||||
|
@ -39,7 +41,7 @@ const RoomList = () => {
|
||||||
false;
|
false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteRoom = api.room.delete.useMutation({});
|
const deleteRoom = trpc.room.delete.useMutation({});
|
||||||
|
|
||||||
const deleteRoomHandler = (roomId: string) => {
|
const deleteRoomHandler = (roomId: string) => {
|
||||||
if (isSignedIn) {
|
if (isSignedIn) {
|
27
src/app/_trpc/Provider.tsx
Normal file
27
src/app/_trpc/Provider.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import { httpBatchLink } from "@trpc/client";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import superjson from "superjson";
|
||||||
|
|
||||||
|
import { trpc } from "./client";
|
||||||
|
|
||||||
|
export default function Provider({ children }: { children: React.ReactNode }) {
|
||||||
|
const [queryClient] = useState(() => new QueryClient({}));
|
||||||
|
const [trpcClient] = useState(() =>
|
||||||
|
trpc.createClient({
|
||||||
|
links: [
|
||||||
|
httpBatchLink({
|
||||||
|
url: "http://localhost:3000/api/trpc",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
transformer: superjson,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
||||||
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||||
|
</trpc.Provider>
|
||||||
|
);
|
||||||
|
}
|
5
src/app/_trpc/client.ts
Normal file
5
src/app/_trpc/client.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { createTRPCReact } from "@trpc/react-query";
|
||||||
|
|
||||||
|
import { type AppRouter } from "~/server/trpc";
|
||||||
|
|
||||||
|
export const trpc = createTRPCReact<AppRouter>({});
|
17
src/app/api/trpc/[trpc]/route.ts
Normal file
17
src/app/api/trpc/[trpc]/route.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
||||||
|
|
||||||
|
import { appRouter } from "~/server/trpc";
|
||||||
|
import { createTRPCContext } from "~/server/trpc/trpc";
|
||||||
|
|
||||||
|
export const runtime = "edge";
|
||||||
|
export const preferredRegion = ["pdx1"];
|
||||||
|
|
||||||
|
const handler = (req: Request) =>
|
||||||
|
fetchRequestHandler({
|
||||||
|
endpoint: "/api/trpc",
|
||||||
|
req,
|
||||||
|
router: appRouter,
|
||||||
|
createContext: createTRPCContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
export { handler as GET, handler as POST };
|
|
@ -1,7 +1,9 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
|
||||||
import RoomList from "~/components/RoomList";
|
import RoomList from "~/app/_components/RoomList";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
29
src/app/layout.tsx
Normal file
29
src/app/layout.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { ClerkProvider } from "@clerk/nextjs";
|
||||||
|
import Footer from "~/app/_components/Footer";
|
||||||
|
import Navbar from "~/app/_components/Navbar";
|
||||||
|
import "~/styles/globals.css";
|
||||||
|
import Provider from "./_trpc/Provider";
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: "Next.js 13 with Clerk",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<ClerkProvider>
|
||||||
|
<html lang="en" className="h-[100%] w-[100%] fixed overflow-y-auto">
|
||||||
|
<body className="block h-[100%]">
|
||||||
|
<Navbar title="Sprint Padawan" />
|
||||||
|
<div className="flex flex-row items-center justify-center min-h-[calc(100%-114px)]">
|
||||||
|
<Provider>{children}</Provider>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</ClerkProvider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import { type NextPage } from "next";
|
import { type NextPage } from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { EventTypes } from "~/utils/types";
|
import { EventTypes } from "~/utils/types";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useParams } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
IoCheckmarkCircleOutline,
|
IoCheckmarkCircleOutline,
|
||||||
IoCopyOutline,
|
IoCopyOutline,
|
||||||
|
@ -16,7 +18,6 @@ import {
|
||||||
IoSaveOutline,
|
IoSaveOutline,
|
||||||
} from "react-icons/io5";
|
} from "react-icons/io5";
|
||||||
import { GiStarFormation } from "react-icons/gi";
|
import { GiStarFormation } from "react-icons/gi";
|
||||||
import { api } from "~/utils/api";
|
|
||||||
import { configureAbly, useChannel, usePresence } from "@ably-labs/react-hooks";
|
import { configureAbly, useChannel, usePresence } from "@ably-labs/react-hooks";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { FaShieldAlt } from "react-icons/fa";
|
import { FaShieldAlt } from "react-icons/fa";
|
||||||
|
@ -25,6 +26,7 @@ import { env } from "~/env.mjs";
|
||||||
import { downloadCSV, isAdmin, isVIP } from "~/utils/helpers";
|
import { downloadCSV, isAdmin, isVIP } from "~/utils/helpers";
|
||||||
import type { PresenceItem } from "~/utils/types";
|
import type { PresenceItem } from "~/utils/types";
|
||||||
import { useUser } from "@clerk/nextjs";
|
import { useUser } from "@clerk/nextjs";
|
||||||
|
import { trpc } from "~/app/_trpc/client";
|
||||||
|
|
||||||
const Room: NextPage = () => {
|
const Room: NextPage = () => {
|
||||||
const { isSignedIn } = useUser();
|
const { isSignedIn } = useUser();
|
||||||
|
@ -52,21 +54,21 @@ export default Room;
|
||||||
|
|
||||||
const RoomBody = ({}) => {
|
const RoomBody = ({}) => {
|
||||||
const { isSignedIn, user } = useUser();
|
const { isSignedIn, user } = useUser();
|
||||||
const { query } = useRouter();
|
const params = useParams();
|
||||||
const roomId = query.id as string;
|
const roomId = params?.id as string;
|
||||||
|
|
||||||
const [storyNameText, setStoryNameText] = useState<string>("");
|
const [storyNameText, setStoryNameText] = useState<string>("");
|
||||||
const [roomScale, setRoomScale] = useState<string>("");
|
const [roomScale, setRoomScale] = useState<string>("");
|
||||||
const [copied, setCopied] = useState<boolean>(false);
|
const [copied, setCopied] = useState<boolean>(false);
|
||||||
|
|
||||||
const { data: roomFromDb, refetch: refetchRoomFromDb } =
|
const { data: roomFromDb, refetch: refetchRoomFromDb } =
|
||||||
api.room.get.useQuery({ id: roomId });
|
trpc.room.get.useQuery({ id: roomId });
|
||||||
|
|
||||||
const { data: votesFromDb, refetch: refetchVotesFromDb } =
|
const { data: votesFromDb, refetch: refetchVotesFromDb } =
|
||||||
api.vote.getAllByRoomId.useQuery({ roomId });
|
trpc.vote.getAllByRoomId.useQuery({ roomId });
|
||||||
|
|
||||||
const setVoteInDb = api.vote.set.useMutation({});
|
const setVoteInDb = trpc.vote.set.useMutation({});
|
||||||
const setRoomInDb = api.room.set.useMutation({});
|
const setRoomInDb = trpc.room.set.useMutation({});
|
||||||
|
|
||||||
configureAbly({
|
configureAbly({
|
||||||
key: env.NEXT_PUBLIC_ABLY_PUBLIC_KEY,
|
key: env.NEXT_PUBLIC_ABLY_PUBLIC_KEY,
|
|
@ -1,5 +1,9 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import { SignIn } from "@clerk/nextjs";
|
import { SignIn } from "@clerk/nextjs";
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
const SignInPage = () => (
|
const SignInPage = () => (
|
||||||
<div style={styles}>
|
<div style={styles}>
|
||||||
<SignIn path="/sign-in" routing="path" signUpUrl="/sign-up" />
|
<SignIn path="/sign-in" routing="path" signUpUrl="/sign-up" />
|
|
@ -1,5 +1,9 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import { SignUp } from "@clerk/nextjs";
|
import { SignUp } from "@clerk/nextjs";
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
const SignUpPage = () => (
|
const SignUpPage = () => (
|
||||||
<div style={styles}>
|
<div style={styles}>
|
||||||
<SignUp path="/sign-up" routing="path" signInUrl="/sign-in" />
|
<SignUp path="/sign-up" routing="path" signInUrl="/sign-in" />
|
|
@ -1,24 +0,0 @@
|
||||||
import { type AppType } from "next/app";
|
|
||||||
import { ClerkProvider } from "@clerk/nextjs";
|
|
||||||
|
|
||||||
import { api } from "~/utils/api";
|
|
||||||
|
|
||||||
import Footer from "~/components/Footer";
|
|
||||||
import Navbar from "~/components/Navbar";
|
|
||||||
import "~/styles/globals.css";
|
|
||||||
|
|
||||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
|
||||||
return (
|
|
||||||
<ClerkProvider {...pageProps}>
|
|
||||||
<div className="block h-[100%]">
|
|
||||||
<Navbar title="Sprint Padawan" />
|
|
||||||
<div className="flex flex-row items-center justify-center min-h-[calc(100%-114px)]">
|
|
||||||
<Component {...pageProps} />
|
|
||||||
</div>
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
</ClerkProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default api.withTRPC(MyApp);
|
|
|
@ -1,62 +0,0 @@
|
||||||
import { Head, Html, Main, NextScript } from "next/document";
|
|
||||||
|
|
||||||
export default function Document() {
|
|
||||||
return (
|
|
||||||
<Html>
|
|
||||||
<Head>
|
|
||||||
<meta name="application-name" content="Sprint Padawan" />
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
|
||||||
<meta name="apple-mobile-web-app-title" content="Sprint Padawan" />
|
|
||||||
<meta name="description" content="Plan. Sprint. Repeat." />
|
|
||||||
<meta name="format-detection" content="telephone=no" />
|
|
||||||
<meta name="mobile-web-app-capable" content="yes" />
|
|
||||||
<meta name="msapplication-config" content="/icons/browserconfig.xml" />
|
|
||||||
<meta name="msapplication-TileColor" content="#1F2937" />
|
|
||||||
<meta name="msapplication-tap-highlight" content="no" />
|
|
||||||
<meta name="theme-color" content="#1F2937" />
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
||||||
|
|
||||||
<link
|
|
||||||
rel="icon"
|
|
||||||
type="image/png"
|
|
||||||
sizes="32x32"
|
|
||||||
href="/favicon-32x32.png"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="icon"
|
|
||||||
type="image/png"
|
|
||||||
sizes="16x16"
|
|
||||||
href="/favicon-16x16.png"
|
|
||||||
/>
|
|
||||||
<link rel="manifest" href="/manifest.json" />
|
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
|
||||||
<link rel="shortcut icon" href="/favicon.ico" />
|
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary" />
|
|
||||||
<meta name="twitter:url" content="https://sprintpadawan.dev" />
|
|
||||||
<meta name="twitter:title" content="Sprint Padawan" />
|
|
||||||
<meta name="twitter:description" content="Plan. Sprint. Repeat." />
|
|
||||||
<meta
|
|
||||||
name="twitter:image"
|
|
||||||
content="https://sprintpadawan.dev/android-chrome-192x192.png"
|
|
||||||
/>
|
|
||||||
<meta name="twitter:creator" content="@atridadl" />
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:title" content="Sprint Padawan" />
|
|
||||||
<meta property="og:description" content="Plan. Sprint. Repeat." />
|
|
||||||
<meta property="og:site_name" content="Sprint Padawan" />
|
|
||||||
<meta property="og:url" content="https://sprintpadawan.dev" />
|
|
||||||
<meta
|
|
||||||
property="og:image"
|
|
||||||
content="https://sprintpadawan.dev/icons/apple-touch-icon.png"
|
|
||||||
/>
|
|
||||||
</Head>
|
|
||||||
<body>
|
|
||||||
<Main />
|
|
||||||
<NextScript />
|
|
||||||
</body>
|
|
||||||
</Html>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { appRouter } from "~/server/api/root";
|
|
||||||
import { createTRPCContext } from "~/server/api/trpc";
|
|
||||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
|
||||||
import type { NextRequest } from "next/server";
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
runtime: "edge",
|
|
||||||
regions: ["pdx1"],
|
|
||||||
unstable_allowDynamic: ["/node_modules/ably/**"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function handler(req: NextRequest) {
|
|
||||||
return fetchRequestHandler({
|
|
||||||
endpoint: "/api/trpc",
|
|
||||||
router: appRouter,
|
|
||||||
req,
|
|
||||||
createContext: createTRPCContext,
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { appRouter } from "~/server/api/root";
|
|
||||||
import { createTRPCContext } from "~/server/api/trpc";
|
|
||||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
|
||||||
import type { NextRequest } from "next/server";
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
runtime: "edge",
|
|
||||||
regions: ["pdx1"],
|
|
||||||
unstable_allowDynamic: ["/node_modules/ably/**"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function handler(req: NextRequest) {
|
|
||||||
return fetchRequestHandler({
|
|
||||||
endpoint: "/api/trpc",
|
|
||||||
router: appRouter,
|
|
||||||
req,
|
|
||||||
createContext: createTRPCContext,
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { roomRouter } from "~/server/api/routers/room";
|
|
||||||
import { createTRPCRouter } from "~/server/api/trpc";
|
|
||||||
import { voteRouter } from "./routers/vote";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the primary router for your server.
|
|
||||||
*
|
|
||||||
* All routers added in /api/routers should be manually added here.
|
|
||||||
*/
|
|
||||||
export const appRouter = createTRPCRouter({
|
|
||||||
room: roomRouter,
|
|
||||||
vote: voteRouter,
|
|
||||||
});
|
|
||||||
|
|
||||||
// export type definition of API
|
|
||||||
export type AppRouter = typeof appRouter;
|
|
|
@ -1,104 +0,0 @@
|
||||||
/**
|
|
||||||
* YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:
|
|
||||||
* 1. You want to modify request context (see Part 1)
|
|
||||||
* 2. You want to create a new middleware or type of procedure (see Part 3)
|
|
||||||
*
|
|
||||||
* tl;dr - this is where all the tRPC server stuff is created and plugged in.
|
|
||||||
* The pieces you will need to use are documented accordingly near the end
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. CONTEXT
|
|
||||||
*
|
|
||||||
* This section defines the "contexts" that are available in the backend API
|
|
||||||
*
|
|
||||||
* These allow you to access things like the database, the session, etc, when
|
|
||||||
* processing a request
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import { type FetchCreateContextFnOptions } from "@trpc/server/adapters/fetch";
|
|
||||||
import { getAuth } from "@clerk/nextjs/server";
|
|
||||||
import type {
|
|
||||||
SignedInAuthObject,
|
|
||||||
SignedOutAuthObject,
|
|
||||||
} from "@clerk/nextjs/api";
|
|
||||||
|
|
||||||
import { db } from "../db";
|
|
||||||
|
|
||||||
interface AuthContext {
|
|
||||||
auth: SignedInAuthObject | SignedOutAuthObject;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This helper generates the "internals" for a tRPC context. If you need to use
|
|
||||||
* it, you can export it from here
|
|
||||||
*
|
|
||||||
* Examples of things you may need it for:
|
|
||||||
* - testing, so we dont have to mock Next.js' req/res
|
|
||||||
* - trpc's `createSSGHelpers` where we don't have req/res
|
|
||||||
* @see https://create.t3.gg/en/usage/trpc#-servertrpccontextts
|
|
||||||
*/
|
|
||||||
|
|
||||||
const createInnerTRPCContext = ({ auth }: AuthContext) => {
|
|
||||||
return {
|
|
||||||
auth,
|
|
||||||
db,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createTRPCContext = ({ req }: FetchCreateContextFnOptions) => {
|
|
||||||
return createInnerTRPCContext({ auth: getAuth(req as NextRequest) });
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Context = inferAsyncReturnType<typeof createTRPCContext>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 2. INITIALIZATION
|
|
||||||
*
|
|
||||||
* This is where the trpc api is initialized, connecting the context and
|
|
||||||
* transformer
|
|
||||||
*/
|
|
||||||
import { type inferAsyncReturnType, initTRPC, TRPCError } from "@trpc/server";
|
|
||||||
import superjson from "superjson";
|
|
||||||
import type { NextRequest } from "next/server";
|
|
||||||
|
|
||||||
const t = initTRPC.context<typeof createTRPCContext>().create({
|
|
||||||
transformer: superjson,
|
|
||||||
errorFormatter({ shape }) {
|
|
||||||
return shape;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// check if the user is signed in, otherwise through a UNAUTHORIZED CODE
|
|
||||||
const isAuthed = t.middleware(({ next, ctx }) => {
|
|
||||||
if (!ctx.auth.userId) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
|
|
||||||
return next({
|
|
||||||
ctx: {
|
|
||||||
auth: ctx.auth,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
/**
|
|
||||||
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
|
|
||||||
*
|
|
||||||
* These are the pieces you use to build your tRPC API. You should import these
|
|
||||||
* a lot in the /src/server/api/routers folder
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is how you create new routers and subrouters in your tRPC API
|
|
||||||
* @see https://trpc.io/docs/router
|
|
||||||
*/
|
|
||||||
export const createTRPCRouter = t.router;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public (unauthed) procedure
|
|
||||||
*
|
|
||||||
* This is the base piece you use to build new queries and mutations on your
|
|
||||||
* tRPC API. It does not guarantee that a user querying is authorized, but you
|
|
||||||
* can still access user session data if they are logged in
|
|
||||||
*/
|
|
||||||
export const publicProcedure = t.procedure;
|
|
||||||
export const protectedProcedure = t.procedure.use(isAuthed);
|
|
10
src/server/trpc/index.ts
Normal file
10
src/server/trpc/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { createTRPCRouter } from "./trpc";
|
||||||
|
import { roomRouter } from "./routers/room";
|
||||||
|
import { voteRouter } from "./routers/vote";
|
||||||
|
|
||||||
|
export const appRouter = createTRPCRouter({
|
||||||
|
room: roomRouter,
|
||||||
|
vote: voteRouter,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AppRouter = typeof appRouter;
|
|
@ -1,6 +1,6 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { publishToChannel } from "~/server/ably";
|
import { publishToChannel } from "~/server/ably";
|
||||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/trpc/trpc";
|
||||||
|
|
||||||
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
||||||
import { logs, rooms, votes } from "~/server/schema";
|
import { logs, rooms, votes } from "~/server/schema";
|
|
@ -1,7 +1,7 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { publishToChannel } from "~/server/ably";
|
import { publishToChannel } from "~/server/ably";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/trpc/trpc";
|
||||||
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
||||||
import { EventTypes } from "~/utils/types";
|
import { EventTypes } from "~/utils/types";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
47
src/server/trpc/trpc.ts
Normal file
47
src/server/trpc/trpc.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { SignedInAuthObject, SignedOutAuthObject } from "@clerk/nextjs/api";
|
||||||
|
import { getAuth } from "@clerk/nextjs/server";
|
||||||
|
import { TRPCError, type inferAsyncReturnType, initTRPC } from "@trpc/server";
|
||||||
|
import { db } from "../db";
|
||||||
|
import { FetchCreateContextFnOptions } from "@trpc/server/adapters/fetch";
|
||||||
|
import { NextRequest } from "next/server";
|
||||||
|
import superjson from "superjson";
|
||||||
|
|
||||||
|
interface AuthContext {
|
||||||
|
auth: SignedInAuthObject | SignedOutAuthObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createInnerTRPCContext = ({ auth }: AuthContext) => {
|
||||||
|
return {
|
||||||
|
auth,
|
||||||
|
db,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTRPCContext = ({ req }: FetchCreateContextFnOptions) => {
|
||||||
|
return createInnerTRPCContext({ auth: getAuth(req as NextRequest) });
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Context = inferAsyncReturnType<typeof createTRPCContext>;
|
||||||
|
|
||||||
|
const t = initTRPC.context<typeof createTRPCContext>().create({
|
||||||
|
transformer: superjson,
|
||||||
|
errorFormatter({ shape }) {
|
||||||
|
return shape;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// check if the user is signed in, otherwise through a UNAUTHORIZED CODE
|
||||||
|
const isAuthed = t.middleware(({ next, ctx }) => {
|
||||||
|
if (!ctx.auth.userId) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({
|
||||||
|
ctx: {
|
||||||
|
auth: ctx.auth,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createTRPCRouter = t.router;
|
||||||
|
export const publicProcedure = t.procedure;
|
||||||
|
export const protectedProcedure = t.procedure.use(isAuthed);
|
|
@ -2,7 +2,7 @@ import { eq } from "drizzle-orm";
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { rooms } from "./schema";
|
import { rooms } from "./schema";
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
import { Welcome } from "~/components/templates/Welcome";
|
import { Welcome } from "~/app/_components/templates/Welcome";
|
||||||
import { Resend } from "resend";
|
import { Resend } from "resend";
|
||||||
|
|
||||||
const resend = new Resend(env.RESEND_API_KEY);
|
const resend = new Resend(env.RESEND_API_KEY);
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
html,
|
|
||||||
container,
|
|
||||||
body #__next {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/**
|
|
||||||
* This is the client-side entrypoint for your tRPC API. It is used to create the `api` object which
|
|
||||||
* contains the Next.js App-wrapper, as well as your type-safe React Query hooks.
|
|
||||||
*
|
|
||||||
* We also create a few inference helpers for input and output types.
|
|
||||||
*/
|
|
||||||
import { httpBatchLink, loggerLink } from "@trpc/client";
|
|
||||||
import { createTRPCNext } from "@trpc/next";
|
|
||||||
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
|
|
||||||
import superjson from "superjson";
|
|
||||||
|
|
||||||
import { type AppRouter } from "~/server/api/root";
|
|
||||||
|
|
||||||
const getBaseUrl = () => {
|
|
||||||
if (typeof window !== "undefined") return ""; // browser should use relative url
|
|
||||||
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
|
|
||||||
return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
|
|
||||||
};
|
|
||||||
|
|
||||||
/** A set of type-safe react-query hooks for your tRPC API. */
|
|
||||||
export const api = createTRPCNext<AppRouter>({
|
|
||||||
config() {
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Transformer used for data de-serialization from the server.
|
|
||||||
*
|
|
||||||
* @see https://trpc.io/docs/data-transformers
|
|
||||||
*/
|
|
||||||
transformer: superjson,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Links used to determine request flow from client to server.
|
|
||||||
*
|
|
||||||
* @see https://trpc.io/docs/links
|
|
||||||
*/
|
|
||||||
links: [
|
|
||||||
loggerLink({
|
|
||||||
enabled: (opts) =>
|
|
||||||
process.env.NODE_ENV === "development" ||
|
|
||||||
(opts.direction === "down" && opts.result instanceof Error),
|
|
||||||
}),
|
|
||||||
httpBatchLink({
|
|
||||||
url: `${getBaseUrl()}/api/trpc`,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Whether tRPC should await queries when server rendering pages.
|
|
||||||
*
|
|
||||||
* @see https://trpc.io/docs/nextjs#ssr-boolean-default-false
|
|
||||||
*/
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inference helper for inputs.
|
|
||||||
*
|
|
||||||
* @example type HelloInput = RouterInputs['example']['hello']
|
|
||||||
*/
|
|
||||||
export type RouterInputs = inferRouterInputs<AppRouter>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inference helper for outputs.
|
|
||||||
*
|
|
||||||
* @example type HelloOutput = RouterOutputs['example']['hello']
|
|
||||||
*/
|
|
||||||
export type RouterOutputs = inferRouterOutputs<AppRouter>;
|
|
|
@ -1,7 +1,11 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2017",
|
"target": "es2017",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
@ -18,8 +22,15 @@
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"~/*": ["./src/*"]
|
"~/*": [
|
||||||
}
|
"./src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
".eslintrc.cjs",
|
".eslintrc.cjs",
|
||||||
|
@ -27,7 +38,10 @@
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
"**/*.cjs",
|
"**/*.cjs",
|
||||||
"**/*.mjs"
|
"**/*.mjs",
|
||||||
|
".next/types/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue