feat: 初始化 Gitea Action 发送钉钉机器人消息项目
This commit is contained in:
349
src/utils.js
Normal file
349
src/utils.js
Normal file
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* 工具函数模块
|
||||
* 提供通用的辅助功能和实用工具
|
||||
*/
|
||||
|
||||
/**
|
||||
* 日志级别枚举
|
||||
*
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
*/
|
||||
export const LOG_LEVELS = {
|
||||
DEBUG: 'DEBUG',
|
||||
INFO: 'INFO',
|
||||
WARN: 'WARN',
|
||||
ERROR: 'ERROR',
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前时间戳字符串
|
||||
*
|
||||
* @returns {string} 格式化的时间戳
|
||||
*/
|
||||
export function getTimestamp() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日志消息
|
||||
*
|
||||
* @param {string} level - 日志级别
|
||||
* @param {string} message - 日志消息
|
||||
* @param {object} [meta] - 额外的元数据
|
||||
* @returns {string} 格式化后的日志消息
|
||||
*/
|
||||
export function formatLogMessage(level, message, meta = null) {
|
||||
const timestamp = getTimestamp();
|
||||
let logMessage = `[${timestamp}] [${level}] ${message}`;
|
||||
|
||||
if (meta && typeof meta === 'object') {
|
||||
logMessage += ` | ${JSON.stringify(meta)}`;
|
||||
}
|
||||
|
||||
return logMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出调试日志
|
||||
*
|
||||
* @param {string} message - 日志消息
|
||||
* @param {object} [meta] - 额外的元数据
|
||||
*/
|
||||
export function logDebug(message, meta) {
|
||||
if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
|
||||
console.log(formatLogMessage(LOG_LEVELS.DEBUG, message, meta));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出信息日志
|
||||
*
|
||||
* @param {string} message - 日志消息
|
||||
* @param {object} [meta] - 额外的元数据
|
||||
*/
|
||||
export function logInfo(message, meta) {
|
||||
console.log(formatLogMessage(LOG_LEVELS.INFO, message, meta));
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出警告日志
|
||||
*
|
||||
* @param {string} message - 日志消息
|
||||
* @param {object} [meta] - 额外的元数据
|
||||
*/
|
||||
export function logWarn(message, meta) {
|
||||
console.warn(formatLogMessage(LOG_LEVELS.WARN, message, meta));
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出错误日志
|
||||
*
|
||||
* @param {string} message - 日志消息
|
||||
* @param {object} [meta] - 额外的元数据
|
||||
*/
|
||||
export function logError(message, meta) {
|
||||
console.error(formatLogMessage(LOG_LEVELS.ERROR, message, meta));
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地解析 JSON 字符串
|
||||
*
|
||||
* @param {string} jsonString - JSON 字符串
|
||||
* @param {*} [defaultValue] - 解析失败时的默认值
|
||||
* @returns {*} 解析结果或默认值
|
||||
*/
|
||||
export function safeJsonParse(jsonString, defaultValue = null) {
|
||||
try {
|
||||
return JSON.parse(jsonString);
|
||||
} catch (error) {
|
||||
logWarn(`Failed to parse JSON: ${error.message}`, { jsonString });
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地序列化对象为 JSON 字符串
|
||||
*
|
||||
* @param {*} obj - 要序列化的对象
|
||||
* @param {string} [defaultValue] - 序列化失败时的默认值
|
||||
* @returns {string} JSON 字符串或默认值
|
||||
*/
|
||||
export function safeJsonStringify(obj, defaultValue = '{}') {
|
||||
try {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
} catch (error) {
|
||||
// 避免循环引用问题,不传递 obj 到 logWarn
|
||||
logWarn(`Failed to stringify object: ${error.message}`);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否为空或只包含空白字符
|
||||
*
|
||||
* @param {string} str - 要检查的字符串
|
||||
* @returns {boolean} 是否为空
|
||||
*/
|
||||
export function isEmpty(str) {
|
||||
return !str || typeof str !== 'string' || str.trim().length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 截断字符串到指定长度
|
||||
*
|
||||
* @param {string} str - 原字符串
|
||||
* @param {number} maxLength - 最大长度
|
||||
* @param {string} [suffix] - 截断后的后缀
|
||||
* @returns {string} 截断后的字符串
|
||||
*/
|
||||
export function truncateString(str, maxLength, suffix = '...') {
|
||||
if (!str || typeof str !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (str.length <= maxLength) {
|
||||
return str;
|
||||
}
|
||||
|
||||
return str.substring(0, maxLength - suffix.length) + suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理和验证手机号
|
||||
*
|
||||
* @param {string} mobile - 手机号字符串
|
||||
* @returns {string|null} 清理后的手机号或null
|
||||
*/
|
||||
export function cleanMobile(mobile) {
|
||||
if (!mobile || typeof mobile !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 移除所有非数字字符
|
||||
const cleaned = mobile.replace(/\D/g, '');
|
||||
|
||||
// 验证中国大陆手机号格式
|
||||
if (/^1[3-9]\d{9}$/.test(cleaned)) {
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 URL 格式
|
||||
*
|
||||
* @param {string} url - URL 字符串
|
||||
* @returns {boolean} 是否为有效的 URL
|
||||
*/
|
||||
export function isValidUrl(url) {
|
||||
if (!url || typeof url !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 HTTP/HTTPS URL
|
||||
*
|
||||
* @param {string} url - URL 字符串
|
||||
* @returns {boolean} 是否为有效的 HTTP/HTTPS URL
|
||||
*/
|
||||
export function isValidHttpUrl(url) {
|
||||
if (!isValidUrl(url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return /^https?:\/\/.+/.test(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建错误对象
|
||||
*
|
||||
* @param {string} message - 错误消息
|
||||
* @param {string} [code] - 错误代码
|
||||
* @param {*} [details] - 错误详情
|
||||
* @returns {Error} 错误对象
|
||||
*/
|
||||
export function createError(message, code = null, details = null) {
|
||||
const error = new Error(message);
|
||||
|
||||
if (code) {
|
||||
error.code = code;
|
||||
}
|
||||
|
||||
if (details) {
|
||||
error.details = details;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 GitHub Actions 输出
|
||||
*
|
||||
* @param {string} name - 输出名称
|
||||
* @param {string} value - 输出值
|
||||
*/
|
||||
export function setActionOutput(name, value) {
|
||||
if (process.env.GITHUB_OUTPUT) {
|
||||
// GitHub Actions 新格式
|
||||
const fs = require('fs');
|
||||
fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`);
|
||||
} else {
|
||||
// 兼容旧格式
|
||||
console.log(`::set-output name=${name}::${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 GitHub Actions 失败状态
|
||||
*
|
||||
* @param {string} message - 失败消息
|
||||
*/
|
||||
export function setActionFailed(message) {
|
||||
console.log(`::error::${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境变量,支持默认值
|
||||
*
|
||||
* @param {string} name - 环境变量名
|
||||
* @param {string} [defaultValue] - 默认值
|
||||
* @returns {string} 环境变量值或默认值
|
||||
*/
|
||||
export function getEnv(name, defaultValue = '') {
|
||||
return process.env[name] || defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否在调试模式
|
||||
*
|
||||
* @returns {boolean} 是否为调试模式
|
||||
*/
|
||||
export function isDebugMode() {
|
||||
return getEnv('DEBUG', 'false').toLowerCase() === 'true' || getEnv('NODE_ENV') === 'development';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化持续时间
|
||||
*
|
||||
* @param {number} milliseconds - 毫秒数
|
||||
* @returns {string} 格式化的持续时间
|
||||
*/
|
||||
export function formatDuration(milliseconds) {
|
||||
if (milliseconds < 1000) {
|
||||
return `${milliseconds}ms`;
|
||||
}
|
||||
|
||||
const seconds = (milliseconds / 1000).toFixed(2);
|
||||
return `${seconds}s`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建重试配置
|
||||
*
|
||||
* @param {number} [maxRetries] - 最大重试次数
|
||||
* @param {number} [delay] - 重试延迟(毫秒)
|
||||
* @param {number} [backoffMultiplier] - 退避乘数
|
||||
* @returns {object} 重试配置
|
||||
*/
|
||||
export function createRetryConfig(maxRetries = 3, delay = 1000, backoffMultiplier = 1.5) {
|
||||
return {
|
||||
maxRetries,
|
||||
delay,
|
||||
backoffMultiplier,
|
||||
getDelay: attempt => Math.floor(delay * Math.pow(backoffMultiplier, attempt)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 掩码敏感信息
|
||||
*
|
||||
* @param {string} str - 原字符串
|
||||
* @param {number} [visibleStart] - 开始可见字符数
|
||||
* @param {number} [visibleEnd] - 结尾可见字符数
|
||||
* @param {string} [maskChar] - 掩码字符
|
||||
* @returns {string} 掩码后的字符串
|
||||
*/
|
||||
export function maskSensitiveInfo(str, visibleStart = 4, visibleEnd = 4, maskChar = '*') {
|
||||
if (!str || typeof str !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (str.length <= visibleStart + visibleEnd) {
|
||||
return maskChar.repeat(str.length);
|
||||
}
|
||||
|
||||
const start = str.substring(0, visibleStart);
|
||||
const end = str.substring(str.length - visibleEnd);
|
||||
const maskLength = str.length - visibleStart - visibleEnd;
|
||||
|
||||
return start + maskChar.repeat(maskLength) + end;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证必需的环境变量
|
||||
*
|
||||
* @param {string[]} requiredVars - 必需的环境变量名列表
|
||||
* @throws {Error} 当缺少必需的环境变量时抛出错误
|
||||
*/
|
||||
export function validateRequiredEnvVars(requiredVars) {
|
||||
const missing = requiredVars.filter(varName => !process.env[varName]);
|
||||
|
||||
if (missing.length > 0) {
|
||||
throw createError(
|
||||
`Missing required environment variables: ${missing.join(', ')}`,
|
||||
'MISSING_ENV_VARS',
|
||||
{ missing },
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user