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.
- package/lowcode/Entry.js +1 -1
- package/lowcode/Entry.ts +1 -1
- package/lowcode/backend-manager/bg-product.js +2 -1
- package/lowcode/backend-manager/bg-product.ts +2 -1
- package/lowcode/cms-plugin/filter-options.js +1 -3
- package/lowcode/cms-plugin/filter-options.ts +1 -3
- package/lowcode/cms-plugin/module/product-excel.js +537 -0
- package/lowcode/cms-plugin/module/product-excel.ts +652 -0
- package/lowcode/cms-plugin/shopping-allowance-manager.js +12 -5
- package/lowcode/cms-plugin/shopping-allowance-manager.ts +12 -5
- package/lowcode/cms-plugin/shopping-discount-setting.js +1 -1
- package/lowcode/cms-plugin/shopping-discount-setting.ts +1 -1
- package/lowcode/cms-plugin/shopping-finance-setting.js +0 -7
- package/lowcode/cms-plugin/shopping-finance-setting.ts +8 -8
- package/lowcode/cms-plugin/shopping-invoice-manager.js +5 -1
- package/lowcode/cms-plugin/shopping-invoice-manager.ts +5 -2
- package/lowcode/cms-plugin/shopping-order-manager.js +8 -0
- package/lowcode/cms-plugin/shopping-order-manager.ts +8 -0
- package/lowcode/cms-plugin/shopping-product-setting.js +61 -638
- package/lowcode/cms-plugin/shopping-product-setting.ts +67 -790
- package/lowcode/jspage/main.ts +0 -1
- package/lowcode/public-components/blogs/blogs-01.js +4 -0
- package/lowcode/public-components/checkout/index.js +967 -209
- package/lowcode/public-components/checkout/index.ts +977 -214
- package/lowcode/public-components/checkout/index_.js +1724 -412
- package/lowcode/public-components/checkout/index_.ts +2032 -704
- package/lowcode/public-components/product/pd-class.js +68 -13
- package/lowcode/public-components/product/pd-class.ts +74 -14
- package/lowcode/public-components/product/product-detail.js +1 -1
- package/lowcode/public-components/product/product-detail.ts +7 -3
- package/lowcode/public-components/product/product-list.js +55 -0
- package/lowcode/public-components/product/product-list.ts +62 -1
- package/lowcode/public-components/public/ad.js +14 -0
- package/lowcode/public-components/public/ad.ts +15 -0
- package/package.json +7 -4
- package/src/api-public/controllers/ai-chat.js.map +1 -1
- package/src/api-public/controllers/app-release.js.map +1 -1
- package/src/api-public/controllers/article.js.map +1 -1
- package/src/api-public/controllers/fcm.js.map +1 -1
- package/src/api-public/controllers/post.js.map +1 -1
- package/src/api-public/controllers/rebate.js.map +1 -1
- package/src/api-public/controllers/shop.js +16 -0
- package/src/api-public/controllers/shop.js.map +1 -1
- package/src/api-public/controllers/shop.ts +27 -3
- package/src/api-public/controllers/user.js.map +1 -1
- package/src/api-public/services/EcInvoice.js +18 -12
- package/src/api-public/services/EcInvoice.js.map +1 -1
- package/src/api-public/services/EcInvoice.ts +19 -14
- package/src/api-public/services/ai-robot.d.ts +0 -1
- package/src/api-public/services/ai-robot.js.map +1 -1
- package/src/api-public/services/auto-send-email.js.map +1 -1
- package/src/api-public/services/chat.js.map +1 -1
- package/src/api-public/services/custom-code.js.map +1 -1
- package/src/api-public/services/delivery.js.map +1 -1
- package/src/api-public/services/ezpay/tool.d.ts +1 -0
- package/src/api-public/services/fb-message.js.map +1 -1
- package/src/api-public/services/financial-service.d.ts +49 -1
- package/src/api-public/services/financial-service.js +226 -1
- package/src/api-public/services/financial-service.js.map +1 -1
- package/src/api-public/services/financial-service.ts +282 -1
- package/src/api-public/services/line-message.js.map +1 -1
- package/src/api-public/services/monitor.d.ts +1 -0
- package/src/api-public/services/post.js.map +1 -1
- package/src/api-public/services/rebate.js.map +1 -1
- package/src/api-public/services/schedule.js.map +1 -1
- package/src/api-public/services/share-permission.js.map +1 -1
- package/src/api-public/services/shopping.d.ts +3 -1
- package/src/api-public/services/shopping.js +57 -58
- package/src/api-public/services/shopping.js.map +1 -1
- package/src/api-public/services/shopping.ts +77 -76
- package/src/api-public/services/sms.js.map +1 -1
- package/src/api-public/services/user.js.map +1 -1
- package/src/api-public/utils/ut-permission.d.ts +1 -0
- package/src/app-project/serverless/src/modules/database.d.ts +1 -1
- package/src/app-project/serverless/src/modules/redis.d.ts +1 -1
- package/src/config.d.ts +1 -1
- package/src/config.js.map +1 -1
- package/src/controllers/ai.js.map +1 -1
- package/src/controllers/filemanager.js.map +1 -1
- package/src/firebase/message.js +2 -1
- package/src/firebase/message.js.map +1 -1
- package/src/helper/app_creater.js +2 -1
- package/src/helper/app_creater.js.map +1 -1
- package/src/helper/glitter-util.js.map +3 -3
- package/src/index.js +111 -92
- package/src/index.js.map +1 -5
- package/src/index.ts +424 -397
- package/src/lambda/interface.d.ts +1 -1
- package/src/lambda/interface.js +2 -2
- package/src/lambda/interface.js.map +1 -1
- package/src/modules/AWSLib.js +3 -2
- package/src/modules/AWSLib.js.map +1 -1
- package/src/modules/database.d.ts +1 -1
- package/src/modules/redis.d.ts +1 -1
- package/src/modules/tool.js +2 -1
- package/src/modules/tool.js.map +1 -1
- package/src/run.js +2 -1
- package/src/run.js.map +1 -1
- package/src/services/backend-service.js.map +1 -1
- package/src/services/create-instance.js +4 -3
- package/src/services/create-instance.js.map +1 -1
- package/src/services/saas-table-check.js +2 -2
- package/src/services/saas-table-check.js.map +1 -1
- package/src/services/ses.js +2 -1
- package/src/services/ses.js.map +1 -1
- package/src/services/tool.js +3 -2
- 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(
|
|
174
|
-
|
|
175
|
-
'/' + encodeURI(dd.appName)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
241
|
-
|
|
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
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
315
|
+
[]
|
|
316
|
+
)
|
|
317
|
+
)[0] ?? {};
|
|
315
318
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
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
|
-
|
|
416
|
-
|
|
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
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
|
|
456
|
-
|
|
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
|
-
|
|
466
|
+
})
|
|
467
|
+
.join('')}
|
|
472
468
|
${(customCode.g_tag || [])
|
|
473
|
-
|
|
474
|
-
|
|
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
|
-
|
|
482
|
-
|
|
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
|
-
|
|
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
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
520
|
-
|
|
521
|
-
|
|
540
|
+
[appName]
|
|
541
|
+
)
|
|
542
|
+
)[0]['domain'];
|
|
522
543
|
|
|
523
|
-
|
|
544
|
+
const site_map = await getSeoSiteMap(appName, req);
|
|
524
545
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
546
|
+
const cols =
|
|
547
|
+
(
|
|
548
|
+
await db.query(
|
|
549
|
+
`SELECT *
|
|
529
550
|
FROM \`${appName}\`.public_config
|
|
530
551
|
WHERE \`key\` = 'collection';`,
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
552
|
+
[]
|
|
553
|
+
)
|
|
554
|
+
)[0] ?? {};
|
|
534
555
|
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
|
|
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
|
-
|
|
547
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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
|
-
|
|
562
|
-
|
|
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
|
-
|
|
587
|
+
}
|
|
588
|
+
})
|
|
589
|
+
.join('')}
|
|
569
590
|
${article.data
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
599
|
+
})
|
|
600
|
+
.join('')}
|
|
580
601
|
${(site_map || [])
|
|
581
|
-
|
|
582
|
-
|
|
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
|
-
|
|
608
|
+
})
|
|
609
|
+
.join('')}
|
|
589
610
|
${extractCols(cols)
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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
|
-
|
|
620
|
+
})
|
|
621
|
+
.join('')}
|
|
599
622
|
${extractProds(products)
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
-
|
|
632
|
+
})
|
|
633
|
+
.join('')}
|
|
609
634
|
</urlset> `;
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
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
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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
|
+
'&': '&',
|
|
682
|
+
'<': '<',
|
|
683
|
+
'>': '>',
|
|
684
|
+
'"': '"',
|
|
685
|
+
"'": ''',
|
|
686
|
+
};
|
|
687
|
+
return text.replace(/[&<>"']/g, (m) => map[m] || m);
|
|
661
688
|
};
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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) {
|