Files
dzc 3ea7c4e872 fix:图片访问
fix:权限修改
2026-10-29 14:25:54 +08:00

80 lines
5.0 KiB
JavaScript

const { test } = require('node:test')
const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const Module = require('node:module')
const ts = require('typescript')
const root = path.resolve(__dirname, '..')
function loadRuntime() {
const filename = path.join(root, 'src/sheep/utils/file-access.ts')
const corePath = path.join(path.dirname(filename), 'file-access-core.ts')
const compile = source => ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }).outputText
const core = new Module(corePath, module); core.filename = corePath; core.paths = Module._nodeModulePaths(path.dirname(corePath))
core._compile(compile(fs.readFileSync(corePath, 'utf8')), corePath)
const appId = root.endsWith('pj_mini_app_cloud') ? 'mini' : 'h5'
const env = { VITE_FILE_ACCESS_APP_ID: appId, VITE_FILE_ACCESS_KEY_ID: 'v1',
VITE_FILE_ACCESS_KEY: JSON.parse(fs.readFileSync(path.join(root, 'test-vectors/file-access.json'), 'utf8'))[0].key,
VITE_IMAGE_DEV_PATH: 'https://files.example/api', VITE_APP_DEV_SERVICE: 'https://files.example/api' }
const runtime = new Module(filename, module); runtime.filename = filename; runtime.paths = core.paths
runtime.require = name => name === './file-access-core' ? core.exports : name === './app' ? { isDevEnv: () => true } : core.require(name)
runtime._compile(compile(fs.readFileSync(filename, 'utf8').replace(/import\.meta\.env/g, '(' + JSON.stringify(env) + ')')), filename)
return { api: runtime.exports, appId }
}
test('rich-text file links use signed downloads and local files instead of browser navigation', () => {
const previousUni = global.uni, previousDocument = global.document
const downloads = [], documents = [], previews = [], browserDownloads = []
global.uni = {
downloadFile: options => { downloads.push(options); options.success({ statusCode: 200, tempFilePath: 'blob:local-file' }) },
openDocument: options => documents.push(options), previewImage: options => previews.push(options), onNetworkStatusChange() {},
}
global.document = undefined
try {
const { api, appId } = loadRuntime()
api.openFileLink('/admin/sys-file/oss/file?fileName=report.pdf')
assert.equal(new URL(downloads[0].url).searchParams.get('appId'), appId)
assert.ok(new URL(downloads[0].url).searchParams.has('sign'))
assert.equal(documents[0].filePath, 'blob:local-file')
let appended = 0, removed = 0
let onClick
global.document = {
addEventListener(name, handler, capture) { assert.equal(name, 'click'); assert.equal(capture, true); onClick = handler },
body: { appendChild() { appended++ } },
createElement: () => ({ click() { browserDownloads.push({ href: this.href, name: this.download }) }, remove() { removed++ } }),
}
api.openFileLink('/admin/sys-file/oss/file?fileName=' + encodeURIComponent('附件 +.pdf'))
assert.deepEqual(browserDownloads, [{ href: 'blob:local-file', name: '附件 +.pdf' }])
assert.equal(appended, 1); assert.equal(removed, 1)
api.openFileLink('/admin/sys-file/oss/file?fileName=photo.png')
assert.equal(previews.length, 1)
assert.ok(new URL(previews[0].urls[0]).searchParams.has('sign'))
assert.equal(downloads.length, 2)
api.installFileAccess({ config: { globalProperties: {} }, mixin() {} })
let prevented = false
onClick({
target: { closest: () => ({ getAttribute: () => '/admin/sys-file/oss/file?fileName=linked.pdf' }) },
preventDefault() { prevented = true }, stopImmediatePropagation() {},
})
assert.equal(prevented, true)
assert.equal(browserDownloads.at(-1).name, 'linked.pdf')
let outsidePrevented = false
onClick({
target: { closest: () => ({ getAttribute: () => 'https://other.example/report.pdf' }) },
preventDefault() { outsidePrevented = true }, stopImmediatePropagation() {},
})
assert.equal(outsidePrevented, false)
} finally { global.uni = previousUni; global.document = previousDocument }
})
test('the actual rich-text component routes owned links through the file helper', () => {
const filename = path.join(root, 'src/uni_modules/tm-ui/components/tm-html/mp-node.vue')
const script = fs.readFileSync(filename, 'utf8').match(/<script>\s*([\s\S]*?)<\/script>/)[1]
const loaded = new Module(filename, module); loaded.filename = filename; loaded.paths = Module._nodeModulePaths(path.dirname(filename))
const opened = []
loaded.require = name => name === './mp-node.vue' ? {} : name === '@/sheep/utils/file-access'
? { accessFileUrl: value => value, isProtectedFile: value => value.includes('/sys-file/oss/file'), openFileLink: value => opened.push(value) }
: require(name)
loaded._compile(ts.transpileModule(script, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }).outputText, filename)
const href = 'https://files.example/api/admin/sys-file/oss/file?fileName=report.pdf'
loaded.exports.default.methods.linkTap.call({ root: { $emit() {}, getText: () => '' } }, { href })
assert.deepEqual(opened, [href])
})