backend-manager 3.2.101 → 3.2.103
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/package.json +4 -4
- package/src/manager/functions/core/actions/api/admin/create-post-new.js +192 -0
- package/src/manager/functions/core/actions/api/admin/create-post.js +167 -142
- package/src/manager/helpers/analytics.js +1 -0
- package/src/manager/functions/core/actions/api/admin/create-post copy.js +0 -215
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.103",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"homepage": "https://itwcreativeworks.com",
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@firebase/rules-unit-testing": "^2.0.7",
|
|
38
|
-
"@google-cloud/storage": "^7.
|
|
38
|
+
"@google-cloud/storage": "^7.10.2",
|
|
39
39
|
"@octokit/rest": "^19.0.13",
|
|
40
40
|
"@sendgrid/mail": "^7.7.0",
|
|
41
41
|
"@sentry/node": "^6.19.7",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"cors": "^2.8.5",
|
|
45
45
|
"dotenv": "^16.4.5",
|
|
46
46
|
"firebase-admin": "^11.11.1",
|
|
47
|
-
"firebase-functions": "^4.
|
|
47
|
+
"firebase-functions": "^4.9.0",
|
|
48
48
|
"fs-jetpack": "^5.1.0",
|
|
49
49
|
"hcaptcha": "^0.1.1",
|
|
50
50
|
"inquirer": "^8.2.5",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"uid-generator": "^2.0.0",
|
|
69
69
|
"ultimate-jekyll-poster": "^1.0.1",
|
|
70
70
|
"uuid": "^9.0.1",
|
|
71
|
-
"wonderful-fetch": "^1.1.
|
|
71
|
+
"wonderful-fetch": "^1.1.5",
|
|
72
72
|
"wonderful-log": "^1.0.5",
|
|
73
73
|
"yargs": "^17.7.2"
|
|
74
74
|
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const fetch = require('wonderful-fetch');
|
|
2
|
+
const moment = require('moment');
|
|
3
|
+
const jetpack = require('fs-jetpack');
|
|
4
|
+
const powertools = require('node-powertools');
|
|
5
|
+
const uuidv4 = require('uuid').v4;
|
|
6
|
+
|
|
7
|
+
const POST_TEMPLATE = jetpack.read(`${__dirname}/templates/post.html`);
|
|
8
|
+
const IMAGE_TAG = `{%- include /master/helpers/blog-image.html name="{name}" alt="{alt}" -%}`;
|
|
9
|
+
const IMAGE_PATH_SRC = `./assets/_src/images/blog/posts/post-{id}/`;
|
|
10
|
+
const IMAGE_PATH_REG = `/assets/images/blog/posts/post-{id}/`;
|
|
11
|
+
const AD_TAG = `{% include /master/modules/adunits/adsense-in-article.html index="{index}" %}`;
|
|
12
|
+
|
|
13
|
+
const IMAGE_REGEX = /(?:!\[(.*?)\]\((.*?)\))/img;
|
|
14
|
+
const LINK_REGEX = /(?:\[(.*?)\]\((.*?)\))/img;
|
|
15
|
+
|
|
16
|
+
function Module() {
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Module.prototype.main = function () {
|
|
21
|
+
const self = this;
|
|
22
|
+
const Manager = self.Manager;
|
|
23
|
+
const Api = self.Api;
|
|
24
|
+
const assistant = self.assistant;
|
|
25
|
+
const payload = self.payload;
|
|
26
|
+
|
|
27
|
+
return new Promise(async function(resolve, reject) {
|
|
28
|
+
// Perform checks
|
|
29
|
+
if (!payload.user.roles.admin && !payload.user.roles.blogger) {
|
|
30
|
+
return reject(assistant.errorify(`Admin required.`, {code: 401}));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Log payload
|
|
34
|
+
assistant.log(`main(): payload.data`, payload.data);
|
|
35
|
+
|
|
36
|
+
// Set now
|
|
37
|
+
const now = assistant.meta.startTime.timestamp;
|
|
38
|
+
const bemRepo = assistant.parseRepo(Manager?.config?.github?.repo_website);
|
|
39
|
+
|
|
40
|
+
// Check for required values
|
|
41
|
+
if (!payload.data.payload.title) {
|
|
42
|
+
return reject(assistant.errorify(`Missing required parameter: title`, {code: 400}));
|
|
43
|
+
} else if (!payload.data.payload.url) {
|
|
44
|
+
return reject(assistant.errorify(`Missing required parameter: url`, {code: 400}));
|
|
45
|
+
} else if (!payload.data.payload.excerpt) {
|
|
46
|
+
return reject(assistant.errorify(`Missing required parameter: excerpt`, {code: 400}));
|
|
47
|
+
} else if (!payload.data.payload.headerImageURL) {
|
|
48
|
+
return reject(assistant.errorify(`Missing required parameter: headerImageURL`, {code: 400}));
|
|
49
|
+
} else if (!payload.data.payload.body) {
|
|
50
|
+
return reject(assistant.errorify(`Missing required parameter: body`, {code: 400}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fix other values
|
|
54
|
+
payload.data.payload.author = payload.data.payload.author || 'alex';
|
|
55
|
+
payload.data.payload.affiliate = payload.data.payload.affiliate || '';
|
|
56
|
+
payload.data.payload.tags = payload.data.payload.tags || '';
|
|
57
|
+
payload.data.payload.categories = payload.data.payload.categories || '';
|
|
58
|
+
|
|
59
|
+
// Fix even more values
|
|
60
|
+
payload.data.payload.layout = payload.data.payload.layout || 'app/blog/post';
|
|
61
|
+
payload.data.payload.date = payload.data.payload.date || moment(now).format('YYYY-MM-DD');
|
|
62
|
+
payload.data.payload.id = payload.data.payload.id || Math.round(new Date(now).getTime() / 1000);
|
|
63
|
+
payload.data.payload.path = payload.data.payload.path || `./_posts/${moment(now).format('YYYY')}/guest`;
|
|
64
|
+
payload.data.payload.githubUser = payload.data.payload.githubUser || bemRepo.user;
|
|
65
|
+
payload.data.payload.githubRepo = payload.data.payload.githubRepo || bemRepo.name;
|
|
66
|
+
|
|
67
|
+
// Log
|
|
68
|
+
assistant.log(`main(): Creating post...`, payload.data.payload);
|
|
69
|
+
|
|
70
|
+
// Extract all images
|
|
71
|
+
await self.extractImages();
|
|
72
|
+
|
|
73
|
+
// Set defaults
|
|
74
|
+
const finalContent = powertools.template(POST_TEMPLATE, payload.data.payload);
|
|
75
|
+
|
|
76
|
+
// Log
|
|
77
|
+
assistant.log(`main(): finalContent`, finalContent);
|
|
78
|
+
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Extract images
|
|
84
|
+
Module.prototype.extractImages = function () {
|
|
85
|
+
const self = this;
|
|
86
|
+
const Manager = self.Manager;
|
|
87
|
+
const Api = self.Api;
|
|
88
|
+
const assistant = self.assistant;
|
|
89
|
+
const payload = self.payload;
|
|
90
|
+
|
|
91
|
+
return new Promise(async function(resolve, reject) {
|
|
92
|
+
// Extract images
|
|
93
|
+
const matches = payload.data.payload.body.matchAll(IMAGE_REGEX);
|
|
94
|
+
const images = Array.from(matches).map(match => ({
|
|
95
|
+
src: match[2] || '',
|
|
96
|
+
alt: match[1] || uuidv4(),
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
// Log
|
|
100
|
+
assistant.log(`extractImages(): images`, images);
|
|
101
|
+
|
|
102
|
+
if (!images) {
|
|
103
|
+
return resolve();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Loop through images
|
|
107
|
+
for (let index = 0; index < images.length; index++) {
|
|
108
|
+
const image = images[index];
|
|
109
|
+
|
|
110
|
+
// download image
|
|
111
|
+
const download = await self.downloadImage(image.src, image.alt);
|
|
112
|
+
|
|
113
|
+
// Log
|
|
114
|
+
assistant.log(`extractImages(): download`, download);
|
|
115
|
+
|
|
116
|
+
// const src = image.src;
|
|
117
|
+
// const alt = image.alt;
|
|
118
|
+
// const hyphenated = hyphenate(alt);
|
|
119
|
+
// const tag = powertools.template(IMAGE_TAG, {name: hyphenate(alt), alt: alt});
|
|
120
|
+
|
|
121
|
+
// console.log(`Image ${index + 1}: src=${image.src}, alt='${image.alt}'`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// const image = images[i];
|
|
125
|
+
// const src = image.match(LINK_REGEX)[2];
|
|
126
|
+
// const alt = image.match(LINK_REGEX)[1] || uuidv4();
|
|
127
|
+
// const name = hyphenate(alt);
|
|
128
|
+
// const tag = IMAGE_TAG.replace('{name}', name).replace('{alt}', alt);
|
|
129
|
+
|
|
130
|
+
// // Log
|
|
131
|
+
// assistant.log(`extractImages(): original`, image.match(LINK_REGEX));
|
|
132
|
+
// assistant.log(`extractImages(): image`, image);
|
|
133
|
+
// assistant.log(`extractImages(): src`, src);
|
|
134
|
+
// assistant.log(`extractImages(): alt`, alt);
|
|
135
|
+
// assistant.log(`extractImages(): name`, name);
|
|
136
|
+
// assistant.log(`extractImages(): tag`, tag);
|
|
137
|
+
|
|
138
|
+
// // Replace image
|
|
139
|
+
// payload.data.payload.body = payload.data.payload.body
|
|
140
|
+
// .replace(image, tag);
|
|
141
|
+
|
|
142
|
+
// // Save image
|
|
143
|
+
// await self.saveImage(src, id, name);
|
|
144
|
+
|
|
145
|
+
// Resolve
|
|
146
|
+
return resolve();
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Downlaod image
|
|
151
|
+
Module.prototype.downloadImage = function (src, alt) {
|
|
152
|
+
const self = this;
|
|
153
|
+
const Manager = self.Manager;
|
|
154
|
+
const Api = self.Api;
|
|
155
|
+
const assistant = self.assistant;
|
|
156
|
+
const payload = self.payload;
|
|
157
|
+
|
|
158
|
+
return new Promise(async function(resolve, reject) {
|
|
159
|
+
// Log
|
|
160
|
+
const hyphenated = hyphenate(alt);
|
|
161
|
+
|
|
162
|
+
assistant.log(`downloadImage(): src=${src}, alt=${alt}, hyphenated=${hyphenated}`);
|
|
163
|
+
|
|
164
|
+
// Get image
|
|
165
|
+
const image = await fetch(src, {
|
|
166
|
+
method: 'GET',
|
|
167
|
+
download: hyphenated,
|
|
168
|
+
})
|
|
169
|
+
.then((r) => {
|
|
170
|
+
// Log
|
|
171
|
+
assistant.log(`downloadImage(): Result`, r);
|
|
172
|
+
|
|
173
|
+
// Save image
|
|
174
|
+
return resolve(r);
|
|
175
|
+
})
|
|
176
|
+
.catch((e) => reject(e));
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
function hyphenate(s) {
|
|
181
|
+
return s
|
|
182
|
+
// Remove everything that is not a letter or a number
|
|
183
|
+
.replace(/[^a-zA-Z0-9]/g, '-')
|
|
184
|
+
// Replace multiple hyphens with a single hyphen
|
|
185
|
+
.replace(/-+/g, '-')
|
|
186
|
+
// Remove leading and trailing hyphens
|
|
187
|
+
.replace(/^-|-$/g, '')
|
|
188
|
+
// Lowercase
|
|
189
|
+
.toLowerCase();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = Module;
|
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
const fetch = require('
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const POST_TEMPLATE = jetpack.read(`${__dirname}/templates/post.html`);
|
|
8
|
-
const IMAGE_TAG = `{%- include /master/helpers/blog-image.html name="{name}" alt="{alt}" -%}`;
|
|
9
|
-
const IMAGE_PATH_SRC = `./assets/_src/images/blog/posts/post-{id}/`;
|
|
10
|
-
const IMAGE_PATH_REG = `/assets/images/blog/posts/post-{id}/`;
|
|
11
|
-
const AD_TAG = `{% include /master/modules/adunits/adsense-in-article.html index="{index}" %}`;
|
|
12
|
-
|
|
13
|
-
const IMAGE_REGEX = /(?:!\[(.*?)\]\((.*?)\))/img;
|
|
14
|
-
const LINK_REGEX = /(?:\[(.*?)\]\((.*?)\))/img;
|
|
1
|
+
const fetch = require('node-fetch');
|
|
2
|
+
const wonderfulFetch = require('wonderful-fetch');
|
|
3
|
+
const Poster = require('ultimate-jekyll-poster');
|
|
4
|
+
const pathApi = require('path');
|
|
5
|
+
const { get } = require('lodash');
|
|
15
6
|
|
|
16
7
|
function Module() {
|
|
17
8
|
|
|
@@ -30,161 +21,195 @@ Module.prototype.main = function () {
|
|
|
30
21
|
return reject(assistant.errorify(`Admin required.`, {code: 401}));
|
|
31
22
|
}
|
|
32
23
|
|
|
33
|
-
//
|
|
34
|
-
assistant.
|
|
35
|
-
|
|
36
|
-
// Set now
|
|
37
|
-
const now = assistant.meta.startTime.timestamp;
|
|
38
|
-
const bemRepo = assistant.parseRepo(Manager?.config?.github?.repo_website);
|
|
39
|
-
|
|
40
|
-
// Check for required values
|
|
41
|
-
if (!payload.data.payload.title) {
|
|
42
|
-
return reject(assistant.errorify(`Missing required parameter: title`, {code: 400}));
|
|
43
|
-
} else if (!payload.data.payload.url) {
|
|
44
|
-
return reject(assistant.errorify(`Missing required parameter: url`, {code: 400}));
|
|
45
|
-
} else if (!payload.data.payload.excerpt) {
|
|
46
|
-
return reject(assistant.errorify(`Missing required parameter: excerpt`, {code: 400}));
|
|
47
|
-
} else if (!payload.data.payload.headerImageURL) {
|
|
48
|
-
return reject(assistant.errorify(`Missing required parameter: headerImageURL`, {code: 400}));
|
|
49
|
-
} else if (!payload.data.payload.body) {
|
|
50
|
-
return reject(assistant.errorify(`Missing required parameter: body`, {code: 400}));
|
|
51
|
-
}
|
|
24
|
+
// Get repo info
|
|
25
|
+
const repoInfo = assistant.parseRepo(get(self.Manager.config, 'github.repo_website'));
|
|
26
|
+
const poster = new Poster();
|
|
52
27
|
|
|
53
|
-
|
|
54
|
-
payload.data.payload.author = payload.data.payload.author || 'alex';
|
|
55
|
-
payload.data.payload.affiliate = payload.data.payload.affiliate || '';
|
|
56
|
-
payload.data.payload.tags = payload.data.payload.tags || '';
|
|
57
|
-
payload.data.payload.categories = payload.data.payload.categories || '';
|
|
58
|
-
|
|
59
|
-
// Fix even more values
|
|
60
|
-
payload.data.payload.layout = payload.data.payload.layout || 'app/blog/post';
|
|
61
|
-
payload.data.payload.date = payload.data.payload.date || moment(now).format('YYYY-MM-DD');
|
|
62
|
-
payload.data.payload.id = payload.data.payload.id || Math.round(new Date(now).getTime() / 1000);
|
|
63
|
-
payload.data.payload.path = payload.data.payload.path || `./_posts/${moment(now).format('YYYY')}/guest`;
|
|
64
|
-
payload.data.payload.githubUser = payload.data.payload.githubUser || bemRepo.user;
|
|
65
|
-
payload.data.payload.githubRepo = payload.data.payload.githubRepo || bemRepo.name;
|
|
66
|
-
|
|
67
|
-
// Log
|
|
68
|
-
assistant.log(`main(): Creating post...`, payload.data.payload);
|
|
69
|
-
|
|
70
|
-
// Extract all images
|
|
71
|
-
await self.extractImages();
|
|
28
|
+
assistant.log(`main(): Creating post...`, repoInfo);
|
|
72
29
|
|
|
73
30
|
// Set defaults
|
|
74
|
-
const
|
|
31
|
+
const githubUser = get(self.Manager.config, 'github.user');
|
|
32
|
+
const githubKey = get(self.Manager.config, 'github.key');
|
|
33
|
+
|
|
34
|
+
// Save to disk OR commit
|
|
35
|
+
poster.onDownload = function (meta) {
|
|
36
|
+
return new Promise(async function(resolve, reject) {
|
|
37
|
+
const tempPath = meta.tempPath;
|
|
38
|
+
const finalPath = poster.removeDirDot(meta.finalPath);
|
|
39
|
+
|
|
40
|
+
// Log
|
|
41
|
+
assistant.log(`onDownload(): tempPath`, tempPath);
|
|
42
|
+
assistant.log(`onDownload(): finalPath`, finalPath);
|
|
43
|
+
|
|
44
|
+
// Check for missing paths (need to check for meta.finalPath as well because it's not always set)
|
|
45
|
+
if (!tempPath || !finalPath || !meta.finalPath) {
|
|
46
|
+
return reject(new Error(`onDownload(): tempPath or finalPath is missing`));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Save to disk
|
|
50
|
+
poster.readImage(tempPath)
|
|
51
|
+
.then(image => {
|
|
52
|
+
self.createFile(githubUser, repoInfo.user, repoInfo.name, githubKey, finalPath, image)
|
|
53
|
+
.then(() => {resolve()})
|
|
54
|
+
.catch((e) => {reject(e)})
|
|
55
|
+
})
|
|
56
|
+
.catch((e) => {reject(e)})
|
|
57
|
+
|
|
58
|
+
});
|
|
59
|
+
}
|
|
75
60
|
|
|
76
|
-
//
|
|
77
|
-
|
|
61
|
+
// Create post
|
|
62
|
+
const finalPost = await poster.create(payload.data).catch(e => e);
|
|
63
|
+
if (finalPost instanceof Error) {
|
|
64
|
+
return reject(assistant.errorify(`Failed to post: ${finalPost}`, {code: 500}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Request indexing
|
|
68
|
+
// DEPRECATED
|
|
69
|
+
// try {
|
|
70
|
+
// const url = get(self.Manager.config, 'brand.url');
|
|
71
|
+
// const encoded = encodeURIComponent(`${url}/sitemap.xml`);
|
|
72
|
+
|
|
73
|
+
// wonderfulFetch(`https://www.google.com/ping?sitemap=${encoded}`)
|
|
74
|
+
|
|
75
|
+
// // TODO
|
|
76
|
+
// // https://developers.google.com/search/apis/indexing-api/v3/prereqs
|
|
77
|
+
// // https://developers.google.com/search/apis/indexing-api/v3/using-api#url
|
|
78
|
+
// } catch (e) {
|
|
79
|
+
// assistant.error(`Failed to ping google: ${e}`);
|
|
80
|
+
// }
|
|
81
|
+
|
|
82
|
+
// Set file path and content
|
|
83
|
+
const filePath = poster.removeDirDot(finalPost.path);
|
|
84
|
+
const fileContent = finalPost.content;
|
|
85
|
+
|
|
86
|
+
// Save post OR commit
|
|
87
|
+
await self.createFile(githubUser, repoInfo.user, repoInfo.name, githubKey, filePath, fileContent)
|
|
88
|
+
.then(() => {
|
|
89
|
+
return resolve({data: finalPost});
|
|
90
|
+
})
|
|
91
|
+
.catch((e) => {
|
|
92
|
+
return reject(assistant.errorify(`Failed to post: ${e}`, {code: 500}));
|
|
93
|
+
})
|
|
78
94
|
|
|
79
95
|
});
|
|
80
96
|
|
|
81
97
|
};
|
|
82
98
|
|
|
83
|
-
//
|
|
84
|
-
Module.prototype.
|
|
99
|
+
// HELPERS //
|
|
100
|
+
Module.prototype.createFile = function (user, repoUser, repoName, key, path, contents) {
|
|
85
101
|
const self = this;
|
|
86
102
|
const Manager = self.Manager;
|
|
87
103
|
const Api = self.Api;
|
|
88
104
|
const assistant = self.assistant;
|
|
89
105
|
const payload = self.payload;
|
|
90
106
|
|
|
91
|
-
return new Promise(async
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
src: match[2] || '',
|
|
96
|
-
alt: match[1] || uuidv4(),
|
|
97
|
-
}));
|
|
107
|
+
return new Promise(async (resolve, reject) => {
|
|
108
|
+
let fileParsed = pathApi.parse(path);
|
|
109
|
+
let base64Data = Buffer.from(contents).toString('base64');
|
|
110
|
+
let sha;
|
|
98
111
|
|
|
99
112
|
// Log
|
|
100
|
-
assistant.log(`
|
|
113
|
+
assistant.log(`createFile(): Writing file to ${repoUser}/${repoName}/${path}`);
|
|
101
114
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// Loop through images
|
|
107
|
-
for (let index = 0; index < images.length; index++) {
|
|
108
|
-
const image = images[index];
|
|
109
|
-
|
|
110
|
-
// download image
|
|
111
|
-
const download = await self.downloadImage(image.src, image.alt);
|
|
115
|
+
// Try to get sha
|
|
116
|
+
try {
|
|
117
|
+
let branch = repoName === 'ultimate-jekyll' ? 'template' : 'master';
|
|
118
|
+
let pathGet = `https://api.github.com/repos/${repoUser}/${repoName}/git/trees/${branch}:${encodeURIComponent(pathApi.dirname(path))}`;
|
|
112
119
|
|
|
113
120
|
// Log
|
|
114
|
-
assistant.log(`
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
assistant.log(`createFile(): pathGet`, pathGet);
|
|
122
|
+
|
|
123
|
+
// Make request
|
|
124
|
+
await makeRequest({
|
|
125
|
+
method: 'GET',
|
|
126
|
+
url: pathGet,
|
|
127
|
+
body: {
|
|
128
|
+
},
|
|
129
|
+
timeout: 30000,
|
|
130
|
+
json: true,
|
|
131
|
+
headers: {
|
|
132
|
+
'User-Agent': user,
|
|
133
|
+
// 'Authorization': `Basic ${user}:${key}`,
|
|
134
|
+
'Authorization': `Basic ${Buffer.from(user + ':' + key).toString('base64')}`,
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
.then(function (resp) {
|
|
138
|
+
// sha = resp.sha;
|
|
139
|
+
sha = resp.tree.find(function (element) {
|
|
140
|
+
// console.log('checiing', element.path, fileParsed.base);
|
|
141
|
+
return element.path === fileParsed.base;
|
|
142
|
+
});
|
|
143
|
+
sha = sha.sha;
|
|
144
|
+
});
|
|
145
|
+
} catch (e) {
|
|
146
|
+
sha = null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let pathPut = `https://api.github.com/repos/${repoUser}/${repoName}/contents/${path}`;
|
|
150
|
+
let writeRequest = {
|
|
151
|
+
// url: `https://api.github.com/repos/:owner/:repo/contents/:path`,
|
|
152
|
+
method: 'PUT',
|
|
153
|
+
url: pathPut,
|
|
154
|
+
body: {
|
|
155
|
+
message: `BackendManager Post: ${new Date().toISOString()}`,
|
|
156
|
+
content: base64Data,
|
|
157
|
+
},
|
|
158
|
+
timeout: 30000,
|
|
159
|
+
json: true,
|
|
160
|
+
headers: {
|
|
161
|
+
'User-Agent': user,
|
|
162
|
+
// 'Authorization': `Basic ${user}:${key}`,
|
|
163
|
+
'Authorization': `Basic ${Buffer.from(user + ':' + key).toString('base64')}`,
|
|
122
164
|
}
|
|
165
|
+
}
|
|
123
166
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// const alt = image.match(LINK_REGEX)[1] || uuidv4();
|
|
127
|
-
// const name = hyphenate(alt);
|
|
128
|
-
// const tag = IMAGE_TAG.replace('{name}', name).replace('{alt}', alt);
|
|
129
|
-
|
|
130
|
-
// // Log
|
|
131
|
-
// assistant.log(`extractImages(): original`, image.match(LINK_REGEX));
|
|
132
|
-
// assistant.log(`extractImages(): image`, image);
|
|
133
|
-
// assistant.log(`extractImages(): src`, src);
|
|
134
|
-
// assistant.log(`extractImages(): alt`, alt);
|
|
135
|
-
// assistant.log(`extractImages(): name`, name);
|
|
136
|
-
// assistant.log(`extractImages(): tag`, tag);
|
|
137
|
-
|
|
138
|
-
// // Replace image
|
|
139
|
-
// payload.data.payload.body = payload.data.payload.body
|
|
140
|
-
// .replace(image, tag);
|
|
141
|
-
|
|
142
|
-
// // Save image
|
|
143
|
-
// await self.saveImage(src, id, name);
|
|
144
|
-
|
|
145
|
-
// Resolve
|
|
146
|
-
return resolve();
|
|
147
|
-
});
|
|
148
|
-
};
|
|
167
|
+
// Log
|
|
168
|
+
assistant.log(`createFile(): pathPut`, pathPut);
|
|
149
169
|
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const Api = self.Api;
|
|
155
|
-
const assistant = self.assistant;
|
|
156
|
-
const payload = self.payload;
|
|
170
|
+
// Add sha if it exists
|
|
171
|
+
if (sha) {
|
|
172
|
+
writeRequest.body.sha = sha;
|
|
173
|
+
}
|
|
157
174
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
175
|
+
// Make request
|
|
176
|
+
await makeRequest(writeRequest)
|
|
177
|
+
.then((json) => {
|
|
178
|
+
if (!json || (json.message && (json.message === 'Not Found' || json.message.includes('Invalid request'))) ) {
|
|
179
|
+
return reject(new Error(json.message));
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
.catch((e) => {
|
|
183
|
+
return reject(e);
|
|
184
|
+
})
|
|
185
|
+
return resolve(true)
|
|
186
|
+
});
|
|
187
|
+
}
|
|
161
188
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
.
|
|
168
|
-
|
|
169
|
-
|
|
189
|
+
function makeRequest(options) {
|
|
190
|
+
return new Promise(function(resolve, reject) {
|
|
191
|
+
options.headers = options.headers || {};
|
|
192
|
+
options.headers['Content-Type'] = 'application/json';
|
|
193
|
+
|
|
194
|
+
let hasBody = Object.keys(options.body || {}).length > 0
|
|
195
|
+
|
|
196
|
+
fetch(options.url, {
|
|
197
|
+
method: options.method,
|
|
198
|
+
body: hasBody ? JSON.stringify(options.body) : undefined,
|
|
199
|
+
timeout: 30000,
|
|
200
|
+
headers: options.headers,
|
|
201
|
+
auth: options.auth,
|
|
202
|
+
})
|
|
203
|
+
.then(res => res.json())
|
|
204
|
+
.then(json => {
|
|
205
|
+
return resolve(json);
|
|
206
|
+
})
|
|
207
|
+
.catch(e => {
|
|
208
|
+
// console.error('e', e);
|
|
209
|
+
return reject(e);
|
|
210
|
+
})
|
|
170
211
|
|
|
171
|
-
// Save image
|
|
172
|
-
return resolve(r);
|
|
173
|
-
})
|
|
174
|
-
.catch((e) => reject(e));
|
|
175
212
|
});
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
function hyphenate(s) {
|
|
179
|
-
return s
|
|
180
|
-
// Remove everything that is not a letter or a number
|
|
181
|
-
.replace(/[^a-zA-Z0-9]/g, '-')
|
|
182
|
-
// Replace multiple hyphens with a single hyphen
|
|
183
|
-
.replace(/-+/g, '-')
|
|
184
|
-
// Remove leading and trailing hyphens
|
|
185
|
-
.replace(/^-|-$/g, '')
|
|
186
|
-
// Lowercase
|
|
187
|
-
.toLowerCase();
|
|
188
213
|
}
|
|
189
214
|
|
|
190
215
|
module.exports = Module;
|
|
@@ -131,6 +131,7 @@ function Analytics(Manager, options) {
|
|
|
131
131
|
|
|
132
132
|
// Fix user data
|
|
133
133
|
// https://developers.google.com/analytics/devguides/collection/ga4/uid-data
|
|
134
|
+
// https://stackoverflow.com/questions/68636233/ga4-measurement-protocol-does-not-display-user-data-location-screen-resolution
|
|
134
135
|
self.userData = {
|
|
135
136
|
sha256_email_address: authUser?.auth?.email
|
|
136
137
|
? toSHA256(authUser?.auth?.email)
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
const fetch = require('node-fetch');
|
|
2
|
-
const wonderfulFetch = require('wonderful-fetch');
|
|
3
|
-
const Poster = require('ultimate-jekyll-poster');
|
|
4
|
-
const pathApi = require('path');
|
|
5
|
-
const { get } = require('lodash');
|
|
6
|
-
|
|
7
|
-
function Module() {
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
Module.prototype.main = function () {
|
|
12
|
-
const self = this;
|
|
13
|
-
const Manager = self.Manager;
|
|
14
|
-
const Api = self.Api;
|
|
15
|
-
const assistant = self.assistant;
|
|
16
|
-
const payload = self.payload;
|
|
17
|
-
|
|
18
|
-
return new Promise(async function(resolve, reject) {
|
|
19
|
-
// Perform checks
|
|
20
|
-
if (!payload.user.roles.admin && !payload.user.roles.blogger) {
|
|
21
|
-
return reject(assistant.errorify(`Admin required.`, {code: 401}));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Get repo info
|
|
25
|
-
const repoInfo = assistant.parseRepo(get(self.Manager.config, 'github.repo_website'));
|
|
26
|
-
const poster = new Poster();
|
|
27
|
-
|
|
28
|
-
assistant.log(`main(): Creating post...`, repoInfo);
|
|
29
|
-
|
|
30
|
-
// Set defaults
|
|
31
|
-
const githubUser = get(self.Manager.config, 'github.user');
|
|
32
|
-
const githubKey = get(self.Manager.config, 'github.key');
|
|
33
|
-
|
|
34
|
-
// Save to disk OR commit
|
|
35
|
-
poster.onDownload = function (meta) {
|
|
36
|
-
return new Promise(async function(resolve, reject) {
|
|
37
|
-
const tempPath = meta.tempPath;
|
|
38
|
-
const finalPath = poster.removeDirDot(meta.finalPath);
|
|
39
|
-
|
|
40
|
-
// Log
|
|
41
|
-
assistant.log(`onDownload(): tempPath`, tempPath);
|
|
42
|
-
assistant.log(`onDownload(): finalPath`, finalPath);
|
|
43
|
-
|
|
44
|
-
// Check for missing paths (need to check for meta.finalPath as well because it's not always set)
|
|
45
|
-
if (!tempPath || !finalPath || !meta.finalPath) {
|
|
46
|
-
return reject(new Error(`onDownload(): tempPath or finalPath is missing`));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Save to disk
|
|
50
|
-
poster.readImage(tempPath)
|
|
51
|
-
.then(image => {
|
|
52
|
-
self.createFile(githubUser, repoInfo.user, repoInfo.name, githubKey, finalPath, image)
|
|
53
|
-
.then(() => {resolve()})
|
|
54
|
-
.catch((e) => {reject(e)})
|
|
55
|
-
})
|
|
56
|
-
.catch((e) => {reject(e)})
|
|
57
|
-
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Create post
|
|
62
|
-
const finalPost = await poster.create(payload.data).catch(e => e);
|
|
63
|
-
if (finalPost instanceof Error) {
|
|
64
|
-
return reject(assistant.errorify(`Failed to post: ${finalPost}`, {code: 500}));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Request indexing
|
|
68
|
-
// DEPRECATED
|
|
69
|
-
// try {
|
|
70
|
-
// const url = get(self.Manager.config, 'brand.url');
|
|
71
|
-
// const encoded = encodeURIComponent(`${url}/sitemap.xml`);
|
|
72
|
-
|
|
73
|
-
// wonderfulFetch(`https://www.google.com/ping?sitemap=${encoded}`)
|
|
74
|
-
|
|
75
|
-
// // TODO
|
|
76
|
-
// // https://developers.google.com/search/apis/indexing-api/v3/prereqs
|
|
77
|
-
// // https://developers.google.com/search/apis/indexing-api/v3/using-api#url
|
|
78
|
-
// } catch (e) {
|
|
79
|
-
// assistant.error(`Failed to ping google: ${e}`);
|
|
80
|
-
// }
|
|
81
|
-
|
|
82
|
-
// Set file path and content
|
|
83
|
-
const filePath = poster.removeDirDot(finalPost.path);
|
|
84
|
-
const fileContent = finalPost.content;
|
|
85
|
-
|
|
86
|
-
// Save post OR commit
|
|
87
|
-
await self.createFile(githubUser, repoInfo.user, repoInfo.name, githubKey, filePath, fileContent)
|
|
88
|
-
.then(() => {
|
|
89
|
-
return resolve({data: finalPost});
|
|
90
|
-
})
|
|
91
|
-
.catch((e) => {
|
|
92
|
-
return reject(assistant.errorify(`Failed to post: ${e}`, {code: 500}));
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// HELPERS //
|
|
100
|
-
Module.prototype.createFile = function (user, repoUser, repoName, key, path, contents) {
|
|
101
|
-
const self = this;
|
|
102
|
-
const Manager = self.Manager;
|
|
103
|
-
const Api = self.Api;
|
|
104
|
-
const assistant = self.assistant;
|
|
105
|
-
const payload = self.payload;
|
|
106
|
-
|
|
107
|
-
return new Promise(async (resolve, reject) => {
|
|
108
|
-
let fileParsed = pathApi.parse(path);
|
|
109
|
-
let base64Data = Buffer.from(contents).toString('base64');
|
|
110
|
-
let sha;
|
|
111
|
-
|
|
112
|
-
// Log
|
|
113
|
-
assistant.log(`createFile(): Writing file to ${repoUser}/${repoName}/${path}`);
|
|
114
|
-
|
|
115
|
-
// Try to get sha
|
|
116
|
-
try {
|
|
117
|
-
let branch = repoName === 'ultimate-jekyll' ? 'template' : 'master';
|
|
118
|
-
let pathGet = `https://api.github.com/repos/${repoUser}/${repoName}/git/trees/${branch}:${encodeURIComponent(pathApi.dirname(path))}`;
|
|
119
|
-
|
|
120
|
-
// Log
|
|
121
|
-
assistant.log(`createFile(): pathGet`, pathGet);
|
|
122
|
-
|
|
123
|
-
// Make request
|
|
124
|
-
await makeRequest({
|
|
125
|
-
method: 'GET',
|
|
126
|
-
url: pathGet,
|
|
127
|
-
body: {
|
|
128
|
-
},
|
|
129
|
-
timeout: 30000,
|
|
130
|
-
json: true,
|
|
131
|
-
headers: {
|
|
132
|
-
'User-Agent': user,
|
|
133
|
-
// 'Authorization': `Basic ${user}:${key}`,
|
|
134
|
-
'Authorization': `Basic ${Buffer.from(user + ':' + key).toString('base64')}`,
|
|
135
|
-
}
|
|
136
|
-
})
|
|
137
|
-
.then(function (resp) {
|
|
138
|
-
// sha = resp.sha;
|
|
139
|
-
sha = resp.tree.find(function (element) {
|
|
140
|
-
// console.log('checiing', element.path, fileParsed.base);
|
|
141
|
-
return element.path === fileParsed.base;
|
|
142
|
-
});
|
|
143
|
-
sha = sha.sha;
|
|
144
|
-
});
|
|
145
|
-
} catch (e) {
|
|
146
|
-
sha = null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
let pathPut = `https://api.github.com/repos/${repoUser}/${repoName}/contents/${path}`;
|
|
150
|
-
let writeRequest = {
|
|
151
|
-
// url: `https://api.github.com/repos/:owner/:repo/contents/:path`,
|
|
152
|
-
method: 'PUT',
|
|
153
|
-
url: pathPut,
|
|
154
|
-
body: {
|
|
155
|
-
message: `BackendManager Post: ${new Date().toISOString()}`,
|
|
156
|
-
content: base64Data,
|
|
157
|
-
},
|
|
158
|
-
timeout: 30000,
|
|
159
|
-
json: true,
|
|
160
|
-
headers: {
|
|
161
|
-
'User-Agent': user,
|
|
162
|
-
// 'Authorization': `Basic ${user}:${key}`,
|
|
163
|
-
'Authorization': `Basic ${Buffer.from(user + ':' + key).toString('base64')}`,
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Log
|
|
168
|
-
assistant.log(`createFile(): pathPut`, pathPut);
|
|
169
|
-
|
|
170
|
-
// Add sha if it exists
|
|
171
|
-
if (sha) {
|
|
172
|
-
writeRequest.body.sha = sha;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Make request
|
|
176
|
-
await makeRequest(writeRequest)
|
|
177
|
-
.then((json) => {
|
|
178
|
-
if (!json || (json.message && (json.message === 'Not Found' || json.message.includes('Invalid request'))) ) {
|
|
179
|
-
return reject(new Error(json.message));
|
|
180
|
-
}
|
|
181
|
-
})
|
|
182
|
-
.catch((e) => {
|
|
183
|
-
return reject(e);
|
|
184
|
-
})
|
|
185
|
-
return resolve(true)
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function makeRequest(options) {
|
|
190
|
-
return new Promise(function(resolve, reject) {
|
|
191
|
-
options.headers = options.headers || {};
|
|
192
|
-
options.headers['Content-Type'] = 'application/json';
|
|
193
|
-
|
|
194
|
-
let hasBody = Object.keys(options.body || {}).length > 0
|
|
195
|
-
|
|
196
|
-
fetch(options.url, {
|
|
197
|
-
method: options.method,
|
|
198
|
-
body: hasBody ? JSON.stringify(options.body) : undefined,
|
|
199
|
-
timeout: 30000,
|
|
200
|
-
headers: options.headers,
|
|
201
|
-
auth: options.auth,
|
|
202
|
-
})
|
|
203
|
-
.then(res => res.json())
|
|
204
|
-
.then(json => {
|
|
205
|
-
return resolve(json);
|
|
206
|
-
})
|
|
207
|
-
.catch(e => {
|
|
208
|
-
// console.error('e', e);
|
|
209
|
-
return reject(e);
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
module.exports = Module;
|