├── .gitignore ├── .vscode └── extensions.json ├── README.md ├── assets └── vue.svg ├── components ├── HelloWorld.vue └── TestDemo.vue ├── entrypoints ├── background.ts ├── content.ts ├── options │ ├── App.vue │ ├── Options.vue │ ├── index.html │ ├── main.ts │ └── style.css ├── popup │ ├── App.vue │ ├── Popup.vue │ ├── index.html │ ├── main.ts │ └── style.css └── testpage │ ├── App.vue │ ├── index.html │ ├── main.ts │ └── style.css ├── package.json ├── pnpm-lock.yaml ├── public ├── _locales │ ├── en │ │ └── messages.json │ └── zh_CN │ │ └── messages.json ├── icon │ ├── 128.png │ ├── 16.png │ ├── 32.png │ ├── 48.png │ └── 96.png └── wxt.svg ├── screenshot ├── gzh.png ├── screen_1.png ├── screen_2.png └── weixin.png ├── tsconfig.json ├── utils ├── base.ts └── storage.ts └── wxt.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .output 12 | stats.html 13 | stats-*.json 14 | .wxt 15 | web-ext.config.ts 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HiProxy 2 | A proxy extension for Chrome 3 | 4 | ``` 5 | Based on Manifest V3 (MV3) 6 | 7 | vue3 + wxt + ts 8 | 9 | 10 | 全新版本,基于谷歌扩展MV3规范开发,适用于Chrome浏览器新版本。 11 | 12 | 代码框架采用 vue3 + wxt + ts 13 | 14 | 15 | 项目代码完全开源,有兴趣的可以自己尝试编译打包,或者省事的师傅可以直接下载我打包好的 zip 文件,直达下载地址: 16 | 17 | https://github.com/hicode0101/HiProxy/releases 18 | 19 | 20 | 21 | 使用方式: 22 | 1、进chrome 浏览器扩展,直达地址 chrome://extensions/ 23 | 2、开启开发者模式 24 | 3、把下载的 HiProxy-3.2.10-chrome.zip 文件直接拖进去,就可以了,不用解压。 25 | 26 | 27 | 通过我编写的另外一个 HiLocalProxy 小工具,可以完美解决目前主流浏览器不支持socks5带密码验证的问题。 28 | 29 | 30 | ``` 31 | 32 |
33 |
34 |
35 | 36 | --- 37 | 38 | ### 插件安装和使用视频教程: 39 | https://www.bilibili.com/video/BV1tVS4YXEUT 40 | 41 | 42 | 43 | 44 | ### 支持socks5用户名密码代理的视频教程: 45 | 46 | https://www.bilibili.com/video/BV18rPsedEXs 47 | 48 | 49 | 点击链接跳转至视频播放页面。 50 | 51 |
52 |
53 |
54 | 55 | 56 | # Screenshot(UI截图) 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ## 作者申明 65 | 66 | 本系列工具软件仅供白帽子安全研究和技术交流使用,禁止用于商业用途。 67 | 68 | 👨‍💻我的白帽昵称:犀利的远哥 69 | 70 | ✉️我的微信:hicode0101 71 | 72 | 💞️微信公众号:远哥说安全 73 | 74 | 如果你在我分享的工具中,遇到了问题,请联系我,我会及时回复。 75 | 76 | 如果你在我分享的工具上,有其它功能需求,也请告知我,我非常乐意尝试满足你的需求。 77 | 78 | 79 | 80 | 81 | --- 82 | 83 | 84 | 85 | ### 我的微信: 86 | 87 | 88 | 89 | 90 | --- 91 | 92 | ### 微信公众号: 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /assets/vue.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 29 | 30 | 35 | -------------------------------------------------------------------------------- /components/TestDemo.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 35 | 36 | 37 | 40 | 41 | -------------------------------------------------------------------------------- /entrypoints/background.ts: -------------------------------------------------------------------------------- 1 | //import { directProxy } from "@/utils/base"; 2 | 3 | export default defineBackground(() => { 4 | console.log('Hello background!', { id: browser.runtime.id }); 5 | 6 | 7 | browser.runtime.onInstalled.addListener((e: any) => { 8 | console.log("onInstalled"); 9 | //console.info("onInstalled"); 10 | console.debug(JSON.stringify(e)); 11 | 12 | if (e.reason === 'install') { 13 | proxyConfigsInit(); 14 | //browser.tabs.create({ url: "/options.html" }); 15 | } 16 | 17 | directProxy(()=>{}); 18 | 19 | }); 20 | 21 | browser.runtime.onStartup.addListener((event: any) => { 22 | console.log("onStartup", new Date().toLocaleTimeString()); 23 | 24 | //directProxy(()=>{}); 25 | 26 | browser.storage.local.get(["useLastProxy"]).then((result : any)=> { 27 | console.log("useLastProxy is ", result.useLastProxy); 28 | 29 | if(result.useLastProxy == true){ 30 | console.log("openCurrProxy"); 31 | openCurrProxy(()=>{}); 32 | }else{ 33 | directProxy(()=>{}); 34 | } 35 | 36 | }); 37 | 38 | }); 39 | 40 | 41 | /* 42 | browser.runtime.openOptionsPage((event: any) => { 43 | console.log("openOptionsPage", new Date().toLocaleTimeString()); 44 | }); 45 | */ 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /entrypoints/content.ts: -------------------------------------------------------------------------------- 1 | export default defineContentScript({ 2 | matches: ['*://*.google.com/*'], 3 | main() { 4 | console.log('Hello content.ts.'); 5 | }, 6 | }); 7 | -------------------------------------------------------------------------------- /entrypoints/options/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /entrypoints/options/Options.vue: -------------------------------------------------------------------------------- 1 | 329 | 330 | 675 | 676 | -------------------------------------------------------------------------------- /entrypoints/options/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HiProxy Options 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /entrypoints/options/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import './style.css'; 3 | import App from './App.vue'; 4 | import naive from "naive-ui"; 5 | 6 | //createApp(App).mount('#app'); 7 | 8 | const app = createApp(App); 9 | app.use(naive); 10 | app.mount("#app"); 11 | -------------------------------------------------------------------------------- /entrypoints/options/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | a { 27 | font-weight: 500; 28 | color: #646cff; 29 | text-decoration: inherit; 30 | } 31 | a:hover { 32 | color: #535bf2; 33 | } 34 | 35 | body { 36 | margin: 0; 37 | min-width: 320px; 38 | min-height: 100vh; 39 | } 40 | 41 | h1 { 42 | font-size: 3.2em; 43 | line-height: 1.1; 44 | } 45 | 46 | button { 47 | border-radius: 8px; 48 | border: 1px solid transparent; 49 | padding: 0.6em 1.2em; 50 | font-size: 1em; 51 | font-weight: 500; 52 | font-family: inherit; 53 | background-color: #1a1a1a; 54 | cursor: pointer; 55 | transition: border-color 0.25s; 56 | } 57 | button:hover { 58 | border-color: #646cff; 59 | } 60 | button:focus, 61 | button:focus-visible { 62 | outline: 4px auto -webkit-focus-ring-color; 63 | } 64 | 65 | .card { 66 | padding: 2em; 67 | } 68 | 69 | #app { 70 | max-width: 1280px; 71 | } 72 | 73 | @media (prefers-color-scheme: light) { 74 | :root { 75 | color: #213547; 76 | background-color: #ffffff; 77 | } 78 | a:hover { 79 | color: #747bff; 80 | } 81 | button { 82 | background-color: #f9f9f9; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /entrypoints/popup/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /entrypoints/popup/Popup.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 192 | 193 | -------------------------------------------------------------------------------- /entrypoints/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HiProxy Popup 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /entrypoints/popup/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import './style.css'; 3 | import App from './App.vue'; 4 | import naive from "naive-ui"; 5 | 6 | //createApp(App).mount('#app'); 7 | 8 | const app = createApp(App); 9 | app.use(naive); 10 | app.mount("#app"); 11 | -------------------------------------------------------------------------------- /entrypoints/popup/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | a { 27 | font-weight: 500; 28 | color: #646cff; 29 | text-decoration: inherit; 30 | } 31 | a:hover { 32 | color: #535bf2; 33 | } 34 | 35 | body { 36 | margin: 0; 37 | display: flex; 38 | place-items: center; 39 | min-width: 210px; 40 | background-color: #fdfdfd; 41 | } 42 | 43 | h1 { 44 | font-size: 3.2em; 45 | line-height: 1.1; 46 | } 47 | 48 | button { 49 | border-radius: 8px; 50 | border: 1px solid transparent; 51 | padding: 0.6em 1.2em; 52 | font-size: 1em; 53 | font-weight: 500; 54 | font-family: inherit; 55 | background-color: #1a1a1a; 56 | cursor: pointer; 57 | transition: border-color 0.25s; 58 | } 59 | button:hover { 60 | border-color: #646cff; 61 | } 62 | button:focus, 63 | button:focus-visible { 64 | outline: 4px auto -webkit-focus-ring-color; 65 | } 66 | 67 | .card { 68 | padding: 2em; 69 | } 70 | 71 | #app { 72 | min-width: 210px; 73 | } 74 | 75 | @media (prefers-color-scheme: light) { 76 | :root { 77 | color: #213547; 78 | background-color: #ffffff; 79 | } 80 | a:hover { 81 | color: #747bff; 82 | } 83 | button { 84 | background-color: #f9f9f9; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /entrypoints/testpage/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /entrypoints/testpage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test Page 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /entrypoints/testpage/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import './style.css'; 3 | import App from './App.vue'; 4 | import naive from "naive-ui"; 5 | 6 | //createApp(App).mount('#app'); 7 | 8 | const app = createApp(App); 9 | app.use(naive); 10 | app.mount("#app"); 11 | -------------------------------------------------------------------------------- /entrypoints/testpage/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | a { 27 | font-weight: 500; 28 | color: #646cff; 29 | text-decoration: inherit; 30 | } 31 | a:hover { 32 | color: #535bf2; 33 | } 34 | 35 | body { 36 | margin: 0; 37 | display: flex; 38 | place-items: center; 39 | min-width: 320px; 40 | min-height: 100vh; 41 | } 42 | 43 | h1 { 44 | font-size: 3.2em; 45 | line-height: 1.1; 46 | } 47 | 48 | button { 49 | border-radius: 8px; 50 | border: 1px solid transparent; 51 | padding: 0.6em 1.2em; 52 | font-size: 1em; 53 | font-weight: 500; 54 | font-family: inherit; 55 | background-color: #1a1a1a; 56 | cursor: pointer; 57 | transition: border-color 0.25s; 58 | } 59 | button:hover { 60 | border-color: #646cff; 61 | } 62 | button:focus, 63 | button:focus-visible { 64 | outline: 4px auto -webkit-focus-ring-color; 65 | } 66 | 67 | .card { 68 | padding: 2em; 69 | } 70 | 71 | #app { 72 | max-width: 1280px; 73 | margin: 0 auto; 74 | padding: 2rem; 75 | text-align: center; 76 | } 77 | 78 | @media (prefers-color-scheme: light) { 79 | :root { 80 | color: #213547; 81 | background-color: #ffffff; 82 | } 83 | a:hover { 84 | color: #747bff; 85 | } 86 | button { 87 | background-color: #f9f9f9; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wxt-vue-starter", 3 | "description": "manifest.json description", 4 | "private": true, 5 | "version": "0.0.0", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "wxt", 9 | "dev:firefox": "wxt -b firefox", 10 | "build": "wxt build", 11 | "build:firefox": "wxt build -b firefox", 12 | "zip": "wxt zip", 13 | "zip:firefox": "wxt zip -b firefox", 14 | "compile": "vue-tsc --noEmit", 15 | "postinstall": "wxt prepare" 16 | }, 17 | "dependencies": { 18 | "@vicons/ionicons4": "^0.12.0", 19 | "@vicons/ionicons5": "^0.12.0", 20 | "vue": "^3.5.11" 21 | }, 22 | "devDependencies": { 23 | "@vicons/fluent": "^0.12.0", 24 | "@wxt-dev/module-vue": "^1.0.1", 25 | "naive-ui": "^2.40.1", 26 | "typescript": "^5.6.2", 27 | "vfonts": "^0.0.3", 28 | "vue-router": "^4.4.5", 29 | "vue-tsc": "^2.1.6", 30 | "wxt": "^0.19.13" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /public/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugin_name": { 3 | "message": "HiProxy" 4 | }, 5 | "plugin_desc": { 6 | "message": "A proxy extension for Chrome" 7 | }, 8 | "pop_menu_direct": { 9 | "message": "Direct" 10 | }, 11 | "pop_menu_system": { 12 | "message": "System" 13 | }, 14 | "pop_menu_options": { 15 | "message": "Options" 16 | }, 17 | "pop_menu_myip": { 18 | "message": "Query My IP" 19 | }, 20 | "options_tab_proxy_config": { 21 | "message": "Proxy Configs" 22 | }, 23 | "options_tab_bypass_doc": { 24 | "message": "Bypass Doc" 25 | }, 26 | "options_tab_export": { 27 | "message": "Export/Import" 28 | }, 29 | "options_tab_others": { 30 | "message": "Others" 31 | }, 32 | "options_tab_about": { 33 | "message": "About" 34 | }, 35 | "options_btn_addNew": { 36 | "message": "Add New Config" 37 | }, 38 | "options_msg_input_proxy_name_ph": { 39 | "message": "a name for config" 40 | }, 41 | "options_btn_save_all": { 42 | "message": "Save All" 43 | }, 44 | "options_btn_reset": { 45 | "message": "Reset" 46 | }, 47 | "options_btn_detail_ok": { 48 | "message": "OK" 49 | }, 50 | "options_btn_detail_ok_tips": { 51 | "message": "After modification, click the batch save button on the list interface to take effect" 52 | }, 53 | "options_about_project": { 54 | "message": "Project" 55 | }, 56 | "options_about_version": { 57 | "message": "Version" 58 | }, 59 | "options_about_author": { 60 | "message": "Author" 61 | }, 62 | "options_msg_repeat_name": { 63 | "message": "There are duplicate configuration names, please check!" 64 | }, 65 | "options_msg_save_success": { 66 | "message": "Saved successfully! " 67 | }, 68 | "options_msg_reset_dialog_title": { 69 | "message": "Operation warning" 70 | }, 71 | "options_msg_reset_dialog_content": { 72 | "message": "Are you sure you want to reset all configurations?" 73 | }, 74 | "options_msg_reset_dialog_positiveText": { 75 | "message": "Confirm" 76 | }, 77 | "options_msg_reset_dialog_negativeText": { 78 | "message": "Cancel" 79 | }, 80 | "options_msg_input_name": { 81 | "message": "The configuration name parameter is incorrect, please check" 82 | }, 83 | "options_msg_input_host": { 84 | "message": "The host address parameters are incorrect, please check" 85 | }, 86 | "options_msg_input_port": { 87 | "message": "The port parameters are incorrect (port range 0~65535), please check" 88 | }, 89 | "options_msg_input_detail_bypass": { 90 | "message": "List of hosts connected without proxy: (one host per row)" 91 | }, 92 | "options_msg_input_detail_bypass_host_ph": { 93 | "message": "host IP or domain wildcard" 94 | }, 95 | "options_msg_input_detail_auth": { 96 | "message": "Proxy login: (If password login is not required, leave all options below blank)" 97 | }, 98 | "options_msg_input_detail_auth_username": { 99 | "message": "UserName" 100 | }, 101 | "options_msg_input_detail_auth_password": { 102 | "message": "PassWord" 103 | }, 104 | "options_msg_export_tips": { 105 | "message": "Here, you can export the current configuration for backup or sharing, or directly restore personalized configuration parameters by importing files." 106 | }, 107 | "options_msg_export_lbl": { 108 | "message": "Export" 109 | }, 110 | "options_msg_export_btn": { 111 | "message": "Export Config" 112 | }, 113 | "options_msg_import_lbl": { 114 | "message": "Import" 115 | }, 116 | "options_msg_import_btn": { 117 | "message": "Import Config" 118 | }, 119 | "options_msg_import_reset_lbl": { 120 | "message": "Reset" 121 | }, 122 | "options_msg_import_reset_btn": { 123 | "message": "Reset (restore to initial configuration)" 124 | }, 125 | "options_msg_others_uselastproxy_lbl": { 126 | "message": "Default Proxy" 127 | }, 128 | "options_msg_others_uselastproxy_switch": { 129 | "message": "(Use the last proxy)" 130 | }, 131 | "options_msg_xxxxxxxxx": { 132 | "message": "xxxxxxxxxxxx" 133 | }, 134 | "test_note": { 135 | "message": "" 136 | } 137 | } -------------------------------------------------------------------------------- /public/_locales/zh_CN/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugin_name": { 3 | "message": "HiProxy" 4 | }, 5 | "plugin_desc": { 6 | "message": "HiProxy 一个代理设置插件" 7 | }, 8 | "pop_menu_direct": { 9 | "message": "直接连接" 10 | }, 11 | "pop_menu_system": { 12 | "message": "系统代理" 13 | }, 14 | "pop_menu_options": { 15 | "message": "配置选项" 16 | }, 17 | "pop_menu_myip": { 18 | "message": "查询当前IP" 19 | }, 20 | "options_tab_proxy_config": { 21 | "message": "代理配置" 22 | }, 23 | "options_tab_bypass_doc": { 24 | "message": "通配符说明文档" 25 | }, 26 | "options_tab_export": { 27 | "message": "导入导出" 28 | }, 29 | "options_tab_others": { 30 | "message": "其它配置" 31 | }, 32 | "options_tab_about": { 33 | "message": "关于" 34 | }, 35 | "options_btn_addNew": { 36 | "message": "新增代理配置" 37 | }, 38 | "options_msg_input_proxy_name_ph": { 39 | "message": "代理配置的名称,例如:http-8080,方便自己区分" 40 | }, 41 | "options_btn_save_all": { 42 | "message": "批量保存" 43 | }, 44 | "options_btn_reset": { 45 | "message": "重置" 46 | }, 47 | "options_btn_detail_ok": { 48 | "message": "确定修改,并返回到列表" 49 | }, 50 | "options_btn_detail_ok_tips": { 51 | "message": "修改后在列表界面,点批量保存按钮生效" 52 | }, 53 | "options_about_project": { 54 | "message": "项目地址" 55 | }, 56 | "options_about_version": { 57 | "message": "版本号" 58 | }, 59 | "options_about_author": { 60 | "message": "作者" 61 | }, 62 | "options_msg_repeat_name": { 63 | "message": "存在重复的配置名,请检查!" 64 | }, 65 | "options_msg_save_success": { 66 | "message": "保存成功!" 67 | }, 68 | "options_msg_reset_dialog_title": { 69 | "message": "操作警告" 70 | }, 71 | "options_msg_reset_dialog_content": { 72 | "message": "您确定要重置全部配置吗?" 73 | }, 74 | "options_msg_reset_dialog_positiveText": { 75 | "message": "确定重置" 76 | }, 77 | "options_msg_reset_dialog_negativeText": { 78 | "message": "取消" 79 | }, 80 | "options_msg_input_name": { 81 | "message": "配置名参数不正确,请检查" 82 | }, 83 | "options_msg_input_host": { 84 | "message": "主机地址参数不正确,请检查" 85 | }, 86 | "options_msg_input_port": { 87 | "message": "端口参数不正确(端口范围 0~ 65535),请检查" 88 | }, 89 | "options_msg_input_detail_bypass": { 90 | "message": "不经过代理连接的主机列表: (每行一个主机,支持通配符)" 91 | }, 92 | "options_msg_input_detail_bypass_host_ph": { 93 | "message": "请输入 主机IP 或 域名通配符" 94 | }, 95 | "options_msg_input_detail_auth": { 96 | "message": "代理登录: (如无需密码登录,则下面选项全部留空)" 97 | }, 98 | "options_msg_input_detail_auth_username": { 99 | "message": "用户名" 100 | }, 101 | "options_msg_input_detail_auth_password": { 102 | "message": "密码" 103 | }, 104 | "options_msg_export_tips": { 105 | "message": "在这里您可以将当前的配置导出进行备份或分享,也可以在这里通过导入文件直接还原个性化的配置参数。" 106 | }, 107 | "options_msg_export_lbl": { 108 | "message": "导出" 109 | }, 110 | "options_msg_export_btn": { 111 | "message": "导出当前配置" 112 | }, 113 | "options_msg_import_lbl": { 114 | "message": "导入" 115 | }, 116 | "options_msg_import_btn": { 117 | "message": "导入配置" 118 | }, 119 | "options_msg_import_reset_lbl": { 120 | "message": "重置" 121 | }, 122 | "options_msg_import_reset_btn": { 123 | "message": "重置(还原到初始配置)" 124 | }, 125 | "options_msg_others_uselastproxy_lbl": { 126 | "message": "默认上一次代理" 127 | }, 128 | "options_msg_others_uselastproxy_switch": { 129 | "message": "(每次重新启动浏览器时,自动使用上一次使用的代理)" 130 | }, 131 | "options_msg_xxxxxxxxx": { 132 | "message": "xxxxxxxxxxxx" 133 | }, 134 | "test_note": { 135 | "message": "" 136 | } 137 | } -------------------------------------------------------------------------------- /public/icon/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/public/icon/128.png -------------------------------------------------------------------------------- /public/icon/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/public/icon/16.png -------------------------------------------------------------------------------- /public/icon/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/public/icon/32.png -------------------------------------------------------------------------------- /public/icon/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/public/icon/48.png -------------------------------------------------------------------------------- /public/icon/96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/public/icon/96.png -------------------------------------------------------------------------------- /public/wxt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /screenshot/gzh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/screenshot/gzh.png -------------------------------------------------------------------------------- /screenshot/screen_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/screenshot/screen_1.png -------------------------------------------------------------------------------- /screenshot/screen_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/screenshot/screen_2.png -------------------------------------------------------------------------------- /screenshot/weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hicode0101/HiProxy/4272c620cde614a446476e99b159b9ed86f27be4/screenshot/weixin.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.wxt/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /utils/base.ts: -------------------------------------------------------------------------------- 1 | 2 | export function everywherefunc(){ 3 | console.log("every-where-func"); 4 | } 5 | 6 | 7 | 8 | export async function directProxy(callback:Function) { 9 | 10 | browser.proxy.settings.set({ value: { mode: "direct" }, scope: "regular" }).then(() => { 11 | callback(); 12 | }); 13 | 14 | 15 | showCurrProxy(); 16 | reDrawIcon("#9b9b9b", ""); 17 | //currProxyPidRemove(); 18 | 19 | let currProxyPid = await currProxyPidGet(); 20 | console.log("directProxy currProxyPid", currProxyPid); 21 | 22 | } 23 | 24 | export function systemProxy(callback:Function) { 25 | 26 | browser.proxy.settings.set({ value: { mode: "system" }, scope: "regular" }).then(() => { 27 | callback(); 28 | }); 29 | 30 | showCurrProxy(); 31 | reDrawIcon("#000000", ""); 32 | //currProxyPidRemove(); 33 | 34 | } 35 | 36 | 37 | 38 | 39 | export function showCurrProxy() { 40 | browser.proxy.settings.get({ 'incognito': false }, function (config: any) { 41 | console.log(JSON.stringify(config)); 42 | }); 43 | } 44 | 45 | export async function proxyConfigsInit() { 46 | console.log("proxyConfigsInit", new Date().toLocaleTimeString()); 47 | 48 | let currProxyPid = await currProxyPidGet(); 49 | console.log("proxyConfigsInit currProxyPid", currProxyPid); 50 | 51 | //storageClear(); 52 | 53 | 54 | let proxyConfigsMap = getDefaultConfigs(); 55 | 56 | await proxyConfigsSet(proxyConfigsMap); 57 | 58 | //proxyConfigsMap = await proxyConfigsGet(); 59 | ///console.log(proxyConfigsMap); 60 | 61 | currProxyPidSet("direct"); 62 | directProxy(()=>{}); 63 | 64 | } 65 | 66 | 67 | export async function openCurrProxy(callback: Function) { 68 | let currProxyPid = await currProxyPidGet(); 69 | console.log("openCurrProxy currProxyPid", currProxyPid); 70 | 71 | proxyConfigsGetByCallback((result: Map)=>{ 72 | let proxyConfigs = result; 73 | if(proxyConfigs instanceof Map){ 74 | if (currProxyPid != undefined && proxyConfigs.has(currProxyPid) == true) { 75 | let proxyConfig = proxyConfigs.get(currProxyPid); 76 | changeProxy(proxyConfig, callback); 77 | }else if(currProxyPid == "system"){ 78 | systemProxy(callback); 79 | } else { 80 | directProxy(callback); 81 | } 82 | }else{ 83 | console.error("proxyConfigs is not a map"); 84 | directProxy(callback); 85 | } 86 | 87 | }); 88 | 89 | 90 | } 91 | 92 | export function changeProxy(proxyConfig: any, callback: Function) { 93 | console.log("changeProxy", proxyConfig); 94 | 95 | var _config = { 96 | mode: proxyConfig.mode, 97 | rules: proxyConfig.rules 98 | }; 99 | 100 | console.log("proxy.settings.set", _config); 101 | browser.proxy.settings.set({ value: _config, scope: 'regular' }, console.log("porxy switched")).then(() => { 102 | callback(); 103 | }); 104 | 105 | 106 | reDrawIcon(proxyConfig.color, ""); 107 | currProxyPidSet(proxyConfig.pid); 108 | showCurrProxy(); 109 | } 110 | 111 | 112 | 113 | export function reDrawIcon(outColor: string, innerColor: string) { 114 | console.log(outColor, innerColor); 115 | 116 | const canvas = new OffscreenCanvas(16, 16); 117 | const context = canvas.getContext('2d'); 118 | if (!context) { 119 | console.error('Failed to get 2D rendering context'); 120 | return; 121 | } 122 | context.scale(16, 16); 123 | context.clearRect(0, 0, 1, 1); 124 | //context.fillStyle = '#00FF00'; 125 | //circleDraw(context, "#4477bb"); 126 | //circleDraw(context, "#4477bb","#00FF00"); 127 | circleDraw(context, outColor, innerColor); 128 | context.setTransform(1, 0, 0, 1, 0, 0); 129 | const imageData = context.getImageData(0, 0, 16, 16); 130 | //chrome.action.setIcon({ imageData: imageData }, () => { /* ... */ }); 131 | browser.action.setIcon({ imageData: imageData }, () => { /* ... */ }); 132 | 133 | } 134 | 135 | export function circleDraw(ctx: OffscreenCanvasRenderingContext2D, outerCircleColor: string, innerCircleColor: string) { 136 | ctx.globalCompositeOperation = "source-over"; 137 | ctx.fillStyle = outerCircleColor; 138 | ctx.beginPath(); 139 | ctx.arc(0.5, 0.5, 0.5, 0, Math.PI * 2, true); 140 | ctx.closePath(); 141 | ctx.fill(); 142 | 143 | if (innerCircleColor != null && innerCircleColor != "") { 144 | ctx.fillStyle = innerCircleColor; 145 | } else { 146 | ctx.globalCompositeOperation = "destination-out"; 147 | } 148 | 149 | ctx.beginPath(); 150 | ctx.arc(0.5, 0.5, 0.25, 0, Math.PI * 2, true); 151 | ctx.closePath(); 152 | ctx.fill(); 153 | } 154 | 155 | -------------------------------------------------------------------------------- /utils/storage.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export async function currProxyPidGet() { 4 | //localStorage.getItem('currProxyPid'); 5 | //chrome.storage.local.get(["currProxyPid"]).then((result)=> {console.log("Value is ",result.currProxyPid);}); 6 | 7 | const _result = await browser.storage.local.get("currProxyPid"); 8 | console.log(_result); 9 | //console.log(_result.currProxyPid); 10 | return _result.currProxyPid; 11 | } 12 | 13 | 14 | export async function currProxyPidSet(currProxyPid: string) { 15 | //localStorage.setItem('currProxyPid', 'taven2'); 16 | //chrome.storage.local.set({"currProxyPid":"system"}).then(()=> {console.log("Value is set");}) 17 | 18 | await browser.storage.local.set({ "currProxyPid": currProxyPid }); 19 | 20 | } 21 | 22 | export function currProxyPidRemove() { 23 | browser.storage.local.remove("currProxyPid"); 24 | } 25 | 26 | export async function useLastProxyGet() { 27 | 28 | const _result = await browser.storage.local.get("useLastProxy"); 29 | console.log(_result); 30 | //console.log(_result.useLastProxy); 31 | return _result.useLastProxy; 32 | } 33 | 34 | 35 | export async function useLastProxySet(useLastProxy: boolean) { 36 | 37 | await browser.storage.local.set({ "useLastProxy": useLastProxy }); 38 | 39 | } 40 | 41 | export async function saveProxyConfigs(proxyConfigsMap : Map){ 42 | 43 | let json = JSON.stringify(Array.from(proxyConfigsMap)); 44 | console.log(json); 45 | 46 | //localStorage.setItem("proxyConfigs", json); 47 | await browser.storage.local.set({"proxyConfigs": json}); 48 | 49 | } 50 | 51 | export function getAllKeys() { 52 | //chrome.storage.local.getKeys().then((result)=> {console.log("Value is ",result);}); 53 | browser.storage.local.getKeys().then((result : any)=> {console.log("Value is ",result);}); 54 | } 55 | 56 | async function storageSet(jsonData : string) { 57 | console.log("storageSet", jsonData); 58 | //chrome.storage.local.set(jsonData).then(() => { 59 | // console.log("Value is set"); 60 | //}); 61 | 62 | await browser.storage.local.set(jsonData); 63 | 64 | } 65 | 66 | async function storageGet(keyName : string) { 67 | 68 | //_result = {}; 69 | //await chrome.storage.local.get(keyName).then((result) => { 70 | // console.log("storageGet is ", keyName, JSON.stringify(result)); 71 | // _result = result; 72 | //}); 73 | 74 | const _result = await browser.storage.local.get(keyName); 75 | console.log(_result); 76 | //console.log(_result.currProxyPid); 77 | 78 | return _result; 79 | } 80 | export function clearStorage() { 81 | //localStorage.clear(); 82 | //chrome.storage.local.clear(); 83 | browser.storage.local.clear(); 84 | console.log("storageClear"); 85 | } 86 | 87 | export function storageRemove(keyName : string) { 88 | browser.storage.local.remove(keyName).then(() => { 89 | console.log("Value removed"); 90 | }); 91 | } 92 | 93 | 94 | 95 | 96 | export async function proxyConfigsSet(proxyConfigsMap: Map) { 97 | //proxyConfigsMap = new Map(); 98 | let json = JSON.stringify(Array.from(proxyConfigsMap)); 99 | await browser.storage.local.set({ "proxyConfigs": json }); 100 | console.log(proxyConfigsMap); 101 | console.log(JSON.stringify(Array.from(proxyConfigsMap))); 102 | } 103 | 104 | 105 | async function proxyConfigsGet() { 106 | const _result = await browser.storage.local.get("proxyConfigs"); 107 | console.log(_result); 108 | console.log(_result.proxyConfigs); 109 | let map = new Map(JSON.parse(_result.proxyConfigs)); 110 | return map; 111 | 112 | } 113 | 114 | export function proxyConfigsGetByCallback(callback: Function) { 115 | browser.storage.local.get(["proxyConfigs"]).then((result: any) => { 116 | console.log("GetValue is ", result); 117 | if (result.proxyConfigs == undefined) { 118 | console.log("GetValue is undefined"); 119 | callback(new Map()); 120 | } else { 121 | let map = new Map(JSON.parse(result.proxyConfigs)); 122 | console.log("map is ", map); 123 | callback(map); 124 | } 125 | 126 | }); 127 | 128 | } 129 | 130 | 131 | 132 | 133 | export function getDefaultConfigs() { 134 | let proxyConfigsMap = new Map(); 135 | 136 | let proxyConfig = { 137 | pid: "http-8080", 138 | name: "http-8080", 139 | color: "#4477bb", 140 | mode: "fixed_servers", 141 | rules: { 142 | singleProxy: { 143 | scheme: "http", 144 | host: "127.0.0.1", 145 | port: 8080, 146 | }, 147 | bypassList: [ 148 | "127.0.0.1", 149 | "::1", 150 | "localhost" 151 | ] 152 | } 153 | }; 154 | 155 | proxyConfigsMap.set(proxyConfig.pid, proxyConfig); 156 | 157 | proxyConfig = { 158 | pid: "socks5-1080", 159 | name: "socks5-1080", 160 | color: "#8169ff", 161 | mode: "fixed_servers", 162 | rules: { 163 | singleProxy: { 164 | scheme: "socks5", 165 | host: "127.0.0.1", 166 | port: 1080, 167 | }, 168 | bypassList: [ 169 | "127.0.0.1", 170 | "::1", 171 | "localhost" 172 | ] 173 | } 174 | }; 175 | 176 | proxyConfigsMap.set(proxyConfig.pid, proxyConfig); 177 | 178 | proxyConfig = { 179 | pid: "socks5-10808", 180 | name: "socks5-10808", 181 | color: "#d497ee", 182 | mode: "fixed_servers", 183 | rules: { 184 | singleProxy: { 185 | scheme: "socks5", 186 | host: "127.0.0.1", 187 | port: 10808, 188 | }, 189 | bypassList: [ 190 | "127.0.0.1", 191 | "::1", 192 | "localhost" 193 | ] 194 | } 195 | }; 196 | 197 | proxyConfigsMap.set(proxyConfig.pid, proxyConfig); 198 | 199 | proxyConfig = { 200 | pid: "socks5-7891", 201 | name: "socks5-7891", 202 | color: "#9117c5", 203 | mode: "fixed_servers", 204 | rules: { 205 | singleProxy: { 206 | scheme: "socks5", 207 | host: "127.0.0.1", 208 | port: 7891, 209 | }, 210 | bypassList: [ 211 | "127.0.0.1", 212 | "::1", 213 | "localhost" 214 | ] 215 | } 216 | }; 217 | 218 | proxyConfigsMap.set(proxyConfig.pid, proxyConfig); 219 | 220 | proxyConfig = { 221 | pid: "http-7890", 222 | name: "http-7890", 223 | color: "#0b4da4", 224 | mode: "fixed_servers", 225 | rules: { 226 | singleProxy: { 227 | scheme: "http", 228 | host: "127.0.0.1", 229 | port: 7890, 230 | }, 231 | bypassList: [ 232 | "127.0.0.1", 233 | "::1", 234 | "localhost" 235 | ] 236 | } 237 | }; 238 | 239 | proxyConfigsMap.set(proxyConfig.pid, proxyConfig); 240 | 241 | proxyConfig = { 242 | pid: "burp-8080", 243 | name: "burp-8080", 244 | color: "#55bb55", 245 | mode: "fixed_servers", 246 | rules: { 247 | singleProxy: { 248 | scheme: "http", 249 | host: "127.0.0.1", 250 | port: 8080, 251 | }, 252 | bypassList: [ 253 | "127.0.0.1", 254 | "::1", 255 | ".google.com", 256 | ".google-analytics.com", 257 | ".googleapis.com", 258 | "mcs.volceapplog.com", 259 | "mssdk.bytedance.com", 260 | "mcs.zijieapi.com", 261 | "mon.zijieapi.com" 262 | ] 263 | } 264 | }; 265 | 266 | proxyConfigsMap.set(proxyConfig.pid, proxyConfig); 267 | 268 | proxyConfig = { 269 | pid: "loop-8080", 270 | name: "loop-8080", 271 | color: "#8892AB", 272 | mode: "fixed_servers", 273 | rules: { 274 | singleProxy: { 275 | scheme: "http", 276 | host: "127.0.0.1", 277 | port: 8080, 278 | }, 279 | bypassList: [ 280 | "<-loopback>" 281 | ] 282 | } 283 | }; 284 | 285 | proxyConfigsMap.set(proxyConfig.pid, proxyConfig); 286 | 287 | return proxyConfigsMap; 288 | } 289 | 290 | -------------------------------------------------------------------------------- /wxt.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'wxt'; 2 | 3 | // See https://wxt.dev/api/config.html 4 | export default defineConfig({ 5 | extensionApi: 'chrome', 6 | modules: ['@wxt-dev/module-vue'], 7 | manifest: { 8 | manifest_version: 3, 9 | name: "__MSG_plugin_name__", 10 | description: "__MSG_plugin_desc__", 11 | default_locale: "en", 12 | version: "3.2.10", 13 | minimum_chrome_version: "88.0.0", 14 | author: "hicode0101@gmail.com", 15 | action: { 16 | default_popup: "popup.html", 17 | default_title: "__MSG_plugin_desc__" 18 | }, 19 | options_page: "options.html", 20 | permissions: [ 21 | "notifications", 22 | "proxy", 23 | "storage", 24 | "webRequest", 25 | "webRequestAuthProvider" 26 | ], 27 | }, 28 | }); 29 | --------------------------------------------------------------------------------