Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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={`h-100 shadow-sm ${styles.issueCard} ${className}`}>
Comment thread
TechQuery marked this conversation as resolved.
Outdated
<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