@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
214 lines (213 loc) • 7.41 kB
JavaScript
import * as dntShim from "../../../_dnt.shims.js";
import path from 'node:path';
import fs from 'node:fs';
import process from "node:process";
import yaml from 'js-yaml';
import { FRONTMATTER_DUMP_OPTS } from '../transform/frontmatters/frontmatter.js';
import { convertActionYaml } from '../action/ActionRunnerContainer.js';
async function execAsync(cmd) {
const command = new dntShim.Deno.Command('/bin/sh', {
args: ['-c', cmd],
stdout: 'piped',
stderr: 'piped',
});
const child = command.spawn();
setTimeout(() => {
console.error('Process timeout: ' + cmd);
child.kill();
}, 10000);
const decoder = new TextDecoder();
const [status] = await Promise.all([
child.status,
child.stdout.pipeTo(new WritableStream({
write(chunk, controller) {
console.log(decoder.decode(chunk));
}
})),
child.stderr.pipeTo(new WritableStream({
write(chunk, controller) {
console.error(decoder.decode(chunk));
}
}))
]);
if (!status.success) {
console.error('Process exited with status: ' + status.code);
throw new Error('Process exited with status: ' + status.code);
}
}
export class UserConfig {
constructor() {
Object.defineProperty(this, "remote_branch", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "hugo_theme", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "config_toml", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "transform_subdir", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "use_google_markdowns", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "auto_sync", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "fm_without_version", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "actions_yaml", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "rewrite_rules", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "preview_rewrite_rule", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "companion_files_rule", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
}
const DEFAULT_CONFIG = {
remote_branch: 'main',
hugo_theme: {
id: 'ananke',
name: 'Anake',
url: 'https://github.com/budparr/gohugo-theme-ananke.git',
preview_img: 'https://raw.githubusercontent.com/budparr/gohugo-theme-ananke/master/images/screenshot.png'
}
};
const DEFAULT_REWRITE_RULES = [
{
mode: 'MD',
tag: 'A',
match: '$alt',
template: '$href',
},
{
mode: 'MD',
match: '(?:https?:\\/\\/)?(?:www\\.)?(?:youtube\\.com\\/(?:[^\\/\\n\\s]+\\/\\S+\\/|(?:v|e(?:mbed)?)\\/|\\S*?[?&]v=)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})',
replace: '(?:https?:\\/\\/)?(?:www\\.)?(?:youtube\\.com\\/(?:[^\\/\\n\\s]+\\/\\S+\\/|(?:v|e(?:mbed)?)\\/|\\S*?[?&]v=)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})',
template: '[$label](https://youtube.be/$value)'
}
];
export class UserConfigService {
constructor(fileService, driveId) {
Object.defineProperty(this, "fileService", {
enumerable: true,
configurable: true,
writable: true,
value: fileService
});
Object.defineProperty(this, "driveId", {
enumerable: true,
configurable: true,
writable: true,
value: driveId
});
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
async load() {
if (await this.fileService.exists('.user_config.json')) {
const json = await this.fileService.readJson('.user_config.json');
this.config = json || {};
}
else {
this.config = structuredClone(DEFAULT_CONFIG);
await this.save();
}
const workflow = await convertActionYaml(this.config.actions_yaml);
this.config.actions_yaml = yaml.dump(workflow, FRONTMATTER_DUMP_OPTS);
if (!this.config.rewrite_rules || this.config.rewrite_rules.length === 0) {
this.config.rewrite_rules = DEFAULT_REWRITE_RULES;
}
if (!this.config.companion_files_rule) {
this.config.companion_files_rule = '(file.path == "content/navigation.md") || (file.path == "content/toc.md") || (commit.id && file.redirectTo == commit.id) || (commit.redirectTo == file.id && file.id)';
}
if (!this.config.preview_rewrite_rule) {
const driveId = this.driveId || this.fileService.getRealPath().split('/').pop();
const match = '^(.*)(\\.md|\\/_index\\.md)$';
const replace = process.env.DOMAIN + '/preview/' + driveId + '$1';
this.config.preview_rewrite_rule = `!${match}!${replace}!`;
}
return this.config;
}
async save() {
await this.fileService.writeJson('.user_config.json', this.config);
await this.genKeys();
}
async getDeployPrivateKeyPath() {
if (await this.fileService.exists('.private/id_rsa')) {
const fullPath = path.join(this.fileService.getRealPath(), '.private', 'id_rsa');
fs.chmodSync(fullPath, '0600');
return fullPath;
}
return null;
}
async getDeployKey() {
if (await this.fileService.exists('.private/id_rsa.pub')) {
return await this.fileService.readFile('.private/id_rsa.pub');
}
return null;
}
async genKeys(force = false) {
const privatePath = path.join(this.fileService.getRealPath(), '.private');
if (!fs.existsSync(privatePath)) {
fs.mkdirSync(privatePath);
}
else {
if (!force) {
return;
}
}
if (fs.existsSync(`${privatePath}/id_rsa`)) {
fs.unlinkSync(`${privatePath}/id_rsa`);
}
if (fs.existsSync(`${privatePath}/id_rsa.pub`)) {
fs.unlinkSync(`${privatePath}/id_rsa.pub`);
}
await execAsync(`ssh-keygen -t ecdsa -b 521 -f ${privatePath}/id_rsa -q -N ""`);
}
}