Skip to content

Commit

Permalink
[Improve code]: lib/* 手動formatter・Linter対応 (#84)
Browse files Browse the repository at this point in the history
* 🚨 astro/src/libにESLintのfixオプションを適用

* 🚨 Astroの環境変数をstring型とみなす記述を追加

* 🚨 nullish判定を明確化

* 💬 コメントの空白を半角に変更

* 🏷️ 変数の型情報を明記

* 🎨 astro/src/libにPrettierを適用

* ✏️ ファイル名getIdの綴りを修正

* ✏️ ファイル名変更に伴いimport文を修正

* 🏷️ Astro API用の型をZodで再定義

* 🦺 APIレスポンス取得箇所にバリデーションチェックを追加

* 🏷️ モジュール外で型の名前が重複しないよう変更

* 🩹 APIレスポンス処理時の型エラーに関する文言を修正

* 🏷️ API呼び出し箇所の型定義更新に伴い、エラー判定箇所を修正

* 🥅 getOgpMetaエラー時のステータス番号を定義

* ✏️ Zodオブジェクトの命名規則を統一

* 🥅 エラーレスポンスからhtmlを削除

* 🔥 使用されていないレスポンス型定義用のJSONを削除

* 🥅 エラー型判定の実装を改善

* 🥅 エラー型の判定を`"error" in val`形式で統一

* ⚰️ 使用されていない変数を削除

* ⚰️ 不要になった行を削除

* ➕ 依存関係にZodを追加

* 🥅 エラー型の判定ロジックを変更 & 型アサーション削除

* 🔥 不要になったインポート行を削除
  • Loading branch information
ZEKE320 authored Apr 12, 2024
1 parent b330f59 commit 6b1ffe1
Show file tree
Hide file tree
Showing 17 changed files with 314 additions and 223 deletions.
2 changes: 1 addition & 1 deletion astro/src/components/Client/bsky/buttons/PostButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export const Component = ({
accessJwt: session.accessJwt,
uri: createRecordResult.uri,
})
if (typeof createPageResult?.error !== "undefined") {
if ("error" in createPageResult) {
const e: Error = new Error(createPageResult.message)
e.name = createPageResult.error
throw e
Expand Down
5 changes: 2 additions & 3 deletions astro/src/components/Client/pagedb/PageDeleteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ const Component = ({
did: session.did,
accessJwt: session.accessJwt
})
if (typeof resDeletePage?.error === "undefined" &&
resDeletePage.result === "ok") {
if (!("error" in resDeletePage)) {
setMsgInfo({
isError: false,
msg: "ページを削除しました!"
})
window.location.reload()
} else {
let e: Error = new Error(resDeletePage.message)
const e: Error = new Error(resDeletePage.message)
e.name = "pagedelete.tsx"
throw e
}
Expand Down
9 changes: 5 additions & 4 deletions astro/src/components/Client/pagedb/PageViewForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useContext, useState, ReactNode, Dispatch, SetStateAction } from "react"
import { pagesPrefix } from "@/env/envs"
import getIds from "@/lib/pagedbAPI/geIds"
import getIds from "@/lib/pagedbAPI/getIds"
import { Session_context } from "../common/contexts"
import { load_circle, link } from "../common/tailwindVariants"
import ProfileCard from "./ProfileCard"
Expand Down Expand Up @@ -34,20 +34,21 @@ const Component = ({
const ids = await getIds({
handle: session.handle!
})
if (typeof ids?.error !== "undefined") {
let e: Error = new Error(ids.message)
if ("error" in ids) {
const e: Error = new Error(ids.message)
e.name = ids.error
throw e
}
setMsgInfo({
isError: false,
msg: "投稿一覧を読み込みました!"
})

setList(
<div className="mx-auto">
<div>OGP生成したページ一覧</div>
<div className="grid sm:grid-cols-3 grid-cols-1">
{ids?.ids.map((value) => {
{ids.ids.map((value) => {
return (
<div className=" bg-white rounded-lg px-2 py-1 m-1 border-2">
<a className={link()} target="_blank"
Expand Down
14 changes: 8 additions & 6 deletions astro/src/lib/api/createErrResponse.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { errorResponse } from "./types"

const createErrResponse = ({
statusCode
statusCode,
}: {
statusCode: number
}): errorResponse => {
Expand All @@ -10,31 +10,33 @@ const createErrResponse = ({
case 405:
response = <errorResponse>{
error: "Method Not Allowed",
message: "Invalid method requested. API allow GET only."
message: "Invalid method requested. API allow GET only.",
}
break
case 406:
response = <errorResponse>{
error: "Not Acceptable",
message: "Invalid parameter requested. Check your request."
message: "Invalid parameter requested. Check your request.",
}
break
case 502:
response = <errorResponse>{
error: "Bad Gateway",
message: "Failed to get correct response from gateway. Announce server administrator."
message:
"Failed to get correct response from gateway. Announce server administrator.",
}
break
case 500:
response = <errorResponse>{
error: "Internal Server Error",
message: "Failed to internal process. Announce server administrator."
message:
"Failed to internal process. Announce server administrator.",
}
break
default:
response = <errorResponse>{
error: "Unexpected Error",
message: "Unexpected Error occured."
message: "Unexpected Error occured.",
}
}
response.status = statusCode
Expand Down
41 changes: 24 additions & 17 deletions astro/src/lib/api/types.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
export type ogpMetaData = {
type: "meta"
title: string,
description: string,
image: string,
}
import { z } from "zod"

export const ZodOgpMetaData = z.object({
type: z.literal("meta"),
title: z.string(),
description: z.string(),
image: z.string(),
})
export type ogpMetaData = z.infer<typeof ZodOgpMetaData>

// APIのエラーレスポンスを定義
export type errorResponse = {
type: "error"
error: string,
message: string,
status: number,
}

export type apiRequest = {
type: "api"
decodedUrl: string,
language: string,
}
export const ZodErrorResponse = z.object({
type: z.literal("error"),
error: z.string(),
message: z.string(),
status: z.number(),
})
export type errorResponse = z.infer<typeof ZodErrorResponse>

export const ZodApiRequest = z.object({
type: z.literal("api"),
decodedUrl: z.string(),
language: z.string(),
})
export type apiRequest = z.infer<typeof ZodApiRequest>
37 changes: 18 additions & 19 deletions astro/src/lib/api/validateRequest.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import createErrResponse from "./createErrResponse";
import createErrResponse from "./createErrResponse"
import { isNotProduction } from "@/lib/vars"
import { apiRequest, errorResponse } from "./types";
import { apiRequest, errorResponse } from "./types"

const protocol_validation: RegExp = /(dict|file|ftp|gopher|ldap|smtp|telnet|tftp):\/\//
const protocol_validation: RegExp =
/(dict|file|ftp|gopher|ldap|smtp|telnet|tftp):\/\//
const loopback_validation: RegExp = /localhost/
const ipv4_validation: RegExp = /(?:\d{0,3}\.){3}\d{0,3}/
const ipv6_validation: RegExp = /\[[0-9a-fA-F:]+\]/
Expand All @@ -13,7 +14,7 @@ const ipv6_validation: RegExp = /\[[0-9a-fA-F:]+\]/
* @returns apiResponse または エラーレスポンス
*/
const validateRequestReturnURL = ({
request
request,
}: {
request: Request
}): apiRequest | errorResponse => {
Expand All @@ -24,8 +25,8 @@ const validateRequestReturnURL = ({
})
}

const url = new URL(request.url).searchParams.get("url");
const lang = new URL(request.url).searchParams.get("lang");
const url: string | null = new URL(request.url).searchParams.get("url")
const lang: string | null = new URL(request.url).searchParams.get("lang")
if (url === null) {
return createErrResponse({
statusCode: 406,
Expand All @@ -36,20 +37,18 @@ const validateRequestReturnURL = ({
statusCode: 406,
})
}
const decodedUrl = decodeURIComponent(url)
const decodedUrl: string = decodeURIComponent(url)
// SSRF対策
// Productionではない環境についてはlocalhostの実行を許可
const validation = !isNotProduction ? (
// Productionの場合は厳格なルールを指定
protocol_validation.test(decodedUrl) ||
loopback_validation.test(decodedUrl) ||
ipv4_validation.test(decodedUrl) ||
ipv6_validation.test(decodedUrl)
) : (
// Not Productionの場合は(Cloudflare Zero Trustといった)
// 低レイヤー対策前提でlocalhostを許可
protocol_validation.test(decodedUrl)
)
const validation: boolean = !isNotProduction
? // Productionの場合は厳格なルールを指定
protocol_validation.test(decodedUrl) ||
loopback_validation.test(decodedUrl) ||
ipv4_validation.test(decodedUrl) ||
ipv6_validation.test(decodedUrl)
: // Not Productionの場合は(Cloudflare Zero Trustといった)
// 低レイヤー対策前提でlocalhostを許可
protocol_validation.test(decodedUrl)
if (validation) {
return createErrResponse({
statusCode: 502,
Expand All @@ -59,7 +58,7 @@ const validateRequestReturnURL = ({
return <apiRequest>{
type: "api",
decodedUrl: decodedUrl,
language: lang
language: lang,
}
}
export default validateRequestReturnURL
90 changes: 58 additions & 32 deletions astro/src/lib/getOgp.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,91 @@
import type { ogpMetaData, errorResponse } from "@/lib/api/types";
import type { errorResponse, ogpMetaData } from "@/lib/api/types"
import { ZodErrorResponse, ZodOgpMetaData } from "@/lib/api/types"

// note: エラー規格を型定義として決めた方がいい( error@Component: message とするなど)
// note: エラー規格を型定義として決めた方がいい( error@Component: message とするなど)
export const getOgpMeta = async ({
siteurl,
externalUrl,
languageCode,
}: {
siteurl: string,
externalUrl: string,
languageCode: string,
siteurl: string
externalUrl: string
languageCode: string
}): Promise<ogpMetaData | errorResponse> => {
const apiUrl = new URL("/api/getOgpMeta", siteurl)
apiUrl.searchParams.append("url", encodeURIComponent(externalUrl))
apiUrl.searchParams.append("lang", languageCode)
return await fetch(apiUrl, {
method: 'GET',
method: "GET",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
"Accept-Language": languageCode,
"Cache-Control": "no-cache",
}
}).then(async (response) => {
if (!response?.ok) {
let res: errorResponse = await response.json()
let e: Error = new Error(res.message)
e.name = res.error
throw e
}
return await response.json() as ogpMetaData
}).catch((e: Error) => {
return <errorResponse>{
type: "error",
error: `${e.name}@getOgpMeta`,
message: e.message
}
},
})
.then(async response => {
const jsonResponse: unknown = await response.json()
const responseParsedAsError =
ZodErrorResponse.safeParse(jsonResponse)
const responseParsedAsOgpMetaData =
ZodOgpMetaData.safeParse(jsonResponse)

if (!(response.ok && responseParsedAsOgpMetaData.success)) {
const e: Error = new Error(
responseParsedAsError.success
? responseParsedAsError.data.message
: "Unexpected Response Type@getOgpMeta",
)
e.name = responseParsedAsError.success
? responseParsedAsError.data.error
: "Unexpected Response Type"
throw e
}

return responseParsedAsOgpMetaData.data
})
.catch((e: Error) => {
return {
type: "error",
error: `${e.name}@getOgpMeta`,
message: e.message,
status: 500,
}
})
}
// Blob型はユニオン型として扱うことが難しいため、エラーハンドリングできない
export const getOgpBlob = async ({
siteurl,
externalUrl,
languageCode,
}: {
siteurl: string,
externalUrl: string,
languageCode: string,
siteurl: string
externalUrl: string
languageCode: string
}): Promise<Blob> => {
const apiUrl = new URL("/api/getOgpBlob", siteurl)
apiUrl.searchParams.append("url", encodeURIComponent(externalUrl))
apiUrl.searchParams.append("lang", languageCode)
return await fetch(apiUrl, {
method: 'GET',
method: "GET",
headers: {
"Accept-Language": languageCode,
"Cache-Control": "no-cache",
}
}).then(async (response) => {
if (!response?.ok) {
let res: errorResponse = await response.json()
let e: Error = new Error(res.message)
e.name = `${res.error}@getOgpBlob`
},
}).then(async response => {
if (!response.ok) {
const responseParsedAsError = ZodErrorResponse.safeParse(
response.json(),
)
const e: Error = new Error(
responseParsedAsError.success
? responseParsedAsError.data.message
: "Unexpected Response Type@getOgpBlob",
)
e.name = `${
responseParsedAsError.success
? responseParsedAsError.data.error
: "Unexpected Response Type"
}@getOgpBlob`
throw e
}
return await response.blob()
Expand Down
Loading

0 comments on commit 6b1ffe1

Please sign in to comment.