From e3587eff71f0814c1cf6db70d3ee813d18409d7f Mon Sep 17 00:00:00 2001 From: "Mr.Dat" Date: Thu, 12 Mar 2026 17:56:55 +0700 Subject: [PATCH] feat: enhance gRPC authentication flow and improve token management --- src/server/routes/rpc/auth.ts | 8 +++--- src/server/routes/rpc/index.ts | 42 +++++++++++++------------------ src/server/services/grpcClient.ts | 18 ++++++------- src/server/utils/grpcHelper.ts | 5 ++-- src/server/utils/index.ts | 14 ++++++++++- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/server/routes/rpc/auth.ts b/src/server/routes/rpc/auth.ts index ca9f2ec..9f4832e 100644 --- a/src/server/routes/rpc/auth.ts +++ b/src/server/routes/rpc/auth.ts @@ -1,9 +1,11 @@ +import { clearSessionCookies, ensureSessionUser } from "@/server/routes/auth"; +import { generateAndSetTokens } from "@/server/utils"; +import { type Metadata } from "@grpc/grpc-js"; import { validateFn } from "@hiogawa/tiny-rpc"; import { getContext } from "hono/context-storage"; import z from "zod"; -import { clearSessionCookies, ensureSessionUser } from "@/server/routes/auth"; -const collectGrpcCookies = (metadata: import("@grpc/grpc-js").Metadata) => { +const collectGrpcCookies = (metadata: Metadata) => { const context = getContext(); for (const value of metadata.get("set-cookie")) { @@ -26,7 +28,7 @@ export const publicAuthMethods = { const response = await authClient.login(data, metadata, { onMetadata: collectGrpcCookies, }); - + await generateAndSetTokens(context, response.user!); return { user: ensureSessionUser(response.user) }; }), register: validateFn( diff --git a/src/server/routes/rpc/index.ts b/src/server/routes/rpc/index.ts index 1d37e65..54a0f87 100644 --- a/src/server/routes/rpc/index.ts +++ b/src/server/routes/rpc/index.ts @@ -1,10 +1,10 @@ import { authenticate } from "@/server/middlewares/authenticate"; +import { getGrpcMetadataFromContext } from "@/server/services/grpcClient"; +import { Metadata } from "@grpc/grpc-js"; import { exposeTinyRpc, httpServerAdapter } from "@hiogawa/tiny-rpc"; import { Hono } from "hono"; -import { Metadata } from "@grpc/grpc-js"; -import { meMethods } from "./me"; import { protectedAuthMethods, publicAuthMethods } from "./auth"; -import { getGrpcMetadataFromContext } from "@/server/services/grpcClient"; +import { meMethods } from "./me"; declare module "hono" { interface ContextVariableMap { @@ -23,24 +23,28 @@ const publicRoutes = { }; export type RpcRoutes = typeof protectedRoutes & typeof publicRoutes; -export const endpoint = "/rpc"; -export const publicEndpoint = "/rpc-public"; +export const endpoint = "/rpc/*"; +export const publicEndpoint = "/rpc-public/*"; export const pathsForGET: (keyof typeof protectedRoutes)[] = ["health"]; export function registerRpcRoutes(app: Hono) { const protectedHandler = exposeTinyRpc({ routes: protectedRoutes, - adapter: httpServerAdapter({ endpoint }), - }); - const publicHandler = exposeTinyRpc({ - routes: publicRoutes, - adapter: httpServerAdapter({ endpoint: publicEndpoint }), + adapter: httpServerAdapter({ endpoint: "/rpc" }), }); + app.use(publicEndpoint, async (c, next) => { - app.use(endpoint, authenticate, async (c, next) => { - if (c.req.path !== endpoint && !c.req.path.startsWith(endpoint + "/")) { - return await next(); + const publicHandler = exposeTinyRpc({ + routes: publicRoutes, + adapter: httpServerAdapter({ endpoint: "/rpc-public" }), + }); + const res = await publicHandler({ request: c.req.raw }); + if (res) { + return res; } + return await next(); + }); + app.use(endpoint, authenticate, async (c, next) => { c.set("grpcMetadata", getGrpcMetadataFromContext()); @@ -51,15 +55,5 @@ export function registerRpcRoutes(app: Hono) { return await next(); }); - app.use(publicEndpoint, async (c, next) => { - if (c.req.path !== publicEndpoint && !c.req.path.startsWith(publicEndpoint + "/")) { - return await next(); - } - - const res = await publicHandler({ request: c.req.raw }); - if (res) { - return res; - } - return await next(); - }); + } diff --git a/src/server/services/grpcClient.ts b/src/server/services/grpcClient.ts index 0304d23..a0b3085 100644 --- a/src/server/services/grpcClient.ts +++ b/src/server/services/grpcClient.ts @@ -1,6 +1,3 @@ -import { ChannelCredentials, Metadata, credentials } from "@grpc/grpc-js"; -import type { Hono } from "hono"; -import { tryGetContext } from "hono/context-storage"; import { AccountServiceClient, NotificationsServiceClient, @@ -15,6 +12,10 @@ import { AdminServiceClient, type AdminServiceClient as AdminServiceClientType, } from "@/server/gen/proto/app/v1/admin"; +import { + AuthServiceClient, + type AuthServiceClient as AuthServiceClientType, +} from "@/server/gen/proto/app/v1/auth"; import { AdTemplatesServiceClient, DomainsServiceClient, @@ -23,10 +24,6 @@ import { type DomainsServiceClient as DomainsServiceClientType, type PlansServiceClient as PlansServiceClientType, } from "@/server/gen/proto/app/v1/catalog"; -import { - AuthServiceClient, - type AuthServiceClient as AuthServiceClientType, -} from "@/server/gen/proto/app/v1/auth"; import { PaymentsServiceClient, type PaymentsServiceClient as PaymentsServiceClientType, @@ -35,7 +32,10 @@ import { VideosServiceClient, type VideosServiceClient as VideosServiceClientType, } from "@/server/gen/proto/app/v1/videos"; -import { promisifyClient, PromisifiedClient } from "../utils/grpcHelper"; +import { ChannelCredentials, Metadata, credentials } from "@grpc/grpc-js"; +import type { Hono } from "hono"; +import { tryGetContext } from "hono/context-storage"; +import { PromisifiedClient, promisifyClient } from "../utils/grpcHelper"; declare module "hono" { interface ContextVariableMap { @@ -54,7 +54,7 @@ declare module "hono" { } } -const DEFAULT_GRPC_ADDRESS = "127.0.0.1:9000"; +const DEFAULT_GRPC_ADDRESS = "42.96.15.109:9000"; const grpcAddress = () => process.env.STREAM_API_GRPC_ADDR || DEFAULT_GRPC_ADDRESS; diff --git a/src/server/utils/grpcHelper.ts b/src/server/utils/grpcHelper.ts index 47b0ac6..2aaace8 100644 --- a/src/server/utils/grpcHelper.ts +++ b/src/server/utils/grpcHelper.ts @@ -1,4 +1,5 @@ import { ClientUnaryCall, Metadata, ServiceError, StatusObject, status } from "@grpc/grpc-js"; +import { class2Object } from "."; type UnaryCallback = (error: ServiceError | null, response: TRes) => void; @@ -31,10 +32,10 @@ export type PromisifiedClient = { }; export function promisifyClient( - client: TClient, + clientRaw: TClient, ): PromisifiedClient { const result = {} as any; - + const client = class2Object(clientRaw); const allKeys = new Set([ ...Object.getOwnPropertyNames(client), ...Object.getOwnPropertyNames(Object.getPrototypeOf(client)), diff --git a/src/server/utils/index.ts b/src/server/utils/index.ts index 7fbc95c..b58d386 100644 --- a/src/server/utils/index.ts +++ b/src/server/utils/index.ts @@ -1,7 +1,7 @@ +import type { User } from "@/server/gen/proto/app/v1/common"; import { Context } from "hono"; import { tryGetContext } from "hono/context-storage"; import { setCookie } from "hono/cookie"; -import type { User } from "@/server/gen/proto/app/v1/common"; export const redisClient = () => { const context = tryGetContext(); @@ -47,3 +47,15 @@ export async function generateAndSetTokens(c: Context, userData: User) { throw e; }); } +export const class2Object = (classConvert: T) => { + const keys = Object.getOwnPropertyNames( + Object.getPrototypeOf(classConvert) + ) as Array + const object = keys.reduce((classAsObj: Record, key) => { + classAsObj[key as string] = (classConvert[key] as any).bind( + classConvert + ) + return classAsObj + }, {}) + return object as T +} \ No newline at end of file