ts-glitter 19.2.2 → 19.2.4

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 (39) hide show
  1. package/lowcode/Entry.js +1 -1
  2. package/lowcode/Entry.ts +1 -1
  3. package/lowcode/cms-plugin/shopping-information.js +136 -30
  4. package/lowcode/cms-plugin/shopping-information.ts +149 -29
  5. package/lowcode/css/editor.css +1 -1
  6. package/lowcode/jspage/main.js +1 -1
  7. package/lowcode/jspage/main.ts +1 -1
  8. package/lowcode/public-components/checkout/index.js +209 -196
  9. package/lowcode/public-components/checkout/index.ts +235 -223
  10. package/package.json +1 -1
  11. package/src/api-public/controllers/delivery.js.map +1 -1
  12. package/src/api-public/controllers/index.js.map +1 -1
  13. package/src/api-public/controllers/shop.js.map +1 -1
  14. package/src/api-public/services/ai-robot.d.ts +0 -1
  15. package/src/api-public/services/fb-api.d.ts +0 -1
  16. package/src/api-public/services/fb-message.d.ts +0 -1
  17. package/src/api-public/services/financial-service.d.ts +1 -1
  18. package/src/api-public/services/financial-service.js +22 -11
  19. package/src/api-public/services/financial-service.js.map +1 -1
  20. package/src/api-public/services/financial-service.ts +30 -15
  21. package/src/api-public/services/invoice.js.map +1 -1
  22. package/src/api-public/services/line-message.d.ts +0 -1
  23. package/src/api-public/services/public-table-check.d.ts +7 -0
  24. package/src/api-public/services/public-table-check.js +10 -1
  25. package/src/api-public/services/public-table-check.js.map +1 -5
  26. package/src/api-public/services/public-table-check.ts +13 -2
  27. package/src/api-public/services/schedule.js.map +1 -1
  28. package/src/api-public/services/share-permission.d.ts +1 -1
  29. package/src/api-public/services/shopee.d.ts +0 -1
  30. package/src/api-public/services/shopping.js.map +1 -1
  31. package/src/api-public/services/shopping.ts +1 -0
  32. package/src/helper/glitter-util.d.ts +3 -2
  33. package/src/helper/glitter-util.js +5 -0
  34. package/src/helper/glitter-util.js.map +1 -1
  35. package/src/helper/glitter-util.ts +6 -2
  36. package/src/index.js +146 -98
  37. package/src/index.js.map +1 -1
  38. package/src/index.ts +763 -685
  39. package/src/services/app.js.map +1 -1
package/src/index.ts CHANGED
@@ -43,27 +43,27 @@ import { SitemapStream, streamToPromise } from 'sitemap';
43
43
  import { Readable } from 'stream';
44
44
  import AWS from 'aws-sdk';
45
45
  import { extractCols, extractProds, SeoConfig } from './seo-config.js';
46
- import {Language} from "./Language.js";
47
- import {FbApi} from "./api-public/services/fb-api.js";
46
+ import { Language } from './Language.js';
47
+ import { FbApi } from './api-public/services/fb-api.js';
48
48
 
49
49
  export const app = express();
50
50
  const logger = new Logger();
51
51
 
52
52
  app.options('/*', (req, res) => {
53
- res.header('Access-Control-Allow-Origin', '*');
54
- res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
55
- res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization,g-app,mac_address,language,currency_code');
56
- res.status(200).send();
53
+ res.header('Access-Control-Allow-Origin', '*');
54
+ res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
55
+ res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization,g-app,mac_address,language,currency_code');
56
+ res.status(200).send();
57
57
  });
58
58
 
59
59
  // 配置 session
60
60
  app.use(
61
- session({
62
- secret: config.SECRET_KEY,
63
- resave: false,
64
- saveUninitialized: true,
65
- cookie: { maxAge: 1000 * 60 * 60 * 24 * 365 }, // 設定 cookie 期限一年
66
- })
61
+ session({
62
+ secret: config.SECRET_KEY,
63
+ resave: false,
64
+ saveUninitialized: true,
65
+ cookie: { maxAge: 1000 * 60 * 60 * 24 * 365 }, // 設定 cookie 期限一年
66
+ })
67
67
  );
68
68
 
69
69
  app.use(cookieParser());
@@ -79,742 +79,820 @@ app.use(contollers);
79
79
  app.use(public_contollers);
80
80
 
81
81
  export async function initial(serverPort: number) {
82
- await (async () => {
83
- //統一設定時區為UTC
84
- process.env.TZ = 'UTC';
85
- await database.createPool();
86
- await Ai.initial();
87
- await SaasScheme.createScheme();
88
- await ApiPublic.createScheme(saasConfig.SAAS_NAME as string);
89
- await redis.connect();
90
- await createAppRoute();
91
- await listBuckets();
92
- await createBucket(config.AWS_S3_NAME as string);
93
- logger.info('[Init]', `Server start with env: ${process.env.NODE_ENV || 'local'}`);
94
- await app.listen(serverPort);
95
- fs.mkdirSync(path.resolve(__filename, '../app-project/work-space'), { recursive: true });
96
- Release.removeAllFilesInFolder(path.resolve(__filename, '../app-project/work-space'));
97
- if (process.env.firebase) {
98
- await Firebase.initial();
99
- }
100
- // await UpdateScript.run()
101
- if (ConfigSetting.runSchedule) {
102
- new Schedule().main();
103
- new SystemSchedule().start();
104
- }
105
- WebSocket.start();
106
- logger.info('[Init]', `Server is listening on port: ${serverPort}`);
107
- console.log('Starting up the server now.');
108
- })();
82
+ await (async () => {
83
+ //統一設定時區為UTC
84
+ process.env.TZ = 'UTC';
85
+ await database.createPool();
86
+ await Ai.initial();
87
+ await SaasScheme.createScheme();
88
+ await ApiPublic.createScheme(saasConfig.SAAS_NAME as string);
89
+ await redis.connect();
90
+ await createAppRoute();
91
+ await listBuckets();
92
+ await createBucket(config.AWS_S3_NAME as string);
93
+ logger.info('[Init]', `Server start with env: ${process.env.NODE_ENV || 'local'}`);
94
+ await app.listen(serverPort);
95
+ fs.mkdirSync(path.resolve(__filename, '../app-project/work-space'), { recursive: true });
96
+ Release.removeAllFilesInFolder(path.resolve(__filename, '../app-project/work-space'));
97
+ if (process.env.firebase) {
98
+ await Firebase.initial();
99
+ }
100
+ // await UpdateScript.run()
101
+ if (ConfigSetting.runSchedule) {
102
+ new Schedule().main();
103
+ new SystemSchedule().start();
104
+ }
105
+ WebSocket.start();
106
+ logger.info('[Init]', `Server is listening on port: ${serverPort}`);
107
+ console.log('Starting up the server now.');
108
+ })();
109
109
  }
110
110
 
111
111
  function createContext(req: express.Request, res: express.Response, next: express.NextFunction) {
112
- const uuid = uuidv4();
113
- const ip = req.ip;
114
- const requestInfo = { uuid: `${uuid}`, method: `${req.method}`, url: `${req.url}`, ip: `${ip}` };
115
- asyncHook.getInstance().createRequestContext(requestInfo);
116
- next();
112
+ const uuid = uuidv4();
113
+ const ip = req.ip;
114
+ const requestInfo = { uuid: `${uuid}`, method: `${req.method}`, url: `${req.url}`, ip: `${ip}` };
115
+ asyncHook.getInstance().createRequestContext(requestInfo);
116
+ next();
117
117
  }
118
118
 
119
119
  async function createAppRoute() {
120
- const apps = await db.execute(
121
- `SELECT appName
122
- FROM \`${saasConfig.SAAS_NAME}\`.app_config;
123
- `,
124
- []
125
- );
126
- for (const dd of apps) {
127
- await createAPP(dd);
128
- }
120
+ const apps = await db.execute(
121
+ `SELECT appName
122
+ FROM \`${saasConfig.SAAS_NAME}\`.app_config;
123
+ `,
124
+ []
125
+ );
126
+ for (const dd of apps) {
127
+ await createAPP(dd);
128
+ }
129
129
  }
130
130
 
131
131
  // 信任代理
132
132
  app.set('trust proxy', true);
133
133
 
134
134
  export async function createAPP(dd: any) {
135
- const html = String.raw;
136
- Live_source.liveAPP.push(dd.appName);
137
- // const brand_type=await App.checkBrandAndMemberType(dd.appName)
138
- //SHOPNEX 才可以跑排程,並且需有DOMAIN
139
- // if(brand_type.brand==='shopnex' && brand_type.domain){
140
- Schedule.app.push(dd.appName);
141
- // }
135
+ const html = String.raw;
136
+ Live_source.liveAPP.push(dd.appName);
137
+ // const brand_type=await App.checkBrandAndMemberType(dd.appName)
138
+ //SHOPNEX 才可以跑排程,並且需有DOMAIN
139
+ // if(brand_type.brand==='shopnex' && brand_type.domain){
140
+ Schedule.app.push(dd.appName);
141
+ // }
142
142
 
143
- const file_path = path.resolve(__dirname, '../lowcode');
144
- return await GlitterUtil.set_frontend_v2(
145
- app,
146
- ['/' + encodeURI(dd.appName) + '/*', '/' + encodeURI(dd.appName)].map((rout) => {
147
- return {
148
- rout: rout,
149
- path: file_path,
150
- app_name: dd.appName,
151
- root_path: '/' + encodeURI(dd.appName) + '/',
152
- seoManager: async (req) => {
153
- const og_url = req.headers['x-original-url'];
154
- const custom_heads:string[]=[];
143
+ const file_path = path.resolve(__dirname, '../lowcode');
144
+ return await GlitterUtil.set_frontend_v2(
145
+ app,
146
+ ['/' + encodeURI(dd.appName) + '/*', '/' + encodeURI(dd.appName)].map(rout => {
147
+ return {
148
+ rout: rout,
149
+ path: file_path,
150
+ app_name: dd.appName,
151
+ root_path: '/' + encodeURI(dd.appName) + '/',
152
+ seoManager: async req => {
153
+ const og_url = req.headers['x-original-url'];
155
154
 
156
- console.log(`req.query.page=>`,req.query.page)
157
- try {
158
- if (req.query.state === 'google_login') {
159
- req.query.page = 'login';
160
- }
161
- let appName = dd.appName;
162
- if (req.query.appName) {
163
- appName = req.query.appName;
164
- } else if (og_url) {
165
- const new_app = (await db.query(`SELECT * FROM \`${saasConfig.SAAS_NAME}\`.app_config where LOWER(domain) = ?`, [og_url]))[0];
166
- if (new_app && new_app.appName) {
167
- appName = new_app && new_app.appName;
168
- } else {
169
- return {
170
- head: '',
171
- body: `<script>window.location.href='https://shopnex.tw'</script>`,
172
- };
173
- }
174
- }
175
- req.headers['g-app'] = appName;
176
- const start = new Date().getTime();
177
- console.log(`getPageInfo==>`, (new Date().getTime() - start) / 1000);
155
+ console.log(`req.query.page=>`, req.query.page);
156
+ try {
157
+ if (req.query.state === 'google_login') {
158
+ req.query.page = 'login';
159
+ }
160
+ let appName = dd.appName;
161
+ if (req.query.appName) {
162
+ appName = req.query.appName;
163
+ } else if (og_url) {
164
+ const new_app = (
165
+ await db.query(
166
+ `SELECT *
167
+ FROM \`${saasConfig.SAAS_NAME}\`.app_config
168
+ where LOWER(domain) = ?`,
169
+ [og_url]
170
+ )
171
+ )[0];
172
+ if (new_app && new_app.appName) {
173
+ appName = new_app && new_app.appName;
174
+ } else {
175
+ return {
176
+ head: '',
177
+ body: `<script>window.location.href='https://shopnex.tw'</script>`,
178
+ };
179
+ }
180
+ }
181
+ req.headers['g-app'] = appName;
182
+ const start = new Date().getTime();
183
+ console.log(`getPageInfo==>`, (new Date().getTime() - start) / 1000);
184
+ //要進行initial避免找不到DB
185
+ await ApiPublic.createScheme(appName);
186
+ const find_app = ApiPublic.app301.find(dd => {
187
+ return dd.app_name === appName;
188
+ });
189
+ if (find_app) {
190
+ const router = find_app.router.find(dd => {
191
+ return dd.legacy_url === req.query.page;
192
+ });
193
+ if(router){
178
194
 
179
- //SEO內容
180
- let seo_content: string[] = [];
181
- let [customCode, FBCode, store_info, language_label, check_schema, brandAndMemberType, login_config, ip_country] = await Promise.all([
182
- new User(appName).getConfigV2({
183
- key: 'ga4_config',
184
- user_id: 'manager',
185
- }),
186
- new User(appName).getConfigV2({
187
- key: 'login_fb_setting',
188
- user_id: 'manager',
189
- }),
190
- new User(appName).getConfigV2({
191
- key: 'store-information',
192
- user_id: 'manager',
193
- }),
194
- new User(appName).getConfigV2({
195
- key: 'language-label',
196
- user_id: 'manager',
197
- }),
198
- ApiPublic.createScheme(appName),
199
- App.checkBrandAndMemberType(appName),
200
- new User(req.get('g-app') as string, req.body.token).getConfigV2({
201
- key: 'login_config',
202
- user_id: 'manager',
203
- }),
204
- User.ipInfo((req.query.ip || req.headers['x-real-ip'] || req.ip) as string),
205
- ]);
206
- //取得多國語言
207
- const language: any = await SeoConfig.language(store_info, req);
208
- //插入瀏覽紀錄
209
- Monitor.insertHistory({
210
- req_type: 'file',
211
- req: req,
212
- });
213
- //取得SEO頁面資訊
214
- let data = await Seo.getPageInfo(appName, req.query.page as string, language);
215
- //首頁SEO
216
- let home_page_data = await (async () => {
217
- return await Seo.getPageInfo(appName, 'index', language);
218
- })();
219
- if (data && data.page_config) {
220
- data.page_config = data.page_config ?? {};
221
- const d = data.page_config.seo ?? {};
222
- //商品搜索
223
- if (`${req.query.page}`.startsWith('products/')) {
224
- await SeoConfig.productSEO({
225
- data,
226
- language,
227
- appName,
228
- product_id: req.query.product_id as any,
229
- page: req.query.page as any,
230
- });
231
- } else if (`${req.query.page}` === ('blogs')) {
232
- //網誌目錄
233
- const seo=await new User(req.get('g-app') as string, req.body.token).getConfigV2({
234
- key: 'article_seo_data_'+language,
235
- user_id: 'manager',
236
- });
237
- data.page_config.seo.title = seo.title || data.page_config.seo.title;
238
- data.page_config.seo.content = seo.content || data.page_config.seo.content;
239
- data.page_config.seo.keywords = seo.keywords || data.page_config.seo.keywords;
240
- data.page_config.seo.image = seo.image || data.page_config.seo.image;
241
- data.page_config.seo.logo = seo.logo || data.page_config.seo.logo;
242
- }else if (`${req.query.page}`.startsWith('blogs/')) {
243
- //網誌搜索
244
- data.page_config.seo = await SeoConfig.articleSeo({
245
- article: req.query.article as any,
246
- page: req.query.page as any,
247
- language,
248
- appName,
249
- data,
250
- });
251
- } else if (`${req.query.page}`.startsWith('pages/')) {
252
- //頁面搜索
253
- await SeoConfig.articleSeo({
254
- article: req.query.article as any,
255
- page: req.query.page as any,
256
- language,
257
- appName,
258
- data,
259
- });
260
- }else if(['privacy','term','refund','delivery'].includes(`${req.query.page}`)){
261
- data.page_config.seo={
262
- title:Language.text(`${req.query.page}`,language),
263
- content:Language.text(`${req.query.page}`,language)
264
- }
265
- }else if (d.type !== 'custom') {
266
- data = home_page_data;
267
- }
268
- const preload = req.query.isIframe === 'true' ? {} : await App.preloadPageData(appName, req.query.page as any, language);
269
- data.page_config = data.page_config ?? {};
270
- data.page_config.seo = data.page_config.seo ?? {};
271
- const seo_detail = await getSeoDetail(appName, req);
272
- if (seo_detail) {
273
- Object.keys(seo_detail).map((dd) => {
274
- data.page_config.seo[dd] = seo_detail[dd];
275
- });
276
- }
277
- let link_prefix = req.originalUrl.split('/')[1];
278
- if (link_prefix.includes('?')) {
279
- link_prefix = link_prefix.substring(0, link_prefix.indexOf('?'));
280
- }
281
- if (ConfigSetting.is_local) {
282
- if (link_prefix !== 'shopnex' && link_prefix !== 'codenex_v2') {
283
- link_prefix = '';
284
- }
285
- } else {
286
- link_prefix = '';
287
- }
288
- let distribution_code = '';
289
- req.query.page = req.query.page || 'index';
290
- if ((req.query.page as string).split('/')[0] === 'order_detail' && req.query.EndCheckout === '1') {
291
- distribution_code = `
195
+ return {
196
+ redirect: `https://${(await App.checkBrandAndMemberType(appName)).domain}/${router.new_url}`,
197
+ }
198
+ }
199
+ }
200
+ //SEO內容
201
+ let seo_content: string[] = [];
202
+ let [
203
+ customCode,
204
+ FBCode,
205
+ store_info,
206
+ language_label,
207
+ check_schema,
208
+ brandAndMemberType,
209
+ login_config,
210
+ ip_country,
211
+ ] = await Promise.all([
212
+ new User(appName).getConfigV2({
213
+ key: 'ga4_config',
214
+ user_id: 'manager',
215
+ }),
216
+ new User(appName).getConfigV2({
217
+ key: 'login_fb_setting',
218
+ user_id: 'manager',
219
+ }),
220
+ new User(appName).getConfigV2({
221
+ key: 'store-information',
222
+ user_id: 'manager',
223
+ }),
224
+ new User(appName).getConfigV2({
225
+ key: 'language-label',
226
+ user_id: 'manager',
227
+ }),
228
+ ApiPublic.createScheme(appName),
229
+ App.checkBrandAndMemberType(appName),
230
+ new User(req.get('g-app') as string, req.body.token).getConfigV2({
231
+ key: 'login_config',
232
+ user_id: 'manager',
233
+ }),
234
+ User.ipInfo((req.query.ip || req.headers['x-real-ip'] || req.ip) as string),
235
+ ]);
236
+ //取得多國語言
237
+ const language: any = await SeoConfig.language(store_info, req);
238
+ //插入瀏覽紀錄
239
+ Monitor.insertHistory({
240
+ req_type: 'file',
241
+ req: req,
242
+ });
243
+ //取得SEO頁面資訊
244
+ let data = await Seo.getPageInfo(appName, req.query.page as string, language);
245
+ //首頁SEO
246
+ let home_page_data = await (async () => {
247
+ return await Seo.getPageInfo(appName, 'index', language);
248
+ })();
249
+ if (data && data.page_config) {
250
+ data.page_config = data.page_config ?? {};
251
+ const d = data.page_config.seo ?? {};
252
+ //商品搜索
253
+ if (`${req.query.page}`.startsWith('products/')) {
254
+ await SeoConfig.productSEO({
255
+ data,
256
+ language,
257
+ appName,
258
+ product_id: req.query.product_id as any,
259
+ page: req.query.page as any,
260
+ });
261
+ } else if (`${req.query.page}` === 'blogs') {
262
+ //網誌目錄
263
+ const seo = await new User(req.get('g-app') as string, req.body.token).getConfigV2({
264
+ key: 'article_seo_data_' + language,
265
+ user_id: 'manager',
266
+ });
267
+ data.page_config.seo.title = seo.title || data.page_config.seo.title;
268
+ data.page_config.seo.content = seo.content || data.page_config.seo.content;
269
+ data.page_config.seo.keywords = seo.keywords || data.page_config.seo.keywords;
270
+ data.page_config.seo.image = seo.image || data.page_config.seo.image;
271
+ data.page_config.seo.logo = seo.logo || data.page_config.seo.logo;
272
+ } else if (`${req.query.page}`.startsWith('blogs/')) {
273
+ //網誌搜索
274
+ data.page_config.seo = await SeoConfig.articleSeo({
275
+ article: req.query.article as any,
276
+ page: req.query.page as any,
277
+ language,
278
+ appName,
279
+ data,
280
+ });
281
+ } else if (`${req.query.page}`.startsWith('pages/')) {
282
+ //頁面搜索
283
+ await SeoConfig.articleSeo({
284
+ article: req.query.article as any,
285
+ page: req.query.page as any,
286
+ language,
287
+ appName,
288
+ data,
289
+ });
290
+ } else if (['privacy', 'term', 'refund', 'delivery'].includes(`${req.query.page}`)) {
291
+ data.page_config.seo = {
292
+ title: Language.text(`${req.query.page}`, language),
293
+ content: Language.text(`${req.query.page}`, language),
294
+ };
295
+ } else if (d.type !== 'custom') {
296
+ data = home_page_data;
297
+ }
298
+ const preload =
299
+ req.query.isIframe === 'true'
300
+ ? {}
301
+ : await App.preloadPageData(appName, req.query.page as any, language);
302
+ data.page_config = data.page_config ?? {};
303
+ data.page_config.seo = data.page_config.seo ?? {};
304
+ const seo_detail = await getSeoDetail(appName, req);
305
+ if (seo_detail) {
306
+ Object.keys(seo_detail).map(dd => {
307
+ data.page_config.seo[dd] = seo_detail[dd];
308
+ });
309
+ }
310
+ let link_prefix = req.originalUrl.split('/')[1];
311
+ if (link_prefix.includes('?')) {
312
+ link_prefix = link_prefix.substring(0, link_prefix.indexOf('?'));
313
+ }
314
+ if (ConfigSetting.is_local) {
315
+ if (link_prefix !== 'shopnex' && link_prefix !== 'codenex_v2') {
316
+ link_prefix = '';
317
+ }
318
+ } else {
319
+ link_prefix = '';
320
+ }
321
+ let distribution_code = '';
322
+ req.query.page = req.query.page || 'index';
323
+ if ((req.query.page as string).split('/')[0] === 'order_detail' && req.query.EndCheckout === '1') {
324
+ distribution_code = `
292
325
  localStorage.setItem('distributionCode','');
293
326
  `;
294
- }
295
- //分銷連結頁面SEO
296
- if ((req.query.page as string).split('/')[0] === 'distribution' && (req.query.page as string).split('/')[1]) {
297
- distribution_code = await SeoConfig.distributionSEO({
298
- appName: appName,
299
- url: req.url,
300
- page: req.query.page as string,
301
- link_prefix: link_prefix,
302
- data,
303
- language,
304
- });
305
- }
306
- //分類頁面SEO
307
- if ((req.query.page as string).split('/')[0] === 'collections' && (req.query.page as string).split('/')[1]) {
308
- await SeoConfig.collectionSeo({ appName, language, data, page: req.query.page as string });
309
- }
310
- //FB像素
311
- if (FBCode) {
312
- seo_content.push(SeoConfig.fbCode(FBCode));
313
- }
327
+ }
328
+ //分銷連結頁面SEO
329
+ if (
330
+ (req.query.page as string).split('/')[0] === 'distribution' &&
331
+ (req.query.page as string).split('/')[1]
332
+ ) {
333
+ distribution_code = await SeoConfig.distributionSEO({
334
+ appName: appName,
335
+ url: req.url,
336
+ page: req.query.page as string,
337
+ link_prefix: link_prefix,
338
+ data,
339
+ language,
340
+ });
341
+ }
342
+ //分類頁面SEO
343
+ if (
344
+ (req.query.page as string).split('/')[0] === 'collections' &&
345
+ (req.query.page as string).split('/')[1]
346
+ ) {
347
+ await SeoConfig.collectionSeo({ appName, language, data, page: req.query.page as string });
348
+ }
349
+ //FB像素
350
+ if (FBCode) {
351
+ seo_content.push(SeoConfig.fbCode(FBCode));
352
+ }
353
+
354
+ const home_seo = home_page_data.page_config.seo || {};
355
+ const head = [
356
+ (() => {
357
+ const d = data.page_config.seo;
358
+ return html`
359
+ ${(() => {
360
+ if (req.query.type === 'editor') {
361
+ return SeoConfig.editorSeo;
362
+ } else {
363
+ return html`<title>
364
+ ${[home_seo.title_prefix || '', d.title || '', home_seo.title_suffix || ''].join('') ||
365
+ '尚未設定標題'}
366
+ </title>
367
+ <link
368
+ rel="canonical"
369
+ href="${(() => {
370
+ if (data.tag === 'index') {
371
+ return `https://${brandAndMemberType.domain}`;
372
+ } else {
373
+ return `https://${brandAndMemberType.domain}/${data.tag}`;
374
+ }
375
+ })()}"
376
+ />
377
+ ${data.tag !== req.query.page || req.query.page === 'index-mobile'
378
+ ? `<meta name="robots" content="noindex">`
379
+ : `<meta name="robots" content="index, follow"/>`}
380
+ <meta name="keywords" content="${(d.keywords || '尚未設定關鍵字').replace(/"/g, '&quot;')}" />
381
+ <link
382
+ id="appImage"
383
+ rel="shortcut icon"
384
+ href="${d.logo || home_seo.logo || ''}"
385
+ type="image/x-icon"
386
+ />
387
+ <link rel="icon" href="${d.logo || home_seo.logo || ''}" type="image/png" sizes="128x128" />
388
+ <meta property="og:image" content="${d.image || home_seo.image || ''}" />
389
+ <meta
390
+ property="og:title"
391
+ content="${(d.title ?? '').replace(/\n/g, '').replace(/"/g, '&quot;')}"
392
+ />
393
+ <meta
394
+ name="description"
395
+ content="${(d.content ?? '').replace(/\n/g, '').replace(/"/g, '&quot;')}"
396
+ />
397
+ <meta
398
+ name="og:description"
399
+ content="${(d.content ?? '').replace(/\n/g, '').replace(/"/g, '&quot;')}"
400
+ />
314
401
 
315
- const home_seo = home_page_data.page_config.seo || {};
316
- const head =[
317
- (() => {
318
- const d = data.page_config.seo;
319
- return html`
320
- ${(() => {
321
- if (req.query.type === 'editor') {
322
- return SeoConfig.editorSeo;
323
- } else {
324
- return html`<title>${[
325
- home_seo.title_prefix || '',
326
- d.title || '',
327
- home_seo.title_suffix || ''
328
- ].join('') || '尚未設定標題'}</title>
329
- <link
330
- rel="canonical"
331
- href="${(() => {
332
- if (data.tag === 'index') {
333
- return `https://${brandAndMemberType.domain}`;
334
- } else {
335
- return `https://${brandAndMemberType.domain}/${data.tag}`;
336
- }
337
- })()}"
338
- />
339
- ${((data.tag !== req.query.page) || (req.query.page==='index-mobile')) ? `<meta name="robots" content="noindex">` : `<meta name="robots" content="index, follow"/>`}
340
- <meta name="keywords" content="${(d.keywords || '尚未設定關鍵字').replace(/"/g, '&quot;')}" />
341
- <link id="appImage" rel="shortcut icon" href="${d.logo || home_seo.logo || ''}" type="image/x-icon" />
342
- <link rel="icon" href="${d.logo || home_seo.logo || ''}" type="image/png" sizes="128x128" />
343
- <meta property="og:image" content="${d.image || home_seo.image || ''}" />
344
- <meta property="og:title" content="${(d.title ?? '').replace(/\n/g, '').replace(/"/g, '&quot;')}" />
345
- <meta name="description" content="${(d.content ?? '').replace(/\n/g, '').replace(/"/g, '&quot;')}" />
346
- <meta name="og:description" content="${(d.content ?? '').replace(/\n/g, '').replace(/"/g, '&quot;')}" />
347
-
348
- ${[
349
- { src: 'css/front-end.css', type: 'text/css' }
350
- ]
351
- .map((dd) => {
352
- return html` <link href="/${link_prefix && `${link_prefix}/`}${dd.src}" type="${dd.type}" rel="stylesheet"></link>`;
353
- })
354
- .join('')}
355
- `;
356
- }
357
- })()}
358
- ${d.code ?? ''}
359
- ${(() => {
360
- if (req.query.type === 'editor') {
361
- return ``;
362
- } else {
363
- return `${(data.config.globalStyle ?? [])
364
- .map((dd: any) => {
365
- try {
366
- if (dd.data.elem === 'link') {
367
- return html` <link
368
- type="text/css"
369
- rel="stylesheet"
370
- href="${dd.data.attr.find((dd: any) => {
371
- return dd.attr === 'href';
372
- }).value}"
373
- />`;
374
- }
375
- } catch (e) {
376
- return ``;
377
- }
378
- })
379
- .join('')}`;
380
- }
381
- })()}
382
- `;
383
- })(),
384
- `<script>
402
+ ${[{ src: 'css/front-end.css', type: 'text/css' }]
403
+ .map(dd => {
404
+ return html`
405
+ <link href="/${link_prefix && `${link_prefix}/`}${dd.src}" type="${dd.type}"
406
+ rel="stylesheet"></link>`;
407
+ })
408
+ .join('')} `;
409
+ }
410
+ })()}
411
+ ${d.code ?? ''}
412
+ ${(() => {
413
+ if (req.query.type === 'editor') {
414
+ return ``;
415
+ } else {
416
+ return `${(data.config.globalStyle ?? [])
417
+ .map((dd: any) => {
418
+ try {
419
+ if (dd.data.elem === 'link') {
420
+ return html` <link
421
+ type="text/css"
422
+ rel="stylesheet"
423
+ href="${dd.data.attr.find((dd: any) => {
424
+ return dd.attr === 'href';
425
+ }).value}"
426
+ />`;
427
+ }
428
+ } catch (e) {
429
+ return ``;
430
+ }
431
+ })
432
+ .join('')}`;
433
+ }
434
+ })()}
435
+ `;
436
+ })(),
437
+ `<script>
385
438
  ${[
386
- (req.query.type !== 'editor' && d.custom_script) ?? '',
387
- `window.login_config = ${JSON.stringify(login_config)};`,
388
- `window.appName = '${appName}';`,
389
- `window.glitterBase = '${brandAndMemberType.brand}';`,
390
- `window.memberType = '${brandAndMemberType.memberType}';`,
391
- `window.glitterBackend = '${config.domain}';`,
392
- `window.preloadData = ${JSON.stringify(preload)
393
- .replace(/<\/script>/g, 'sdjuescript_prepand')
394
- .replace(/<script>/g, 'sdjuescript_prefix')};`,
395
- `window.glitter_page = '${req.query.page}';`,
396
- `window.store_info = ${JSON.stringify(store_info)};`,
397
- `window.server_execute_time = ${(new Date().getTime() - start) / 1000};`,
398
- `window.language = '${language}';`,
399
- `${distribution_code}`,
400
- `window.ip_country = '${ip_country.country || 'TW'}';`,
401
- `window.currency_covert = ${JSON.stringify(await Shopping.currencyCovert((req.query.base || 'TWD') as string))};`,
402
- `window.language_list = ${JSON.stringify(language_label.label)};`,
403
- `window.home_seo=${JSON.stringify(home_seo) .replace(/<\/script>/g, 'sdjuescript_prepand')
404
- .replace(/<script>/g, 'sdjuescript_prefix')};`
439
+ (req.query.type !== 'editor' && d.custom_script) ?? '',
440
+ `window.login_config = ${JSON.stringify(login_config)};`,
441
+ `window.appName = '${appName}';`,
442
+ `window.glitterBase = '${brandAndMemberType.brand}';`,
443
+ `window.memberType = '${brandAndMemberType.memberType}';`,
444
+ `window.glitterBackend = '${config.domain}';`,
445
+ `window.preloadData = ${JSON.stringify(preload)
446
+ .replace(/<\/script>/g, 'sdjuescript_prepand')
447
+ .replace(/<script>/g, 'sdjuescript_prefix')};`,
448
+ `window.glitter_page = '${req.query.page}';`,
449
+ `window.store_info = ${JSON.stringify(store_info)};`,
450
+ `window.server_execute_time = ${(new Date().getTime() - start) / 1000};`,
451
+ `window.language = '${language}';`,
452
+ `${distribution_code}`,
453
+ `window.ip_country = '${ip_country.country || 'TW'}';`,
454
+ `window.currency_covert = ${JSON.stringify(await Shopping.currencyCovert((req.query.base || 'TWD') as string))};`,
455
+ `window.language_list = ${JSON.stringify(language_label.label)};`,
456
+ `window.home_seo=${JSON.stringify(home_seo)
457
+ .replace(/<\/script>/g, 'sdjuescript_prepand')
458
+ .replace(/<script>/g, 'sdjuescript_prefix')};`,
405
459
  ]
406
- .map((dd) => {
407
- return (dd || '').trim();
408
- })
409
- .filter((dd) => {
410
- return dd;
411
- })
412
- .join(';\n')}
460
+ .map(dd => {
461
+ return (dd || '').trim();
462
+ })
463
+ .filter(dd => {
464
+ return dd;
465
+ })
466
+ .join(';\n')}
413
467
  </script>
414
468
  ${[
415
- { src: 'glitterBundle/GlitterInitial.js', type: 'module' },
416
- { src: 'glitterBundle/module/html-generate.js', type: 'module' },
417
- { src: 'glitterBundle/html-component/widget.js', type: 'module' },
418
- { src: 'glitterBundle/plugins/trigger-event.js', type: 'module' },
419
- { src: 'api/pageConfig.js', type: 'module' },
420
- ]
421
- .map((dd) => {
422
- return html` <script src="/${link_prefix && `${link_prefix}/`}${dd.src}" type="${dd.type}"></script>`;
423
- })
424
- .join('')}
469
+ { src: 'glitterBundle/GlitterInitial.js', type: 'module' },
470
+ { src: 'glitterBundle/module/html-generate.js', type: 'module' },
471
+ { src: 'glitterBundle/html-component/widget.js', type: 'module' },
472
+ { src: 'glitterBundle/plugins/trigger-event.js', type: 'module' },
473
+ { src: 'api/pageConfig.js', type: 'module' },
474
+ ]
475
+ .map(dd => {
476
+ return html` <script
477
+ src="/${link_prefix && `${link_prefix}/`}${dd.src}"
478
+ type="${dd.type}"
479
+ ></script>`;
480
+ })
481
+ .join('')}
425
482
  ${(preload.event ?? [])
426
- .filter((dd: any) => {
427
- return dd;
428
- })
429
- .map((dd: any) => {
430
- const link = dd.fun.replace(`TriggerEvent.setEventRouter(import.meta.url, '.`, 'official_event');
431
- return link.substring(0, link.length - 2);
432
- })
433
- .map((dd: any) => html` <script src="/${link_prefix && `${link_prefix}/`}${dd}" type="module"></script>`)
434
- .join('')}
483
+ .filter((dd: any) => {
484
+ return dd;
485
+ })
486
+ .map((dd: any) => {
487
+ const link = dd.fun.replace(
488
+ `TriggerEvent.setEventRouter(import.meta.url, '.`,
489
+ 'official_event'
490
+ );
491
+ return link.substring(0, link.length - 2);
492
+ })
493
+ .map(
494
+ (dd: any) =>
495
+ html` <script src="/${link_prefix && `${link_prefix}/`}${dd}" type="module"></script>`
496
+ )
497
+ .join('')}
435
498
  ${(() => {
436
- if (req.query.type === 'editor') {
437
- return ``;
438
- } else {
439
- return html`
440
- ${SeoConfig.gA4(customCode.ga4)} ${SeoConfig.gTag(customCode.g_tag)}
441
- ${seo_content
442
- .map((dd) => {
443
- return dd.trim();
444
- })
445
- .join('\n')}
446
- `;
447
- }
448
- })()}`
449
- ].join('')
450
- return {
451
- head: head,
452
- body: ``,
453
- };
454
- } else {
455
- return {
456
- head: await Seo.redirectToHomePage(appName, req),
457
- body: ``,
458
- };
459
- }
460
- } catch (e: any) {
461
- console.error(e);
462
- return {
463
- head: ``,
464
- body: `${e}`,
465
- };
466
- }
467
- },
468
- sitemap: async (req, resp) => {
469
- let appName = dd.appName;
470
- if (req.query.appName) {
471
- appName = req.query.appName;
472
- }
473
- let query = [`(content->>'$.type'='article')`];
474
- const article: any = await new UtDatabase(appName, `t_manager_post`).querySql(query, {
475
- page: 0,
476
- limit: 10000,
477
- });
478
- const domain = (
479
- await db.query(
480
- `select \`domain\`
481
- from \`${saasConfig.SAAS_NAME}\`.app_config
482
- where appName = ?`,
483
- [appName]
484
- )
485
- )[0]['domain'];
499
+ if (req.query.type === 'editor') {
500
+ return ``;
501
+ } else {
502
+ return html`
503
+ ${SeoConfig.gA4(customCode.ga4)} ${SeoConfig.gTag(customCode.g_tag)}
504
+ ${seo_content
505
+ .map(dd => {
506
+ return dd.trim();
507
+ })
508
+ .join('\n')}
509
+ `;
510
+ }
511
+ })()}`,
512
+ ].join('');
513
+ return {
514
+ head: head,
515
+ body: ``,
516
+ };
517
+ } else {
518
+ return {
519
+ head: await Seo.redirectToHomePage(appName, req),
520
+ body: ``,
521
+ };
522
+ }
523
+ } catch (e: any) {
524
+ console.error(e);
525
+ return {
526
+ head: ``,
527
+ body: `${e}`,
528
+ };
529
+ }
530
+ },
531
+ sitemap: async (req, resp) => {
532
+ let appName = dd.appName;
533
+ if (req.query.appName) {
534
+ appName = req.query.appName;
535
+ }
536
+ let query = [`(content->>'$.type'='article')`];
537
+ const article: any = await new UtDatabase(appName, `t_manager_post`).querySql(query, {
538
+ page: 0,
539
+ limit: 10000,
540
+ });
541
+ const domain = (
542
+ await db.query(
543
+ `select \`domain\`
544
+ from \`${saasConfig.SAAS_NAME}\`.app_config
545
+ where appName = ?`,
546
+ [appName]
547
+ )
548
+ )[0]['domain'];
486
549
 
487
- const site_map = await getSeoSiteMap(appName, req);
550
+ const site_map = await getSeoSiteMap(appName, req);
488
551
 
489
- const cols =
490
- (
491
- await db.query(
492
- `SELECT *
493
- FROM \`${appName}\`.public_config
494
- WHERE \`key\` = 'collection';`,
495
- []
496
- )
497
- )[0] ?? {};
552
+ const cols =
553
+ (
554
+ await db.query(
555
+ `SELECT *
556
+ FROM \`${appName}\`.public_config
557
+ WHERE \`key\` = 'collection';`,
558
+ []
559
+ )
560
+ )[0] ?? {};
498
561
 
499
- const language_setting = (
500
- await new User(appName).getConfigV2({
501
- key: 'store-information',
502
- user_id: 'manager',
503
- })
504
- ).language_setting;
562
+ const language_setting = (
563
+ await new User(appName).getConfigV2({
564
+ key: 'store-information',
565
+ user_id: 'manager',
566
+ })
567
+ ).language_setting;
505
568
 
506
- const product = (
507
- await new Shopping(appName).getProduct({
508
- page: 0,
509
- limit: 100000,
510
- collection: '',
511
- accurate_search_text: false,
512
- accurate_search_collection: true,
513
- min_price: undefined,
514
- max_price: undefined,
515
- status: undefined,
516
- channel: undefined,
517
- id_list: undefined,
518
- order_by: 'order by id desc',
519
- with_hide_index: undefined,
520
- is_manger: true,
521
- productType: 'product',
522
- filter_visible: 'true',
523
- language: 'zh-TW',
524
- currency_code: 'TWD',
525
- })
526
- ).data;
527
- // 創建 SitemapStream
528
- const stream = new SitemapStream({ hostname: `https://${domain}` });
569
+ const product = (
570
+ await new Shopping(appName).getProduct({
571
+ page: 0,
572
+ limit: 100000,
573
+ collection: '',
574
+ accurate_search_text: false,
575
+ accurate_search_collection: true,
576
+ min_price: undefined,
577
+ max_price: undefined,
578
+ status: undefined,
579
+ channel: undefined,
580
+ id_list: undefined,
581
+ order_by: 'order by id desc',
582
+ with_hide_index: undefined,
583
+ is_manger: true,
584
+ productType: 'product',
585
+ filter_visible: 'true',
586
+ language: 'zh-TW',
587
+ currency_code: 'TWD',
588
+ })
589
+ ).data;
590
+ // 創建 SitemapStream
591
+ const stream = new SitemapStream({ hostname: `https://${domain}` });
529
592
 
530
- // 將 links 添加到 stream
531
- const xml = await streamToPromise(
532
- Readable.from([
533
- ...(
534
- await db.query(
535
- `select page_config, tag, updated_time
536
- from \`${saasConfig.SAAS_NAME}\`.page_config
537
- where appName = ?
538
- and page_config ->>'$.seo.type'='custom'
539
- `,
540
- [appName]
541
- )
542
- ).map((d2: any) => {
543
- if (d2.tag === 'index') {
544
- return { url: `https://${domain}`, changefreq: 'weekly' };
545
- } else {
546
- return { url: `https://${domain}/${d2.tag}`, changefreq: 'weekly' };
547
- }
548
- }),
549
- ...article.data
550
- .filter((d2: any) => {
551
- return d2.content.template;
552
- })
553
- .map((d2: any) => {
554
- return {
555
- url: `https://${domain}/${d2.content.for_index === 'false' ? `pages` : `blogs`}/${d2.content.tag}`,
556
- changefreq: 'weekly',
557
- lastmod: formatDateToISO(new Date(d2.updated_time)),
558
- };
559
- }),
560
- ...(site_map || []).map((d2: any) => {
561
- return { url: `https://${domain}/${d2.url}`, changefreq: 'weekly' };
562
- }),
563
- ...(() => {
564
- let array: string[] = [];
565
- extractCols(cols).map((item: any) => {
566
- array = array.concat(
567
- language_setting.support.map((d1: any) => {
568
- const seo = (item.language_data && item.language_data[d1] && item.language_data[d1].seo && item.language_data[d1].seo.domain) || item.code || item.title;
569
- if (d1 === language_setting.def) {
570
- return { url: `https://${domain}/collections/${seo}`, changefreq: 'weekly' };
571
- } else if (d1 === 'zh-TW') {
572
- return { url: `https://${domain}/tw/collections/${seo}`, changefreq: 'weekly' };
573
- } else if (d1 === 'zh-CN') {
574
- return { url: `https://${domain}/cn/collections/${seo}`, changefreq: 'weekly' };
575
- } else if (d1 === 'en-US') {
576
- return { url: `https://${domain}/en/collections/${seo}`, changefreq: 'weekly' };
577
- } else {
578
- return { url: `https://${domain}/${d1}/collections/${seo}`, changefreq: 'weekly' };
579
- }
580
- })
581
- );
582
- });
583
- return array;
584
- })(),
585
- ...(() => {
586
- let array: string[] = [];
587
- product.map((dd: any) => {
588
- dd = dd.content;
589
- array = array.concat(
590
- language_setting.support.map((d1: any) => {
591
- const seo = (dd.language_data && dd.language_data[d1] && dd.language_data[d1].seo && dd.language_data[d1].seo.domain) || dd.seo.domain;
592
- if (d1 === language_setting.def) {
593
- return { url: `https://${domain}/products/${seo}`, changefreq: 'weekly' };
594
- } else if (d1 === 'zh-TW') {
595
- return { url: `https://${domain}/tw/products/${seo}`, changefreq: 'weekly' };
596
- } else if (d1 === 'zh-CN') {
597
- return { url: `https://${domain}/cn/products/${seo}`, changefreq: 'weekly' };
598
- } else if (d1 === 'en-US') {
599
- return { url: `https://${domain}/en/products/${seo}`, changefreq: 'weekly' };
600
- } else {
601
- return { url: `https://${domain}/${d1}/products/${seo}`, changefreq: 'weekly' };
602
- }
603
- })
604
- );
605
- });
593
+ // 將 links 添加到 stream
594
+ const xml = await streamToPromise(
595
+ Readable.from(
596
+ [
597
+ ...(
598
+ await db.query(
599
+ `select page_config, tag, updated_time
600
+ from \`${saasConfig.SAAS_NAME}\`.page_config
601
+ where appName = ?
602
+ and page_config ->>'$.seo.type'='custom'
603
+ `,
604
+ [appName]
605
+ )
606
+ ).map((d2: any) => {
607
+ if (d2.tag === 'index') {
608
+ return { url: `https://${domain}`, changefreq: 'weekly' };
609
+ } else {
610
+ return { url: `https://${domain}/${d2.tag}`, changefreq: 'weekly' };
611
+ }
612
+ }),
613
+ ...article.data
614
+ .filter((d2: any) => {
615
+ return d2.content.template;
616
+ })
617
+ .map((d2: any) => {
618
+ return {
619
+ url: `https://${domain}/${d2.content.for_index === 'false' ? `pages` : `blogs`}/${d2.content.tag}`,
620
+ changefreq: 'weekly',
621
+ lastmod: formatDateToISO(new Date(d2.updated_time)),
622
+ };
623
+ }),
624
+ ...(site_map || []).map((d2: any) => {
625
+ return { url: `https://${domain}/${d2.url}`, changefreq: 'weekly' };
626
+ }),
627
+ ...(() => {
628
+ let array: string[] = [];
629
+ extractCols(cols).map((item: any) => {
630
+ array = array.concat(
631
+ language_setting.support.map((d1: any) => {
632
+ const seo =
633
+ (item.language_data &&
634
+ item.language_data[d1] &&
635
+ item.language_data[d1].seo &&
636
+ item.language_data[d1].seo.domain) ||
637
+ item.code ||
638
+ item.title;
639
+ if (d1 === language_setting.def) {
640
+ return { url: `https://${domain}/collections/${seo}`, changefreq: 'weekly' };
641
+ } else if (d1 === 'zh-TW') {
642
+ return { url: `https://${domain}/tw/collections/${seo}`, changefreq: 'weekly' };
643
+ } else if (d1 === 'zh-CN') {
644
+ return { url: `https://${domain}/cn/collections/${seo}`, changefreq: 'weekly' };
645
+ } else if (d1 === 'en-US') {
646
+ return { url: `https://${domain}/en/collections/${seo}`, changefreq: 'weekly' };
647
+ } else {
648
+ return { url: `https://${domain}/${d1}/collections/${seo}`, changefreq: 'weekly' };
649
+ }
650
+ })
651
+ );
652
+ });
653
+ return array;
654
+ })(),
655
+ ...(() => {
656
+ let array: string[] = [];
657
+ product.map((dd: any) => {
658
+ dd = dd.content;
659
+ array = array.concat(
660
+ language_setting.support.map((d1: any) => {
661
+ const seo =
662
+ (dd.language_data &&
663
+ dd.language_data[d1] &&
664
+ dd.language_data[d1].seo &&
665
+ dd.language_data[d1].seo.domain) ||
666
+ dd.seo.domain;
667
+ if (d1 === language_setting.def) {
668
+ return { url: `https://${domain}/products/${seo}`, changefreq: 'weekly' };
669
+ } else if (d1 === 'zh-TW') {
670
+ return { url: `https://${domain}/tw/products/${seo}`, changefreq: 'weekly' };
671
+ } else if (d1 === 'zh-CN') {
672
+ return { url: `https://${domain}/cn/products/${seo}`, changefreq: 'weekly' };
673
+ } else if (d1 === 'en-US') {
674
+ return { url: `https://${domain}/en/products/${seo}`, changefreq: 'weekly' };
675
+ } else {
676
+ return { url: `https://${domain}/${d1}/products/${seo}`, changefreq: 'weekly' };
677
+ }
678
+ })
679
+ );
680
+ });
606
681
 
607
- return array;
608
- })(),
609
- ].filter((dd)=>{
610
- return dd.url!==`https://${domain}/blogs`
611
- }).concat([
612
- { url: `https://${domain}/blogs`, changefreq: 'weekly' }
613
- ])).pipe(stream)
614
- ).then((data: any) => data.toString());
682
+ return array;
683
+ })(),
684
+ ]
685
+ .filter(dd => {
686
+ return dd.url !== `https://${domain}/blogs`;
687
+ })
688
+ .concat([{ url: `https://${domain}/blogs`, changefreq: 'weekly' }])
689
+ ).pipe(stream)
690
+ ).then((data: any) => data.toString());
615
691
 
616
- return xml;
617
- },
618
- sitemap_list: async (req, resp) => {
619
- let appName = dd.appName;
620
- if (req.query.appName) {
621
- appName = req.query.appName;
622
- }
623
- const domain = (
624
- await db.query(
625
- `select \`domain\`
626
- from \`${saasConfig.SAAS_NAME}\`.app_config
627
- where appName = ?`,
628
- [appName]
629
- )
630
- )[0]['domain'];
631
- return `<?xml version="1.0" encoding="UTF-8"?>
692
+ return xml;
693
+ },
694
+ sitemap_list: async (req, resp) => {
695
+ let appName = dd.appName;
696
+ if (req.query.appName) {
697
+ appName = req.query.appName;
698
+ }
699
+ const domain = (
700
+ await db.query(
701
+ `select \`domain\`
702
+ from \`${saasConfig.SAAS_NAME}\`.app_config
703
+ where appName = ?`,
704
+ [appName]
705
+ )
706
+ )[0]['domain'];
707
+ return `<?xml version="1.0" encoding="UTF-8"?>
632
708
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
633
709
  <!-- 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. -->
634
710
  <sitemap>
635
711
  <loc>https://${domain}/sitemap_detail.xml</loc>
636
712
  </sitemap>
637
713
  </sitemapindex> `;
638
- },
639
- robots: async (req, resp) => {
640
- let appName = dd.appName;
641
- if (req.query.appName) {
642
- appName = req.query.appName;
643
- }
644
- const robots = await new User(appName).getConfigV2({
645
- key: 'robots_text',
646
- user_id: 'manager',
647
- });
648
- robots.text = robots.text || '';
649
- const domain = (
650
- await db.query(
651
- `select \`domain\`
652
- from \`${saasConfig.SAAS_NAME}\`.app_config
653
- where appName = ?`,
654
- [appName]
655
- )
656
- )[0]['domain'];
657
- return robots.text.replace(/\s+/g, '').replace(/\n/g, '') ? robots.text : html`User-agent: * Allow: / Sitemap: https://${domain}/sitemap.xml`;
658
- },
659
- tw_shop: async (req, resp) => {
660
- let appName = dd.appName;
661
- const escapeHtml = (text: string): string => {
662
- const map: Record<string, string> = {
663
- '&': '&amp;',
664
- '<': '&lt;',
665
- '>': '&gt;',
666
- '"': '&quot;',
667
- "'": '&#039;',
668
- };
669
- return text.replace(/[&<>"']/g, (m) => map[m] || m);
670
- };
671
- if (req.query.appName) {
672
- appName = req.query.appName;
673
- }
674
- const products = await db.query(
675
- `SELECT *
676
- FROM \`${dd.appName}\`.t_manager_post
677
- WHERE JSON_EXTRACT(content, '$.type') = 'product';
678
- `,
679
- []
680
- );
681
- const domain = (
682
- await db.query(
683
- `select \`domain\`
684
- from \`${saasConfig.SAAS_NAME}\`.app_config
685
- where appName = ?`,
686
- [appName]
687
- )
688
- )[0]['domain'];
689
- let printData = products
690
- .map((product: any) => {
691
- return product.content.variants
692
- .map((variant: any) => {
693
- return html`
694
- <Product>
695
- <SKU>${variant.sku}</SKU>
696
- <Name>${product.content.title}</Name>
697
- <Description>${dd.appName} - ${product.content.title}</Description>
698
- <URL> ${`https://` + domain + '/products/' + product.content.title}</URL>
699
- <Price>${variant.compare_price ?? variant.sale_price}</Price>
700
- <LargeImage> ${variant.preview_image ?? ''}</LargeImage>
701
- <SalePrice>${variant.sale_price}</SalePrice>
702
- <Category>${product.content.collection.join('')}</Category>
703
- </Product>
704
- `;
705
- })
706
- .join('');
707
- })
708
- .join('');
709
- return xmlFormatter(`<Product>${printData}</Product>`, {
710
- indentation: ' ', // 使用兩個空格進行縮進
711
- filter: (node) => node.type !== 'Comment', // 選擇性過濾節點
712
- collapseContent: true, // 折疊內部文本
713
- });
714
- },
714
+ },
715
+ robots: async (req, resp) => {
716
+ let appName = dd.appName;
717
+ if (req.query.appName) {
718
+ appName = req.query.appName;
719
+ }
720
+ const robots = await new User(appName).getConfigV2({
721
+ key: 'robots_text',
722
+ user_id: 'manager',
723
+ });
724
+ robots.text = robots.text || '';
725
+ const domain = (
726
+ await db.query(
727
+ `select \`domain\`
728
+ from \`${saasConfig.SAAS_NAME}\`.app_config
729
+ where appName = ?`,
730
+ [appName]
731
+ )
732
+ )[0]['domain'];
733
+ return robots.text.replace(/\s+/g, '').replace(/\n/g, '')
734
+ ? robots.text
735
+ : html`User-agent: * Allow: / Sitemap: https://${domain}/sitemap.xml`;
736
+ },
737
+ tw_shop: async (req, resp) => {
738
+ let appName = dd.appName;
739
+ const escapeHtml = (text: string): string => {
740
+ const map: Record<string, string> = {
741
+ '&': '&amp;',
742
+ '<': '&lt;',
743
+ '>': '&gt;',
744
+ '"': '&quot;',
745
+ "'": '&#039;',
715
746
  };
716
- })
717
- );
747
+ return text.replace(/[&<>"']/g, m => map[m] || m);
748
+ };
749
+ if (req.query.appName) {
750
+ appName = req.query.appName;
751
+ }
752
+ const products = await db.query(
753
+ `SELECT *
754
+ FROM \`${dd.appName}\`.t_manager_post
755
+ WHERE JSON_EXTRACT(content, '$.type') = 'product';
756
+ `,
757
+ []
758
+ );
759
+ const domain = (
760
+ await db.query(
761
+ `select \`domain\`
762
+ from \`${saasConfig.SAAS_NAME}\`.app_config
763
+ where appName = ?`,
764
+ [appName]
765
+ )
766
+ )[0]['domain'];
767
+ let printData = products
768
+ .map((product: any) => {
769
+ return product.content.variants
770
+ .map((variant: any) => {
771
+ return html`
772
+ <Product>
773
+ <SKU>${variant.sku}</SKU>
774
+ <Name>${product.content.title}</Name>
775
+ <Description>${dd.appName} - ${product.content.title}</Description>
776
+ <URL> ${`https://` + domain + '/products/' + product.content.title}</URL>
777
+ <Price>${variant.compare_price ?? variant.sale_price}</Price>
778
+ <LargeImage> ${variant.preview_image ?? ''}</LargeImage>
779
+ <SalePrice>${variant.sale_price}</SalePrice>
780
+ <Category>${product.content.collection.join('')}</Category>
781
+ </Product>
782
+ `;
783
+ })
784
+ .join('');
785
+ })
786
+ .join('');
787
+ return xmlFormatter(`<Product>${printData}</Product>`, {
788
+ indentation: ' ', // 使用兩個空格進行縮進
789
+ filter: node => node.type !== 'Comment', // 選擇性過濾節點
790
+ collapseContent: true, // 折疊內部文本
791
+ });
792
+ },
793
+ };
794
+ })
795
+ );
718
796
  }
719
797
 
720
798
  async function getSeoDetail(appName: string, req: any) {
721
- const sqlData = await Private_config.getConfig({
722
- appName: appName,
723
- key: 'seo_webhook',
724
- });
725
- if (!sqlData[0] || !sqlData[0].value) {
726
- return undefined;
727
- }
728
- const html = String.raw;
729
- return await db.queryLambada(
799
+ const sqlData = await Private_config.getConfig({
800
+ appName: appName,
801
+ key: 'seo_webhook',
802
+ });
803
+ if (!sqlData[0] || !sqlData[0].value) {
804
+ return undefined;
805
+ }
806
+ const html = String.raw;
807
+ return await db.queryLambada(
808
+ {
809
+ database: appName,
810
+ },
811
+ async db => {
812
+ (db as any).execute = (db as any).query;
813
+ const functionValue: { key: string; data: () => any }[] = [
730
814
  {
731
- database: appName,
815
+ key: 'db',
816
+ data: () => {
817
+ return db;
818
+ },
732
819
  },
733
- async (db) => {
734
- (db as any).execute = (db as any).query;
735
- const functionValue: { key: string; data: () => any }[] = [
736
- {
737
- key: 'db',
738
- data: () => {
739
- return db;
740
- },
741
- },
742
- {
743
- key: 'req',
744
- data: () => {
745
- return req;
746
- },
747
- },
748
- ];
749
- const evalString = `
820
+ {
821
+ key: 'req',
822
+ data: () => {
823
+ return req;
824
+ },
825
+ },
826
+ ];
827
+ const evalString = `
750
828
  return {
751
829
  execute:(${functionValue
752
- .map((d2) => {
753
- return d2.key;
754
- })
755
- .join(',')})=>{
830
+ .map(d2 => {
831
+ return d2.key;
832
+ })
833
+ .join(',')})=>{
756
834
  try {
757
835
  ${sqlData[0].value.value.replace(
758
- /new\s*Promise\s*\(\s*async\s*\(\s*resolve\s*,\s*reject\s*\)\s*=>\s*\{([\s\S]*)\}\s*\)/i,
759
- 'new Promise(async (resolve, reject) => { try { $1 } catch (error) { console.log(error);reject(error); } })'
836
+ /new\s*Promise\s*\(\s*async\s*\(\s*resolve\s*,\s*reject\s*\)\s*=>\s*\{([\s\S]*)\}\s*\)/i,
837
+ 'new Promise(async (resolve, reject) => { try { $1 } catch (error) { console.log(error);reject(error); } })'
760
838
  )}
761
839
  }catch (e) { console.log(e) } } }
762
840
  `;
763
- const myFunction = new Function(evalString);
764
- return await myFunction().execute(functionValue[0].data(), functionValue[1].data());
765
- }
766
- );
841
+ const myFunction = new Function(evalString);
842
+ return await myFunction().execute(functionValue[0].data(), functionValue[1].data());
843
+ }
844
+ );
767
845
  }
768
846
 
769
847
  async function getSeoSiteMap(appName: string, req: any) {
770
- const sqlData = await Private_config.getConfig({
771
- appName: appName,
772
- key: 'sitemap_webhook',
773
- });
774
- if (!sqlData[0] || !sqlData[0].value) {
775
- return undefined;
776
- }
777
- const html = String.raw;
778
- return await db.queryLambada(
848
+ const sqlData = await Private_config.getConfig({
849
+ appName: appName,
850
+ key: 'sitemap_webhook',
851
+ });
852
+ if (!sqlData[0] || !sqlData[0].value) {
853
+ return undefined;
854
+ }
855
+ const html = String.raw;
856
+ return await db.queryLambada(
857
+ {
858
+ database: appName,
859
+ },
860
+ async db => {
861
+ (db as any).execute = (db as any).query;
862
+ const functionValue: { key: string; data: () => any }[] = [
779
863
  {
780
- database: appName,
864
+ key: 'db',
865
+ data: () => {
866
+ return db;
867
+ },
781
868
  },
782
- async (db) => {
783
- (db as any).execute = (db as any).query;
784
- const functionValue: { key: string; data: () => any }[] = [
785
- {
786
- key: 'db',
787
- data: () => {
788
- return db;
789
- },
790
- },
791
- {
792
- key: 'req',
793
- data: () => {
794
- return req;
795
- },
796
- },
797
- ];
798
- const evalString = `
869
+ {
870
+ key: 'req',
871
+ data: () => {
872
+ return req;
873
+ },
874
+ },
875
+ ];
876
+ const evalString = `
799
877
  return {
800
878
  execute:(${functionValue
801
- .map((d2) => {
802
- return d2.key;
803
- })
804
- .join(',')})=>{
879
+ .map(d2 => {
880
+ return d2.key;
881
+ })
882
+ .join(',')})=>{
805
883
  try {
806
884
  ${sqlData[0].value.value.replace(
807
- /new\s*Promise\s*\(\s*async\s*\(\s*resolve\s*,\s*reject\s*\)\s*=>\s*\{([\s\S]*)\}\s*\)/i,
808
- 'new Promise(async (resolve, reject) => { try { $1 } catch (error) { console.log(error);reject(error); } })'
885
+ /new\s*Promise\s*\(\s*async\s*\(\s*resolve\s*,\s*reject\s*\)\s*=>\s*\{([\s\S]*)\}\s*\)/i,
886
+ 'new Promise(async (resolve, reject) => { try { $1 } catch (error) { console.log(error);reject(error); } })'
809
887
  )}
810
888
  }catch (e) { console.log(e) } } }
811
889
  `;
812
- const myFunction = new Function(evalString);
813
- return await myFunction().execute(functionValue[0].data(), functionValue[1].data());
814
- }
815
- );
890
+ const myFunction = new Function(evalString);
891
+ return await myFunction().execute(functionValue[0].data(), functionValue[1].data());
892
+ }
893
+ );
816
894
  }
817
895
 
818
896
  function formatDateToISO(date: Date) {
819
- return `${date.toISOString().substring(0, date.toISOString().length - 5)}+00:00`;
897
+ return `${date.toISOString().substring(0, date.toISOString().length - 5)}+00:00`;
820
898
  }