-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support command npm command line to quickly deploy private serv…
…er (doocs#106)
- Loading branch information
Showing
9 changed files
with
352 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
doocs-md-cli-* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#!/usr/bin/env node | ||
|
||
const getPort = require(`get-port`) | ||
const { | ||
colors, | ||
spawn, | ||
parseArgv, | ||
} = require(`./util.js`) | ||
|
||
const arg = parseArgv() | ||
|
||
new Promise(async () => { | ||
let { port = 8800, testPort, replayPort } = arg | ||
port = Number(port) | ||
;[port, testPort, replayPort] = await Promise.all([port, port+1, port+2].map(item => getPort({port: item}) )).catch(err => console.log(`err`, err)) | ||
const line = Object.entries({ | ||
...arg, | ||
proxy: `https://doocs.gitee.io/`, | ||
port, | ||
testPort, | ||
replayPort, | ||
'--config': `"${__dirname}/mm.config.js"`, | ||
}).map(([key, val]) => `${key}=${val}`).join(` `) | ||
const cliArg = [`"${__dirname}/node_modules/mockm/run.js"`, `--log-line`, line] | ||
spawn(`node`, cliArg) | ||
setTimeout(() => { | ||
// process.stdout.write('\33c\33[3J') | ||
console.log(``) | ||
console.log(`doocs/md 服务已启动:`) | ||
console.log(`打开链接 ${colors.green(`http://127.0.0.1:${port}/md/`)} 即刻使用吧~`) | ||
console.log(``) | ||
}, 3*1e3); | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
const fs = require(`fs`) | ||
const path = require(`path`) | ||
|
||
const { | ||
dcloud, | ||
parseArgv, | ||
} = require(`./util.js`) | ||
|
||
const arg = parseArgv() | ||
|
||
// unicloud 服务空间配置 | ||
const spaceInfo = { | ||
spaceId: ``, | ||
clientSecret: ``, | ||
...arg, | ||
} | ||
|
||
/** | ||
* 配置说明请参考文档: | ||
* https://hongqiye.com/doc/mockm/config/option.html | ||
* @type {import('mockm/@types/config').Config} | ||
*/ | ||
module.exports = util => { | ||
const port = Number(arg.port) || 9000 | ||
return { | ||
api: { | ||
async '/upload'(req, res) { | ||
const multiparty = await util.toolObj.generate.initPackge(`multiparty`) | ||
const form = new multiparty.Form({ | ||
uploadDir: `${__dirname}/public/upload/`, | ||
}) | ||
form.parse(req, async (err, fields = [], files) => { | ||
const file = files.file[0] | ||
let url = `http://127.0.0.1:${port}/public/upload/${path.parse(file.path).base}` | ||
try { | ||
url = await dcloud(spaceInfo)({name: file.originalFilename, file: fs.createReadStream(file.path)}) | ||
} catch (err) { | ||
// console.log(err) | ||
} | ||
res.json({url}) | ||
}) | ||
}, | ||
}, | ||
static: [ | ||
{ | ||
fileDir: `${__dirname}/dist`, | ||
path: `/md`, | ||
}, | ||
{ // 访问公共目录 | ||
fileDir: `${__dirname}/public`, | ||
path: `/public`, | ||
}, | ||
], | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "@doocs/md-cli", | ||
"version": "0.0.2", | ||
"description": "✍ 一款高度简洁的微信 Markdown 编辑器:支持 Markdown 所有基础语法、色盘取色、一键复制并粘贴到公众号后台、多图上传、一键下载文档、自定义 CSS 样式、一键重置等特性", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"bin": { | ||
"md-cli": "index.js" | ||
}, | ||
"files": [ | ||
"dist", | ||
"public", | ||
"index.js", | ||
"mm.config.js", | ||
"util.js" | ||
], | ||
"keywords": [], | ||
"author": "wll8", | ||
"license": "ISC", | ||
"dependencies": { | ||
"form-data": "2.3.3", | ||
"get-port": "5.1.1", | ||
"mockm": "^1.1.25", | ||
"node-fetch": "2.6.2" | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
const fetch = require('node-fetch') | ||
const FormData = require(`form-data`) | ||
|
||
/** | ||
* 自定义控制台颜色 | ||
* https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color | ||
* nodejs 内置颜色: https://nodejs.org/api/util.html#util_foreground_colors | ||
*/ | ||
function colors () { | ||
const util = require('util') | ||
|
||
function colorize (color, text) { | ||
const codes = util.inspect.colors[color] | ||
return `\x1b[${codes[0]}m${text}\x1b[${codes[1]}m` | ||
} | ||
|
||
let returnValue = {} | ||
Object.keys(util.inspect.colors).forEach((color) => { | ||
returnValue[color] = (text) => colorize(color, text) | ||
}) | ||
|
||
const colorTable = new Proxy(returnValue, { | ||
get (obj, prop) { | ||
// 在没有对应的具名颜色函数时, 返回空函数作为兼容处理 | ||
const res = obj[prop] ? obj[prop] : (arg => arg) | ||
return res | ||
} | ||
}) | ||
|
||
// 取消下行注释, 查看所有的颜色和名字: | ||
// Object.keys(returnValue).forEach((color) => console.log(returnValue[color](color))) | ||
return colorTable | ||
} | ||
|
||
/** | ||
* 以 Promise 方式运行 spawn | ||
* @param {*} cmd 主程序 | ||
* @param {*} args 程序参数数组 | ||
* @param {*} opts spawn 选项 | ||
*/ | ||
function spawn (cmd, args, opts) { | ||
opts = { stdio: `inherit`, ...opts } | ||
opts.shell = opts.shell || process.platform === 'win32' | ||
return new Promise((resolve, reject) => { | ||
const cp = require('child_process') | ||
const child = cp.spawn(cmd, args, opts) | ||
let stdout = '' | ||
let stderr = '' | ||
child.stdout && child.stdout.on('data', d => { stdout += d }) | ||
child.stderr && child.stderr.on('data', d => { stderr += d }) | ||
child.on('error', reject) | ||
child.on('close', code => { | ||
resolve({code, stdout, stderr}) | ||
}) | ||
}) | ||
} | ||
|
||
/** | ||
* 解析命令行参数 | ||
* @param {*} arr | ||
* @returns | ||
*/ | ||
function parseArgv(arr) { | ||
return (arr || process.argv.slice(2)).reduce((acc, arg) => { | ||
let [k, ...v] = arg.split('=') | ||
v = v.join(`=`) // 把带有 = 的值合并为字符串 | ||
acc[k] = v === '' // 没有值时, 则表示为 true | ||
? true | ||
: ( | ||
/^(true|false)$/.test(v) // 转换指明的 true/false | ||
? v === 'true' | ||
: ( | ||
/[\d|.]+/.test(v) | ||
? (isNaN(Number(v)) ? v : Number(v)) // 如果转换为数字失败, 则使用原始字符 | ||
: v | ||
) | ||
) | ||
return acc | ||
}, {}) | ||
} | ||
|
||
function dcloud(spaceInfo) { | ||
if(Boolean(spaceInfo.spaceId && spaceInfo.clientSecret) === false) { | ||
throw new Error(`请填写 spaceInfo`) | ||
} | ||
|
||
function sign(data, secret) { | ||
const hmac = require(`crypto`).createHmac(`md5`, secret) | ||
// 排序 obj 再转换为 key=val&key=val 的格式 | ||
const str = Object.keys(data).sort().reduce((acc, cur) => `${acc}&${cur}=${data[cur]}`, ``).slice(1) | ||
hmac.update(str) | ||
return hmac.digest(`hex`) | ||
} | ||
|
||
async function anonymousAuthorize() { | ||
const data = { | ||
method: `serverless.auth.user.anonymousAuthorize`, | ||
params: `{}`, | ||
spaceId: spaceInfo.spaceId, | ||
timestamp: Date.now(), | ||
} | ||
return await fetch(`https://api.bspapp.com/client`, { | ||
headers: { | ||
'x-serverless-sign': sign(data, spaceInfo.clientSecret), | ||
}, | ||
body: `{"method":"serverless.auth.user.anonymousAuthorize","params":"{}","spaceId":"${spaceInfo.spaceId}","timestamp":${data.timestamp}}`, | ||
method: `POST`, | ||
}).then((res) => res.json()) | ||
} | ||
|
||
async function report({ id, token }) { | ||
const reportReq = { | ||
method: `serverless.file.resource.report`, | ||
params: `{"id":"${id}"}`, | ||
spaceId: spaceInfo.spaceId, | ||
timestamp: Date.now(), | ||
token: token, | ||
} | ||
return await fetch(`https://api.bspapp.com/client`, { | ||
headers: { | ||
'x-basement-token': reportReq.token, | ||
'x-serverless-sign': sign(reportReq, spaceInfo.clientSecret), | ||
}, | ||
body: JSON.stringify(reportReq), | ||
method: `POST`, | ||
}).then((res) => res.json()) | ||
} | ||
|
||
async function generateProximalSign({ name, token }) { | ||
const data = { | ||
method: `serverless.file.resource.generateProximalSign`, | ||
params: `{"env":"public","filename":"${name}"}`, | ||
spaceId: spaceInfo.spaceId, | ||
timestamp: Date.now(), | ||
token, | ||
} | ||
const res = await fetch(`https://api.bspapp.com/client`, { | ||
headers: { | ||
'x-basement-token': data.token, | ||
'x-serverless-sign': sign(data, spaceInfo.clientSecret), | ||
}, | ||
body: JSON.stringify(data), | ||
method: `POST`, | ||
}).then((res) => res.json()) | ||
return res | ||
} | ||
|
||
async function upload({ data, file }) { | ||
const formdata = new FormData() | ||
Object.entries({ | ||
'Cache-Control': `max-age=2592000`, | ||
'Content-Disposition': `attachment`, | ||
OSSAccessKeyId: data.accessKeyId, | ||
Signature: data.signature, | ||
host: data.host, | ||
id: data.id, | ||
key: data.ossPath, | ||
policy: data.policy, | ||
success_action_status: 200, | ||
file, | ||
}).forEach(([key, val]) => formdata.append(key, val)) | ||
|
||
return await fetch(`https://${data.host}`, { | ||
headers: { | ||
'X-OSS-server-side-encrpytion': `AES256`, | ||
}, | ||
body: formdata, | ||
method: `POST`, | ||
}) | ||
} | ||
|
||
async function uploadFile({ name = `unnamed.file`, file }) { | ||
const token = (await anonymousAuthorize()).data.accessToken | ||
const res = await generateProximalSign({ name, token }) | ||
await upload({ data: res.data, file }) | ||
await report({ id: res.data.id, token }) | ||
const fileUrl = `https://${res.data.cdnDomain}/${res.data.ossPath}` | ||
return fileUrl | ||
} | ||
|
||
return uploadFile | ||
|
||
} | ||
|
||
module.exports = { | ||
colors: colors(), | ||
spawn, | ||
parseArgv, | ||
dcloud, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.