Skip to content

Commit

Permalink
feat(wolai): wolai flow
Browse files Browse the repository at this point in the history
  • Loading branch information
LetTTGACO committed May 19, 2024
1 parent ae846d5 commit adf194c
Show file tree
Hide file tree
Showing 9 changed files with 761 additions and 0 deletions.
32 changes: 32 additions & 0 deletions playground/plugin-from-wolai/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@elogx-test/plugin-from-wolai",
"version": "1.0.1",
"description": "",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"build": "tsup"
},
"author": "",
"license": "MIT",
"peerDependencies": {
"@elogx-test/elog": "^1.0.0"
},
"peerDependenciesMeta": {
"@elogx-test/elog": {
"optional": true
}
},
"devDependencies": {
"tsup": "^6.7.0",
"typescript": "~5.2.2",
"@types/node": "~18.15.3",
"@elogx-test/elog": "workspace:*"
}
}
270 changes: 270 additions & 0 deletions playground/plugin-from-wolai/src/WolaiApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
import {
WoLaiCatalogConfig,
WoLaiConfig,
WoLaiDoc,
WolaiFilterAndSortParams,
WoLaiTablePage,
WoLaiTableRows,
} from './types';
import {
DocDetail,
DocProperties,
DocStructure,
ElogBaseContext,
PluginContext,
} from '@elogx-test/elog';
import { WolaiSortPresetEnum, WolaiSortDirectionEnum } from './const';
import { filterDocs, genCatalog, props, sortDocs } from './utils';
import buffer from 'buffer';

export default class WolaiApi extends ElogBaseContext {
config: WoLaiConfig;
constructor(config: WoLaiConfig, ctx: PluginContext) {
super(ctx);
this.config = config;
this.config.baseUrl = config.baseUrl || 'https://api.wolai.com/v1';
if (!config.token || !config.pageId) {
this.ctx.error('缺少WoLai配置信息');
}
this.initCatalogConfig();
}

/**
* 初始化目录配置
*/
private initCatalogConfig() {
if (typeof this.config.catalog === 'boolean') {
if (!this.config.catalog) {
// 不启用目录
this.config.catalog = { enable: false };
} else {
// 启用目录
this.ctx.success('开启分类', '默认按照 catalog 字段分类,请检查FlowUs多维表是否存在该属性');
this.config.catalog = { enable: true, property: 'catalog' };
}
} else if (typeof this.config.catalog === 'object') {
if (this.config.catalog.enable) {
// 检查分类字段是否存在
if (!this.config.catalog.property) {
this.config.catalog.property = 'catalog';
this.ctx.warn(
'未设置分类字段,默认按照 catalog 字段分类,请检查FlowUs多维表是否存在该属性',
);
}
}
}
}

/**
* 初始化过滤和排序参数
*/
private initFilterAndSortParamsParams(): WolaiFilterAndSortParams {
let sort = this.config.sort as WolaiFilterAndSortParams['sort'];
if (typeof this.config.sort === 'boolean') {
if (!this.config.sort) {
// 不排序
sort = undefined;
} else {
// 默认排序
sort = { property: 'createdAt', direction: WolaiSortDirectionEnum.descending };
}
} else if (typeof this.config.sort === 'string') {
// 预设值
const sortPreset = this.config.sort as WolaiSortPresetEnum;
switch (sortPreset) {
case WolaiSortPresetEnum.dateDesc:
sort = { property: 'date', direction: WolaiSortDirectionEnum.descending };
break;
case WolaiSortPresetEnum.dateAsc:
sort = { property: 'date', direction: WolaiSortDirectionEnum.ascending };
break;
case WolaiSortPresetEnum.sortDesc:
sort = { property: 'sort', direction: WolaiSortDirectionEnum.descending };
break;
case WolaiSortPresetEnum.sortAsc:
sort = { property: 'sort', direction: WolaiSortDirectionEnum.ascending };
break;
case WolaiSortPresetEnum.createTimeDesc:
sort = {
property: 'createdAt',
direction: WolaiSortDirectionEnum.descending,
};
break;
case WolaiSortPresetEnum.createTimeAsc:
sort = {
property: 'createdAt',
direction: WolaiSortDirectionEnum.ascending,
};
break;
case WolaiSortPresetEnum.updateTimeDesc:
sort = {
property: 'updatedAt',
direction: WolaiSortDirectionEnum.descending,
};
break;
case WolaiSortPresetEnum.updateTimeAsc:
sort = {
property: 'updatedAt',
direction: WolaiSortDirectionEnum.ascending,
};
break;
default:
sort = {
property: 'createdAt',
direction: WolaiSortDirectionEnum.descending,
};
}
}

let filter = this.config.filter as WolaiFilterAndSortParams['filter'];
// 如果是boolean类型
if (typeof this.config.filter === 'boolean') {
// 如果设置为false
if (!this.config.filter) {
filter = undefined;
} else {
// 如果设置为true
filter = {
property: 'status',
value: '已发布',
};
}
}
return {
filter,
sort,
};
}

/**
* send api request to yuque
* @param api
* @param reqOpts
* @param custom
*/
private async requestInternal<T>(api: string, reqOpts: any, custom?: boolean): Promise<T> {
const url = `${this.config.baseUrl}/${api}`;
// 将 token 设置到请求 cookie 中
const cookie = `token=${this.config.token}`;
const opts: any = {
headers: {
cookie,
},
...reqOpts,
};
if (custom) {
const res = await this.ctx.request<T>(url, opts);
return res.data;
}
const res = await this.ctx.request<any>(url, opts);
// TODO 校验数据库是否公开网络
// if (res.status !== 200) {
// if (res.status === 404 && res.data?.message === 'book not found') {
// this.ctx.info('请参考配置文档:https://elog.1874.cool/notion/write-platform');
// this.ctx.error('知识库不存在,请检查配置');
// } else {
// this.ctx.error(res.data?.message || res);
// }
// }
return res.data.data;
}

async getSortedDocList() {
// 获取表格信息
const tablePage = await this.requestInternal<WoLaiTablePage>('pages/getPageChunks', {
method: 'post',
data: {
pageId: this.config.pageId,
limit: 100,
position: {
stack: [],
},
chunkNumber: 0,
},
});
const databaseId = tablePage.block[this.config.pageId].value.database_id;
// 获取表格文档列表
const list = await this.requestInternal<WoLaiTableRows>('database/tableViewRows', {
method: 'post',
data: {
table_id: databaseId,
// TODO "view_id": "",
limit: 1000,
value: 'all',
offset: 0,
disableGroup: false,
filters: {
logical: 'and',
filters: [],
},
sorters: [],
group: false,
search: '',
snapshot: null,
timezoneOffset: -480,
},
});
// 转换 props
const tableFields = tablePage.database_tables[databaseId].properties;
let docs = list.rows.map((row) => {
const properties = props(row, tableFields);
return {
...row,
createdAt: row.created_time,
updatedAt: row.edited_time,
properties,
id: row.block_id,
} as WoLaiDoc;
});
const { filter, sort } = this.initFilterAndSortParamsParams();
// 过滤
docs = filterDocs(docs, filter);
// 排序
docs = sortDocs(docs, sort);
return docs;
}

/**
* 获取文档详情
* @param row
*/
async getDocDetail(row: WoLaiDoc): Promise<DocDetail> {
const url = await this.requestInternal<string>('exportMarkdown', {
method: 'post',
data: {
pageId: row.block_id,
pageTitle: row.properties.title,
options: {
recoverTree: false,
generateToc: 'none',
includeSubPage: false,
},
},
});
// 从 url 下载buffer
const res = await this.requestInternal<Buffer>(
url,
{ method: 'get', dataType: 'buffer' },
true,
);
// Buffer 转字符串
const body = buffer.Buffer.from(res).toString('utf-8');
const doc: DocDetail = {
id: row.block_id,
properties: row.properties as DocProperties,
body,
updateTime: row.edited_time,
title: row.properties.title,
};
let catalog: DocStructure[] | undefined = [];
const catalogConfig = this.config.catalog as WoLaiCatalogConfig;
if (catalogConfig?.enable) {
// 生成目录
catalog = genCatalog(doc, catalogConfig.property || 'catalog');
}
return {
...doc,
docStructure: catalog,
};
}
}
47 changes: 47 additions & 0 deletions playground/plugin-from-wolai/src/WolaiClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ElogFromContext, PluginContext } from '@elogx-test/elog';
import WolaiApi from './WolaiApi';
import { WoLaiConfig, WoLaiDoc } from './types';

export default class WolaiClient extends ElogFromContext {
config: WoLaiConfig;
api: WolaiApi;

constructor(config: WoLaiConfig, ctx: PluginContext) {
super(ctx, config);
this.config = config;
this.api = new WolaiApi(config, ctx);
}

/**
* 获取文章列表
*/
async getDocDetailList() {
this.ctx.info('正在获取文档列表,请稍等...');
// 获取已排序的文档
const sortedDocList = await this.api.getSortedDocList();
const { docList: needUpdateDocList, idMap } = this.filterDocs(sortedDocList, 'id', 'updatedAt');
// 没有则不需要更新
if (!needUpdateDocList.length) {
this.ctx.success('任务结束', '没有需要同步的文档');
process.exit();
}
this.ctx.info('待下载数', String(needUpdateDocList.length));

const promise = async (doc: WoLaiDoc) => {
this.ctx.info(`下载文档 ${doc._index}/${needUpdateDocList.length} `, doc.properties.title);
return this.api.getDocDetail(doc);
};
const docDetailList = await this.asyncPool(this.config.limit || 3, needUpdateDocList, promise);
// 更新缓存
this.updateCache(docDetailList, idMap);
this.ctx.info('已下载数', String(needUpdateDocList.length));
// 写入缓存
this.writeCache({
sortedDocList: sortedDocList.map((item) => ({
id: item.id,
title: item.properties.title,
})),
});
return docDetailList;
}
}
21 changes: 21 additions & 0 deletions playground/plugin-from-wolai/src/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export enum WolaiSortPresetEnum {
/** 按自定义日期排序 */
dateDesc = 'dateDesc',
dateAsc = 'dateAsc',
/** 按创建时间排序 */
createTimeDesc = 'createTimeDesc',
createTimeAsc = 'createTimeAsc',
/** 按更新时间排序 */
updateTimeDesc = 'updateTimeDesc',
updateTimeAsc = 'updateTimeAsc',
/** 按sort字段排序 */
sortDesc = 'sortDesc',
sortAsc = 'sortAsc',
}

export enum WolaiSortDirectionEnum {
/** 降序 */
descending = 'descending',
/** 升序 */
ascending = 'ascending',
}
13 changes: 13 additions & 0 deletions playground/plugin-from-wolai/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { IPlugin } from '@elogx-test/elog';
import type { FlowUsConfig } from './types';
import WolaiClient from './WolaiClient';

export default function yuque(options: Partial<FlowUsConfig>): IPlugin {
return {
name: 'from-flowus',
async down(this) {
const notion = new WolaiClient(options as FlowUsConfig, this);
return notion.getDocDetailList();
},
};
}
Loading

0 comments on commit adf194c

Please sign in to comment.