Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI & CD
on:
push:
branches:
- '*'
- '**'
jobs:
Build-and-Deploy:
env:
Expand Down
21 changes: 11 additions & 10 deletions components/Git/ArticleEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { readAs } from 'koajax';
import { debounce } from 'lodash';
import { marked } from 'marked';
import { computed, observable } from 'mobx';
import { GitContent } from 'mobx-github';
import { Content, ContentModel } from 'mobx-github';
import { observer } from 'mobx-react';
import { ObservedComponent } from 'mobx-react-helper';
import { DataObject } from 'mobx-restful';
import { SearchableInput } from 'mobx-restful-table';
import { ChangeEvent, FormEvent } from 'react';
import { Button, Col, Form } from 'react-bootstrap';
import { blobOf, formatDate, uniqueID } from 'web-utility';
import { blobOf, encodeBase64, formatDate, uniqueID } from 'web-utility';
import YAML from 'yaml';

import { GitFileSearchModel } from '../../models/GitFile';
Expand Down Expand Up @@ -67,7 +67,7 @@ export class ArticleEditor extends ObservedComponent<{}, typeof i18n> {
@observable
accessor meta: PostMeta | null = null;

static contentFilter({ type, name }: GitContent) {
static contentFilter({ type, name }: Content) {
return (
type === 'dir' ||
(type === 'file' && Object.values(fileType).flat().includes(name.split('.').slice(-1)[0]))
Expand Down Expand Up @@ -181,30 +181,31 @@ export class ArticleEditor extends ObservedComponent<{}, typeof i18n> {

if (!editorContent) return;

const root = document.querySelector('div[contenteditable]');
const contentStore = new ContentModel(currentRepository.owner, currentRepository.name),
root = document.querySelector('div[contenteditable]');
const media: HTMLMediaElement[] = [].filter.call(
root!.querySelectorAll('img[src], audio[src], video[src]'),
({ src }) => new URL(src).protocol === 'blob:',
);

for (const file of media) {
const blob = await blobOf(file.src);
const [, content] = ((await readAs(blob, 'dataURL').result) as string).split(',');

const filePath = this.path.replace(/\.\w+$/, `/${uniqueID()}.${blob.type.split('/')[1]}`);
const { download_url } = await repositoryStore.updateContent(

const { download_url } = await contentStore.updateOne(
{ content },
filePath,
blob,
'[Upload] from Git-Pager',
currentRepository.name,
);
file.src = download_url!;
}

await repositoryStore.updateContent(
await contentStore.updateOne(
{ content: encodeBase64(this.getContent() || '') },
this.path,
this.getContent() as string,
message.value.trim(),
currentRepository.name,
);
window.alert('Submitted');
};
Expand Down
21 changes: 21 additions & 0 deletions components/Git/IssueCard.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.issueCard {
transition:
transform 0.2s ease,
box-shadow 0.2s ease;

&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
}
}
.issueTitle {
transition: color 0.2s ease;

&:hover {
color: #028dfa !important;
}
}
.authorInfo {
color: #586069;
font-size: 14px;
}
54 changes: 54 additions & 0 deletions components/Git/IssueCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Issue } from 'mobx-github';
import { observer } from 'mobx-react';
import { FC, useContext } from 'react';
import { Badge, Card, CardProps } from 'react-bootstrap';

import { I18nContext } from '../../models/Translation';
import styles from './IssueCard.module.less';
import { LabelBar } from './LabelBar';

export type IssueCardProps = Omit<Issue, 'id'> & Omit<CardProps, 'body'>;

export const IssueCard: FC<IssueCardProps> = observer(
({ className = '', number, title, labels, body, user, created_at, state }) => {
const { t } = useContext(I18nContext);

return (
<Card className={`shadow-sm ${styles.issueCard} ${className}`}>
<Card.Header className="d-flex justify-content-between align-items-center">
<Badge bg={state === 'open' ? 'success' : 'secondary'}>
{state === 'open' ? t('open') : t('closed')}
</Badge>
<small className="text-muted">#{number}</small>
</Card.Header>

<Card.Body className="d-flex flex-column gap-3">
<Card.Title className="h5">
<a
href={`/weekly/${number}`}
className={`text-decoration-none text-dark ${styles.issueTitle}`}
>
{title}
</a>
</Card.Title>

{body && (
<Card.Text className="text-muted flex-grow-1">
{body.slice(0, 150)}
{body.length > 150 && '...'}
</Card.Text>
)}
{labels?.[0] && <LabelBar labels={labels} />}
</Card.Body>

<Card.Footer
className={`d-flex justify-content-between align-items-center small text-muted ${styles.authorInfo}`}
>
<span>{user && `${t('weekly_author')}: ${user.login}`}</span>

<time dateTime={created_at}>{new Date(created_at).toLocaleDateString('zh-CN')}</time>
</Card.Footer>
</Card>
);
},
);
22 changes: 22 additions & 0 deletions components/Git/LabelBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Issue } from 'mobx-github';
import { FC } from 'react';

export type LabelBarProps = Pick<Issue, 'labels'>;

export const LabelBar: FC<LabelBarProps> = ({ labels }) => (
<ul className="list-unstyled d-flex flex-wrap gap-2">
{labels.map((label, index) => {
const { name, color } = typeof label === 'object' ? label : {};

return (
<li
key={index}
className="p-2 rounded small"
style={{ backgroundColor: color || 'lightgray' }}
>
{typeof label === 'string' ? label : name}
</li>
);
})}
</ul>
);
2 changes: 2 additions & 0 deletions components/Navigator/MainNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const MainNavigator: FC = observer(() => {

<Nav.Link href="/activity">{t('activity')}</Nav.Link>

<Nav.Link href="/weekly">{t('weekly')}</Nav.Link>

<Nav.Link href="/community">{t('community')}</Nav.Link>

<Nav.Link href="/article/Wiki/_posts/Profile/about">{t('about')}</Nav.Link>
Expand Down
72 changes: 35 additions & 37 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,78 +7,76 @@
"node": ">=22"
},
"dependencies": {
"@koa/router": "^13.1.1",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next/mdx": "^15.4.1",
"@sentry/nextjs": "^9.39.0",
"copy-webpack-plugin": "^13.0.0",
"core-js": "^3.44.0",
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "^15.5.2",
"@sentry/nextjs": "^10.10.0",
"copy-webpack-plugin": "^13.0.1",
"core-js": "^3.45.1",
"file-type": "^21.0.0",
"idea-react": "^2.0.0-rc.13",
"koa": "^2.16.1",
"koa": "^3.0.1",
"koajax": "^3.1.2",
"less": "^4.3.0",
"less": "^4.4.1",
"less-loader": "^12.3.0",
"lodash": "^4.17.21",
"marked": "^16.0.0",
"marked": "^16.2.1",
"mime": "^4.0.7",
"mobx": "^6.13.7",
"mobx-github": "^0.3.11",
"mobx-github": "^0.5.1",
"mobx-i18n": "^0.7.1",
"mobx-lark": "^2.2.0",
"mobx-lark": "^2.4.1",
"mobx-react": "^9.2.0",
"mobx-react-helper": "^0.5.1",
"mobx-restful": "^2.1.0",
"mobx-restful-table": "^2.5.2",
"next": "^15.4.1",
"mobx-restful-table": "^2.5.3",
"next": "^15.5.2",
"next-pwa": "~5.6.0",
"next-ssr-middleware": "^1.0.1",
"next-ssr-middleware": "^1.0.3",
"next-with-less": "^3.0.1",
"react": "^19.1.0",
"react": "^19.1.1",
"react-bootstrap": "^2.10.10",
"react-bootstrap-editor": "^2.1.1",
"react-dom": "^19.1.0",
"react-dom": "^19.1.1",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1",
"remark-mdx-frontmatter": "^5.2.0",
"undici": "^7.11.0",
"web-utility": "^4.4.3",
"webpack": "^5.100.2",
"yaml": "^2.8.0"
"undici": "^7.15.0",
"web-utility": "^4.5.3",
"webpack": "^5.101.3",
"yaml": "^2.8.1"
},
"devDependencies": {
"@babel/plugin-proposal-decorators": "^7.28.0",
"@babel/plugin-transform-typescript": "^7.28.0",
"@babel/preset-react": "^7.27.1",
"@cspell/eslint-plugin": "^9.1.5",
"@eslint/compat": "^1.3.1",
"@cspell/eslint-plugin": "^9.2.1",
"@eslint/compat": "^1.3.2",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.31.0",
"@next/eslint-plugin-next": "^15.4.1",
"@eslint/js": "^9.35.0",
"@next/eslint-plugin-next": "^15.5.2",
"@octokit/openapi-types": "^25.1.0",
"@softonus/prettier-plugin-duplicate-remover": "^1.1.2",
"@stylistic/eslint-plugin": "^5.1.0",
"@stylistic/eslint-plugin": "^5.3.1",
"@types/eslint-config-prettier": "^6.11.3",
"@types/koa": "^2.15.0",
"@types/koa__router": "^12.0.4",
"@types/koa": "^3.0.0",
"@types/lodash": "^4.17.20",
"@types/next-pwa": "^5.6.9",
"@types/node": "^22.16.4",
"@types/react": "^19.1.8",
"eslint": "^9.31.0",
"eslint-config-next": "^15.4.1",
"eslint-config-prettier": "^10.1.5",
"@types/node": "^22.18.1",
"@types/react": "^19.1.12",
"eslint": "^9.35.0",
"eslint-config-next": "^15.5.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-simple-import-sort": "^12.1.1",
"globals": "^16.3.0",
"husky": "^9.1.7",
"jiti": "^2.4.2",
"lint-staged": "^16.1.2",
"jiti": "^2.5.1",
"lint-staged": "^16.1.6",
"prettier": "^3.6.2",
"prettier-plugin-css-order": "^2.1.2",
"typescript": "~5.8.3",
"typescript-eslint": "^8.37.0"
"typescript": "~5.9.2",
"typescript-eslint": "^8.42.0"
},
"resolutions": {
"next": "$next"
Expand Down
22 changes: 10 additions & 12 deletions pages/api/Lark/bitable/v1/[...slug].ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { Context } from 'koa';
import { LarkPageData, TableRecord, TableRecordData } from 'mobx-lark';
import { DataObject } from 'mobx-restful';
import { createKoaRouter } from 'next-ssr-middleware';
import { createKoaRouter, withKoaRouter } from 'next-ssr-middleware';

import { withSafeKoaRouter } from '../../../core';
import { safeAPI } from '../../../core';
import { proxyLark, proxyLarkAll } from '../../core';

export const config = { api: { bodyParser: false } };

const router = createKoaRouter(import.meta.url);

function filterData(fields: DataObject) {
for (const key of Object.keys(fields))
if (!/^\w+$/.test(key)) delete fields[key];
for (const key of Object.keys(fields)) if (!/^\w+$/.test(key)) delete fields[key];
}

router.get('/apps/:app/tables/:table/records/:record', async context => {
const { status, body } =
await proxyLark<TableRecordData<DataObject>>(context);
router.get('/apps/:app/tables/:table/records/:record', safeAPI, async (context: Context) => {
const { status, body } = await proxyLark<TableRecordData<DataObject>>(context);

const { fields } = body!.data!.record;

Expand All @@ -26,9 +25,8 @@ router.get('/apps/:app/tables/:table/records/:record', async context => {
context.body = body;
});

router.get('/apps/:app/tables/:table/records', async context => {
const { status, body } =
await proxyLark<LarkPageData<TableRecord<DataObject>>>(context);
router.get('/apps/:app/tables/:table/records', safeAPI, async (context: Context) => {
const { status, body } = await proxyLark<LarkPageData<TableRecord<DataObject>>>(context);

const list = body!.data!.items || [];

Expand All @@ -38,6 +36,6 @@ router.get('/apps/:app/tables/:table/records', async context => {
context.body = body;
});

router.all('/(.*)', proxyLarkAll);
router.all('/(.*)', safeAPI, proxyLarkAll);

export default withSafeKoaRouter(router);
export default withKoaRouter(router);
20 changes: 11 additions & 9 deletions pages/api/Lark/file/[id]/[name].ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { fileTypeFromStream } from 'file-type';
import { Middleware } from 'koa';
import MIME from 'mime';
import { createKoaRouter } from 'next-ssr-middleware';
import { createKoaRouter, withKoaRouter } from 'next-ssr-middleware';
import { Readable } from 'stream';

import { CACHE_HOST } from '../../../../../models/configuration';
import { withSafeKoaRouter } from '../../../core';
import { safeAPI } from '../../../core';
import { lark } from '../../core';

const router = createKoaRouter(import.meta.url);

router.all('/:id/:name', async context => {
const downloader: Middleware = async context => {
const { method, url, params, query } = context;
const { id, name } = params;

Expand All @@ -21,10 +22,9 @@ router.all('/:id/:name', async context => {

const token = await lark.getAccessToken();

const response = await fetch(
lark.client.baseURI + `drive/v1/medias/${id}/download`,
{ headers: { Authorization: `Bearer ${token}` } },
);
const response = await fetch(lark.client.baseURI + `drive/v1/medias/${id}/download`, {
headers: { Authorization: `Bearer ${token}` },
});
const { ok, status, headers, body } = response;

if (!ok) {
Expand All @@ -47,6 +47,8 @@ router.all('/:id/:name', async context => {
if (method === 'GET')
// @ts-expect-error Web type compatibility
context.body = Readable.fromWeb(stream2);
});
};

export default withSafeKoaRouter(router);
router.head('/:id/:name', safeAPI, downloader).get('/:id/:name', safeAPI, downloader);

export default withKoaRouter(router);
Loading