2025-01-27 10:10:19 +08:00
|
|
|
|
import { createReadStream } from 'fs';
|
|
|
|
|
import { createInterface } from 'readline';
|
2024-12-30 08:26:40 +08:00
|
|
|
|
|
2025-01-27 10:10:19 +08:00
|
|
|
|
import { db } from '@nice/common';
|
|
|
|
|
import * as tus from 'tus-js-client';
|
2024-12-30 08:26:40 +08:00
|
|
|
|
import ExcelJS from 'exceljs';
|
|
|
|
|
|
|
|
|
|
export function truncateStringByByte(str, maxBytes) {
|
2025-01-27 10:10:19 +08:00
|
|
|
|
let byteCount = 0;
|
|
|
|
|
let index = 0;
|
|
|
|
|
while (
|
|
|
|
|
index < str.length &&
|
|
|
|
|
byteCount + new TextEncoder().encode(str[index]).length <= maxBytes
|
|
|
|
|
) {
|
|
|
|
|
byteCount += new TextEncoder().encode(str[index]).length;
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
return str.substring(0, index) + (index < str.length ? '...' : '');
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
|
|
|
|
export async function loadPoliciesFromCSV(filePath: string) {
|
2025-01-27 10:10:19 +08:00
|
|
|
|
const policies = {
|
|
|
|
|
p: [],
|
|
|
|
|
g: [],
|
|
|
|
|
};
|
|
|
|
|
const stream = createReadStream(filePath);
|
|
|
|
|
const rl = createInterface({
|
|
|
|
|
input: stream,
|
|
|
|
|
crlfDelay: Infinity,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Updated regex to handle commas inside parentheses as part of a single field
|
|
|
|
|
const regex =
|
|
|
|
|
/(?:\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)|"(?:\\"|[^"])*"|[^,"()\s]+)(?=\s*,|\s*$)/g;
|
|
|
|
|
|
|
|
|
|
for await (const line of rl) {
|
|
|
|
|
// Ignore empty lines and comments
|
|
|
|
|
if (line.trim() && !line.startsWith('#')) {
|
|
|
|
|
const parts = [];
|
|
|
|
|
let match;
|
|
|
|
|
while ((match = regex.exec(line)) !== null) {
|
|
|
|
|
// Remove quotes if present and trim whitespace
|
|
|
|
|
parts.push(match[0].replace(/^"|"$/g, '').trim());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check policy type (p or g)
|
|
|
|
|
const ptype = parts[0];
|
|
|
|
|
const rule = parts.slice(1);
|
|
|
|
|
|
|
|
|
|
if (ptype === 'p' || ptype === 'g') {
|
|
|
|
|
policies[ptype].push(rule);
|
|
|
|
|
} else {
|
|
|
|
|
console.warn(`Unknown policy type '${ptype}' in policy: ${line}`);
|
|
|
|
|
}
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
2025-01-27 10:10:19 +08:00
|
|
|
|
}
|
2024-12-30 08:26:40 +08:00
|
|
|
|
|
2025-01-27 10:10:19 +08:00
|
|
|
|
return policies;
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function uploadFile(blob: any, fileName: string) {
|
2025-01-27 10:10:19 +08:00
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const upload = new tus.Upload(blob, {
|
|
|
|
|
endpoint: `${process.env.TUS_URL}/files/`,
|
|
|
|
|
retryDelays: [0, 1000, 3000, 5000],
|
|
|
|
|
metadata: {
|
|
|
|
|
filename: fileName,
|
|
|
|
|
filetype:
|
|
|
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
|
|
|
},
|
|
|
|
|
onError: (error) => {
|
|
|
|
|
console.error('Failed because: ' + error);
|
|
|
|
|
reject(error); // 错误时,我们要拒绝 promise
|
|
|
|
|
},
|
|
|
|
|
onProgress: (bytesUploaded, bytesTotal) => {
|
|
|
|
|
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
|
|
|
|
|
},
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
resolve(upload.url); // 成功后,我们解析 promise,并返回上传的 URL
|
|
|
|
|
},
|
2024-12-30 08:26:40 +08:00
|
|
|
|
});
|
2025-01-27 10:10:19 +08:00
|
|
|
|
upload.start();
|
|
|
|
|
});
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class TreeNode {
|
2025-01-27 10:10:19 +08:00
|
|
|
|
value: string;
|
|
|
|
|
children: TreeNode[];
|
|
|
|
|
|
|
|
|
|
constructor(value: string) {
|
|
|
|
|
this.value = value;
|
|
|
|
|
this.children = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addChild(childValue: string): TreeNode {
|
|
|
|
|
let newChild = undefined;
|
|
|
|
|
if (this.children.findIndex((child) => child.value === childValue) === -1) {
|
|
|
|
|
newChild = new TreeNode(childValue);
|
|
|
|
|
this.children.push(newChild);
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
2025-01-27 10:10:19 +08:00
|
|
|
|
return this.children.find((child) => child.value === childValue);
|
|
|
|
|
}
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
|
|
|
|
function buildTree(data: string[][]): TreeNode {
|
2025-01-27 10:10:19 +08:00
|
|
|
|
const root = new TreeNode('root');
|
|
|
|
|
try {
|
|
|
|
|
for (const path of data) {
|
|
|
|
|
let currentNode = root;
|
|
|
|
|
for (const value of path) {
|
|
|
|
|
currentNode = currentNode.addChild(value);
|
|
|
|
|
}
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
2025-01-27 10:10:19 +08:00
|
|
|
|
return root;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
}
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
|
|
|
|
export function printTree(node: TreeNode, level: number = 0): void {
|
2025-01-27 10:10:19 +08:00
|
|
|
|
const indent = ' '.repeat(level);
|
|
|
|
|
for (const child of node.children) {
|
|
|
|
|
printTree(child, level + 1);
|
|
|
|
|
}
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
|
|
|
|
export async function generateTreeFromFile(file: Buffer): Promise<TreeNode> {
|
2025-01-27 10:10:19 +08:00
|
|
|
|
const workbook = new ExcelJS.Workbook();
|
|
|
|
|
await workbook.xlsx.load(file);
|
|
|
|
|
const worksheet = workbook.getWorksheet(1);
|
|
|
|
|
|
|
|
|
|
const data: string[][] = [];
|
|
|
|
|
|
|
|
|
|
worksheet.eachRow((row, rowNumber) => {
|
|
|
|
|
if (rowNumber > 1) {
|
|
|
|
|
// Skip header row if any
|
|
|
|
|
const rowData: string[] = (row.values as string[])
|
|
|
|
|
.slice(2)
|
|
|
|
|
.map((cell) => (cell || '').toString());
|
|
|
|
|
data.push(rowData.map((value) => value.trim()));
|
2024-12-30 08:26:40 +08:00
|
|
|
|
}
|
2025-01-27 10:10:19 +08:00
|
|
|
|
});
|
|
|
|
|
// Fill forward values
|
|
|
|
|
for (let i = 1; i < data.length; i++) {
|
|
|
|
|
for (let j = 0; j < data[i].length; j++) {
|
|
|
|
|
if (!data[i][j]) data[i][j] = data[i - 1][j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return buildTree(data);
|
|
|
|
|
}
|