aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Emoji.tsx3
-rw-r--r--src/Image.tsx7
-rw-r--r--src/benzinConfig.ts9
-rw-r--r--src/deepReadDir.tsx12
-rw-r--r--src/lib/nginxAdapter.ts50
-rw-r--r--src/lib/types.ts15
-rw-r--r--src/pages/[...path].tsx39
-rw-r--r--src/pages/_app.tsx2
8 files changed, 101 insertions, 36 deletions
diff --git a/src/Emoji.tsx b/src/Emoji.tsx
index b928087..df5108d 100644
--- a/src/Emoji.tsx
+++ b/src/Emoji.tsx
@@ -1,5 +1,6 @@
import { FC } from 'react';
import Image from 'next/future/image';
+import benzinConfig from './benzinConfig';
interface Props {
children: string[];
@@ -9,7 +10,7 @@ const Emoji: FC<Props> = ({ children }) => {
const src = children[0];
return (
<Image
- src={`/emoji/${src}`}
+ src={`${benzinConfig.CDN}/public/emoji/${src}`}
width={16}
alt={`${src}-emoji`}
/>
diff --git a/src/Image.tsx b/src/Image.tsx
index 770c383..37e55ba 100644
--- a/src/Image.tsx
+++ b/src/Image.tsx
@@ -1,13 +1,12 @@
import ImageBase from 'next/future/image';
import { FC } from 'react';
+import benzinConfig from './benzinConfig';
type Props = Record<'src' | 'alt', string>;
-const IMAGE_CDN = 'http://localhost:8000';
-
const localizeSrc = (src: string) => {
- if (process.env.NODE_ENV === 'production' || src.startsWith('http')) return src;
- return IMAGE_CDN + src;
+ if (src.startsWith('http')) return src;
+ return benzinConfig.CDN + src;
}
const Image: FC<Props> = ({ src, ...props }) => {
diff --git a/src/benzinConfig.ts b/src/benzinConfig.ts
new file mode 100644
index 0000000..d310bb3
--- /dev/null
+++ b/src/benzinConfig.ts
@@ -0,0 +1,9 @@
+import { BenzinConfig } from './lib/types';
+import nginxAdapter from './lib/nginxAdapter';
+
+const benzinConfig: BenzinConfig = {
+ CDN: 'http://localhost:8000',
+ adapter: nginxAdapter,
+};
+
+export default benzinConfig;
diff --git a/src/deepReadDir.tsx b/src/deepReadDir.tsx
deleted file mode 100644
index ec585ec..0000000
--- a/src/deepReadDir.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import {lstat, readdir} from 'node:fs/promises'
-import {join} from 'node:path'
-
-const deepReadDir = async (dirPath: string): Promise<string[]> => await Promise.all(
- (await readdir(dirPath)).map(async (entity) => {
- const path = join(dirPath, entity)
- return (await lstat(path)).isDirectory() ? await deepReadDir(path) : path
- }),
-).then(arr => arr.flat());
-
-
-export default deepReadDir;
diff --git a/src/lib/nginxAdapter.ts b/src/lib/nginxAdapter.ts
new file mode 100644
index 0000000..fdadd87
--- /dev/null
+++ b/src/lib/nginxAdapter.ts
@@ -0,0 +1,50 @@
+import _ from 'lodash';
+import Bluebird from 'bluebird';
+import axios from 'axios';
+import mem from 'mem';
+import { Adapter } from './types';
+
+const listNginxDirectory = async (path: string): Promise<string[]> => {
+ const basePath = _.trimEnd(path.match('http[s]?://(.*?)/')?.[0], '/');
+ const response = await axios(path);
+ const [_thisDir, ...results] = response.data
+ .match(/href='(.*)'/g)
+ .map((s: string) => s.match(/'(.*)'/)?.[1])
+ .map((s: string) => basePath + s);
+ return results;
+}
+
+const deepListNginxDirectory = async (path: string): Promise<string[]> => {
+ const objects = await listNginxDirectory(path);
+ const fileUrls = objects.filter(url => !url.endsWith('/'));
+ const dirUrls = objects.filter(url => url.endsWith('/'));
+ const deepFileUrls = await Bluebird.map(dirUrls, deepListNginxDirectory);
+ return _.flattenDeep([fileUrls, deepFileUrls]);
+}
+
+const memoizedDeepListNginxDirectory = mem(deepListNginxDirectory, { maxAge: 60000 });
+
+
+// An adapter to fetch markdown & images from Nginx server with enabled directory view
+const nginxAdapter: Adapter = {
+ async getStaticMarkdownPaths(cdn) {
+ const urls = await memoizedDeepListNginxDirectory(cdn);
+ const markdownPaths = _.compact(urls.map(globalPath => globalPath.match(`${cdn}/(.*)\.md`)?.[1]));
+
+ return markdownPaths
+ .map(path => path.split('/'))
+ .map(path => ({ params: { path } }));
+ },
+
+ async getMarkdownSource(cdn, path) {
+ const { data: markdownSource } = await axios(`${cdn}/${path?.join('/')}.md`);
+ return markdownSource;
+ },
+
+ async getEmojiFileNames(cdn) {
+ const urls = await memoizedDeepListNginxDirectory(cdn);
+ return _.compact(urls.map((s: string) => s.match(/emoji\/(.*)/)?.[1]));
+ },
+}
+
+export default nginxAdapter;
diff --git a/src/lib/types.ts b/src/lib/types.ts
new file mode 100644
index 0000000..8bbf5bb
--- /dev/null
+++ b/src/lib/types.ts
@@ -0,0 +1,15 @@
+import { GetStaticPathsResult } from 'next';
+
+/* Collection of methods to fetch data & metadata from CDN */
+export interface Adapter {
+ getStaticMarkdownPaths: (cdn: string) => Promise<GetStaticPathsResult['paths']>;
+ getMarkdownSource: (cdn: string, path: string[]) => Promise<string>;
+ getEmojiFileNames: (cdn: string) => Promise<string[]>;
+}
+
+export interface BenzinConfig {
+ CDN: string;
+ adapter: Adapter;
+}
+
+
diff --git a/src/pages/[...path].tsx b/src/pages/[...path].tsx
index ea67d17..a49286d 100644
--- a/src/pages/[...path].tsx
+++ b/src/pages/[...path].tsx
@@ -4,23 +4,30 @@ import ReactMarkdown from 'react-markdown';
import Head from 'next/head';
import Emoji from '../Emoji';
import Image from '../Image';
-import deepReadDir from '../deepReadDir';
import emojiPlugin from '../emojiPlugin';
-import fs from 'fs';
import remarkGemoji from 'remark-gemoji';
+import benzinConfig from '../benzinConfig';
-
-const MARKDOWN_DIR = '../eug-vs-xyz/src';
-const EMOJI_DIR = 'public/emoji';
-
-const transformLinkURI = (uri: string): string => {
+const transformLinkUri = (uri: string): string => {
return uri.match(/(.*)\.md/)?.[1] || uri;
}
+const transformImageUri = (uri: string): string => {
+ return uri.startsWith('http') ? uri : benzinConfig.CDN + uri;
+}
+
+export const config = {
+ unstable_runtimeJS: false,
+};
+
export const getStaticProps = async (context: GetStaticPropsContext) => {
- const path = _.isArray(context.params?.path) && context.params?.path || [context.params?.path];
- const markdownSource = fs.readFileSync(`${MARKDOWN_DIR}/${path?.join('/')}.md`).toString();
- const emojiFileNames = fs.readdirSync(EMOJI_DIR);
+ const path = _.compact(_.isArray(context.params?.path)
+ ? context.params?.path
+ : [context.params?.path]
+ );
+
+ const markdownSource = await benzinConfig.adapter.getMarkdownSource(benzinConfig.CDN, path);
+ const emojiFileNames = await benzinConfig.adapter.getEmojiFileNames(benzinConfig.CDN);
return {
props: {
@@ -32,12 +39,8 @@ export const getStaticProps = async (context: GetStaticPropsContext) => {
}
export const getStaticPaths = async () => {
- const globalPaths = await deepReadDir(MARKDOWN_DIR);
- const paths = globalPaths
- .map(globalPath => globalPath.match(`${MARKDOWN_DIR}/(.*)\.md`)?.[1] )
- .filter(p => p)
- .map(p => p?.split('/'))
- .map(path => ({ params: { path } }));
+ const paths = await benzinConfig.adapter.getStaticMarkdownPaths(benzinConfig.CDN);
+
return {
paths,
fallback: 'blocking',
@@ -54,7 +57,8 @@ const Page: NextPage = ({ markdownSource, emojiFileNames }: any) => {
</Head>
<main>
<ReactMarkdown
- transformLinkUri={transformLinkURI}
+ transformLinkUri={transformLinkUri}
+ transformImageUri={transformImageUri}
rehypePlugins={[emojiPlugin(emojiFileNames), remarkGemoji]}
components={{
emoji: Emoji,
@@ -74,4 +78,3 @@ const Page: NextPage = ({ markdownSource, emojiFileNames }: any) => {
};
export default Page;
-
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index ba9372f..18692df 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -7,7 +7,7 @@ function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<a href="/" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textDecoration: 'none', marginBottom: '12px' }}>
- <Image src={logo} width={128} height={128} />
+ <Image src={logo} width={128} height={128} alt="logo" />
<h1>{"Eugene's Space"}</h1>
</a>
<Component {...pageProps} />