UNPKG

@mieweb/wikigdrive

Version:

Google Drive to MarkDown synchronization

214 lines (213 loc) 7.41 kB
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 ""`); } }