11import type { SsConfig } from '../types' ;
2+ import { base64Decode , base64Encode } from 'cloudflare-tools' ;
23import { Faker } from '../../../shared/faker' ;
34import { PsUtil } from '../../../shared/ps' ;
45
@@ -35,8 +36,9 @@ export class SsParser extends Faker {
3536 * @param {string } v
3637 */
3738 private setOriginConfig ( v : string ) : void {
38- this . #originLink = v ;
39- this . #originConfig = new URL ( v ) ;
39+ const _v = this . toStandard ( v ) ;
40+ this . #originLink = _v ;
41+ this . #originConfig = new URL ( _v ) ;
4042 this . #originPs = this . #originConfig. hash ?? '' ;
4143 }
4244
@@ -56,13 +58,13 @@ export class SsParser extends Faker {
5658 * @param {string } v
5759 */
5860 private setConfuseConfig ( v : string ) : void {
59- this . #confuseConfig = new URL ( v ) ;
61+ this . #confuseConfig = new URL ( this . toStandard ( v ) ) ;
6062 this . #confuseConfig. username = this . getUsername ( ) ;
6163 this . #confuseConfig. host = this . getHost ( ) ;
6264 this . #confuseConfig. hostname = this . getHostName ( ) ;
6365 this . #confuseConfig. port = this . getPort ( ) ;
6466 this . #confuseConfig. hash = PsUtil . setPs ( this . #originPs, this . #confusePs) ;
65- this . #confuseLink = `ss://${ decodeURIComponent ( this . #originConfig. username ! ) } @${ this . #confuseConfig. host } :${ this . #confuseConfig. port } ${ this . #confuseConfig. search } #${ this . #confuseConfig. hash } ` ;
67+ this . #confuseLink = `ss://${ decodeURIComponent ( this . #originConfig. username ! ) } @${ this . #confuseConfig. hostname } :${ this . #confuseConfig. port } ${ this . #confuseConfig. search } #${ this . #confuseConfig. hash } ` ;
6668 }
6769
6870 public restoreClash ( proxy : Record < string , string | number > , ps : string ) : Record < string , string | number > {
@@ -124,5 +126,87 @@ export class SsParser extends Faker {
124126 get confuseConfig ( ) : Partial < SsConfig > {
125127 return this . #confuseConfig;
126128 }
129+
130+ /**
131+ * @description 将传统格式 (SIP002) 的链接转换为标准新版 URI 格式的链接
132+ * @param {string } sipUrl SIP002 格式链接
133+ * @returns {string } 标准新版 URI 格式链接
134+ */
135+ private toStandard ( sipUrl : string ) : string {
136+ // 检查是否为传统 SIP002 格式: ss://[base64(method:password@server:port)]#remark
137+ // 先提取 fragment (备注部分)
138+ const fragmentMatch = sipUrl . match ( / # ( .* ) $ / ) ;
139+ const fragment = fragmentMatch ? `#${ fragmentMatch [ 1 ] } ` : '' ;
140+
141+ // 移除 fragment 部分,只保留主体
142+ const mainUrl = sipUrl . replace ( / # .* $ / , '' ) ;
143+
144+ // 检查是否为 ss:// 开头
145+ if ( ! mainUrl . startsWith ( 'ss://' ) ) {
146+ return sipUrl ;
147+ }
148+
149+ // 提取 base64 编码部分
150+ const base64Part = mainUrl . substring ( 5 ) ; // 移除 'ss://'
151+
152+ // 检查是否包含 @ 符号,如果包含说明已经是标准格式
153+ if ( base64Part . includes ( '@' ) ) {
154+ return sipUrl ;
155+ }
156+
157+ try {
158+ // 解码 base64 获取完整配置: method:password@server:port
159+ const decodedConfig = base64Decode ( base64Part ) ;
160+
161+ // 解析格式: method:password@server:port
162+ const atIndex = decodedConfig . lastIndexOf ( '@' ) ;
163+ if ( atIndex === - 1 ) {
164+ throw new Error ( 'Invalid SIP002 format: missing @ separator' ) ;
165+ }
166+
167+ const userInfo = decodedConfig . substring ( 0 , atIndex ) ; // method:password
168+ const serverPart = decodedConfig . substring ( atIndex + 1 ) ; // server:port
169+
170+ const colonIndex = userInfo . indexOf ( ':' ) ;
171+ if ( colonIndex === - 1 ) {
172+ throw new Error ( 'Invalid user info: missing colon separator' ) ;
173+ }
174+
175+ const method = userInfo . substring ( 0 , colonIndex ) ;
176+ const password = userInfo . substring ( colonIndex + 1 ) ;
177+
178+ // 解析服务器和端口
179+ const lastColonIndex = serverPart . lastIndexOf ( ':' ) ;
180+ if ( lastColonIndex === - 1 ) {
181+ throw new Error ( 'Invalid server info: missing port' ) ;
182+ }
183+
184+ const server = serverPart . substring ( 0 , lastColonIndex ) ;
185+ const port = serverPart . substring ( lastColonIndex + 1 ) ;
186+
187+ if ( ! method || ! password || ! server || ! port ) {
188+ throw new Error ( 'Invalid format: missing required fields' ) ;
189+ }
190+
191+ // 构造标准新版 URI 格式的 userinfo (method:password)
192+ const newUserInfo = `${ method } :${ password } ` ;
193+ const encodedNewUserInfo = base64Encode ( newUserInfo ) ;
194+
195+ // 构造标准新版 URI 格式
196+ let newUrl = `ss://${ encodedNewUserInfo } @${ server } :${ port } ` ;
197+
198+ // 添加默认参数 type=tcp
199+ newUrl += '?type=tcp' ;
200+
201+ // 添加 fragment (备注)
202+ if ( fragment ) {
203+ newUrl += fragment ;
204+ }
205+
206+ return newUrl ;
207+ } catch {
208+ return sipUrl ;
209+ }
210+ }
127211}
128212
0 commit comments