added option to use a command line script as image uploader (#2100)

This commit is contained in:
Tarcisio Gruppi 2022-01-08 13:00:28 -03:00 committed by GitHub
parent 4c517b1632
commit 2d15933c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 10 deletions

View File

@ -53,6 +53,19 @@
<el-button size="mini" :disabled="githubDisable" @click="setCurrentUploader('github')">Set as default</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="Command-line Script" name="cliScript">
<div class="description">The script will be executed with the image file path as its only argument and it should output any valid value for the <code>src</code> attribute of a <em>HTMLImageElement</em>.</div>
<div class="form-group">
<div class="label">
Shell script location
</div>
<el-input v-model="cliScript" placeholder="Script absolute path" size="mini"></el-input>
</div>
<div class="form-group">
<el-button size="mini" :disabled="cliScriptDisable" @click="save('cliScript')">Save</el-button>
<el-button size="mini" :disabled="cliScriptDisable" @click="setCurrentUploader('cliScript')">Set as default</el-button>
</div>
</el-tab-pane>
</el-tabs>
</section>
</div>
@ -62,6 +75,7 @@
import { shell } from 'electron'
import services, { isValidService } from './services.js'
import legalNoticesCheckbox from './legalNoticesCheckbox'
import { isFileExecutable } from '../../util/fileSystem'
export default {
components: {
@ -76,6 +90,7 @@ export default {
repo: '',
branch: ''
},
cliScript: '',
uploadServices: services,
legalNoticesErrorStates: {
smms: false,
@ -99,8 +114,19 @@ export default {
return this.$store.state.preferences.githubToken
}
},
prefCliScript: {
get: function () {
return this.$store.state.preferences.cliScript
}
},
githubDisable () {
return !this.githubToken || !this.github.owner || !this.github.repo
},
cliScriptDisable () {
if (!this.cliScript) {
return true
}
return !isFileExecutable(this.cliScript)
}
},
watch: {
@ -114,6 +140,7 @@ export default {
this.$nextTick(() => {
this.github = this.imageBed.github
this.githubToken = this.prefGithubToken
this.cliScript = this.prefCliScript
if (services.hasOwnProperty(this.currentUploader)) {
services[this.currentUploader].agreedToLegalNotices = true
@ -143,11 +170,22 @@ export default {
value: this.githubToken
})
}
if (withNotice) {
if (type === 'cliScript') {
this.$store.dispatch('SET_USER_DATA', {
type: 'cliScript',
value: this.cliScript
})
}
if (withNotice && type === 'github') {
new Notification('Save Image Uploader', {
body: 'The Github configration has been saved.'
})
}
if (withNotice && type === 'cliScript') {
new Notification('Save Image Uploader', {
body: 'The command line script configuration has been saved'
})
}
},
setCurrentUploader (value) {
const service = services[value]
@ -162,10 +200,12 @@ export default {
return
}
// Save the setting before set it as default uploader.
if (value === 'github') {
if (value === 'github' || value === 'cliScript') {
this.save(value, false)
}
this.legalNoticesErrorStates[value] = false
if (this.legalNoticesErrorStates[value] !== undefined) {
this.legalNoticesErrorStates[value] = false
}
const type = 'currentUploader'
this.$store.dispatch('SET_USER_DATA', { type, value })

View File

@ -35,6 +35,14 @@ const services = {
// Currently a non-persistent value
agreedToLegalNotices: false
},
cliScript: {
name: 'command line script',
isGdprCompliant: true,
privacyUrl: '',
tosUrl: '',
agreedToLegalNotices: true
}
}

View File

@ -94,7 +94,8 @@ const state = {
repo: '',
branch: ''
}
}
},
cliScript: ''
}
const getters = {}

View File

@ -6,6 +6,9 @@ import dayjs from 'dayjs'
import { Octokit } from '@octokit/rest'
import { ensureDirSync } from 'common/filesystem'
import { isImageFile } from 'common/filesystem/paths'
import cp from 'child_process'
import { tmpdir } from 'os'
import { unlink, writeFileSync, statSync, constants } from 'fs'
import { isWindows, dataURItoBlob } from './index'
import axios from '../axios'
@ -104,6 +107,7 @@ export const uploadImage = async (pathname, image, preferences) => {
const { currentUploader } = preferences
const { owner, repo, branch } = preferences.imageBed.github
const token = preferences.githubToken
const cliScript = preferences.cliScript
const isPath = typeof image === 'string'
const MAX_SIZE = 5 * 1024 * 1024
let re
@ -164,6 +168,25 @@ export const uploadImage = async (pathname, image, preferences) => {
})
}
const uploadByCliScript = (filepath, name = null) => {
let isPath = true
if (typeof filepath !== 'string') {
isPath = false
const data = new Uint8Array(filepath)
filepath = path.join(tmpdir(), name || +new Date())
writeFileSync(filepath, data)
}
cp.execFile(cliScript, [filepath], (err, data) => {
if (!isPath) {
unlink(filepath)
}
if (err) {
return rj(err)
}
re(data.trim())
})
}
const notification = () => {
rj('Cannot upload more than 5M image, the image will be copied to the image folder')
}
@ -177,6 +200,10 @@ export const uploadImage = async (pathname, image, preferences) => {
if (size > MAX_SIZE) {
notification()
} else {
if (currentUploader === 'cliScript') {
uploadByCliScript(imagePath)
return promise
}
const imageFile = await fs.readFile(imagePath)
const blobFile = new Blob([imageFile])
if (currentUploader === 'smms') {
@ -196,17 +223,34 @@ export const uploadImage = async (pathname, image, preferences) => {
} else {
const reader = new FileReader()
reader.onload = async () => {
const blobFile = dataURItoBlob(reader.result, image.name)
if (currentUploader === 'smms') {
uploadToSMMS(blobFile)
} else {
uploadByGithub(reader.result, image.name)
switch (currentUploader) {
case 'cliScript':
uploadByCliScript(reader.result, image.name)
break
case 'smms':
uploadToSMMS(dataURItoBlob(reader.result, image.name))
break
default:
uploadByGithub(reader.result, image.name)
}
}
reader.readAsDataURL(image)
const readerFunction = currentUploader === 'cliScript' ? 'readAsArrayBuffer' : 'readAsDataURL'
reader[readerFunction](image)
}
}
return promise
}
export const isFileExecutable = (filepath) => {
try {
const stat = statSync(filepath)
return stat.isFile() && (stat.mode & (constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH)) !== 0
} catch (err) {
// err ignored
return false
}
}