add sync download
This commit is contained in:
parent
4aec2bc231
commit
eaccbf5ada
5
kevisual.json
Normal file
5
kevisual.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"sync": {
|
||||||
|
"build/01-summary.md": "https://kevisual.xiongxiao.me/root/ai/kevisual/01-summary.md"
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,7 @@
|
|||||||
"bun.config.mjs"
|
"bun.config.mjs"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun src/run.ts ",
|
"dev": "NODE_ENV=development bun src/run.ts ",
|
||||||
"dev:tsx": "tsx src/run.ts ",
|
"dev:tsx": "tsx src/run.ts ",
|
||||||
"build": "rimraf dist && bun run bun.config.mjs",
|
"build": "rimraf dist && bun run bun.config.mjs",
|
||||||
"postbuild": "cd assistant && pnpm build",
|
"postbuild": "cd assistant && pnpm build",
|
||||||
@ -42,6 +42,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kevisual/load": "^0.0.6",
|
"@kevisual/load": "^0.0.6",
|
||||||
|
"@kevisual/logger": "^0.0.2",
|
||||||
"@kevisual/query": "0.0.17",
|
"@kevisual/query": "0.0.17",
|
||||||
"@kevisual/query-login": "0.0.5",
|
"@kevisual/query-login": "0.0.5",
|
||||||
"@types/bun": "^1.2.13",
|
"@types/bun": "^1.2.13",
|
||||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -15,6 +15,9 @@ importers:
|
|||||||
'@kevisual/load':
|
'@kevisual/load':
|
||||||
specifier: ^0.0.6
|
specifier: ^0.0.6
|
||||||
version: 0.0.6
|
version: 0.0.6
|
||||||
|
'@kevisual/logger':
|
||||||
|
specifier: ^0.0.2
|
||||||
|
version: 0.0.2
|
||||||
'@kevisual/query':
|
'@kevisual/query':
|
||||||
specifier: 0.0.17
|
specifier: 0.0.17
|
||||||
version: 0.0.17(encoding@0.1.13)(ws@8.18.0)
|
version: 0.0.17(encoding@0.1.13)(ws@8.18.0)
|
||||||
@ -609,6 +612,9 @@ packages:
|
|||||||
'@kevisual/use-config': ^1.0.11
|
'@kevisual/use-config': ^1.0.11
|
||||||
pm2: ^5.4.3
|
pm2: ^5.4.3
|
||||||
|
|
||||||
|
'@kevisual/logger@0.0.2':
|
||||||
|
resolution: {integrity: sha512-4NVdNsOHmMRg+OuZPoNNdI3p7jRII7lMJHRar1IoBck7fFIV7YGMNQirrrjk07MHv+Eh+U+uUljjgEWbse92RA==}
|
||||||
|
|
||||||
'@kevisual/query-login@0.0.5':
|
'@kevisual/query-login@0.0.5':
|
||||||
resolution: {integrity: sha512-389cMMWAisjQoafxX+cUEa2z41S5koDjiyHkucfCkhRoP4M6g0iqbBMavLKmLOWSKx3R8e3ZmXT6RfsYGBb8Ww==}
|
resolution: {integrity: sha512-389cMMWAisjQoafxX+cUEa2z41S5koDjiyHkucfCkhRoP4M6g0iqbBMavLKmLOWSKx3R8e3ZmXT6RfsYGBb8Ww==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2513,6 +2519,8 @@ snapshots:
|
|||||||
'@kevisual/use-config': 1.0.11(dotenv@16.5.0)
|
'@kevisual/use-config': 1.0.11(dotenv@16.5.0)
|
||||||
pm2: 6.0.5(supports-color@10.0.0)
|
pm2: 6.0.5(supports-color@10.0.0)
|
||||||
|
|
||||||
|
'@kevisual/logger@0.0.2': {}
|
||||||
|
|
||||||
'@kevisual/query-login@0.0.5(@kevisual/query@0.0.17(@kevisual/ws@8.0.0)(encoding@0.1.13))(rollup@4.40.2)(typescript@5.8.2)':
|
'@kevisual/query-login@0.0.5(@kevisual/query@0.0.17(@kevisual/ws@8.0.0)(encoding@0.1.13))(rollup@4.40.2)(typescript@5.8.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@kevisual/cache': 0.0.2(rollup@4.40.2)(tslib@2.8.1)(typescript@5.8.2)
|
'@kevisual/cache': 0.0.2(rollup@4.40.2)(tslib@2.8.1)(typescript@5.8.2)
|
||||||
|
@ -9,9 +9,8 @@ import inquirer from 'inquirer';
|
|||||||
import { packLib, unpackLib } from './publish.ts';
|
import { packLib, unpackLib } from './publish.ts';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { installDeps } from '@/uitls/npm.ts';
|
import { installDeps } from '@/uitls/npm.ts';
|
||||||
import cryptojs from 'crypto-js';
|
|
||||||
import { upload } from '@/module/download/upload.ts';
|
import { upload } from '@/module/download/upload.ts';
|
||||||
const MD5 = cryptojs.MD5;
|
import { getHash } from '@/uitls/hash.ts';
|
||||||
/**
|
/**
|
||||||
* 获取package.json 中的 basename, version, user, appKey
|
* 获取package.json 中的 basename, version, user, appKey
|
||||||
* @returns
|
* @returns
|
||||||
@ -150,10 +149,7 @@ const command = new Command('deploy')
|
|||||||
console.error('error', error);
|
console.error('error', error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
export const getHash = (file: string) => {
|
|
||||||
const content = fs.readFileSync(file, 'utf-8');
|
|
||||||
return MD5(content).toString();
|
|
||||||
};
|
|
||||||
type UploadFileOptions = {
|
type UploadFileOptions = {
|
||||||
key: string;
|
key: string;
|
||||||
version: string;
|
version: string;
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import { program as app, Command } from '@/program.ts';
|
|
||||||
|
|
||||||
const command = new Command('sync')
|
|
||||||
.option('-d --dir <dir>')
|
|
||||||
.description('同步项目')
|
|
||||||
.action(() => {
|
|
||||||
console.log('同步项目');
|
|
||||||
});
|
|
||||||
const syncUpload = new Command('upload').description('上传项目').action(() => {
|
|
||||||
console.log('上传项目');
|
|
||||||
});
|
|
||||||
const syncDownload = new Command('download').description('下载项目').action(() => {
|
|
||||||
console.log('下载项目');
|
|
||||||
});
|
|
||||||
|
|
||||||
command.addCommand(syncUpload);
|
|
||||||
command.addCommand(syncDownload);
|
|
||||||
app.addCommand(command);
|
|
87
src/command/sync/modules/base.ts
Normal file
87
src/command/sync/modules/base.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { Config, SyncList } from './type.ts';
|
||||||
|
import { fileIsExist } from '@/uitls/file.ts';
|
||||||
|
|
||||||
|
export type SyncOptions = {
|
||||||
|
dir?: string;
|
||||||
|
configFilename?: string;
|
||||||
|
baseURL?: string;
|
||||||
|
};
|
||||||
|
export class SyncBase {
|
||||||
|
config: Config;
|
||||||
|
#filename: string;
|
||||||
|
#dir: string;
|
||||||
|
baseURL: string;
|
||||||
|
constructor(opts?: SyncOptions) {
|
||||||
|
const filename = opts?.configFilename || 'kevisual.json';
|
||||||
|
const dir = opts?.dir || process.cwd();
|
||||||
|
this.#filename = filename;
|
||||||
|
this.#dir = path.resolve(dir);
|
||||||
|
this.baseURL = opts?.baseURL ?? '';
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
async init() {
|
||||||
|
try {
|
||||||
|
const dir = this.#dir;
|
||||||
|
const filename = this.#filename;
|
||||||
|
const filepath = path.join(dir, filename);
|
||||||
|
if (!fileIsExist(filepath)) throw new Error('config file not found');
|
||||||
|
const config = JSON.parse(fs.readFileSync(filepath, 'utf-8'));
|
||||||
|
this.config = config;
|
||||||
|
return config;
|
||||||
|
} catch (err) {
|
||||||
|
this.config = {} as Config;
|
||||||
|
return {} as Config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSyncList(): Promise<SyncList[]> {
|
||||||
|
const config = this.config!;
|
||||||
|
const sync = config?.sync || {};
|
||||||
|
const syncKeys = Object.keys(sync);
|
||||||
|
const baseURL = this.baseURL;
|
||||||
|
const syncList = syncKeys.map((key) => {
|
||||||
|
const value = sync[key];
|
||||||
|
const filepath = path.join(this.#dir, key); // 文件的路径
|
||||||
|
|
||||||
|
const checkAuth = (value: string = '', baseURL: string = '') => {
|
||||||
|
if (value.startsWith(baseURL)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return {
|
||||||
|
filepath,
|
||||||
|
url: value,
|
||||||
|
auth: checkAuth(value, baseURL),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
filepath,
|
||||||
|
...value,
|
||||||
|
auth: checkAuth(value.url, baseURL),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return syncList;
|
||||||
|
}
|
||||||
|
async getDir(filepath: string, check = false) {
|
||||||
|
const dir = path.dirname(filepath);
|
||||||
|
if (check) {
|
||||||
|
if (!fileIsExist(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
async download() {
|
||||||
|
// const syncList = await this.getSyncList();
|
||||||
|
// for (const item of syncList) {
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
async upload() {
|
||||||
|
// need check permission
|
||||||
|
}
|
||||||
|
}
|
21
src/command/sync/modules/type.ts
Normal file
21
src/command/sync/modules/type.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export type SyncConfig = {
|
||||||
|
type?: 'sync'; // 是否可以同步
|
||||||
|
url: string; // 文件具体的 url 的地址
|
||||||
|
};
|
||||||
|
export interface Config {
|
||||||
|
name?: string; // 项目名称
|
||||||
|
version?: string; // 项目版本号
|
||||||
|
ignore?: string[]; // 忽略的目录或则文件,默认忽略 node_modules 使用 fast-glob 去匹配
|
||||||
|
|
||||||
|
sync: {
|
||||||
|
[key: string]: SyncConfig | string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SyncList = {
|
||||||
|
filepath: string;
|
||||||
|
/**
|
||||||
|
* 是否需要鉴权, baseURL 为 kevisual 服务时,需要鉴权
|
||||||
|
*/
|
||||||
|
auth?: boolean;
|
||||||
|
} & SyncConfig;
|
33
src/command/sync/sync.ts
Normal file
33
src/command/sync/sync.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { program as app, Command } from '@/program.ts';
|
||||||
|
import { SyncBase } from './modules/base.ts';
|
||||||
|
import { baseURL } from '@/module/query.ts';
|
||||||
|
import { fetchLink } from '@/module/download/install.ts';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
|
const command = new Command('sync')
|
||||||
|
.option('-d --dir <dir>')
|
||||||
|
.description('同步项目')
|
||||||
|
.action(() => {
|
||||||
|
console.log('同步项目');
|
||||||
|
});
|
||||||
|
const syncUpload = new Command('upload').description('上传项目').action(() => {
|
||||||
|
console.log('上传项目');
|
||||||
|
});
|
||||||
|
const syncDownload = new Command('download')
|
||||||
|
.option('-d --dir <dir>', '配置目录')
|
||||||
|
.description('下载项目')
|
||||||
|
.action(async () => {
|
||||||
|
console.log('下载项目');
|
||||||
|
const sync = new SyncBase({ baseURL: baseURL });
|
||||||
|
const syncList = await sync.getSyncList();
|
||||||
|
console.log(syncList);
|
||||||
|
for (const item of syncList) {
|
||||||
|
const { content } = await fetchLink(item.url, { setToken: item.auth, returnContent: true });
|
||||||
|
await sync.getDir(item.filepath, true);
|
||||||
|
fs.writeFileSync(item.filepath, content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
command.addCommand(syncUpload);
|
||||||
|
command.addCommand(syncDownload);
|
||||||
|
app.addCommand(command);
|
@ -8,7 +8,7 @@ import './command/npm.ts';
|
|||||||
import './command/publish.ts';
|
import './command/publish.ts';
|
||||||
import './command/init.ts';
|
import './command/init.ts';
|
||||||
import './command/proxy.ts';
|
import './command/proxy.ts';
|
||||||
import './command/sync.ts';
|
import './command/sync/sync.ts';
|
||||||
|
|
||||||
import './command/app/index.ts';
|
import './command/app/index.ts';
|
||||||
|
|
||||||
|
@ -23,21 +23,25 @@ export type Package = {
|
|||||||
type Options = {
|
type Options = {
|
||||||
check?: boolean;
|
check?: boolean;
|
||||||
returnContent?: boolean;
|
returnContent?: boolean;
|
||||||
|
setToken?: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
export const fetchLink = async (url: string, opts?: Options) => {
|
export const fetchLink = async (url: string, opts?: Options) => {
|
||||||
const token = process.env.KEVISUAL_TOKEN || storage.getItem('token');
|
const token = process.env.KEVISUAL_TOKEN || storage.getItem('token');
|
||||||
const fetchURL = new URL(url);
|
const fetchURL = new URL(url);
|
||||||
const check = opts?.check ?? false;
|
const check = opts?.check ?? false;
|
||||||
|
const setToken = opts?.setToken ?? true;
|
||||||
if (check) {
|
if (check) {
|
||||||
if (!url.startsWith(baseURL)) {
|
if (!url.startsWith(baseURL)) {
|
||||||
throw new Error('url must start with ' + baseURL);
|
throw new Error('url must start with ' + baseURL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (token) {
|
if (token && setToken) {
|
||||||
fetchURL.searchParams.set('token', token);
|
fetchURL.searchParams.set('token', token);
|
||||||
}
|
}
|
||||||
fetchURL.searchParams.set('download', 'true');
|
fetchURL.searchParams.set('download', 'true');
|
||||||
|
console.log('fetchURL', fetchURL.toString());
|
||||||
|
|
||||||
const res = await fetch(fetchURL.toString());
|
const res = await fetch(fetchURL.toString());
|
||||||
const blob = await res.blob();
|
const blob = await res.blob();
|
||||||
const type = blob.type;
|
const type = blob.type;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { getBufferHash, getHash } from '@/uitls/hash.ts';
|
||||||
import FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
export const handleResponse = async (err: any, res: any) => {
|
export const handleResponse = async (err: any, res: any) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@ -37,6 +38,7 @@ export const getFormParams = (opts: UploadOptions, headers: any): FormData.Submi
|
|||||||
...headers,
|
...headers,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
console.log('getFormParams', value);
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
type UploadOptions = {
|
type UploadOptions = {
|
||||||
@ -44,14 +46,25 @@ type UploadOptions = {
|
|||||||
file?: string | Buffer | File;
|
file?: string | Buffer | File;
|
||||||
token?: string;
|
token?: string;
|
||||||
form?: FormData;
|
form?: FormData;
|
||||||
|
needHash?: boolean;
|
||||||
};
|
};
|
||||||
export const upload = (opts: UploadOptions): Promise<{ code?: number; message?: string; [key: string]: any }> => {
|
export const upload = (opts: UploadOptions): Promise<{ code?: number; message?: string; [key: string]: any }> => {
|
||||||
const form = opts?.form || new FormData();
|
const form = opts?.form || new FormData();
|
||||||
if (!opts.form) {
|
if (!opts.form) {
|
||||||
|
let hash = '';
|
||||||
|
let value: any;
|
||||||
|
let type = 'string';
|
||||||
if (typeof opts.file === 'string') {
|
if (typeof opts.file === 'string') {
|
||||||
form.append('file', Buffer.from(opts.file));
|
value = Buffer.from(opts.file);
|
||||||
} else {
|
} else {
|
||||||
form.append('file', opts.file);
|
type = 'buffer';
|
||||||
|
value = opts.file;
|
||||||
|
}
|
||||||
|
form.append('file', value);
|
||||||
|
if (opts.needHash) {
|
||||||
|
hash = getBufferHash(value);
|
||||||
|
opts.url = new URL(opts.url.toString());
|
||||||
|
opts.url.searchParams.append('hash', hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const headers = form.getHeaders();
|
const headers = form.getHeaders();
|
||||||
|
5
src/module/logger.ts
Normal file
5
src/module/logger.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Logger } from '@kevisual/logger/node';
|
||||||
|
|
||||||
|
export const logger = new Logger({
|
||||||
|
level: 'info',
|
||||||
|
});
|
@ -3,9 +3,11 @@ import { baseURL, storage } from '@/module/query.ts';
|
|||||||
import { upload } from '@/module/download/upload.ts';
|
import { upload } from '@/module/download/upload.ts';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
import { getHash, getBufferHash } from '@/uitls/hash.ts';
|
||||||
|
import { logger } from '@/module/logger.ts';
|
||||||
const scriptPath = path.join(process.cwd(), 'src', 'scripts');
|
const scriptPath = path.join(process.cwd(), 'src', 'scripts');
|
||||||
const sum = 'https://kevisual.xiongxiao.me/root/ai/kevisual/01-summary.md';
|
const sum = 'https://kevisual.xiongxiao.me/root/ai/kevisual/01-summary.md';
|
||||||
|
const sum2 = 'https://kevisual.xiongxiao.me/root/resources/ai/1.0.0/kevisual/01-summary.md';
|
||||||
const download = async () => {
|
const download = async () => {
|
||||||
const { content } = await fetchLink(sum, { returnContent: true });
|
const { content } = await fetchLink(sum, { returnContent: true });
|
||||||
console.log(content.toString());
|
console.log(content.toString());
|
||||||
@ -16,9 +18,28 @@ const download = async () => {
|
|||||||
const uploadTest = async () => {
|
const uploadTest = async () => {
|
||||||
const file = fs.readFileSync(path.join(scriptPath, './summary.md'));
|
const file = fs.readFileSync(path.join(scriptPath, './summary.md'));
|
||||||
const token = storage.getItem('token');
|
const token = storage.getItem('token');
|
||||||
|
const url = new URL(sum);
|
||||||
// const res = await upload({ url: sum, file: '# 汇总 123', token });
|
// const res = await upload({ url: sum, file: '# 汇总 123', token });
|
||||||
const res = await upload({ url: sum, file: file, token });
|
url.searchParams.append('force', 'true');
|
||||||
console.log(res);
|
// url.searchParams.append('meta', encodeURIComponent(JSON.stringify({ m: 'meta-test' })));
|
||||||
|
const res = await upload({ url: url, file: file, token, needHash: true });
|
||||||
|
logger.info('上传成功', res);
|
||||||
};
|
};
|
||||||
|
|
||||||
uploadTest();
|
uploadTest();
|
||||||
|
const hashCheck = () => {
|
||||||
|
const filepath = path.join(scriptPath, './summary.md');
|
||||||
|
const file = fs.readFileSync(filepath);
|
||||||
|
console.log(getHash(filepath));
|
||||||
|
console.log(getBufferHash(file));
|
||||||
|
};
|
||||||
|
|
||||||
|
// hashCheck();
|
||||||
|
|
||||||
|
// const buf = Buffer.from('123');
|
||||||
|
// const abc = {
|
||||||
|
// a: 1,
|
||||||
|
// b: 2,
|
||||||
|
// c: 3,
|
||||||
|
// };
|
||||||
|
// console.log(typeof buf, buf instanceof Buffer, abc instanceof Buffer);
|
||||||
|
11
src/uitls/hash.ts
Normal file
11
src/uitls/hash.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import MD5 from 'crypto-js/md5.js';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
|
export const getHash = (file: string) => {
|
||||||
|
const content = fs.readFileSync(file, 'utf-8');
|
||||||
|
return MD5(content).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBufferHash = (buffer: Buffer) => {
|
||||||
|
return MD5(buffer.toString()).toString();
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user