diff --git a/.env b/.env index d630622..4076e3a 100644 --- a/.env +++ b/.env @@ -9,4 +9,8 @@ USERS_SUPER=["akalinina","vchikalkin"] URL_GET_USER_DIRECT= URL_CRM_GRAPHQL_DIRECT= URL_CORE_FINGAP_DIRECT= -URL_1C_TRANSTAX_DIRECT= \ No newline at end of file +URL_1C_TRANSTAX_DIRECT= + +####### SERVER ######## +# DEFAULT: 'token' +COOKIE_TOKEN_NAME= diff --git a/apps/web/config/schema/env.js b/apps/web/config/schema/env.js index 8d54d22..f192725 100644 --- a/apps/web/config/schema/env.js +++ b/apps/web/config/schema/env.js @@ -2,6 +2,7 @@ const { z } = require('zod'); const envSchema = z.object({ BASE_PATH: z.string().optional().default(''), + COOKIE_TOKEN_NAME: z.string().optional().default('token'), PORT: z.string().optional(), URL_1C_TRANSTAX_DIRECT: z.string(), URL_CORE_FINGAP_DIRECT: z.string(), diff --git a/apps/web/config/schema/runtime-config.js b/apps/web/config/schema/runtime-config.js index 49a7c87..945d9ff 100644 --- a/apps/web/config/schema/runtime-config.js +++ b/apps/web/config/schema/runtime-config.js @@ -8,6 +8,7 @@ const publicRuntimeConfigSchema = envSchema.pick({ const serverRuntimeConfigSchema = envSchema.pick({ BASE_PATH: true, + COOKIE_TOKEN_NAME: true, PORT: true, URL_1C_TRANSTAX_DIRECT: true, URL_CORE_FINGAP_DIRECT: true, diff --git a/apps/web/config/server.js b/apps/web/config/server.js new file mode 100644 index 0000000..517737f --- /dev/null +++ b/apps/web/config/server.js @@ -0,0 +1,12 @@ +import { serverRuntimeConfigSchema } from './schema/runtime-config'; +import getConfig from 'next/config'; + +const { serverRuntimeConfig } = getConfig(); + +function getServerConfig() { + const { COOKIE_TOKEN_NAME } = serverRuntimeConfigSchema.parse(serverRuntimeConfig); + + return { COOKIE_TOKEN_NAME }; +} + +export default getServerConfig; diff --git a/apps/web/package.json b/apps/web/package.json index 62c31be..cb154df 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -21,7 +21,9 @@ "@trpc/react-query": "^10.13.0", "@trpc/server": "^10.13.0", "axios": "^1.3.4", + "cookies-next": "^2.1.1", "dayjs": "^1.11.7", + "jwt-decode": "^3.1.2", "less": "^4.1.3", "less-loader": "^11.1.0", "mobx": "^6.8.0", diff --git a/apps/web/pages/api/trpc/[trpc].ts b/apps/web/pages/api/trpc/[trpc].ts index e0b9eaf..3b8d088 100644 --- a/apps/web/pages/api/trpc/[trpc].ts +++ b/apps/web/pages/api/trpc/[trpc].ts @@ -1,8 +1,9 @@ /* eslint-disable canonical/filename-match-regex */ +import { createContext } from '@/trpc/context'; import appRouter from '@/trpc/routers'; import * as trpcNext from '@trpc/server/adapters/next'; export default trpcNext.createNextApiHandler({ - createContext: () => ({}), + createContext, router: appRouter, }); diff --git a/apps/web/trpc/context.ts b/apps/web/trpc/context.ts new file mode 100644 index 0000000..0b41bae --- /dev/null +++ b/apps/web/trpc/context.ts @@ -0,0 +1,16 @@ +import getServerConfig from '@/config/server'; +import type { inferAsyncReturnType } from '@trpc/server'; +import type { CreateNextContextOptions } from '@trpc/server/adapters/next'; +import { getCookie } from 'cookies-next'; + +const { COOKIE_TOKEN_NAME } = getServerConfig(); + +export async function createContext({ req, res }: CreateNextContextOptions) { + const token = getCookie(COOKIE_TOKEN_NAME, { req, res }); + + return { + token, + }; +} + +export type Context = inferAsyncReturnType; diff --git a/apps/web/trpc/middleware.ts b/apps/web/trpc/middleware.ts new file mode 100644 index 0000000..337916c --- /dev/null +++ b/apps/web/trpc/middleware.ts @@ -0,0 +1,28 @@ +import { t } from './server'; +import { TRPCError } from '@trpc/server'; +import jwtDecode from 'jwt-decode'; + +type TokenPayload = { + domain: string; + username: string; +}; + +export const user = t.middleware(async ({ ctx, next }) => { + if (ctx.token === undefined || ctx.token === null || typeof ctx.token !== 'string') { + throw new TRPCError({ + code: 'UNAUTHORIZED', + }); + } + + const decoded = jwtDecode(ctx.token); + const domainName = `${decoded.domain}\\${decoded.username}`; + + return next({ + ctx: { + user: { + ...decoded, + domainName, + }, + }, + }); +}); diff --git a/apps/web/trpc/routers/calculate/index.ts b/apps/web/trpc/routers/calculate/index.ts index 409b208..940004a 100644 --- a/apps/web/trpc/routers/calculate/index.ts +++ b/apps/web/trpc/routers/calculate/index.ts @@ -1,3 +1,4 @@ +import { user } from '../../middleware'; import { t } from '../../server'; import { CalculateInputSchema, CalculateOutputSchema } from './types'; import { validate } from './validation'; @@ -6,6 +7,7 @@ import { QueryClient } from '@tanstack/react-query'; const calculateRouter = t.router({ calculate: t.procedure + .use(user) .input(CalculateInputSchema) .output(CalculateOutputSchema) .query(async ({ input }) => { diff --git a/apps/web/trpc/server.ts b/apps/web/trpc/server.ts index cc152ab..b11f129 100644 --- a/apps/web/trpc/server.ts +++ b/apps/web/trpc/server.ts @@ -1,6 +1,7 @@ +import type { Context } from './context'; import { initTRPC } from '@trpc/server'; import SuperJSON from 'superjson'; -export const t = initTRPC.create({ +export const t = initTRPC.context().create({ transformer: SuperJSON, }); diff --git a/yarn.lock b/yarn.lock index 84c93f2..bda22c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2112,6 +2112,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.2.tgz#c076ed1d7b6095078ad3cf21dfeea951842778b1" integrity sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA== +"@types/node@^16.10.2": + version "16.18.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.16.tgz#09ff98b144abae2d7cce3e9fe9040ab2bf73222c" + integrity sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA== + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -3369,11 +3374,20 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie@^0.4.2: +cookie@^0.4.0, cookie@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookies-next@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cookies-next/-/cookies-next-2.1.1.tgz#8d82f1b78fccfb19d9d7c26766fa5707a3ec4695" + integrity sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA== + dependencies: + "@types/cookie" "^0.4.1" + "@types/node" "^16.10.2" + cookie "^0.4.0" + copy-anything@^2.0.1: version "2.0.6" resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480" @@ -6494,6 +6508,11 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== + keyv@^4.5.2: version "4.5.2" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56"