@@ -8,6 +8,9 @@ const execute = Shell.execute
8
8
const execFile = Shell . execFile
9
9
const log = require ( '../../../utils/util.log' )
10
10
const extraPath = require ( '../extra-path/index' )
11
+ const fs = require ( 'fs' )
12
+ const path = require ( 'path' )
13
+ const request = require ( 'request' )
11
14
12
15
let config = null
13
16
function loadConfig ( ) {
@@ -47,6 +50,137 @@ async function _winUnsetProxy (exec, setEnv) {
47
50
}
48
51
}
49
52
53
+ function getChinaDomainAllowListTmpFilePath ( ) {
54
+ return path . join ( config . get ( ) . server . setting . userBasePath , '/china-domain-allowlist.txt' )
55
+ }
56
+
57
+ async function downloadChinaDomainAllowListAsync ( ) {
58
+ loadConfig ( )
59
+
60
+ const remoteFileUrl = config . get ( ) . proxy . remoteChinaDomainAllowListFileUrl
61
+ log . info ( '开始下载远程 china-domain-allowlist.txt 文件:' , remoteFileUrl )
62
+ request ( remoteFileUrl , ( error , response , body ) => {
63
+ if ( error ) {
64
+ log . error ( '下载远程 china-domain-allowlist.txt 文件失败, error:' , error , ', response:' , response , ', body:' , body )
65
+ return
66
+ }
67
+ if ( response && response . statusCode === 200 ) {
68
+ if ( body == null || body . length < 100 ) {
69
+ log . warn ( '下载远程 china-domain-allowlist.txt 文件成功,但内容为空或内容太短,判断为无效的 china-domain-allowlist.txt 文件:' , remoteFileUrl , ', body:' , body )
70
+ return
71
+ } else {
72
+ log . info ( '下载远程 china-domain-allowlist.txt 文件成功:' , remoteFileUrl )
73
+ }
74
+
75
+ let fileTxt = body
76
+ try {
77
+ if ( fileTxt . indexOf ( '*.' ) < 0 ) {
78
+ fileTxt = Buffer . from ( fileTxt , 'base64' ) . toString ( 'utf8' )
79
+ // log.debug('解析 base64 后的 china-domain-allowlist:', fileTxt)
80
+ }
81
+ } catch ( e ) {
82
+ if ( fileTxt . indexOf ( '*.' ) < 0 ) {
83
+ log . error ( `远程 china-domain-allowlist.txt 文件内容即不是base64格式,也不是要求的格式,url: ${ remoteFileUrl } ,body: ${ body } ` )
84
+ return
85
+ }
86
+ }
87
+
88
+ // 保存到本地
89
+ saveChinaDomainAllowListFile ( fileTxt )
90
+ } else {
91
+ log . error ( '下载远程 china-domain-allowlist.txt 文件失败, response:' , response , ', body:' , body )
92
+ }
93
+ } )
94
+ }
95
+
96
+ function loadLastModifiedTimeFromTxt ( fileTxt ) {
97
+ const matched = fileTxt . match ( / (?< = ; U p d a t e D a t e : ) [ ^ \r \n ] + / g)
98
+ if ( matched && matched . length > 0 ) {
99
+ try {
100
+ return new Date ( matched [ 0 ] )
101
+ } catch ( ignore ) {
102
+ return null
103
+ }
104
+ }
105
+ }
106
+
107
+ // 保存 中国域名白名单 内容到 `~/china-domain-allowlist.txt.txt` 文件中
108
+ function saveChinaDomainAllowListFile ( fileTxt ) {
109
+ const filePath = getChinaDomainAllowListTmpFilePath ( )
110
+ fs . writeFileSync ( filePath , fileTxt . replaceAll ( / \r \n ? / g, '\n' ) )
111
+ log . info ( '保存 china-domain-allowlist.txt 文件成功:' , filePath )
112
+
113
+ // 尝试解析和修改 china-domain-allowlist.txt 文件时间
114
+ const lastModifiedTime = loadLastModifiedTimeFromTxt ( fileTxt )
115
+ if ( lastModifiedTime ) {
116
+ fs . stat ( filePath , ( err , stats ) => {
117
+ if ( err ) {
118
+ log . error ( '修改 china-domain-allowlist.txt 文件时间失败:' , err )
119
+ return
120
+ }
121
+
122
+ // 修改文件的访问时间和修改时间为当前时间
123
+ fs . utimes ( filePath , lastModifiedTime , lastModifiedTime , ( utimesErr ) => {
124
+ if ( utimesErr ) {
125
+ log . error ( '修改 china-domain-allowlist.txt 文件时间失败:' , utimesErr )
126
+ } else {
127
+ log . info ( `'${ filePath } ' 文件的修改时间已更新为其最近更新时间 '${ formatDate ( lastModifiedTime ) } '` )
128
+ }
129
+ } )
130
+ } )
131
+ }
132
+
133
+ return filePath
134
+ }
135
+
136
+ function formatDate ( date ) {
137
+ const year = date . getFullYear ( )
138
+ const month = ( date . getMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' )
139
+ const day = date . getDate ( ) . toString ( ) . padStart ( 2 , '0' )
140
+ const hours = date . getHours ( ) . toString ( ) . padStart ( 2 , '0' )
141
+ const minutes = date . getMinutes ( ) . toString ( ) . padStart ( 2 , '0' )
142
+ const seconds = date . getSeconds ( ) . toString ( ) . padStart ( 2 , '0' )
143
+ return `${ year } -${ month } -${ day } ${ hours } :${ minutes } :${ seconds } `
144
+ }
145
+
146
+ function getChinaDomainAllowList ( ) {
147
+ loadConfig ( )
148
+
149
+ if ( ! config . get ( ) . proxy . excludeChinaDomainAllowList ) {
150
+ return null
151
+ }
152
+
153
+ // 判断是否需要自动更新中国域名
154
+ let fileAbsolutePath = config . get ( ) . proxy . chinaDomainAllowListFileAbsolutePath
155
+ if ( ! fileAbsolutePath && config . get ( ) . proxy . autoUpdateChinaDomainAllowList ) {
156
+ // 异步下载,下载成功后,下次系统代理生效
157
+ downloadChinaDomainAllowListAsync ( ) . then ( )
158
+ }
159
+
160
+ // 加载本地文件
161
+ if ( ! fileAbsolutePath ) {
162
+ const tmpFilePath = getChinaDomainAllowListTmpFilePath ( )
163
+ if ( fs . existsSync ( tmpFilePath ) ) {
164
+ // 如果临时文件已存在,则使用临时文件
165
+ fileAbsolutePath = tmpFilePath
166
+ log . info ( '读取已下载的 china-domain-allowlist.txt 文件:' , fileAbsolutePath )
167
+ } else {
168
+ // 如果临时文件不存在,则使用内置文件
169
+ fileAbsolutePath = path . join ( __dirname , '../../gui/' , config . get ( ) . proxy . chinaDomainAllowListFilePath )
170
+ log . info ( '读取内置的 china-domain-allowlist.txt 文件:' , fileAbsolutePath )
171
+ }
172
+ } else {
173
+ log . info ( '读取自定义路径的 china-domain-allowlist.txt 文件:' , fileAbsolutePath )
174
+ }
175
+
176
+ try {
177
+ return fs . readFileSync ( fileAbsolutePath ) . toString ( )
178
+ } catch ( e ) {
179
+ log . error ( '读取 china-domain-allowlist.txt 文件失败:' , fileAbsolutePath )
180
+ return null
181
+ }
182
+ }
183
+
50
184
async function _winSetProxy ( exec , ip , port , setEnv ) {
51
185
// 延迟加载config
52
186
loadConfig ( )
@@ -58,6 +192,24 @@ async function _winSetProxy (exec, ip, port, setEnv) {
58
192
}
59
193
}
60
194
195
+ // 排除中国域名
196
+ if ( config . get ( ) . proxy . excludeChinaDomainAllowList ) {
197
+ try {
198
+ let chinaDomainAllowList = getChinaDomainAllowList ( )
199
+ if ( chinaDomainAllowList ) {
200
+ chinaDomainAllowList = ( chinaDomainAllowList + '\n' ) . replaceAll ( / [ \r \n ] + / g, '\n' ) . replaceAll ( / [ ^ \n ] * [ ^ * . a - z A - Z \d - \n ] + [ ^ \n ] * \r ? \n / g, '' ) . replaceAll ( / \s * \n + \s * / g, ';' )
201
+ if ( chinaDomainAllowList ) {
202
+ excludeIpStr += chinaDomainAllowList
203
+ log . info ( '系统代理排除列表拼接中国域名' )
204
+ } else {
205
+ log . info ( '中国域名为空,不进行系统代理排除列表拼接中国域名' )
206
+ }
207
+ }
208
+ } catch ( e ) {
209
+ log . error ( '系统代理排除列表拼接中国域名失败:' , e )
210
+ }
211
+ }
212
+
61
213
const proxyPath = extraPath . getProxyExePath ( )
62
214
const execFun = 'global'
63
215
0 commit comments