-
-
- {qrCodeShow && }
-
-
- {locale.COMMON.SCAN_QR_CODE}
-
+ switch (singleService) {
+ case 'facebook':
+ return (
+
+
+
+ )
+ case 'messenger':
+ return (
+
+
+
+ )
+ case 'line':
+ return (
+
+
+
+ )
+ case 'reddit':
+ return (
+
+
+
+ )
+ case 'email':
+ return (
+
+
+
+ )
+ case 'twitter':
+ return (
+
+
+
+ )
+ case 'telegram':
+ return (
+
+
+
+ )
+ case 'whatsapp':
+ return (
+
+
+
+ )
+ case 'linkedin':
+ return (
+
+
+
+ )
+ case 'pinterest':
+ return (
+
+
+
+ )
+ case 'vkshare':
+ return (
+
+
+
+ )
+ case 'okshare':
+ return (
+
+
+
+ )
+ case 'tumblr':
+ return (
+
+
+
+ )
+ case 'livejournal':
+ return (
+
+
+
+ )
+ case 'mailru':
+ return (
+
+
+
+ )
+ case 'viber':
+ return (
+
+
+
+ )
+ case 'workplace':
+ return (
+
+
+
+ )
+ case 'weibo':
+ return (
+
+
+
+ )
+ case 'pocket':
+ return (
+
+
+
+ )
+ case 'instapaper':
+ return (
+
+
+
+ )
+ case 'hatena':
+ return (
+
+
+
+ )
+ case 'qq':
+ return (
+
+ )
+ case 'wechat':
+ return (
+
-
- )
+
+
+
+ {qrCodeShow && }
+
+
+ {locale.COMMON.SCAN_QR_CODE}
+
+
+
+
+ )
+ case 'link':
+ return (
+
+ )
+ default:
+ return <>>
}
- if (singleService === 'link') {
- return (
-
- )
- }
- return <>>
})}
>
)
diff --git a/conf/dev.config.js b/conf/dev.config.js
index 2cbdfa93..c34a8c64 100644
--- a/conf/dev.config.js
+++ b/conf/dev.config.js
@@ -7,6 +7,10 @@ module.exports = {
// TAILWINDCSS 配置的自定义颜色,作废
BACKGROUND_LIGHT: '#eeeeee', // use hex value, don't forget '#' e.g #fffefc
BACKGROUND_DARK: '#000000', // use hex value, don't forget '#'
+
+ // Redis 缓存数据库地址
+ REDIS_URL: process.env.REDIS_URL || '',
+
ENABLE_CACHE:
process.env.ENABLE_CACHE ||
process.env.npm_lifecycle_event === 'build' ||
diff --git a/conf/plugin.config.js b/conf/plugin.config.js
index 371e75d6..aa9e0016 100644
--- a/conf/plugin.config.js
+++ b/conf/plugin.config.js
@@ -2,6 +2,13 @@
* 一些插件
*/
module.exports = {
+ // 网站全文搜索
+ ALGOLIA_APP_ID: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || null, // 在这里查看 https://dashboard.algolia.com/account/api-keys/
+ ALGOLIA_ADMIN_APP_KEY: process.env.ALGOLIA_ADMIN_APP_KEY || null, // 管理后台的KEY,不要暴露在代码中,在这里查看 https://dashboard.algolia.com/account/api-keys/
+ ALGOLIA_SEARCH_ONLY_APP_KEY:
+ process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY || null, // 客户端搜索用的KEY
+ ALGOLIA_INDEX: process.env.NEXT_PUBLIC_ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库
+
// AI 文章摘要生成
AI_SUMMARY_API: process.env.AI_SUMMARY_API || '',
diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js
index f35a1fe1..f0c412ab 100644
--- a/lib/cache/cache_manager.js
+++ b/lib/cache/cache_manager.js
@@ -1,6 +1,55 @@
import BLOG from '@/blog.config'
import FileCache from './local_file_cache'
import MemoryCache from './memory_cache'
+import RedisCache from './redis_cache'
+
+// 配置是否开启Vercel环境中的缓存,因为Vercel中现有两种缓存方式在无服务环境下基本都是无意义的,纯粹的浪费资源
+const enableCacheInVercel =
+ process.env.npm_lifecycle_event === 'build' ||
+ process.env.npm_lifecycle_event === 'export' ||
+ !BLOG['isProd']
+
+/**
+ * 尝试从缓存中获取数据,如果没有则尝试获取数据并写入缓存,最终返回所需数据
+ * @param key
+ * @param getDataFunction
+ * @param getDataArgs
+ * @returns {Promise<*|null>}
+ */
+export async function getOrSetDataWithCache(
+ key,
+ getDataFunction,
+ ...getDataArgs
+) {
+ return getOrSetDataWithCustomCache(key, null, getDataFunction, ...getDataArgs)
+}
+
+/**
+ * 尝试从缓存中获取数据,如果没有则尝试获取数据并自定义写入缓存,最终返回所需数据
+ * @param key
+ * @param customCacheTime
+ * @param getDataFunction
+ * @param getDataArgs
+ * @returns {Promise<*|null>}
+ */
+export async function getOrSetDataWithCustomCache(
+ key,
+ customCacheTime,
+ getDataFunction,
+ ...getDataArgs
+) {
+ const dataFromCache = await getDataFromCache(key)
+ if (dataFromCache) {
+ console.log('[缓存-->>API]:', key)
+ return dataFromCache
+ }
+ const data = await getDataFunction(...getDataArgs)
+ if (data) {
+ console.log('[API-->>缓存]:', key)
+ await setDataToCache(key, data, customCacheTime)
+ }
+ return data || null
+}
/**
* 为减少频繁接口请求,notion数据将被缓存
@@ -20,8 +69,15 @@ export async function getDataFromCache(key, force) {
}
}
+/**
+ * 写入缓存
+ * @param {*} key
+ * @param {*} data
+ * @param {*} customCacheTime
+ * @returns
+ */
export async function setDataToCache(key, data, customCacheTime) {
- if (!data) {
+ if (!enableCacheInVercel || !data) {
return
}
// console.trace('[API-->>缓存写入]:', key)
@@ -39,8 +95,10 @@ export async function delCacheData(key) {
* 缓存实现类
* @returns
*/
-function getApi() {
- if (process.env.ENABLE_FILE_CACHE) {
+export function getApi() {
+ if (BLOG.REDIS_URL) {
+ return RedisCache
+ } else if (process.env.ENABLE_FILE_CACHE) {
return FileCache
} else {
return MemoryCache
diff --git a/lib/cache/redis_cache.js b/lib/cache/redis_cache.js
new file mode 100644
index 00000000..b35f472a
--- /dev/null
+++ b/lib/cache/redis_cache.js
@@ -0,0 +1,41 @@
+import BLOG from '@/blog.config'
+import { siteConfig } from '@/lib/config'
+import Redis from 'ioredis'
+
+export const redisClient = BLOG.REDIS_URL ? new Redis(BLOG.REDIS_URL) : {}
+
+const cacheTime = Math.trunc(
+ siteConfig('NEXT_REVALIDATE_SECOND', BLOG.NEXT_REVALIDATE_SECOND) * 1.5
+)
+
+export async function getCache(key) {
+ try {
+ const data = await redisClient.get(key)
+ return data ? JSON.parse(data) : null
+ } catch (e) {
+ console.error('redisClient读取失败 ' + e)
+ }
+}
+
+export async function setCache(key, data, customCacheTime) {
+ try {
+ await redisClient.set(
+ key,
+ JSON.stringify(data),
+ 'EX',
+ customCacheTime || cacheTime
+ )
+ } catch (e) {
+ console.error('redisClient写入失败 ' + e)
+ }
+}
+
+export async function delCache(key) {
+ try {
+ await redisClient.del(key)
+ } catch (e) {
+ console.error('redisClient删除失败 ' + e)
+ }
+}
+
+export default { getCache, setCache, delCache }
diff --git a/lib/config.js b/lib/config.js
index 4244a04e..54b043a7 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -43,6 +43,7 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
case 'AI_SUMMARY_KEY':
case 'AI_SUMMARY_CACHE_TIME':
case 'AI_SUMMARY_WORD_LIMIT':
+ case 'UUID_REDIRECT':
// LINK比较特殊,
if (key === 'LINK') {
if (!extendConfig || Object.keys(extendConfig).length === 0) {
diff --git a/lib/db/getSiteData.js b/lib/db/getSiteData.js
index 21a0d4a4..a40334b5 100755
--- a/lib/db/getSiteData.js
+++ b/lib/db/getSiteData.js
@@ -12,6 +12,7 @@ import { deepClone } from '@/lib/utils'
import { idToUuid } from 'notion-utils'
import { siteConfig } from '../config'
import { extractLangId, extractLangPrefix, getShortId } from '../utils/pageId'
+import { getOrSetDataWithCache } from '@/lib/cache/cache_manager'
export { getAllTags } from '../notion/getAllTags'
export { getPost } from '../notion/getNotionPost'
@@ -65,14 +66,15 @@ export async function getGlobalData({
*/
export async function getSiteDataByPageId({ pageId, from }) {
// 获取NOTION原始数据,此接支持mem缓存。
- const pageRecordMap = await getPage(pageId, from)
- // 将Notion数据按规则转成站点数据
- const data = await converNotionToSiteDate(
+ return await getOrSetDataWithCache(
+ `site_data_${pageId}`,
+ async (pageId, from) => {
+ const pageRecordMap = await getPage(pageId, from)
+ return convertNotionToSiteDate(pageId, from, deepClone(pageRecordMap))
+ },
pageId,
- from,
- deepClone(pageRecordMap)
+ from
)
- return data
}
/**
@@ -139,7 +141,7 @@ const EmptyData = pageId => {
* 这里统一对数据格式化
* @returns {Promise
}
*/
-async function converNotionToSiteDate(pageId, from, pageRecordMap) {
+async function convertNotionToSiteDate(pageId, from, pageRecordMap) {
if (!pageRecordMap) {
console.error('can`t get Notion Data ; Which id is: ', pageId)
return {}
@@ -273,11 +275,12 @@ async function converNotionToSiteDate(pageId, from, pageRecordMap) {
categoryOptions: getCategoryOptions(schema)
})
// 所有标签
- const tagOptions = getAllTags({
- allPages,
- tagOptions: getTagOptions(schema),
- NOTION_CONFIG
- })
+ const tagOptions =
+ getAllTags({
+ allPages,
+ tagOptions: getTagOptions(schema),
+ NOTION_CONFIG
+ }) || null
// 旧的菜单
const customNav = getCustomNav({
allPages: collectionData.filter(
diff --git a/lib/notion/getNotionAPI.js b/lib/notion/getNotionAPI.js
index c2cbb6a6..757534a3 100644
--- a/lib/notion/getNotionAPI.js
+++ b/lib/notion/getNotionAPI.js
@@ -1,10 +1,14 @@
-import { NotionAPI } from 'notion-client'
+import { NotionAPI as NotionLibrary } from 'notion-client'
import BLOG from '@/blog.config'
-export default function getNotionAPI() {
- return new NotionAPI({
+const notionAPI = getNotionAPI()
+
+function getNotionAPI() {
+ return new NotionLibrary({
activeUser: BLOG.NOTION_ACTIVE_USER || null,
authToken: BLOG.NOTION_TOKEN_V2 || null,
userTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
})
}
+
+export default notionAPI
diff --git a/lib/notion/getPageProperties.js b/lib/notion/getPageProperties.js
index 5d5e7ff6..571c1b8c 100644
--- a/lib/notion/getPageProperties.js
+++ b/lib/notion/getPageProperties.js
@@ -1,5 +1,4 @@
import BLOG from '@/blog.config'
-import { NotionAPI } from 'notion-client'
import { getDateValue, getTextContent } from 'notion-utils'
import formatDate from '../utils/formatDate'
// import { createHash } from 'crypto'
@@ -12,7 +11,7 @@ import {
} from '../utils'
import { extractLangPrefix } from '../utils/pageId'
import { mapImgUrl } from './mapImage'
-import getNotionAPI from '@/lib/notion/getNotionAPI'
+import notionAPI from '@/lib/notion/getNotionAPI'
/**
* 获取页面元素成员属性
@@ -57,12 +56,11 @@ export default async function getPageProperties(
case 'person': {
const rawUsers = val.flat()
const users = []
- const api = getNotionAPI()
for (let i = 0; i < rawUsers.length; i++) {
if (rawUsers[i][0][1]) {
const userId = rawUsers[i][0]
- const res = await api.getUsers(userId)
+ const res = await notionAPI.getUsers(userId)
const resValue =
res?.recordMapWithRoles?.notion_user?.[userId[1]]?.value
const user = {
diff --git a/lib/notion/getPostBlocks.js b/lib/notion/getPostBlocks.js
index d5896ddc..3c08f0bd 100644
--- a/lib/notion/getPostBlocks.js
+++ b/lib/notion/getPostBlocks.js
@@ -1,8 +1,7 @@
import BLOG from '@/blog.config'
-import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager'
-import { NotionAPI } from 'notion-client'
+import { getDataFromCache, getOrSetDataWithCache, setDataToCache } from '@/lib/cache/cache_manager'
import { deepClone, delay } from '../utils'
-import getNotionAPI from '@/lib/notion/getNotionAPI'
+import notionAPI from '@/lib/notion/getNotionAPI'
/**
* 获取文章内容
@@ -12,21 +11,28 @@ import getNotionAPI from '@/lib/notion/getNotionAPI'
* @returns
*/
export async function getPage(id, from = null, slice) {
- const cacheKey = `page_block_${id}`
- let pageBlock = await getDataFromCache(cacheKey)
- if (pageBlock) {
- // console.debug('[API<<--缓存]', `from:${from}`, cacheKey)
- return convertNotionBlocksToPost(id, pageBlock, slice)
- }
+ return await getOrSetDataWithCache(
+ `page_content_${id}_${slice}`,
+ async (id, slice) => {
+ const cacheKey = `page_block_${id}`
+ let pageBlock = await getDataFromCache(cacheKey)
+ if (pageBlock) {
+ // console.debug('[API<<--缓存]', `from:${from}`, cacheKey)
+ return convertNotionBlocksToPost(id, pageBlock, slice)
+ }
- // 抓取最新数据
- pageBlock = await getPageWithRetry(id, from)
+ // 抓取最新数据
+ pageBlock = await getPageWithRetry(id, from)
- if (pageBlock) {
- await setDataToCache(cacheKey, pageBlock)
- return convertNotionBlocksToPost(id, pageBlock, slice)
- }
- return pageBlock
+ if (pageBlock) {
+ await setDataToCache(cacheKey, pageBlock)
+ return convertNotionBlocksToPost(id, pageBlock, slice)
+ }
+ return pageBlock
+ },
+ id,
+ slice
+ )
}
/**
@@ -43,9 +49,8 @@ export async function getPageWithRetry(id, from, retryAttempts = 3) {
retryAttempts < 3 ? `剩余重试次数:${retryAttempts}` : ''
)
try {
- const api = getNotionAPI()
const start = new Date().getTime()
- const pageData = await api.getPage(id)
+ const pageData = await notionAPI.getPage(id)
const end = new Date().getTime()
console.log('[API<<--响应]', `耗时:${end - start}ms - from:${from}`)
return pageData
@@ -163,14 +168,12 @@ export const fetchInBatches = async (ids, batchSize = 100) => {
ids = [ids]
}
- const api = getNotionAPI()
-
let fetchedBlocks = {}
for (let i = 0; i < ids.length; i += batchSize) {
const batch = ids.slice(i, i + batchSize)
console.log('[API-->>请求] Fetching missing blocks', batch, ids.length)
const start = new Date().getTime()
- const pageChunk = await api.getBlocks(batch)
+ const pageChunk = await notionAPI.getBlocks(batch)
const end = new Date().getTime()
console.log(
`[API<<--响应] 耗时:${end - start}ms Fetching missing blocks count:${ids.length} `
diff --git a/lib/redirect.js b/lib/redirect.js
new file mode 100644
index 00000000..7a05dfee
--- /dev/null
+++ b/lib/redirect.js
@@ -0,0 +1,15 @@
+import fs from 'fs'
+
+export function generateRedirectJson({ allPages }) {
+ let uuidSlugMap = {}
+ allPages.forEach(page => {
+ if (page.type === 'Post' && page.status === 'Published') {
+ uuidSlugMap[page.id] = page.slug
+ }
+ })
+ try {
+ fs.writeFileSync('./public/redirect.json', JSON.stringify(uuidSlugMap))
+ } catch (error) {
+ console.warn('无法写入文件', error)
+ }
+}
diff --git a/middleware.ts b/middleware.ts
index dc623d08..550c6f8b 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -1,5 +1,8 @@
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
-import { NextResponse } from 'next/server'
+import { NextRequest, NextResponse } from 'next/server'
+import { checkStrIsNotionId, getLastPartOfUrl } from '@/lib/utils'
+import { idToUuid } from 'notion-utils'
+import BLOG from './blog.config'
/**
* Clerk 身份验证中间件
@@ -30,8 +33,31 @@ const isTenantAdminRoute = createRouteMatcher([
* @returns
*/
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
-const noAuthMiddleware = async (req: any, ev: any) => {
+const noAuthMiddleware = async (req: NextRequest, ev: any) => {
// 如果没有配置 Clerk 相关环境变量,返回一个默认响应或者继续处理请求
+ if (BLOG['UUID_REDIRECT']) {
+ let redirectJson: Record = {}
+ try {
+ const response = await fetch(`${req.nextUrl.origin}/redirect.json`)
+ if (response.ok) {
+ redirectJson = (await response.json()) as Record
+ }
+ } catch (err) {
+ console.error('Error fetching static file:', err)
+ }
+ let lastPart = getLastPartOfUrl(req.nextUrl.pathname) as string
+ if (checkStrIsNotionId(lastPart)) {
+ lastPart = idToUuid(lastPart)
+ }
+ if (lastPart && redirectJson[lastPart]) {
+ const redirectToUrl = req.nextUrl.clone()
+ redirectToUrl.pathname = '/' + redirectJson[lastPart]
+ console.log(
+ `redirect from ${req.nextUrl.pathname} to ${redirectToUrl.pathname}`
+ )
+ return NextResponse.redirect(redirectToUrl, 308)
+ }
+ }
return NextResponse.next()
}
/**
diff --git a/package.json b/package.json
index 85dff78c..f9d7ec20 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "notion-next",
- "version": "4.8.0",
+ "version": "4.8.1",
"homepage": "https://github.com/tangly1024/NotionNext.git",
"license": "MIT",
"repository": {
@@ -31,6 +31,7 @@
"algoliasearch": "^4.18.0",
"axios": "^1.7.2",
"feed": "^4.2.2",
+ "ioredis": "^5.4.2",
"js-md5": "^0.7.3",
"lodash.throttle": "^4.1.1",
"memory-cache": "^0.2.0",
diff --git a/pages/index.js b/pages/index.js
index 31e2610b..b66b5610 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -5,6 +5,7 @@ import { generateRobotsTxt } from '@/lib/robots.txt'
import { generateRss } from '@/lib/rss'
import { generateSitemapXml } from '@/lib/sitemap.xml'
import { DynamicLayout } from '@/themes/theme'
+import { generateRedirectJson } from '@/lib/redirect'
/**
* 首页布局
@@ -60,6 +61,10 @@ export async function getStaticProps(req) {
generateRss(props)
// 生成
generateSitemapXml(props)
+ if (siteConfig('UUID_REDIRECT', false, props?.NOTION_CONFIG)) {
+ // 生成重定向 JSON
+ generateRedirectJson(props)
+ }
// 生成全文索引 - 仅在 yarn build 时执行 && process.env.npm_lifecycle_event === 'build'
diff --git a/public/css/spoiler-text.css b/public/css/spoiler-text.css
index 1dcd045a..ba68870b 100644
--- a/public/css/spoiler-text.css
+++ b/public/css/spoiler-text.css
@@ -1,15 +1,19 @@
-/* Spoiler text styles */
+/* Spoiler text styles with sharp edges */
.spoiler-text {
color: transparent; /* 文字透明 */
- background-color: #808080; /* 背景为黑色 */
- border-color: #808080;
- text-decoration-color: #808080;
- text-emphasis-color: #808080;
- border-radius: 8px;
- filter: blur(1px); /* 初始模糊 */
+ background-color: #000000; /* 背景为黑色 */
+ border-color: #000000; /* 边框颜色 */
+ text-decoration-color: #000000; /* 删除线颜色 */
+ text-emphasis-color: #000000; /* 强调文字颜色 */
+ filter: none; /* 移除模糊效果 */
--hide-transition: 0.3s ease-out;
- transition: opacity var(--hide-transition),
- filter var(--hide-transition);
+ transition:
+ color var(--hide-transition),
+ background-color var(--hide-transition),
+ border-color var(--hide-transition),
+ text-decoration-color var(--hide-transition),
+ text-emphasis-color var(--hide-transition),
+ opacity 0.35s cubic-bezier(.25,.46,.45,.94); /* 平滑过渡 */
}
.spoiler-text:hover {
@@ -19,5 +23,26 @@
text-decoration-color: inherit;
text-emphasis-color: inherit;
opacity: 1; /* 鼠标悬停时恢复不透明度 */
- filter: blur(0); /* 鼠标悬停时解除模糊 */
-}
\ No newline at end of file
+}
+
+/* Spoiler child elements with transition */
+.spoiler-text * {
+ transition: opacity 0.35s cubic-bezier(.25,.46,.45,.94); /* 子元素透明度平滑过渡 */
+}
+
+/* Spoiler when not hovered */
+.spoiler-text:not(:hover) {
+ color: transparent!important; /* 非悬停时文字透明 */
+ background-color: #000000!important; /* 非悬停时背景为黑色 */
+ border-color: #000000!important; /* 非悬停时边框为黑色 */
+}
+
+.spoiler-text:not(:hover) * {
+ opacity: 0!important; /* 非悬停时子元素透明 */
+}
+
+/* Remove border in non-hover states */
+.spoiler-text:not(:hover),
+.spoiler-text:not(:hover) * {
+ border: none!important;
+}
diff --git a/themes/starter/components/CTA.js b/themes/starter/components/CTA.js
index 643a0d48..2bf97e76 100644
--- a/themes/starter/components/CTA.js
+++ b/themes/starter/components/CTA.js
@@ -26,7 +26,7 @@ export const CTA = () => {
- {siteConfig('STARTER_CTA_DESCRIOTN')}
+ {siteConfig('STARTER_CTA_DESCRIPTION')}
{siteConfig('STARTER_CTA_BUTTON') && (
<>
diff --git a/themes/starter/config.js b/themes/starter/config.js
index 39ea4773..520e4d92 100644
--- a/themes/starter/config.js
+++ b/themes/starter/config.js
@@ -364,7 +364,7 @@ const CONFIG = {
STARTER_CTA_ENABLE: true,
STARTER_CTA_TITLE: '你还在等待什么呢?',
STARTER_CTA_TITLE_2: '现在开始吧',
- STARTER_CTA_DESCRIOTN:
+ STARTER_CTA_DESCRIPTION:
'访问NotionNext的操作文档,我们提供了详细的教程,帮助你即刻搭建站点',
STARTER_CTA_BUTTON: true, // 是否显示按钮
STARTER_CTA_BUTTON_URL:
diff --git a/themes/theme.js b/themes/theme.js
index 97fcd64a..3d89f878 100644
--- a/themes/theme.js
+++ b/themes/theme.js
@@ -6,7 +6,7 @@ import { useRouter } from 'next/router'
import { getQueryParam, getQueryVariable, isBrowser } from '../lib/utils'
// 在next.config.js中扫描所有主题
-export const { THEMES = [] } = getConfig().publicRuntimeConfig
+export const { THEMES = [] } = getConfig()?.publicRuntimeConfig || {}
/**
* 获取主题配置
diff --git a/yarn.lock b/yarn.lock
index 9ee525b4..74fd3138 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -299,6 +299,11 @@
resolved "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
+"@ioredis/commands@^1.1.1":
+ version "1.2.0"
+ resolved "https://mirrors.cloud.tencent.com/npm/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11"
+ integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==
+
"@isaacs/cliui@^8.0.2":
version "8.0.2"
resolved "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
@@ -1238,6 +1243,11 @@ clone-response@^1.0.2:
dependencies:
mimic-response "^1.0.0"
+cluster-key-slot@^1.1.0:
+ version "1.1.2"
+ resolved "https://mirrors.cloud.tencent.com/npm/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
+ integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==
+
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
@@ -1443,6 +1453,11 @@ delayed-stream@~1.0.0:
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+denque@^2.1.0:
+ version "2.1.0"
+ resolved "https://mirrors.cloud.tencent.com/npm/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
+ integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
+
didyoumean@^1.2.2:
version "1.2.2"
resolved "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
@@ -2404,6 +2419,21 @@ invariant@^2.2.4:
dependencies:
loose-envify "^1.0.0"
+ioredis@^5.4.2:
+ version "5.4.2"
+ resolved "https://mirrors.cloud.tencent.com/npm/ioredis/-/ioredis-5.4.2.tgz#ebb6f1a10b825b2c0fb114763d7e82114a0bee6c"
+ integrity sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg==
+ dependencies:
+ "@ioredis/commands" "^1.1.1"
+ cluster-key-slot "^1.1.0"
+ debug "^4.3.4"
+ denque "^2.1.0"
+ lodash.defaults "^4.2.0"
+ lodash.isarguments "^3.1.0"
+ redis-errors "^1.2.0"
+ redis-parser "^3.0.0"
+ standard-as-callback "^2.1.0"
+
is-array-buffer@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98"
@@ -2770,6 +2800,16 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash.defaults@^4.2.0:
+ version "4.2.0"
+ resolved "https://mirrors.cloud.tencent.com/npm/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+ integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
+
+lodash.isarguments@^3.1.0:
+ version "3.1.0"
+ resolved "https://mirrors.cloud.tencent.com/npm/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
+ integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==
+
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@@ -3604,6 +3644,18 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
+redis-errors@^1.0.0, redis-errors@^1.2.0:
+ version "1.2.0"
+ resolved "https://mirrors.cloud.tencent.com/npm/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad"
+ integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==
+
+redis-parser@^3.0.0:
+ version "3.0.0"
+ resolved "https://mirrors.cloud.tencent.com/npm/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4"
+ integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==
+ dependencies:
+ redis-errors "^1.0.0"
+
reflect.getprototypeof@^1.0.4, reflect.getprototypeof@^1.0.6:
version "1.0.8"
resolved "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz#c58afb17a4007b4d1118c07b92c23fca422c5d82"
@@ -3917,6 +3969,11 @@ stacktrace-js@^2.0.2:
stack-generator "^2.0.5"
stacktrace-gps "^3.0.4"
+standard-as-callback@^2.1.0:
+ version "2.1.0"
+ resolved "https://mirrors.cloud.tencent.com/npm/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
+ integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==
+
std-env@^3.7.0:
version "3.8.0"
resolved "https://registry.npmmirror.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5"
@@ -3927,16 +3984,7 @@ streamsearch@^1.1.0:
resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
-"string-width-cjs@npm:string-width@^4.2.0":
- version "4.2.3"
- resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
-string-width@^4.1.0:
+"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
version "4.2.3"
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -4017,14 +4065,7 @@ string.prototype.trimstart@^1.0.8:
define-properties "^1.2.1"
es-object-atoms "^1.0.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
- version "6.0.1"
- resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==