Skip to content

Commit 1202922

Browse files
authored
Merge pull request #41 from g0ngjie/feature/function-redirect
Feature/function redirect
2 parents 5bddf85 + 3a8469a commit 1202922

File tree

14 files changed

+359
-165
lines changed

14 files changed

+359
-165
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "ajax-proxy",
33
"private": true,
44
"description": "Modify your Ajax response to test",
5-
"version": "2.2.6",
5+
"version": "2.2.7",
66
"scripts": {
77
"dev": "pnpm -C ./packages/vue-panels serve",
88
"watch": "run-p watch:lib watch:chrome",

packages/code-editor/examples/App.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
ref="codeEditor"
66
v-model="codeText"
77
@change="handleChange"
8+
type="redirector"
89
/>
910
<button type="button" @click="reset">reset</button>
1011
</div>

packages/code-editor/packages/index.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export default {
1414
type: String,
1515
default: "",
1616
},
17+
type: {
18+
type: String,
19+
default: "interceptor",
20+
},
1721
},
1822
watch: {
1923
value: {
@@ -35,7 +39,7 @@ export default {
3539
3640
methods: {
3741
initEditor() {
38-
this.aceEditor = useInit(this.$refs.ace);
42+
this.aceEditor = useInit(this.$refs.ace, this.type);
3943
4044
this.aceEditor.getSession().on("change", () => {
4145
this.$emit("change", this.aceEditor.getSession().getValue());
@@ -49,7 +53,7 @@ export default {
4953
},
5054
setValue(value) {
5155
if (this.aceEditor) {
52-
if (!value) this.aceEditor.setValue(getDefaultContent());
56+
if (!value) this.aceEditor.setValue(getDefaultContent(this.type));
5357
else this.aceEditor.setValue(value);
5458
}
5559
},

packages/code-editor/packages/useEditor.js

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import "ace-builds/src-noconflict/snippets/javascript"; //代码提示
88

99
// https://ace.c9.io/#nav=howto
1010
// 初始化
11-
export function useInit(container) {
11+
export function useInit(container, type) {
1212
// 初始化
1313
const target = ace.edit(container, {
1414
maxLines: 20, // 最大行数,超过会自动出现滚动条
@@ -25,46 +25,75 @@ export function useInit(container) {
2525
});
2626

2727
// 自定义提示
28-
customCompletions(target)
28+
customCompletions(target, type)
2929

3030
return target;
3131
}
3232

3333
// 自定义提示
34-
function customCompletions(target) {
35-
target.completers.push({
36-
getCompletions: function (state, session, pos, prefix, callback) {
37-
if (prefix.length === 0) {
38-
callback(null, []);
39-
return;
40-
}
41-
callback(null, [
42-
{ meta: 'AjaxProxy::Ctx.req', caption: 'req.url: string', value: 'req.url', score: 100 },
43-
{ meta: 'AjaxProxy::Ctx.req', caption: 'req.method: string', value: 'req.method', score: 100 },
44-
{ meta: 'AjaxProxy::Ctx.req', caption: 'req.body?: any', value: 'req.body', score: 100 },
45-
{ meta: 'AjaxProxy::Ctx.res', caption: 'res.status: string', value: 'res.status', score: 100 },
46-
{ meta: 'AjaxProxy::Ctx.res', caption: 'res.customStatus: string', value: 'res.customStatus', score: 100 },
47-
{ meta: 'AjaxProxy::Ctx.res', caption: 'res.response: any', value: 'res.response', score: 100 },
48-
{
49-
meta: 'AjaxProxy::Next',
50-
caption: 'next({ override?: string, status?: string | number })',
51-
value: 'next({ override: "", status: "" });',
52-
score: 100
53-
},
54-
]);
55-
},
56-
});
34+
function customCompletions(target, type = 'interceptor') {
35+
if (type === 'interceptor')
36+
target.completers.push({
37+
getCompletions: function (state, session, pos, prefix, callback) {
38+
if (prefix.length === 0) {
39+
callback(null, []);
40+
return;
41+
}
42+
callback(null, [
43+
{ meta: 'AjaxProxy::Ctx.req', caption: 'req.url: string', value: 'req.url', score: 100 },
44+
{ meta: 'AjaxProxy::Ctx.req', caption: 'req.method: string', value: 'req.method', score: 100 },
45+
{ meta: 'AjaxProxy::Ctx.req', caption: 'req.body?: any', value: 'req.body', score: 100 },
46+
{ meta: 'AjaxProxy::Ctx.res', caption: 'res.status: string', value: 'res.status', score: 100 },
47+
{ meta: 'AjaxProxy::Ctx.res', caption: 'res.customStatus: string', value: 'res.customStatus', score: 100 },
48+
{ meta: 'AjaxProxy::Ctx.res', caption: 'res.response: any', value: 'res.response', score: 100 },
49+
{
50+
meta: 'AjaxProxy::Next',
51+
caption: 'next({ override?: string, status?: string | number })',
52+
value: 'next({ override: "", status: "" });',
53+
score: 100
54+
},
55+
]);
56+
},
57+
});
58+
else
59+
target.completers.push({
60+
getCompletions: function (state, session, pos, prefix, callback) {
61+
if (prefix.length === 0) {
62+
callback(null, []);
63+
return;
64+
}
65+
callback(null, [
66+
{ meta: 'AjaxProxy::Ctx.req', caption: 'req.url: string', value: 'req.url', score: 100 },
67+
{ meta: 'AjaxProxy::Ctx.req', caption: 'req.method: string', value: 'req.method', score: 100 },
68+
{
69+
meta: 'AjaxProxy::Next',
70+
caption: 'next({ url: string, headers?: { [key: string]: string } })',
71+
value: 'next({ url: req.url });',
72+
score: 100
73+
},
74+
]);
75+
},
76+
});
5777
}
5878

5979
// 默认内容
60-
export function getDefaultContent() {
61-
const defaultContent =
80+
export function getDefaultContent(type = "interceptor") {
81+
let defaultContent = type === 'interceptor' ?
6282
`
6383
function setup(req, res, next) {
6484
// TODO...
6585
// type Next = { override?: string, status?: string | number }
6686
next({ override: "", status: "" });
6787
}
88+
` :
89+
`
90+
function setup(
91+
req, /** req: { url: string, method: string }*/
92+
next /**{ url: string, headers?: { [key: string]: string } }*/
93+
) {
94+
// TODO...
95+
next({ url: "" });
96+
}
6897
`
6998
return defaultContent
7099
}

packages/proxy-lib/src/redirectFetch.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { finalRedirectUrl, matchIgnoresAndRule } from "./common";
2+
import { execSetup } from "./redirectUrlFunc";
23
import { RefGlobalState } from "./types";
34

45
// 共享状态
@@ -7,7 +8,7 @@ const OriginFetch = window.fetch.bind(window)
78
// 初始化共享状态
89
export const initRedirectFetchState = (state: RefGlobalState) => globalState = state
910

10-
export default function CustomFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
11+
export default async function CustomFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
1112
let fetchMethod: string | undefined | "ANY" = "ANY"
1213
let customInit: RequestInit = init || {}
1314
if (init) {
@@ -23,11 +24,39 @@ export default function CustomFetch(input: RequestInfo | URL, init?: RequestInit
2324
redirect_url = "",
2425
headers = [],
2526
ignores = [],
27+
redirect_type = "text",
28+
redirect_func = ""
2629
} = globalState.value.redirector_matching_content[i];
2730
if (switch_on) {
2831
// 判断是否存在协议匹配
2932
if (method && ![fetchMethod, "ANY"].includes(method.toUpperCase())) break
30-
if (matchIgnoresAndRule(input.toString(), domain, filter_type, ignores)) {
33+
if (redirect_type === "function") {
34+
const payload = await execSetup({ url: input.toString(), method: fetchMethod }, redirect_func)
35+
input = payload.url
36+
37+
if (init?.headers) {
38+
// 初始化 RequestInit
39+
customInit = init
40+
const initHeaders = new Headers(init.headers)
41+
if (payload.headers) {
42+
for (const key in payload.headers) {
43+
if (Object.prototype.hasOwnProperty.call(payload.headers, key)) {
44+
const value = payload.headers[key];
45+
if (key && value) initHeaders.set(key, value)
46+
}
47+
}
48+
}
49+
customInit.headers = initHeaders
50+
} else {
51+
const newHeaders: HeadersInit = payload.headers ? Object.keys(payload.headers).map(key => [key, payload.headers![key]]) : []
52+
customInit = {
53+
headers: newHeaders
54+
}
55+
}
56+
57+
// 值取当前命中的第一个,后续再命中的忽略
58+
break;
59+
} else if (matchIgnoresAndRule(input.toString(), domain, filter_type, ignores)) {
3160
input = finalRedirectUrl(input.toString(), domain, redirect_url, filter_type)
3261

3362
if (init?.headers) {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { IRedirectHeader } from './types';
2+
3+
4+
const errLog = function (...args: any[]) {
5+
console.log("%c[AjaxProxy][error]: ", "color: #ff4d4f", ...args)
6+
}
7+
8+
type Req = {
9+
url: string
10+
method: string
11+
}
12+
13+
type Next = {
14+
url: string,
15+
headers?: { [key: IRedirectHeader['key']]: IRedirectHeader['value'] }
16+
}
17+
18+
function isNext(x: any): x is Next {
19+
if (!!x) return true
20+
if (x.url && !x.headers) return true
21+
return x.headers && Object.prototype.toString.call(x.headers) == '[object Object]'
22+
}
23+
24+
export function execSetup(req: Req, funcText: string): Promise<Next> {
25+
return new Promise(resolve => {
26+
try {
27+
const source = ';(' + funcText + ')'
28+
const execFunc = window.eval(source)
29+
const type = typeof execFunc
30+
if (type === "function") {
31+
if (!funcText.includes('next(')) {
32+
errLog("The structure of 'next' is incorrect [code 1]")
33+
// original
34+
return resolve({ url: req.url })
35+
}
36+
execFunc(req, (next: Next) => {
37+
// custom
38+
if (isNext(next)) {
39+
return resolve({ url: next.url, headers: next.headers })
40+
}
41+
})
42+
return resolve({ url: req.url })
43+
}
44+
errLog("Please enter a correct 'function' [code 2]")
45+
return resolve({ url: req.url })
46+
} catch (error) {
47+
errLog(error)
48+
return resolve({ url: req.url })
49+
}
50+
})
51+
}

packages/proxy-lib/src/redirectXHR.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { finalRedirectUrl, fmtURLToString, matchIgnoresAndRule } from "./common";
2+
import { execSetup } from "./redirectUrlFunc";
23
import { RefGlobalState } from "./types";
34

45
// 状态
@@ -21,7 +22,7 @@ export default class CustomRedirectXHR extends XMLHttpRequest {
2122
const origin_XHR_open = open
2223
const origin_XHR_setRequestHeader = this.setRequestHeader
2324
const origin_XHR_send = this.send
24-
this.open = (method: string,
25+
this.open = async (method: string,
2526
url: string | URL,
2627
async?: boolean,
2728
username?: string | null,
@@ -37,13 +38,31 @@ export default class CustomRedirectXHR extends XMLHttpRequest {
3738
redirect_url = "",
3839
headers = [],
3940
ignores = [],
41+
redirect_type = "text",
42+
redirect_func = ""
4043
} = globalState.value.redirector_matching_content[i];
4144
if (switch_on) {
4245
// 判断是否存在协议匹配
4346
if (method && ![this.method, "ANY"].includes(method.toUpperCase())) return
4447
// 规则判断
4548
const currentUrl = fmtURLToString(url)
46-
if (matchIgnoresAndRule(currentUrl, domain, filter_type, ignores)) {
49+
if (redirect_type === "function") {
50+
const payload = await execSetup({ url: currentUrl, method: this.method }, redirect_func)
51+
url = payload.url
52+
this.send = (body?: Document | XMLHttpRequestBodyInit | null) => {
53+
if (payload.headers) {
54+
for (const key in payload.headers) {
55+
if (Object.prototype.hasOwnProperty.call(payload.headers, key)) {
56+
const value = payload.headers[key];
57+
if (key && value)
58+
this.setRequestHeader(key, value)
59+
}
60+
}
61+
}
62+
origin_XHR_send.call(this, body)
63+
}
64+
break;
65+
} else if (matchIgnoresAndRule(currentUrl, domain, filter_type, ignores)) {
4766
url = finalRedirectUrl(currentUrl, domain, redirect_url, filter_type)
4867

4968
// 获取 自定义 header 映射关系
@@ -59,6 +78,7 @@ export default class CustomRedirectXHR extends XMLHttpRequest {
5978
for (let k = 0; k < headers.length; k++) {
6079
const header = headers[k];
6180
// 移除掉 映射关系
81+
// Reflect.deleteProperty(cacheHeaderMap, header.key)
6282
delete cacheHeaderMap[header.key]
6383
this.setRequestHeader(header.key, header.value)
6484
}

packages/proxy-lib/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export type IMode = "interceptor" | "redirector"
88
export type RefGlobalState<T = IGlobalState> = { value: T }
99
/**响应式类型 */
1010
export type OverrideType = 'json' | 'function'
11+
/**重定向类型 */
12+
export type RedirectType = 'text' | 'function'
1113

1214
type CommonContent = {
1315
/**是否需要匹配 */
@@ -53,6 +55,10 @@ export type IMatchRedirectContent = {
5355
headers?: IRedirectHeader[]
5456
/**忽略名单 */
5557
ignores?: string[]
58+
/**重定向类型 */
59+
redirect_type?: RedirectType
60+
/**函数响应 */
61+
redirect_func?: string
5662
} & CommonContent
5763

5864
export type IGlobalState = {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { IRedirectHeader } from './types';
2+
declare type Req = {
3+
url: string;
4+
method: string;
5+
};
6+
declare type Next = {
7+
url: string;
8+
headers?: {
9+
[key: IRedirectHeader['key']]: IRedirectHeader['value'];
10+
};
11+
};
12+
export declare function execSetup(req: Req, funcText: string): Promise<Next>;
13+
export {};

packages/proxy-lib/types/types.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export declare type RefGlobalState<T = IGlobalState> = {
1010
};
1111
/**响应式类型 */
1212
export declare type OverrideType = 'json' | 'function';
13+
/**重定向类型 */
14+
export declare type RedirectType = 'text' | 'function';
1315
declare type CommonContent = {
1416
/**是否需要匹配 */
1517
switch_on: boolean;
@@ -51,6 +53,10 @@ export declare type IMatchRedirectContent = {
5153
headers?: IRedirectHeader[];
5254
/**忽略名单 */
5355
ignores?: string[];
56+
/**重定向类型 */
57+
redirect_type?: RedirectType;
58+
/**函数响应 */
59+
redirect_func?: string;
5460
} & CommonContent;
5561
export declare type IGlobalState = {
5662
/**全局开关 */

0 commit comments

Comments
 (0)