feat: 下载page到本地
This commit is contained in:
@@ -9,10 +9,13 @@ app
|
||||
.route({
|
||||
path: 'chat-prompt',
|
||||
key: 'list',
|
||||
// middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const chatPrompt = await ChatPrompt.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
// 列出被删除的
|
||||
// paranoid: false,
|
||||
});
|
||||
ctx.body = chatPrompt;
|
||||
})
|
||||
@@ -78,6 +81,7 @@ app
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const id = ctx.query.id;
|
||||
@@ -93,6 +97,7 @@ app
|
||||
.route({
|
||||
path: 'chat-prompt',
|
||||
key: 'getByKey',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { key } = ctx.query.data || {};
|
||||
|
||||
@@ -9,6 +9,9 @@ import { getHTML, getDataJs } from './file-template.ts';
|
||||
import { minioClient } from '@/app.ts';
|
||||
import { bucketName } from '@/modules/minio.ts';
|
||||
import { getContentType } from '@/utils/get-content-type.ts';
|
||||
import archiver from 'archiver';
|
||||
import { CustomError } from '@abearxiong/router';
|
||||
|
||||
export const cacheFile = useFileStore('cache-file', {
|
||||
needExists: true,
|
||||
});
|
||||
@@ -72,3 +75,80 @@ export const uploadMinio = async ({ tokenUser, key, version, path, filePath }) =
|
||||
fs.unlinkSync(filePath); // 删除临时文件
|
||||
return minioPath;
|
||||
};
|
||||
export const uploadMinioTemp = async ({ tokenUser, filePath, path }) => {
|
||||
const minioPath = `${tokenUser.username}/temp/${path}`;
|
||||
const isHTML = filePath.endsWith('.html');
|
||||
await minioClient.fPutObject(bucketName, minioPath, filePath, {
|
||||
'Content-Type': getContentType(filePath),
|
||||
'app-source': 'user-app',
|
||||
'Cache-Control': isHTML ? 'no-cache' : 'max-age=31536000, immutable', // 缓存一年
|
||||
});
|
||||
fs.unlinkSync(filePath); // 删除临时文件
|
||||
return minioPath;
|
||||
};
|
||||
export const getZip = async (page: PageModel, opts: { tokenUser: any }) => {
|
||||
const _result = await getDeck(page);
|
||||
const result = getContainerData(_result);
|
||||
const html = getHTML({ rootId: page.id, title: page?.publish?.key });
|
||||
const dataJs = getDataJs(result);
|
||||
const zip = archiver('zip', {
|
||||
zlib: { level: 9 },
|
||||
});
|
||||
// 创建 zip 文件的输出流
|
||||
const zipCache = path.join(cacheFile, `${page.id}.zip`);
|
||||
if (checkFileExistsSync(zipCache)) {
|
||||
throw new CustomError('page is on uploading');
|
||||
}
|
||||
return await new Promise((resolve, reject) => {
|
||||
const output = fs.createWriteStream(zipCache);
|
||||
// 监听事件
|
||||
output.on('close', async () => {
|
||||
console.log(`Zip file has been created successfully. Total size: ${zip.pointer()} bytes.`);
|
||||
let time = (new Date().getTime() / 1000).toFixed(0);
|
||||
const name = page.title || page.id;
|
||||
const minioPath = await uploadMinioTemp({ ...opts, filePath: zipCache, path: `${name + '-' + time}.zip` });
|
||||
resolve(minioPath);
|
||||
});
|
||||
|
||||
output.on('end', () => {
|
||||
console.log('Data has been drained.'); // 数据已被耗尽
|
||||
throw new CustomError('Data has been drained.');
|
||||
});
|
||||
|
||||
zip.on('warning', (err) => {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.warn('File not found:', err);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
zip.on('error', (err) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// 通过管道将 zip 数据流输出到指定文件
|
||||
zip.pipe(output);
|
||||
|
||||
// 添加 HTML 字符串作为文件到 zip 中
|
||||
zip.append(html, { name: 'index.html' });
|
||||
|
||||
// 添加 JavaScript 字符串作为文件到 zip 中
|
||||
zip.append(dataJs, { name: 'data.js' });
|
||||
|
||||
// 可以继续添加更多内容,文件或目录等
|
||||
// zip.append('Another content', { name: 'other.txt' });
|
||||
|
||||
// 结束归档(必须调用,否则 zip 文件无法完成)
|
||||
zip.finalize();
|
||||
});
|
||||
};
|
||||
export const checkFileExistsSync = (filePath: string) => {
|
||||
try {
|
||||
// 使用 F_OK 检查文件或目录是否存在
|
||||
fs.accessSync(filePath, fs.constants.F_OK);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { ContainerModel } from '../container/models/index.ts';
|
||||
import { Op } from 'sequelize';
|
||||
import { AppListModel, AppModel } from '../app-manager/index.ts';
|
||||
import { cachePage } from './module/cache-file.ts';
|
||||
import { cachePage, getZip } from './module/cache-file.ts';
|
||||
import _ from 'lodash';
|
||||
import semver from 'semver';
|
||||
|
||||
@@ -67,3 +67,26 @@ app
|
||||
}
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'page',
|
||||
key: 'download',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const { id } = ctx.query;
|
||||
const page = await PageModel.findByPk(id);
|
||||
if (!page) {
|
||||
throw new CustomError('page not found');
|
||||
}
|
||||
try {
|
||||
const files = await getZip(page, { tokenUser });
|
||||
ctx.body = files;
|
||||
} catch (e) {
|
||||
console.log('error', e);
|
||||
throw new CustomError(e.message || 'download error');
|
||||
}
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
Reference in New Issue
Block a user