Skip to content

Commit 5aa9782

Browse files
authored
[add] URL crawler API & Lark Image component (#69)
1 parent bda23b6 commit 5aa9782

File tree

9 files changed

+121
-24
lines changed

9 files changed

+121
-24
lines changed

.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
NEXT_PUBLIC_SITE_NAME = idea2app
22
NEXT_PUBLIC_SITE_SUMMARY = 全行业信息化转型专家
3+
NEXT_PUBLIC_LOGO = https://github.com/idea2app.png
4+
5+
NEXT_PUBLIC_CACHE_HOST = https://cache.idea2.app
36

47
NEXT_PUBLIC_SENTRY_DSN = https://03e5d951172f411a04c1bab44022e22b@o4506471366852608.ingest.sentry.io/4506484563705856
58
SENTRY_ORG = idea2app

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ You can check out [the Next.js GitHub repository][27] - your feedback and contri
6868

6969
| name | file | description |
7070
| :----------------------: | :----------: | :---------------------: |
71+
| `CRAWLER_TOKEN` | `.env.local` | Web hooks authorization |
7172
| `SENTRY_AUTH_TOKEN` | `.env.local` | [Official document][28] |
7273
| `SENTRY_ORG` | `.env` | [Official document][29] |
7374
| `SENTRY_PROJECT` | `.env` | [Official document][29] |

components/Base/LarkImage.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { TableCellValue } from 'mobx-lark';
2+
import { FC } from 'react';
3+
import { Image, ImageProps } from 'react-bootstrap';
4+
5+
import { DefaultImage, fileURLOf } from '../../pages/api/Lark/file/[id]';
6+
7+
export interface LarkImageProps extends Omit<ImageProps, 'src'> {
8+
src?: TableCellValue;
9+
}
10+
11+
export const LarkImage: FC<LarkImageProps> = ({
12+
src = DefaultImage,
13+
alt,
14+
...props
15+
}) => (
16+
<Image
17+
fluid
18+
loading="lazy"
19+
{...props}
20+
src={fileURLOf(src, true)}
21+
alt={alt}
22+
onError={({ currentTarget: image }) => {
23+
const path = fileURLOf(src),
24+
errorURL = decodeURI(image.src);
25+
26+
if (!path) return;
27+
28+
if (errorURL.endsWith(path)) {
29+
if (!alt) image.src = DefaultImage;
30+
} else if (!errorURL.endsWith(DefaultImage)) {
31+
image.src = path;
32+
}
33+
}}
34+
/>
35+
);

components/Client/Partner.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { FC } from 'react';
2-
import { Image } from 'react-bootstrap';
32

43
import { Client } from '../../models/Client';
5-
import { fileURLOf } from '../../pages/api/Lark/file/[id]';
4+
import { LarkImage } from '../Base/LarkImage';
65

76
export interface PartnerProps extends Client {
87
className?: string;
@@ -18,18 +17,18 @@ export const Partner: FC<PartnerProps> = ({
1817
<div
1918
className={`d-flex flex-column align-items-center justify-content-center gap-3 position-relative ${className}`}
2019
>
21-
<Image
20+
<LarkImage
2221
fluid
2322
className="object-fit-contain"
2423
style={{ height: '5rem' }}
2524
loading="lazy"
26-
src={fileURLOf(image)}
25+
src={image}
2726
/>
2827
<h3>
2928
<a
3029
className="stretched-link"
3130
target="_blank"
32-
href={address + ''}
31+
href={address?.toString()}
3332
rel="noreferrer"
3433
>
3534
{name + ''}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"koajax": "^3.1.1",
1313
"less": "^4.2.1",
1414
"less-loader": "^12.2.0",
15+
"mime": "^4.0.4",
1516
"mobx": "^6.13.5",
1617
"mobx-github": "^0.3.5",
1718
"mobx-i18n": "^0.6.0",

pages/api/Lark/file/[id].ts

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
11
import { fileTypeFromBuffer } from 'file-type';
2+
import MIME from 'mime';
23
import { TableCellMedia, TableCellValue } from 'mobx-lark';
4+
import { parse } from 'path';
35

46
import { safeAPI } from '../../core';
57
import { lark } from '../core';
68

7-
export const fileURLOf = (field: TableCellValue) =>
8-
field instanceof Array
9-
? field[0]
10-
? `/api/Lark/file/${(field[0] as TableCellMedia).file_token}`
11-
: field + ''
12-
: field + '';
9+
export const DefaultImage = process.env.NEXT_PUBLIC_LOGO!;
1310

14-
export default safeAPI(async (req, res) => {
15-
switch (req.method) {
11+
export function fileURLOf(field: TableCellValue, cache = false) {
12+
if (!(field instanceof Array) || !field[0]) return field + '';
13+
14+
const { file_token, type } = field[0] as TableCellMedia;
15+
16+
let URI = `/api/Lark/file/${file_token}`;
17+
18+
if (cache) URI += '.' + MIME.getExtension(type);
19+
20+
return URI;
21+
}
22+
23+
export const CACHE_HOST = process.env.NEXT_PUBLIC_CACHE_HOST!;
24+
25+
export default safeAPI(async ({ method, url, query, headers }, res) => {
26+
const { ext } = parse(url!);
27+
28+
if (ext)
29+
return void res.redirect(
30+
new URL(new URL(url!, `http://${headers.host}`).pathname, CACHE_HOST) +
31+
'',
32+
);
33+
switch (method) {
34+
case 'HEAD':
1635
case 'GET': {
17-
const { id } = req.query;
36+
const { id } = query;
1837

1938
await lark.getAccessToken();
2039

2140
const file = await lark.downloadFile(id as string);
2241

23-
const buffer = Buffer.alloc(file.byteLength),
24-
view = new Uint8Array(file);
25-
26-
for (let i = 0; i < buffer.length; i++) buffer[i] = view[i];
27-
28-
const { mime } = (await fileTypeFromBuffer(buffer)) || {};
42+
const { mime } = (await fileTypeFromBuffer(file)) || {};
2943

3044
res.setHeader('Content-Type', mime as string);
31-
res.send(buffer);
45+
46+
return void (method === 'GET' ? res.send(Buffer.from(file)) : res.end());
3247
}
3348
}
49+
res.status(405).end();
3450
});

pages/api/crawler/task.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { githubClient } from 'mobx-github';
2+
3+
import { safeAPI } from '../core';
4+
5+
export interface CrawlerTask {
6+
URI: string;
7+
title?: string;
8+
}
9+
10+
const CRAWLER_TOKEN = process.env.CRAWLER_TOKEN;
11+
12+
export default safeAPI(async ({ method, headers, body }, response) => {
13+
if (
14+
!CRAWLER_TOKEN ||
15+
CRAWLER_TOKEN !== headers.authorization?.split(/\s+/)[1]
16+
)
17+
return void response.status(401).end();
18+
19+
if (method !== 'POST') return void response.status(405).end();
20+
21+
const { URI, title = URI } = body as CrawlerTask;
22+
23+
const { status, body: data } = await githubClient.post(
24+
'repos/idea2app/OWS-cache/issues',
25+
{
26+
title,
27+
body: `### URL\n\n${URI}`,
28+
labels: ['crawler'],
29+
},
30+
);
31+
response.status(status).send(data);
32+
});

pages/project/[id].tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import { GitRepository } from 'mobx-github';
22
import { observer } from 'mobx-react';
33
import { compose, errorLogger, translator } from 'next-ssr-middleware';
44
import { FC } from 'react';
5-
import { Col, Container, Image, Row } from 'react-bootstrap';
5+
import { Col, Container, Row } from 'react-bootstrap';
66

7+
import { LarkImage } from '../../components/Base/LarkImage';
78
import { GitCard } from '../../components/Git/Card';
89
import { PageHead } from '../../components/PageHead';
910
import { ProjectCard } from '../../components/Project/Card';
1011
import { Project, ProjectModel } from '../../models/Project';
1112
import { GitRepositoryModel } from '../../models/Repository';
1213
import { i18n, t } from '../../models/Translation';
1314
import { solidCache } from '../api/core';
14-
import { fileURLOf } from '../api/Lark/file/[id]';
1515

1616
interface ProjectDetailPageProps {
1717
project: Project;
@@ -58,7 +58,7 @@ const ProjectDetailPage: FC<ProjectDetailPageProps> = observer(
5858
className="text-decoration-none"
5959
href={project.link || '#'}
6060
>
61-
<Image fluid src={fileURLOf(project.image)} alt={project.name + ''} />
61+
<LarkImage fluid src={project.image} alt={project.name + ''} />
6262
</Col>
6363
<Col xs={12} sm={4}>
6464
<ProjectCard {...project} />

pnpm-lock.yaml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)