192 lines
5.5 KiB
TypeScript
192 lines
5.5 KiB
TypeScript
import path from 'node:path';
|
|
import fs from 'node:fs';
|
|
import { Config, SyncList, SyncConfigType, SyncConfig } from './type.ts';
|
|
import { fileIsExist } from '@/uitls/file.ts';
|
|
import { getHash } from '@/uitls/hash.ts';
|
|
import glob from 'fast-glob';
|
|
import { logger } from '@/module/logger.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'));
|
|
const sync = config.sync || {};
|
|
const keys = Object.keys(sync);
|
|
const newConfigSync: any = {};
|
|
for (let key of keys) {
|
|
const keyPath = path.join(dir, key);
|
|
const newKey = path.relative(dir, keyPath);
|
|
newConfigSync[newKey] = sync[key];
|
|
}
|
|
config.sync = newConfigSync;
|
|
this.config = config;
|
|
return config;
|
|
} catch (err) {
|
|
this.config = {} as Config;
|
|
return {} as Config;
|
|
}
|
|
}
|
|
async canDone(syncType: SyncConfigType, type?: SyncConfigType) {
|
|
if (syncType === 'sync') return true;
|
|
return syncType === type;
|
|
}
|
|
/**
|
|
*
|
|
* @param opts
|
|
* @param opts.getFile 是否检测文件是否存在
|
|
* @returns
|
|
*/
|
|
async getSyncList(opts?: { getFile?: boolean }): Promise<SyncList[]> {
|
|
const config = this.config!;
|
|
let sync = config?.sync || {};
|
|
const syncDirectory = await this.getSyncDirectoryList();
|
|
sync = this.getMergeSync(sync, syncDirectory.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') {
|
|
const auth = checkAuth(value, baseURL);
|
|
const type = auth ? 'sync' : 'none';
|
|
return {
|
|
key,
|
|
type: type as any,
|
|
filepath,
|
|
url: value,
|
|
auth,
|
|
};
|
|
}
|
|
const auth = checkAuth(value.url, baseURL);
|
|
const type = auth ? 'sync' : 'none';
|
|
return {
|
|
key,
|
|
filepath,
|
|
...value,
|
|
type: value?.type ?? type,
|
|
auth: checkAuth(value.url, baseURL),
|
|
};
|
|
});
|
|
if (opts?.getFile) {
|
|
return this.getSyncListFile(syncList);
|
|
}
|
|
return syncList;
|
|
}
|
|
getMergeSync(sync: Config['sync'] = {}, fileSync: Config['sync'] = {}) {
|
|
const syncFileSyncKeys = Object.keys(fileSync);
|
|
const syncKeys = Object.keys(sync);
|
|
const keys = [...syncKeys, ...syncFileSyncKeys];
|
|
const obj: Config['sync'] = {};
|
|
for (let key of keys) {
|
|
const value = sync[key] ?? fileSync[key];
|
|
obj[key] = value;
|
|
}
|
|
return obj;
|
|
}
|
|
async getSyncDirectoryList() {
|
|
const config = this.config;
|
|
const syncDirectory = config?.syncDirectory || [];
|
|
let obj: Record<string, string> = {};
|
|
const keys: string[] = [];
|
|
for (let item of syncDirectory) {
|
|
const { registry, ignore = [], files = [], replace = {} } = item;
|
|
const cwd = this.#dir;
|
|
const glob_files = await glob(files, {
|
|
ignore,
|
|
onlyFiles: true,
|
|
cwd,
|
|
dot: true,
|
|
absolute: true,
|
|
});
|
|
const registyURL = registry || config.registry;
|
|
if (!registyURL) {
|
|
logger.error('请配置 registry', item);
|
|
continue;
|
|
}
|
|
for (let file of glob_files) {
|
|
const key = path.relative(cwd, file);
|
|
const _registryURL = new URL(registyURL);
|
|
const replaceKeys = Object.keys(replace);
|
|
let newKey = key;
|
|
for (let replaceKey of replaceKeys) {
|
|
if (newKey.startsWith(replaceKey)) {
|
|
newKey = key.replace(replaceKey, replace[replaceKey]);
|
|
}
|
|
}
|
|
const pathname = path.join(_registryURL.pathname, newKey);
|
|
_registryURL.pathname = pathname;
|
|
keys.push(key);
|
|
obj[key] = _registryURL.toString();
|
|
}
|
|
}
|
|
return { sync: obj, keys };
|
|
}
|
|
/**
|
|
* 获取文件列表,检测文件是否存在
|
|
* @param syncList
|
|
* @returns
|
|
*/
|
|
async getSyncListFile(syncList: SyncList[]) {
|
|
let syncListFile: SyncList[] = [];
|
|
for (let item of syncList) {
|
|
const { filepath, auth } = item;
|
|
if (filepath && fileIsExist(filepath) && auth) {
|
|
syncListFile.push({
|
|
...item,
|
|
exist: true,
|
|
hash: getHash(filepath),
|
|
});
|
|
} else {
|
|
syncListFile.push({ ...item, exist: false });
|
|
}
|
|
}
|
|
return syncListFile;
|
|
}
|
|
getHash = getHash;
|
|
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
|
|
}
|
|
}
|