172 lines
4.2 KiB
JavaScript
172 lines
4.2 KiB
JavaScript
|
import xf from './xfetch'
|
||
|
|
||
|
class GoogleDrive {
|
||
|
constructor(auth) {
|
||
|
this.auth = auth
|
||
|
this.expires = 0
|
||
|
this._getIdCache = new Map()
|
||
|
}
|
||
|
async initializeClient() {
|
||
|
// any method that do api call must call this beforehand
|
||
|
if (Date.now() < this.expires) return
|
||
|
const resp = await xf
|
||
|
.post('https://www.googleapis.com/oauth2/v4/token', {
|
||
|
urlencoded: {
|
||
|
client_id: this.auth.client_id,
|
||
|
client_secret: this.auth.client_secret,
|
||
|
refresh_token: this.auth.refresh_token,
|
||
|
grant_type: 'refresh_token'
|
||
|
}
|
||
|
})
|
||
|
.json()
|
||
|
this.client = xf.extend({
|
||
|
baseURI: 'https://www.googleapis.com/drive/v3/',
|
||
|
headers: {
|
||
|
Authorization: `Bearer ${resp.access_token}`
|
||
|
}
|
||
|
})
|
||
|
this.expires = Date.now() + 3500 * 1000 // normally, it should expiers after 3600 seconds
|
||
|
}
|
||
|
async listDrive() {
|
||
|
await this.initializeClient()
|
||
|
return this.client.get('drives').json()
|
||
|
}
|
||
|
async download(id, range = '') {
|
||
|
await this.initializeClient()
|
||
|
return this.client.get(`files/${id}`, {
|
||
|
qs: {
|
||
|
includeItemsFromAllDrives: true,
|
||
|
supportsAllDrives: true,
|
||
|
alt: 'media'
|
||
|
},
|
||
|
headers: {
|
||
|
Range: range
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
async downloadByPath(path, rootId = 'root', range = '') {
|
||
|
const id = await this.getId(path, rootId)
|
||
|
if (!id) return null
|
||
|
return this.download(id, range)
|
||
|
}
|
||
|
async getMeta(id) {
|
||
|
await this.initializeClient()
|
||
|
return this.client
|
||
|
.get(`files/${id}`, {
|
||
|
qs: {
|
||
|
includeItemsFromAllDrives: true,
|
||
|
supportsAllDrives: true,
|
||
|
fields: '*'
|
||
|
}
|
||
|
})
|
||
|
.json()
|
||
|
}
|
||
|
async getMetaByPath(path, rootId = 'root') {
|
||
|
const id = await this.getId(path, rootId)
|
||
|
if (!id) return null
|
||
|
return this.getMeta(id)
|
||
|
}
|
||
|
async listFolder(id) {
|
||
|
await this.initializeClient()
|
||
|
const getList = pageToken => {
|
||
|
const qs = {
|
||
|
includeItemsFromAllDrives: true,
|
||
|
supportsAllDrives: true,
|
||
|
q: `'${id}' in parents and trashed = false`,
|
||
|
orderBy: 'folder,name,modifiedTime desc',
|
||
|
fields:
|
||
|
'files(id,name,mimeType,size,modifiedTime),nextPageToken',
|
||
|
pageSize: 1000
|
||
|
}
|
||
|
if (pageToken) {
|
||
|
qs.pageToken = pageToken
|
||
|
}
|
||
|
return this.client
|
||
|
.get('files', {
|
||
|
qs
|
||
|
})
|
||
|
.json()
|
||
|
}
|
||
|
const files = []
|
||
|
let pageToken
|
||
|
do {
|
||
|
const resp = await getList(pageToken)
|
||
|
files.push(...resp.files)
|
||
|
pageToken = resp.nextPageToken
|
||
|
} while (pageToken)
|
||
|
return { files }
|
||
|
}
|
||
|
async listFolderByPath(path, rootId = 'root') {
|
||
|
const id = await this.getId(path, rootId)
|
||
|
if (!id) return null
|
||
|
return this.listFolder(id)
|
||
|
}
|
||
|
async getId(path, rootId = 'root') {
|
||
|
const toks = path.split('/').filter(Boolean)
|
||
|
let id = rootId
|
||
|
for (const tok of toks) {
|
||
|
id = await this._getId(id, tok)
|
||
|
}
|
||
|
return id
|
||
|
}
|
||
|
async _getId(parentId, childName) {
|
||
|
if (this._getIdCache.has(parentId + childName)) {
|
||
|
return this._getIdCache.get(parentId + childName)
|
||
|
}
|
||
|
await this.initializeClient()
|
||
|
childName = childName.replace(/\'/g, `\\'`) // escape single quote
|
||
|
const resp = await this.client
|
||
|
.get('files', {
|
||
|
qs: {
|
||
|
includeItemsFromAllDrives: true,
|
||
|
supportsAllDrives: true,
|
||
|
q: `'${parentId}' in parents and name = '${childName}' and trashed = false`,
|
||
|
fields: 'files(id)'
|
||
|
}
|
||
|
})
|
||
|
.json()
|
||
|
.catch(e => ({ files: [] })) // if error, make it empty
|
||
|
if (resp.files.length === 0) {
|
||
|
return null
|
||
|
}
|
||
|
this._getIdCache.has(parentId + childName)
|
||
|
return resp.files[0].id // when there are more than 1 items, simply return the first one
|
||
|
}
|
||
|
async upload(parentId, name, file) {
|
||
|
await this.initializeClient()
|
||
|
const createResp = await this.client.post(
|
||
|
'https://www.googleapis.com/upload/drive/v3/files',
|
||
|
{
|
||
|
qs: {
|
||
|
uploadType: 'resumable',
|
||
|
supportsAllDrives: true
|
||
|
},
|
||
|
json: {
|
||
|
name,
|
||
|
parents: [parentId]
|
||
|
}
|
||
|
}
|
||
|
)
|
||
|
const putUrl = createResp.headers.get('Location')
|
||
|
return this.client
|
||
|
.put(putUrl, {
|
||
|
body: file
|
||
|
})
|
||
|
.json()
|
||
|
}
|
||
|
async uploadByPath(path, name, file, rootId = 'root') {
|
||
|
const id = await this.getId(path, rootId)
|
||
|
if (!id) return null
|
||
|
return this.upload(id, name, file)
|
||
|
}
|
||
|
async delete(fileId) {
|
||
|
return this.client.delete(`files/${fileId}`)
|
||
|
}
|
||
|
async deleteByPath(path, rootId = 'root') {
|
||
|
const id = await this.getId(path, rootId)
|
||
|
if (!id) return null
|
||
|
return this.delete(id)
|
||
|
}
|
||
|
}
|
||
|
export default GoogleDrive
|