Skip to content

Commit e8441ef

Browse files
committed
[refactor] make GitHub & Lark API clearer & safer with Koa 2 & Next-SSR-middleware 0.10
[optimize] upgrade to PNPM 10, MobX Lark 2.2 & other latest Upstream packages [optimize] reduce Dependency conflicts with indenpendent Configuration module [optimize] use TS for ESLint configuration [fix] Sentry 9 configuration
1 parent 9d4b58a commit e8441ef

23 files changed

+3266
-1964
lines changed

components/Client/Partner.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Tooltip } from '@mui/material';
22
import { FC, ReactNode } from 'react';
33

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

88
export interface PartnerProps extends Client {
@@ -41,7 +41,7 @@ export const LogoWithLink: FC<Omit<PartnerOverviewProps, 'tooltip'>> = ({
4141
address,
4242
logo,
4343
logoDark,
44-
className
44+
className,
4545
}) => (
4646
<a
4747
key={name}

components/Icon.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { FC, HTMLAttributes, PropsWithRef } from 'react';
1+
import { FC, HTMLAttributes } from 'react';
22

3-
export type IconProps = PropsWithRef<
4-
HTMLAttributes<HTMLSpanElement> & {
5-
name: string;
6-
variant?: 'outlined' | 'rounded' | 'sharp';
7-
}
8-
>;
3+
export interface IconProps extends HTMLAttributes<HTMLSpanElement> {
4+
name: string;
5+
variant?: 'outlined' | 'rounded' | 'sharp';
6+
}
97

108
export const SymbolIcon: FC<IconProps> = ({ className, name, variant = 'outlined', ...props }) => (
119
<span

components/LarkImage.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { TableCellValue } from 'mobx-lark';
22
import { ImageProps } from 'next/image';
33
import { FC } from 'react';
44

5-
import { DefaultImage, fileURLOf } from '../pages/api/Lark/file/[id]';
5+
import { fileURLOf } from '../models/Base';
6+
import { DefaultImage } from '../models/configuration';
67

78
export interface LarkImageProps extends Omit<ImageProps, 'src'> {
89
src?: TableCellValue;

components/PageHead.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import Head from 'next/head';
22
import type { FC, PropsWithChildren } from 'react';
33

4+
import { Name, Summary } from '../models/configuration';
5+
46
export type PageHeadProps = PropsWithChildren<{
57
title?: string;
68
description?: string;
79
}>;
810

9-
const Name = process.env.NEXT_PUBLIC_SITE_NAME,
10-
Summary = process.env.NEXT_PUBLIC_SITE_SUMMARY;
11-
1211
export const PageHead: FC<PageHeadProps> = ({ title, description = Summary, children }) => (
1312
<Head>
1413
<title>{(title ? `${title} - ` : '') + Name}</title>
File renamed without changes.

instrumentation.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const { NEXT_RUNTIME } = process.env;
2+
3+
export async function register() {
4+
if (NEXT_RUNTIME === 'nodejs') await import('./sentry.server.config');
5+
6+
if (NEXT_RUNTIME === 'edge') await import('./sentry.edge.config');
7+
}

models/Base.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import { HTTPClient } from 'koajax';
2+
import MIME from 'mime';
3+
import { TableCellValue, TableCellMedia, TableCellAttachment } from 'mobx-lark';
24

3-
export const isServer = () => typeof window === 'undefined';
4-
5-
export const VercelHost = process.env.VERCEL_URL,
6-
LarkBaseId = process.env.NEXT_PUBLIC_LARK_BASE!;
7-
8-
export const API_Host = isServer()
9-
? VercelHost
10-
? `https://${VercelHost}`
11-
: 'http://localhost:3000'
12-
: globalThis.location.origin;
5+
import { API_Host } from './configuration';
136

147
export const larkClient = new HTTPClient({
158
baseURI: `${API_Host}/api/Lark/`,
169
responseType: 'json',
1710
});
11+
12+
export function fileURLOf(field: TableCellValue, cache = false) {
13+
if (!(field instanceof Array) || !field[0]) return field + '';
14+
15+
const file = field[0] as TableCellMedia | TableCellAttachment;
16+
17+
let URI = `/api/Lark/file/${'file_token' in file ? file.file_token : file.attachmentToken}`;
18+
19+
if (cache) URI += '.' + MIME.getExtension('type' in file ? file.type : file.mimeType);
20+
21+
return URI;
22+
}

models/Client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BiDataQueryOptions, BiDataTable, TableCellValue } from 'mobx-lark';
22

3-
import { LarkBaseId, larkClient } from './Base';
3+
import { larkClient } from './Base';
4+
import { LarkBaseId } from './configuration';
45

56
export type Client = Record<
67
'id' | 'name' | 'type' | 'partnership' | 'image' | 'summary' | 'address',

models/Member.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BiDataQueryOptions, BiDataTable, TableCellValue } from 'mobx-lark';
22

3-
import { LarkBaseId, larkClient } from './Base';
3+
import { larkClient } from './Base';
4+
import { LarkBaseId } from './configuration';
45

56
export type Member = Record<
67
'id' | 'nickname' | 'type' | 'skill' | 'position' | 'summary' | 'github' | 'joinedAt',

models/Project.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import {
55
normalizeText,
66
TableCellLink,
77
TableCellValue,
8-
TableRecord
8+
TableRecord,
99
} from 'mobx-lark';
1010
import { NewData } from 'mobx-restful';
1111
import { isEmpty } from 'web-utility';
1212

13-
import { LarkBaseId, larkClient } from './Base';
13+
import { larkClient } from './Base';
14+
import { LarkBaseId } from './configuration';
1415

1516
export type Project = Record<
1617
| 'id'
@@ -45,17 +46,17 @@ export class ProjectModel extends BiDataTable<Project>() {
4546
makeFilter(filter: NewData<Project>) {
4647
return [
4748
'NOT(CurrentValue.[settlementDate]="")',
48-
isEmpty(filter) ? undefined : makeSimpleFilter(filter)
49+
isEmpty(filter) ? undefined : makeSimpleFilter(filter),
4950
]
5051
.filter(Boolean)
5152
.join('&&');
5253
}
5354

54-
normalize({ id, fields: { link, ...fields } }: TableRecord<Project>) {
55+
extractFields({ id, fields: { link, ...fields } }: TableRecord<Project>) {
5556
return {
5657
...fields,
5758
id,
58-
link: link && normalizeText(link as TableCellLink)
59+
link: link && normalizeText(link as TableCellLink),
5960
};
6061
}
6162
}

models/Repository.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
import { observable } from 'mobx';
22
import { githubClient, GitRepository, RepositoryFilter, RepositoryModel } from 'mobx-github';
3-
import { parseCookie } from 'mobx-i18n';
43
import { Stream } from 'mobx-restful';
54

6-
import { API_Host, isServer } from './Base';
7-
8-
const GithubToken =
9-
parseCookie(globalThis.document?.cookie || '').token || process.env.GITHUB_TOKEN;
5+
import { API_Host, GithubToken, isServer } from './configuration';
106

117
if (!isServer()) githubClient.baseURI = `${API_Host}/api/GitHub/`;
128

139
githubClient.use(({ request }, next) => {
1410
if (GithubToken)
1511
request.headers = {
1612
authorization: `Bearer ${GithubToken}`,
17-
...request.headers
13+
...request.headers,
1814
};
1915
return next();
2016
});
@@ -42,7 +38,7 @@ export class GitRepositoryModel extends Stream<GitRepository, RepositoryFilter>(
4238
const { pageData, totalCount } = await loadPage.call(this, i, this.pageSize, filter);
4339
const list = pageData.filter(
4440
({ description, topics, fork, archived }) =>
45-
description?.trim() && topics?.[0] && !fork && !archived
41+
description?.trim() && topics?.[0] && !fork && !archived,
4642
);
4743
const droppedCount = pageData.length - list.length;
4844

models/configuration.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { parseCookie } from 'mobx-i18n';
2+
3+
export const Name = process.env.NEXT_PUBLIC_SITE_NAME,
4+
Summary = process.env.NEXT_PUBLIC_SITE_SUMMARY,
5+
DefaultImage = process.env.NEXT_PUBLIC_LOGO || '/og.png';
6+
7+
export const isServer = () => typeof window === 'undefined';
8+
9+
export const VercelHost = process.env.VERCEL_URL;
10+
11+
export const API_Host = isServer()
12+
? VercelHost
13+
? `https://${VercelHost}`
14+
: 'http://localhost:3000'
15+
: globalThis.location.origin;
16+
17+
export const CACHE_HOST = process.env.NEXT_PUBLIC_CACHE_HOST!;
18+
19+
export const { CRAWLER_TOKEN, JWT_SECRET } = process.env;
20+
21+
export const GithubToken =
22+
parseCookie(globalThis.document?.cookie || '').token || process.env.GITHUB_TOKEN;
23+
24+
export const LARK_API_HOST = `${API_Host}/api/Lark/`;
25+
26+
export const LarkAppMeta = {
27+
id: process.env.LARK_APP_ID!,
28+
secret: process.env.LARK_APP_SECRET!,
29+
};
30+
export const LarkBaseId = process.env.NEXT_PUBLIC_LARK_BASE!;

package.json

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,62 +10,78 @@
1010
"@emotion/react": "^11.14.0",
1111
"@emotion/styled": "^11.14.0",
1212
"@giscus/react": "^3.1.0",
13+
"@koa/bodyparser": "^5.1.1",
14+
"@koa/router": "^13.1.0",
1315
"@mui/lab": "6.0.0-beta.31",
14-
"@mui/material": "^6.4.8",
15-
"@sentry/nextjs": "^9.6.0",
16-
"file-type": "^20.4.1",
17-
"koajax": "^3.1.1",
16+
"@mui/material": "^6.4.11",
17+
"@sentry/nextjs": "^9.15.0",
18+
"file-type": "^20.5.0",
19+
"jsonwebtoken": "^9.0.2",
20+
"koa": "^2.16.1",
21+
"koa-jwt": "^4.0.4",
22+
"koajax": "^3.1.2",
1823
"lodash": "^4.17.21",
19-
"marked": "^15.0.7",
20-
"mime": "^4.0.6",
24+
"marked": "^15.0.11",
25+
"mime": "^4.0.7",
2126
"mobx": "^6.13.7",
2227
"mobx-github": "^0.3.5",
2328
"mobx-i18n": "^0.6.0",
24-
"mobx-lark": "^2.1.0",
29+
"mobx-lark": "^2.2.0",
2530
"mobx-react": "^9.2.0",
2631
"mobx-restful": "^2.1.0",
27-
"next": "^15.2.3",
32+
"next": "^15.3.1",
2833
"next-pwa": "~5.6.0",
29-
"next-ssr-middleware": "^0.8.10",
30-
"react": "^19.0.0",
31-
"react-dom": "^19.0.0",
34+
"next-ssr-middleware": "^0.10.0",
35+
"react": "^19.1.0",
36+
"react-dom": "^19.1.0",
3237
"web-utility": "^4.4.3",
33-
"webpack": "^5.98.0"
38+
"webpack": "^5.99.7"
3439
},
3540
"devDependencies": {
36-
"@babel/plugin-proposal-decorators": "^7.25.9",
37-
"@babel/plugin-transform-typescript": "^7.26.8",
38-
"@babel/preset-react": "^7.26.3",
39-
"@cspell/eslint-plugin": "^8.17.5",
40-
"@eslint/compat": "^1.2.7",
41-
"@eslint/js": "^9.22.0",
42-
"@next/eslint-plugin-next": "^15.2.3",
41+
"@babel/plugin-proposal-decorators": "^7.27.1",
42+
"@babel/plugin-transform-typescript": "^7.27.1",
43+
"@babel/preset-react": "^7.27.1",
44+
"@cspell/eslint-plugin": "^8.19.4",
45+
"@eslint/compat": "^1.2.9",
46+
"@eslint/js": "^9.26.0",
47+
"@next/eslint-plugin-next": "^15.3.1",
4348
"@stylistic/eslint-plugin": "^4.2.0",
44-
"@tailwindcss/postcss": "^4.0.14",
49+
"@tailwindcss/postcss": "^4.1.5",
4550
"@types/eslint-config-prettier": "^6.11.3",
51+
"@types/jsonwebtoken": "^9.0.9",
52+
"@types/koa": "^2.15.0",
53+
"@types/koa__router": "^12.0.4",
4654
"@types/lodash": "^4.17.16",
4755
"@types/next-pwa": "^5.6.9",
48-
"@types/node": "^22.13.10",
49-
"@types/react": "^19.0.11",
50-
"eslint": "^9.22.0",
51-
"eslint-config-next": "^15.2.3",
52-
"eslint-config-prettier": "^10.1.1",
53-
"eslint-plugin-react": "^7.37.4",
56+
"@types/node": "^22.15.3",
57+
"@types/react": "^19.1.2",
58+
"eslint": "^9.26.0",
59+
"eslint-config-next": "^15.3.1",
60+
"eslint-config-prettier": "^10.1.2",
61+
"eslint-plugin-react": "^7.37.5",
5462
"eslint-plugin-simple-import-sort": "^12.1.1",
5563
"globals": "^16.0.0",
5664
"husky": "^9.1.7",
57-
"lint-staged": "^15.5.0",
65+
"jiti": "^2.4.2",
66+
"lint-staged": "^15.5.1",
5867
"postcss": "^8.5.3",
5968
"prettier": "^3.5.3",
6069
"prettier-plugin-css-order": "^2.1.2",
6170
"prettier-plugin-tailwindcss": "^0.6.11",
62-
"tailwindcss": "^4.0.14",
63-
"typescript": "~5.8.2",
64-
"typescript-eslint": "^8.26.1"
71+
"tailwindcss": "^4.1.5",
72+
"typescript": "~5.8.3",
73+
"typescript-eslint": "^8.31.1"
6574
},
6675
"resolutions": {
6776
"next": "$next"
6877
},
78+
"pnpm": {
79+
"onlyBuiltDependencies": [
80+
"@sentry/cli",
81+
"sharp",
82+
"unrs-resolver"
83+
]
84+
},
6985
"prettier": {
7086
"singleQuote": true,
7187
"trailingComma": "all",

pages/_app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Head from 'next/head';
99

1010
import { Footer } from '../components/Layout/Footer';
1111
import { MainNavigator } from '../components/Layout/MainNavigator';
12-
import { isServer } from '../models/Base';
12+
import { isServer } from '../models/configuration';
1313

1414
configure({ enforceActions: 'never' });
1515

pages/_document.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
22
import { Head, Html, Main, NextScript } from 'next/document';
33
import Script from 'next/script';
44

5-
import { DefaultImage } from './api/Lark/file/[id]';
5+
import { DefaultImage } from '../models/configuration';
66

77
/**
88
* Influence Google Search to display search results with the name "idea2app" instead of idea2.app

pages/api/GitHub/[...slug].ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1-
import { proxyGithub } from './core';
1+
import { createKoaRouter } from 'next-ssr-middleware';
22

3-
export default proxyGithub();
3+
import { withSafeKoaRouter } from '../core';
4+
import { proxyGitHubAll } from './core';
5+
6+
export const config = { api: { bodyParser: false } };
7+
8+
const router = createKoaRouter(import.meta.url);
9+
10+
router.get('/(.*)', proxyGitHubAll);
11+
12+
export default withSafeKoaRouter(router);

0 commit comments

Comments
 (0)