diff --git a/package.json b/package.json
index 6daa5df..82e28f0 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "ajax-proxy",
"private": true,
"description": "Modify your Ajax response to test",
- "version": "2.2.6",
+ "version": "2.2.7",
"scripts": {
"dev": "pnpm -C ./packages/vue-panels serve",
"watch": "run-p watch:lib watch:chrome",
diff --git a/packages/code-editor/examples/App.vue b/packages/code-editor/examples/App.vue
index 10a8fe2..0171234 100644
--- a/packages/code-editor/examples/App.vue
+++ b/packages/code-editor/examples/App.vue
@@ -5,6 +5,7 @@
ref="codeEditor"
v-model="codeText"
@change="handleChange"
+ type="redirector"
/>
diff --git a/packages/code-editor/packages/index.vue b/packages/code-editor/packages/index.vue
index 3193e6c..3764f3e 100644
--- a/packages/code-editor/packages/index.vue
+++ b/packages/code-editor/packages/index.vue
@@ -14,6 +14,10 @@ export default {
type: String,
default: "",
},
+ type: {
+ type: String,
+ default: "interceptor",
+ },
},
watch: {
value: {
@@ -35,7 +39,7 @@ export default {
methods: {
initEditor() {
- this.aceEditor = useInit(this.$refs.ace);
+ this.aceEditor = useInit(this.$refs.ace, this.type);
this.aceEditor.getSession().on("change", () => {
this.$emit("change", this.aceEditor.getSession().getValue());
@@ -49,7 +53,7 @@ export default {
},
setValue(value) {
if (this.aceEditor) {
- if (!value) this.aceEditor.setValue(getDefaultContent());
+ if (!value) this.aceEditor.setValue(getDefaultContent(this.type));
else this.aceEditor.setValue(value);
}
},
diff --git a/packages/code-editor/packages/useEditor.js b/packages/code-editor/packages/useEditor.js
index 77eee1c..b931c4b 100644
--- a/packages/code-editor/packages/useEditor.js
+++ b/packages/code-editor/packages/useEditor.js
@@ -8,7 +8,7 @@ import "ace-builds/src-noconflict/snippets/javascript"; //代码提示
// https://ace.c9.io/#nav=howto
// 初始化
-export function useInit(container) {
+export function useInit(container, type) {
// 初始化
const target = ace.edit(container, {
maxLines: 20, // 最大行数,超过会自动出现滚动条
@@ -25,46 +25,75 @@ export function useInit(container) {
});
// 自定义提示
- customCompletions(target)
+ customCompletions(target, type)
return target;
}
// 自定义提示
-function customCompletions(target) {
- target.completers.push({
- getCompletions: function (state, session, pos, prefix, callback) {
- if (prefix.length === 0) {
- callback(null, []);
- return;
- }
- callback(null, [
- { meta: 'AjaxProxy::Ctx.req', caption: 'req.url: string', value: 'req.url', score: 100 },
- { meta: 'AjaxProxy::Ctx.req', caption: 'req.method: string', value: 'req.method', score: 100 },
- { meta: 'AjaxProxy::Ctx.req', caption: 'req.body?: any', value: 'req.body', score: 100 },
- { meta: 'AjaxProxy::Ctx.res', caption: 'res.status: string', value: 'res.status', score: 100 },
- { meta: 'AjaxProxy::Ctx.res', caption: 'res.customStatus: string', value: 'res.customStatus', score: 100 },
- { meta: 'AjaxProxy::Ctx.res', caption: 'res.response: any', value: 'res.response', score: 100 },
- {
- meta: 'AjaxProxy::Next',
- caption: 'next({ override?: string, status?: string | number })',
- value: 'next({ override: "", status: "" });',
- score: 100
- },
- ]);
- },
- });
+function customCompletions(target, type = 'interceptor') {
+ if (type === 'interceptor')
+ target.completers.push({
+ getCompletions: function (state, session, pos, prefix, callback) {
+ if (prefix.length === 0) {
+ callback(null, []);
+ return;
+ }
+ callback(null, [
+ { meta: 'AjaxProxy::Ctx.req', caption: 'req.url: string', value: 'req.url', score: 100 },
+ { meta: 'AjaxProxy::Ctx.req', caption: 'req.method: string', value: 'req.method', score: 100 },
+ { meta: 'AjaxProxy::Ctx.req', caption: 'req.body?: any', value: 'req.body', score: 100 },
+ { meta: 'AjaxProxy::Ctx.res', caption: 'res.status: string', value: 'res.status', score: 100 },
+ { meta: 'AjaxProxy::Ctx.res', caption: 'res.customStatus: string', value: 'res.customStatus', score: 100 },
+ { meta: 'AjaxProxy::Ctx.res', caption: 'res.response: any', value: 'res.response', score: 100 },
+ {
+ meta: 'AjaxProxy::Next',
+ caption: 'next({ override?: string, status?: string | number })',
+ value: 'next({ override: "", status: "" });',
+ score: 100
+ },
+ ]);
+ },
+ });
+ else
+ target.completers.push({
+ getCompletions: function (state, session, pos, prefix, callback) {
+ if (prefix.length === 0) {
+ callback(null, []);
+ return;
+ }
+ callback(null, [
+ { meta: 'AjaxProxy::Ctx.req', caption: 'req.url: string', value: 'req.url', score: 100 },
+ { meta: 'AjaxProxy::Ctx.req', caption: 'req.method: string', value: 'req.method', score: 100 },
+ {
+ meta: 'AjaxProxy::Next',
+ caption: 'next({ url: string, headers?: { [key: string]: string } })',
+ value: 'next({ url: req.url });',
+ score: 100
+ },
+ ]);
+ },
+ });
}
// 默认内容
-export function getDefaultContent() {
- const defaultContent =
+export function getDefaultContent(type = "interceptor") {
+ let defaultContent = type === 'interceptor' ?
`
function setup(req, res, next) {
// TODO...
// type Next = { override?: string, status?: string | number }
next({ override: "", status: "" });
}
+` :
+ `
+function setup(
+ req, /** req: { url: string, method: string }*/
+ next /**{ url: string, headers?: { [key: string]: string } }*/
+) {
+ // TODO...
+ next({ url: "" });
+}
`
return defaultContent
}
diff --git a/packages/proxy-lib/src/redirectFetch.ts b/packages/proxy-lib/src/redirectFetch.ts
index ffde62f..746747b 100644
--- a/packages/proxy-lib/src/redirectFetch.ts
+++ b/packages/proxy-lib/src/redirectFetch.ts
@@ -1,4 +1,5 @@
import { finalRedirectUrl, matchIgnoresAndRule } from "./common";
+import { execSetup } from "./redirectUrlFunc";
import { RefGlobalState } from "./types";
// 共享状态
@@ -7,7 +8,7 @@ const OriginFetch = window.fetch.bind(window)
// 初始化共享状态
export const initRedirectFetchState = (state: RefGlobalState) => globalState = state
-export default function CustomFetch(input: RequestInfo | URL, init?: RequestInit): Promise {
+export default async function CustomFetch(input: RequestInfo | URL, init?: RequestInit): Promise {
let fetchMethod: string | undefined | "ANY" = "ANY"
let customInit: RequestInit = init || {}
if (init) {
@@ -23,11 +24,39 @@ export default function CustomFetch(input: RequestInfo | URL, init?: RequestInit
redirect_url = "",
headers = [],
ignores = [],
+ redirect_type = "text",
+ redirect_func = ""
} = globalState.value.redirector_matching_content[i];
if (switch_on) {
// 判断是否存在协议匹配
if (method && ![fetchMethod, "ANY"].includes(method.toUpperCase())) break
- if (matchIgnoresAndRule(input.toString(), domain, filter_type, ignores)) {
+ if (redirect_type === "function") {
+ const payload = await execSetup({ url: input.toString(), method: fetchMethod }, redirect_func)
+ input = payload.url
+
+ if (init?.headers) {
+ // 初始化 RequestInit
+ customInit = init
+ const initHeaders = new Headers(init.headers)
+ if (payload.headers) {
+ for (const key in payload.headers) {
+ if (Object.prototype.hasOwnProperty.call(payload.headers, key)) {
+ const value = payload.headers[key];
+ if (key && value) initHeaders.set(key, value)
+ }
+ }
+ }
+ customInit.headers = initHeaders
+ } else {
+ const newHeaders: HeadersInit = payload.headers ? Object.keys(payload.headers).map(key => [key, payload.headers![key]]) : []
+ customInit = {
+ headers: newHeaders
+ }
+ }
+
+ // 值取当前命中的第一个,后续再命中的忽略
+ break;
+ } else if (matchIgnoresAndRule(input.toString(), domain, filter_type, ignores)) {
input = finalRedirectUrl(input.toString(), domain, redirect_url, filter_type)
if (init?.headers) {
diff --git a/packages/proxy-lib/src/redirectUrlFunc.ts b/packages/proxy-lib/src/redirectUrlFunc.ts
new file mode 100644
index 0000000..62b926b
--- /dev/null
+++ b/packages/proxy-lib/src/redirectUrlFunc.ts
@@ -0,0 +1,51 @@
+import { IRedirectHeader } from './types';
+
+
+const errLog = function (...args: any[]) {
+ console.log("%c[AjaxProxy][error]: ", "color: #ff4d4f", ...args)
+}
+
+type Req = {
+ url: string
+ method: string
+}
+
+type Next = {
+ url: string,
+ headers?: { [key: IRedirectHeader['key']]: IRedirectHeader['value'] }
+}
+
+function isNext(x: any): x is Next {
+ if (!!x) return true
+ if (x.url && !x.headers) return true
+ return x.headers && Object.prototype.toString.call(x.headers) == '[object Object]'
+}
+
+export function execSetup(req: Req, funcText: string): Promise {
+ return new Promise(resolve => {
+ try {
+ const source = ';(' + funcText + ')'
+ const execFunc = window.eval(source)
+ const type = typeof execFunc
+ if (type === "function") {
+ if (!funcText.includes('next(')) {
+ errLog("The structure of 'next' is incorrect [code 1]")
+ // original
+ return resolve({ url: req.url })
+ }
+ execFunc(req, (next: Next) => {
+ // custom
+ if (isNext(next)) {
+ return resolve({ url: next.url, headers: next.headers })
+ }
+ })
+ return resolve({ url: req.url })
+ }
+ errLog("Please enter a correct 'function' [code 2]")
+ return resolve({ url: req.url })
+ } catch (error) {
+ errLog(error)
+ return resolve({ url: req.url })
+ }
+ })
+}
\ No newline at end of file
diff --git a/packages/proxy-lib/src/redirectXHR.ts b/packages/proxy-lib/src/redirectXHR.ts
index e5e0cb3..13d7762 100644
--- a/packages/proxy-lib/src/redirectXHR.ts
+++ b/packages/proxy-lib/src/redirectXHR.ts
@@ -1,4 +1,5 @@
import { finalRedirectUrl, fmtURLToString, matchIgnoresAndRule } from "./common";
+import { execSetup } from "./redirectUrlFunc";
import { RefGlobalState } from "./types";
// 状态
@@ -21,7 +22,7 @@ export default class CustomRedirectXHR extends XMLHttpRequest {
const origin_XHR_open = open
const origin_XHR_setRequestHeader = this.setRequestHeader
const origin_XHR_send = this.send
- this.open = (method: string,
+ this.open = async (method: string,
url: string | URL,
async?: boolean,
username?: string | null,
@@ -37,13 +38,31 @@ export default class CustomRedirectXHR extends XMLHttpRequest {
redirect_url = "",
headers = [],
ignores = [],
+ redirect_type = "text",
+ redirect_func = ""
} = globalState.value.redirector_matching_content[i];
if (switch_on) {
// 判断是否存在协议匹配
if (method && ![this.method, "ANY"].includes(method.toUpperCase())) return
// 规则判断
const currentUrl = fmtURLToString(url)
- if (matchIgnoresAndRule(currentUrl, domain, filter_type, ignores)) {
+ if (redirect_type === "function") {
+ const payload = await execSetup({ url: currentUrl, method: this.method }, redirect_func)
+ url = payload.url
+ this.send = (body?: Document | XMLHttpRequestBodyInit | null) => {
+ if (payload.headers) {
+ for (const key in payload.headers) {
+ if (Object.prototype.hasOwnProperty.call(payload.headers, key)) {
+ const value = payload.headers[key];
+ if (key && value)
+ this.setRequestHeader(key, value)
+ }
+ }
+ }
+ origin_XHR_send.call(this, body)
+ }
+ break;
+ } else if (matchIgnoresAndRule(currentUrl, domain, filter_type, ignores)) {
url = finalRedirectUrl(currentUrl, domain, redirect_url, filter_type)
// 获取 自定义 header 映射关系
@@ -59,6 +78,7 @@ export default class CustomRedirectXHR extends XMLHttpRequest {
for (let k = 0; k < headers.length; k++) {
const header = headers[k];
// 移除掉 映射关系
+ // Reflect.deleteProperty(cacheHeaderMap, header.key)
delete cacheHeaderMap[header.key]
this.setRequestHeader(header.key, header.value)
}
diff --git a/packages/proxy-lib/src/types.ts b/packages/proxy-lib/src/types.ts
index 8992a35..77ff4d8 100644
--- a/packages/proxy-lib/src/types.ts
+++ b/packages/proxy-lib/src/types.ts
@@ -8,6 +8,8 @@ export type IMode = "interceptor" | "redirector"
export type RefGlobalState = { value: T }
/**响应式类型 */
export type OverrideType = 'json' | 'function'
+/**重定向类型 */
+export type RedirectType = 'text' | 'function'
type CommonContent = {
/**是否需要匹配 */
@@ -53,6 +55,10 @@ export type IMatchRedirectContent = {
headers?: IRedirectHeader[]
/**忽略名单 */
ignores?: string[]
+ /**重定向类型 */
+ redirect_type?: RedirectType
+ /**函数响应 */
+ redirect_func?: string
} & CommonContent
export type IGlobalState = {
diff --git a/packages/proxy-lib/types/redirectUrlFunc.d.ts b/packages/proxy-lib/types/redirectUrlFunc.d.ts
new file mode 100644
index 0000000..c029e08
--- /dev/null
+++ b/packages/proxy-lib/types/redirectUrlFunc.d.ts
@@ -0,0 +1,13 @@
+import { IRedirectHeader } from './types';
+declare type Req = {
+ url: string;
+ method: string;
+};
+declare type Next = {
+ url: string;
+ headers?: {
+ [key: IRedirectHeader['key']]: IRedirectHeader['value'];
+ };
+};
+export declare function execSetup(req: Req, funcText: string): Promise;
+export {};
diff --git a/packages/proxy-lib/types/types.d.ts b/packages/proxy-lib/types/types.d.ts
index 6518c0b..8d20dca 100644
--- a/packages/proxy-lib/types/types.d.ts
+++ b/packages/proxy-lib/types/types.d.ts
@@ -10,6 +10,8 @@ export declare type RefGlobalState = {
};
/**响应式类型 */
export declare type OverrideType = 'json' | 'function';
+/**重定向类型 */
+export declare type RedirectType = 'text' | 'function';
declare type CommonContent = {
/**是否需要匹配 */
switch_on: boolean;
@@ -51,6 +53,10 @@ export declare type IMatchRedirectContent = {
headers?: IRedirectHeader[];
/**忽略名单 */
ignores?: string[];
+ /**重定向类型 */
+ redirect_type?: RedirectType;
+ /**函数响应 */
+ redirect_func?: string;
} & CommonContent;
export declare type IGlobalState = {
/**全局开关 */
diff --git a/packages/shell-chrome/manifest.json b/packages/shell-chrome/manifest.json
index 4411a52..f049a3c 100644
--- a/packages/shell-chrome/manifest.json
+++ b/packages/shell-chrome/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Ajax Proxy",
- "version": "2.2.6",
+ "version": "2.2.7",
"description": "Modify your Ajax response to test",
"author": "Gj",
"icons": {
diff --git a/packages/vue-panels/src/views/index.vue b/packages/vue-panels/src/views/index.vue
index 17a170d..b183a33 100644
--- a/packages/vue-panels/src/views/index.vue
+++ b/packages/vue-panels/src/views/index.vue
@@ -244,7 +244,7 @@ export default {
this.currentMode = useMode.get();
// 初始化title
const manifest = chrome.runtime?.getManifest();
- document.title = `Ajax Proxy ${manifest?.version}`;
+ document.title = `Ajax Proxy ${manifest?.version || 'DEV'}`;
},
},
mounted() {
diff --git a/packages/vue-panels/src/views/redirector/modal.vue b/packages/vue-panels/src/views/redirector/modal.vue
index 0d3e1de..d152df6 100644
--- a/packages/vue-panels/src/views/redirector/modal.vue
+++ b/packages/vue-panels/src/views/redirector/modal.vue
@@ -46,141 +46,171 @@
-
-
-
-
-
-
-
-
- +{{ $t("append") }}
+
+
+
-
- Key
- Value
- {{ $t("describe") }}
- {{ $t("option") }}
-
-
+
+
+
-
-
-
+
+ +{{ $t("append") }}
-
-
-
-
-
-
+ Key
+ Value
+ {{ $t("describe") }}
+ {{ $t("option") }}
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("del") }}
+
+
+
+
+
+
+
+
{{ $t("del") }}+{{ $t("append") }}
-
-
-
-
-
-
-
-
- +{{ $t("append") }}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
import { uniqueId } from "@alrale/common-lib";
import { useTags } from "@/common/store";
+import { VueCodeEditor } from "@proxy/code-editor";
export default {
+ components: { CodeEditor: VueCodeEditor },
data() {
return {
isShow: false,
@@ -245,6 +277,8 @@ export default {
this.isEdit = true;
// 编辑
this.title = this.$t("edit");
+ // 重定向类型
+ if (!row.redirect_type) row.redirect_type = "text";
// 兼容版本迭代,旧数据未存在白名单
if (!row.ignores) row.ignores = [];
} else {
@@ -258,6 +292,7 @@ export default {
headers: [],
ignores: [],
filter_type: "normal",
+ redirect_type: "text",
};
this.$nextTick(() => this.$refs.form.clearValidate());
},