backend-manager 3.2.103 → 3.2.104
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.
|
@@ -0,0 +1,215 @@
|
|
|
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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.104",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"lodash": "^4.17.21",
|
|
54
54
|
"lowdb": "^1.0.0",
|
|
55
55
|
"mailchimp-api-v3": "^1.15.0",
|
|
56
|
+
"mime-types": "^2.1.35",
|
|
56
57
|
"mocha": "^8.4.0",
|
|
57
58
|
"moment": "^2.30.1",
|
|
58
59
|
"nanoid": "^3.3.7",
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
"uid-generator": "^2.0.0",
|
|
69
70
|
"ultimate-jekyll-poster": "^1.0.1",
|
|
70
71
|
"uuid": "^9.0.1",
|
|
71
|
-
"wonderful-fetch": "^1.1.
|
|
72
|
+
"wonderful-fetch": "^1.1.8",
|
|
72
73
|
"wonderful-log": "^1.0.5",
|
|
73
74
|
"yargs": "^17.7.2"
|
|
74
75
|
}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
const fetch = require('
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
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
|
+
const path = require('path');
|
|
7
|
+
const { Octokit } = require('@octokit/rest');
|
|
8
|
+
|
|
9
|
+
const POST_TEMPLATE = jetpack.read(`${__dirname}/templates/post.html`);
|
|
10
|
+
const IMAGE_TAG = `{%- include /master/helpers/blog-image.html name="{name}" alt="{alt}" -%}`;
|
|
11
|
+
const IMAGE_PATH_SRC = `assets/_src/images/blog/posts/post-{id}/`;
|
|
12
|
+
// const IMAGE_PATH_REG = `/assets/images/blog/posts/post-{id}/`;
|
|
13
|
+
const AD_TAG = `{% include /master/modules/adunits/adsense-in-article.html index="{index}" %}`;
|
|
14
|
+
|
|
15
|
+
const IMAGE_REGEX = /(?:!\[(.*?)\]\((.*?)\))/img;
|
|
16
|
+
const LINK_REGEX = /(?:\[(.*?)\]\((.*?)\))/img;
|
|
6
17
|
|
|
7
18
|
function Module() {
|
|
8
19
|
|
|
@@ -16,200 +27,318 @@ Module.prototype.main = function () {
|
|
|
16
27
|
const payload = self.payload;
|
|
17
28
|
|
|
18
29
|
return new Promise(async function(resolve, reject) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
try {
|
|
31
|
+
// Perform checks
|
|
32
|
+
if (!payload.user.roles.admin && !payload.user.roles.blogger) {
|
|
33
|
+
return reject(assistant.errorify(`Admin required.`, {code: 401}));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Log payload
|
|
37
|
+
assistant.log(`main(): payload.data`, payload.data);
|
|
23
38
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
// Set now
|
|
40
|
+
const now = assistant.meta.startTime.timestamp;
|
|
41
|
+
const bemRepo = assistant.parseRepo(Manager?.config?.github?.repo_website);
|
|
27
42
|
|
|
28
|
-
|
|
43
|
+
// Setup Octokit
|
|
44
|
+
self.octokit = new Octokit({
|
|
45
|
+
auth: Manager?.config?.github?.key,
|
|
46
|
+
});
|
|
29
47
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
// Check for required values
|
|
49
|
+
if (!payload.data.payload.title) {
|
|
50
|
+
return reject(assistant.errorify(`Missing required parameter: title`, {code: 400}));
|
|
51
|
+
} else if (!payload.data.payload.url) {
|
|
52
|
+
return reject(assistant.errorify(`Missing required parameter: url`, {code: 400}));
|
|
53
|
+
} else if (!payload.data.payload.excerpt) {
|
|
54
|
+
return reject(assistant.errorify(`Missing required parameter: excerpt`, {code: 400}));
|
|
55
|
+
} else if (!payload.data.payload.headerImageURL) {
|
|
56
|
+
return reject(assistant.errorify(`Missing required parameter: headerImageURL`, {code: 400}));
|
|
57
|
+
} else if (!payload.data.payload.body) {
|
|
58
|
+
return reject(assistant.errorify(`Missing required parameter: body`, {code: 400}));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Fix required values
|
|
62
|
+
payload.data.payload.url = payload.data.payload.url
|
|
63
|
+
// Replace /blog/ (slashes may or may not be present)
|
|
64
|
+
.replace(/blog\//ig, '')
|
|
65
|
+
// Remove leading and trailing slashes
|
|
66
|
+
.replace(/^\/|\/$/g, '');
|
|
67
|
+
|
|
68
|
+
// Fix body
|
|
69
|
+
payload.data.payload.body = payload.data.payload.body
|
|
70
|
+
// Replace heading text (# + payload.data.payload.title) (just the first instance in case it is repeated)
|
|
71
|
+
.replace(powertools.regexify(`/# ${payload.data.payload.title}/i`), '')
|
|
72
|
+
// Remove extra newlines
|
|
73
|
+
.replace(/\n\n\n+/g, '\n\n')
|
|
74
|
+
// Trim
|
|
75
|
+
.trim();
|
|
76
|
+
|
|
77
|
+
// Fix other values
|
|
78
|
+
payload.data.payload.author = payload.data.payload.author || 'alex';
|
|
79
|
+
payload.data.payload.affiliate = payload.data.payload.affiliate || '';
|
|
80
|
+
payload.data.payload.tags = payload.data.payload.tags || '';
|
|
81
|
+
payload.data.payload.categories = payload.data.payload.categories || '';
|
|
82
|
+
|
|
83
|
+
// Fix even more values
|
|
84
|
+
payload.data.payload.layout = payload.data.payload.layout || 'app/blog/post';
|
|
85
|
+
payload.data.payload.date = payload.data.payload.date || moment(now).format('YYYY-MM-DD');
|
|
86
|
+
payload.data.payload.id = payload.data.payload.id || Math.round(new Date(now).getTime() / 1000);
|
|
87
|
+
payload.data.payload.path = payload.data.payload.path || `_posts/${moment(now).format('YYYY')}/guest`;
|
|
88
|
+
payload.data.payload.githubUser = payload.data.payload.githubUser || bemRepo.user;
|
|
89
|
+
payload.data.payload.githubRepo = payload.data.payload.githubRepo || bemRepo.name;
|
|
33
90
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return new Promise(async function(resolve, reject) {
|
|
37
|
-
const tempPath = meta.tempPath;
|
|
38
|
-
const finalPath = poster.removeDirDot(meta.finalPath);
|
|
91
|
+
// Log
|
|
92
|
+
assistant.log(`main(): Creating post...`, payload.data.payload);
|
|
39
93
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
assistant.log(`onDownload(): finalPath`, finalPath);
|
|
94
|
+
// Extract all images
|
|
95
|
+
await self.extractImages();
|
|
43
96
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return reject(new Error(`onDownload(): tempPath or finalPath is missing`));
|
|
47
|
-
}
|
|
97
|
+
// Set defaults
|
|
98
|
+
const formattedContent = powertools.template(POST_TEMPLATE, payload.data.payload);
|
|
48
99
|
|
|
49
|
-
|
|
50
|
-
|
|
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)})
|
|
100
|
+
// Insert ads
|
|
101
|
+
const articleWithAds = insertAds(formattedContent, 3, 1500);
|
|
57
102
|
|
|
58
|
-
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
// Log
|
|
106
|
+
assistant.log(`main(): articleWithAds`, articleWithAds);
|
|
107
|
+
|
|
108
|
+
// Upload post
|
|
109
|
+
const uploadPost = await self.uploadPost(articleWithAds);
|
|
110
|
+
|
|
111
|
+
// Log
|
|
112
|
+
assistant.log(`main(): uploadPost`, uploadPost);
|
|
113
|
+
|
|
114
|
+
// Resolve
|
|
115
|
+
return resolve({data: payload.data.payload});
|
|
116
|
+
} catch (e) {
|
|
117
|
+
return reject(e);
|
|
59
118
|
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Extract images
|
|
123
|
+
Module.prototype.extractImages = function () {
|
|
124
|
+
const self = this;
|
|
125
|
+
const Manager = self.Manager;
|
|
126
|
+
const Api = self.Api;
|
|
127
|
+
const assistant = self.assistant;
|
|
128
|
+
const payload = self.payload;
|
|
129
|
+
|
|
130
|
+
return new Promise(async function(resolve, reject) {
|
|
131
|
+
// Extract images
|
|
132
|
+
const matches = payload.data.payload.body.matchAll(IMAGE_REGEX);
|
|
133
|
+
const images = Array.from(matches).map(match => ({
|
|
134
|
+
full: match[0] || '',
|
|
135
|
+
src: match[2] || '',
|
|
136
|
+
alt: match[1] || uuidv4(),
|
|
137
|
+
replace: true,
|
|
138
|
+
}));
|
|
139
|
+
|
|
140
|
+
// Add heading image to beginning of images
|
|
141
|
+
images.unshift({
|
|
142
|
+
full: '',
|
|
143
|
+
src: payload.data.payload.headerImageURL,
|
|
144
|
+
alt: payload.data.payload.url,
|
|
145
|
+
replace: false,
|
|
146
|
+
});
|
|
60
147
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
148
|
+
// Log
|
|
149
|
+
assistant.log(`extractImages(): images`, images);
|
|
150
|
+
|
|
151
|
+
// Check if no images
|
|
152
|
+
if (!images) {
|
|
153
|
+
return resolve();
|
|
65
154
|
}
|
|
66
155
|
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
})
|
|
156
|
+
// Loop through images
|
|
157
|
+
for (let index = 0; index < images.length; index++) {
|
|
158
|
+
const image = images[index];
|
|
94
159
|
|
|
95
|
-
|
|
160
|
+
// Download image
|
|
161
|
+
const download = await self.downloadImage(image.src, image.alt).catch((e) => e);
|
|
162
|
+
|
|
163
|
+
// Log
|
|
164
|
+
assistant.log(`extractImages(): download`, download);
|
|
165
|
+
|
|
166
|
+
// Check for error
|
|
167
|
+
if (download instanceof Error) {
|
|
168
|
+
return reject(download);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Upload image
|
|
172
|
+
const upload = await self.uploadImage(download).catch((e) => e);
|
|
173
|
+
|
|
174
|
+
// Log
|
|
175
|
+
assistant.log(`extractImages(): upload`, upload);
|
|
176
|
+
|
|
177
|
+
// Check for error
|
|
178
|
+
if (upload instanceof Error) {
|
|
179
|
+
return reject(upload);
|
|
180
|
+
}
|
|
96
181
|
|
|
182
|
+
// Create new image tag
|
|
183
|
+
const tag = powertools.template(IMAGE_TAG, {name: download.filename, alt: image.alt});
|
|
184
|
+
|
|
185
|
+
// Replace image in body
|
|
186
|
+
if (image.replace) {
|
|
187
|
+
payload.data.payload.body = payload.data.payload.body
|
|
188
|
+
.replace(image.full, tag);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Log
|
|
192
|
+
assistant.log(`extractImages(): tag`, tag);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Resolve
|
|
196
|
+
return resolve();
|
|
197
|
+
});
|
|
97
198
|
};
|
|
98
199
|
|
|
99
|
-
//
|
|
100
|
-
Module.prototype.
|
|
200
|
+
// Downlaod image
|
|
201
|
+
Module.prototype.downloadImage = function (src, alt) {
|
|
101
202
|
const self = this;
|
|
102
203
|
const Manager = self.Manager;
|
|
103
204
|
const Api = self.Api;
|
|
104
205
|
const assistant = self.assistant;
|
|
105
206
|
const payload = self.payload;
|
|
106
207
|
|
|
107
|
-
return new Promise(async (resolve, reject)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
let sha;
|
|
208
|
+
return new Promise(async function(resolve, reject) {
|
|
209
|
+
// Log
|
|
210
|
+
const hyphenated = hyphenate(alt);
|
|
111
211
|
|
|
112
212
|
// Log
|
|
113
|
-
assistant.log(`
|
|
213
|
+
assistant.log(`downloadImage(): src=${src}, alt=${alt}, hyphenated=${hyphenated}`);
|
|
114
214
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
215
|
+
// Get image
|
|
216
|
+
await fetch(src, {
|
|
217
|
+
method: 'get',
|
|
218
|
+
download: `${assistant.tmpdir}/${hyphenated}`,
|
|
219
|
+
})
|
|
220
|
+
.then((r) => {
|
|
221
|
+
r.filename = path.basename(r.path);
|
|
222
|
+
r.ext = path.extname(r.path);
|
|
119
223
|
|
|
120
224
|
// Log
|
|
121
|
-
assistant.log(`
|
|
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
|
-
}
|
|
225
|
+
assistant.log(`downloadImage(): Result`, r.path);
|
|
148
226
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
}
|
|
227
|
+
// If not .jpg, reject
|
|
228
|
+
if (r.ext !== '.jpg') {
|
|
229
|
+
return reject(assistant.errorify(`Images must be .jpg (not ${r.ext})`, {code: 400}));
|
|
230
|
+
}
|
|
166
231
|
|
|
167
|
-
|
|
168
|
-
|
|
232
|
+
// Save image
|
|
233
|
+
return resolve(r);
|
|
234
|
+
})
|
|
235
|
+
.catch((e) => reject(e));
|
|
236
|
+
});
|
|
237
|
+
};
|
|
169
238
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
239
|
+
// Upload image to GitHub
|
|
240
|
+
Module.prototype.uploadImage = function (image) {
|
|
241
|
+
const self = this;
|
|
242
|
+
const Manager = self.Manager;
|
|
243
|
+
const Api = self.Api;
|
|
244
|
+
const assistant = self.assistant;
|
|
245
|
+
const payload = self.payload;
|
|
174
246
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
247
|
+
return new Promise(async function(resolve, reject) {
|
|
248
|
+
// Save variables
|
|
249
|
+
const filepath = image.path;
|
|
250
|
+
const filename = image.filename;
|
|
251
|
+
const assetsPath = powertools.template(IMAGE_PATH_SRC, payload.data.payload);
|
|
252
|
+
|
|
253
|
+
// Log
|
|
254
|
+
assistant.log(`uploadImage(): image`, image);
|
|
255
|
+
assistant.log(`uploadImage(): path`, `${assetsPath}${filename}`);
|
|
256
|
+
|
|
257
|
+
// Upload image
|
|
258
|
+
await self.octokit.rest.repos.createOrUpdateFileContents({
|
|
259
|
+
owner: payload.data.payload.githubUser,
|
|
260
|
+
repo: payload.data.payload.githubRepo,
|
|
261
|
+
path: `${assetsPath}${filename}`,
|
|
262
|
+
message: `📦 admin:create-post:upload-image ${filename}`,
|
|
263
|
+
content: jetpack.read(filepath, 'buffer').toString('base64'),
|
|
264
|
+
})
|
|
265
|
+
.then((r) => {
|
|
266
|
+
// Log
|
|
267
|
+
assistant.log(`uploadImage(): Result`, r);
|
|
268
|
+
|
|
269
|
+
// Resolve
|
|
270
|
+
return resolve(r);
|
|
271
|
+
})
|
|
272
|
+
.catch((e) => reject(e));
|
|
186
273
|
});
|
|
187
|
-
}
|
|
274
|
+
};
|
|
188
275
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
})
|
|
276
|
+
// Upload post to GitHub
|
|
277
|
+
Module.prototype.uploadPost = function (content) {
|
|
278
|
+
const self = this;
|
|
279
|
+
const Manager = self.Manager;
|
|
280
|
+
const Api = self.Api;
|
|
281
|
+
const assistant = self.assistant;
|
|
282
|
+
const payload = self.payload;
|
|
283
|
+
|
|
284
|
+
return new Promise(async function(resolve, reject) {
|
|
285
|
+
// Save variables
|
|
286
|
+
const filename = `${payload.data.payload.path}/${payload.data.payload.date}-${payload.data.payload.url}.md`;
|
|
211
287
|
|
|
288
|
+
// Log
|
|
289
|
+
assistant.log(`uploadPost(): filename`, filename);
|
|
290
|
+
|
|
291
|
+
// Upload post
|
|
292
|
+
await self.octokit.rest.repos.createOrUpdateFileContents({
|
|
293
|
+
owner: payload.data.payload.githubUser,
|
|
294
|
+
repo: payload.data.payload.githubRepo,
|
|
295
|
+
path: filename,
|
|
296
|
+
message: `📦 admin:create-post:upload-post ${filename}`,
|
|
297
|
+
content: Buffer.from(content).toString('base64'),
|
|
298
|
+
})
|
|
299
|
+
.then((r) => {
|
|
300
|
+
// Log
|
|
301
|
+
assistant.log(`uploadPost(): Result`, r);
|
|
302
|
+
|
|
303
|
+
// Resolve
|
|
304
|
+
return resolve(r);
|
|
305
|
+
})
|
|
306
|
+
.catch((e) => reject(e));
|
|
212
307
|
});
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
function hyphenate(s) {
|
|
311
|
+
return s
|
|
312
|
+
// Remove everything that is not a letter or a number
|
|
313
|
+
.replace(/[^a-zA-Z0-9]/g, '-')
|
|
314
|
+
// Replace multiple hyphens with a single hyphen
|
|
315
|
+
.replace(/-+/g, '-')
|
|
316
|
+
// Remove leading and trailing hyphens
|
|
317
|
+
.replace(/^-|-$/g, '')
|
|
318
|
+
// Lowercase
|
|
319
|
+
.toLowerCase();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function insertAds(article, paragraphsPerAd, minCharsBetweenAds) {
|
|
323
|
+
const paragraphs = article.split('\n\n');
|
|
324
|
+
const newParagraphs = [];
|
|
325
|
+
let charCount = 0;
|
|
326
|
+
let adCount = 0;
|
|
327
|
+
|
|
328
|
+
for (let i = 0, total = paragraphs.length; i < total; i++) {
|
|
329
|
+
const collection = paragraphs[i];
|
|
330
|
+
|
|
331
|
+
charCount += collection.length;
|
|
332
|
+
|
|
333
|
+
newParagraphs.push(collection);
|
|
334
|
+
|
|
335
|
+
if (i >= paragraphsPerAd && charCount >= minCharsBetweenAds) {
|
|
336
|
+
newParagraphs.push(powertools.template(AD_TAG, {index: adCount++}));
|
|
337
|
+
charCount = 0;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return newParagraphs.join('\n\n');
|
|
213
342
|
}
|
|
214
343
|
|
|
215
344
|
module.exports = Module;
|
|
@@ -1,192 +0,0 @@
|
|
|
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;
|