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.
- package/lowcode/Entry.js +1 -1
- package/lowcode/Entry.ts +1 -1
- package/lowcode/cms-plugin/shopping-information.js +136 -30
- package/lowcode/cms-plugin/shopping-information.ts +149 -29
- package/lowcode/css/editor.css +1 -1
- package/lowcode/jspage/main.js +1 -1
- package/lowcode/jspage/main.ts +1 -1
- package/lowcode/public-components/checkout/index.js +209 -196
- package/lowcode/public-components/checkout/index.ts +235 -223
- package/package.json +1 -1
- package/src/api-public/controllers/delivery.js.map +1 -1
- package/src/api-public/controllers/index.js.map +1 -1
- package/src/api-public/controllers/shop.js.map +1 -1
- package/src/api-public/services/ai-robot.d.ts +0 -1
- package/src/api-public/services/fb-api.d.ts +0 -1
- package/src/api-public/services/fb-message.d.ts +0 -1
- package/src/api-public/services/financial-service.d.ts +1 -1
- package/src/api-public/services/financial-service.js +22 -11
- package/src/api-public/services/financial-service.js.map +1 -1
- package/src/api-public/services/financial-service.ts +30 -15
- package/src/api-public/services/invoice.js.map +1 -1
- package/src/api-public/services/line-message.d.ts +0 -1
- package/src/api-public/services/public-table-check.d.ts +7 -0
- package/src/api-public/services/public-table-check.js +10 -1
- package/src/api-public/services/public-table-check.js.map +1 -5
- package/src/api-public/services/public-table-check.ts +13 -2
- package/src/api-public/services/schedule.js.map +1 -1
- package/src/api-public/services/share-permission.d.ts +1 -1
- package/src/api-public/services/shopee.d.ts +0 -1
- package/src/api-public/services/shopping.js.map +1 -1
- package/src/api-public/services/shopping.ts +1 -0
- package/src/helper/glitter-util.d.ts +3 -2
- package/src/helper/glitter-util.js +5 -0
- package/src/helper/glitter-util.js.map +1 -1
- package/src/helper/glitter-util.ts +6 -2
- package/src/index.js +146 -98
- package/src/index.js.map +1 -1
- package/src/index.ts +763 -685
- 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
|
|
47
|
-
import {FbApi} from
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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, '"')}" />
|
|
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, '"')}"
|
|
392
|
+
/>
|
|
393
|
+
<meta
|
|
394
|
+
name="description"
|
|
395
|
+
content="${(d.content ?? '').replace(/\n/g, '').replace(/"/g, '"')}"
|
|
396
|
+
/>
|
|
397
|
+
<meta
|
|
398
|
+
name="og:description"
|
|
399
|
+
content="${(d.content ?? '').replace(/\n/g, '').replace(/"/g, '"')}"
|
|
400
|
+
/>
|
|
314
401
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
.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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
460
|
+
.map(dd => {
|
|
461
|
+
return (dd || '').trim();
|
|
462
|
+
})
|
|
463
|
+
.filter(dd => {
|
|
464
|
+
return dd;
|
|
465
|
+
})
|
|
466
|
+
.join(';\n')}
|
|
413
467
|
</script>
|
|
414
468
|
${[
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
550
|
+
const site_map = await getSeoSiteMap(appName, req);
|
|
488
551
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
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
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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
|
+
'&': '&',
|
|
742
|
+
'<': '<',
|
|
743
|
+
'>': '>',
|
|
744
|
+
'"': '"',
|
|
745
|
+
"'": ''',
|
|
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
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
-
|
|
815
|
+
key: 'db',
|
|
816
|
+
data: () => {
|
|
817
|
+
return db;
|
|
818
|
+
},
|
|
732
819
|
},
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
830
|
+
.map(d2 => {
|
|
831
|
+
return d2.key;
|
|
832
|
+
})
|
|
833
|
+
.join(',')})=>{
|
|
756
834
|
try {
|
|
757
835
|
${sqlData[0].value.value.replace(
|
|
758
|
-
|
|
759
|
-
|
|
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
|
-
|
|
764
|
-
|
|
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
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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
|
-
|
|
864
|
+
key: 'db',
|
|
865
|
+
data: () => {
|
|
866
|
+
return db;
|
|
867
|
+
},
|
|
781
868
|
},
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
879
|
+
.map(d2 => {
|
|
880
|
+
return d2.key;
|
|
881
|
+
})
|
|
882
|
+
.join(',')})=>{
|
|
805
883
|
try {
|
|
806
884
|
${sqlData[0].value.value.replace(
|
|
807
|
-
|
|
808
|
-
|
|
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
|
-
|
|
813
|
-
|
|
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
|
-
|
|
897
|
+
return `${date.toISOString().substring(0, date.toISOString().length - 5)}+00:00`;
|
|
820
898
|
}
|