All files / src/handlers/api/line/v2/bot/message/push route.ts

1.06% Statements 1/94
100% Branches 0/0
0% Functions 0/2
1.06% Lines 1/94

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 1181x                                                                                                                                                                                                                                          
import {
  EncryptedValue,
  generateJwt,
  issueChannelAccessToken,
} from "@messaging-gateway/lib";
import { PrismaClient } from "@prisma/client";
import { messagingApi, HTTPFetchError } from "@line/bot-sdk";
import { v4 as uuidv4 } from "uuid";
import { Env } from "@/Env";
import { Logger } from "@/Logger";
import { ErrorObject } from "@/types/api";
import express from "express";
 
export async function POST(
  env: Env,
  parentLogger: Logger,
  req: express.Request,
  res: express.Response
) {
  const requestId = uuidv4();
  const logger = parentLogger.child({ requestId });
 
  const channelId = req.get("X-MessagingGateway-Line-Channel-Id");
  const body = req.body as messagingApi.PushMessageRequest;
 
  logger.info("received request", { channelId, body });
 
  if (!channelId) {
    const errObj: ErrorObject = {
      message: "X-MessagingGateway-Line-Channel-Id is empty or not exists",
    };
    res.status(400).json(errObj);
    return;
  }
 
  const childLogger = logger.child({ channelId });
  const resObj = await sendMessage(env, channelId, body, childLogger);
  if (typeof resObj === "string") {
    const errObj: ErrorObject = { message: resObj };
    res.status(400).json(errObj);
  } else {
    childLogger.info("success to send message");
    res.status(200).json(resObj);
  }
}
 
async function sendMessage(
  env: Env,
  channelId: string,
  body: messagingApi.PushMessageRequest,
  logger: Logger
): Promise<messagingApi.PushMessageResponse | string> {
  const prisma = new PrismaClient();
  const lineChannel = await prisma.line_channels.findUnique({
    where: {
      id: channelId,
    },
  });
  if (!lineChannel) {
    logger.error("failed to find lineChannel, channel is not found");
    return `channel(id=${channelId}) is not found`;
  }
  logger.debug("found lineChannel record");
 
  const encryptedSecretKey = EncryptedValue.makeFromSerializedText(
    lineChannel.encrypted_secret_key
  );
 
  const secretKey = encryptedSecretKey.decrypt(env.encryptionPassword);
  logger.debug("decrypted secret key");
 
  const kid = lineChannel.kid;
  const tokenExpSec = 60 * 1;
  const jwt = await generateJwt(channelId, secretKey.value(), kid, tokenExpSec);
  logger.debug("generated jwt");
 
  let accessToken: string;
  try {
    const result = await issueChannelAccessToken(jwt);
    accessToken = result.accessToken;
  } catch (err) {
    let msg = "failed to issue channel access token";
    if (err instanceof HTTPFetchError) {
      try {
        const body = JSON.parse(err.body);
        msg += `, ${body.error}(${body.error_description})`;
      } catch (err) {
        logger.warn("failed to parse response", { message: err });
      }
    }
    logger.error(msg, { message: err });
    return msg;
  }
  logger.debug("issued channel access token");
 
  const client = new messagingApi.MessagingApiClient({
    channelAccessToken: accessToken,
  });
  try {
    return await client.pushMessage(body);
  } catch (err) {
    let msg = "failed to push message";
    try {
      if (err instanceof HTTPFetchError) {
        const body = JSON.parse(err.body) as messagingApi.ErrorResponse;
        for (const detail of body.details) {
          msg += `, ${detail.message}(${detail.property})`;
        }
      }
    } catch (err) {
      logger.warn("failed to parse response", { message: err });
    }
 
    logger.error(msg, { message: err });
    return msg;
  }
}