1import { getSiteData, getSiteNavData, checkMember, getSite301, getSitePolicyList } from './api/site.js' 2import { 3 getPathLang, 4 isLanguageDomain, 5 isLanguagePath, 6 isBot, 7 CustomError, 8 checkRedirect 9} from './function.js' 10import { getPageType } from '../config/index.js' 11import Page from './page.js' 12 13class Site { 14 /** 15 * æé 彿° 16 */ 17 constructor(ctx) { 18 this.ctx = ctx 19 this.siteData = {} 20 } 21 22 /** 23 * åå§å 24 */ 25 async init() { 26 if (this.ctx.state.postCtxState && this.ctx.state.postCtxState.site) { 27 return this.ctx.state.postCtxState.site 28 } 29 // æ ¹æ®ååè·åç«ç¹æ°æ® 30 const lang = getPathLang(this.ctx.path) 31 this.siteData = await getSiteData(this.ctx, { domain: this.ctx.state.local.domain, language_code: lang }) 32 // æ£æ¥éå®å 33 if (this.siteData.www_prefix) { 34 checkRedirect(this.ctx) 35 } 36 // åå§å模æ¿ç±»å 37 this.initTemplateType() 38 // æ ç«ç¹id 39 if (!this.siteData.site_id) { 40 throw new CustomError(404, 'æ æç«ç¹') 41 } 42 // æ£æ¥è£ ä¿®ç¶æ 43 this.checkUpdating() 44 // æ£æ¥ç½ç«è¿æ 45 this.checkDue() 46 // æ£æ¥é¡µé¢è·³è½¬ 47 await this.checkSite301() 48 49 // å¤çç»è®¡ä»£ç ,è¿æ¯æ§ç,å·²åºå¼,ææ¶ä¿ç 50 // this.initStatusCode() 51 // è·å头é¨å¯¼èª
52 await this.getSiteNav() 53 // è·åéç§æ¡æ¬¾å¼å¯ç¶æ 54 await this.initSiteTremsAndPolicyStatus() 55 // è·åç¤¾åª 56 // è¿åå¤çå¥½çæ°æ® 57 return this.siteData 58 } 59 60 /** åå§å模æ¿ç±»å */ 61 initTemplateType() { 62 const siteTypeToTemplateType = { 1: 'b2b', 11: 'b2b2c', 21: 'b2c', 51: 'b2b', 61: 'b2b2c' } 63 this.siteData.template_type = siteTypeToTemplateType[this.siteData.site_type || 1] 64 this.siteData.is_shop = this.siteData.template_type === 'b2b' ? false : true 65 } 66 67 /** 68 * è·åç½ç«è·³è½¬ 69 */ 70 async checkSite301() { 71 if (this.ctx.query.design || !this.siteData.site_id) { 72 return 73 } 74 75 if (this.siteData.language_code === 'not_found' && this.siteData.redirect_mode === 1) { 76 this.ctx.state.redirect(301, '/') 77 } 78 79 const site301 = await getSite301(this.ctx, { 80 site_id: this.siteData.site_id 81 }) 82 83 let lang_prefix = '' 84 let url_path = this.ctx.request.url 85 if (this.ctx.query.force && this.ctx.query.force === 'html') { 86 url_path = this.ctx.path 87 } 88 89 // 妿æ¯åç®å½å½¢å¼, è·¯å¾ä¸å»æå°è¯ç§åç¼ 90 if (this.siteData.domain_mode === 2 && this.siteData.lang_prefix !== '/') { 91 url_path = url_path.replace(this.siteData.lang_prefix, '') 92 lang_prefix = this.siteData.lang_prefix 93 } 94 95 let redirect_url = '' 96 if (site301.length > 0) { 97 let objExp = new RegExp(/^http/) 98 for (let i = 0; i < site301.length; i++) { 99 if (url_path === site301[i]['source_url']) { 100 if (objExp.test(site301[i]['dest_url']) === true) { 101 redirect_url = site301[i]['dest_url'] 102 } else { 103 redirect_url = lang_prefix + site301[i]['dest_url'] 104 } 105 } 106 107 if (this.ctx.query.force && this.ctx.query.force === 'html') { 108 if (redirect_url || site301[i]['source_url'].indexOf(url_path + '?') === 0) { 109 this.siteData.page_type = '301' 110 redirect_url = '' 111 } 112 } 113 } 114 } 115 if (redirect_url) { 116 this.ctx.request.url = '/' 117 this.ctx.state.redirect(301, redirect_url) 118 } 119 } 120 121 /** 122 * éå®å accept-language // zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 123 * ä» é¦æ¬¡è®¿é®ç«ç¹è¿è¡èªå¨éå®å 124 * å°è¯ç§äºçº§ååä¸éå®å 125 * å°è¯ç§urlä¸éå®å 126 * @param ctx 127 */ 128 static redirectClientLanguage(ctx) { 129 ctx.set('Vary', 'Accept-Language') 130 /** 131 * @type {string} cookie domain æå¤ä¿ç2级åå 132 */ 133 let domain = (ctx.state.local?.domain || '').replace(/^.*(?=\.([0-9a-z]+[.]){2}[0-9a-z]+$)/g, '') 134 const hasCookie = ctx.cookies.get('lf_language') 135 if (domain && ctx.host.indexOf(domain) >= 0) { 136 try { 137 const lf_language = ctx.state.site.language_code || 'en' 138 ctx.cookies.set('lf_language', lf_language, { 139 domain: (domain && domain[0] !== '.') ? `.${domain}` : domain, 140 path: '/', 141 httpOnly: true, 142 overwrite: true, 143 secure: false, 144 maxAge: 31536000000 145 }) 146 } catch (e) { 147 console.error(e) 148 } 149 } 150 if (!ctx.state.site?.automatic_language_switching 151 || hasCookie || isLanguageDomain(ctx.state.local?.domain || '') || isLanguagePath(ctx.request.url) 152 || isBot(ctx.request.header['user-agent'] || '') 153 ) { 154 return 155 } 156 try { 157 let accept_languages = (ctx.request.header['accept-language'] || '') 158 .toLowerCase() 159 .replace(/_/g, '-') 160 .replace(/q=[0-9.]+/gi, '') 161 .replace(/[;\s]/g, '') 162 .split(',').reduce((prev, lang) => { 163 if (lang === 'zh-hk') { 164 lang = 'zh-tw' 165 } 166 prev.push(lang) 167 if (lang.indexOf('-') > -1) { 168 const langCode = lang.slice(0, lang.indexOf('-')) 169 prev.push(langCode) 170 } 171 return prev 172 }, []) 173 174 ctx.state.accept_languages = [...new Set(accept_languages)] 175 176 } catch (e) { 177 console.error(e) 178 } 179 180 let language = null 181 182 for (let lang of ctx.state.accept_languages) { 183 language = ctx.state.site.language_list.find(language => language.code === lang) 184 if (language) { 185 break; 186 } 187 } 188 189 const current_url = ctx.host + ctx.request.url 190 191 if (language && language.href 192 && current_url !== language.href.replace(/^https?:\/{2}/i, '') 193 && ctx.state.site.language_code !== language.code) { 194 console.info(`303 redirect "${current_url}" -> "${language.href}"`) 195 ctx.state.redirect(303, language.href) 196 } 197 198 } 199 200 /** 201 * è·å头é¨å¯¼èª 202 */ 203 async getSiteNav() { 204 if (!this.siteData.site_id) return 205 const isDesign = this.ctx.query.design 206 const queryLanguageCode = isDesign ? (globalThis.window.location.search.match(new RegExp("(\\?|&)language_code=([^&]*)(&|$)")) || []) : [] 207 208 if (this.ctx.state.postCtxState && this.ctx.state.postCtxState.site) { 209 this.siteData.nav = this.ctx.state.postCtxState.site.nav 210 } else { 211 let [nav, nav_all] = await getSiteNavData(this.ctx, { 212 site_id: this.siteData.site_id, 213 language_code: queryLanguageCode[2] || this.siteData.language_code, 214 lang_prefix: this.siteData.lang_prefix 215 }) 216 this.siteData.nav = nav 217 this.siteData.nav_all = nav_all 218 } 219 if (this.siteData.is_show_home) { 220 this.siteData.nav.unshift({ title: this.ctx.state.$T ? this.ctx.state.$T.$t(this.siteData.language_code, this.siteData.home_name) : this.siteData.home_name, href: '/', children: [], bubble: {} }) 221 } 222 223 // æ£æ¥å½å页颿¯å¦æ¯éå®åé¡µé¢ 224 if (this.ctx.query.design) return false 225 226 let redirect_url = '' 227 let url_path = this.ctx.path 228 // 妿æ¯åç®å½å½¢å¼, è·¯å¾ä¸å»æå°è¯ç§åç¼ 229 if (this.siteData.domain_mode === 2 && this.siteData.lang_prefix !== '/') { 230 url_path = this.ctx.path.replace(this.siteData.lang_prefix, '') 231 } 232 url_path = url_path.replace('.html', '')
233 const calc = (arr) => { 234 if (arr.length > 0) { 235 arr.forEach(item => { 236 if (item.href.replace('.html', '') === url_path) { 237 redirect_url = item.jump_url 238 } 239 if (item.children.length > 0) calc(item.children) 240 }) 241 } 242 } 243 calc(this.siteData.nav) 244 245 if (redirect_url) { 246 if (this.ctx.query.force && this.ctx.query.force === 'html') { 247 this.siteData.page_type = '301' 248 } else { 249 this.ctx.state.redirect(303, globalThis.Server.getRinseHref(redirect_url, this.siteData)) 250 } 251 } 252 } 253 254 /** 255 * æ£æ¥è£ ä¿®ç¶æ 256 */ 257 checkUpdating() { 258 const isupdating = 'updating' === getPageType(this.ctx.path, this.siteData.site_type) 259 if (!this.ctx.query.design) { 260 let url = '' 261 if (isupdating && this.siteData.site_status !== 1) { 262 url = globalThis.Server.getRinseHref('/', this.siteData) 263 } else if (!isupdating && this.siteData.site_status === 1) { 264 url = globalThis.Server.getRinseHref('/updating.html', this.siteData) 265 } 266 if (url) { 267 if (this.ctx.query.force && this.ctx.query.force === 'html') { 268 this.siteData.page_type = '301' 269 } else { 270 this.ctx.state.redirect(303, url) 271 } 272 } 273 } 274 } 275 276 /** 277 * æ£æ¥ç½ç«è¿æ 278 */ 279 checkDue() { 280 const isdue = 'due' === getPageType(this.ctx.path, this.siteData.site_type) 281 if (!this.ctx.query.design) { 282 let url = '' 283 if (isdue && this.siteData.site_status !== 2) { 284 url = globalThis.Server.getRinseHref('/', this.siteData) 285 } else if (!isdue && this.siteData.site_status === 2) { 286 url = globalThis.Server.getRinseHref('/due.html', this.siteData) 287 } 288 if (url) { 289 if (this.ctx.query.force && this.ctx.query.force === 'html') { 290 this.siteData.page_type = '301' 291 } else { 292 this.ctx.state.redirect(303, url) 293 } 294 } 295 } 296 } 297 298 /** 299 * æ£æ¥ä¼ååè½ 300 */ 301 async checkMember(siteId) { 302 const memberData = await checkMember(this.ctx, { site_id: siteId || this.siteData.site_id }) 303 this.siteData.member_status = memberData.status 304 } 305 /** 306 * 307 * @param {*} code 308 * @returns 309 */ 310 static optimizeScript(code, ctx) { 311 if (ctx.state?.site_config?.server_config?.disable_partytown) { 312 if (ctx.state?.site_config?.ga_async) { 313 const matches = code.matchAll(/<script async src=\"https:\/\/www.googletagmanager.com\/gtag\/js\?id=([\d\-a-z]+)\">\s*<\/script>/gi) 314 ctx.state.site.ga_id = [] 315 for (const match of matches) { 316 ctx.state.site.ga_id.push(match[1]) 317 } 318 code = code.replace(/<script async src=\"https:\/\/www.googletagmanager.com\/gtag\/js\?id=([\d\-a-z]+)\">\s*<\/script>/gi, '') 319 } 320 return code 321 } 322 // ctx.state.site_config 323 if (code.indexOf('https://www.googletagmanager.com/') > -1) { 324 325 // fix by zhhnch 2023/09/18 ga script æ ç¾ä¸æ¯partytownçå ä¸ type="text/partytown" 326 code = code.replace(/<script(?=[^>]*src=['"][^'"]*www.googletagmanager.com[^'"]*['"][^>]*>)/gi, '<script __scrptwn__') 327 .replace(/<script([^>]*?>)(?=[^<>]*window.dataLayer)/gis, '<script __scrptwn__$1') 328 .replace(/<script([^>]*?>)(?=[^<>]*www.googletagmanager.com)/gis, '<script __scrptwn__$1') 329 .replace(/<script __scrptwn__(?!.*partytown)([^>]*)type="[^">]*"/gi, '<script __scrptwn__ $1 type="text/partytown"') 330 .replace(/<script __scrptwn__(?!.*partytown)(?!.*type=)(?=[^>]*)/gi, '<script __scrptwn__ type="text/partytown"') 331 .replace(/<script __scrptwn__\s*/gi, '<script ') 332 333 } 334 335 return code 336 } 337 static initNewStatusCode(siteData, page, ctx) { 338 const { page_url } = ctx.state.page 339 const { page_type } = page 340 const { path } = ctx 341 // æ§çå表 342 // let code_types = [ 343 // 'ga', 344 // 'fbpixel', 345 // 'binguet', 346 // 'yahoodot', 347 // 'pinterestpixel', 348 // 'criteopixel', 349 // 'tiktokpixel', 350 // 'snapchatpixel', 351 // 'thirdchat', 352 // 'inquiry', 353 // 'cart', 354 // 'checkout', 355 // 'complete', 356 // 'productlist', 357 // 'productdetail', 358 // 'search', 359 // 'index', 360 // 'other' 361 // ] 362
363 // æ°çå表,ä¸åå°çç»è®¡ä»£ç ä¿æä¸è´ 364 let code_types = [ 365 'ga', // GAç»è®¡ 366 // 'verification', // æç´¢å¼æç«é¿éªè¯,æ¤é¡¹ä¸è¿å ¥å¾ªç¯,éè¦åç¬å¤ç 367 'inquiry', // 询çè·è¸ª 368 'fbpixel', // FBåç´ 369 'binguet', // Bing UET 370 'yahoodot', // Yahoo Dot 371 'pinterestpixel', // Pintereståç´ 372 'criteopixel', // Criteoåç´ 373 'tiktokpixel', // TikTokåç´ 374 'snapchatpixel', // Snapchatåç´ 375 'thirdchat', // ç¬¬ä¸æ¹è天 376 'meta', // å æ ç¾ 377 'style', // æ ·å¼ä»£ç 378 'customstyle' // 个æ§å代ç 379 ] 380 let head_code = '' // å¤´é¨ 381 let header_code = '' 382 let bottom_code = '' 383 let header_code_other = '' 384 let bottom_code_other = '' 385 let header_code_ai_page = '' 386 let bottom_code_ai_page = '' 387 let transfer_product_id = page.transfer_product_id 388 code_types.forEach(codeType => { 389 let curCodeType = siteData['code_' + codeType] 390 if (JSON.stringify(curCodeType) !== '{}') { 391 if (codeType === 'ga') { 392 page.effect['partytown_' + (curCodeType.hasOwnProperty('1') ? 'header' : 'bottom')] = { 'forward': ['dataLayer.push'] } 393 } 394 395 for (const position in curCodeType) { 396 curCodeType[position].forEach(item => { 397 let code = item.code 398 if (codeType === 'ga') { 399 code = '' 400 if (!item.code.includes('consent')) { 401 code += `<script> 402 window.dataLayer = window.dataLayer || []; 403 function gtag(){dataLayer.push(arguments);} 404 gtag('consent', 'default', { 405 'ad_storage': 'granted', 406 'ad_user_data': 'granted', 407 'ad_personalization': 'granted', 408 'analytics_storage': 'granted' 409 }); 410 </script>` 411 } 412 code += Site.optimizeScript(item.code, ctx) 413 } 414 let isAllPage = item.page_type === 'all' 415 let isSpecifiedPage = item.page_type === page_type || (item.url_path && page_url.replace('.html', '') === item.url_path) //å½åé¡µé¢ 416 let isNoLandingPage = item.page_type === 'no_landing_page' && page_type !== 'landing_page' 417 let isDetailPage = false 418 // 妿æ¯è¯¦æ 页,åºåå°åºæ¯'æå¨å建'è¿æ¯'åºéºæ¬å®¶',è¿è¦åºåæ¯äº§å详æ ,è§é¢è¯¦æ è¿æ¯æç« 详æ (ææ¶åªæè¿ä¸ç§) 419 if (item.source_apply_type !== 0 && item.page_type.includes('product_detail') && (item.page_type === page_type)) { 420 isSpecifiedPage = false 421 isDetailPage = (transfer_product_id && item.source_apply_type === 2) || (!transfer_product_id && item.source_apply_type === 1) 422 } 423 if (isAllPage) { 424 if (codeType === 'meta') { 425 head_code += code 426 } else { 427 position === '1' ? header_code_other += code : bottom_code_other += code 428 } 429 const aiPageFilter = ['fbpixel', 'binguet', 'yahoodot', 'pinterestpixel', 'criteopixel', 'tiktokpixel', 'snapchatpixel', 'thirdchat'] 430 if (ctx.state.site_config.ga_filter && ctx.state.site_config.ga_filter == 'ai_page') { 431 aiPageFilter.push('ga') 432 } 433 if (!aiPageFilter.includes(codeType)) { 434 position === '1' ? header_code_ai_page += code : bottom_code_ai_page += code 435 } 436 } else if (isSpecifiedPage || isNoLandingPage || isDetailPage) { 437 if (codeType === 'meta') { 438 head_code += code 439 } else { 440 position === '1' ? header_code += code : bottom_code += code 441 } 442 } 443 }); 444 } 445 } 446 }) 447 let code_verification = '' 448 if (JSON.stringify(siteData.code_verification) !== '{}') { 449 siteData.code_verification[1].forEach(item => { 450 code_verification += item.code 451 }) 452 } 453 454 siteData.code_verification = code_verification 455 siteData.header_code = header_code 456 siteData.head_code = head_code 457 siteData.bottom_code = bottom_code 458 siteData.header_code_other = header_code_other 459 siteData.bottom_code_other = bottom_code_other 460 siteData.header_code_ai_page = header_code_ai_page 461 siteData.bottom_code_ai_page = bottom_code_ai_page 462 } 463 /** è·åéç§æ¡æ¬¾å¼å¯ç¶æ */
464 async initSiteTremsAndPolicyStatus() { 465 if (!this.siteData.site_id) return 466 const tremsUrl = 'terms_and_conditions' 467 const privacyUrl = 'privacy_policy' 468 const page_url = `${tremsUrl},${privacyUrl}` 469 const params = { site_id: this.siteData.site_id, page_url } 470 const list = await getSitePolicyList(this.ctx, params) 471 // const list = [{page_url: 'privacy_policy', status: 0}] 472 // const list = [{page_url: 'terms_and_conditions', status: 1}] 473 // const list = [] 474 if (list.length > 0) { 475 const trems = list.find(p => p.page_url === tremsUrl) || { status: 0 } 476 const privacy = list.find(p => p.page_url === privacyUrl) || { status: 1 } 477 this.siteData.is_show_trems = trems.status === 1 478 this.siteData.is_show_privacy = privacy.status === 1 479 } else { 480 this.siteData.is_show_trems = 0 481 this.siteData.is_show_privacy = 1 482 } 483 } 484 485 /** ç»è®¡ä»£ç */ 486 // initStatusCode() { 487 // if (!this.ctx.query.design) { 488 // this.siteData.code_verification = this._getStatusCode(this.siteData.code_verification, 1) 489 490 // this.siteData.code_robots = this._getStatusCode(this.siteData.code_robots, 1) 491 // this.siteData.code_fbmessenger = this.siteData.code_fbmessenger.code || '' 492 493 // this.siteData.code_ga_header = this._getStatusCode(this.siteData.code_ga, 1) 494 // this.siteData.code_fbpixel_header = this._getStatusCode(this.siteData.code_fbpixel, 1) 495 // this.siteData.code_binguet_header = this._getStatusCode(this.siteData.code_binguet, 1) 496 // this.siteData.code_yahoodot_header = this._getStatusCode(this.siteData.code_yahoodot, 1) 497 // this.siteData.code_pinterestpixel_header = this._getStatusCode(this.siteData.code_pinterestpixel, 1) 498 // this.siteData.code_criteopixel_header = this._getStatusCode(this.siteData.code_criteopixel, 1) 499 // this.siteData.code_tiktokpixel_header = this._getStatusCode(this.siteData.code_tiktokpixel, 1) 500 // this.siteData.code_snapchatpixel_header = this._getStatusCode(this.siteData.code_snapchatpixel, 1) 501 // this.siteData.code_thirdchat_header = this._getStatusCode(this.siteData.code_thirdchat, 1) 502 // this.siteData.code_inquiry_header = this._getStatusCode(this.siteData.code_inquiry, 1) 503 // this.siteData.code_cart_header = this._getStatusCode(this.siteData.code_cart, 1) 504 // this.siteData.code_checkout_header = this._getStatusCode(this.siteData.code_checkout, 1) 505 // this.siteData.code_complete_header = this._getStatusCode(this.siteData.code_complete, 1) 506 // this.siteData.code_productlist_header = this._getStatusCode(this.siteData.code_productlist, 1) 507 // this.siteData.code_productdetail_header = this._getStatusCode(this.siteData.code_productdetail, 1) 508 // this.siteData.code_search_header = this._getStatusCode(this.siteData.code_search, 1) 509 // this.siteData.code_other_header = this._getStatusCode(this.siteData.code_other, 1) 510 // this.siteData.code_index_header = this._getStatusCode(this.siteData.code_index, 1) 511 512 // this.siteData.code_ga_bottom = this._getStatusCode(this.siteData.code_ga, 2) 513 // this.siteData.code_fbpixel_bottom = this._getStatusCode(this.siteData.code_fbpixel, 2) 514 // this.siteData.code_binguet_bottom = this._getStatusCode(this.siteData.code_binguet, 2) 515 // this.siteData.code_yahoodot_bottom = this._getStatusCode(this.siteData.code_yahoodot, 2) 516 // this.siteData.code_pinterestpixel_bottom = this._getStatusCode(this.siteData.code_pinterestpixel, 2) 517 // this.siteData.code_criteopixel_bottom = this._getStatusCode(this.siteData.code_criteopixel, 2) 518 // this.siteData.code_tiktokpixel_bottom = this._getStatusCode(this.siteData.code_tiktokpixel, 2) 519 // this.siteData.code_snapchatpixel_bottom = this._getStatusCode(this.siteData.code_snapchatpixel, 2) 520 // this.siteData.code_thirdchat_bottom = this._getStatusCode(this.siteData.code_thirdchat, 2) 521 // this.siteData.code_inquiry_bottom = this._getStatusCode(this.siteData.code_inquiry, 2) 522 // this.siteData.code_cart_bottom = this._getStatusCode(this.siteData.code_cart, 2) 523 // this.siteData.code_checkout_bottom = this._getStatusCode(this.siteData.code_checkout, 2) 524 // this.siteData.code_complete_bottom = this._getStatusCode(this.siteData.code_complete, 2) 525 // this.siteData.code_productlist_bottom = this._getStatusCode(this.siteData.code_productlist, 2) 526 // this.siteData.code_productdetail_bottom = this._getStatusCode(this.siteData.code_productdetail, 2) 527 // this.siteData.code_search_bottom = this._getStatusCode(this.siteData.code_search, 2) 528 // this.siteData.code_index_bottom = this._getStatusCode(this.siteData.code_index, 2) 529 // this.siteData.code_other_bottom = this._getStatusCode(this.siteData.code_other, 2) 530 // } 531 // } 532 533 534 /** è·åç»è®¡ä»£ç */ 535 // _getStatusCode(codeInfo, attr) { 536 // const info = codeInfo[attr] || [] 537 // return info.code || "" 538 // } 539} 540 541export default Site
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.