Fix some logic handling error

This commit is contained in:
delivan 2022-12-23 22:30:35 +09:00
parent da01e40550
commit d33788e2b1
9 changed files with 143 additions and 86 deletions

View File

@ -23,7 +23,8 @@ class ErrorBoundary extends Component<Props, State> {
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("Uncaught error:", error, errorInfo);
captureException(error);
const errorMessage = error.message || error;
captureException(errorMessage);
}
public render() {

View File

@ -2,12 +2,11 @@
const { withSentryConfig } = require("@sentry/nextjs");
const nextConfig = {
sentry: {
hideSourceMaps: true,
},
api: {
externalResolver: true,
},
...(!!process.env.NEXT_PUBLIC_SENTRY_DSN && {
sentry: {
hideSourceMaps: true,
},
}),
reactStrictMode: false,
swcMinify: true,
compiler: {

View File

@ -1,8 +1,7 @@
import { captureException } from "@sentry/nextjs";
import { withIronSessionApiRoute } from "iron-session/next";
import type { NextApiRequest, NextApiResponse } from "next";
import { ironOptions } from "../../iron.config";
import { IcnsVerificationResponse } from "../../types";
import { request } from "../../utils/url";
export default withIronSessionApiRoute(async function handler(
req: NextApiRequest,
@ -10,30 +9,42 @@ export default withIronSessionApiRoute(async function handler(
) {
try {
if (!process.env.ICNS_VERIFIER_ORIGIN_LIST) {
console.log(".env is not set");
const errorMessage = ".env is not set";
console.error(errorMessage);
captureException(errorMessage);
return res.status(500).json({ error: "Internal server error" });
}
const verifierOriginList = process.env.ICNS_VERIFIER_ORIGIN_LIST.split(",");
const verificationList = await Promise.allSettled(
verifierOriginList.map((verfierOrigin) =>
request<IcnsVerificationResponse>(
`${verfierOrigin}/api/verify_twitter`,
{
verifierOriginList.map(async (verfierOrigin) =>
(
await fetch(`${verfierOrigin}/api/verify_twitter`, {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(req.body),
},
),
})
).json(),
),
);
const errorTrimmedVerificationList = verificationList.map(
(verification) => ({
...verification,
...(verification.status === "rejected" && {
reason: verification.reason.message || verification.reason,
}),
}),
);
res.status(200).json({
verificationList,
verificationList: errorTrimmedVerificationList,
});
} catch (error) {
console.error(error);
captureException(error);
res.status(500).json({ error: "Internal server error" });
}
},

View File

@ -1,8 +1,8 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { captureException } from "@sentry/nextjs";
import { withIronSessionApiRoute } from "iron-session/next";
import { request } from "../../utils/url";
import { ironOptions } from "../../iron.config";
import type { NextApiRequest, NextApiResponse } from "next";
import { twitterApiBaseUrl } from "../../constants/twitter";
import { ironOptions } from "../../iron.config";
export default withIronSessionApiRoute(async function handler(
req: NextApiRequest,
@ -45,53 +45,60 @@ export default withIronSessionApiRoute(async function handler(
params.append("code_verifier", req.session.code_verifier);
}
const { access_token: accessToken, refresh_token } =
await request<TwitterOAuth2TokenResponse>(
`${twitterApiBaseUrl}/oauth2/token`,
{
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${Buffer.from(
`${process.env.TWITTER_CLIENT_ID}:${process.env.TWITTER_CLIENT_SECRET}`,
).toString("base64")}`,
},
body: params,
const {
access_token: accessToken,
refresh_token,
}: TwitterOAuth2TokenResponse = await (
await fetch(`${twitterApiBaseUrl}/oauth2/token`, {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${Buffer.from(
`${process.env.TWITTER_CLIENT_ID}:${process.env.TWITTER_CLIENT_SECRET}`,
).toString("base64")}`,
},
);
body: params,
})
).json();
req.session.refresh_token = refresh_token;
await req.session.save();
const {
data: {
id,
username,
name,
profile_image_url,
description,
public_metrics,
},
} = await request<TwitterUsersMeResponse>(
`${twitterApiBaseUrl}/users/me?user.fields=profile_image_url,public_metrics,description`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
const { data, title }: TwitterUsersMeResponse = await (
await fetch(
`${twitterApiBaseUrl}/users/me?user.fields=profile_image_url,public_metrics,description`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
},
);
)
).json();
const {
id,
username,
name,
profile_image_url,
description,
public_metrics,
} = data || {};
res.status(200).json({
accessToken,
id,
username,
name,
profile_image_url: profile_image_url.replace("normal.jpg", "400x400.jpg"),
profile_image_url: profile_image_url?.replace(
"normal.jpg",
"400x400.jpg",
),
description,
public_metrics,
error: title,
});
} catch (error) {
} catch (error: any) {
console.error(error);
res.status(500).json({ error: "Internal server error " });
captureException(error);
res.status(500).json({ error: "Internal Server Error" });
}
},
ironOptions);
@ -105,7 +112,7 @@ interface TwitterOAuth2TokenResponse {
}
interface TwitterUsersMeResponse {
data: {
data?: {
id: string;
username: string;
name: string;
@ -113,6 +120,11 @@ interface TwitterUsersMeResponse {
description: string;
public_metrics: TwitterPublicMetrics;
};
// Error data
title?: string;
detail?: string;
type?: string;
status?: string;
}
export interface TwitterPublicMetrics {

View File

@ -1,13 +1,14 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { captureException } from "@sentry/nextjs";
import crypto from "crypto";
import { withIronSessionApiRoute } from "iron-session/next";
import { base64URLEncode } from "../../utils/encoding";
import { buildQueryString } from "../../utils/url";
import { ironOptions } from "../../iron.config";
import type { NextApiRequest, NextApiResponse } from "next";
import {
twitterOAuthBaseUrl,
twitterOAuthScopes,
} from "../../constants/twitter";
import { ironOptions } from "../../iron.config";
import { base64URLEncode } from "../../utils/encoding";
import { buildQueryString } from "../../utils/url";
export default withIronSessionApiRoute(async function handler(
req: NextApiRequest,
@ -46,6 +47,7 @@ export default withIronSessionApiRoute(async function handler(
res.status(200).json({ authUrl });
} catch (error) {
console.error(error);
captureException(error);
res.status(500).json({ error: "Internal server error" });
}
},

View File

@ -239,13 +239,12 @@ export default function VerificationPage() {
}
} catch (error) {
if (error instanceof Error) {
if (error.message === TWITTER_LOGIN_ERROR) {
setErrorMessage({ message: TWITTER_LOGIN_ERROR, path: "/" });
setErrorModalOpen(true);
}
setErrorMessage({ message: error.message, path: "/" });
setErrorModalOpen(true);
}
console.error(error);
captureException(error);
} finally {
setIsLoadingInit(false);
}
@ -404,8 +403,7 @@ export default function VerificationPage() {
if (verification.status === "rejected") {
if (verification.reason) {
const errorMessage =
verification.reason.message || verification.reason;
const errorMessage = verification.reason;
captureException(errorMessage);
}
}
@ -443,8 +441,6 @@ export default function VerificationPage() {
rest: REST_URL,
};
console.log(aminoMsgs);
const simulated = await simulateMsgs(
chainInfo,
walletKey.bech32Address,

View File

@ -1,5 +1,5 @@
import {
IcnsVerificationResponse,
IcnsVerificationResponseOnFrontend,
TwitterAuthInfoResponse,
TwitterAuthUrlResponse,
} from "../types";
@ -27,15 +27,18 @@ export const verifyTwitterAccount = async (
accessToken: string,
) => {
return (
await request<IcnsVerificationResponse>("/api/icns-verification", {
method: "post",
headers: {
"Content-Type": "application/json",
await request<IcnsVerificationResponseOnFrontend>(
"/api/icns-verification",
{
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
claimer: claimer,
authToken: accessToken,
}),
},
body: JSON.stringify({
claimer: claimer,
authToken: accessToken,
}),
})
)
).verificationList;
};

View File

@ -38,6 +38,30 @@ export interface IcnsVerificationResponse {
)[];
}
export interface IcnsVerificationResponseOnFrontend {
verificationList: (
| {
status: "fulfilled";
value: {
errors: Error[];
data: {
// JSON string
verifying_msg: string;
// Base64 encoded
public_key: string;
// Base64 encoded
signature: string;
algorithm: string;
};
};
}
| {
status: "rejected";
reason: string;
}
)[];
}
export interface NameByTwitterIdQueryResponse {
data: {
name: string;

View File

@ -2,23 +2,32 @@ import { TwitterLoginSuccess } from "../types";
import { TWITTER_LOGIN_ERROR } from "../constants/error-message";
import { WALLET_INSTALL_URL } from "../constants/wallet";
export function request<TResponse>(
export async function request<TResponse>(
url: string,
config: RequestInit = {},
customConfig?: {
isErrorIgnore?: boolean;
},
): Promise<TResponse> {
return fetch(url, config)
.then((response) => {
if (!response.ok && !customConfig?.isErrorIgnore) {
throw new Error(
`This is an HTTP error: The status is ${response.status} ${response.statusText}`,
);
}
return response.json();
})
.then((data) => data as TResponse);
const response = await fetch(url, config);
const data = await response.json();
if (
(!response.ok || data.error || data.errors) &&
!customConfig?.isErrorIgnore
) {
const { error, errors } = data;
let errorMessage;
if (error && error.error) {
errorMessage = error.error_description;
} else {
errorMessage =
(error && error.toString()) || (errors && errors.toString());
}
throw new Error(errorMessage);
}
return data;
}
export function buildQueryString(query: Record<string, any>): string {