Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
reruin committed Oct 10, 2021
1 parent 27350fb commit 135a058
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 159 deletions.
9 changes: 9 additions & 0 deletions packages/sharelist-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## [0.1.5](https://github.com/reruin/sharelist/compare/v0.3.3...v0.1.5) (2021-10-10)


### Bug Fixes

* **core:** add createReadStream ([27350fb](https://github.com/reruin/sharelist/commit/27350fb6a036ab13e65dc0b52dab3f85732d5667))



4 changes: 2 additions & 2 deletions packages/sharelist-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sharelist/core",
"version": "0.1.4",
"version": "0.1.5",
"repository": "https://github.com/reruin/sharelist",
"license": "MIT",
"main": "index.js",
Expand All @@ -16,4 +16,4 @@
"write-file-atomic": "^3.0.3",
"yaml": "^1.10.2"
}
}
}
195 changes: 53 additions & 142 deletions packages/sharelist-plugin/lib/driver.googledrive.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

const protocol = 'googledrive'

const DEFAULT_ROOT_ID = 'root'

class Manager {
static getInstance(app) {
if (!this.instance) {
Expand Down Expand Up @@ -41,7 +43,10 @@ class Manager {
needUpdate = true
}
}

if (!data.path) {
data.path = data.root_id || DEFAULT_ROOT_ID
needUpdate = true
}
if (needUpdate) {
await this.app.saveDrive(data, { refresh_token })
}
Expand All @@ -58,7 +63,7 @@ class Manager {
* @api private
*/
async refreshAccessToken(credentials) {
let { client_id, client_secret, redirect_uri, refresh_token, path } = credentials
let { client_id, client_secret, redirect_uri, refresh_token, ...rest } = credentials

if (!(client_id && client_secret && refresh_token)) {
return { error: { message: 'Invalid parameters: An error occurred during refresh access token' } }
Expand All @@ -72,27 +77,25 @@ class Manager {
grant_type: 'refresh_token',
}

let { data, error } = await this.app.request.post(this.OAUTH2_TOKEN_URL, { data: formdata })

if (error) return { error }
let { data } = await this.app.request.post(this.OAUTH2_TOKEN_URL, {
data: formdata,
contentType: 'json'
})

if (data.error)
return {
error: { message: data.error_description || data.error },
}
if (data.error) {
this.app.error({ message: data.error_description || data.error })
}

let expires_at = data.expires_in * 1000 + Date.now()

return {
credentials: {
client_id,
client_secret,
path,
redirect_uri,
refresh_token, // refresh_token 永久有效
access_token: data.access_token,
expires_at,
},
...rest,
client_id,
client_secret,
redirect_uri,
refresh_token, // refresh_token 永久有效
access_token: data.access_token,
expires_at,
}
}

Expand All @@ -106,38 +109,17 @@ class Manager {
async getCredentials(key) {
let credentials = this.clientMap[key]
if (!credentials) {
return { error: { message: 'unmounted' } }
return this.app.error({ message: 'unmounted' })
}

// 未初始化(access_token不存在)、即将过期 刷新token
if (!credentials.access_token || (credentials.expires_at && credentials.expires_at - Date.now() < 5 * 60 * 1000)) {
let result = await this.refreshAccessToken(credentials)
if (result.error) {
return result
} else {
credentials = result.credentials

this.clientMap[key] = {
...credentials,
}
// refresh_token 永久有效 不需要更新
// await this.app.saveDrive({ key, refresh_token: credentials.refresh_token })
}
if (!(credentials.access_token && credentials.expires_at && credentials.expires_at - Date.now() > 5 * 60 * 1000)) {
credentials = await this.refreshAccessToken(credentials)
this.clientMap[key] = { ...credentials }
// refresh_token 永久有效 不需要更新
}

return { credentials }
}

async parse(id) {
let { key, path } = this.app.decode(id)
let { error, credentials } = await this.getCredentials(key)

let ret = { key, path, error }
if (!error) {
ret.access_token = credentials.access_token
}
return ret
}
}

/**
Expand Down Expand Up @@ -172,98 +154,26 @@ class Driver {
this.manager = Manager.getInstance(app)
}

/**
* Lists or search files
*
* @param {string} [id] folder id
* @param {object} [options] list options
* @param {object} [options.sort] sort methods
* @param {object} [options.search] search key
* @return {object | error}
*
* @api public
*/
async list(id, { sort, search }) {
let { manager, app, max_age_dir } = this

let { error, path, key, access_token } = await manager.parse(id)

if (error) return { error }

let cacheId = `${id}#list`

let r = app.cache.get(cacheId)

if (r && !search) {
console.log(`${new Date().toISOString()} CACHE ${this.name} ${id}`)
return r
}

let data = await this._list(access_token, { id: path, search })

if (data.error) return data

data.forEach((i) => {
i.id = this.app.encode({ key, path: i.id })
})

let result = {
id,
files: data,
}

if (!search) app.cache.set(cacheId, result, max_age_dir)

return result
}

/**
* get file details
*
* @param {string} [id] onedrive:https://{key}/{id}?query
* @return {object} { id, files } | error
* @api public
*
*/
async get(id) {
let { manager, app, max_age_dir } = this

let { error, path, access_token } = await manager.parse(id)

if (error) return { error }

let cacheId = `${id}#get`

let r = app.cache.get(cacheId)

if (r) {
console.log(`${new Date().toISOString()} CACHE ${this.name} ${id}`)
return r
}
let data = await this._get(access_token, { id: path })

if (data.error) return data

data.id = id

return app.cache.set(cacheId, data, max_age_dir)
async getCredentials(key) {
return await this.manager.getCredentials(key)
}

//docs: https://developers.google.com/drive/api/v3/reference/files/list
async _list(access_token, { id, search }) {
async list(id, options, key) {
const {
request,
utils: { timestamp },
} = this.app

let { access_token } = await this.getCredentials(key)

const url = 'https://www.googleapis.com/drive/v3/files'

const q = ['trashed = false', `'${id || 'root'}' in parents`]
const q = ['trashed = false', `'${id}' in parents`]

if (search) {
q.push(`name contains '${search}'`)
if (options.search) {
q.push(`name contains '${options.search}'`)
}

const params = {
includeItemsFromAllDrives: true,
supportsAllDrives: true,
Expand All @@ -283,9 +193,7 @@ class Driver {
data: { ...params, ...(pageToken ? { pageToken } : {}) },
})

if (error) return { error }

if (data.error) return { error: { message: data.error.message } }
if (data.error) return this.app.error({ message: data.error.message })

pageToken = data.nextPageToken

Expand Down Expand Up @@ -323,14 +231,25 @@ class Driver {
}

//docs: https://developers.google.com/drive/api/v3/reference/files/get
async _get(access_token, { id }) {
/**
* get file
*
* @param {string} [file_id] path id
* @param {string} [key] key
* @return {object}
*
* @api public
*/
async get(id, key) {
let { access_token } = await this.getCredentials(key)

const {
request,
utils: { timestamp },
} = this.app

let url = `https://www.googleapis.com/drive/v3/files/${id}`
let { data, error } = await request.get(url, {
let { data } = await request.get(url, {
headers: {
Authorization: `Bearer ${access_token}`,
},
Expand All @@ -340,9 +259,7 @@ class Driver {
},
})

if (error) return error

if (data.error) return { error: { message: data.error.message } }
if (data.error) return this.app.error({ message: data.error.message })

return {
id: data.id,
Expand All @@ -361,10 +278,6 @@ class Driver {
}
}

async mkdir() {}

async rm() {}

async isAbusiveFile(url, access_token) {
if (url in this.abusiveFilesMap) {
return this.abusiveFilesMap[url]
Expand All @@ -383,19 +296,18 @@ class Driver {
}
}

async createReadStream(id, options = {}) {
async createReadStream(id, options = {}, key) {
let {
manager,
app: { request },
} = this

let { error, path, access_token } = await manager.parse(id)
let { access_token } = await this.getCredentials(key)

if (error) return { error }

let url = `https://www.googleapis.com/drive/v3/files/${path}?alt=media`
let url = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`

if (await this.isAbusiveFile(url)) {
if (await this.isAbusiveFile(url, access_token)) {
url += '&acknowledgeAbuse=true'
}

Expand All @@ -416,7 +328,6 @@ class Driver {
return resp.data
}

async createWriteStream() {}
}

module.exports = Driver
8 changes: 6 additions & 2 deletions packages/sharelist-webdav/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import http from 'http'
import { Context, WebDAVDepth } from './types'
import { parseXML } from './operations/shared'
import { URL } from 'url'

export default (req: http.IncomingMessage, base: string): Context => {
export default (req: http.IncomingMessage, base: string, allows: Array<string>): Context => {
const value = req.headers?.authorization?.split(' ')[1]

const path = new URL(req.url as string, `http:https://${req.headers.host}`).pathname
const ctx: Context = {
req: req,
depth: req.headers?.depth as WebDAVDepth,
method: (req.method as string || '').toLowerCase(),
path: req.url?.replace(base, ''),
path: path.replace(base, ''),
base,
config: {},
allows,
get(field: string): string | undefined {
const req: http.IncomingMessage = this.req
switch (field = field.toLowerCase()) {
Expand Down
7 changes: 3 additions & 4 deletions packages/sharelist-webdav/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class WebDAVServer {

protected config: Record<string, any>

protected allow: string
protected allows: Array<string>

constructor({ driver, base, redirect, auth }: WebDAVServerOptions = { redirect: false, auth: () => true }) {
this.methods = {}
Expand All @@ -45,11 +45,11 @@ export class WebDAVServer {
this.unknownMethod = commands[k]
else
this.methods[k.toLowerCase()] = commands[k]
this.allow = Object.keys(this.methods).map(i => i.toUpperCase()).join(', ')
this.allows = Object.keys(this.methods).map(i => i.toUpperCase())//.join(', ')
}

async request(req: WebDAVRequest): Promise<Response> {
const ctx: Context = createContext(req, this.base)
const ctx: Context = createContext(req, this.base, this.allows)
if (this.auth) {
if (!ctx.auth || !this.auth(ctx.auth.user, ctx.auth.pass) === true) {
return {
Expand All @@ -63,7 +63,6 @@ export class WebDAVServer {

ctx.driver = this.driver
ctx.config = this.config
ctx.allow = this.allow
const method = this.methods[ctx.method] || this.unknownMethod

const res = await method(ctx)
Expand Down
Loading

0 comments on commit 135a058

Please sign in to comment.