From af3dd1a9b47906086e519f23ce2e63186b733e03 Mon Sep 17 00:00:00 2001 From: Ahmed Elsakaan Date: Sun, 12 May 2024 19:35:44 +0100 Subject: [PATCH 1/5] feat: trpc api --- apps/web/next.config.js | 7 +- apps/web/package.json | 7 ++ apps/web/src/app/(auth)/login/page.tsx | 10 +- apps/web/src/app/api/trpc/[trpc]/route.ts | 28 +++++ apps/web/src/app/api/uploadthing/core.ts | 4 +- apps/web/src/app/layout.tsx | 4 +- apps/web/src/app/page.tsx | 8 +- apps/web/src/lib/trpc/react.tsx | 66 +++++++++++ apps/web/src/lib/trpc/server.ts | 17 +++ cspell.config.yaml | 3 + packages/api/.eslintrc.cjs | 7 ++ packages/api/package.json | 34 ++++++ packages/api/src/index.ts | 12 ++ packages/api/src/routers/hello.ts | 9 ++ packages/api/src/trpc.ts | 49 ++++++++ packages/api/tsconfig.json | 7 ++ packages/auth/src/actions/logout.ts | 4 +- packages/auth/src/auth.ts | 39 +++++++ packages/auth/src/getSession.ts | 41 ------- packages/auth/src/index.ts | 2 +- pnpm-lock.yaml | 133 ++++++++++++++++++++++ 21 files changed, 440 insertions(+), 51 deletions(-) create mode 100644 apps/web/src/app/api/trpc/[trpc]/route.ts create mode 100644 apps/web/src/lib/trpc/react.tsx create mode 100644 apps/web/src/lib/trpc/server.ts create mode 100644 packages/api/.eslintrc.cjs create mode 100644 packages/api/package.json create mode 100644 packages/api/src/index.ts create mode 100644 packages/api/src/routers/hello.ts create mode 100644 packages/api/src/trpc.ts create mode 100644 packages/api/tsconfig.json create mode 100644 packages/auth/src/auth.ts delete mode 100644 packages/auth/src/getSession.ts diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 06389809..87a7752c 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -12,7 +12,12 @@ const nextConfig = { experimental: { typedRoutes: true, }, - transpilePackages: ['@orbitkit/db', '@orbitkit/auth', '@orbitkit/env'], + transpilePackages: [ + '@orbitkit/db', + '@orbitkit/auth', + '@orbitkit/env', + '@orbitkit/api', + ], }; export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json index ef06b36f..35bae226 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -15,12 +15,17 @@ "typecheck": "tsc --noEmit --tsBuildInfoFile .tsbuildinfo" }, "dependencies": { + "@orbitkit/api": "workspace:^", "@orbitkit/auth": "workspace:^", "@orbitkit/db": "workspace:^", "@orbitkit/env": "workspace:^", "@orbitkit/ui": "workspace:^", "@t3-oss/env-nextjs": "^0.10.1", + "@tanstack/react-query": "^5.35.5", "@total-typescript/ts-reset": "^0.5.1", + "@trpc/client": "next", + "@trpc/react-query": "next", + "@trpc/server": "next", "@unkey/ratelimit": "^0.1.6", "@uploadthing/react": "^6.5.3", "geist": "^1.3.0", @@ -28,7 +33,9 @@ "next-themes": "^0.3.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-error-boundary": "^4.0.13", "server-only": "^0.0.1", + "superjson": "^2.2.1", "uploadthing": "^6.10.3", "zod": "^3.23.8" }, diff --git a/apps/web/src/app/(auth)/login/page.tsx b/apps/web/src/app/(auth)/login/page.tsx index 90c91699..005ffb39 100644 --- a/apps/web/src/app/(auth)/login/page.tsx +++ b/apps/web/src/app/(auth)/login/page.tsx @@ -1,5 +1,7 @@ import Link from 'next/link'; +import { redirect } from 'next/navigation'; +import { auth } from '@orbitkit/auth'; import { env } from '@orbitkit/env/web'; const googleAuthIsEnabled = @@ -10,7 +12,13 @@ const googleAuthIsEnabled = const githubAuthIsEnabled = env.AUTH_GITHUB_SECRET !== undefined && env.AUTH_GITHUB_ID !== undefined; -export default function Page() { +export default async function Page() { + const { user } = await auth(); + + if (user) { + redirect('/'); + } + return (
{githubAuthIsEnabled && ( diff --git a/apps/web/src/app/api/trpc/[trpc]/route.ts b/apps/web/src/app/api/trpc/[trpc]/route.ts new file mode 100644 index 00000000..58ebaafc --- /dev/null +++ b/apps/web/src/app/api/trpc/[trpc]/route.ts @@ -0,0 +1,28 @@ +import { type NextRequest } from 'next/server'; + +import { fetchRequestHandler } from '@trpc/server/adapters/fetch'; + +import { appRouter, createTRPCContext } from '@orbitkit/api'; +import { env } from '@orbitkit/env/web'; + +const createContext = async (req: NextRequest) => { + return createTRPCContext({ + headers: req.headers, + }); +}; + +const handler = (req: NextRequest) => + fetchRequestHandler({ + endpoint: '/api/trpc', + req, + router: appRouter, + createContext: () => createContext(req), + onError: ({ path, error }) => { + env.NODE_ENV === 'development' && + console.error( + `❌ tRPC failed on ${path ?? ''}: ${error.message}`, + ); + }, + }); + +export { handler as GET, handler as POST }; diff --git a/apps/web/src/app/api/uploadthing/core.ts b/apps/web/src/app/api/uploadthing/core.ts index b1e5ea97..26258fff 100644 --- a/apps/web/src/app/api/uploadthing/core.ts +++ b/apps/web/src/app/api/uploadthing/core.ts @@ -3,14 +3,14 @@ import type { FileRouter } from 'uploadthing/next'; import { createUploadthing } from 'uploadthing/next'; import { UploadThingError } from 'uploadthing/server'; -import { getSession } from '@orbitkit/auth'; +import { auth } from '@orbitkit/auth'; const f = createUploadthing(); export const fileRouter = { imageUploader: f({ image: { maxFileSize: '4MB' } }) .middleware(async () => { - const session = await getSession(); + const session = await auth(); if (!session.user) // eslint-disable-next-line @typescript-eslint/only-throw-error diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 4cfce891..a68f0a05 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -11,6 +11,8 @@ import { extractRouterConfig } from 'uploadthing/server'; import { Toaster } from '@orbitkit/ui/toast'; +import { TRPCReactProvider } from '@/lib/trpc/react'; + import { fileRouter } from './api/uploadthing/core'; export const metadata: Metadata = { @@ -28,7 +30,7 @@ export default function RootLayout({ - {children} + {children} diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 05395ea8..5b89d5a4 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,20 +1,23 @@ import { redirect } from 'next/navigation'; -import { getSession } from '@orbitkit/auth'; +import { auth } from '@orbitkit/auth'; import { logout } from '@orbitkit/auth/actions/logout'; import { Avatar, AvatarFallback, AvatarImage } from '@orbitkit/ui/avatar'; import { ThemeSwitcher } from '@/components/ThemeSwitcher'; +import { api } from '@/lib/trpc/server'; import { ShowToast } from './show-toast'; import { UploadExample } from './upload'; export default async function Home() { - const { user } = await getSession(); + const { user } = await auth(); if (!user) { return redirect('/login'); } + const hello = await api.hello.protected(); + return (
@@ -35,6 +38,7 @@ export default async function Home() { +

{hello.message}

); } diff --git a/apps/web/src/lib/trpc/react.tsx b/apps/web/src/lib/trpc/react.tsx new file mode 100644 index 00000000..47babdc4 --- /dev/null +++ b/apps/web/src/lib/trpc/react.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { useState } from 'react'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { loggerLink, unstable_httpBatchStreamLink } from '@trpc/client'; +import { createTRPCReact } from '@trpc/react-query'; +import { type inferRouterInputs, type inferRouterOutputs } from '@trpc/server'; +import superjson from 'superjson'; + +import { type AppRouter } from '@orbitkit/api'; +import { env } from '@orbitkit/env/web'; + +const createQueryClient = () => new QueryClient(); + +let clientQueryClientSingleton: QueryClient | undefined = undefined; +const getQueryClient = () => { + if (typeof window === 'undefined') { + return createQueryClient(); + } + return (clientQueryClientSingleton ??= createQueryClient()); +}; + +export const api = createTRPCReact(); + +export type RouterInputs = inferRouterInputs; +export type RouterOutputs = inferRouterOutputs; + +export function TRPCReactProvider(props: { children: React.ReactNode }) { + const queryClient = getQueryClient(); + + const [trpcClient] = useState(() => + api.createClient({ + links: [ + loggerLink({ + enabled: (op) => + process.env.NODE_ENV === 'development' || + (op.direction === 'down' && op.result instanceof Error), + }), + unstable_httpBatchStreamLink({ + transformer: superjson, + url: getBaseUrl() + '/api/trpc', + headers: () => { + const headers = new Headers(); + headers.set('x-trpc-source', 'nextjs-react'); + return headers; + }, + }), + ], + }), + ); + + return ( + + + {props.children} + + + ); +} + +function getBaseUrl() { + if (typeof window !== 'undefined') return window.location.origin; + if (env.VERCEL_URL) return `https://${env.VERCEL_URL}`; + return `http://localhost:${String(env.PORT)}`; +} diff --git a/apps/web/src/lib/trpc/server.ts b/apps/web/src/lib/trpc/server.ts new file mode 100644 index 00000000..90499670 --- /dev/null +++ b/apps/web/src/lib/trpc/server.ts @@ -0,0 +1,17 @@ +import 'server-only'; + +import { cache } from 'react'; +import { headers } from 'next/headers'; + +import { createCaller, createTRPCContext } from '@orbitkit/api'; + +const createContext = cache(async () => { + const heads = new Headers(headers()); + heads.set('x-trpc-source', 'rsc'); + + return createTRPCContext({ + headers: heads, + }); +}); + +export const api = createCaller(createContext); diff --git a/cspell.config.yaml b/cspell.config.yaml index d3c319bc..ca91f783 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -63,11 +63,14 @@ words: - starterkit - starterkits - stylesheet + - superjson - tada - tailwindcss + - tanstack - thollander - todos - topbar + - trpc - tsbuildinfo - tsconfigs - tsup diff --git a/packages/api/.eslintrc.cjs b/packages/api/.eslintrc.cjs new file mode 100644 index 00000000..c7b13b75 --- /dev/null +++ b/packages/api/.eslintrc.cjs @@ -0,0 +1,7 @@ +/** @type {import('eslint').Linter.Config} */ +const config = { + root: true, + extends: ['orbitkit/base'], +}; + +module.exports = config; diff --git a/packages/api/package.json b/packages/api/package.json new file mode 100644 index 00000000..ee489502 --- /dev/null +++ b/packages/api/package.json @@ -0,0 +1,34 @@ +{ + "name": "@orbitkit/api", + "version": "0.1.3", + "private": true, + "description": "A tRPC API package used in the web application", + "license": "MIT", + "author": "OrbitKit", + "sideEffects": false, + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "lint": "eslint . --cache --max-warnings 0", + "typecheck": "tsc --noEmit --tsBuildInfoFile .tsbuildinfo" + }, + "dependencies": { + "@orbitkit/auth": "workspace:^", + "@orbitkit/db": "workspace:^", + "@orbitkit/env": "workspace:^", + "@orbitkit/utils": "workspace:^", + "@trpc/server": "next", + "superjson": "^2.2.1", + "zod": "^3.23.8" + }, + "devDependencies": { + "@orbitkit/tsconfig": "workspace:^", + "@types/node": "^20.12.11", + "eslint-config-orbitkit": "workspace:^" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts new file mode 100644 index 00000000..e0199ada --- /dev/null +++ b/packages/api/src/index.ts @@ -0,0 +1,12 @@ +import { helloRouter } from './routers/hello'; +import { createCallerFactory, createRouter } from './trpc'; + +export const appRouter = createRouter({ + hello: helloRouter, +}); + +export type AppRouter = typeof appRouter; + +export const createCaller = createCallerFactory(appRouter); + +export { createTRPCContext } from './trpc'; diff --git a/packages/api/src/routers/hello.ts b/packages/api/src/routers/hello.ts new file mode 100644 index 00000000..ca851c0a --- /dev/null +++ b/packages/api/src/routers/hello.ts @@ -0,0 +1,9 @@ +import { createRouter, protectedProcedure } from '../trpc'; + +export const helloRouter = createRouter({ + protected: protectedProcedure.query(({ ctx }) => { + return { + message: `Hello, ${ctx.user.name ?? 'world'} from tRPC!`, + }; + }), +}); diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts new file mode 100644 index 00000000..eacdc86a --- /dev/null +++ b/packages/api/src/trpc.ts @@ -0,0 +1,49 @@ +import { initTRPC, TRPCError } from '@trpc/server'; +import superjson from 'superjson'; +import { ZodError } from 'zod'; + +import { uncachedAuth } from '@orbitkit/auth'; +import { db } from '@orbitkit/db'; + +export const createTRPCContext = async (opts: { headers: Headers }) => { + const { session, user } = await uncachedAuth(); + + return { + db, + session, + user, + ...opts, + }; +}; + +const t = initTRPC.context().create({ + transformer: superjson, + errorFormatter({ shape, error }) { + return { + ...shape, + data: { + ...shape.data, + zodError: + error.cause instanceof ZodError ? error.cause.flatten() : null, + }, + }; + }, +}); +export const createCallerFactory = t.createCallerFactory; + +export const createRouter = t.router; + +export const publicProcedure = t.procedure; + +export const protectedProcedure = t.procedure.use(({ ctx, next }) => { + if (!ctx.session || !ctx.user) { + throw new TRPCError({ code: 'UNAUTHORIZED' }); + } + + return next({ + ctx: { + session: { ...ctx.session }, + user: { ...ctx.user }, + }, + }); +}); diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json new file mode 100644 index 00000000..57d843cf --- /dev/null +++ b/packages/api/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": ["@orbitkit/tsconfig/base.json"], + "compilerOptions": { + "tsBuildInfoFile": ".tsbuildinfo" + }, + "include": ["**/*.ts", "**/*.mjs", "**/*.js", "**/*.cjs", ".eslintrc.cjs"] +} diff --git a/packages/auth/src/actions/logout.ts b/packages/auth/src/actions/logout.ts index 0445684c..dfbfa4ab 100644 --- a/packages/auth/src/actions/logout.ts +++ b/packages/auth/src/actions/logout.ts @@ -3,11 +3,11 @@ import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; -import { getSession } from '../getSession'; +import { auth } from '../auth'; import { lucia } from '../lucia'; export async function logout() { - const { session } = await getSession(); + const { session } = await auth(); if (!session) { return { error: 'Unauthorized', diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts new file mode 100644 index 00000000..e494f5ff --- /dev/null +++ b/packages/auth/src/auth.ts @@ -0,0 +1,39 @@ +import { cache } from 'react'; +import { cookies } from 'next/headers'; + +import { type Session, type User } from 'lucia'; + +import { lucia } from './lucia'; + +export const uncachedAuth = async (): Promise< + { user: User; session: Session } | { user: null; session: null } +> => { + const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null; + if (!sessionId) { + return { user: null, session: null }; + } + const result = await lucia.validateSession(sessionId); + try { + if (result.session?.fresh) { + const sessionCookie = lucia.createSessionCookie(result.session.id); + cookies().set( + sessionCookie.name, + sessionCookie.value, + sessionCookie.attributes, + ); + } + if (!result.session) { + const sessionCookie = lucia.createBlankSessionCookie(); + cookies().set( + sessionCookie.name, + sessionCookie.value, + sessionCookie.attributes, + ); + } + } catch { + console.error('Failed to set session cookie'); + } + return result; +}; + +export const auth = cache(uncachedAuth); diff --git a/packages/auth/src/getSession.ts b/packages/auth/src/getSession.ts deleted file mode 100644 index daefca43..00000000 --- a/packages/auth/src/getSession.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { cache } from 'react'; -import { cookies } from 'next/headers'; - -import { type Session, type User } from 'lucia'; - -import { lucia } from './lucia'; - -export const getSession = cache( - async (): Promise< - { user: User; session: Session } | { user: null; session: null } - > => { - const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null; - if (!sessionId) { - return { - user: null, - session: null, - }; - } - - const result = await lucia.validateSession(sessionId); - try { - if (result.session?.fresh) { - const sessionCookie = lucia.createSessionCookie(result.session.id); - cookies().set( - sessionCookie.name, - sessionCookie.value, - sessionCookie.attributes, - ); - } - if (!result.session) { - const sessionCookie = lucia.createBlankSessionCookie(); - cookies().set( - sessionCookie.name, - sessionCookie.value, - sessionCookie.attributes, - ); - } - } catch {} - return result; - }, -); diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts index 27168101..fc5b1792 100644 --- a/packages/auth/src/index.ts +++ b/packages/auth/src/index.ts @@ -1,2 +1,2 @@ export * from './lucia'; -export * from './getSession'; +export * from './auth'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d50cfd7a..53e224b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -183,6 +183,9 @@ importers: apps/web: dependencies: + '@orbitkit/api': + specifier: workspace:^ + version: link:../../packages/api '@orbitkit/auth': specifier: workspace:^ version: link:../../packages/auth @@ -198,9 +201,21 @@ importers: '@t3-oss/env-nextjs': specifier: ^0.10.1 version: 0.10.1(typescript@5.4.5)(zod@3.23.8) + '@tanstack/react-query': + specifier: ^5.35.5 + version: 5.35.5(react@18.3.1) '@total-typescript/ts-reset': specifier: ^0.5.1 version: 0.5.1 + '@trpc/client': + specifier: next + version: 11.0.0-rc.366(@trpc/server@11.0.0-rc.366) + '@trpc/react-query': + specifier: next + version: 11.0.0-rc.366(@tanstack/react-query@5.35.5(react@18.3.1))(@trpc/client@11.0.0-rc.366(@trpc/server@11.0.0-rc.366))(@trpc/server@11.0.0-rc.366)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@trpc/server': + specifier: next + version: 11.0.0-rc.366 '@unkey/ratelimit': specifier: ^0.1.6 version: 0.1.6 @@ -222,9 +237,15 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-error-boundary: + specifier: ^4.0.13 + version: 4.0.13(react@18.3.1) server-only: specifier: ^0.0.1 version: 0.0.1 + superjson: + specifier: ^2.2.1 + version: 2.2.1 uploadthing: specifier: ^6.10.3 version: 6.10.3(express@4.19.2)(next@14.2.3(@babel/core@7.24.5)(@playwright/test@1.44.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.3) @@ -269,6 +290,40 @@ importers: specifier: ^3.4.3 version: 3.4.3 + packages/api: + dependencies: + '@orbitkit/auth': + specifier: workspace:^ + version: link:../auth + '@orbitkit/db': + specifier: workspace:^ + version: link:../db + '@orbitkit/env': + specifier: workspace:^ + version: link:../env + '@orbitkit/utils': + specifier: workspace:^ + version: link:../utils + '@trpc/server': + specifier: next + version: 11.0.0-rc.366 + superjson: + specifier: ^2.2.1 + version: 2.2.1 + zod: + specifier: ^3.23.8 + version: 3.23.8 + devDependencies: + '@orbitkit/tsconfig': + specifier: workspace:^ + version: link:../config/tsconfig + '@types/node': + specifier: ^20.12.11 + version: 20.12.11 + eslint-config-orbitkit: + specifier: workspace:^ + version: link:../config/eslint + packages/assets: {} packages/auth: @@ -4060,6 +4115,14 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' + '@tanstack/query-core@5.35.5': + resolution: {integrity: sha512-OMWvlEqG01RfGj+XZb/piDzPp0eZkkHWSDHt2LvE/fd1zWburP/xwm0ghk6Iv8cuPlP+ACFkZviKXK0OVt6lhg==} + + '@tanstack/react-query@5.35.5': + resolution: {integrity: sha512-sppX7L+PVn5GBV3In6zzj0zcKfnZRKhXbX1MfIfKo1OjIq2GMaopvAFOP0x1bRYTUk2ikrdYcQYOozX7PWkb8A==} + peerDependencies: + react: ^18.0.0 + '@testing-library/dom@9.3.4': resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} engines: {node: '>=14'} @@ -4094,6 +4157,23 @@ packages: '@total-typescript/ts-reset@0.5.1': resolution: {integrity: sha512-AqlrT8YA1o7Ff5wPfMOL0pvL+1X+sw60NN6CcOCqs658emD6RfiXhF7Gu9QcfKBH7ELY2nInLhKSCWVoNL70MQ==} + '@trpc/client@11.0.0-rc.366': + resolution: {integrity: sha512-bIbcF/UhCifU5i9hbbaMlmOS2NgnRq5W0pJH2SJhrp6jyIIaCF+uNAIZtkfO0GriBb0agBmGIXqvGLFEexiTKA==} + peerDependencies: + '@trpc/server': 11.0.0-rc.366+237dbb3f9 + + '@trpc/react-query@11.0.0-rc.366': + resolution: {integrity: sha512-LCdJFymEdaKBBNRQPVAcEw9qNu3m+n9hH3qIPKDanLWYlo4h+8lQnj8JIhYpClD2G3gkvD0ZSZ+F54QvwrI3nA==} + peerDependencies: + '@tanstack/react-query': ^5.25.0 + '@trpc/client': 11.0.0-rc.366+237dbb3f9 + '@trpc/server': 11.0.0-rc.366+237dbb3f9 + react: '>=18.2.0' + react-dom: '>=18.2.0' + + '@trpc/server@11.0.0-rc.366': + resolution: {integrity: sha512-Pr7SdpIVrOGtIGt9vs7i3v0hqzlpXbi8/8RV7XfrXVJ5hlzKYOz5VE0AjckhHXuhrUlpTGTDm0/2nz0ywLcQ2g==} + '@tybys/wasm-util@0.8.3': resolution: {integrity: sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==} @@ -5247,6 +5327,10 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + core-js-compat@3.37.0: resolution: {integrity: sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==} @@ -7059,6 +7143,10 @@ packages: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -8719,6 +8807,11 @@ packages: react: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0 react-dom: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0 + react-error-boundary@4.0.13: + resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==} + peerDependencies: + react: '>=16.13.1' + react-hook-form@7.51.4: resolution: {integrity: sha512-V14i8SEkh+V1gs6YtD0hdHYnoL4tp/HX/A45wWQN15CYr9bFRmmRdYStSO5L65lCCZRF+kYiSKhm9alqbcdiVA==} engines: {node: '>=12.22.0'} @@ -9411,6 +9504,10 @@ packages: suf-log@2.5.3: resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==} + superjson@2.2.1: + resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==} + engines: {node: '>=16'} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -14274,6 +14371,13 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 3.4.3 + '@tanstack/query-core@5.35.5': {} + + '@tanstack/react-query@5.35.5(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.35.5 + react: 18.3.1 + '@testing-library/dom@9.3.4': dependencies: '@babel/code-frame': 7.24.2 @@ -14302,6 +14406,20 @@ snapshots: '@total-typescript/ts-reset@0.5.1': {} + '@trpc/client@11.0.0-rc.366(@trpc/server@11.0.0-rc.366)': + dependencies: + '@trpc/server': 11.0.0-rc.366 + + '@trpc/react-query@11.0.0-rc.366(@tanstack/react-query@5.35.5(react@18.3.1))(@trpc/client@11.0.0-rc.366(@trpc/server@11.0.0-rc.366))(@trpc/server@11.0.0-rc.366)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/react-query': 5.35.5(react@18.3.1) + '@trpc/client': 11.0.0-rc.366(@trpc/server@11.0.0-rc.366) + '@trpc/server': 11.0.0-rc.366 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@trpc/server@11.0.0-rc.366': {} + '@tybys/wasm-util@0.8.3': dependencies: tslib: 2.6.2 @@ -15718,6 +15836,10 @@ snapshots: cookie@0.6.0: {} + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + core-js-compat@3.37.0: dependencies: browserslist: 4.23.0 @@ -18014,6 +18136,8 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-what@4.1.16: {} + is-windows@1.0.2: {} is-wsl@2.2.0: @@ -20193,6 +20317,11 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-is: 18.1.0 + react-error-boundary@4.0.13(react@18.3.1): + dependencies: + '@babel/runtime': 7.24.5 + react: 18.3.1 + react-hook-form@7.51.4(react@18.3.1): dependencies: react: 18.3.1 @@ -21123,6 +21252,10 @@ snapshots: dependencies: s.color: 0.0.15 + superjson@2.2.1: + dependencies: + copy-anything: 3.0.5 + supports-color@5.5.0: dependencies: has-flag: 3.0.0 From 30028e1d6e485fc649111663a57a09379e7e8493 Mon Sep 17 00:00:00 2001 From: Ahmed Elsakaan Date: Sun, 12 May 2024 20:02:10 +0100 Subject: [PATCH 2/5] refactor: better code structure --- apps/web/package.json | 1 + apps/web/src/app/(auth)/login/page.tsx | 4 +--- apps/web/src/app/layout.tsx | 2 +- apps/web/src/app/page.tsx | 4 +--- apps/web/src/lib/trpc/react.tsx | 8 +------- packages/api/src/trpc.ts | 4 ++-- packages/auth/src/providers/google.ts | 2 +- packages/ui/tsup.config.ts | 5 ++++- packages/utils/package.json | 10 +++++++--- packages/utils/src/index.ts | 2 -- packages/utils/src/url.ts | 2 +- packages/utils/tsup.config.ts | 2 +- pnpm-lock.yaml | 3 +++ 13 files changed, 24 insertions(+), 25 deletions(-) delete mode 100644 packages/utils/src/index.ts diff --git a/apps/web/package.json b/apps/web/package.json index 35bae226..03250081 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -20,6 +20,7 @@ "@orbitkit/db": "workspace:^", "@orbitkit/env": "workspace:^", "@orbitkit/ui": "workspace:^", + "@orbitkit/utils": "workspace:^", "@t3-oss/env-nextjs": "^0.10.1", "@tanstack/react-query": "^5.35.5", "@total-typescript/ts-reset": "^0.5.1", diff --git a/apps/web/src/app/(auth)/login/page.tsx b/apps/web/src/app/(auth)/login/page.tsx index 005ffb39..0e9dae0c 100644 --- a/apps/web/src/app/(auth)/login/page.tsx +++ b/apps/web/src/app/(auth)/login/page.tsx @@ -15,9 +15,7 @@ const githubAuthIsEnabled = export default async function Page() { const { user } = await auth(); - if (user) { - redirect('/'); - } + if (user) redirect('/'); return (
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index a68f0a05..bc9dffb0 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -26,7 +26,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 5b89d5a4..187ea164 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -12,9 +12,7 @@ import { UploadExample } from './upload'; export default async function Home() { const { user } = await auth(); - if (!user) { - return redirect('/login'); - } + if (!user) redirect('/login'); const hello = await api.hello.protected(); diff --git a/apps/web/src/lib/trpc/react.tsx b/apps/web/src/lib/trpc/react.tsx index 47babdc4..6ea4d7cd 100644 --- a/apps/web/src/lib/trpc/react.tsx +++ b/apps/web/src/lib/trpc/react.tsx @@ -9,7 +9,7 @@ import { type inferRouterInputs, type inferRouterOutputs } from '@trpc/server'; import superjson from 'superjson'; import { type AppRouter } from '@orbitkit/api'; -import { env } from '@orbitkit/env/web'; +import { getBaseUrl } from '@orbitkit/utils/url'; const createQueryClient = () => new QueryClient(); @@ -58,9 +58,3 @@ export function TRPCReactProvider(props: { children: React.ReactNode }) { ); } - -function getBaseUrl() { - if (typeof window !== 'undefined') return window.location.origin; - if (env.VERCEL_URL) return `https://${env.VERCEL_URL}`; - return `http://localhost:${String(env.PORT)}`; -} diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts index eacdc86a..727140c5 100644 --- a/packages/api/src/trpc.ts +++ b/packages/api/src/trpc.ts @@ -2,11 +2,11 @@ import { initTRPC, TRPCError } from '@trpc/server'; import superjson from 'superjson'; import { ZodError } from 'zod'; -import { uncachedAuth } from '@orbitkit/auth'; +import { auth } from '@orbitkit/auth'; import { db } from '@orbitkit/db'; export const createTRPCContext = async (opts: { headers: Headers }) => { - const { session, user } = await uncachedAuth(); + const { session, user } = await auth(); return { db, diff --git a/packages/auth/src/providers/google.ts b/packages/auth/src/providers/google.ts index bd3a881b..39753ffe 100644 --- a/packages/auth/src/providers/google.ts +++ b/packages/auth/src/providers/google.ts @@ -7,7 +7,7 @@ import { z } from 'zod'; import { db } from '@orbitkit/db'; import { oauthAccountTable, userTable } from '@orbitkit/db/schema'; import { env } from '@orbitkit/env/web'; -import { getBaseUrl } from '@orbitkit/utils'; +import { getBaseUrl } from '@orbitkit/utils/url'; import { lucia } from '../lucia'; diff --git a/packages/ui/tsup.config.ts b/packages/ui/tsup.config.ts index 3bf80990..eb13f999 100644 --- a/packages/ui/tsup.config.ts +++ b/packages/ui/tsup.config.ts @@ -1,7 +1,10 @@ import { readPackageJSON } from 'pkg-types'; import { defineConfig } from 'tsup'; -import { formatAndWriteWithPrettier, listDirectories } from '@orbitkit/utils'; +import { + formatAndWriteWithPrettier, + listDirectories, +} from '@orbitkit/utils/filesystem'; const getPrimitives = async () => { const listOfPrimitives = (await listDirectories('./src/primitives')).map( diff --git a/packages/utils/package.json b/packages/utils/package.json index b9acd037..b9bb3eed 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -8,9 +8,13 @@ "sideEffects": false, "type": "module", "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" + "./filesystem": { + "types": "./dist/filesystem.d.ts", + "default": "./dist/filesystem.js" + }, + "./url": { + "types": "./dist/url.d.ts", + "default": "./dist/url.js" } }, "scripts": { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts deleted file mode 100644 index a2375f91..00000000 --- a/packages/utils/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './filesystem'; -export * from './url'; diff --git a/packages/utils/src/url.ts b/packages/utils/src/url.ts index 12e71667..c5532d69 100644 --- a/packages/utils/src/url.ts +++ b/packages/utils/src/url.ts @@ -1,5 +1,5 @@ export function getBaseUrl() { - if (typeof window !== 'undefined') return ''; + if (typeof window !== 'undefined') return window.location.origin; if (process.env['VERCEL_URL']) return `https://${process.env['VERCEL_URL']}`; return `http://localhost:${process.env['PORT'] ?? 3000}`; } diff --git a/packages/utils/tsup.config.ts b/packages/utils/tsup.config.ts index 847989d5..10051fc2 100644 --- a/packages/utils/tsup.config.ts +++ b/packages/utils/tsup.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsup'; export default defineConfig((opts) => ({ - entry: ['./src/index.ts'], + entry: ['./src/filesystem.ts', './src/url.ts'], format: ['esm'], splitting: true, sourcemap: true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53e224b4..20ca6098 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -198,6 +198,9 @@ importers: '@orbitkit/ui': specifier: workspace:^ version: link:../../packages/ui + '@orbitkit/utils': + specifier: workspace:^ + version: link:../../packages/utils '@t3-oss/env-nextjs': specifier: ^0.10.1 version: 0.10.1(typescript@5.4.5)(zod@3.23.8) From 69d14a74b35b4c90f78c43f9e2305b7867d4c215 Mon Sep 17 00:00:00 2001 From: Ahmed Elsakaan Date: Sun, 12 May 2024 20:15:02 +0100 Subject: [PATCH 3/5] refactor: removes react error boundary --- apps/web/package.json | 1 - pnpm-lock.yaml | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 03250081..2e4db231 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -34,7 +34,6 @@ "next-themes": "^0.3.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-error-boundary": "^4.0.13", "server-only": "^0.0.1", "superjson": "^2.2.1", "uploadthing": "^6.10.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 20ca6098..7d5fdf04 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -240,9 +240,6 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) - react-error-boundary: - specifier: ^4.0.13 - version: 4.0.13(react@18.3.1) server-only: specifier: ^0.0.1 version: 0.0.1 @@ -8810,11 +8807,6 @@ packages: react: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0 react-dom: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0 - react-error-boundary@4.0.13: - resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==} - peerDependencies: - react: '>=16.13.1' - react-hook-form@7.51.4: resolution: {integrity: sha512-V14i8SEkh+V1gs6YtD0hdHYnoL4tp/HX/A45wWQN15CYr9bFRmmRdYStSO5L65lCCZRF+kYiSKhm9alqbcdiVA==} engines: {node: '>=12.22.0'} @@ -20320,11 +20312,6 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-is: 18.1.0 - react-error-boundary@4.0.13(react@18.3.1): - dependencies: - '@babel/runtime': 7.24.5 - react: 18.3.1 - react-hook-form@7.51.4(react@18.3.1): dependencies: react: 18.3.1 From c033cef46ea8efd2f137ef70381f3fe9ef9c1514 Mon Sep 17 00:00:00 2001 From: Ahmed Elsakaan Date: Sun, 12 May 2024 23:21:17 +0100 Subject: [PATCH 4/5] feat: adds trpc to list of features --- README.md | 1 + apps/docs/introduction.mdx | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 71622856..412094ab 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - [**TypeScript**](https://www.typescriptlang.org/): type-safety is a core principle of OrbitKit. - [**Astrojs**](https://astro.build): for a clean slate to build your marketing website on top. - [**Next.js**](https://nextjs.org): Web application is included, giving you a solid foundation for your product. +- [**tRPC**](https://trpc.io) for a fully type-safe api. - [**Mintlify**](https://mintlify.com): for a clean, fast, and easy to use platform to document your project. - [**Turborepo**](https://turbo.build/repo): caching builds so you never have to run the same command twice. - [**Drizzle ORM**](https://orm.drizzle.team): providing a fully type-safe way to interact with your database. diff --git a/apps/docs/introduction.mdx b/apps/docs/introduction.mdx index f3e66613..9ca81855 100644 --- a/apps/docs/introduction.mdx +++ b/apps/docs/introduction.mdx @@ -23,6 +23,7 @@ description: '🚀 OrbitKit is an enterprise ready monorepo StarterKit ready to - [**TypeScript**](https://www.typescriptlang.org/): type-safety is a core principle of OrbitKit. - [**Astrojs**](https://astro.build): for a clean slate to build your marketing website on top. - [**Next.js**](https://nextjs.org): Web application is included, giving you a solid foundation for your product. +- [**tRPC**](https://trpc.io) for a fully type-safe api. - [**Mintlify**](https://mintlify.com): for a clean, fast, and easy to use platform to document your project. - [**Turborepo**](https://turbo.build/repo): caching builds so you never have to run the same command twice. - [**Drizzle ORM**](https://orm.drizzle.team): providing a fully type-safe way to interact with your database. From 4f0f72d4ada223edeed28508d32851fc68e56d9d Mon Sep 17 00:00:00 2001 From: Ahmed Elsakaan Date: Sun, 12 May 2024 23:32:37 +0100 Subject: [PATCH 5/5] feat: changeset --- .changeset/shaggy-experts-tease.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .changeset/shaggy-experts-tease.md diff --git a/.changeset/shaggy-experts-tease.md b/.changeset/shaggy-experts-tease.md new file mode 100644 index 00000000..97e88b20 --- /dev/null +++ b/.changeset/shaggy-experts-tease.md @@ -0,0 +1,28 @@ +--- +"@orbitkit/utils": patch +"@orbitkit/auth": patch +"@orbitkit/api": patch +"@orbitkit/ui": patch +"@orbitkit/docs": patch +"@orbitkit/web": patch +"@orbitkit/marketing": patch +"@orbitkit/assets": patch +"eslint-config-orbitkit": patch +"@orbitkit/storybook": patch +"@orbitkit/tailwind": patch +"@orbitkit/tsconfig": patch +"@orbitkit/vite": patch +"@orbitkit/core": patch +"@orbitkit/db": patch +"@orbitkit/env": patch +--- + +feat: creates a trpc api package + +changes in this release: + +- creates a new `packages/api` package that hosts a tRPC api to be used for the web application. +- renames the lucia auth `getSession` function to `auth` and provides a new uncached version of it. +- refactors the code in some places to be generally better. +- splits the utils package from a barrel export to multi-file export. +- `getBaseUrl` util now returns `window.location.origin` instead of an empty string when the `window` object is not `undefined`.