diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e62a6fb7..4cb24d0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: - version: 9.6.0 + version: 9.10.0 - uses: actions/setup-node@v4 with: node-version: 20 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index e77dc86c..76bad9a5 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: - version: 9.6.0 + version: 9.10.0 - uses: actions/setup-node@v4 with: node-version: 20 @@ -70,7 +70,7 @@ jobs: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: - version: 9.5.0 + version: 9.10.0 - uses: actions/setup-node@v4 with: node-version: 20 diff --git a/apps/partners/next.config.mjs b/apps/partners/next.config.mjs index 09958f20..1f2e1d06 100644 --- a/apps/partners/next.config.mjs +++ b/apps/partners/next.config.mjs @@ -1,6 +1,11 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - transpilePackages: ["@umamin/ui", "@umamin/db", "@umamin/gql"], + transpilePackages: [ + "@umamin/ui", + "@umamin/db", + "@umamin/gql", + "@umamin/shared", + ], experimental: { serverComponentsExternalPackages: ["@node-rs/argon2"], }, diff --git a/apps/partners/package.json b/apps/partners/package.json index a118ac7d..0845deac 100644 --- a/apps/partners/package.json +++ b/apps/partners/package.json @@ -6,44 +6,43 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "gql:generate-persisted": "gql.tada generate-persisted", + "gql:generate-schema": "gql.tada generate-schema http://localhost:3000/api/graphql" }, "dependencies": { - "@graphql-yoga/plugin-csrf-prevention": "^3.6.2", - "@graphql-yoga/plugin-disable-introspection": "^2.6.2", - "@graphql-yoga/plugin-persisted-operations": "^3.6.2", - "@graphql-yoga/plugin-response-cache": "^3.8.2", + "@graphql-yoga/plugin-csrf-prevention": "^3.7.0", + "@graphql-yoga/plugin-disable-introspection": "^2.7.0", + "@graphql-yoga/plugin-persisted-operations": "^3.7.0", + "@graphql-yoga/plugin-response-cache": "^3.9.0", "@lucia-auth/adapter-drizzle": "^1.0.7", "@node-rs/argon2": "^1.8.3", + "@tanstack/react-virtual": "^3.9.0", "@umamin/db": "workspace:*", "@umamin/gql": "workspace:*", "@umamin/ui": "workspace:*", - "@urql/core": "^5.0.5", - "@urql/exchange-graphcache": "^7.1.1", - "@urql/exchange-persisted": "^4.3.0", - "@urql/next": "^1.1.1", + "@umamin/shared": "workspace:*", "@whatwg-node/server": "^0.9.46", "arctic": "^1.9.2", "date-fns": "^3.6.0", "geist": "^1.3.1", - "gql.tada": "^1.8.5", + "gql.tada": "^1.8.6", "graphql": "^16.9.0", - "graphql-yoga": "^5.6.2", + "graphql-yoga": "^5.7.0", "lucia": "^3.2.0", "lucide-react": "^0.407.0", "modern-screenshot": "^4.4.39", "nanoid": "^5.0.7", - "next": "14.2.5", - "nextjs-toploader": "^1.6.12", - "react": "^18", - "react-dom": "^18", + "next": "14.2.11", + "nextjs-toploader": "^3.6.15", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-intersection-observer": "^9.10.2", "sonner": "^1.5.0", - "urql": "^4.1.0", "zod": "^3.22.4" }, "devDependencies": { - "@0no-co/graphqlsp": "^1.12.12", + "@0no-co/graphqlsp": "^1.12.13", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -54,6 +53,6 @@ "eslint-config-next": "14.2.5", "postcss": "^8", "tailwindcss": "^3.4.1", - "typescript": "^5" + "typescript": "^5.5.4" } } diff --git a/apps/partners/src/app/api/graphql/route.ts b/apps/partners/src/app/api/graphql/route.ts new file mode 100644 index 00000000..6b604d0d --- /dev/null +++ b/apps/partners/src/app/api/graphql/route.ts @@ -0,0 +1,97 @@ +import { cookies } from "next/headers"; +import { createYoga } from "graphql-yoga"; +import { getSession, lucia } from "@umamin/shared/lib/auth"; +import persistedOperations from "@/persisted-operations.json"; +import { partners_schema, initContextCache } from "@umamin/gql"; +import { useResponseCache } from "@graphql-yoga/plugin-response-cache"; +import { useCSRFPrevention } from "@graphql-yoga/plugin-csrf-prevention"; +import { usePersistedOperations } from "@graphql-yoga/plugin-persisted-operations"; +import { useDisableIntrospection } from "@graphql-yoga/plugin-disable-introspection"; + +const { handleRequest } = createYoga({ + schema: partners_schema, + context: async () => { + const { session } = await getSession(); + + return { + ...initContextCache(), + userId: session?.userId, + }; + }, + graphqlEndpoint: "/api/graphql", + graphiql: process.env.NODE_ENV === "development", + fetchAPI: { Response }, + cors: { + origin: + process.env.NODE_ENV === "production" + ? "https://www.umamin.link" + : "http://localhost:3000", + credentials: true, + methods: ["POST", "GET", "OPTIONS"], + allowedHeaders: ["Content-Type", "Authorization"], + }, + plugins: [ + useCSRFPrevention({ + requestHeaders: ["x-graphql-yoga-csrf"], + }), + useResponseCache({ + session: () => cookies().get(lucia.sessionCookieName)?.value, + invalidateViaMutation: false, + scopePerSchemaCoordinate: { + "Query.user": "PRIVATE", + "Query.note": "PRIVATE", + "Query.messages": "PRIVATE", + "Query.messagesFromCursor": "PRIVATE", + }, + ttl: 30_000, + ttlPerSchemaCoordinate: { + "Query.notes": 120_000, + "Query.notesFromCursor": 120_000, + "Query.userByUsername": 120_000, + }, + }), + useDisableIntrospection({ + isDisabled: () => process.env.NODE_ENV === "production", + }), + usePersistedOperations({ + allowArbitraryOperations: process.env.NODE_ENV === "development", + customErrors: { + notFound: { + message: "Operation is not found", + extensions: { + http: { + status: 404, + }, + }, + }, + keyNotFound: { + message: "Key is not found", + extensions: { + http: { + status: 404, + }, + }, + }, + persistedQueryOnly: { + message: "Operation is not allowed", + extensions: { + http: { + status: 403, + }, + }, + }, + }, + skipDocumentValidation: true, + async getPersistedOperation(key: string) { + // @ts-ignore + return persistedOperations[key]; + }, + }), + ], +}); + +export { + handleRequest as GET, + handleRequest as POST, + handleRequest as OPTIONS, +}; diff --git a/apps/partners/src/app/dashboard/components/navbar.tsx b/apps/partners/src/app/components/navbar.tsx similarity index 61% rename from apps/partners/src/app/dashboard/components/navbar.tsx rename to apps/partners/src/app/components/navbar.tsx index 3dccf48e..5da33036 100644 --- a/apps/partners/src/app/dashboard/components/navbar.tsx +++ b/apps/partners/src/app/components/navbar.tsx @@ -1,20 +1,24 @@ import Link from "next/link"; -import { Badge } from "@umamin/ui/components/badge"; +import { logout } from "@umamin/shared/actions"; import { SignOutButton } from "./sign-out-btn"; +import { Badge } from "@umamin/ui/components/badge"; -export async function Navbar() { +export function Navbar() { return ( -