mira-app-server
Version:
Mira Server - standalone server application using mira-app-core
243 lines • 9.62 kB
JavaScript
;
/**
* 文件模块命令
*
* 注意:SDK 的 upload 使用 FormData/Blob。Node 18+ 已内置全局 FormData、File、Blob,
* 因此可直接复用 SDK 的 uploadFiles,仅需用 Blob 包装读取出的 Buffer。
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerFiles = registerFiles;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const client_1 = require("../client");
const format_1 = require("../format");
/**
* 读取本地文件并构造为全局 File 对象(Node 18+ 内置)
*/
function readFileAsFile(filePath) {
const abs = path_1.default.resolve(filePath);
if (!fs_1.default.existsSync(abs)) {
throw new Error(`文件不存在: ${abs}`);
}
const buffer = fs_1.default.readFileSync(abs);
const blob = new Blob([new Uint8Array(buffer)]);
const name = path_1.default.basename(abs);
// File 构造签名: new File(bits, name)
return new File([blob], name);
}
/**
* 兼容 Node 环境的文件上传。
*
* 注意:SDK 的 FileModule.uploadFiles 在判断 `files instanceof FileList` 时,
* 会因 Node 中未定义 FileList 而抛出 ReferenceError。因此这里直接构造
* FormData 并通过 HttpClient.upload 发起上传,绕过该问题。
*/
async function uploadFilesToLibrary(client, libraryId, paths, options) {
const formData = new FormData();
paths.forEach(p => {
const file = readFileAsFile(p);
formData.append('files', file, file.name);
});
formData.append('libraryId', libraryId);
if (options.tags && options.tags.length) {
formData.append('payload', JSON.stringify({ data: { tags: options.tags } }));
}
if (options.folderId) {
formData.append('payload', JSON.stringify({ data: { folder_id: options.folderId } }));
}
return await client.getHttpClient().upload('/api/files/upload', formData);
}
function registerFiles(program) {
const files = program.command('files').description('文件管理');
files
.command('list <libraryId>')
.description('获取文件列表')
.option('--title <title>', '按标题模糊搜索')
.option('--ext <extension>', '按扩展名筛选')
.option('--tag <tag>', '按标签筛选(可多次)', collect, [])
.option('--folder-id <id>', '按文件夹 ID 筛选')
.option('--limit <n>', '数量限制', parseInt)
.option('--offset <n>', '偏移量', parseInt)
.action(async (libraryId, options) => {
try {
const { client } = (0, client_1.getClient)();
const filters = {};
if (options.title)
filters.title = options.title;
if (options.ext)
filters.extension = options.ext;
if (options.tag && options.tag.length)
filters.tags = options.tag;
if (options.folderId)
filters.folder = parseInt(options.folderId);
if (options.limit)
filters.limit = options.limit;
if (options.offset)
filters.offset = options.offset;
// 服务器返回 { result, limit, offset, total }
const resp = await client.files().getFiles({ libraryId, filters });
const list = Array.isArray(resp) ? resp : resp?.result || [];
const rows = list.map(f => ({
id: f.id,
title: f.title || f.name,
extension: f.extension,
size: f.size,
tags: f.tags || '',
folder_id: f.folder_id,
}));
(0, format_1.output)(rows, () => (0, format_1.formatTable)(rows));
}
catch (error) {
(0, format_1.fatal)(error);
}
});
files
.command('get <libraryId> <fileId>')
.description('获取单个文件信息')
.action(async (libraryId, fileId) => {
try {
const { client } = (0, client_1.getClient)();
const info = await client.files().getFile(libraryId, fileId);
(0, format_1.output)(info, () => (0, format_1.formatKeyValue)(info));
}
catch (error) {
(0, format_1.fatal)(error);
}
});
files
.command('upload <libraryId> <paths...>')
.description('上传文件到指定素材库')
.option('--tag <tag>', '添加标签(可多次)', collect, [])
.option('--folder-id <id>', '目标文件夹 ID')
.action(async (libraryId, paths, options) => {
try {
const { client } = (0, client_1.getClient)();
const opts = {};
if (options.tag && options.tag.length)
opts.tags = options.tag;
if (options.folderId)
opts.folderId = options.folderId;
const res = await uploadFilesToLibrary(client, libraryId, paths, opts);
(0, format_1.success)(`已上传 ${paths.length} 个文件到 ${libraryId}`);
(0, format_1.output)(res);
}
catch (error) {
(0, format_1.fatal)(error);
}
});
files
.command('download <libraryId> <fileId>')
.description('下载文件到本地')
.option('-o, --output <path>', '保存路径(默认当前目录,使用文件原名)')
.action(async (libraryId, fileId, options) => {
try {
const { client } = (0, client_1.getClient)();
const blob = await client.files().download(libraryId, fileId);
const buffer = Buffer.from(await blob.arrayBuffer());
let outPath = options.output;
if (!outPath) {
try {
const info = await client.files().getFile(libraryId, fileId);
outPath = path_1.default.resolve(process.cwd(), info.title || fileId);
}
catch {
outPath = path_1.default.resolve(process.cwd(), String(fileId));
}
}
fs_1.default.writeFileSync(outPath, buffer);
(0, format_1.success)(`已下载到: ${outPath} (${buffer.length} bytes)`);
}
catch (error) {
(0, format_1.fatal)(error);
}
});
files
.command('rename <libraryId> <fileId> <name>')
.description('重命名文件')
.action(async (libraryId, fileId, name) => {
try {
const { client } = (0, client_1.getClient)();
const res = await client.files().renameFile(libraryId, fileId, name);
(0, format_1.success)(`文件已重命名为: ${name}`);
(0, format_1.output)(res);
}
catch (error) {
(0, format_1.fatal)(error);
}
});
files
.command('update <libraryId> <fileId> <json>')
.description('更新文件元数据(json 为 JSON 字符串)')
.action(async (libraryId, fileId, json) => {
try {
let data;
try {
data = JSON.parse(json);
}
catch {
throw new Error('json 参数不是合法的 JSON');
}
const { client } = (0, client_1.getClient)();
const res = await client.files().updateFile(libraryId, fileId, data);
(0, format_1.success)(`文件 ${fileId} 已更新`);
(0, format_1.output)(res);
}
catch (error) {
(0, format_1.fatal)(error);
}
});
files
.command('delete <libraryId> <fileId>')
.alias('rm')
.description('删除文件(默认进回收站)')
.option('--permanent', '彻底删除(不进回收站)')
.action(async (libraryId, fileId, options) => {
try {
const { client } = (0, client_1.getClient)();
const res = await client.files().delete(libraryId, fileId, {
moveToRecycleBin: !options.permanent,
});
(0, format_1.success)(options.permanent ? `文件 ${fileId} 已彻底删除` : `文件 ${fileId} 已移入回收站`);
(0, format_1.output)(res);
}
catch (error) {
(0, format_1.fatal)(error);
}
});
files
.command('restore <libraryId> <fileId>')
.description('从回收站恢复文件')
.action(async (libraryId, fileId) => {
try {
const { client } = (0, client_1.getClient)();
const res = await client.files().restoreFile(libraryId, fileId);
(0, format_1.success)(`文件 ${fileId} 已恢复`);
(0, format_1.output)(res);
}
catch (error) {
(0, format_1.fatal)(error);
}
});
files
.command('empty-trash <libraryId>')
.description('清空素材库回收站')
.action(async (libraryId) => {
try {
const { client } = (0, client_1.getClient)();
const res = await client.files().emptyTrash(libraryId);
(0, format_1.success)(`素材库 ${libraryId} 回收站已清空`);
(0, format_1.output)(res);
}
catch (error) {
(0, format_1.fatal)(error);
}
});
}
/** commander collect 辅助:收集多次出现的选项 */
function collect(value, previous) {
return previous.concat([value]);
}
//# sourceMappingURL=files.js.map