Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
helios2003 committed Aug 8, 2024
1 parent a8c8cb5 commit 730df15
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 67 deletions.
22 changes: 12 additions & 10 deletions apps/studio-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,33 @@
"@asyncapi/protobuf-schema-parser": "^3.2.8",
"@asyncapi/react-component": "^1.2.2",
"@asyncapi/specs": "^6.5.4",
"@codemirror/view": "^6.26.3",
"@ebay/nice-modal-react": "^1.2.10",
"@headlessui/react": "^1.7.4",
"@hookstate/core": "^4.0.0-rc21",
"@monaco-editor/react": "^4.4.6",
"@tippyjs/react": "^4.2.6",
"js-base64": "^3.7.3",
"js-file-download": "^0.4.12",
"js-yaml": "^4.1.0",
"monaco-editor": "0.34.1",
"monaco-yaml": "4.0.2",
"react-hot-toast": "2.4.0",
"react-icons": "^4.6.0",
"reactflow": "^11.2.0",
"@stoplight/yaml": "^4.3.0",
"@codemirror/view": "^6.26.3",
"@tippyjs/react": "^4.2.6",
"@types/node": "20.4.6",
"@types/react": "18.2.18",
"@types/react-dom": "18.2.7",
"autoprefixer": "10.4.14",
"axios": "^1.7.3",
"codemirror": "^6.0.1",
"crawler-user-agents": "^1.0.142",
"eslint-config-next": "13.4.12",
"js-base64": "^3.7.3",
"js-file-download": "^0.4.12",
"js-yaml": "^4.1.0",
"monaco-editor": "0.34.1",
"monaco-yaml": "4.0.2",
"next": "14.2.3",
"postcss": "8.4.31",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hot-toast": "2.4.0",
"react-icons": "^4.6.0",
"reactflow": "^11.2.0",
"tailwindcss": "3.3.3",
"tippy.js": "^6.3.7",
"typescript": "5.1.6",
Expand Down
104 changes: 104 additions & 0 deletions apps/studio-next/src/app/api/crawler/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { NextRequest, NextResponse } from "next/server";
import parseURL from "@/helpers/parser";
import { DocumentInfo } from "@/types";
import axios from "axios";
import { metadata } from "@/app/page";

export async function GET(request: NextRequest) {
const Base64searchParams = request.nextUrl.searchParams.get('base64');
const URLsearchParams = request.nextUrl.searchParams.get('url');

try {
if (!Base64searchParams && !URLsearchParams) return new NextResponse(null, { status: 200 });
let info: DocumentInfo | null = null;

if (Base64searchParams) {
// directly run the parsing function
info = await parseURL(Base64searchParams);
}
if (URLsearchParams) {
// fetch the document information from the URL
try {
const response = await axios.get(URLsearchParams);
if (response.status === 200) {
info = await parseURL(response.data);
} else {
return new NextResponse("Not a valid URL", { status: 500 });
}
} catch (error) {
return new NextResponse("Not a valid URL", { status: 500 });
}
}

if (!info) {
const ogImage = "https://raw.githubusercontent.com/asyncapi/studio/master/apps/studio-next/public/img/meta-studio-og-image.jpeg";

const crawlerInfo = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>"${metadata.openGraph?.title}"</title>
<meta property="og:title" content="${metadata.openGraph?.title}" />
<meta property="og:description" content="${metadata.openGraph?.description}" />
<meta property="og:url" content="${metadata.openGraph?.url}" />
<meta property="og:image" content="${ogImage}" />
`
return new NextResponse(crawlerInfo, {
headers: {
'Content-Type': 'text/html',
},
})
}

let ogImageParams = new URLSearchParams();

if (info.title) {
ogImageParams.append('title', info.title.toString());
}
if (info.description) {
ogImageParams.append('description', info.description.toString());
}
if (info.numServers) {
ogImageParams.append('numServers', info.numServers.toString());
}
if (info.numChannels) {
ogImageParams.append('numChannels', info.numChannels.toString());
}
if (info.numOperations) {
ogImageParams.append('numOperations', info.numOperations.toString());
}
if (info.numMessages) {
ogImageParams.append('numMessages', info.numMessages.toString());
}

const ogImageurl = `https://ogp-studio.netlify.app/api/og?${ogImageParams.toString()}`;

const crawlerInfo = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${info.title}</title>
${info.title ? `<meta property="og:title" content="${info.title}" />` : ''}
${info.description ? `<meta property="og:description" content="${info.description}" />` : ''}
<meta property="og:image" content=${ogImageurl} />
</head>
</html>
`;

return new NextResponse(crawlerInfo, {
status: 200,
headers: {
'Content-Type': 'text/html',
},
});
} catch (err) {
return new NextResponse("Not a valid URL", { status: 500 });
}
}

53 changes: 53 additions & 0 deletions apps/studio-next/src/helpers/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Input, Parser } from '@asyncapi/parser';
import { DocumentInfo } from '@/types';

export default async function parseURL(asyncapiDocument: string): Promise<DocumentInfo | null> {
const parser = new Parser();

const base64Regex = /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$/;

let decodedDocument: Input = "";
if (base64Regex.test(asyncapiDocument)) {
decodedDocument = Buffer.from(asyncapiDocument, "base64").toString("utf-8");
} else {
decodedDocument = asyncapiDocument;
}

const { document, diagnostics } = await parser.parse(decodedDocument);

if (diagnostics.length) {
return null;
}

let title = document?.info().title();
if (title) {
title = title.length <= 20 ? title : title.slice(0, 20) + "...";
}
const version = document?.info().version();

let description = document?.info().description();
if (description) {
description = description.length <= 100 ? description : description.slice(0, 100) + "...";
}

const servers = document?.allServers();
const channels = document?.allChannels();
const operations = document?.allOperations();
const messages = document?.allMessages();

const numServers = servers?.length;
const numChannels = channels?.length;
const numOperations = operations?.length;
const numMessages = messages?.length;

const response = {
title,
version,
description,
numServers,
numChannels,
numOperations,
numMessages
};
return response;
}
27 changes: 27 additions & 0 deletions apps/studio-next/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NextRequest, NextResponse, userAgent } from "next/server";
import crawlers from 'crawler-user-agents';

export async function middleware(request: NextRequest) {
const userAgents = crawlers.map(crawler => crawler.pattern);
const requestInfo = userAgent(request);
const res = NextResponse.next();

for (const ua of userAgents) {
if (requestInfo.ua.toLowerCase().includes(ua.toLowerCase())) {

const documentURL = request.nextUrl.searchParams.get("url");
const encodedDocument = request.nextUrl.searchParams.get("base64");

if (!encodedDocument && !documentURL) {
return res;
}
if (encodedDocument) {
return NextResponse.rewrite(new URL(`/api/crawler?base64=${encodedDocument}`, request.url));
}
if (documentURL) {
return NextResponse.rewrite(new URL(`/api/crawler?url=${documentURL}`, request.url));
}
}
}
return res;
}
10 changes: 10 additions & 0 deletions apps/studio-next/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import type specs from '@asyncapi/specs';

export type SpecVersions = keyof typeof specs.schemas;

export interface DocumentInfo {
title? : string,
version? : string,
description? : string,
numServers? : number,
numChannels? : number,
numOperations? : number,
numMessages?: number
}
Loading

0 comments on commit 730df15

Please sign in to comment.