gd-utils/validate-sa.js

140 lines
4.0 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
const { argv } = require('yargs')
.usage('用法: ./$0 folder-id\nfolder-id 是你想檢測SA是否對其有閱讀權限的目錄ID')
.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)) {
console.log('開始檢測', SA_TOKENS.length, '個SA帳號')
const invalid_sa = await get_invalid_sa(SA_TOKENS, fid)
if (!invalid_sa.length) return console.log('已檢測', SA_TOKENS.length, '個SA未檢測到無效帳號')
const choice = await choose(invalid_sa.length)
if (choice === 'yes') {
mv_sa(invalid_sa)
console.log('成功移動')
} else {
console.log('成功退出無效的SA記錄', invalid_sa)
}
} else {
console.warn('目錄ID缺失或格式錯誤')
}
}
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',
message: `檢測到 ${count} 個無效的SA是否將它們移動到 sa/invalid 目錄下?`,
choices: [
{ title: 'Yes', description: '確認移動', value: 'yes' },
{ title: 'No', description: '不做更改,直接退出', value: 'no' }
],
initial: 0
})
return answer.value
}
async function get_invalid_sa (arr, fid) {
if (!fid) throw new Error('請指定要檢測權限的目錄ID')
const fails = []
let flag = 0
let good = 0
for (const v of arr) {
console.log('檢測進度', `${flag++}/${arr.length}`)
console.log('正常/異常', `${good}/${fails.length}`)
const {gtoken, filename} = v
try {
const access_token = await get_sa_token(gtoken)
await get_info(fid, access_token)
good++
} catch (e) {
handle_error(e)
const status = e && e.response && e.response.status
if (Number(status) === 400) fails.push(filename) // access_token 获取失败
const data = e && e.response && e.response.data
const code = data && data.error && data.error.code
if ([404, 403].includes(Number(code))) fails.push(filename) // 读取文件夹信息失败
}
}
return fails
}
function handle_error (err) {
const data = err && err.response && err.response.data
if (data) {
console.error(JSON.stringify(data))
} else {
console.error(err.message)
}
}
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)
}