diff --git a/.github/workflows/scraping.yml b/.github/workflows/scraping.yml deleted file mode 100644 index 87546e9..0000000 --- a/.github/workflows/scraping.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: update data.json - -on: - workflow_dispatch: - schedule: - - cron: "0 * * * *" - -jobs: - scrape_and_save: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Run scraping script - env: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - DATABASE_ID: ${{ secrets.DATABASE_ID }} - run: | - python scripts/scrape.py - - name: Commit data.json - run: | - git diff --quiet --exit-code public/assets/data.json || ( - git config --global user.name "NowScott" - git config --global user.email "nowscott@qq.com" - git add public/assets/data.json - DATE=$(TZ="Asia/Shanghai" date "+%Y-%m-%d %H:%M:%S") - git commit -m "Update data.json at $DATE" - git push origin main - ) - - diff --git a/.gitignore b/.gitignore index d3e06cd..df6cc99 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,99 @@ -.env -.DS_store -node_modules -package-lock.json \ No newline at end of file +# Node.js 相关 +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# 日志文件 +logs +*.log +pids +*.pid +*.seed +*.pid.lock + +# 运行时数据 +lib-cov +coverage/ +*.lcov + +# jscoverage/JSCover 生成的目录 +lib-cov + +# istanbul 使用的覆盖率目录 +coverage/ + +# nyc 测试覆盖率输出目录 +.nyc_output/ + +# Grunt 中间存储目录(https://gruntjs.com/creating-plugins#storing-task-files) +.grunt/ + +# Bower 依赖目录(https://bower.io/) +bower_components/ + +# 编译的二进制扩展(https://nodejs.org/api/addons.html) +build/Release + +# 依赖目录 +jspm_packages/ + +# Next.js 构建输出 +.next/ + +# dotenv 环境变量文件 +.env.local +.env.development.local +.env.test.local +.env.production.local +.env + +# Next.js 生成的文件 +*.next +out/ +.next/ +out/ +build/ +dist/ + +# macOS 特定文件 +.DS_Store +.AppleDouble +.LSOverride + +# 图标文件必须以两个回车结尾 +Icon\r\r + +# 缩略图 +._* + +# 可能出现在卷根目录下的文件 +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# 可能在远程 AFP 共享上创建的目录 +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# 编辑器和 IDE 相关 +.idea/ +.vscode/ +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# TypeScript 缓存文件 +*.tsbuildinfo + +# 可选的 npm 缓存目录 +.npm diff --git a/lib/contextMenu.js b/lib/contextMenu.js new file mode 100644 index 0000000..dfd0371 --- /dev/null +++ b/lib/contextMenu.js @@ -0,0 +1,110 @@ +const fontData = { + "fonts": [ + { + "name": "Smiley Sans Oblique", + "displayName": "得意黑" + }, + { + "name": "LXGW WenKai", + "displayName": "霞鹜文楷" + }, + { + "name": "KingHwa_OldSong", + "displayName": "京華老宋体" + }, + { + "name": "MuzaiPixel", + "displayName": "目哉像素体" + }, + { + "name": "LXGW Marker Gothic", + "displayName": "霞鹜漫黑" + } + ] + }; + + export const populateContextMenu = () => { + const menu = document.getElementById('customContextMenu').querySelector('ul'); + menu.innerHTML = ''; // 清空现有的菜单项,防止重复添加 + + fontData.fonts.forEach((font) => { + let li = document.createElement('li'); + li.textContent = font.displayName; // 显示中文字体名 + li.style.cursor = 'pointer'; + li.style.fontFamily = font.name; // 设置每个字体选项的字体样式 + li.onclick = () => { + changeFont(font.name); // 调用更改字体的函数 + }; + menu.appendChild(li); + }); + }; + + export const changeFont = (font) => { + document.documentElement.style.setProperty('--main-font-family', `"${font}"`); + setCookie('userFont', font, 7); // 更新Cookie,持续7天 + updateMenuSelection(font); // 更新菜单选择状态 + document.getElementById('customContextMenu').style.display = 'none'; // 隐藏菜单 + }; + + const updateMenuSelection = (selectedFont) => { + const items = document.querySelectorAll('#customContextMenu li'); + items.forEach(item => { + if (item.textContent.trim() === fontData.fonts.find(f => f.name === selectedFont).displayName) { // 使用正确的友好名称进行比较 + item.classList.add('selected-font'); + } else { + item.classList.remove('selected-font'); + } + }); + }; + + const setCookie = (name, value, days) => { + var expires = ""; + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toUTCString(); + } + document.cookie = name + "=" + (value || "") + expires + "; path=/"; + }; + + const getCookie = (name) => { + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); + } + return null; + }; + + const loadFont = () => { + var userFont = getCookie('userFont'); + if (userFont) { + document.documentElement.style.setProperty('--main-font-family', userFont); + updateMenuSelection(userFont); // 更新菜单选择状态 + } + }; + + export const initializeContextMenu = () => { + populateContextMenu(); // 填充右键菜单 + loadFont(); // 加载用户之前选择的字体 + + // 处理右键菜单事件 + document.addEventListener('contextmenu', function (event) { + event.preventDefault(); + var contextMenu = document.getElementById('customContextMenu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = event.pageX + 'px'; + contextMenu.style.top = event.pageY + 'px'; + }); + + // 处理点击事件,隐藏自定义的右键菜单 + document.addEventListener('click', function (event) { + var contextMenu = document.getElementById('customContextMenu'); + if (event.target.offsetParent !== contextMenu) { + contextMenu.style.display = 'none'; + } + }); + }; + \ No newline at end of file diff --git a/lib/dataLoader.js b/lib/dataLoader.js new file mode 100644 index 0000000..3ed58fe --- /dev/null +++ b/lib/dataLoader.js @@ -0,0 +1,80 @@ +// 随机排序 +export const randomSort = arr => { + for (let i = 0, l = arr.length; i < l; i++) { + let rc = parseInt(Math.random() * l); + const empty = arr[i]; + arr[i] = arr[rc]; + arr[rc] = empty; + } + return arr; +}; +// 去重 +export const unique = arr => Array.from(new Set(arr)); +// 提取tags +export const extractTags = posts => { + const allTags = new Set(); + posts.forEach(post => { + if (post.state !== '隐藏') {//避免隐藏网页的tag被写入 + post.tags.forEach(tag => allTags.add(tag)); + } + }); + return [...allTags]; +}; + +// 通过tags筛选 +export const filterPostsByTags = (posts, tags) => { + return posts.filter(post => tags.every(tag => post.tags.includes(tag))); +}; + +//通过搜索筛选 +export const filterPostsBySearch = (posts, query) => { + if(query === '隐藏'){ + return posts + } + // 返回符合查询条件的帖子 + return posts.filter(post => + post.name.toLowerCase().includes(query.toLowerCase()) || + post.brief.toLowerCase().includes(query.toLowerCase()) || + post.tags.some(tag => tag.toLowerCase().includes(query.toLowerCase())) + ); +}; + + +export const renderTags = (tags, onList, toggleTagButton) => { + return tags.map((tag, index) => ( + + )); +}; + +export const toggleTagButton = (tag, onList, setOnList, tags, setTags) => { + let newOnList; + if (onList.includes(tag)) { + newOnList = onList.filter(item => item !== tag); + } else { + newOnList = [...onList, tag]; + } + setOnList(newOnList); + // 重新排序标签列表,使选中的标签排在前面 + const sortedTags = newOnList.concat(tags.filter(t => !newOnList.includes(t))); + setTags(sortedTags); +}; + +export const updateResults = (posts, onList, setFilteredPosts, setTags) => { + const filtered = posts.filter(post => + onList.every(tag => post.tags.includes(tag)) + ); + setFilteredPosts(filtered); + // 更新标签列表,只显示可选的标签 + const availableTags = Array.from(new Set(filtered.flatMap(post => post.tags))); + const sortedTags = onList.concat(availableTags.filter(tag => !onList.includes(tag))); + setTags(sortedTags); +}; + + diff --git a/lib/notion.js b/lib/notion.js new file mode 100644 index 0000000..37674a5 --- /dev/null +++ b/lib/notion.js @@ -0,0 +1,50 @@ +import { Client } from '@notionhq/client'; + +// 创建 Notion 客户端实例,使用环境变量中的 NOTION_TOKEN 进行身份验证 +const notion = new Client({ auth: process.env.NOTION_TOKEN }); + +// 获取指定数据库的所有页面 +export const getAllPages = async (databaseId) => { + let results = []; // 存储所有页面的数组 + let hasMore = true; // 用于迭代查询,检查是否还有更多页面需要获取 + let startCursor = undefined; // 用于获取下一页的游标 + + // 当仍有更多页面需要获取时循环执行 + while (hasMore) { + // 查询数据库的一页数据 + const response = await notion.databases.query({ + database_id: databaseId, + start_cursor: startCursor, + }); + + // 将当前页的结果追加到结果数组中 + results = results.concat(response.results); + // 更新是否还有更多页面的标志 + hasMore = response.has_more; + // 更新下一页的游标,以便下次查询时获取下一页数据 + startCursor = response.next_cursor; + } + + return results; // 返回所有页面的数组 +}; + +// 获取指定数据库的页面并转换为适合显示的格式 +export const getDatabase = async (databaseId) => { + // 获取指定数据库的所有页面 + const pages = await getAllPages(databaseId); + // 将页面格式化为适合显示的格式 + return pages + .filter(page => page.properties.empty.formula.string !== 'empty') // 过滤掉空的页面 + .filter(page => page.properties.state.select.name !== '断开连接') // 过滤掉状态为 "断开连接" 的页面 + .map(page => { + const properties = page.properties; + return { + id: page.id, // 页面的 ID + name: properties.name.title[0].plain_text, // 页面的名称 + state: properties.state.select.name, // 页面的状态 + brief: properties.brief.rich_text[0].plain_text, // 页面的简介 + tags: properties.tags.multi_select.map(tag => tag.name), // 页面的标签数组 + web: properties.web.url, // 页面的网址 + }; + }); +}; \ No newline at end of file diff --git a/lib/theme.js b/lib/theme.js new file mode 100644 index 0000000..50f1b7c --- /dev/null +++ b/lib/theme.js @@ -0,0 +1,31 @@ +export const applyTheme = (theme) => { + const dark_mode = document.getElementById('darkcss'); + const dark_mode_icon = document.getElementById('icon'); + const dark_mode_btn = document.getElementById('darkbtn'); + + if (theme === 'dark') { + dark_mode.setAttribute('href', '/css/dark.css'); + dark_mode_icon.src = '/assets/svg/sun.svg'; + dark_mode_btn.className = 'dark'; + console.log('切换为深色模式'); + } else { + dark_mode.setAttribute('href', '/css/daytime.css'); + dark_mode_icon.src = '/assets/svg/moon.svg'; + dark_mode_btn.className = 'daytime'; + console.log('切换为浅色模式'); + } +}; + +export const initializeTheme = () => { + const dark_mode_btn = document.getElementById('darkbtn'); + const currentHour = new Date().getHours(); + const theme = (currentHour >= 22 || currentHour < 8) ? 'dark' : 'daytime'; + + applyTheme(theme); + + dark_mode_btn.onclick = () => { + const currentTheme = dark_mode_btn.className; + const newTheme = (currentTheme === 'daytime') ? 'dark' : 'daytime'; + applyTheme(newTheme); + }; +}; diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..5a06939 --- /dev/null +++ b/next.config.js @@ -0,0 +1,9 @@ +module.exports = { + env: { + NOTION_TOKEN: process.env.NOTION_TOKEN, + DATABASE_ID: process.env.DATABASE_ID, + }, + future: { + webpack5: true, + }, +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..151f2a5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,528 @@ +{ + "name": "my-vercel-notion-app", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "my-vercel-notion-app", + "version": "1.0.0", + "dependencies": { + "@notionhq/client": "^0.4.11", + "next": "latest", + "react": "latest", + "react-dom": "latest" + } + }, + "node_modules/@next/env": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@notionhq/client": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/@notionhq/client/-/client-0.4.13.tgz", + "integrity": "sha512-tHC95h4JZYteHmIL49xdta0Yb9q0peXySqo9cqTQEVzpPwUdcPNgWQljMqfrSVKqlpGNX+DhXztY0LR6f3Iw6A==", + "dependencies": { + "@types/node-fetch": "^2.5.10", + "node-fetch": "^2.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001620", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", + "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", + "dependencies": { + "@next/env": "14.2.3", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/package.json b/package.json index 773253a..d9c362e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,16 @@ -{ - "dependencies": { - "axios": "^1.4.0" - } -} +{ + "name": "my-vercel-notion-app", + "version": "1.0.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@notionhq/client": "^0.4.11", + "next": "latest", + "react": "latest", + "react-dom": "latest" + } + } + \ No newline at end of file diff --git a/pages/_document.js b/pages/_document.js new file mode 100644 index 0000000..409574d --- /dev/null +++ b/pages/_document.js @@ -0,0 +1,22 @@ +import Document, { Html, Head, Main, NextScript } from 'next/document'; + +class MyDocument extends Document { + render() { + return ( + + + + + + + + +
+ + + + ); + } +} + +export default MyDocument; diff --git a/pages/index.js b/pages/index.js new file mode 100644 index 0000000..3185024 --- /dev/null +++ b/pages/index.js @@ -0,0 +1,140 @@ +import Head from 'next/head'; +import { getDatabase } from '../lib/notion'; +import { useEffect, useState } from 'react'; +import { + randomSort, + unique, + extractTags, + filterPostsBySearch, + toggleTagButton, + updateResults, +} from '../lib/dataLoader'; +import { initializeTheme } from '../lib/theme'; +import { initializeContextMenu } from '../lib/contextMenu'; + +export default function Home({ initialPosts, lastFetched }) { + const [posts, setPosts] = useState(initialPosts); + const [normalPosts, setNormalPosts] = useState([]); + const [hiddenPosts, setHiddenPosts] = useState([]); + const [searchQuery, setSearchQuery] = useState(''); + const [tags, setTags] = useState([]); + const [onList, setOnList] = useState([]); + const [filteredPosts, setFilteredPosts] = useState(initialPosts); + + useEffect(() => { + initializeTheme(); + initializeContextMenu(); + console.log(`数据更新时间: ${new Date(lastFetched).toLocaleString()}`); + // 使用 setPosts 更新状态 + setPosts(initialPosts); + }, [initialPosts, lastFetched]); + + useEffect(() => { + const filteredNormalPosts = initialPosts.filter(post => post.state !== '隐藏'); + const filteredHiddenPosts = initialPosts.filter(post => post.state === '隐藏'); + setNormalPosts(filteredNormalPosts); + setHiddenPosts(filteredHiddenPosts); + }, [initialPosts]); + + useEffect(() => { + const extractedTags = randomSort(extractTags(normalPosts)); + setTags(extractedTags); + }, [normalPosts]); + + useEffect(() => { + if(searchQuery === '隐藏'){ + setFilteredPosts(filterPostsBySearch(hiddenPosts, searchQuery)); + }else{ + setFilteredPosts(filterPostsBySearch(normalPosts, searchQuery)); + } + }, [searchQuery, normalPosts, hiddenPosts]); + + useEffect(() => { + updateResults(posts, onList, setFilteredPosts, setTags); + }, [onList]); + + const handleToggleTagButton = tag => { + toggleTagButton(tag, onList, setOnList, tags, setTags); + }; + + const searchFunction = () => { + const keyword = searchQuery.toLowerCase(); + const reslist = filterPostsBySearch(posts, keyword); + setFilteredPosts(reslist.length > 0 ? reslist : []); + }; + + return ( +
+ + 网站索引 + + + + + +
+
    +
    + +
    +

    + + Individual Web Index. + +

    +

    + + 如何部署 + +

    +
    + setSearchQuery(e.target.value)} /> + +
    +

    选择标签

    +
    + {tags.map(tag => ( + + ))} +
    +

    筛选网页

    +
    + {filteredPosts.length > 0 ? ( + filteredPosts.map(post => ( + + {post.name} + + )) + ) : ( +

    未找到符合条件的网页

    + )} +
    +

    Copyright © 2021 - NowScott

    +
    +
    + ); +} + +export async function getStaticProps() { + const databaseId = process.env.DATABASE_ID; + const posts = await getDatabase(databaseId); + // 获取数据的时间点 + const lastFetched = new Date().toISOString(); + // 排序和去重操作在服务器端执行,以确保一致性 + const sortedPosts = randomSort(unique(posts)); + return { + props: { + initialPosts: sortedPosts, + lastFetched + }, + revalidate: 3600, // 每小时重新生成静态页面 + }; +} diff --git a/public/assets/data.json b/public/assets/data.json deleted file mode 100644 index 489ede3..0000000 --- a/public/assets/data.json +++ /dev/null @@ -1,1910 +0,0 @@ -[ - { - "name": "数学求解", - "web": "https://zs.symbolab.com/", - "tags": [ - "🧠学习" - ], - "brief": "Symbolab:方程搜索和数学求解器 - 一步步求解代数,三角函数和微积分问题", - "state": "正常" - }, - { - "name": "Poe", - "web": "https://poe.com/", - "tags": [ - "🤖AI" - ], - "brief": "Poe可让您提问、获得即时回答、并与AI机器人互动对话。", - "state": "正常" - }, - { - "name": "开发文档", - "web": "https://wangdoc.com/", - "tags": [ - "🐱Code", - "📒笔记", - "🧠学习" - ], - "brief": "提供高质量的、自主版权的、可以自由使用的中文软件文档", - "state": "正常" - }, - { - "name": "矩阵计算", - "web": "https://m.matrix.reshish.com/zh/", - "tags": [ - "🧠学习" - ], - "brief": "最为便捷的在线矩阵计算器", - "state": "正常" - }, - { - "name": "即刻", - "web": "https://web.okjike.com/", - "tags": [ - "📱数码", - "👁️‍🗨️媒体" - ], - "brief": "与更多同好分享你的见闻与感受,每一个独到的声音,都值得被更多人倾听。", - "state": "正常" - }, - { - "name": "Telegraph", - "web": "https://telegra.ph/", - "tags": [ - "📒笔记" - ], - "brief": "匿名发布文章", - "state": "正常" - }, - { - "name": "荔枝商店", - "web": "https://lizhi.shop/", - "tags": [ - "📱数码", - "🧰软件", - "🛒购物" - ], - "brief": "数码荔枝 x 软件商店 - 专注于分享最新鲜优秀的正版软件", - "state": "正常" - }, - { - "name": "云盘资源", - "web": "https://www.aliyunpanziyuan.com/", - "tags": [ - "📦资源", - "🔍搜索" - ], - "brief": "阿里云盘资源网-网盘资源共享平台", - "state": "正常" - }, - { - "name": "Sudoku", - "web": "https://sudoku.nowscott.top/", - "tags": [ - "🕹️游戏" - ], - "brief": "基于React的Sudoku", - "state": "正常" - }, - { - "name": "渐变色", - "web": "https://uigradients.com/", - "tags": [ - "🪣色彩" - ], - "brief": "提供上百种渐变色", - "state": "正常" - }, - { - "name": "日本色", - "web": "https://nipponcolors.com/", - "tags": [ - "🪣色彩" - ], - "brief": "日本传统颜色", - "state": "正常" - }, - { - "name": "中文网字", - "web": "https://chinese-font.netlify.app/", - "tags": [ - "📦资源", - "🐱Code" - ], - "brief": "收集免费可商用的 Web 字体文件,字体分包工具", - "state": "正常" - }, - { - "name": "IT之家", - "web": "https://www.ithome.com/", - "tags": [ - "🗞️新闻", - "📱数码" - ], - "brief": "青岛软媒旗下的前沿科技门户网站", - "state": "正常" - }, - { - "name": "OpenArt", - "web": "https://openart.ai/home", - "tags": [ - "🖼️图片", - "🤖AI" - ], - "brief": "AI艺术生成器", - "state": "正常" - }, - { - "name": "7ED", - "web": "https://www.7ed.net/", - "tags": [ - "🕸️网络", - "🔧工具" - ], - "brief": "包含静态资源 CDN 加速服务、Git 资源加速服务、海外图片内地加速服务、Bing 每日图片 API等。", - "state": "正常" - }, - { - "name": "文心一言", - "web": "https://yiyan.baidu.com/", - "tags": [ - "🤖AI" - ], - "brief": "百度的大语言模型", - "state": "正常" - }, - { - "name": "通义千问", - "web": "https://tongyi.aliyun.com/qianwen/", - "tags": [ - "🤖AI" - ], - "brief": "阿里做的大语言模型助手", - "state": "正常" - }, - { - "name": "Buzzing", - "web": "https://www.buzzing.cc/", - "tags": [ - "🗞️新闻" - ], - "brief": "用中文浏览国外社交媒体里的热门讨论,母语快速导读, 感兴趣再进原文深度阅读", - "state": "正常" - }, - { - "name": "SpeedTest", - "web": "https://speed.cloudflare.com/", - "tags": [ - "🕸️网络", - "🔧工具" - ], - "brief": "Cloudflare的测速工具", - "state": "正常" - }, - { - "name": "网站图标", - "web": "https://fontawesome.com/", - "tags": [ - "♻︎图标", - "🐱Code" - ], - "brief": "一些图标", - "state": "正常" - }, - { - "name": "在线工具", - "web": "https://tool.lu/", - "tags": [ - "🔧工具" - ], - "brief": "有很多各式各样的在线工具", - "state": "正常" - }, - { - "name": "抖音", - "web": "https://www.douyin.com/", - "tags": [ - "⛹️‍♀️娱乐", - "👁️‍🗨️媒体" - ], - "brief": "抖音 - 记录美好生活", - "state": "正常" - }, - { - "name": "小红书", - "web": "https://www.xiaohongshu.com/explore", - "tags": [ - "👁️‍🗨️媒体", - "⛹️‍♀️娱乐" - ], - "brief": "小红书 - 你的生活指南", - "state": "正常" - }, - { - "name": "GropAI", - "web": "https://groq.com/", - "tags": [ - "🤖AI" - ], - "brief": "人工智能接口系统", - "state": "正常" - }, - { - "name": "HuggingChat", - "web": "https://huggingface.co/chat/", - "tags": [ - "🤖AI" - ], - "brief": "让社区最好的AI聊天模型面向所有人开放。", - "state": "正常" - }, - { - "name": "站长之家", - "web": "https://tool.chinaz.com/", - "tags": [ - "🔧工具", - "🕸️网络" - ], - "brief": "提供各种查询工具", - "state": "正常" - }, - { - "name": "豆包AI", - "web": "https://www.doubao.com/chat/", - "tags": [ - "🤖AI" - ], - "brief": "抖音旗下的AI助手", - "state": "正常" - }, - { - "name": "敲字背单词", - "web": "https://qwerty.kaiyi.cool/", - "tags": [ - "🧠学习", - "⛹️‍♀️娱乐" - ], - "brief": "为键盘工作者设计的单词记忆与英语肌肉记忆锻炼软件", - "state": "正常" - }, - { - "name": "免费歌曲", - "web": "https://tool.liumingye.cn/music/#/", - "tags": [ - "🎵音频" - ], - "brief": "免费下载歌曲", - "state": "正常" - }, - { - "name": "牛客网", - "web": "https://www.nowcoder.com/", - "tags": [ - "🐱Code", - "🧠学习" - ], - "brief": "找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决", - "state": "正常" - }, - { - "name": "秒搜", - "web": "https://miaosou.fun/", - "tags": [ - "🔍搜索", - "📦资源" - ], - "brief": "一个很好用的网盘资源搜索引擎", - "state": "正常" - }, - { - "name": "在线测试", - "web": "https://www.onlinemictest.com/", - "tags": [ - "🔧工具" - ], - "brief": "提供各种测试工具", - "state": "正常" - }, - { - "name": "开源程序书", - "web": "https://github.com/EbookFoundation/free-programming-books/blob/main/books/free-programming-books-zh.md", - "tags": [ - "🐱Code", - "🧠学习" - ], - "brief": "一些开源的程序书籍", - "state": "正常" - }, - { - "name": "SOC-PK", - "web": "https://www.socpk.com/", - "tags": [ - "📱数码" - ], - "brief": "极客湾做的移动芯片排行", - "state": "正常" - }, - { - "name": "BibiGPT", - "web": "https://b.jimmylv.cn/", - "tags": [ - "🤖AI", - "📒笔记", - "📺视频" - ], - "brief": "一键总结音视频", - "state": "正常" - }, - { - "name": "深言达意", - "web": "https://shenyandayi.com/", - "tags": [ - "🤖AI", - "🧠学习" - ], - "brief": "清华开发的根据句子找词的工具,基于AI模型", - "state": "正常" - }, - { - "name": "图形计算器", - "web": "https://www.desmos.com/calculator?lang=zh-CN", - "tags": [ - "🧠学习", - "🖼️图片" - ], - "brief": "绘制函数图形并进行可视化", - "state": "正常" - }, - { - "name": "B站封面", - "web": "https://bilicover.magecorn.com/", - "tags": [ - "🖼️图片", - "🔧工具" - ], - "brief": "提取B站视频封面", - "state": "正常" - }, - { - "name": "LaTeX公式", - "web": " https://www.latexlive.com/", - "tags": [ - "🐱Code", - "📒笔记" - ], - "brief": "在线编辑LaTeX公式的工具", - "state": "正常" - }, - { - "name": "Notion头像", - "web": "https://notion-avatar.vercel.app/zh", - "tags": [ - "✒️设计", - "🖼️图片" - ], - "brief": "制作notion风格的头像", - "state": "正常" - }, - { - "name": "Gemini", - "web": "https://gemini.google.com/", - "tags": [ - "🤖AI" - ], - "brief": "谷歌人工智能的一个大型语言模型", - "state": "正常" - }, - { - "name": "Claude", - "web": "https://claude.ai/", - "tags": [ - "🤖AI" - ], - "brief": "Anthropic公司开发的一个人工智能助手", - "state": "正常" - }, - { - "name": "Theb.AI", - "web": "https://beta.theb.ai/", - "tags": [ - "🤖AI" - ], - "brief": "一个基于API的聊天机器人 可选择多种模型", - "state": "正常" - }, - { - "name": "硬件测试仪", - "web": "https://hardwaretester.com/", - "tags": [ - "🕹️游戏", - "🔧工具" - ], - "brief": "提供手柄、GPU、麦克风以及midi功能的测试", - "state": "正常" - }, - { - "name": "中文手写板", - "web": "https://handraw.top/", - "tags": [ - "🧠学习", - "📒笔记" - ], - "brief": "中文友好的手写白板工具", - "state": "正常" - }, - { - "name": "星标历史图", - "web": "https://star-history.com/", - "tags": [ - "🐱Code", - "🖼️图片" - ], - "brief": "GitHub上缺失的星标历史图", - "state": "正常" - }, - { - "name": "Figma", - "web": "https://www.figma.com/", - "tags": [ - "✏️协作", - "✒️设计", - "🖼️图片" - ], - "brief": "Figma是一款协作设计工具,用于创建用户界面和原型。", - "state": "正常" - }, - { - "name": "手写板", - "web": "https://excalidraw.com/", - "tags": [ - "🧠学习", - "📒笔记" - ], - "brief": "手写效果白板工具", - "state": "正常" - }, - { - "name": "AColor", - "web": "https://color.adobe.com/", - "tags": [ - "🪣色彩" - ], - "brief": "Adobe的一个配色网站", - "state": "正常" - }, - { - "name": "徽章制作", - "web": "https://shields.io/", - "tags": [ - "🐱Code", - "♻︎图标" - ], - "brief": "提供各种服务的简洁、一致的易读徽章(badge、徽标)", - "state": "正常" - }, - { - "name": "SIcon", - "web": "https://simpleicons.org/", - "tags": [ - "🐱Code", - "♻︎图标" - ], - "brief": "SimpleIcons包含很多种流行品牌的免费 SVG 图标库", - "state": "正常" - }, - { - "name": "Gmail", - "web": "https://mail.google.com/mail/", - "tags": [ - "📧邮箱" - ], - "brief": "谷歌邮箱", - "state": "正常" - }, - { - "name": "Outlook", - "web": "https://outlook.live.com/mail/", - "tags": [ - "📧邮箱" - ], - "brief": "微软邮箱", - "state": "正常" - }, - { - "name": "instagram", - "web": "https://www.instagram.com/", - "tags": [ - "👁️‍🗨️媒体" - ], - "brief": "一款Meta的社交媒体平台", - "state": "正常" - }, - { - "name": "PH风格", - "web": "https://logoly.pro/", - "tags": [ - "⛹️‍♀️娱乐", - "♻︎图标", - "🖼️图片" - ], - "brief": "在线的 PornHub 风格 Logo 生成工具", - "state": "正常" - }, - { - "name": "HomeBrew", - "web": "https://brew.sh/index_zh-cn", - "tags": [ - "🍎Apple", - "🐱Code" - ], - "brief": "macOS(或 Linux)缺失的软件包的管理器", - "state": "正常" - }, - { - "name": "MongoDB", - "web": "https://cloud.mongodb.com/", - "tags": [ - "🐱Code", - "🔢数据" - ], - "brief": "一种高性能、可扩展和灵活的开源文档数据库管理系统", - "state": "正常" - }, - { - "name": "开发者搜索", - "web": "https://kaifa.baidu.com/", - "tags": [ - "🐱Code", - "🧠学习", - "🔍搜索" - ], - "brief": "一个面向开发者的知识搜索平台", - "state": "正常" - }, - { - "name": "网页存档", - "web": "https://archive.ph/", - "tags": [ - "📒笔记", - "🔢数据" - ], - "brief": "将网页永久保存,即使原来的消失了,仍然可以通过存档查看网页内容", - "state": "正常" - }, - { - "name": "阿里云盘", - "web": "https://www.aliyundrive.com/", - "tags": [ - "☁️云", - "📦资源" - ], - "brief": "一款速度快、不打扰、够安全、易于分享的网盘", - "state": "正常" - }, - { - "name": "TMDB", - "web": "https://www.themoviedb.org/", - "tags": [ - "🔢数据", - "📺视频" - ], - "brief": "一个电影数据库网站", - "state": "正常" - }, - { - "name": "Steam", - "web": "https://store.steampowered.com/", - "tags": [ - "🕹️游戏", - "🏠主页" - ], - "brief": "一个游戏集合的官网", - "state": "正常" - }, - { - "name": "Discord", - "web": "https://discord.com/app/", - "tags": [ - "✏️协作", - "👁️‍🗨️媒体" - ], - "brief": "在这里,您可以轻而易举地每日谈天说地,时常消遣娱乐", - "state": "正常" - }, - { - "name": "谷歌学术", - "web": "https://scholar.google.com.hk/?hl=zh-CN", - "tags": [ - "🧠学习", - "🔍搜索" - ], - "brief": "轻松地大范围搜索学术文献", - "state": "正常" - }, - { - "name": "微信读书", - "web": "https://weread.qq.com/", - "tags": [ - "🧠学习" - ], - "brief": "一个看书的网站", - "state": "正常" - }, - { - "name": "代码高亮", - "web": "http://word.wd1x.com/", - "tags": [ - "🐱Code", - "📒笔记" - ], - "brief": "在线生成可以在word/PPT中高亮的代码,支持各类主流语言在线着色", - "state": "正常" - }, - { - "name": "Mac应用", - "web": "https://decrypt.day/", - "tags": [ - "🍎Apple", - "🧰软件" - ], - "brief": "查找并下载最新解密的IPA应用程序", - "state": "正常" - }, - { - "name": "Netlify", - "web": "https://app.netlify.com/", - "tags": [ - "🐱Code", - "☁️云" - ], - "brief": "一个免服务器构建网页的网站", - "state": "正常" - }, - { - "name": "Notion", - "web": "https://www.notion.so/", - "tags": [ - "✏️协作", - "📒笔记", - "✅效率" - ], - "brief": "一款功能强大的协作工具和笔记应用", - "state": "正常" - }, - { - "name": "Slack", - "web": "https://slack.com/", - "tags": [ - "✏️协作", - "✅效率" - ], - "brief": "一个团队协作的网站", - "state": "正常" - }, - { - "name": "飞布API", - "web": "https://www.fireboom.io/", - "tags": [ - "🐱Code", - "☁️云" - ], - "brief": "可视化API开发平台", - "state": "正常" - }, - { - "name": "Caj2PDF", - "web": "https://caj2pdf.cn/", - "tags": [ - "⚙️转换", - "🧠学习" - ], - "brief": "学术必备!!!", - "state": "正常" - }, - { - "name": "DS可视化", - "web": "https://visualgo.net/", - "tags": [ - "🐱Code", - "🧠学习" - ], - "brief": "数据结构和算法动态可视化", - "state": "正常" - }, - { - "name": "CodeForces", - "web": "https://codeforces.com/", - "tags": [ - "🐱Code" - ], - "brief": "一个俄罗斯的刷题网站,有模拟竞赛", - "state": "正常" - }, - { - "name": "字幕库", - "web": "https://zmk.pw/", - "tags": [ - "📦资源" - ], - "brief": "字幕下载网站", - "state": "正常" - }, - { - "name": "国家统计局", - "web": "http://www.stats.gov.cn/", - "tags": [ - "🔢数据" - ], - "brief": "中国统计信息网", - "state": "正常" - }, - { - "name": "任务清单", - "web": "https://habitica.com/", - "tags": [ - "✅效率" - ], - "brief": "用闯关的方式完成任务", - "state": "正常" - }, - { - "name": "隔离食用手册", - "web": "https://cook.yunyoujun.cn/", - "tags": [ - "🔹其他" - ], - "brief": "多种多样的菜谱任你选择", - "state": "正常" - }, - { - "name": "Jobs纪念馆", - "web": "https://stevejobsarchive.com/", - "tags": [ - "🍎Apple", - "🔹其他" - ], - "brief": "乔布斯纪念网站", - "state": "正常" - }, - { - "name": "中国色", - "web": "http://zhongguose.com/", - "tags": [ - "🪣色彩" - ], - "brief": "提供各种中国的传统颜色的名称,CMYK值,RGB值,16进制表示", - "state": "正常" - }, - { - "name": "京东", - "web": "https://www.jd.com/", - "tags": [ - "🛒购物" - ], - "brief": "电商购物平台", - "state": "正常" - }, - { - "name": "W32DI", - "web": "https://win32diskimager.org/", - "tags": [ - "🧰软件" - ], - "brief": "Win32DiskImager一个将原始磁盘映像写入可移动设备的程序", - "state": "正常" - }, - { - "name": "推文图制作", - "web": "https://poet.so/", - "tags": [ - "🔧工具", - "🖼️图片" - ], - "brief": "创造漂亮的推文图片", - "state": "正常" - }, - { - "name": "轻松传", - "web": "https://easychuan.cn/", - "tags": [ - "📁文件" - ], - "brief": "轻松传文件到各种地方", - "state": "正常" - }, - { - "name": "在线LaTeX", - "web": "https://cn.overleaf.com/", - "tags": [ - "🐱Code", - "📒笔记" - ], - "brief": "在线 LaTeX 编辑器,无需安装,实时共享,版本控制,数百免费模板", - "state": "正常" - }, - { - "name": "影视飓风", - "web": "https://www.ysjf.com/index", - "tags": [ - "🕊️up主", - "🏠主页" - ], - "brief": "影视飓风官网", - "state": "正常" - }, - { - "name": "Lightly", - "web": "https://lightly.teamcode.com/", - "tags": [ - "🐱Code", - "☁️云" - ], - "brief": "在线编译工具,免费用户500Mb内存", - "state": "正常" - }, - { - "name": "ChatGPT", - "web": "https://chat.openai.com/", - "tags": [ - "🤖AI" - ], - "brief": "OpenAi做的智能助手", - "state": "正常" - }, - { - "name": "小红书创作", - "web": "https://creator.xiaohongshu.com", - "tags": [ - "👁️‍🗨️媒体" - ], - "brief": "小红书后台", - "state": "正常" - }, - { - "name": "小米有品", - "web": "https://www.xiaomiyoupin.com/", - "tags": [ - "🛒购物" - ], - "brief": "小米有品,购物平台", - "state": "正常" - }, - { - "name": "网易新闻", - "web": "https://news.163.com/", - "tags": [ - "🗞️新闻" - ], - "brief": "包含各种新闻的门户网站", - "state": "正常" - }, - { - "name": "Pixabay", - "web": "https://pixabay.com/", - "tags": [ - "🖼️图片", - "📦资源" - ], - "brief": "令人惊叹的免费图片和免版税库存", - "state": "正常" - }, - { - "name": "Horizons", - "web": "https://yx.g8hh.com/heart-of-galaxy-horizons/", - "tags": [ - "🕹️游戏" - ], - "brief": "一款图文并茂的网页游戏", - "state": "正常" - }, - { - "name": "谷歌", - "web": "https://www.google.com/", - "tags": [ - "🔍搜索" - ], - "brief": "需要魔法才能用的检索工具", - "state": "正常" - }, - { - "name": "Github", - "web": "https://github.com/", - "tags": [ - "🐱Code", - "✏️协作", - "☁️云" - ], - "brief": "存代码的地方", - "state": "正常" - }, - { - "name": "全球logo", - "web": "https://www.logonews.cn/", - "tags": [ - "🖼️图片", - "📦资源" - ], - "brief": "logo情报网站", - "state": "正常" - }, - { - "name": "爱给网", - "web": "https://www.aigei.com/", - "tags": [ - "🎵音频", - "📦资源", - "📺视频" - ], - "brief": "中国最大的数字娱乐免费素材下载网站", - "state": "正常" - }, - { - "name": "学习通", - "web": "https://i.chaoxing.com/", - "tags": [ - "🧠学习" - ], - "brief": "网课学习", - "state": "正常" - }, - { - "name": "微博", - "web": "https://weibo.com/", - "tags": [ - "👁️‍🗨️媒体" - ], - "brief": "国内最大的媒体", - "state": "正常" - }, - { - "name": "ChatExcel", - "web": "https://chatexcel.com/", - "tags": [ - "🤖AI" - ], - "brief": "仅通过聊天来操控您的Excel表格", - "state": "正常" - }, - { - "name": "猜中国省份", - "web": "https://vultr.youmu.moe/quiz/", - "tags": [ - "⛹️‍♀️娱乐" - ], - "brief": "你对我们的国家熟悉嘛?", - "state": "正常" - }, - { - "name": "知乎", - "web": "https://www.zhihu.com/", - "tags": [ - "🧠学习", - "👁️‍🗨️媒体" - ], - "brief": "看看别人怎么说?", - "state": "正常" - }, - { - "name": "Wordle", - "web": "https://www.nytimes.com/games/wordle/index.html", - "tags": [ - "🕹️游戏" - ], - "brief": "很好玩的填字游戏", - "state": "正常" - }, - { - "name": "哔哩哔哩", - "web": "https://www.bilibili.com/", - "tags": [ - "📺视频", - "👁️‍🗨️媒体" - ], - "brief": "一个看视频的网站", - "state": "正常" - }, - { - "name": "软件目录", - "web": "https://www.yuque.com/books/share/ec226b31-5272-4792-811b-c22334cf0a9a?#%20%E3%80%8A%E8%BD%AF%E4%BB%B6%E7%9B%AE%E5%BD%95%E3%80%8B", - "tags": [ - "🧰软件" - ], - "brief": "一些付费软件的免费安装包", - "state": "正常" - }, - { - "name": "itellyou", - "web": "https://next.itellyou.cn/", - "tags": [ - "🧰软件" - ], - "brief": "操作系统镜像|windows|linux", - "state": "正常" - }, - { - "name": "羽享平台", - "web": "http://yuzhuyi.ysepan.com/", - "tags": [ - "🧠学习", - "📦资源", - "🧰软件" - ], - "brief": "各种资源的网页", - "state": "正常" - }, - { - "name": "花生壳", - "web": "https://hsk.oray.com/", - "tags": [ - "☁️云" - ], - "brief": "提供内网穿透和动态域名解析服务,支持外网访问内网服务器", - "state": "正常" - }, - { - "name": "Vercel", - "web": "https://vercel.com/", - "tags": [ - "🐱Code", - "☁️云" - ], - "brief": "搭建网站", - "state": "正常" - }, - { - "name": "Youtube", - "web": "https://www.youtube.com/", - "tags": [ - "📺视频" - ], - "brief": "需要魔法才能访问的视频平台", - "state": "正常" - }, - { - "name": "Logo制作", - "web": "https://www.designevo.com/cn", - "tags": [ - "🔧工具", - "♻︎图标" - ], - "brief": "一款专业制作Logo的免费软件", - "state": "正常" - }, - { - "name": "全渠道搜索", - "web": "http://dir.scmor.com/", - "tags": [ - "🔍搜索", - "🔧工具" - ], - "brief": "各种搜索渠道", - "state": "正常" - }, - { - "name": "阿里云", - "web": "https://www.aliyun.com/", - "tags": [ - "☁️云" - ], - "brief": "阿里云平台,图床服务", - "state": "正常" - }, - { - "name": "耳聆网", - "web": "https://www.ear0.com/", - "tags": [ - "🎵音频", - "📦资源" - ], - "brief": "提供全面的声音资源和版权保护机制,满足各方面音频素材需求", - "state": "正常" - }, - { - "name": "Phind", - "web": "https://phind.com/", - "tags": [ - "🔍搜索", - "🐱Code" - ], - "brief": "面向程序员的搜索引擎", - "state": "正常" - }, - { - "name": "爱奇艺体育", - "web": "https://ssports.iqiyi.com/", - "tags": [ - "📺视频" - ], - "brief": "中国知名的体育赛事平台", - "state": "正常" - }, - { - "name": "擦除画面", - "web": "https://www.magiceraser.io/", - "tags": [ - "🖼️图片", - "🔧工具" - ], - "brief": "可以去背景", - "state": "正常" - }, - { - "name": "测速网", - "web": "https://www.speedtest.cn/", - "tags": [ - "🕸️网络", - "🔧工具" - ], - "brief": "你的网速怎么样?", - "state": "正常" - }, - { - "name": "QQ邮箱", - "web": "https://mail.qq.com", - "tags": [ - "📧邮箱" - ], - "brief": "腾讯邮箱", - "state": "正常" - }, - { - "name": "-LKs-", - "web": "http://lkssite.vip/", - "tags": [ - "🕊️up主" - ], - "brief": "一个墨镜区up做的网址大全", - "state": "正常" - }, - { - "name": "打字网站", - "web": "https://dazidazi.com/", - "tags": [ - "⛹️‍♀️娱乐" - ], - "brief": "一个up主做的打字网站", - "state": "正常" - }, - { - "name": "芒果商城", - "web": "https://www.mgidshop.com/", - "tags": [ - "🍎Apple", - "🛒购物" - ], - "brief": "购买日区美区小火箭、美区苹果ID", - "state": "正常" - }, - { - "name": "据义查句", - "web": "https://wantquotes.net/", - "tags": [ - "🧠学习", - "🔧工具" - ], - "brief": "根据意思来查找名人名言", - "state": "正常" - }, - { - "name": "微信公众", - "web": "https://mp.weixin.qq.com/", - "tags": [ - "👁️‍🗨️媒体" - ], - "brief": "公众号和小程序的后台", - "state": "正常" - }, - { - "name": "飞桨", - "web": "https://aistudio.baidu.com/aistudio/index", - "tags": [ - "🐱Code", - "☁️云" - ], - "brief": "一个强大且易用的开源深度学习平台", - "state": "正常" - }, - { - "name": "Pexels", - "web": "https://www.pexels.com/zh-cn/", - "tags": [ - "🖼️图片", - "📦资源" - ], - "brief": "摄影作者在这里免费分享最精彩的素材图片和视频", - "state": "正常" - }, - { - "name": "Git文档", - "web": "https://gitee.com/progit/index.html", - "tags": [ - "🐱Code", - "🧠学习", - "📒笔记" - ], - "brief": "快来学习Git!", - "state": "正常" - }, - { - "name": "SpellingBee", - "web": "https://www.nytimes.com/puzzles/spelling-bee", - "tags": [ - "🕹️游戏" - ], - "brief": "很难的凑字游戏", - "state": "正常" - }, - { - "name": "sojson在线", - "web": "https://www.sojson.com/", - "tags": [ - "⚙️转换" - ], - "brief": "json转换为其他的格式", - "state": "正常" - }, - { - "name": "FirstWeb", - "web": "http://info.cern.ch/", - "tags": [ - "🔹其他" - ], - "brief": "万维网第一个网页", - "state": "正常" - }, - { - "name": "格式转换", - "web": "https://www.aconvert.com/cn/", - "tags": [ - "⚙️转换" - ], - "brief": "内含多种格式转换", - "state": "正常" - }, - { - "name": "青柠起始页", - "web": "https://limestart.cn/", - "tags": [ - "🏠主页" - ], - "brief": "一个up主做的起始页,很精美", - "state": "正常" - }, - { - "name": "AppleStore", - "web": "https://www.apple.com.cn/retail/storelist/", - "tags": [ - "🍎Apple", - "🛒购物" - ], - "brief": "中国苹果直营店位置", - "state": "正常" - }, - { - "name": "猫啃网", - "web": "https://www.maoken.com/", - "tags": [ - "📦资源" - ], - "brief": "最新最全的可免费商用中文字体下载网站", - "state": "正常" - }, - { - "name": "钢笔工具", - "web": "https://bezier.method.ac/", - "tags": [ - "⛹️‍♀️娱乐" - ], - "brief": "一个帮助你熟练掌握钢笔工具的网站", - "state": "正常" - }, - { - "name": "今日头条", - "web": "https://www.toutiao.com/", - "tags": [ - "👁️‍🗨️媒体" - ], - "brief": "字节跳动旗下文字和视频网站", - "state": "正常" - }, - { - "name": "亚马逊", - "web": "https://www.amazon.cn/", - "tags": [ - "🛒购物" - ], - "brief": "全球购物平台", - "state": "正常" - }, - { - "name": "比特虫.ico", - "web": "https://www.bitbug.net/", - "tags": [ - "♻︎图标", - "✒️设计", - "⚙️转换" - ], - "brief": "制作网站图标的网站", - "state": "正常" - }, - { - "name": "油管解析", - "web": "https://fotoun.com/?p=161", - "tags": [ - "🔧工具", - "📺视频" - ], - "brief": "YouTube视频在线解析网站", - "state": "正常" - }, - { - "name": "monkeytype", - "web": "https://monkeytype.com/", - "tags": [ - "⛹️‍♀️娱乐" - ], - "brief": "练习打字的网站", - "state": "正常" - }, - { - "name": "Unsplash", - "web": "https://unsplash.com/", - "tags": [ - "🖼️图片", - "📦资源" - ], - "brief": "互联网的视觉资源,由各地的创作者提供支持", - "state": "正常" - }, - { - "name": "知到", - "web": "https://www.zhihuishu.com/", - "tags": [ - "🧠学习" - ], - "brief": "网课平台", - "state": "正常" - }, - { - "name": "密码测试", - "web": "https://www.passwordmonster.com/", - "tags": [ - "🔧工具", - "⛹️‍♀️娱乐" - ], - "brief": "测试密码安全程度", - "state": "正常" - }, - { - "name": "油管封面", - "web": "https://www.strerr.com/index.html", - "tags": [ - "🔧工具", - "🖼️图片" - ], - "brief": "提供油管封面预览和下载功能", - "state": "正常" - }, - { - "name": "Apple.cn", - "web": "https://www.apple.com.cn/", - "tags": [ - "🍎Apple" - ], - "brief": "一个伟大公司的官网", - "state": "正常" - }, - { - "name": "食物图片", - "web": "https://www.foodiesfeed.com/", - "tags": [ - "🖼️图片", - "📦资源" - ], - "brief": "搜索超过1802张免费美食照片", - "state": "正常" - }, - { - "name": "网易邮箱", - "web": "https://mail.163.com/", - "tags": [ - "📧邮箱" - ], - "brief": "网易邮箱163、126", - "state": "正常" - }, - { - "name": "赛博耶稣", - "web": "https://yesu.ink/", - "tags": [ - "🤖AI" - ], - "brief": "一个配备有ChatGPT的耶稣陪伴在您的身边", - "state": "正常" - }, - { - "name": "图寻", - "web": "https://tuxun.fun/", - "tags": [ - "⛹️‍♀️娱乐" - ], - "brief": "探索真实世界,收集线索,找出自己的位置", - "state": "正常" - }, - { - "name": "Potplayer", - "web": "https://potplayer.daum.net/", - "tags": [ - "🧰软件" - ], - "brief": "贼好用的win播放器", - "state": "正常" - }, - { - "name": "汉兜", - "web": "https://handle.antfu.me/", - "tags": [ - "🕹️游戏" - ], - "brief": "汉字猜字游戏", - "state": "正常" - }, - { - "name": "世界名画", - "web": "https://www.nbfox.com/", - "tags": [ - "🖼️图片", - "📦资源" - ], - "brief": "高清著名画作", - "state": "正常" - }, - { - "name": "ChatMind", - "web": "https://www.chatmind.tech/", - "tags": [ - "🤖AI" - ], - "brief": "AI生成思维导图的效率工具, 为用户提供智能化思维导图方案", - "state": "正常" - }, - { - "name": "找鼠标", - "web": "https://pointerpointer.com/", - "tags": [ - "⛹️‍♀️娱乐" - ], - "brief": "你的鼠标在哪都能被找到", - "state": "正常" - }, - { - "name": "腾讯云", - "web": "https://cloud.tencent.com/", - "tags": [ - "☁️云" - ], - "brief": "腾讯云平台,云主机", - "state": "正常" - }, - { - "name": "图标库", - "web": "https://www.iconfont.cn/", - "tags": [ - "♻︎图标", - "✒️设计" - ], - "brief": "可以找到很多官方图标", - "state": "正常" - }, - { - "name": "离谱相关性", - "web": "http://tylervigen.com/spurious-correlations", - "tags": [ - "⛹️‍♀️娱乐", - "🔹其他" - ], - "brief": "什么是离谱的呢?", - "state": "正常" - }, - { - "name": "连通性检验", - "web": "https://ip.skk.moe/", - "tags": [ - "🕸️网络", - "🔧工具" - ], - "brief": "检测你的电脑是否能连接一些节点", - "state": "正常" - }, - { - "name": "UU在线工具", - "web": "https://uutool.cn/", - "tags": [ - "🔧工具" - ], - "brief": "很多的工具", - "state": "正常" - }, - { - "name": "淘宝", - "web": "https://www.taobao.com/", - "tags": [ - "🛒购物" - ], - "brief": "买!买!买!", - "state": "正常" - }, - { - "name": "80S", - "web": "https://www.80sgod.com/", - "tags": [ - "📺视频" - ], - "brief": "提供最新高清MP4电影,电视剧,动漫下载.采用迅雷磁力链方式下载", - "state": "正常" - }, - { - "name": "油猴脚本", - "web": "https://greasyfork.org/zh-CN", - "tags": [ - "🔧工具", - "🧰软件" - ], - "brief": "这里是一个提供用户脚本的网站", - "state": "正常" - }, - { - "name": "DeepL", - "web": "https://www.deepl.com/translator", - "tags": [ - "🧠学习" - ], - "brief": "一个很好用的翻译网站", - "state": "正常" - }, - { - "name": "国外手机号", - "web": "https://sms-activate.org", - "tags": [ - "🔧工具" - ], - "brief": "虚拟号码来在线接受短信", - "state": "正常" - }, - { - "name": "Kaggle", - "web": "https://www.kaggle.com/", - "tags": [ - "🔢数据", - "🧠学习" - ], - "brief": "找一些成品的数据文件以及其他开发者对数据的处理", - "state": "正常" - }, - { - "name": "视频号", - "web": "https://channels.weixin.qq.com/", - "tags": [ - "👁️‍🗨️媒体" - ], - "brief": "微信视频号", - "state": "正常" - }, - { - "name": "在线剪视频", - "web": "https://online-video-cutter.com/cn/", - "tags": [ - "🔧工具", - "📺视频" - ], - "brief": "从任何在线视频中剪切片段", - "state": "正常" - }, - { - "name": "拼接音视频", - "web": "https://maple3142.github.io/mergemp4/", - "tags": [ - "🔧工具", - "👁️‍🗨️媒体" - ], - "brief": "将音频和视频拼到一起(不负责对齐)", - "state": "正常" - }, - { - "name": "赛博佛祖", - "web": "https://fozu.ink/", - "tags": [ - "🤖AI" - ], - "brief": "一个配备有ChatGPT的佛陪伴在您的身边", - "state": "正常" - }, - { - "name": "MD排版", - "web": "https://md.guozh.net/", - "tags": [ - "⚙️转换", - "📒笔记" - ], - "brief": "让markdown可以很好的发在公众号上", - "state": "正常" - }, - { - "name": "谷歌翻译", - "web": "https://translate.google.com/", - "tags": [ - "🧠学习" - ], - "brief": "(也许)需要魔法才能用的翻译工具", - "state": "正常" - }, - { - "name": "即时工具", - "web": "https://www.67tool.com/", - "tags": [ - "🔧工具" - ], - "brief": "一些工具", - "state": "正常" - }, - { - "name": "Jetbrains", - "web": "https://www.jetbrains.com/zh-cn/", - "tags": [ - "🐱Code", - "🧰软件", - "🏠主页" - ], - "brief": "为专业人士和团队制作开发者工具", - "state": "正常" - }, - { - "name": "新榜", - "web": "https://newrank.cn/", - "tags": [ - "👁️‍🗨️媒体" - ], - "brief": "更多更好的KOL自媒体,品效合一的传播销售方案,尽在新榜", - "state": "正常" - }, - { - "name": "耳机评测站", - "web": "https://www.woodenears.com/", - "tags": [ - "📱数码" - ], - "brief": "耳机测评网站", - "state": "正常" - }, - { - "name": "图司机", - "web": "https://www.tusij.com/", - "tags": [ - "🖼️图片", - "✒️设计" - ], - "brief": "简单修图网页", - "state": "正常" - }, - { - "name": "OTP", - "web": "https://otp.landian.vip/zh-cn/", - "tags": [ - "🧰软件", - "🔧工具" - ], - "brief": "OfficeToolPlus软件的工具网页", - "state": "正常" - }, - { - "name": "ChatDoc", - "web": "https://chatdoc.com/chatdoc/#/upload", - "tags": [ - "🤖AI" - ], - "brief": "一个文件阅读助手,可以快速从文档中提取、定位和汇总信息", - "state": "正常" - }, - { - "name": "壁纸", - "web": "https://wallhaven.cc/", - "tags": [ - "🖼️图片", - "📦资源" - ], - "brief": "好看高清壁纸", - "state": "正常" - }, - { - "name": "慕课", - "web": "https://www.icourse163.org/", - "tags": [ - "🧠学习" - ], - "brief": "网课,内含很多精品公开课", - "state": "正常" - }, - { - "name": "知网", - "web": "https://www.cnki.net/", - "tags": [ - "🧠学习", - "📦资源" - ], - "brief": "面向读者提供各类学术资源统一检索、统一导航、在线阅读和下载服务", - "state": "正常" - }, - { - "name": "Gitee", - "web": "https://gitee.com/", - "tags": [ - "🐱Code", - "☁️云", - "✏️协作" - ], - "brief": "存代码用的,国内访问很快", - "state": "正常" - }, - { - "name": "Typora", - "web": "https://www.typoraio.cn/", - "tags": [ - "📒笔记" - ], - "brief": "一款 Markdown 编辑器和阅读器", - "state": "正常" - }, - { - "name": "流程图导图", - "web": "https://www.processon.com/", - "tags": [ - "🖼️图片", - "🧠学习" - ], - "brief": "流程清晰可见", - "state": "正常" - }, - { - "name": "进化", - "web": "https://evolve.g8hh.com/", - "tags": [ - "🕹️游戏" - ], - "brief": "从原生质开始,逐步解锁高级生命形式,开启属于自己的文明", - "state": "正常" - }, - { - "name": "一网一匠", - "web": "https://ywyj.cn/", - "tags": [ - "🔧工具", - "🕊️up主" - ], - "brief": "一个up主叫一网一匠做的主页", - "state": "正常" - }, - { - "name": "PY标准库", - "web": "https://docs.python.org/zh-cn/3/library/", - "tags": [ - "🐱Code", - "🧠学习" - ], - "brief": "语言参考手册描述了Python的语法和语义,库参考则介绍了Python标准库", - "state": "正常" - }, - { - "name": "W3school", - "web": "https://www.w3school.com.cn/", - "tags": [ - "🧠学习" - ], - "brief": "全球最大的中文 Web 技术教程", - "state": "正常" - }, - { - "name": "cal计算器", - "web": "https://www.calculator.net/calorie-calculator.html", - "tags": [ - "🔧工具" - ], - "brief": "估算每天需要摄入的卡路里数量,以维持、减重或增重", - "state": "正常" - }, - { - "name": "重开模拟器", - "web": "https://liferestart.syaro.io/public/index.html", - "tags": [ - "🕹️游戏" - ], - "brief": "如果能重来~", - "state": "正常" - }, - { - "name": "空投", - "web": "https://airportal.cn/", - "tags": [ - "📁文件" - ], - "brief": "快速传文件", - "state": "正常" - }, - { - "name": "少数派", - "web": "https://sspai.com/", - "tags": [ - "📱数码", - "👁️‍🗨️媒体", - "🗞️新闻" - ], - "brief": "写文字的平台,数码科技相关", - "state": "正常" - }, - { - "name": "ChatPDF", - "web": "https://www.chatpdf.com/", - "tags": [ - "🤖AI" - ], - "brief": "ChatPDF是一种快速简便的方法,可以与任何PDF文件进行对话", - "state": "正常" - }, - { - "name": "SQLKiller", - "web": "https://www.sqlkiller.com/", - "tags": [ - "🤖AI" - ], - "brief": "直接描述你的取数需求,一键生成SQL", - "state": "正常" - }, - { - "name": "X", - "web": "https://twitter.com/", - "tags": [ - "👁️‍🗨️媒体" - ], - "brief": "国外媒体,已被马斯克收购", - "state": "正常" - }, - { - "name": "播客搜索", - "web": "https://pod.link/", - "tags": [ - "🔧工具", - "🎵音频" - ], - "brief": "搜索播客的网站", - "state": "正常" - }, - { - "name": "CSDN", - "web": "https://www.csdn.net/", - "tags": [ - "🐱Code", - "🔍搜索" - ], - "brief": "代码有关的问题来问我", - "state": "正常" - }, - { - "name": "Topbook", - "web": "https://topbook.cc/overview", - "tags": [ - "🕊️up主", - "🏠主页", - "✅效率" - ], - "brief": "一个百科全书", - "state": "正常" - }, - { - "name": "XFastest", - "web": "https://news.xfastest.com/", - "tags": [ - "🗞️新闻" - ], - "brief": "香港科技新闻网站", - "state": "正常" - } -] \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css index c649e9f..6254abb 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,7 +1,8 @@ -@import url('https://cdn.jsdelivr.net/npm/cn-fontsource-smiley-sans-oblique-regular@1.0.1/font.min.css'); -@import url('https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css'); -@import url('https://jhlst.nowscott.top/result.css'); -@import url('https://zqfs.nowscott.top/result.css'); +@import url('https://chinese-fonts-cdn.deno.dev/chinesefonts3/packages/dyh/dist/SmileySans-Oblique/result.css'); +@import url('https://chinese-fonts-cdn.deno.dev/chinesefonts3/packages/lxgwwenkai/dist/LXGWWenKai-Regular/result.css'); +@import url('https://chinese-fonts-cdn.deno.dev/chinesefonts3/packages/jhlst/dist/京華老宋体v1_007/result.css'); +@import url('https://chinese-fonts-cdn.deno.dev/chinesefonts3/packages/mzxst/dist/MZPXorig/result.css'); +@import url('https://chinese-fonts-cdn.deno.dev/chinesefonts3/packages/lxgwmanhei/dist/LXGWMarkerGothic/result.css'); * { margin: 0; diff --git a/public/image/favicon.ico b/public/images/favicon.ico similarity index 100% rename from public/image/favicon.ico rename to public/images/favicon.ico diff --git a/public/index.html b/public/index.html deleted file mode 100644 index a09772b..0000000 --- a/public/index.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - 网站索引 - - - - - - - - - - - -
    -
      -
      - -
      -

      - - Individual Web Index. - -

      -

      - - 如何部署 - -

      - -

      选择标签

      -
      -

      筛选网页

      -
      - -
      - - \ No newline at end of file diff --git a/public/js/context-menu.js b/public/js/context-menu.js deleted file mode 100644 index 4c311c9..0000000 --- a/public/js/context-menu.js +++ /dev/null @@ -1,91 +0,0 @@ -// 字体列表 -const fonts = ['Smiley Sans Oblique', 'LXGW WenKai', 'Zhuque Fangsong', 'KingHwa_OldSong']; -const fontnames = ['得意黑', '霞鹜文楷', '朱雀仿宋', '京華老宋体']; - -function populateContextMenu() { - const menu = document.getElementById('customContextMenu').querySelector('ul'); - menu.innerHTML = ''; // 清空现有的菜单项,防止重复添加 - - fonts.forEach((font, index) => { - let li = document.createElement('li'); - li.textContent = fontnames[index]; // 显示中文字体名 - li.style.cursor = 'pointer'; - li.style.fontFamily = font; // 设置每个字体选项的字体样式 - li.onclick = () => { - changeFont(font); // 调用更改字体的函数 - }; - menu.appendChild(li); - }); -} - - -function changeFont(font) { - document.documentElement.style.setProperty('--main-font-family', `"${font}"`); - setCookie('userFont', font, 7); // 更新Cookie,持续7天 - updateMenuSelection(font); // 更新菜单选择状态 - document.getElementById('customContextMenu').style.display = 'none'; // 隐藏菜单 -} - -function updateMenuSelection(selectedFont) { - const items = document.querySelectorAll('#customContextMenu li'); - items.forEach(item => { - if (item.textContent.trim() === fontnames[fonts.indexOf(selectedFont)]) { // 使用正确的友好名称进行比较 - item.classList.add('selected-font'); - } else { - item.classList.remove('selected-font'); - } - }); -} - - - -document.addEventListener('DOMContentLoaded', function() { - populateContextMenu(); // 填充右键菜单 - loadFont(); // 加载用户之前选择的字体 -}); - -// 处理右键菜单事件 -document.addEventListener('contextmenu', function (event) { - event.preventDefault(); - var contextMenu = document.getElementById('customContextMenu'); - contextMenu.style.display = 'block'; - contextMenu.style.left = event.pageX + 'px'; - contextMenu.style.top = event.pageY + 'px'; -}); - -// 处理点击事件,隐藏自定义的右键菜单 -document.addEventListener('click', function (event) { - var contextMenu = document.getElementById('customContextMenu'); - if (event.target.offsetParent !== contextMenu) { - contextMenu.style.display = 'none'; - } -}); - -function setCookie(name, value, days) { - var expires = ""; - if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toUTCString(); - } - document.cookie = name + "=" + (value || "") + expires + "; path=/"; -} - -function getCookie(name) { - var nameEQ = name + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) == ' ') c = c.substring(1, c.length); - if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); - } - return null; -} - -function loadFont() { - var userFont = getCookie('userFont'); - if (userFont) { - document.documentElement.style.setProperty('--main-font-family', userFont); - updateMenuSelection(userFont); // 更新菜单选择状态 - } -} diff --git a/public/js/data-loader.js b/public/js/data-loader.js deleted file mode 100644 index 850d094..0000000 --- a/public/js/data-loader.js +++ /dev/null @@ -1,119 +0,0 @@ -window.onload = function () { - var sources, taglist = [], onlist = [], reslist = []; - - function randomSort(arr) { - for (let i = 0, l = arr.length; i < l; i++) { - let rc = parseInt(Math.random() * l); - const empty = arr[i]; - arr[i] = arr[rc]; - arr[rc] = empty; - } - return arr; - } - - function unique(arr) { return Array.from(new Set(arr)); } - - function renderWebs(list) { - for (let l in list) { - var a = document.createElement('a'); - a.id = 'web'; - a.href = list[l].web; - a.target = '_blank'; - a.innerHTML = list[l].name; - a.title = list[l].brief; - document.getElementById('webs-container').appendChild(a); - } - } - - function renderTags(tags) { - document.getElementById('tags-container').innerHTML = ''; // 清空旧标签以重新渲染 - tags.forEach((tag, index) => { - var button = document.createElement('button'); - button.id = 'tag' + index; - button.innerHTML = tag; - // 设置按钮的类名基于是否被选中 - button.className = onlist.includes(tag) ? 'tag on' : 'tag off'; - document.getElementById('tags-container').appendChild(button); - button.onclick = () => toggleTagButton(button.id); - }); - } - - function toggleTagButton(buttonId) { - var button = document.getElementById(buttonId); - var tagText = button.textContent; - var tagIndex = onlist.indexOf(tagText); - - if (tagIndex === -1) { - onlist.push(tagText); // 添加到选中列表 - } else { - onlist.splice(tagIndex, 1); // 从选中列表移除 - } - - button.className = button.className === 'tag off' ? 'tag on' : 'tag off'; - updateResults(); // 更新结果和标签列表 - } - - function updateResults() { - reslist = sources.filter(source => onlist.every(tag => source.tags.includes(tag))); - document.getElementById('webs-container').innerHTML = reslist.length > 0 ? '' : '未找到符合条件的网页'; - renderWebs(randomSort(unique(reslist))); - - // 更新标签列表,将选中的标签放在前面 - let availableTags = []; - reslist.forEach(source => { - source.tags.forEach(tag => { - if (!availableTags.includes(tag)) { - availableTags.push(tag); - } - }); - }); - - onlist.forEach(tag => { - const index = availableTags.indexOf(tag); - if (index > -1) { - availableTags.splice(index, 1); - } - }); - - const sortedTags = onlist.concat(availableTags.filter(tag => !onlist.includes(tag))); - renderTags(sortedTags); - } - - function searchFunction() { - var keyword = document.getElementById('s-in').value.toLowerCase(); - reslist = sources.filter(source => - source.name.toLowerCase().includes(keyword) || - source.brief.toLowerCase().includes(keyword) || - source.tags.some(tag => tag.toLowerCase().includes(keyword)) - ); - document.getElementById('webs-container').innerHTML = reslist.length > 0 ? '' : '未找到符合条件的网页'; - renderWebs(randomSort(unique(reslist))); - } - - function extractAndRenderTags(sources) { - let taglist = []; - sources.forEach(source => { - source.tags.forEach(tag => { - if (!taglist.includes(tag)) { - taglist.push(tag); - } - }); - }); - taglist = randomSort(unique(taglist)); // 随机排序并去重标签列表 - renderTags(taglist); // 渲染标签列表 - } - - fetch("/assets/data.json") // 从服务器上获取数据 - .then(response => response.json()) - .then(data => { - sources = data; // 存储数据源 - renderWebs(randomSort(unique(sources))); // 初始渲染网页列表 - extractAndRenderTags(sources); // 使用封装后的函数处理和渲染标签 - document.getElementById('s-btn').onclick = searchFunction; // 绑定搜索按钮的点击事件 - document.addEventListener('keydown', event => { - if (event.keyCode === 13) { - document.getElementById('s-btn').click(); - } - }); - }); -} diff --git a/public/js/main.js b/public/js/main.js deleted file mode 100644 index e98c977..0000000 --- a/public/js/main.js +++ /dev/null @@ -1,23 +0,0 @@ -if ('serviceWorker' in navigator) { - window.addEventListener('load', () => { - navigator.serviceWorker.register('/service-worker.js').then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // 新内容已下载,用户需要重新加载页面以获取更新 - if (confirm('新版本已更新,是否立即刷新页面?')) { - window.location.reload(); - } - } else { - console.log('Content is now available offline!'); - } - } - }; - }; - }).catch(error => { - console.log('Service Worker registration failed:', error); - }); - }); -} diff --git a/public/js/theme.js b/public/js/theme.js deleted file mode 100644 index 0721c25..0000000 --- a/public/js/theme.js +++ /dev/null @@ -1,65 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - var currentTime = new Date(); // 创建一个 Date 对象 - var currentHour = currentTime.getHours(); // 获取当前时间(小时) - - var dark_mode = document.getElementById('darkcss'); - var dark_mode_icon = document.getElementById('icon'); - var dark_mode_btn = document.getElementById('darkbtn'); - - // 读取 cookie 中的模式设置 - var userPref = getCookie('themeMode'); - - if (userPref) { - applyTheme(userPref); - } else { - // 如果没有用户偏好设置,根据时间自动切换模式 - if (currentHour >= 22 || currentHour < 8) { - applyTheme('dark'); - } else { - applyTheme('daytime'); - } - } - - dark_mode_btn.onclick = function () { - var newTheme = (dark_mode_btn.className === 'daytime') ? 'dark' : 'daytime'; - applyTheme(newTheme); - setCookie('themeMode', newTheme, 12); // 设置 cookie,有效期为 12 小时 - } -}); - -function applyTheme(theme) { - var dark_mode = document.getElementById('darkcss'); - var dark_mode_icon = document.getElementById('icon'); - var dark_mode_btn = document.getElementById('darkbtn'); - - if (theme === 'dark') { - dark_mode.href = "/css/dark.css"; - dark_mode_icon.src = 'assets/svg/sun.svg'; - dark_mode_btn.className = 'dark'; - } else { - dark_mode.href = "/css/daytime.css"; - dark_mode_icon.src = 'assets/svg/moon.svg'; - dark_mode_btn.className = 'daytime'; - } -} - -function setCookie(name, value, hours) { - var expires = ""; - if (hours) { - var date = new Date(); - date.setTime(date.getTime() + (hours * 60 * 60 * 1000)); - expires = "; expires=" + date.toUTCString(); - } - document.cookie = name + "=" + (value || "") + expires + "; path=/"; -} - -function getCookie(name) { - var nameEQ = name + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) == ' ') c = c.substring(1, c.length); - if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); - } - return null; -} diff --git a/public/service-worker.js b/public/service-worker.js deleted file mode 100644 index 8dd3715..0000000 --- a/public/service-worker.js +++ /dev/null @@ -1,90 +0,0 @@ -const CACHE_NAME = 'indwebindex-cache-v2'; // 更新缓存版本 -const urlsToCache = [ - '/', - '/index.html', - '/css/dark.css', - '/css/context-menu.css', - '/css/style.css', - '/css/daytime.css', - '/js/data-loader.js', - '/js/theme.js', - '/js/context-menu.js', - '/js/main.js', - '/image/favicon.ico', - '/assets/svg/search.svg', - '/assets/svg/sun.svg', - '/assets/svg/moon.svg', - '/assets/data.json', - 'https://cdn.jsdelivr.net/npm/cn-fontsource-smiley-sans-oblique-regular@1.0.1/font.min.css', - 'https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css', - 'https://jhlst.nowscott.top/result.css', - 'https://zqfs.nowscott.top/result.css' -]; - -self.addEventListener('install', event => { - event.waitUntil( - caches.open(CACHE_NAME) - .then(cache => { - console.log('Opened cache'); - return cache.addAll(urlsToCache); - }) - ); -}); - -self.addEventListener('fetch', event => { - // 检查请求的来源是否为 chrome-extension 或者其他不支持的协议 - if (event.request.url.startsWith('chrome-extension://') || - !event.request.url.startsWith('http') || - event.request.method !== 'GET') { - return; // 直接返回,不处理此请求 - } - - event.respondWith( - caches.match(event.request).then(response => { - if (response) { - // 检查缓存版本是否过期 - return caches.open(CACHE_NAME).then(cache => { - return fetch(event.request).then(fetchResponse => { - if (fetchResponse && fetchResponse.status === 200) { - cache.put(event.request, fetchResponse.clone()); - } - return fetchResponse; - }).catch(() => { - console.error('Fetch failed, returning cached response', event.request.url); - return response; - }); // 如果网络请求失败,使用缓存 - }); - } else { - return fetch(event.request).then(fetchResponse => { - if (fetchResponse && fetchResponse.status === 200) { - return caches.open(CACHE_NAME).then(cache => { - cache.put(event.request, fetchResponse.clone()); - return fetchResponse; - }); - } else { - return fetchResponse; - } - }).catch(error => { - console.error('Fetch failed for:', event.request.url, error); - throw error; // 处理错误并返回 - }); - } - }) - ); -}); - -self.addEventListener('activate', event => { - const cacheWhitelist = [CACHE_NAME]; - event.waitUntil( - caches.keys().then(cacheNames => { - return Promise.all( - cacheNames.map(cacheName => { - if (cacheWhitelist.indexOf(cacheName) === -1) { - console.log('Deleting old cache:', cacheName); - return caches.delete(cacheName); - } - }) - ); - }) - ); -}); diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 69f7794..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -notion_client==2.2.1 diff --git a/scripts/scrape.py b/scripts/scrape.py deleted file mode 100644 index 57c324b..0000000 --- a/scripts/scrape.py +++ /dev/null @@ -1,72 +0,0 @@ -import os -import json -from notion_client import Client -import traceback # 引入 traceback 用于打印详细的错误信息 - -# 本地调试时候取消注释来加载本地环境变量 -# from dotenv import load_dotenv -# dotenv_path = '.env' -# load_dotenv(dotenv_path) - -notion = Client(auth=os.getenv("ACCESS_TOKEN")) -database_id = os.getenv("DATABASE_ID") - -def process_data(pages): - processed_data = [] - for item in pages['results']: - properties = item['properties'] - name = properties['name']['title'][0]['text']['content'] if properties['name']['title'] else '' - web = properties.get('web', {}).get('url', '') - tags = [tag['name'] for tag in properties.get('tags', {}).get('multi_select', [])] - brief_text_list = properties.get('brief', {}).get('rich_text', []) - brief = brief_text_list[0].get('text', {}).get('content', '') if brief_text_list else '' - - # 对 state 的处理添加 None 检查 - state_prop = properties.get('state') - if state_prop and 'select' in state_prop and state_prop['select']: - state = state_prop['select'].get('name', '') - else: - state = '' - - processed_data.append({ - "name": name, - "web": web, - "tags": tags, - "brief": brief, - "state": state - }) - return processed_data - - -def fetch_database(database_id): - """从Notion数据库获取所有数据,并处理""" - data = [] - has_more = True - start_cursor = None - while has_more: - try: - response = notion.databases.query(database_id=database_id, start_cursor=start_cursor) - data.extend(process_data(response)) - has_more = response.get('has_more', False) # type: ignore - start_cursor = response.get('next_cursor') # type: ignore - except Exception as e: - print("Failed to query database:", e) - traceback.print_exc() - break # 出错时退出循环 - return data - -def main(): - """主函数,获取数据并保存到文件""" - try: - data = fetch_database(database_id) - # 过滤数据 - filtered_data = [item for item in data if item['name'] and item['state'] == '正常'] - # 保存到JSON文件 - with open('public/assets/data.json', 'w', encoding='utf-8') as f: - json.dump(filtered_data, f, ensure_ascii=False, indent=4) - except Exception as e: - print(f"An error occurred: {e}") - traceback.print_exc() - -if __name__ == "__main__": - main() diff --git a/vercel.json b/vercel.json index c6d9b51..c0d1f72 100644 --- a/vercel.json +++ b/vercel.json @@ -1,4 +1,13 @@ { - "cleanUrls": true, - "trailingSlash": false -} \ No newline at end of file + "version": 2, + "builds": [ + { + "src": "next.config.js", + "use": "@vercel/next" + } + ], + "routes": [ + { "src": "/(.*)", "dest": "/$1" } + ] + } + \ No newline at end of file