import { env } from "@config/env";
import grpc from "@grpc/grpc-js";
import protoLoader from "@grpc/proto-loader";
import { getLogger } from "@utils/asyncLocalStorage";
import path, { dirname } from "node:path";
import { fileURLToPath } from "node:url";
/**
* Auth gRPC Service
* @category Services
*/
export class AuthGrpcService {
client = null;
/**
* Constructor
* @category Services
* @param {string} accessToken - The access token to authenticate the client.
*/
constructor(accessToken) {
if (!this.client) {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROTO_PATH = path.resolve(__dirname, "../grpc/auth.proto");
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: false,
oneofs: true,
});
const loaded = grpc.loadPackageDefinition(packageDefinition);
this.client = new loaded.auth.Auth(`${env.AUTH_GRPC_HOST}:${env.AUTH_GRPC_PORT}`, grpc.credentials.createInsecure(), {
interceptors: [this.authInterceptor(accessToken || null)],
});
}
}
/**
* Creates a gRPC interceptor to add an authorization token to request metadata.
* @category Services
* @param {string | null} token - The Bearer token to attach to outgoing gRPC requests. If `null`, no authorization header is added.
* @returns {Function} A gRPC interceptor function that can be used with gRPC clients.
*/
authInterceptor(token) {
return function (options, nextCall) {
return new grpc.InterceptingCall(nextCall(options), {
start: function (metadata, listener, next) {
if (token) {
metadata.add("authorization", `Bearer ${token}`);
}
next(metadata, listener);
},
});
};
}
/**
* Fetches a user by ID via the Auth gRPC service.
* @category Services
* @param {string} userId - The unique identifier of the user to fetch.
* @returns {Promise<User>} - The user object corresponding to the given ID.
* @throws {Error} If the user is not found or if the gRPC request fails.
*/
async getUserById(userId) {
return new Promise((resolve, reject) => {
const logger = getLogger();
const client = this.client;
client?.getUserById({ id: userId }, (error, response) => {
if (error) {
logger.error({ error, userId }, "gRPC getUserById failed");
reject(error);
return;
}
if (!response?.user) {
reject(new Error("User not found"));
return;
}
resolve(response.user);
});
});
}
}
Source