Skip to content

Commit

Permalink
✨ feat: add reusable gateway function
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Aug 22, 2023
1 parent 6f6e67c commit eba16ab
Show file tree
Hide file tree
Showing 11 changed files with 373 additions and 161 deletions.
5 changes: 5 additions & 0 deletions .fatherrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineConfig } from 'father';

export default defineConfig({
cjs: { output: 'dist' },
});
32 changes: 32 additions & 0 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Dependabot Auto Merge
on:
pull_request_target:
types: [labeled, edited]

jobs:
merge:
if: contains(github.event.pull_request.labels.*.name, 'dependencies')
name: Dependabot Auto Merge
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 7

- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: '18'

- name: Install deps
run: pnpm install

- name: Merge
uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
command: merge
target: minor
github-token: ${{ secrets.GH_TOKEN }}
29 changes: 29 additions & 0 deletions .github/workflows/contributor-help.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Contributor Helper

on:
# 🌏 Think about the planet! No need to update stats too frequently
schedule: [{ cron: '0 18 * * *' }]
# 💡 The following line lets you run workflow manually from the action tab!
workflow_dispatch:
jobs:
contributor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master

- uses: actions-cool/contributor-helper@v1
with:
token: ${{ secrets.GH_TOKEN }}
style: 'simple'
update-files: 'README.md'
update-places: '<!-- CONTRIBUTION GROUP -->/<!-- CONTRIBUTION END -->'

- name: Commit and push if changed
run: |-
git diff
git config --global user.email "[email protected]"
git config --global user.name "github-actions"
git pull
git add -A
git commit -m "🤖 docs: Auto update contributors" || exit 0
git push
22 changes: 22 additions & 0 deletions .github/workflows/issue-check-inactive.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Issue Check Inactive

on:
schedule:
- cron: '0 0 */15 * *'

permissions:
contents: read

jobs:
issue-check-inactive:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: check-inactive
uses: actions-cool/issues-helper@v3
with:
actions: 'check-inactive'
inactive-label: 'Inactive'
inactive-day: 30
46 changes: 46 additions & 0 deletions .github/workflows/issue-close-require.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Issue Close Require

on:
schedule:
- cron: '0 0 * * *'

permissions:
contents: read

jobs:
issue-close-require:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: '✅ Fixed'
inactive-day: 3
body: |
Since the issue was labeled with `✅ Fixed`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
由于该 issue 被标记为已修复,同时 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: '🤔 Need Reproduce'
inactive-day: 3
body: |
Since the issue was labeled with `🤔 Need Reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
由于该 issue 被标记为需要更多信息,却 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: "🙅🏻‍♀️ WON'T DO"
inactive-day: 3
body: |
Since the issue was labeled with `🙅🏻‍♀️ WON'T DO`, and no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
由于该 issue 被标记为暂不处理,同时 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
25 changes: 25 additions & 0 deletions .github/workflows/issue-remove-inactive.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Issue Remove Inactive

on:
issues:
types: [edited]
issue_comment:
types: [created, edited]

permissions:
contents: read

jobs:
issue-remove-inactive:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: remove inactive
if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login
uses: actions-cool/issues-helper@v3
with:
actions: 'remove-labels'
issue-number: ${{ github.event.issue.number }}
labels: 'Inactive'
36 changes: 32 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ on:
push:
branches:
- master
- alpha
- beta
- rc

jobs:
release:
name: Release
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand All @@ -18,15 +22,39 @@ jobs:
- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: '20'
node-version: '18'

- name: Install deps
run: pnpm install

- name: lint
run: pnpm run lint && pnpm run doctor

- name: Test
run: pnpm run test

release:
needs: test
name: Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8

- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: '18'

- name: Install deps
run: pnpm install

- name: release
run: pnpm run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
156 changes: 2 additions & 154 deletions api/v1/runner.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
// reason to use cfworker json schema:
// https://github.com/vercel/next.js/discussions/47063#discussioncomment-5303951
import { Validator } from '@cfworker/json-schema';
import {
ErrorType,
LobeChatPlugin,
LobeChatPluginsMarketIndex,
PluginRequestPayload,
createErrorResponse,
marketIndexSchema,
pluginManifestSchema,
pluginMetaSchema,
pluginRequestPayloadSchema,
} from '@lobehub/chat-plugin-sdk';
import { createLobeChatPluginGateway } from '../../src';

export const config = {
runtime: 'edge',
Expand All @@ -21,143 +8,4 @@ const DEFAULT_INDEX_URL =
process.env.PLUGINS_INDEX_URL ??
'https://registry.npmmirror.com/@lobehub/lobe-chat-plugins/latest/files';

export default async (req: Request) => {
// ========== 1. 校验请求方法 ========== //
if (req.method !== 'POST')
return createErrorResponse(ErrorType.MethodNotAllowed, {
message: '[gateway] only allow POST method',
});

// ========== 2. 校验请求入参基础格式 ========== //
const requestPayload = (await req.json()) as PluginRequestPayload;

const payloadParseResult = pluginRequestPayloadSchema.safeParse(requestPayload);

if (!payloadParseResult.success)
return createErrorResponse(ErrorType.BadRequest, payloadParseResult.error);

const { name, arguments: args, indexUrl } = requestPayload;

console.info(`plugin call: ${name}`);

const marketIndexUrl = indexUrl ?? DEFAULT_INDEX_URL;
// ========== 3. 获取插件市场索引 ========== //

let marketIndex: LobeChatPluginsMarketIndex | undefined;
try {
const indexRes = await fetch(marketIndexUrl);
marketIndex = await indexRes.json();
} catch (error) {
console.error(error);
marketIndex = undefined;
}

// 插件市场索引不存在
if (!marketIndex)
return createErrorResponse(ErrorType.PluginMarketIndexNotFound, {
indexUrl,
message: '[gateway] plugin market index not found',
});

// 插件市场索引解析失败
const indexParseResult = marketIndexSchema.safeParse(marketIndex);

if (!indexParseResult.success)
return createErrorResponse(ErrorType.PluginMarketIndexInvalid, {
error: indexParseResult.error,
indexUrl,
marketIndex,
message: '[gateway] plugin market index is invalid',
});

console.info('marketIndex:', marketIndex);

// ========== 4. 校验插件 meta 完备性 ========== //

const pluginMeta = marketIndex.plugins.find((i) => i.name === name);

// 一个不规范的插件示例
// const pluginMeta = {
// createAt: '2023-08-12',
// homepage: 'https://github.com/lobehub/chat-plugin-real-time-weather',
// manifest: 'https://registry.npmmirror.com/@lobehub/lobe-chat-plugins/latest/files',
// meta: {
// avatar: '☂️',
// tags: ['weather', 'realtime'],
// },
// name: 'realtimeWeather',
// schemaVersion: 'v1',
// };

// 校验插件是否存在
if (!pluginMeta)
return createErrorResponse(ErrorType.PluginMetaNotFound, {
message: `[gateway] plugin '${name}' is not found,please check the plugin list in ${indexUrl}, or create an issue to [lobe-chat-plugins](https://github.com/lobehub/lobe-chat-plugins/issues)`,
name,
});

const metaParseResult = pluginMetaSchema.safeParse(pluginMeta);

if (!metaParseResult.success)
return createErrorResponse(ErrorType.PluginMetaInvalid, {
error: metaParseResult.error,
message: '[plugin] plugin meta is invalid',
pluginMeta,
});

// ========== 5. 校验插件 manifest 完备性 ========== //

// 获取插件的 manifest
let manifest: LobeChatPlugin | undefined;
try {
const pluginRes = await fetch(pluginMeta.manifest);
manifest = (await pluginRes.json()) as LobeChatPlugin;
} catch (error) {
console.error(error);
manifest = undefined;
}

if (!manifest)
return createErrorResponse(ErrorType.PluginManifestNotFound, {
manifestUrl: pluginMeta.manifest,
message: '[plugin] plugin manifest not found',
});

const manifestParseResult = pluginManifestSchema.safeParse(manifest);

if (!manifestParseResult.success)
return createErrorResponse(ErrorType.PluginManifestInvalid, {
error: manifestParseResult.error,
manifest: manifest,
message: '[plugin] plugin manifest is invalid',
});

console.log(`[${name}] plugin manifest:`, manifest);

// ========== 6. 校验请求入参与 manifest 要求一致性 ========== //

if (args) {
const v = new Validator(manifest.schema.parameters as any);
const validator = v.validate(JSON.parse(args!));

if (!validator.valid)
return createErrorResponse(ErrorType.BadRequest, {
error: validator.errors,
manifest,
message: '[plugin] args is invalid with plugin manifest schema',
});
}

// ========== 7. 发送请求 ========== //

const response = await fetch(manifest.server.url, { body: args, method: 'post' });

// 不正常的错误,直接返回请求
if (!response.ok) return response;

const data = await response.text();

console.log(`[${name}]`, args, `result:`, data.slice(0, 1000));

return new Response(data);
};
export default createLobeChatPluginGateway(DEFAULT_INDEX_URL);
Loading

0 comments on commit eba16ab

Please sign in to comment.