-
-
Notifications
You must be signed in to change notification settings - Fork 230
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
184 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"name": "koishi-plugin-assets", | ||
"description": "GitHub webhook plugin for Koishi", | ||
"version": "3.0.0", | ||
"main": "dist/index.js", | ||
"typings": "dist/index.d.ts", | ||
"files": [ | ||
"dist" | ||
], | ||
"author": "Shigma <1700011071@pku.edu.cn>", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/koishijs/plugin-assets.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/koishijs/plugin-assets/issues" | ||
}, | ||
"homepage": "https://koishi.js.org/plugins/other/assets", | ||
"keywords": [ | ||
"bot", | ||
"qqbot", | ||
"cqhttp", | ||
"coolq", | ||
"chatbot", | ||
"koishi", | ||
"plugin", | ||
"assets", | ||
"server" | ||
], | ||
"peerDependencies": { | ||
"koishi-core": "^3.4.0" | ||
}, | ||
"dependencies": { | ||
"axios": "^0.21.1" | ||
} | ||
} |
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,137 @@ | ||
import { Assets, Context, Random, sanitize, trimSlash } from 'koishi-core' | ||
import axios, { AxiosRequestConfig } from 'axios' | ||
import { promises as fs, createReadStream, existsSync } from 'fs' | ||
import { extname, resolve } from 'path' | ||
import { createHmac } from 'crypto' | ||
|
||
interface ServerConfig { | ||
path?: string | ||
root?: string | ||
selfUrl?: string | ||
secret?: string | ||
axiosConfig?: AxiosRequestConfig | ||
} | ||
|
||
class AssetServer implements Assets { | ||
types: ['video', 'audio', 'image'] | ||
|
||
private _promise: Promise<void> | ||
private _stats: Assets.Stats = { | ||
assetCount: 0, | ||
assetSize: 0, | ||
} | ||
|
||
constructor(public ctx: Context, public config: ServerConfig) { | ||
const path = sanitize(config.path || '/assets') | ||
config.root ||= resolve(__dirname, '../public') | ||
|
||
if (config.selfUrl) { | ||
config.selfUrl = trimSlash(config.selfUrl) | ||
} else if (!(config.selfUrl = ctx.app.options.selfUrl)) { | ||
throw new Error(`missing configuration "selfUrl" or "server"`) | ||
} | ||
|
||
ctx.router.get(path, async (ctx) => { | ||
return ctx.body = await this.stats() | ||
}) | ||
|
||
ctx.router.get(path + '/:name', (ctx) => { | ||
const filename = resolve(config.root, ctx.params.name) | ||
ctx.type = extname(filename) | ||
return ctx.body = createReadStream(filename) | ||
}) | ||
|
||
ctx.router.post(path, async (ctx) => { | ||
const { salt, sign, url, file } = ctx.query | ||
if (Array.isArray(file) || Array.isArray(url)) { | ||
return ctx.status = 400 | ||
} | ||
|
||
if (config.secret) { | ||
if (!salt || !sign) return ctx.status = 400 | ||
const hash = createHmac('sha1', config.secret).update(file + salt).digest('hex') | ||
if (hash !== sign) return ctx.status = 403 | ||
} | ||
|
||
await this.upload(url, file) | ||
return ctx.status = 200 | ||
}) | ||
|
||
this._promise = this.init() | ||
} | ||
|
||
async init() { | ||
const root = this.config.root | ||
await fs.mkdir(root, { recursive: true }) | ||
const filenames = await fs.readdir(root) | ||
this._stats.assetCount = filenames.length | ||
await Promise.all(filenames.map(async (file) => { | ||
const { size } = await fs.stat(resolve(root, file)) | ||
this._stats.assetSize += size | ||
})) | ||
} | ||
|
||
async upload(url: string, file: string) { | ||
await this._promise | ||
const { selfUrl, path, root, axiosConfig } = this.config | ||
const filename = resolve(root, file) | ||
if (!existsSync(filename)) { | ||
const { data } = await axios.get<ArrayBuffer>(url, { | ||
...axiosConfig, | ||
responseType: 'arraybuffer', | ||
}) | ||
await fs.writeFile(filename, Buffer.from(data)) | ||
this._stats.assetCount += 1 | ||
this._stats.assetSize += data.byteLength | ||
} | ||
return `${selfUrl}${path}/${file}` | ||
} | ||
|
||
async stats() { | ||
await this._promise | ||
return this._stats | ||
} | ||
} | ||
|
||
interface ClientConfig { | ||
server: string | ||
secret?: string | ||
axiosConfig?: AxiosRequestConfig | ||
} | ||
|
||
class AssetClient implements Assets { | ||
types: ['video', 'audio', 'image'] | ||
|
||
constructor(public ctx: Context, public config: ClientConfig) {} | ||
|
||
async upload(url: string, file: string) { | ||
const { server, secret, axiosConfig } = this.config | ||
const params = { url, file } as any | ||
if (secret) { | ||
params.salt = Random.uuid() | ||
params.sign = createHmac('sha1', secret).update(file + params.salt).digest('hex') | ||
} | ||
const { data } = await axios.post(server, { ...axiosConfig, params }) | ||
return data | ||
} | ||
|
||
async stats() { | ||
const { data } = await axios.get(this.config.server, this.config.axiosConfig) | ||
return data | ||
} | ||
} | ||
|
||
export type Config = ServerConfig | ClientConfig | ||
|
||
export const name = 'assets' | ||
|
||
export function apply(ctx: Context, config: Config = {}) { | ||
config.axiosConfig = { | ||
...ctx.app.options.axiosConfig, | ||
...config.axiosConfig, | ||
} | ||
|
||
ctx.assets = 'server' in config | ||
? new AssetClient(ctx, config) | ||
: new AssetServer(ctx, config) | ||
} |
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,10 @@ | ||
{ | ||
"extends": "../../tsconfig.base", | ||
"compilerOptions": { | ||
"outDir": "dist", | ||
"rootDir": "src", | ||
}, | ||
"include": [ | ||
"src", | ||
], | ||
} |