2020-06-29 23:29:03 +08:00
|
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
const { argv } = require('yargs')
|
2020-07-15 14:36:39 +08:00
|
|
|
|
.usage('用法: ./$0 folder-id\nfolder-id 是你想檢測SA是否對其有閱讀權限的目錄ID')
|
2020-06-29 23:29:03 +08:00
|
|
|
|
.help('h')
|
|
|
|
|
.alias('h', 'help')
|
|
|
|
|
|
|
|
|
|
const fs = require('fs')
|
|
|
|
|
const path = require('path')
|
|
|
|
|
const prompts = require('prompts')
|
|
|
|
|
const { GoogleToken } = require('gtoken')
|
|
|
|
|
const axios = require('@viegg/axios')
|
|
|
|
|
const HttpsProxyAgent = require('https-proxy-agent')
|
|
|
|
|
|
|
|
|
|
const { https_proxy } = process.env
|
|
|
|
|
const axins = axios.create(https_proxy ? { httpsAgent: new HttpsProxyAgent(https_proxy) } : {})
|
|
|
|
|
|
|
|
|
|
const SA_FILES = fs.readdirSync(path.join(__dirname, 'sa')).filter(v => v.endsWith('.json'))
|
|
|
|
|
const SA_TOKENS = SA_FILES.map(filename => {
|
|
|
|
|
const gtoken = new GoogleToken({
|
|
|
|
|
keyFile: path.join(__dirname, 'sa', filename),
|
|
|
|
|
scope: ['https://www.googleapis.com/auth/drive']
|
|
|
|
|
})
|
|
|
|
|
return {gtoken, filename}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
main()
|
|
|
|
|
async function main () {
|
|
|
|
|
const [fid] = argv._
|
|
|
|
|
if (validate_fid(fid)) {
|
2020-07-15 14:36:39 +08:00
|
|
|
|
console.log('開始檢測', SA_TOKENS.length, '個SA帳號')
|
2020-06-29 23:29:03 +08:00
|
|
|
|
const invalid_sa = await get_invalid_sa(SA_TOKENS, fid)
|
2020-07-15 14:36:39 +08:00
|
|
|
|
if (!invalid_sa.length) return console.log('已檢測', SA_TOKENS.length, '個SA,未檢測到無效帳號')
|
2020-06-29 23:29:03 +08:00
|
|
|
|
const choice = await choose(invalid_sa.length)
|
|
|
|
|
if (choice === 'yes') {
|
|
|
|
|
mv_sa(invalid_sa)
|
2020-07-15 14:36:39 +08:00
|
|
|
|
console.log('成功移動')
|
2020-06-29 23:29:03 +08:00
|
|
|
|
} else {
|
2020-07-15 14:36:39 +08:00
|
|
|
|
console.log('成功退出,無效的SA記錄:', invalid_sa)
|
2020-06-29 23:29:03 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2020-07-15 14:36:39 +08:00
|
|
|
|
console.warn('目錄ID缺失或格式錯誤')
|
2020-06-29 23:29:03 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mv_sa (arr) {
|
|
|
|
|
for (const filename of arr) {
|
|
|
|
|
const oldpath = path.join(__dirname, 'sa', filename)
|
|
|
|
|
const new_path = path.join(__dirname, 'sa/invalid', filename)
|
|
|
|
|
fs.renameSync(oldpath, new_path)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function choose (count) {
|
|
|
|
|
const answer = await prompts({
|
|
|
|
|
type: 'select',
|
|
|
|
|
name: 'value',
|
2020-07-15 14:36:39 +08:00
|
|
|
|
message: `檢測到 ${count} 個無效的SA,是否將它們移動到 sa/invalid 目錄下?`,
|
2020-06-29 23:29:03 +08:00
|
|
|
|
choices: [
|
2020-07-15 14:36:39 +08:00
|
|
|
|
{ title: 'Yes', description: '確認移動', value: 'yes' },
|
2020-06-29 23:29:03 +08:00
|
|
|
|
{ title: 'No', description: '不做更改,直接退出', value: 'no' }
|
|
|
|
|
],
|
|
|
|
|
initial: 0
|
|
|
|
|
})
|
|
|
|
|
return answer.value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function get_invalid_sa (arr, fid) {
|
2020-07-15 14:36:39 +08:00
|
|
|
|
if (!fid) throw new Error('請指定要檢測權限的目錄ID')
|
2020-06-29 23:29:03 +08:00
|
|
|
|
const fails = []
|
|
|
|
|
let flag = 0
|
|
|
|
|
let good = 0
|
|
|
|
|
for (const v of arr) {
|
2020-07-15 14:36:39 +08:00
|
|
|
|
console.log('檢測進度', `${flag++}/${arr.length}`)
|
|
|
|
|
console.log('正常/異常', `${good}/${fails.length}`)
|
2020-06-29 23:29:03 +08:00
|
|
|
|
const {gtoken, filename} = v
|
|
|
|
|
try {
|
|
|
|
|
const access_token = await get_sa_token(gtoken)
|
|
|
|
|
await get_info(fid, access_token)
|
|
|
|
|
good++
|
|
|
|
|
} catch (e) {
|
2020-07-30 17:43:16 +08:00
|
|
|
|
handle_error(e)
|
2020-06-29 23:29:03 +08:00
|
|
|
|
const status = e && e.response && e.response.status
|
2020-07-30 17:43:16 +08:00
|
|
|
|
if (Number(status) === 400) fails.push(filename) // access_token 获取失败
|
2020-06-29 23:29:03 +08:00
|
|
|
|
|
|
|
|
|
const data = e && e.response && e.response.data
|
|
|
|
|
const code = data && data.error && data.error.code
|
2020-07-30 17:43:16 +08:00
|
|
|
|
if ([404, 403].includes(Number(code))) fails.push(filename) // 读取文件夹信息失败
|
2020-06-29 23:29:03 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return fails
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 17:43:16 +08:00
|
|
|
|
function handle_error (err) {
|
|
|
|
|
const data = err && err.response && err.response.data
|
|
|
|
|
if (data) {
|
|
|
|
|
console.error(JSON.stringify(data))
|
|
|
|
|
} else {
|
|
|
|
|
console.error(err.message)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:29:03 +08:00
|
|
|
|
async function get_info (fid, access_token) {
|
|
|
|
|
let url = `https://www.googleapis.com/drive/v3/files/${fid}`
|
|
|
|
|
let params = {
|
|
|
|
|
includeItemsFromAllDrives: true,
|
|
|
|
|
supportsAllDrives: true,
|
|
|
|
|
corpora: 'allDrives',
|
|
|
|
|
fields: 'id,name'
|
|
|
|
|
}
|
|
|
|
|
url += '?' + params_to_query(params)
|
|
|
|
|
const headers = { authorization: 'Bearer ' + access_token }
|
|
|
|
|
const { data } = await axins.get(url, { headers })
|
|
|
|
|
return data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function params_to_query (data) {
|
|
|
|
|
const ret = []
|
|
|
|
|
for (let d in data) {
|
|
|
|
|
ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]))
|
|
|
|
|
}
|
|
|
|
|
return ret.join('&')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function get_sa_token (gtoken) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
gtoken.getToken((err, tk) => {
|
|
|
|
|
err ? reject(err) : resolve(tk.access_token)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function validate_fid (fid) {
|
|
|
|
|
if (!fid) return false
|
|
|
|
|
fid = String(fid)
|
|
|
|
|
if (fid.length < 10 || fid.length > 100) return false
|
|
|
|
|
const reg = /^[a-zA-Z0-9_-]+$/
|
|
|
|
|
return fid.match(reg)
|
|
|
|
|
}
|