feat: storage modes + git improvements
This commit is contained in:
		| @@ -4,10 +4,19 @@ description: Local storage on disk or network shares. | ||||
| author: requarks.io | ||||
| logo: https://static.requarks.io/logo/local-fs.svg | ||||
| website: https://wiki.js.org | ||||
| isAvailable: true | ||||
| supportedModes: | ||||
|   - push | ||||
| defaultMode: push | ||||
| props: | ||||
|   path: | ||||
|     type: String | ||||
|     title: Path | ||||
|     hint: Absolute path without a trailing slash (e.g. /home/wiki/backup, C:\wiki\backup) | ||||
|     order: 1 | ||||
|   createDailyBackups: | ||||
|     type: Boolean | ||||
|     default: false | ||||
|     title: Create Daily Backups | ||||
|     hint: A tar.gz archive containing all content will be created daily in subfolder named _daily. Archives are kept for a month. | ||||
|     order: 2 | ||||
|   | ||||
| @@ -46,23 +46,32 @@ module.exports = { | ||||
|     // not used | ||||
|   }, | ||||
|   async init() { | ||||
|     WIKI.logger.info('(STORAGE/DISK) Initializing...') | ||||
|     await fs.ensureDir(this.config.path) | ||||
|     WIKI.logger.info('(STORAGE/DISK) Initialization completed.') | ||||
|   }, | ||||
|   async created() { | ||||
|     const filePath = path.join(this.config.path, `${this.page.path}.${getFileExtension(this.page.contentType)}`) | ||||
|     await fs.outputFile(filePath, injectMetadata(this.page), 'utf8') | ||||
|   async sync() { | ||||
|     // not used | ||||
|   }, | ||||
|   async updated() { | ||||
|     const filePath = path.join(this.config.path, `${this.page.path}.${getFileExtension(this.page.contentType)}`) | ||||
|     await fs.outputFile(filePath, injectMetadata(this.page), 'utf8') | ||||
|   async created(page) { | ||||
|     WIKI.logger.info(`(STORAGE/DISK) Creating file ${page.path}...`) | ||||
|     const filePath = path.join(this.config.path, `${page.path}.${getFileExtension(page.contentType)}`) | ||||
|     await fs.outputFile(filePath, injectMetadata(page), 'utf8') | ||||
|   }, | ||||
|   async deleted() { | ||||
|     const filePath = path.join(this.config.path, `${this.page.path}.${getFileExtension(this.page.contentType)}`) | ||||
|   async updated(page) { | ||||
|     WIKI.logger.info(`(STORAGE/DISK) Updating file ${page.path}...`) | ||||
|     const filePath = path.join(this.config.path, `${page.path}.${getFileExtension(page.contentType)}`) | ||||
|     await fs.outputFile(filePath, injectMetadata(page), 'utf8') | ||||
|   }, | ||||
|   async deleted(page) { | ||||
|     WIKI.logger.info(`(STORAGE/DISK) Deleting file ${page.path}...`) | ||||
|     const filePath = path.join(this.config.path, `${page.path}.${getFileExtension(page.contentType)}`) | ||||
|     await fs.unlink(filePath) | ||||
|   }, | ||||
|   async renamed() { | ||||
|     const sourceFilePath = path.join(this.config.path, `${this.page.sourcePath}.${getFileExtension(this.page.contentType)}`) | ||||
|     const destinationFilePath = path.join(this.config.path, `${this.page.destinationPath}.${getFileExtension(this.page.contentType)}`) | ||||
|   async renamed(page) { | ||||
|     WIKI.logger.info(`(STORAGE/DISK) Renaming file ${page.sourcePath} to ${page.destinationPath}...`) | ||||
|     const sourceFilePath = path.join(this.config.path, `${page.sourcePath}.${getFileExtension(page.contentType)}`) | ||||
|     const destinationFilePath = path.join(this.config.path, `${page.destinationPath}.${getFileExtension(page.contentType)}`) | ||||
|     await fs.move(sourceFilePath, destinationFilePath, { overwrite: true }) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -4,10 +4,12 @@ description: Git is a version control system for tracking changes in computer fi | ||||
| author: requarks.io | ||||
| logo: https://static.requarks.io/logo/git-alt.svg | ||||
| website: https://git-scm.com/ | ||||
| isAvailable: true | ||||
| supportedModes: | ||||
|   - sync | ||||
|   - push | ||||
|   - pull | ||||
| defaultMode: sync | ||||
| props: | ||||
|   authType: | ||||
|     type: String | ||||
| @@ -17,42 +19,52 @@ props: | ||||
|     enum: | ||||
|       - 'basic' | ||||
|       - 'ssh' | ||||
|     order: 1 | ||||
|   repoUrl: | ||||
|     type: String | ||||
|     title: Repository URI | ||||
|     hint: Git-compliant URI (e.g. git@github.com:org/repo.git for ssh, https://github.com/org/repo.git for basic) | ||||
|     order: 2 | ||||
|   branch: | ||||
|     type: String | ||||
|     default: 'master' | ||||
|     order: 3 | ||||
|   verifySSL: | ||||
|     type: Boolean | ||||
|     default: true | ||||
|     title: Verify SSL Certificate | ||||
|     hint: Some hosts requires SSL certificate checking to be disabled. Leave enabled for proper security. | ||||
|     order: 31 | ||||
|   sshPrivateKeyPath: | ||||
|     type: String | ||||
|     title: SSH Private Key Path | ||||
|     hint: SSH Authentication Only - Absolute path to the key. The key must NOT be passphrase-protected. | ||||
|     order: 10 | ||||
|   basicUsername: | ||||
|     type: String | ||||
|     title: Username | ||||
|     hint: Basic Authentication Only | ||||
|     order: 11 | ||||
|   basicPassword: | ||||
|     type: String | ||||
|     title: Password / PAT | ||||
|     hint: Basic Authentication Only | ||||
|     order: 12 | ||||
|   localRepoPath: | ||||
|     type: String | ||||
|     title: Local Repository Path | ||||
|     default: './data/repo' | ||||
|     hint: 'Path where the local git repository will be created.' | ||||
|     order: 30 | ||||
|   defaultEmail: | ||||
|     type: String | ||||
|     title: Default Author Email | ||||
|     default: 'name@company.com' | ||||
|     hint: 'Used as fallback in case the author of the change is not present.' | ||||
|     order: 20 | ||||
|   defaultName: | ||||
|     type: String | ||||
|     title: Default Author Name | ||||
|     default: 'John Smith' | ||||
|     hint: 'Used as fallback in case the author of the change is not present.' | ||||
|     order: 21 | ||||
|   | ||||
| @@ -3,8 +3,6 @@ const sgit = require('simple-git/promise') | ||||
| const fs = require('fs-extra') | ||||
| const _ = require('lodash') | ||||
|  | ||||
| let repoPath = path.join(process.cwd(), 'data/repo') | ||||
|  | ||||
| /** | ||||
|  * Get file extension based on content type | ||||
|  */ | ||||
| @@ -43,72 +41,80 @@ const injectMetadata = (page) => { | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|   git: null, | ||||
|   repoPath: path.join(process.cwd(), 'data/repo'), | ||||
|   async activated() { | ||||
|  | ||||
|     // not used | ||||
|   }, | ||||
|   async deactivated() { | ||||
|  | ||||
|     // not used | ||||
|   }, | ||||
|   async init() { | ||||
|     WIKI.logger.info('(STORAGE/GIT) Initializing...') | ||||
|     repoPath = path.resolve(WIKI.ROOTPATH, this.config.localRepoPath) | ||||
|     await fs.ensureDir(repoPath) | ||||
|     const git = sgit(repoPath) | ||||
|     this.repoPath = path.resolve(WIKI.ROOTPATH, this.config.localRepoPath) | ||||
|     await fs.ensureDir(this.repoPath) | ||||
|     this.git = sgit(this.repoPath) | ||||
|  | ||||
|     // Initialize repo (if needed) | ||||
|     WIKI.logger.info('(STORAGE/GIT) Checking repository state...') | ||||
|     const isRepo = await git.checkIsRepo() | ||||
|     const isRepo = await this.git.checkIsRepo() | ||||
|     if (!isRepo) { | ||||
|       WIKI.logger.info('(STORAGE/GIT) Initializing local repository...') | ||||
|       await git.init() | ||||
|       await this.git.init() | ||||
|     } | ||||
|  | ||||
|     // Set default author | ||||
|     await git.raw(['config', '--local', 'user.email', this.config.defaultEmail]) | ||||
|     await git.raw(['config', '--local', 'user.name', this.config.defaultName]) | ||||
|     await this.git.raw(['config', '--local', 'user.email', this.config.defaultEmail]) | ||||
|     await this.git.raw(['config', '--local', 'user.name', this.config.defaultName]) | ||||
|  | ||||
|     // Purge existing remotes | ||||
|     WIKI.logger.info('(STORAGE/GIT) Listing existing remotes...') | ||||
|     const remotes = await git.getRemotes() | ||||
|     const remotes = await this.git.getRemotes() | ||||
|     if (remotes.length > 0) { | ||||
|       WIKI.logger.info('(STORAGE/GIT) Purging existing remotes...') | ||||
|       for(let remote of remotes) { | ||||
|         await git.removeRemote(remote.name) | ||||
|         await this.git.removeRemote(remote.name) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Add remote | ||||
|     WIKI.logger.info('(STORAGE/GIT) Setting SSL Verification config...') | ||||
|     await git.raw(['config', '--local', '--bool', 'http.sslVerify', _.toString(this.config.verifySSL)]) | ||||
|     await this.git.raw(['config', '--local', '--bool', 'http.sslVerify', _.toString(this.config.verifySSL)]) | ||||
|     switch (this.config.authType) { | ||||
|       case 'ssh': | ||||
|         WIKI.logger.info('(STORAGE/GIT) Setting SSH Command config...') | ||||
|         await git.addConfig('core.sshCommand', `ssh -i "${this.config.sshPrivateKeyPath}" -o StrictHostKeyChecking=no`) | ||||
|         await this.git.addConfig('core.sshCommand', `ssh -i "${this.config.sshPrivateKeyPath}" -o StrictHostKeyChecking=no`) | ||||
|         WIKI.logger.info('(STORAGE/GIT) Adding origin remote via SSH...') | ||||
|         await git.addRemote('origin', this.config.repoUrl) | ||||
|         await this.git.addRemote('origin', this.config.repoUrl) | ||||
|         break | ||||
|       default: | ||||
|         WIKI.logger.info('(STORAGE/GIT) Adding origin remote via HTTPS...') | ||||
|         await git.addRemote('origin', `https://${this.config.basicUsername}:${this.config.basicPassword}@${this.config.repoUrl}`) | ||||
|         await this.git.addRemote('origin', `https://${this.config.basicUsername}:${this.config.basicPassword}@${this.config.repoUrl}`) | ||||
|         break | ||||
|     } | ||||
|  | ||||
|     // Fetch updates for remote | ||||
|     WIKI.logger.info('(STORAGE/GIT) Fetch updates from remote...') | ||||
|     await git.raw(['remote', 'update', 'origin']) | ||||
|     await this.git.raw(['remote', 'update', 'origin']) | ||||
|  | ||||
|     // Checkout branch | ||||
|     const branches = await git.branch() | ||||
|     const branches = await this.git.branch() | ||||
|     if (!_.includes(branches.all, this.config.branch) && !_.includes(branches.all, `remotes/origin/${this.config.branch}`)) { | ||||
|       throw new Error('Invalid branch! Make sure it exists on the remote first.') | ||||
|     } | ||||
|     WIKI.logger.info(`(STORAGE/GIT) Checking out branch ${this.config.branch}...`) | ||||
|     await git.checkout(this.config.branch) | ||||
|     await this.git.checkout(this.config.branch) | ||||
|  | ||||
|     // Perform initial sync | ||||
|     await this.sync() | ||||
|  | ||||
|     WIKI.logger.info('(STORAGE/GIT) Initialization completed.') | ||||
|   }, | ||||
|   async sync() { | ||||
|     // Pull rebase | ||||
|     if (_.includes(['sync', 'pull'], this.mode)) { | ||||
|       WIKI.logger.info(`(STORAGE/GIT) Performing pull rebase from origin on branch ${this.config.branch}...`) | ||||
|       await git.pull('origin', this.config.branch, ['--rebase']) | ||||
|       await this.git.pull('origin', this.config.branch, ['--rebase']) | ||||
|     } | ||||
|  | ||||
|     // Push | ||||
| @@ -118,50 +124,48 @@ module.exports = { | ||||
|       if (this.mode === 'push') { | ||||
|         pushOpts.push('--force') | ||||
|       } | ||||
|       await git.push('origin', this.config.branch, pushOpts) | ||||
|       await this.git.push('origin', this.config.branch, pushOpts) | ||||
|     } | ||||
|  | ||||
|     WIKI.logger.info('(STORAGE/GIT) Initialization completed.') | ||||
|   }, | ||||
|   async created() { | ||||
|     const fileName = `${this.page.path}.${getFileExtension(this.page.contentType)}` | ||||
|     const filePath = path.join(repoPath, fileName) | ||||
|     await fs.outputFile(filePath, injectMetadata(this.page), 'utf8') | ||||
|   async created(page) { | ||||
|     WIKI.logger.info(`(STORAGE/GIT) Committing new file ${page.path}...`) | ||||
|     const fileName = `${page.path}.${getFileExtension(page.contentType)}` | ||||
|     const filePath = path.join(this.repoPath, fileName) | ||||
|     await fs.outputFile(filePath, injectMetadata(page), 'utf8') | ||||
|  | ||||
|     const git = sgit(repoPath) | ||||
|     await git.add(`./${fileName}`) | ||||
|     await git.commit(`docs: create ${this.page.path}`, fileName, { | ||||
|       '--author': `"${this.page.authorName} <${this.page.authorEmail}>"` | ||||
|     await this.git.add(`./${fileName}`) | ||||
|     await this.git.commit(`docs: create ${page.path}`, fileName, { | ||||
|       '--author': `"${page.authorName} <${page.authorEmail}>"` | ||||
|     }) | ||||
|   }, | ||||
|   async updated() { | ||||
|     const fileName = `${this.page.path}.${getFileExtension(this.page.contentType)}` | ||||
|     const filePath = path.join(repoPath, fileName) | ||||
|     await fs.outputFile(filePath, injectMetadata(this.page), 'utf8') | ||||
|   async updated(page) { | ||||
|     WIKI.logger.info(`(STORAGE/GIT) Committing updated file ${page.path}...`) | ||||
|     const fileName = `${page.path}.${getFileExtension(page.contentType)}` | ||||
|     const filePath = path.join(this.repoPath, fileName) | ||||
|     await fs.outputFile(filePath, injectMetadata(page), 'utf8') | ||||
|  | ||||
|     const git = sgit(repoPath) | ||||
|     await git.add(`./${fileName}`) | ||||
|     await git.commit(`docs: update ${this.page.path}`, fileName, { | ||||
|       '--author': `"${this.page.authorName} <${this.page.authorEmail}>"` | ||||
|     await this.git.add(`./${fileName}`) | ||||
|     await this.git.commit(`docs: update ${page.path}`, fileName, { | ||||
|       '--author': `"${page.authorName} <${page.authorEmail}>"` | ||||
|     }) | ||||
|   }, | ||||
|   async deleted() { | ||||
|     const fileName = `${this.page.path}.${getFileExtension(this.page.contentType)}` | ||||
|   async deleted(page) { | ||||
|     WIKI.logger.info(`(STORAGE/GIT) Committing removed file ${page.path}...`) | ||||
|     const fileName = `${page.path}.${getFileExtension(page.contentType)}` | ||||
|  | ||||
|     const git = sgit(repoPath) | ||||
|     await git.rm(`./${fileName}`) | ||||
|     await git.commit(`docs: delete ${this.page.path}`, fileName, { | ||||
|       '--author': `"${this.page.authorName} <${this.page.authorEmail}>"` | ||||
|     await this.git.rm(`./${fileName}`) | ||||
|     await this.git.commit(`docs: delete ${page.path}`, fileName, { | ||||
|       '--author': `"${page.authorName} <${page.authorEmail}>"` | ||||
|     }) | ||||
|   }, | ||||
|   async renamed() { | ||||
|     const sourceFilePath = `${this.page.sourcePath}.${getFileExtension(this.page.contentType)}` | ||||
|     const destinationFilePath = `${this.page.destinationPath}.${getFileExtension(this.page.contentType)}` | ||||
|   async renamed(page) { | ||||
|     WIKI.logger.info(`(STORAGE/GIT) Committing file move from ${page.sourcePath} to ${page.destinationPath}...`) | ||||
|     const sourceFilePath = `${page.sourcePath}.${getFileExtension(page.contentType)}` | ||||
|     const destinationFilePath = `${page.destinationPath}.${getFileExtension(page.contentType)}` | ||||
|  | ||||
|     const git = sgit(repoPath) | ||||
|     await git.mv(`./${sourceFilePath}`, `./${destinationFilePath}`) | ||||
|     await git.commit(`docs: rename ${this.page.sourcePath} to ${destinationFilePath}`, destinationFilePath, { | ||||
|       '--author': `"${this.page.authorName} <${this.page.authorEmail}>"` | ||||
|     await this.git.mv(`./${sourceFilePath}`, `./${destinationFilePath}`) | ||||
|     await this.git.commit(`docs: rename ${page.sourcePath} to ${destinationFilePath}`, destinationFilePath, { | ||||
|       '--author': `"${page.authorName} <${page.authorEmail}>"` | ||||
|     }) | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user