diff --git a/.gitignore b/.gitignore index 506e4c3..82434cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ # deps node_modules/ + +debug.log diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..9f14697 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "tabWidth": 4, + "useTabs": true, + "singleQuote": false, + "trailingComma": "all", + "arrowParens": "avoid" +} diff --git a/README.md b/README.md index 6dd13e7..d950ba6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ To install dependencies: + ```sh bun install ``` To run: + ```sh bun run dev ``` diff --git a/bun.lockb b/bun.lockb index ec02b3d..79d48b7 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7ffc6c9..ba142ad 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,17 @@ { - "name": "amiopen", - "scripts": { - "dev": "bun run --hot src/index.ts" - }, - "dependencies": { - "hono": "^4.6.9", - "ip": "^2.0.1", - "zod": "^3.23.8" - }, - "devDependencies": { - "@types/bun": "latest", - "@types/ip": "^1.1.3" - } + "name": "amiopen", + "scripts": { + "dev": "bun run --hot src/index.ts", + "prettier": "prettier --write ." + }, + "dependencies": { + "hono": "^4.6.9", + "ip": "^2.0.1", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/bun": "latest", + "@types/ip": "^1.1.3", + "prettier": "^3.3.3" + } } diff --git a/src/index.ts b/src/index.ts index f3269cf..0bf799b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,8 @@ -import { Hono } from 'hono' -import { portRoute } from './routes/port' +import { Hono } from "hono"; +import { portRoute } from "./routes/port"; -const app = new Hono() +const app = new Hono(); -app.route( - "/", - portRoute, -) +app.route("/", portRoute); -export default app +export default app; diff --git a/src/routes/port.ts b/src/routes/port.ts index 543c790..be1ec8d 100644 --- a/src/routes/port.ts +++ b/src/routes/port.ts @@ -2,52 +2,78 @@ import { Hono } from "hono"; import { getConnInfo } from "hono/bun"; import connectToAddress from "../utils/connect-to-address"; import { z } from "zod"; -import * as IP from "ip" +import * as IP from "ip"; export const portRoute = new Hono(); const schema = z.object({ - ip: z.string().refine(ip => IP.isPublic(ip), "This IP address is not valid"), - port: z.string().transform(Number).pipe(z.number().min(1).max(2**16 - 1)), + ip: z + .string() + .refine(ip => IP.isPublic(ip), "This IP address is not valid"), + port: z + .string() + .transform(Number) + .pipe( + z + .number() + .min(1) + .max(2 ** 16 - 1), + ), + timeout: z + .string() + .optional() + .transform(val => (val === undefined ? 5_000 : Number(val))) + .pipe(z.number().min(1_000).max(60_000)), }); portRoute.get("/:port", async context => { - const info = getConnInfo(context) - const rawData = { - ip: info.remote.address || "", - port: context.req.param("port"), - }; - const parsedData = schema.safeParse(rawData); + const info = getConnInfo(context); + const rawData = { + ip: info.remote.address || "", + port: context.req.param("port"), + timeout: context.req.query("timeout") || "", + }; + const parsedData = schema.safeParse(rawData); - if (!parsedData.success) { - return context.json({ error: parsedData.error }, 401); - } + if (!parsedData.success) { + return context.json( + { + error: parsedData.error, + }, + 401, + ); + } - const { ip, port } = parsedData.data; + const { ip, port } = parsedData.data; - const result = await connectToAddress(ip, port) + const result = await connectToAddress(ip, port); - return context.json({ - isOpen: result.isOpen - }) + return context.json({ + isOpen: result.isOpen, + }); }); portRoute.get("/:ip/:port", async context => { - const rawData = { - ip: context.req.param("ip"), - port: context.req.param("port"), - }; - const parsedData = schema.safeParse(rawData); + const rawData = { + ip: context.req.param("ip"), + port: context.req.param("port"), + }; + const parsedData = schema.safeParse(rawData); - if (!parsedData.success) { - return context.json({ error: parsedData.error }, 401); - } + if (!parsedData.success) { + return context.json( + { + error: parsedData.error, + }, + 401, + ); + } - const { ip, port } = parsedData.data; + const { ip, port } = parsedData.data; - const result = await connectToAddress(ip, port) + const result = await connectToAddress(ip, port); - return context.json({ - isOpen: result.isOpen - }) -}) + return context.json({ + isOpen: result.isOpen, + }); +}); diff --git a/src/utils/connect-to-address.ts b/src/utils/connect-to-address.ts index 1434bbc..6a412fc 100644 --- a/src/utils/connect-to-address.ts +++ b/src/utils/connect-to-address.ts @@ -1,54 +1,57 @@ -import * as net from "net" +import * as net from "net"; export interface ConnectionResult { - isOpen: boolean + isOpen: boolean; } -const TIMEOUT = 5_000 +const TIMEOUT = 5_000; -export default function connectToAddress(address: string, port: number): Promise { - return new Promise(async (resolve) => { - const onIsOpen = () => { - if (!socket.destroyed) { - socket.end() - socket.destroy() - } +export default function connectToAddress( + address: string, + port: number, +): Promise { + return new Promise(async resolve => { + const onIsOpen = () => { + if (!socket.destroyed) { + socket.end(); + socket.destroy(); + } - resolve({ - isOpen: true - }) - } - const onIsClosed = () => { - if (!socket.destroyed) { - socket.end() - socket.destroy() - } + resolve({ + isOpen: true, + }); + }; + const onIsClosed = () => { + if (!socket.destroyed) { + socket.end(); + socket.destroy(); + } - resolve({ - isOpen: false - }) - } + resolve({ + isOpen: false, + }); + }; - const socket = new net.Socket() + const socket = new net.Socket(); - // The `setTimeout` function from a socket does not work when connecting, - // so we need to use a custom timeout function ourselves. - setTimeout(() => { - if (!socket.destroyed) { - socket.end() - socket.destroy() - } + // The `setTimeout` function from a socket does not work when connecting, + // so we need to use a custom timeout function ourselves. + setTimeout(() => { + if (!socket.destroyed) { + socket.end(); + socket.destroy(); + } - resolve({ - isOpen: false - }) - }, TIMEOUT) + resolve({ + isOpen: false, + }); + }, TIMEOUT); - socket.on("ready", onIsOpen) - socket.on("close", onIsClosed) - socket.on("error", onIsClosed) - socket.on("timeout", onIsClosed) + socket.on("ready", onIsOpen); + socket.on("close", onIsClosed); + socket.on("error", onIsClosed); + socket.on("timeout", onIsClosed); - socket.connect(port, address) - }) + socket.connect(port, address); + }); } diff --git a/src/utils/timeout.ts b/src/utils/timeout.ts index 9ec9b47..5228274 100644 --- a/src/utils/timeout.ts +++ b/src/utils/timeout.ts @@ -1,4 +1,3 @@ export default function timeout(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)) + return new Promise(resolve => setTimeout(resolve, ms)); } - diff --git a/tsconfig.json b/tsconfig.json index c442b33..9c845ae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { - "compilerOptions": { - "strict": true, - "jsx": "react-jsx", - "jsxImportSource": "hono/jsx" - } -} \ No newline at end of file + "compilerOptions": { + "strict": true, + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx" + } +}