ts-glitter 14.2.4 → 14.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/lowcode/Entry.js +1 -1
  2. package/lowcode/Entry.ts +1 -1
  3. package/lowcode/backend-manager/bg-product.js +2 -1
  4. package/lowcode/backend-manager/bg-product.ts +2 -1
  5. package/lowcode/cms-plugin/filter-options.js +1 -3
  6. package/lowcode/cms-plugin/filter-options.ts +1 -3
  7. package/lowcode/cms-plugin/module/product-excel.js +537 -0
  8. package/lowcode/cms-plugin/module/product-excel.ts +652 -0
  9. package/lowcode/cms-plugin/shopping-allowance-manager.js +12 -5
  10. package/lowcode/cms-plugin/shopping-allowance-manager.ts +12 -5
  11. package/lowcode/cms-plugin/shopping-discount-setting.js +1 -1
  12. package/lowcode/cms-plugin/shopping-discount-setting.ts +1 -1
  13. package/lowcode/cms-plugin/shopping-finance-setting.js +0 -7
  14. package/lowcode/cms-plugin/shopping-finance-setting.ts +8 -8
  15. package/lowcode/cms-plugin/shopping-invoice-manager.js +5 -1
  16. package/lowcode/cms-plugin/shopping-invoice-manager.ts +5 -2
  17. package/lowcode/cms-plugin/shopping-order-manager.js +8 -0
  18. package/lowcode/cms-plugin/shopping-order-manager.ts +8 -0
  19. package/lowcode/cms-plugin/shopping-product-setting.js +61 -638
  20. package/lowcode/cms-plugin/shopping-product-setting.ts +67 -790
  21. package/lowcode/jspage/main.ts +0 -1
  22. package/lowcode/public-components/blogs/blogs-01.js +4 -0
  23. package/lowcode/public-components/checkout/index.js +967 -209
  24. package/lowcode/public-components/checkout/index.ts +977 -214
  25. package/lowcode/public-components/checkout/index_.js +1724 -412
  26. package/lowcode/public-components/checkout/index_.ts +2032 -704
  27. package/lowcode/public-components/product/pd-class.js +68 -13
  28. package/lowcode/public-components/product/pd-class.ts +74 -14
  29. package/lowcode/public-components/product/product-detail.js +1 -1
  30. package/lowcode/public-components/product/product-detail.ts +7 -3
  31. package/lowcode/public-components/product/product-list.js +55 -0
  32. package/lowcode/public-components/product/product-list.ts +62 -1
  33. package/lowcode/public-components/public/ad.js +14 -0
  34. package/lowcode/public-components/public/ad.ts +15 -0
  35. package/package.json +7 -4
  36. package/src/api-public/controllers/ai-chat.js.map +1 -1
  37. package/src/api-public/controllers/app-release.js.map +1 -1
  38. package/src/api-public/controllers/article.js.map +1 -1
  39. package/src/api-public/controllers/fcm.js.map +1 -1
  40. package/src/api-public/controllers/post.js.map +1 -1
  41. package/src/api-public/controllers/rebate.js.map +1 -1
  42. package/src/api-public/controllers/shop.js +16 -0
  43. package/src/api-public/controllers/shop.js.map +1 -1
  44. package/src/api-public/controllers/shop.ts +27 -3
  45. package/src/api-public/controllers/user.js.map +1 -1
  46. package/src/api-public/services/EcInvoice.js +18 -12
  47. package/src/api-public/services/EcInvoice.js.map +1 -1
  48. package/src/api-public/services/EcInvoice.ts +19 -14
  49. package/src/api-public/services/ai-robot.d.ts +0 -1
  50. package/src/api-public/services/ai-robot.js.map +1 -1
  51. package/src/api-public/services/auto-send-email.js.map +1 -1
  52. package/src/api-public/services/chat.js.map +1 -1
  53. package/src/api-public/services/custom-code.js.map +1 -1
  54. package/src/api-public/services/delivery.js.map +1 -1
  55. package/src/api-public/services/ezpay/tool.d.ts +1 -0
  56. package/src/api-public/services/fb-message.js.map +1 -1
  57. package/src/api-public/services/financial-service.d.ts +49 -1
  58. package/src/api-public/services/financial-service.js +226 -1
  59. package/src/api-public/services/financial-service.js.map +1 -1
  60. package/src/api-public/services/financial-service.ts +282 -1
  61. package/src/api-public/services/line-message.js.map +1 -1
  62. package/src/api-public/services/monitor.d.ts +1 -0
  63. package/src/api-public/services/post.js.map +1 -1
  64. package/src/api-public/services/rebate.js.map +1 -1
  65. package/src/api-public/services/schedule.js.map +1 -1
  66. package/src/api-public/services/share-permission.js.map +1 -1
  67. package/src/api-public/services/shopping.d.ts +3 -1
  68. package/src/api-public/services/shopping.js +57 -58
  69. package/src/api-public/services/shopping.js.map +1 -1
  70. package/src/api-public/services/shopping.ts +77 -76
  71. package/src/api-public/services/sms.js.map +1 -1
  72. package/src/api-public/services/user.js.map +1 -1
  73. package/src/api-public/utils/ut-permission.d.ts +1 -0
  74. package/src/app-project/serverless/src/modules/database.d.ts +1 -1
  75. package/src/app-project/serverless/src/modules/redis.d.ts +1 -1
  76. package/src/config.d.ts +1 -1
  77. package/src/config.js.map +1 -1
  78. package/src/controllers/ai.js.map +1 -1
  79. package/src/controllers/filemanager.js.map +1 -1
  80. package/src/firebase/message.js +2 -1
  81. package/src/firebase/message.js.map +1 -1
  82. package/src/helper/app_creater.js +2 -1
  83. package/src/helper/app_creater.js.map +1 -1
  84. package/src/helper/glitter-util.js.map +3 -3
  85. package/src/index.js +111 -92
  86. package/src/index.js.map +1 -5
  87. package/src/index.ts +424 -397
  88. package/src/lambda/interface.d.ts +1 -1
  89. package/src/lambda/interface.js +2 -2
  90. package/src/lambda/interface.js.map +1 -1
  91. package/src/modules/AWSLib.js +3 -2
  92. package/src/modules/AWSLib.js.map +1 -1
  93. package/src/modules/database.d.ts +1 -1
  94. package/src/modules/redis.d.ts +1 -1
  95. package/src/modules/tool.js +2 -1
  96. package/src/modules/tool.js.map +1 -1
  97. package/src/run.js +2 -1
  98. package/src/run.js.map +1 -1
  99. package/src/services/backend-service.js.map +1 -1
  100. package/src/services/create-instance.js +4 -3
  101. package/src/services/create-instance.js.map +1 -1
  102. package/src/services/saas-table-check.js +2 -2
  103. package/src/services/saas-table-check.js.map +1 -1
  104. package/src/services/ses.js +2 -1
  105. package/src/services/ses.js.map +1 -1
  106. package/src/services/tool.js +3 -2
  107. package/src/services/tool.js.map +1 -1
package/src/index.ts CHANGED
@@ -170,241 +170,242 @@ export async function createAPP(dd: any) {
170
170
  Live_source.liveAPP.push(dd.appName);
171
171
  Schedule.app.push(dd.appName);
172
172
  const file_path = path.resolve(__dirname, '../lowcode');
173
- return await GlitterUtil.set_frontend_v2(app, [
174
- '/' + encodeURI(dd.appName) + '/*',
175
- '/' + encodeURI(dd.appName)
176
- ].map((rout)=>{
177
- return {
178
- rout: rout,
179
- path: file_path,
180
- app_name: dd.appName,
181
- root_path: '/' + encodeURI(dd.appName) + '/',
182
- seoManager: async (req) => {
183
- try {
184
- if (req.query.state === 'google_login') {
185
- req.query.page = 'login';
186
- }
187
- let appName = dd.appName;
188
- if (req.query.appName) {
189
- appName = req.query.appName;
190
- }
191
- req.headers['g-app'] = appName;
192
- const start = new Date().getTime();
193
- //插入瀏覽紀錄
194
- Monitor.insertHistory({
195
- req_type: 'file',
196
- req: req,
197
- });
198
- console.log(`insertHistory==>`, (new Date().getTime() - start) / 1000);
199
- //確認資料庫是否存在
200
- await ApiPublic.createScheme(appName);
201
- console.log(`createScheme==>`, (new Date().getTime() - start) / 1000);
202
- //確認SAAS用戶資訊
203
- const brandAndMemberType = await App.checkBrandAndMemberType(appName);
204
- console.log(`brandAndMemberType==>`, (new Date().getTime() - start) / 1000);
205
- //取得頁面資訊
206
- let data = await Seo.getPageInfo(appName, req.query.page as string);
207
- //首頁SEO
208
- let home_page_data = await (async () => {
209
- if (data && data.config) {
210
- return await Seo.getPageInfo(appName, data.config.homePage);
211
- } else {
212
- return await Seo.getPageInfo(appName, 'index');
173
+ return await GlitterUtil.set_frontend_v2(
174
+ app,
175
+ ['/' + encodeURI(dd.appName) + '/*', '/' + encodeURI(dd.appName)].map((rout) => {
176
+ return {
177
+ rout: rout,
178
+ path: file_path,
179
+ app_name: dd.appName,
180
+ root_path: '/' + encodeURI(dd.appName) + '/',
181
+ seoManager: async (req) => {
182
+ try {
183
+ if (req.query.state === 'google_login') {
184
+ req.query.page = 'login';
213
185
  }
214
- })();
215
- console.log(`getPageInfo==>`, (new Date().getTime() - start) / 1000);
216
- let customCode = await new User(appName).getConfigV2({
217
- key: 'ga4_config',
218
- user_id: 'manager',
219
- });
220
- console.log(`customCode==>`, (new Date().getTime() - start) / 1000);
221
- if (data && data.page_config) {
222
- data.page_config = data.page_config ?? {};
223
- const d = data.page_config.seo ?? {};
224
- if (data.page_type === 'article' && data.page_config.template_type === 'product') {
225
- const product_domain = (req.query.page as string).split('/')[1];
226
- const pd = await new Shopping(appName, undefined).getProduct(
227
- product_domain
228
- ? {
229
- page: 0,
230
- limit: 1,
231
- domain: decodeURIComponent(product_domain),
232
- }
233
- : {
234
- page: 0,
235
- limit: 1,
236
- id: req.query.product_id as string,
237
- }
238
- );
186
+ let appName = dd.appName;
187
+ if (req.query.appName) {
188
+ appName = req.query.appName;
189
+ }
190
+ req.headers['g-app'] = appName;
191
+ const start = new Date().getTime();
192
+ //插入瀏覽紀錄
193
+ Monitor.insertHistory({
194
+ req_type: 'file',
195
+ req: req,
196
+ });
197
+ console.log(`insertHistory==>`, (new Date().getTime() - start) / 1000);
198
+ //確認資料庫是否存在
199
+ await ApiPublic.createScheme(appName);
200
+ console.log(`createScheme==>`, (new Date().getTime() - start) / 1000);
201
+ //確認SAAS用戶資訊
202
+ const brandAndMemberType = await App.checkBrandAndMemberType(appName);
203
+ console.log(`brandAndMemberType==>`, (new Date().getTime() - start) / 1000);
204
+ //取得頁面資訊
205
+ let data = await Seo.getPageInfo(appName, req.query.page as string);
206
+ //首頁SEO
207
+ let home_page_data = await (async () => {
208
+ if (data && data.config) {
209
+ return await Seo.getPageInfo(appName, data.config.homePage);
210
+ } else {
211
+ return await Seo.getPageInfo(appName, 'index');
212
+ }
213
+ })();
214
+ console.log(`getPageInfo==>`, (new Date().getTime() - start) / 1000);
215
+ let customCode = await new User(appName).getConfigV2({
216
+ key: 'ga4_config',
217
+ user_id: 'manager',
218
+ });
219
+ let FBCode = await new User(appName).getConfigV2({
220
+ key: 'login_fb_setting',
221
+ user_id: 'manager',
222
+ });
223
+ console.log(`customCode==>`, (new Date().getTime() - start) / 1000);
224
+ if (data && data.page_config) {
225
+ data.page_config = data.page_config ?? {};
226
+ const d = data.page_config.seo ?? {};
227
+ if (data.page_type === 'article' && data.page_config.template_type === 'product') {
228
+ const product_domain = (req.query.page as string).split('/')[1];
229
+ const pd = await new Shopping(appName, undefined).getProduct(
230
+ product_domain
231
+ ? {
232
+ page: 0,
233
+ limit: 1,
234
+ domain: decodeURIComponent(product_domain),
235
+ }
236
+ : {
237
+ page: 0,
238
+ limit: 1,
239
+ id: req.query.product_id as string,
240
+ }
241
+ );
239
242
 
240
- if (pd.data.content) {
241
- const productSeo = pd.data.content.seo ?? {};
243
+ if (pd.data.content) {
244
+ const productSeo = pd.data.content.seo ?? {};
245
+ data = await Seo.getPageInfo(appName, data.config.homePage);
246
+ data.page_config = data.page_config ?? {};
247
+ data.page_config.seo = data.page_config.seo ?? {};
248
+ data.page_config.seo.title = productSeo.title;
249
+ data.page_config.seo.image = pd.data.content.preview_image[0];
250
+ data.page_config.seo.content = productSeo.content;
251
+ } else {
252
+ data = await Seo.getPageInfo(appName, data.config.homePage);
253
+ }
254
+ } else if (data.page_type === 'article' && data.page_config.template_type === 'blog') {
255
+ req.query.article = req.query.article || (req.query.page as any).split('/')[1];
256
+ let query = [`(content->>'$.type'='article')`, `(content->>'$.tag'='${req.query.article}')`];
257
+ const article: any = await new UtDatabase(appName, `t_manager_post`).querySql(query, {
258
+ page: 0,
259
+ limit: 1,
260
+ });
242
261
  data = await Seo.getPageInfo(appName, data.config.homePage);
243
262
  data.page_config = data.page_config ?? {};
244
263
  data.page_config.seo = data.page_config.seo ?? {};
245
- data.page_config.seo.title = productSeo.title;
246
- data.page_config.seo.image = pd.data.content.preview_image[0];
247
- data.page_config.seo.content = productSeo.content;
248
- } else {
249
- data = await Seo.getPageInfo(appName, data.config.homePage);
264
+ if (article.data[0]) {
265
+ data.page_config.seo.title = article.data[0].content.seo.title;
266
+ data.page_config.seo.content = article.data[0].content.seo.content;
267
+ data.page_config.seo.keywords = article.data[0].content.seo.keywords;
268
+ }
269
+ } else if (d.type !== 'custom') {
270
+ data = home_page_data;
250
271
  }
251
- } else if (data.page_type === 'article' && data.page_config.template_type === 'blog') {
252
- req.query.article = req.query.article || (req.query.page as any).split('/')[1];
253
- let query = [`(content->>'$.type'='article')`, `(content->>'$.tag'='${req.query.article}')`];
254
- const article: any = await new UtDatabase(appName, `t_manager_post`).querySql(query, {
255
- page: 0,
256
- limit: 1,
257
- });
258
- data = await Seo.getPageInfo(appName, data.config.homePage);
272
+ const preload = req.query.isIframe === 'true' ? {} : await App.preloadPageData(appName, req.query.page as any);
259
273
  data.page_config = data.page_config ?? {};
260
274
  data.page_config.seo = data.page_config.seo ?? {};
261
- if (article.data[0]) {
262
- data.page_config.seo.title = article.data[0].content.seo.title;
263
- data.page_config.seo.content = article.data[0].content.seo.content;
264
- data.page_config.seo.keywords = article.data[0].content.seo.keywords;
275
+ const seo_detail = await getSeoDetail(appName, req);
276
+ if (seo_detail) {
277
+ Object.keys(seo_detail).map((dd) => {
278
+ data.page_config.seo[dd] = seo_detail[dd];
279
+ });
265
280
  }
266
- } else if (d.type !== 'custom') {
267
- data = home_page_data;
268
- }
269
- const preload = req.query.isIframe === 'true' ? {} : await App.preloadPageData(appName, req.query.page as any);
270
- data.page_config = data.page_config ?? {};
271
- data.page_config.seo = data.page_config.seo ?? {};
272
- const seo_detail = await getSeoDetail(appName, req);
273
- if (seo_detail) {
274
- Object.keys(seo_detail).map((dd) => {
275
- data.page_config.seo[dd] = seo_detail[dd];
276
- });
277
- }
278
- let link_prefix = req.originalUrl.split('/')[1];
279
- if (ConfigSetting.is_local) {
280
- if (link_prefix !== 'shopnex' && link_prefix !== 'codenex_v2') {
281
+ let link_prefix = req.originalUrl.split('/')[1];
282
+ if (ConfigSetting.is_local) {
283
+ if (link_prefix !== 'shopnex' && link_prefix !== 'codenex_v2') {
284
+ link_prefix = '';
285
+ }
286
+ } else {
281
287
  link_prefix = '';
282
288
  }
283
- } else {
284
- link_prefix = '';
285
- }
286
- let distribution_code = '';
287
- req.query.page = req.query.page || 'index'
288
- if ((req.query.page as string).split('/')[0] === 'distribution' && (req.query.page as string).split('/')[1]) {
289
- const redURL = new URL(`https://127.0.0.1${req.url}`);
290
- const page = (
291
- await db.query(
292
- `SELECT *
289
+ let distribution_code = '';
290
+ req.query.page = req.query.page || 'index';
291
+ if ((req.query.page as string).split('/')[0] === 'distribution' && (req.query.page as string).split('/')[1]) {
292
+ const redURL = new URL(`https://127.0.0.1${req.url}`);
293
+ const page = (
294
+ await db.query(
295
+ `SELECT *
293
296
  FROM \`${appName}\`.t_recommend_links
294
297
  WHERE content ->>'$.link' = ?;
295
298
  `,
296
- [(req.query.page as string).split('/')[1]]
297
- )
298
- )[0].content;
299
- distribution_code = `
299
+ [(req.query.page as string).split('/')[1]]
300
+ )
301
+ )[0].content;
302
+ distribution_code = `
300
303
  localStorage.setItem('distributionCode','${page.code}');
301
304
  location.href='${page.redirect}${redURL.search}';
302
305
  `;
303
- }
304
- if ((req.query.page as string).split('/')[0] === 'collections' && (req.query.page as string).split('/')[1]) {
305
- const cols =
306
- (
307
- await db.query(
308
- `SELECT *
306
+ }
307
+ if ((req.query.page as string).split('/')[0] === 'collections' && (req.query.page as string).split('/')[1]) {
308
+ const cols =
309
+ (
310
+ await db.query(
311
+ `SELECT *
309
312
  FROM \`${appName}\`.public_config
310
313
  WHERE \`key\` = 'collection';
311
314
  `,
312
- []
313
- )
314
- )[0] ?? {};
315
+ []
316
+ )
317
+ )[0] ?? {};
315
318
 
316
- const colJson = extractCols(cols);
317
- const urlCode = decodeURI((req.query.page as string).split('/')[1]);
318
- const colData = colJson.find((item: { code: string }) => item.code === urlCode);
319
- if (colData) {
320
- data.page_config.seo.title = colData.seo_title;
321
- data.page_config.seo.content = colData.seo_content;
322
- data.page_config.seo.keywords = colData.seo_keywords;
319
+ const colJson = extractCols(cols);
320
+ const urlCode = decodeURI((req.query.page as string).split('/')[1]);
321
+ const colData = colJson.find((item: { code: string }) => item.code === urlCode);
322
+ if (colData) {
323
+ data.page_config.seo.title = colData.seo_title;
324
+ data.page_config.seo.content = colData.seo_content;
325
+ data.page_config.seo.keywords = colData.seo_keywords;
326
+ }
323
327
  }
324
- }
325
- console.log(`wait_return==>`, (new Date().getTime() - start) / 1000);
326
- return html`${(() => {
327
- const d = data.page_config.seo;
328
- const home_seo = home_page_data.page_config.seo;
329
- return html`
330
- <head>
331
- ${(() => {
332
- if (req.query.type === 'editor') {
333
- return html`<title>SHOPNEX後台系統</title>
334
- <link rel="canonical" href="/index"/>
335
- <meta name="keywords" content="SHOPNEX,電商平台"/>
336
- <link
337
- id="appImage"
338
- rel="shortcut icon"
339
- href="https://d3jnmi1tfjgtti.cloudfront.net/file/234285319/size1440_s*px$_sas0s9s0s1sesas0_1697354801736-Glitterlogo.png"
340
- type="image/x-icon"
341
- />
342
- <link
343
- rel="icon"
344
- href="https://d3jnmi1tfjgtti.cloudfront.net/file/234285319/size1440_s*px$_sas0s9s0s1sesas0_1697354801736-Glitterlogo.png"
345
- type="image/png"
346
- sizes="128x128"
347
- />
348
- <meta property="og:image"
349
- content="https://d3jnmi1tfjgtti.cloudfront.net/file/252530754/1718778766524-shopnex_banner.jpg"/>
350
- <meta property="og:title" content="SHOPNEX後台系統"/>
351
- <meta
352
- name="description"
353
- content="SHOPNEX電商開店平台,零抽成、免手續費。提供精美模板和豐富插件,操作簡單,3分鐘內快速打造專屬商店。購物車、金物流、SEO行銷、資料分析一站搞定。支援APP上架,並提供100%客製化設計,立即免費體驗30天。"
354
- />
355
- <meta
356
- name="og:description"
357
- content="SHOPNEX電商開店平台,零抽成、免手續費。提供精美模板和豐富插件,操作簡單,3分鐘內快速打造專屬商店。購物車、金物流、SEO行銷、資料分析一站搞定。支援APP上架,並提供100%客製化設計,立即免費體驗30天。"
358
- />`;
359
- } else {
360
- return html`<title>${d.title ?? '尚未設定標題'}</title>
361
- <link rel="canonical" href="${(() => {
362
- if (data.tag === 'index') {
363
- return `https://${brandAndMemberType.domain}`
364
- } else {
365
- return `https://${brandAndMemberType.domain}/${data.tag}`
366
- }
367
- })()}"/>
368
- <meta name="keywords" content="${d.keywords ?? '尚未設定關鍵字'}"/>
369
- <link id="appImage" rel="shortcut icon"
370
- href="${d.logo || home_seo.logo || ''}" type="image/x-icon"/>
371
- <link rel="icon" href="${d.logo || home_seo.logo || ''}" type="image/png"
372
- sizes="128x128"/>
373
- <meta property="og:image" content="${d.image || home_seo.image || ''}"/>
374
- <meta property="og:title" content="${(d.title ?? '').replace(/\n/g, '')}"/>
375
- <meta name="description" content="${(d.content ?? '').replace(/\n/g, '')}"/>
376
- <meta name="og:description"
377
- content="${(d.content ?? '').replace(/\n/g, '')}"/>`;
378
- }
379
- })()}
380
- ${d.code ?? ''}
381
- ${(() => {
382
- if (req.query.type === 'editor') {
383
- return ``;
384
- } else {
385
- return `${(data.config.globalStyle ?? [])
328
+ console.log(`wait_return==>`, (new Date().getTime() - start) / 1000);
329
+ return html`${(() => {
330
+ const d = data.page_config.seo;
331
+ const home_seo = home_page_data.page_config.seo;
332
+ return html`
333
+ <head>
334
+ ${(() => {
335
+ if (req.query.type === 'editor') {
336
+ return html`<title>SHOPNEX後台系統</title>
337
+ <link rel="canonical" href="/index" />
338
+ <meta name="keywords" content="SHOPNEX,電商平台" />
339
+ <link
340
+ id="appImage"
341
+ rel="shortcut icon"
342
+ href="https://d3jnmi1tfjgtti.cloudfront.net/file/234285319/size1440_s*px$_sas0s9s0s1sesas0_1697354801736-Glitterlogo.png"
343
+ type="image/x-icon"
344
+ />
345
+ <link
346
+ rel="icon"
347
+ href="https://d3jnmi1tfjgtti.cloudfront.net/file/234285319/size1440_s*px$_sas0s9s0s1sesas0_1697354801736-Glitterlogo.png"
348
+ type="image/png"
349
+ sizes="128x128"
350
+ />
351
+ <meta property="og:image" content="https://d3jnmi1tfjgtti.cloudfront.net/file/252530754/1718778766524-shopnex_banner.jpg" />
352
+ <meta property="og:title" content="SHOPNEX後台系統" />
353
+ <meta
354
+ name="description"
355
+ content="SHOPNEX電商開店平台,零抽成、免手續費。提供精美模板和豐富插件,操作簡單,3分鐘內快速打造專屬商店。購物車、金物流、SEO行銷、資料分析一站搞定。支援APP上架,並提供100%客製化設計,立即免費體驗30天。"
356
+ />
357
+ <meta
358
+ name="og:description"
359
+ content="SHOPNEX電商開店平台,零抽成、免手續費。提供精美模板和豐富插件,操作簡單,3分鐘內快速打造專屬商店。購物車、金物流、SEO行銷、資料分析一站搞定。支援APP上架,並提供100%客製化設計,立即免費體驗30天。"
360
+ />`;
361
+ } else {
362
+ return html`<title>${d.title ?? '尚未設定標題'}</title>
363
+ <link
364
+ rel="canonical"
365
+ href="${(() => {
366
+ if (data.tag === 'index') {
367
+ return `https://${brandAndMemberType.domain}`;
368
+ } else {
369
+ return `https://${brandAndMemberType.domain}/${data.tag}`;
370
+ }
371
+ })()}"
372
+ />
373
+ <meta name="keywords" content="${d.keywords ?? '尚未設定關鍵字'}" />
374
+ <link id="appImage" rel="shortcut icon" href="${d.logo || home_seo.logo || ''}" type="image/x-icon" />
375
+ <link rel="icon" href="${d.logo || home_seo.logo || ''}" type="image/png" sizes="128x128" />
376
+ <meta property="og:image" content="${d.image || home_seo.image || ''}" />
377
+ <meta property="og:title" content="${(d.title ?? '').replace(/\n/g, '')}" />
378
+ <meta name="description" content="${(d.content ?? '').replace(/\n/g, '')}" />
379
+ <meta name="og:description" content="${(d.content ?? '').replace(/\n/g, '')}" />`;
380
+ }
381
+ })()}
382
+ ${d.code ?? ''}
383
+ ${(() => {
384
+ if (req.query.type === 'editor') {
385
+ return ``;
386
+ } else {
387
+ return `${(data.config.globalStyle ?? [])
386
388
  .map((dd: any) => {
387
389
  try {
388
390
  if (dd.data.elem === 'link') {
389
- return html`
390
- <link
391
- type="text/css"
392
- rel="stylesheet"
393
- href="${dd.data.attr.find((dd: any) => {
394
- return dd.attr === 'href';
395
- }).value}"
396
- />`;
391
+ return html` <link
392
+ type="text/css"
393
+ rel="stylesheet"
394
+ href="${dd.data.attr.find((dd: any) => {
395
+ return dd.attr === 'href';
396
+ }).value}"
397
+ />`;
397
398
  }
398
399
  } catch (e) {
399
400
  return ``;
400
401
  }
401
402
  })
402
403
  .join('')}`;
403
- }
404
- })()}
405
- </head>
406
- `;
407
- })()}
404
+ }
405
+ })()}
406
+ </head>
407
+ `;
408
+ })()}
408
409
  <script>
409
410
  ${d.custom_script ?? ''}
410
411
  window.appName = '${appName}';
@@ -412,39 +413,35 @@ export async function createAPP(dd: any) {
412
413
  window.memberType = '${brandAndMemberType.memberType}';
413
414
  window.glitterBackend = '${config.domain}';
414
415
  window.preloadData = ${JSON.stringify(preload)
415
- .replace(/<\/script>/g, 'sdjuescript_prepand')
416
- .replace(/<script>/g, 'sdjuescript_prefix')};
416
+ .replace(/<\/script>/g, 'sdjuescript_prepand')
417
+ .replace(/<script>/g, 'sdjuescript_prefix')};
417
418
  window.preloadData = JSON.parse(JSON.stringify(window.preloadData).replace(/sdjuescript_prepand/g, '</s' + 'cript>').replace(/sdjuescript_prefix/g, '<s' + 'cript>'))
418
419
  window.glitter_page = '${req.query.page}';
419
420
  ${distribution_code}
420
421
  </script>
421
422
  ${[
422
- {src: 'glitterBundle/GlitterInitial.js', type: 'module'},
423
- {src: 'glitterBundle/module/html-generate.js', type: 'module'},
424
- {src: 'glitterBundle/html-component/widget.js', type: 'module'},
425
- {src: 'glitterBundle/plugins/trigger-event.js', type: 'module'},
426
- {src: 'api/pageConfig.js', type: 'module'},
423
+ { src: 'glitterBundle/GlitterInitial.js', type: 'module' },
424
+ { src: 'glitterBundle/module/html-generate.js', type: 'module' },
425
+ { src: 'glitterBundle/html-component/widget.js', type: 'module' },
426
+ { src: 'glitterBundle/plugins/trigger-event.js', type: 'module' },
427
+ { src: 'api/pageConfig.js', type: 'module' },
427
428
  ]
428
- .map((dd) => {
429
- return html`
430
- <script src="/${link_prefix && `${link_prefix}/`}${dd.src}"
431
- type="${dd.type}"></script>`;
432
- })
433
- .join('')}
429
+ .map((dd) => {
430
+ return html` <script src="/${link_prefix && `${link_prefix}/`}${dd.src}" type="${dd.type}"></script>`;
431
+ })
432
+ .join('')}
434
433
  ${(preload.event ?? [])
435
- .filter((dd: any) => {
436
- return dd;
437
- })
438
- .map((dd: any) => {
439
- const link = dd.fun.replace(`TriggerEvent.setEventRouter(import.meta.url, '.`, 'official_event');
440
- return link.substring(0, link.length - 2);
441
- })
442
- .map((dd: any) => {
443
- return html`
444
- <script src="/${link_prefix && `${link_prefix}/`}${dd}"
445
- type="module"></script>`;
446
- })
447
- .join('')}
434
+ .filter((dd: any) => {
435
+ return dd;
436
+ })
437
+ .map((dd: any) => {
438
+ const link = dd.fun.replace(`TriggerEvent.setEventRouter(import.meta.url, '.`, 'official_event');
439
+ return link.substring(0, link.length - 2);
440
+ })
441
+ .map((dd: any) => {
442
+ return html` <script src="/${link_prefix && `${link_prefix}/`}${dd}" type="module"></script>`;
443
+ })
444
+ .join('')}
448
445
  </head>
449
446
  ${(() => {
450
447
  if (req.query.type === 'editor') {
@@ -452,10 +449,9 @@ export async function createAPP(dd: any) {
452
449
  } else {
453
450
  return html`
454
451
  ${(customCode.ga4 || [])
455
- .map((dd: any) => {
456
- return html`<!-- Google tag (gtag.js) -->
457
- <script async
458
- src="https://www.googletagmanager.com/gtag/js?id=${dd.code}"></script>
452
+ .map((dd: any) => {
453
+ return html`<!-- Google tag (gtag.js) -->
454
+ <script async src="https://www.googletagmanager.com/gtag/js?id=${dd.code}"></script>
459
455
  <script>
460
456
  window.dataLayer = window.dataLayer || [];
461
457
 
@@ -467,245 +463,276 @@ export async function createAPP(dd: any) {
467
463
 
468
464
  gtag('config', '${dd.code}');
469
465
  </script>`;
470
- })
471
- .join('')}
466
+ })
467
+ .join('')}
472
468
  ${(customCode.g_tag || [])
473
- .map((dd: any) => {
474
- return html`<!-- Google tag (gtag.js) -->
469
+ .map((dd: any) => {
470
+ return html`<!-- Google tag (gtag.js) -->
475
471
  <!-- Google Tag Manager -->
476
472
  <script>
477
473
  (function (w, d, s, l, i) {
478
474
  w[l] = w[l] || [];
479
- w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
475
+ w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
480
476
  var f = d.getElementsByTagName(s)[0],
481
- j = d.createElement(s),
482
- dl = l != 'dataLayer' ? '&l=' + l : '';
477
+ j = d.createElement(s),
478
+ dl = l != 'dataLayer' ? '&l=' + l : '';
483
479
  j.async = true;
484
480
  j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
485
481
  f.parentNode.insertBefore(j, f);
486
482
  })(window, document, 'script', 'dataLayer', '${dd.code}');
487
483
  </script>
488
484
  <!-- End Google Tag Manager -->`;
489
- })
490
- .join('')}
485
+ })
486
+ .join('')}
487
+ ${FBCode && FBCode.pixel
488
+ ? html`<!-- Meta Pixel Code -->
489
+ <script>
490
+ !(function (f, b, e, v, n, t, s) {
491
+ if (f.fbq) return;
492
+ n = f.fbq = function () {
493
+ n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);
494
+ };
495
+ if (!f._fbq) f._fbq = n;
496
+ n.push = n;
497
+ n.loaded = !0;
498
+ n.version = '2.0';
499
+ n.queue = [];
500
+ t = b.createElement(e);
501
+ t.async = !0;
502
+ t.src = v;
503
+ s = b.getElementsByTagName(e)[0];
504
+ s.parentNode.insertBefore(t, s);
505
+ })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
506
+ fbq('init', '${FBCode.pixel}');
507
+ fbq('track', 'PageView');
508
+ </script>
509
+ <noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=617830100580621&ev=PageView&noscript=1" /></noscript>
510
+ <!-- End Meta Pixel Code -->`
511
+ : ''}
491
512
  `;
492
513
  }
493
514
  })()}
494
515
  `;
495
- } else {
496
- console.log(`brandAndMemberType==>redirect`);
497
- return await Seo.redirectToHomePage(appName, req);
516
+ } else {
517
+ console.log(`brandAndMemberType==>redirect`);
518
+ return await Seo.redirectToHomePage(appName, req);
519
+ }
520
+ } catch (e: any) {
521
+ console.error(e);
522
+ return `${e}`;
498
523
  }
499
- } catch (e: any) {
500
- console.error(e);
501
- return `${e}`;
502
- }
503
- },
504
- sitemap: async (req, resp) => {
505
- let appName = dd.appName;
506
- if (req.query.appName) {
507
- appName = req.query.appName;
508
- }
509
- let query = [`(content->>'$.type'='article')`];
510
- const article: any = await new UtDatabase(appName, `t_manager_post`).querySql(query, {
511
- page: 0,
512
- limit: 10000,
513
- });
514
- const domain = (
515
- await db.query(
516
- `select \`domain\`
524
+ },
525
+ sitemap: async (req, resp) => {
526
+ let appName = dd.appName;
527
+ if (req.query.appName) {
528
+ appName = req.query.appName;
529
+ }
530
+ let query = [`(content->>'$.type'='article')`];
531
+ const article: any = await new UtDatabase(appName, `t_manager_post`).querySql(query, {
532
+ page: 0,
533
+ limit: 10000,
534
+ });
535
+ const domain = (
536
+ await db.query(
537
+ `select \`domain\`
517
538
  from \`${saasConfig.SAAS_NAME}\`.app_config
518
539
  where appName = ?`,
519
- [appName]
520
- )
521
- )[0]['domain'];
540
+ [appName]
541
+ )
542
+ )[0]['domain'];
522
543
 
523
- const site_map = await getSeoSiteMap(appName, req);
544
+ const site_map = await getSeoSiteMap(appName, req);
524
545
 
525
- const cols =
526
- (
527
- await db.query(
528
- `SELECT *
546
+ const cols =
547
+ (
548
+ await db.query(
549
+ `SELECT *
529
550
  FROM \`${appName}\`.public_config
530
551
  WHERE \`key\` = 'collection';`,
531
- []
532
- )
533
- )[0] ?? {};
552
+ []
553
+ )
554
+ )[0] ?? {};
534
555
 
535
- const products = await db.query(
536
- `SELECT *
556
+ const products = await db.query(
557
+ `SELECT *
537
558
  FROM \`${appName}\`.t_manager_post
538
559
  WHERE JSON_EXTRACT(content, '$.type') = 'product';
539
560
  `,
540
- []
541
- );
561
+ []
562
+ );
542
563
 
543
- const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
564
+ const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
544
565
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
545
566
  ${(
546
- await db.query(
547
- `select page_config, tag, updated_time
567
+ await db.query(
568
+ `select page_config, tag, updated_time
548
569
  from \`${saasConfig.SAAS_NAME}\`.page_config
549
570
  where appName = ?
550
571
  and page_config ->>'$.seo.type'='custom'
551
572
  `,
552
- [appName]
553
- )
554
- )
555
- .map((d2: any) => {
556
- if (d2.tag === 'index') {
557
- return `<url>
573
+ [appName]
574
+ )
575
+ )
576
+ .map((d2: any) => {
577
+ if (d2.tag === 'index') {
578
+ return `<url>
558
579
  <loc>${`https://${domain}`.replace(/ /g, '+')}</loc>
559
580
  <lastmod>${moment(new Date(d2.updated_time)).format('YYYY-MM-DD')}</lastmod>
560
581
  </url> `;
561
- } else {
562
- return `<url>
582
+ } else {
583
+ return `<url>
563
584
  <loc>${`https://${domain}/${d2.tag}`.replace(/ /g, '+')}</loc>
564
585
  <lastmod>${moment(new Date(d2.updated_time)).format('YYYY-MM-DD')}</lastmod>
565
586
  </url> `;
566
- }
567
- })
568
- .join('')}
587
+ }
588
+ })
589
+ .join('')}
569
590
  ${article.data
570
- .map((d2: any) => {
571
- if (!d2.content.template) {
572
- return ``;
573
- }
574
- return `<url>
591
+ .map((d2: any) => {
592
+ if (!d2.content.template) {
593
+ return ``;
594
+ }
595
+ return `<url>
575
596
  <loc>${`https://${domain}/${d2.content.for_index === 'false' ? `pages` : `blogs`}/${d2.content.tag}`.replace(/ /g, '+')}</loc>
576
597
  <lastmod>${moment(new Date(d2.updated_time)).format('YYYY-MM-DD')}</lastmod>
577
598
  </url> `;
578
- })
579
- .join('')}
599
+ })
600
+ .join('')}
580
601
  ${(site_map || [])
581
- .map((d2: any) => {
582
- return `<url>
602
+ .map((d2: any) => {
603
+ return `<url>
583
604
  <loc>${`https://${domain}/${d2.url}`.replace(/ /g, '+')}</loc>
584
605
  <lastmod>${d2.updated_time ? moment(new Date(d2.updated_time)).format('YYYY-MM-DD') : moment(new Date()).format('YYYY-MM-DDTHH:mm:SS+00:00')}</lastmod>
585
606
  <changefreq>weekly</changefreq>
586
607
  </url> `;
587
- })
588
- .join('')}
608
+ })
609
+ .join('')}
589
610
  ${extractCols(cols)
590
- .map((item: { code: string; updated_at: string }) => {
591
- if(!item.code){return ``}
592
- return `<url>
611
+ .map((item: { code: string; updated_at: string }) => {
612
+ if (!item.code) {
613
+ return ``;
614
+ }
615
+ return `<url>
593
616
  <loc>https://${domain}/collections/${item.code}</loc>
594
617
  <lastmod>${item.updated_at}</lastmod>
595
618
  <changefreq>weekly</changefreq>
596
619
  </url>`;
597
- })
598
- .join('')}
620
+ })
621
+ .join('')}
599
622
  ${extractProds(products)
600
- .map((item: { code: string; updated_at: string }) => {
601
- if(!item.code){return ``}
602
- return `<url>
623
+ .map((item: { code: string; updated_at: string }) => {
624
+ if (!item.code) {
625
+ return ``;
626
+ }
627
+ return `<url>
603
628
  <loc>https://${domain}/products/${item.code}</loc>
604
629
  <lastmod>${item.updated_at}</lastmod>
605
630
  <changefreq>weekly</changefreq>
606
631
  </url>`;
607
- })
608
- .join('')}
632
+ })
633
+ .join('')}
609
634
  </urlset> `;
610
- return xmlFormatter(sitemap, {
611
- indentation: ' ', // 使用兩個空格進行縮進
612
- filter: (node) => node.type !== 'Comment', // 選擇性過濾節點
613
- collapseContent: true, // 折疊內部文本
614
- });
615
- },
616
- sitemap_list: async (req, resp) => {
617
- let appName = dd.appName;
618
- if (req.query.appName) {
619
- appName = req.query.appName;
620
- }
621
- const domain = (
622
- await db.query(
623
- `select \`domain\`
635
+ return xmlFormatter(sitemap, {
636
+ indentation: ' ', // 使用兩個空格進行縮進
637
+ filter: (node) => node.type !== 'Comment', // 選擇性過濾節點
638
+ collapseContent: true, // 折疊內部文本
639
+ });
640
+ },
641
+ sitemap_list: async (req, resp) => {
642
+ let appName = dd.appName;
643
+ if (req.query.appName) {
644
+ appName = req.query.appName;
645
+ }
646
+ const domain = (
647
+ await db.query(
648
+ `select \`domain\`
624
649
  from \`${saasConfig.SAAS_NAME}\`.app_config
625
650
  where appName = ?`,
626
- [appName]
627
- )
628
- )[0]['domain'];
629
- return `<?xml version="1.0" encoding="UTF-8"?>
651
+ [appName]
652
+ )
653
+ )[0]['domain'];
654
+ return `<?xml version="1.0" encoding="UTF-8"?>
630
655
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
631
656
  <!-- This is the parent sitemap linking to additional sitemaps for products, collections and pages as shown below. The sitemap can not be edited manually, but is kept up to date in real time. -->
632
657
  <sitemap>
633
658
  <loc>https://${domain}/sitemap_detail.xml</loc>
634
659
  </sitemap>
635
660
  </sitemapindex> `;
636
- },
637
- robots: async (req, resp) => {
638
- let appName = dd.appName;
639
- if (req.query.appName) {
640
- appName = req.query.appName;
641
- }
642
- const domain = (
643
- await db.query(
644
- `select \`domain\`
661
+ },
662
+ robots: async (req, resp) => {
663
+ let appName = dd.appName;
664
+ if (req.query.appName) {
665
+ appName = req.query.appName;
666
+ }
667
+ const domain = (
668
+ await db.query(
669
+ `select \`domain\`
645
670
  from \`${saasConfig.SAAS_NAME}\`.app_config
646
671
  where appName = ?`,
647
- [appName]
648
- )
649
- )[0]['domain'];
650
- return html`# we use SHOPNEX as our ecommerce platform User-agent: * Sitemap: https://${domain}/sitemap.xml `;
651
- },
652
- tw_shop: async (req, resp) => {
653
- let appName = dd.appName;
654
- const escapeHtml = (text: string): string => {
655
- const map: Record<string, string> = {
656
- "&": "&amp;",
657
- "<": "&lt;",
658
- ">": "&gt;",
659
- '"': "&quot;",
660
- "'": "&#039;",
672
+ [appName]
673
+ )
674
+ )[0]['domain'];
675
+ return html`# we use SHOPNEX as our ecommerce platform User-agent: * Sitemap: https://${domain}/sitemap.xml `;
676
+ },
677
+ tw_shop: async (req, resp) => {
678
+ let appName = dd.appName;
679
+ const escapeHtml = (text: string): string => {
680
+ const map: Record<string, string> = {
681
+ '&': '&amp;',
682
+ '<': '&lt;',
683
+ '>': '&gt;',
684
+ '"': '&quot;',
685
+ "'": '&#039;',
686
+ };
687
+ return text.replace(/[&<>"']/g, (m) => map[m] || m);
661
688
  };
662
- return text.replace(/[&<>"']/g, (m) => map[m] || m);
663
- };
664
- if (req.query.appName) {
665
- appName = req.query.appName;
666
- }
667
- const products = await db.query(
668
- `SELECT *
689
+ if (req.query.appName) {
690
+ appName = req.query.appName;
691
+ }
692
+ const products = await db.query(
693
+ `SELECT *
669
694
  FROM \`${dd.appName}\`.t_manager_post
670
695
  WHERE JSON_EXTRACT(content, '$.type') = 'product';
671
696
  `,
672
- []
673
- );
674
- const domain = (
675
- await db.query(
676
- `select \`domain\`
697
+ []
698
+ );
699
+ const domain = (
700
+ await db.query(
701
+ `select \`domain\`
677
702
  from \`${saasConfig.SAAS_NAME}\`.app_config
678
703
  where appName = ?`,
679
- [appName]
680
- )
681
- )[0]['domain'];
682
- let printData = (products).map((product: any) => {
683
- return product.content.variants.map((variant: any) => {
684
- return html`
685
- <Product>
686
- <SKU>${variant.sku}</SKU>
687
- <Name>${product.content.title}</Name>
688
- <Description>${dd.appName} - ${product.content.title}</Description>
689
- <URL>
690
- ${`https://` + domain + '/products/' + product.content.title}
691
- </URL>
692
- <Price>${variant.compare_price ?? variant.sale_price}</Price>
693
- <LargeImage>
694
- ${variant.preview_image ?? ""}
695
- </LargeImage>
696
- <SalePrice>${variant.sale_price}</SalePrice>
697
- <Category>${product.content.collection.join('')}</Category>
698
- </Product>
699
- `
700
- }).join('');
701
- }).join('')
702
- return xmlFormatter(`<Product>${printData}</Product>`, {
703
- indentation: ' ', // 使用兩個空格進行縮進
704
- filter: (node) => node.type !== 'Comment', // 選擇性過濾節點
705
- collapseContent: true, // 折疊內部文本
706
- });
707
- }
708
- }}))
704
+ [appName]
705
+ )
706
+ )[0]['domain'];
707
+ let printData = products
708
+ .map((product: any) => {
709
+ return product.content.variants
710
+ .map((variant: any) => {
711
+ return html`
712
+ <Product>
713
+ <SKU>${variant.sku}</SKU>
714
+ <Name>${product.content.title}</Name>
715
+ <Description>${dd.appName} - ${product.content.title}</Description>
716
+ <URL> ${`https://` + domain + '/products/' + product.content.title} </URL>
717
+ <Price>${variant.compare_price ?? variant.sale_price}</Price>
718
+ <LargeImage> ${variant.preview_image ?? ''} </LargeImage>
719
+ <SalePrice>${variant.sale_price}</SalePrice>
720
+ <Category>${product.content.collection.join('')}</Category>
721
+ </Product>
722
+ `;
723
+ })
724
+ .join('');
725
+ })
726
+ .join('');
727
+ return xmlFormatter(`<Product>${printData}</Product>`, {
728
+ indentation: ' ', // 使用兩個空格進行縮進
729
+ filter: (node) => node.type !== 'Comment', // 選擇性過濾節點
730
+ collapseContent: true, // 折疊內部文本
731
+ });
732
+ },
733
+ };
734
+ })
735
+ );
709
736
  }
710
737
 
711
738
  async function getSeoDetail(appName: string, req: any) {