update: log

This commit is contained in:
Kerwin
2025-11-20 16:05:41 +08:00
parent 3370f75d5e
commit 040e6bc6bf
6 changed files with 52 additions and 397 deletions

View File

@@ -56,22 +56,18 @@ func LoggingMiddleware(next http.Handler) http.Handler {
}) })
} }
// logRequest 记录请求日志 - 优化后仅记录异常和关键请求 // logRequest 记录请求日志 - 恢复正常请求日志记录
func logRequest(r *http.Request, rw *responseWriter, duration time.Duration, requestBody []byte) { func logRequest(r *http.Request, rw *responseWriter, duration time.Duration, requestBody []byte) {
// 获取客户端IP // 获取客户端IP
clientIP := getClientIP(r) clientIP := getClientIP(r)
// 判断是否需要记录日志的条件 // 判断是否需要详细记录日志的条件
shouldLog := rw.statusCode >= 400 || // 错误状态码 shouldDetailLog := rw.statusCode >= 400 || // 错误状态码
duration > 5*time.Second || // 耗时过长 duration > 5*time.Second || // 耗时过长
shouldLogPath(r.URL.Path) || // 关键路径 shouldLogPath(r.URL.Path) || // 关键路径
isAdminPath(r.URL.Path) // 管理员路径 isAdminPath(r.URL.Path) // 管理员路径
if !shouldLog { // 所有API请求都记录基本信息但详细日志只记录重要请求
return // 正常请求不记录日志,减少日志噪音
}
// 简化的日志格式移除User-Agent以减少噪音
if rw.statusCode >= 400 { if rw.statusCode >= 400 {
// 错误请求记录详细信息 // 错误请求记录详细信息
utils.Error("HTTP异常 - %s %s - IP: %s - 状态码: %d - 耗时: %v", utils.Error("HTTP异常 - %s %s - IP: %s - 状态码: %d - 耗时: %v",
@@ -85,10 +81,14 @@ func logRequest(r *http.Request, rw *responseWriter, duration time.Duration, req
// 慢请求警告 // 慢请求警告
utils.Warn("HTTP慢请求 - %s %s - IP: %s - 耗时: %v", utils.Warn("HTTP慢请求 - %s %s - IP: %s - 耗时: %v",
r.Method, r.URL.Path, clientIP, duration) r.Method, r.URL.Path, clientIP, duration)
} else { } else if shouldDetailLog {
// 关键路径的正常请求 // 关键路径的正常请求
utils.Info("HTTP关键请求 - %s %s - IP: %s - 状态码: %d - 耗时: %v", utils.Info("HTTP关键请求 - %s %s - IP: %s - 状态码: %d - 耗时: %v",
r.Method, r.URL.Path, clientIP, rw.statusCode, duration) r.Method, r.URL.Path, clientIP, rw.statusCode, duration)
} else {
// 普通API请求记录简化日志 - 使用Info级别确保能被看到
// utils.Info("HTTP请求 - %s %s - 状态码: %d - 耗时: %v",
// r.Method, r.URL.Path, rw.statusCode, duration)
} }
} }
@@ -100,6 +100,13 @@ func shouldLogPath(path string) bool {
"/api/admin/config", "/api/admin/config",
"/api/admin/users", "/api/admin/users",
"/telegram/webhook", "/telegram/webhook",
"/api/resources",
"/api/version",
"/api/cks",
"/api/pans",
"/api/categories",
"/api/tags",
"/api/tasks",
} }
for _, keyPath := range keyPaths { for _, keyPath := range keyPaths {
@@ -113,7 +120,7 @@ func shouldLogPath(path string) bool {
// isAdminPath 判断是否为管理员路径 // isAdminPath 判断是否为管理员路径
func isAdminPath(path string) bool { func isAdminPath(path string) bool {
return strings.HasPrefix(path, "/api/admin/") || return strings.HasPrefix(path, "/api/admin/") ||
strings.HasPrefix(path, "/admin/") strings.HasPrefix(path, "/admin/")
} }
// getClientIP 获取客户端真实IP地址 // getClientIP 获取客户端真实IP地址

View File

@@ -28,23 +28,40 @@ func GetTelegramLogs(startTime *time.Time, endTime *time.Time, limit int) ([]Tel
return []TelegramLogEntry{}, nil return []TelegramLogEntry{}, nil
} }
files, err := filepath.Glob(filepath.Join(logDir, "app_*.log")) // 查找所有日志文件,包括当前的app.log和历史日志文件
allFiles, err := filepath.Glob(filepath.Join(logDir, "*.log"))
if err != nil { if err != nil {
return nil, fmt.Errorf("查找日志文件失败: %v", err) return nil, fmt.Errorf("查找日志文件失败: %v", err)
} }
if len(files) == 0 { if len(allFiles) == 0 {
return []TelegramLogEntry{}, nil return []TelegramLogEntry{}, nil
} }
// 按时间排序,最近的在前面 // 将app.log放在最前面其他文件按时间排序
sort.Sort(sort.Reverse(sort.StringSlice(files))) var files []string
var otherFiles []string
for _, file := range allFiles {
if filepath.Base(file) == "app.log" {
files = append(files, file) // 当前日志文件优先
} else {
otherFiles = append(otherFiles, file)
}
}
// 其他文件按时间排序,最近的在前面
sort.Sort(sort.Reverse(sort.StringSlice(otherFiles)))
files = append(files, otherFiles...)
// files现在已经是app.log优先然后是其他文件按时间倒序排列
var allEntries []TelegramLogEntry var allEntries []TelegramLogEntry
// 编译Telegram相关的正则表达式 // 编译Telegram相关的正则表达式
telegramRegex := regexp.MustCompile(`(?i)(\[TELEGRAM.*?\])`) telegramRegex := regexp.MustCompile(`(?i)(\[TELEGRAM.*?\])`)
messageRegex := regexp.MustCompile(`\[(\w+)\]\s+(\d{4}/\d{2}/\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[.*?\]\s+(.*)`) // 修正正则表达式以匹配实际的日志格式: 2025/01/20 14:30:15 [INFO] [file:line] [TELEGRAM] message
messageRegex := regexp.MustCompile(`(\d{4}/\d{2}/\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+\[.*?:\d+\]\s+\[TELEGRAM.*?\]\s+(.*)`)
for _, file := range files { for _, file := range files {
entries, err := parseTelegramLogsFromFile(file, telegramRegex, messageRegex, startTime, endTime) entries, err := parseTelegramLogsFromFile(file, telegramRegex, messageRegex, startTime, endTime)
@@ -119,18 +136,23 @@ func parseTelegramLogsFromFile(filePath string, telegramRegex, messageRegex *reg
// parseLogLine 解析单行日志 // parseLogLine 解析单行日志
func parseLogLine(line string, messageRegex *regexp.Regexp) (TelegramLogEntry, error) { func parseLogLine(line string, messageRegex *regexp.Regexp) (TelegramLogEntry, error) {
// 匹配日志格式: [LEVEL] 2006/01/02 15:04:05 [file:line] message // 匹配日志格式: 2006/01/02 15:04:05 [LEVEL] [file:line] [TELEGRAM] message
matches := messageRegex.FindStringSubmatch(line) matches := messageRegex.FindStringSubmatch(line)
if len(matches) < 4 { if len(matches) < 4 {
return TelegramLogEntry{}, fmt.Errorf("无法解析日志行: %s", line) return TelegramLogEntry{}, fmt.Errorf("无法解析日志行: %s", line)
} }
level := matches[1] timeStr := matches[1]
timeStr := matches[2] level := matches[2]
message := matches[3] message := matches[3]
// 解析时间 // 解析时间(使用本地时区)
timestamp, err := time.Parse("2006/01/02 15:04:05", timeStr) location, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
return TelegramLogEntry{}, fmt.Errorf("加载时区失败: %v", err)
}
timestamp, err := time.ParseInLocation("2006/01/02 15:04:05", timeStr, location)
if err != nil { if err != nil {
return TelegramLogEntry{}, fmt.Errorf("时间解析失败: %v", err) return TelegramLogEntry{}, fmt.Errorf("时间解析失败: %v", err)
} }
@@ -203,7 +225,7 @@ func ClearOldTelegramLogs(daysToKeep int) error {
return nil // 日志目录不存在,无需清理 return nil // 日志目录不存在,无需清理
} }
files, err := filepath.Glob(filepath.Join(logDir, "app_*.log")) files, err := filepath.Glob(filepath.Join(logDir, "*.log"))
if err != nil { if err != nil {
return fmt.Errorf("查找日志文件失败: %v", err) return fmt.Errorf("查找日志文件失败: %v", err)
} }

View File

@@ -1,345 +0,0 @@
<template>
<div class="share-container">
<!-- 直接显示分享按钮 -->
<div
ref="socialShareElement"
class="social-share-wrapper"
></div>
</div>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
url: {
type: String,
default: ''
},
tags: {
type: Array,
default: () => []
}
})
const route = useRoute()
// 响应式数据
const socialShareElement = ref(null)
// 计算属性 - 避免在SSR中访问客户端API
const shareTitle = computed(() => {
return props.title && props.title !== 'undefined' ? props.title : '精彩资源分享'
})
const shareDescription = computed(() => {
return props.description && props.description !== 'undefined' ? props.description : '发现更多优质资源尽在urlDB'
})
const shareTags = computed(() => {
if (props.tags && Array.isArray(props.tags) && props.tags.length > 0) {
return props.tags.filter(tag => tag && tag !== 'undefined').slice(0, 3).join(',') || '资源分享,网盘,urldb'
}
return '资源分享,网盘,urldb'
})
// 获取完整URL - 使用运行时配置
const getFullUrl = () => {
const config = useRuntimeConfig()
if (props.url) {
// 如果props.url已经是完整URL则直接返回
if (props.url.startsWith('http://') || props.url.startsWith('https://')) {
return props.url
}
// 否则拼接站点URL
let siteUrl = config.public.siteUrl
if (!siteUrl || siteUrl === 'https://yourdomain.com') {
// 优先在客户端使用当前页面的origin
if (typeof window !== 'undefined') {
siteUrl = window.location.origin
} else {
// 在服务端渲染时使用默认值
siteUrl = process.env.NUXT_PUBLIC_SITE_URL || 'https://yourdomain.com'
}
}
return `${siteUrl}${props.url.startsWith('/') ? props.url : '/' + props.url}`
}
if (typeof window !== 'undefined') {
return `${window.location.origin}${route.fullPath}`
}
return route.fullPath
}
// 初始化 social-share - 仅在客户端调用
const initSocialShare = () => {
if (typeof window === 'undefined') return
if (socialShareElement.value) {
// 清空容器
socialShareElement.value.innerHTML = ''
// 创建 social-share 元素
const shareElement = document.createElement('div')
shareElement.className = 'social-share'
shareElement.setAttribute('data-sites', 'facebook,twitter,reddit')
shareElement.setAttribute('data-title', shareTitle.value)
shareElement.setAttribute('data-description', shareDescription.value)
shareElement.setAttribute('data-url', getFullUrl())
shareElement.setAttribute('data-image', '') // 设置默认图片
shareElement.setAttribute('data-pics', '') // 设置图片QQ空间使用
shareElement.setAttribute('data-via', '') // Twitter via
shareElement.setAttribute('data-wechat-qrcode-title', '微信扫一扫:分享')
shareElement.setAttribute('data-wechat-qrcode-helper', '<p>微信里点"发现",扫一下</p><p>二维码便可将本文分享至朋友圈。</p>')
socialShareElement.value.appendChild(shareElement)
// 初始化 social-share - 等待一段时间确保库已完全加载
setTimeout(() => {
console.log('检查 SocialShare 对象:', window.SocialShare)
console.log('检查 social-share 元素:', shareElement)
// 尝试使用 social-share.js 的正确初始化方式
if (window.socialShare) {
try {
// 传入选择器来初始化
window.socialShare('.social-share')
console.log('socialShare() 函数调用成功')
} catch (error) {
console.error('socialShare 初始化失败:', error)
// 如果上面失败,尝试另一种方式
try {
if (typeof window.socialShare === 'function') {
window.socialShare()
console.log('socialShare 全局调用成功')
}
} catch (error2) {
console.error('socialShare 全局调用也失败:', error2)
}
}
} else if (window.SocialShare) {
try {
window.SocialShare.init()
console.log('SocialShare.init() 调用成功')
} catch (error) {
console.error('SocialShare 初始化失败:', error)
}
} else {
console.error('SocialShare 对象不存在,库可能未正确加载')
}
}, 300)
}
}
// 动态加载 social-share.js 和 CSS - 仅在客户端调用
const loadSocialShare = () => {
if (typeof window === 'undefined') return
// 加载 CSS 文件
if (!document.querySelector('link[href*="social-share.min.css"]')) {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = 'https://cdn.jsdelivr.net/npm/social-share.js@1.0.16/dist/css/share.min.css'
link.onload = () => {
console.log('social-share.css 加载完成')
}
link.onerror = () => {
console.error('social-share.css 加载失败')
// 如果CDN加载失败尝试备用链接
const backupLink = document.createElement('link')
backupLink.rel = 'stylesheet'
backupLink.href = 'https://unpkg.com/social-share.js@1.0.16/dist/css/share.min.css'
backupLink.onload = () => {
console.log('备用 social-share.css 加载完成')
}
backupLink.onerror = () => {
console.error('备用 social-share.css 也加载失败')
}
document.head.appendChild(backupLink)
}
document.head.appendChild(link)
}
if (!window.socialShare) {
console.log('开始加载 social-share.js...')
const script = document.createElement('script')
script.src = 'https://cdn.jsdelivr.net/npm/social-share.js@1.0.16/dist/js/social-share.min.js'
script.onload = () => {
console.log('social-share.js 加载完成,检查全局对象:', window.socialShare)
// 加载完成后初始化
nextTick(() => {
setTimeout(() => {
initSocialShare()
}, 300) // 稍微增加等待时间确保CSS和JS都完全加载
})
}
script.onerror = () => {
console.error('social-share.js 加载失败')
// 如果CDN加载失败尝试备用链接
const backupScript = document.createElement('script')
backupScript.src = 'https://unpkg.com/social-share.js@1.0.16/dist/js/social-share.min.js'
backupScript.onload = () => {
console.log('备用 social-share.js 加载完成,检查全局对象:', window.socialShare)
nextTick(() => {
setTimeout(() => {
initSocialShare()
}, 300)
})
}
backupScript.onerror = () => {
console.error('备用 social-share.js 也加载失败')
// 如果无法加载外部库,创建基本分享按钮
createFallbackShareButtons()
}
document.head.appendChild(backupScript)
}
document.head.appendChild(script)
} else {
// 如果已经加载过,直接初始化
console.log('socialShare 已存在,直接初始化')
initSocialShare()
}
}
// 创建备选分享按钮当social-share.js无法加载时使用
const createFallbackShareButtons = () => {
if (typeof window === 'undefined' || !socialShareElement.value) return
// 清空容器
socialShareElement.value.innerHTML = ''
// 创建包含基本分享功能的按钮
const shareContainer = document.createElement('div')
shareContainer.className = 'fallback-share-buttons'
const fullUrl = getFullUrl()
const encodedUrl = encodeURIComponent(fullUrl)
const encodedTitle = encodeURIComponent(shareTitle.value)
const encodedDesc = encodeURIComponent(shareDescription.value)
// Facebook分享链接
const facebookLink = document.createElement('a')
facebookLink.href = `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}&t=${encodedTitle}`
facebookLink.target = '_blank'
facebookLink.innerHTML = '<i class="fa fa-facebook" style="font-size: 20px; color: #1877f2;"></i>'
facebookLink.style.display = 'inline-block'
facebookLink.style.margin = '0 3px'
facebookLink.title = '分享到Facebook'
// Twitter分享链接
const twitterLink = document.createElement('a')
twitterLink.href = `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`
twitterLink.target = '_blank'
twitterLink.innerHTML = '<i class="fa fa-twitter" style="font-size: 20px; color: #1da1f2;"></i>'
twitterLink.style.display = 'inline-block'
twitterLink.style.margin = '0 3px'
twitterLink.title = '分享到Twitter'
// Reddit分享链接
const redditLink = document.createElement('a')
redditLink.href = `https://www.reddit.com/submit?url=${encodedUrl}&title=${encodedTitle}`
redditLink.target = '_blank'
redditLink.innerHTML = '<i class="fa fa-reddit" style="font-size: 20px; color: #ff4500;"></i>'
redditLink.style.display = 'inline-block'
redditLink.style.margin = '0 3px'
redditLink.title = '分享到Reddit'
// 添加到容器
shareContainer.appendChild(facebookLink)
shareContainer.appendChild(twitterLink)
shareContainer.appendChild(redditLink)
socialShareElement.value.appendChild(shareContainer)
}
// 组件挂载时直接初始化 - 仅在客户端执行
onMounted(() => {
if (typeof window !== 'undefined') {
// 页面加载完成后直接初始化 social-share
nextTick(() => {
loadSocialShare()
})
}
})
</script>
<style scoped>
.share-container {
position: relative;
display: inline-block;
}
/* social-share.js 样式适配 */
.social-share-wrapper {
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
/* social-share.js 默认样式覆盖 */
.social-share-wrapper .social-share {
display: flex !important;
flex-wrap: wrap;
gap: 6px;
}
.social-share-wrapper .social-share-icon {
width: 28px !important;
height: 28px !important;
margin: 0 !important;
border-radius: 4px;
transition: all 0.2s ease;
}
.social-share-wrapper .social-share-icon:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* 暗色模式下的 social-share 图标 */
.dark .social-share-wrapper .social-share-icon {
filter: brightness(0.9);
}
/* 备选分享按钮样式 */
.fallback-share-buttons {
display: flex;
gap: 6px;
align-items: center;
}
.fallback-share-buttons a {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 4px;
transition: all 0.2s ease;
text-decoration: none;
}
.fallback-share-buttons a:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* 响应式设计 */
@media (max-width: 640px) {
.social-share-wrapper .social-share-icon,
.fallback-share-buttons a {
width: 26px !important;
height: 26px !important;
}
}
</style>

View File

@@ -36,8 +36,7 @@
"qr-code-styling": "^1.9.2", "qr-code-styling": "^1.9.2",
"vfonts": "^0.0.3", "vfonts": "^0.0.3",
"vue": "^3.3.0", "vue": "^3.3.0",
"vue-router": "^4.2.0", "vue-router": "^4.2.0"
"vue-social-share": "^0.0.3"
}, },
"packageManager": "pnpm@9.13.0+sha512.beb9e2a803db336c10c9af682b58ad7181ca0fbd0d4119f2b33d5f2582e96d6c0d93c85b23869295b765170fbdaa92890c0da6ada457415039769edf3c959efe" "packageManager": "pnpm@9.13.0+sha512.beb9e2a803db336c10c9af682b58ad7181ca0fbd0d4119f2b33d5f2582e96d6c0d93c85b23869295b765170fbdaa92890c0da6ada457415039769edf3c959efe"
} }

View File

@@ -154,14 +154,7 @@
<i class="fas" :class="isDetecting ? 'fa-spinner fa-spin' : 'fa-sync-alt'"></i> <i class="fas" :class="isDetecting ? 'fa-spinner fa-spin' : 'fa-sync-alt'"></i>
<span class="hidden sm:inline">{{ isDetecting ? '检测中' : '链接检测' }}</span> <span class="hidden sm:inline">{{ isDetecting ? '检测中' : '链接检测' }}</span>
</button> </button>
<!-- 分享按钮 --> </div>
<ShareButtons
:title="mainResource?.title"
:description="mainResource?.description"
:url="getResourceUrl"
:tags="mainResource?.tags?.map(tag => tag.name)"
/>
</div>
</div> </div>
<div class="space-y-3"> <div class="space-y-3">

21
web/pnpm-lock.yaml generated
View File

@@ -50,9 +50,6 @@ importers:
vue-router: vue-router:
specifier: ^4.2.0 specifier: ^4.2.0
version: 4.5.1(vue@3.5.18(typescript@5.8.3)) version: 4.5.1(vue@3.5.18(typescript@5.8.3))
vue-social-share:
specifier: ^0.0.3
version: 0.0.3
devDependencies: devDependencies:
'@nuxt/devtools': '@nuxt/devtools':
specifier: latest specifier: latest
@@ -3916,9 +3913,6 @@ packages:
smob@1.5.0: smob@1.5.0:
resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==}
social-share.js@1.0.16:
resolution: {integrity: sha512-NSV6fYFft/U0fEbjXdumZGU3c2oTbnJ6Ha5eNMEEBGsJpD+nu+nbg3LiRygO5GnoNgUa/dOmJyVHb/kM4dJa6g==}
source-map-js@1.2.1: source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -4524,17 +4518,11 @@ packages:
vue-devtools-stub@0.1.0: vue-devtools-stub@0.1.0:
resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==} resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==}
vue-github-badge@1.0.1:
resolution: {integrity: sha512-8X+FUWapnnDfs6cRUg3mCfHUf2r5arUfCSRdvbIn860oj9us3Rz3VOtioUgmfzh6EhaaYTs0Oh78EzJ+Z6uqAA==}
vue-router@4.5.1: vue-router@4.5.1:
resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==}
peerDependencies: peerDependencies:
vue: ^3.2.0 vue: ^3.2.0
vue-social-share@0.0.3:
resolution: {integrity: sha512-zzZGloWVTE/OrEFT0oVfVxzWBvak9KLWiIRWWkPWag10PlGgxTI4o1oN+kXIT+8U3MkRVA8cQLPf5CPqDGmfqw==}
vue@3.5.18: vue@3.5.18:
resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==} resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==}
peerDependencies: peerDependencies:
@@ -8947,8 +8935,6 @@ snapshots:
smob@1.5.0: {} smob@1.5.0: {}
social-share.js@1.0.16: {}
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
source-map-support@0.5.21: source-map-support@0.5.21:
@@ -9549,18 +9535,11 @@ snapshots:
vue-devtools-stub@0.1.0: {} vue-devtools-stub@0.1.0: {}
vue-github-badge@1.0.1: {}
vue-router@4.5.1(vue@3.5.18(typescript@5.8.3)): vue-router@4.5.1(vue@3.5.18(typescript@5.8.3)):
dependencies: dependencies:
'@vue/devtools-api': 6.6.4 '@vue/devtools-api': 6.6.4
vue: 3.5.18(typescript@5.8.3) vue: 3.5.18(typescript@5.8.3)
vue-social-share@0.0.3:
dependencies:
social-share.js: 1.0.16
vue-github-badge: 1.0.1
vue@3.5.18(typescript@5.8.3): vue@3.5.18(typescript@5.8.3):
dependencies: dependencies:
'@vue/compiler-dom': 3.5.18 '@vue/compiler-dom': 3.5.18