mirror of
https://gitlab.com/octtspacc/sitoctt
synced 2025-06-05 22:09:20 +02:00
Scripts
This commit is contained in:
@ -3,36 +3,51 @@ require('./Lib/Syncers.js').importAll();
|
||||
const Path = require('path');
|
||||
const Http = require('http');
|
||||
const Https = require('https');
|
||||
const Axios = require('axios');
|
||||
const JsDom = require('jsdom').JSDOM;
|
||||
|
||||
// true: Reuse the local directory structure for URL slugs
|
||||
// false: Keep a flat URL slug composing only of the file name
|
||||
const KeepTree = false;
|
||||
|
||||
// Word separation characters in slugs; first is preferred
|
||||
const WordSeps = '-–—_| ';
|
||||
|
||||
// Local OAuth server
|
||||
const Host = '127.0.0.1';
|
||||
|
||||
// TODO:
|
||||
// * Handle locally removed posts, only present on remote (delete or hide them on remote)
|
||||
// * Handle posts which filename changed and redirect URL has been set
|
||||
|
||||
let [Auth, Session] = [
|
||||
JSON.parse(process.env.WordpressAuth || '{}'),
|
||||
JSON.parse(process.env.WordpressSession || '{}'),
|
||||
];
|
||||
|
||||
const ApiBase = 'public-api.wordpress.com';
|
||||
const ApiStr = {
|
||||
protocol: 'https://',
|
||||
host: 'public-api.wordpress.com',
|
||||
path: '/rest/v1.1',
|
||||
};
|
||||
ApiStr.url = `${ApiStr.protocol}${ApiStr.host}${ApiStr.path}`;
|
||||
const Msg = {
|
||||
NoAuth: '\nPlease set the "WordpressAuth" ENV variable as a JSON string with keys "client_id" and "client_secret".\n(<https://developer.wordpress.com/apps/>)',
|
||||
NoSession: "\nNo valid session is available. You need to log in.\nOpen this link in a Web browser to log into Wordpress.com:\n",
|
||||
GotSession: '\nGot a new session string. Store it, and load it via the "WordpressSession" ENV variable for future use:\n',
|
||||
};
|
||||
|
||||
const AuthHeaders = () => {
|
||||
const AuthOpts = () => {
|
||||
return {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${Session.access_token}`,
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
let [LocalPosts, RemotePosts] = [[], []];
|
||||
let FetchingRemotePosts = false;
|
||||
let [LocalPosts, RemotePosts] = [{}, {}];
|
||||
let [GotLocalPosts, GotRemotePosts] = [false, false];
|
||||
|
||||
// https://stackoverflow.com/a/73594511
|
||||
Fs.walkSync = (Dir, Files = []) => {
|
||||
@ -48,6 +63,32 @@ Fs.walkSync = (Dir, Files = []) => {
|
||||
return Files;
|
||||
};
|
||||
|
||||
const MakeSlug = (File) => {
|
||||
let Slug = File
|
||||
.slice('./Posts/'.length)
|
||||
.split('.').slice(0, -1).join('.');
|
||||
const Last = Slug.split('/').slice(-1)[0];
|
||||
return ((!KeepTree && !(IsSlugTooSimple(Last) || Slug in LocalPosts))
|
||||
? Last
|
||||
: Slug);
|
||||
};
|
||||
|
||||
const IsSlugTooSimple = (Slug) => {
|
||||
let Nums = 0;
|
||||
WordSeps.split('').forEach((Sep) => {
|
||||
Slug = Slug.replaceAll(Sep, WordSeps[0]);
|
||||
});
|
||||
Slug = Slug.split(WordSeps[0]);
|
||||
for (let i=0; i<Slug.length; i++) {
|
||||
if (isNaN(Slug[i])) {
|
||||
break;
|
||||
} else {
|
||||
Nums += 1;
|
||||
};
|
||||
};
|
||||
return (Nums <= 1);
|
||||
};
|
||||
|
||||
// https://developer.wordpress.com/docs/oauth2/
|
||||
const AuthServer = () => {
|
||||
const Serv = Http.createServer((Req, Res) => {
|
||||
@ -59,9 +100,9 @@ const AuthServer = () => {
|
||||
Res.end('This window can now be closed.');
|
||||
Req = Https.request({
|
||||
method: "POST",
|
||||
host: ApiBase,
|
||||
host: ApiStr.host,
|
||||
path: "/oauth2/token",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded", },
|
||||
headers: AuthOpts().headers,
|
||||
}, (Res) => {
|
||||
let Data = '';
|
||||
Res.on('data', (Frag) => {
|
||||
@ -80,7 +121,7 @@ const AuthServer = () => {
|
||||
|
||||
Serv.listen(0, Host, () => {
|
||||
if (Auth.client_id && Auth.client_secret) {
|
||||
console.log(`${Msg.NoSession}<https://public-api.wordpress.com/oauth2/authorize?client_id=${Auth.client_id}&redirect_uri=http://${Host}:${Serv.address().port}&response_type=code>`);
|
||||
console.log(`${Msg.NoSession}<${ApiStr.protocol}${ApiStr.host}/oauth2/authorize?client_id=${Auth.client_id}&redirect_uri=http://${Host}:${Serv.address().port}&response_type=code>`);
|
||||
} else {
|
||||
console.log(Msg.NoAuth);
|
||||
Serv.close();
|
||||
@ -88,63 +129,106 @@ const AuthServer = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const GetLocalPosts = () => {
|
||||
Fs.walkSync('./Posts').forEach((File) => {
|
||||
const Meta = ParseMeta(Fs.readFileSync(File, 'utf8').trim().split('\n\n')[0]);
|
||||
if (Meta.Meta.Type === 'Post' || !Meta.Meta.Type) {
|
||||
const BuiltFile = `./public.Content/${File.split('.').slice(0, -1).join('.')}.html`;
|
||||
const Content = Fs.readFileSync(BuiltFile, 'utf8');
|
||||
const Slug = MakeSlug(File);
|
||||
LocalPosts[Slug] = Object.assign({ Path: Slug, Content: Content, Macros: Meta.Macros, }, Meta.Meta);
|
||||
LocalPosts[Slug].Title ||= JsDom.fragment(Content)
|
||||
.querySelector('h1, h2, h3, h4, h5, h6')
|
||||
.querySelector('.SectionTitle, .staticoso-SectionTitle')
|
||||
.textContent;
|
||||
};
|
||||
});
|
||||
GotLocalPosts = true;
|
||||
};
|
||||
|
||||
const GetRemotePosts = (Page) => {
|
||||
FetchingRemotePosts = true;
|
||||
const Opts = {
|
||||
Url: `https://${ApiBase}/rest/v1.1/sites/${Session.blog_id}/posts/`,
|
||||
const QueryOpts = new URLSearchParams({
|
||||
context: "edit",
|
||||
fields: "ID,slug,status,categories,tags,title,content",
|
||||
page_handle: encodeURIComponent(Page || ''),
|
||||
};
|
||||
Https.get(`${Opts.Url}?&fields=${Opts.fields}&context=edit&page_handle=${Opts.page_handle}`, AuthHeaders(),
|
||||
(Res) => {
|
||||
let Data = '';
|
||||
Res.on('data', (Frag) => {
|
||||
Data += Frag;
|
||||
}).on('end', () => {
|
||||
const JsonData = JSON.parse(Data);
|
||||
const NextPage = JsonData.meta.next_page;
|
||||
RemotePosts = RemotePosts.concat(JsonData.posts);
|
||||
if (NextPage) {
|
||||
GetRemotePosts(NextPage);
|
||||
process.stdout.write('.');
|
||||
} else {
|
||||
FetchingRemotePosts = false;
|
||||
};
|
||||
}).toString();
|
||||
// https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/posts/
|
||||
Axios.get(`${ApiStr.url}/sites/${Session.blog_id}/posts/?&${QueryOpts}`, AuthOpts()).then((Res) => {
|
||||
const NextPage = Res.data.meta.next_page;
|
||||
Res.data.posts.forEach((Post) => {
|
||||
RemotePosts[Post.slug] = Post;
|
||||
});
|
||||
if (NextPage) {
|
||||
GetRemotePosts(NextPage);
|
||||
process.stdout.write('.');
|
||||
} else {
|
||||
GotRemotePosts = true;
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const IsLocalRemotePostEqual = (Loc, Rem) => {
|
||||
if (true
|
||||
&& Loc.Title === Rem.title
|
||||
&& Loc.Content === Rem.content
|
||||
&& Loc.Description === Rem.excerpt
|
||||
&& Loc.Categories === Rem.categories
|
||||
&& Loc.Tags === Rem.tags
|
||||
&& Loc.CreatedOn === Rem.date
|
||||
) return true;
|
||||
else return false;
|
||||
};
|
||||
|
||||
const AfterFetch = () => {
|
||||
//let HaveNewPosts = false;
|
||||
console.log(LocalPosts);
|
||||
console.log(RemotePosts);
|
||||
Object.values(LocalPosts).forEach(async (Post) => {
|
||||
const Slug = Post.Path;
|
||||
const RemPost = RemotePosts[Slug];
|
||||
const ReqBody = {
|
||||
ID: RemPost ? RemPost.ID : "",
|
||||
slug: Slug,
|
||||
status: "draft",
|
||||
date: Post.CreatedOn || "",
|
||||
title: Post.Title,
|
||||
content: Post.Content || "",
|
||||
excerpt: Post.Description || "",
|
||||
categories: Post.Categories || "",
|
||||
tags: Post.Tags || "",
|
||||
};
|
||||
const QueryOpts = new URLSearchParams({
|
||||
context: "edit",
|
||||
}).toString();
|
||||
console.log(Slug, Slug in RemotePosts);
|
||||
if (RemPost) {
|
||||
// Post is on remote: Check if remote data is same as local, update remote if not
|
||||
if (!IsLocalRemotePostEqual(Post, RemPost)) {
|
||||
// https://developer.wordpress.com/docs/api/1.1/post/sites/%24site/posts/%24post_ID/
|
||||
//await Axios.post(`${ApiStr.url}/sites/${Session.blog_id}/posts/new/?&${QueryOpts}`, Object.assign(ReqBody, {}), AuthOpts()).then((Res) => {
|
||||
//console.log(Res.data);
|
||||
//});
|
||||
};
|
||||
} else {
|
||||
// Post doesnt exist; create blank post on remote (as draft), then edit it like the first case
|
||||
//HaveNewPosts = true;
|
||||
// https://developer.wordpress.com/docs/api/1.1/post/sites/%24site/posts/new/
|
||||
//await Axios.post(`${ApiStr.url}/sites/${Session.blog_id}/posts/new/?&${QueryOpts}`, Object.assign(ReqBody, { status: "draft", }), AuthOpts()).then((Res) => {
|
||||
//console.log(Res.data);
|
||||
//});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const TryUpsync = () => {
|
||||
console.log('[I] Reading local posts...');
|
||||
Fs.walkSync('./Posts').forEach((File) => {
|
||||
let Content = Fs.readFileSync(File, 'utf8').trim();
|
||||
const Meta = ParseMeta(Content.split('\n\n')[0]);
|
||||
if (Meta.Meta.Type === 'Post' || !Meta.Meta.Type) {
|
||||
Content = Content.split('\n\n').slice('1').join('\n\n');
|
||||
const Slug = KeepTree
|
||||
? File.slice('./Posts/'.length)
|
||||
: File.split('/').slice(-1)[0];
|
||||
const Obj = { Path: Slug, Meta: Meta, Content: Content, };
|
||||
LocalPosts.push(Obj);
|
||||
//LocalPosts[Slug] = Obj;
|
||||
};
|
||||
});
|
||||
console.log(LocalPosts);
|
||||
console.log('[I] Fetching remote posts...');
|
||||
console.log('[I] ^ Reading local posts...');
|
||||
GetLocalPosts();
|
||||
console.log('[I] ^ Fetching remote posts...');
|
||||
GetRemotePosts();
|
||||
var Interv = setInterval(() => {
|
||||
if (!FetchingRemotePosts) {
|
||||
if (GotLocalPosts && GotRemotePosts) {
|
||||
clearInterval(Interv);
|
||||
console.log(RemotePosts);
|
||||
// Find out which posts exist on remote and which not
|
||||
// Exist on remote and local:
|
||||
// Check if remote data is same as local, update remote if not
|
||||
// https://developer.wordpress.com/docs/api/1.1/post/sites/%24site/posts/%24post_ID/
|
||||
// Exist on local only:
|
||||
// Create blank post on remote (as draft), then edit it like the first case
|
||||
// https://developer.wordpress.com/docs/api/1.1/post/sites/%24site/posts/new/
|
||||
// Exist on remote only:
|
||||
// Was probably deleted or moved locally, what to do here?
|
||||
AfterFetch();
|
||||
};
|
||||
}, 50);
|
||||
};
|
||||
|
Reference in New Issue
Block a user