Fix PWA shortcut icon on Windows
Allow unauthenticated access to favicon and manifest, generate favicon.ico, and add multi-size manifest icons. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,4 +38,8 @@ node_modules/
|
|||||||
.vitepress/cache/
|
.vitepress/cache/
|
||||||
.vitepress/public/
|
.vitepress/public/
|
||||||
server/auth.config.json
|
server/auth.config.json
|
||||||
|
assets/site/favicon.ico
|
||||||
|
assets/site/favicon.png
|
||||||
|
assets/site/icon-192.png
|
||||||
|
assets/site/icon-512.png
|
||||||
|
|
||||||
|
|||||||
@@ -26,16 +26,20 @@ export default defineConfig({
|
|||||||
cleanUrls: true,
|
cleanUrls: true,
|
||||||
ignoreDeadLinks: true,
|
ignoreDeadLinks: true,
|
||||||
head: [
|
head: [
|
||||||
|
['link', { rel: 'icon', href: '/favicon.ico', sizes: 'any' }],
|
||||||
|
['link', { rel: 'shortcut icon', href: '/favicon.ico' }],
|
||||||
['link', { rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' }],
|
['link', { rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' }],
|
||||||
['link', { rel: 'icon', href: '/favicon.png', type: 'image/png', sizes: '512x512' }],
|
['link', { rel: 'icon', href: '/favicon.png', type: 'image/png', sizes: '512x512' }],
|
||||||
['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '180x180' }],
|
['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '180x180' }],
|
||||||
['link', { rel: 'manifest', href: '/site.webmanifest' }],
|
['link', { rel: 'manifest', href: '/site.webmanifest' }],
|
||||||
['meta', { name: 'theme-color', content: '#0f3460' }],
|
['meta', { name: 'theme-color', content: '#0f3460' }],
|
||||||
|
['meta', { name: 'msapplication-TileColor', content: '#0f3460' }],
|
||||||
|
['meta', { name: 'msapplication-TileImage', content: '/apple-touch-icon.png' }],
|
||||||
['meta', { name: 'apple-mobile-web-app-title', content: '道德经' }],
|
['meta', { name: 'apple-mobile-web-app-title', content: '道德经' }],
|
||||||
['meta', { name: 'application-name', content: 'DAO DE JING' }],
|
['meta', { name: 'application-name', content: '道德经' }],
|
||||||
],
|
],
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
logo: { src: '/favicon.png', width: 24, height: 24 },
|
logo: { src: '/favicon.ico', width: 24, height: 24 },
|
||||||
nav: [
|
nav: [
|
||||||
{ text: '首页', link: '/' },
|
{ text: '首页', link: '/' },
|
||||||
{ text: '五行', link: '/金、木、水、火、土 - 五行/' },
|
{ text: '五行', link: '/金、木、水、火、土 - 五行/' },
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -3,20 +3,33 @@
|
|||||||
"short_name": "道德经",
|
"short_name": "道德经",
|
||||||
"description": "传统文化典籍资料库",
|
"description": "传统文化典籍资料库",
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#1a1a2e",
|
"background_color": "#1a1a2e",
|
||||||
"theme_color": "#0f3460",
|
"theme_color": "#0f3460",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/apple-touch-icon.png",
|
"src": "/favicon.ico",
|
||||||
"sizes": "512x512",
|
"sizes": "48x48",
|
||||||
"type": "image/png",
|
"type": "image/x-icon"
|
||||||
"purpose": "any maskable"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/favicon.png",
|
"src": "/icon-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icon-512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png"
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icon-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1586
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,8 @@
|
|||||||
"serve": "npm run build && npm run start"
|
"serve": "npm run build && npm run start"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"sharp": "^0.33.5",
|
||||||
|
"to-ico": "^1.1.5",
|
||||||
"vitepress": "^1.6.3"
|
"vitepress": "^1.6.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import sharp from 'sharp'
|
||||||
|
import toIco from 'to-ico'
|
||||||
|
|
||||||
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||||
|
const siteDir = path.join(root, 'assets', 'site')
|
||||||
|
const sourcePng = path.join(siteDir, 'apple-touch-icon.png')
|
||||||
|
|
||||||
|
if (!fs.existsSync(sourcePng)) {
|
||||||
|
console.warn('[generate-icons] 跳过:未找到 apple-touch-icon.png')
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resizePng(size) {
|
||||||
|
return sharp(sourcePng).resize(size, size).png().toBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
const icoSizes = [16, 32, 48, 64, 128, 256]
|
||||||
|
const icoBuffers = await Promise.all(icoSizes.map((size) => resizePng(size)))
|
||||||
|
const icoBuffer = await toIco(icoBuffers)
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(siteDir, 'favicon.ico'), icoBuffer)
|
||||||
|
fs.writeFileSync(path.join(siteDir, 'icon-192.png'), await resizePng(192))
|
||||||
|
fs.writeFileSync(path.join(siteDir, 'icon-512.png'), await resizePng(512))
|
||||||
|
fs.writeFileSync(path.join(siteDir, 'favicon.png'), await resizePng(512))
|
||||||
|
|
||||||
|
console.log('[generate-icons] 已生成 favicon.ico、icon-192.png、icon-512.png')
|
||||||
@@ -28,14 +28,21 @@ function resetPublicDir() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetPublicDir()
|
resetPublicDir()
|
||||||
|
await import('./generate-icons.mjs')
|
||||||
copyDir(path.join(root, 'assets'), path.join(publicDir, 'assets'))
|
copyDir(path.join(root, 'assets'), path.join(publicDir, 'assets'))
|
||||||
copyDir(path.join(root, 'images'), path.join(publicDir, 'images'))
|
copyDir(path.join(root, 'images'), path.join(publicDir, 'images'))
|
||||||
|
|
||||||
const siteDir = path.join(root, 'assets', 'site')
|
const siteDir = path.join(root, 'assets', 'site')
|
||||||
if (fs.existsSync(siteDir)) {
|
if (fs.existsSync(siteDir)) {
|
||||||
|
const skipInPublicRoot = new Set(['apple-touch-icon.png'])
|
||||||
for (const file of fs.readdirSync(siteDir)) {
|
for (const file of fs.readdirSync(siteDir)) {
|
||||||
|
if (skipInPublicRoot.has(file)) continue
|
||||||
fs.copyFileSync(path.join(siteDir, file), path.join(publicDir, file))
|
fs.copyFileSync(path.join(siteDir, file), path.join(publicDir, file))
|
||||||
}
|
}
|
||||||
|
fs.copyFileSync(
|
||||||
|
path.join(siteDir, 'icon-512.png'),
|
||||||
|
path.join(publicDir, 'apple-touch-icon.png'),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[prepare-public] assets、images 与站点图标已同步到 .vitepress/public')
|
console.log('[prepare-public] assets、images 与站点图标已同步到 .vitepress/public')
|
||||||
|
|||||||
@@ -89,11 +89,33 @@ function isAuthenticated(req) {
|
|||||||
return Boolean(req.session?.user)
|
return Boolean(req.session?.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PUBLIC_PATHS = new Set([
|
||||||
|
'/favicon.ico',
|
||||||
|
'/favicon.png',
|
||||||
|
'/favicon.svg',
|
||||||
|
'/apple-touch-icon.png',
|
||||||
|
'/icon-192.png',
|
||||||
|
'/icon-512.png',
|
||||||
|
'/site.webmanifest',
|
||||||
|
'/vp-icons.css',
|
||||||
|
'/hashmap.json',
|
||||||
|
])
|
||||||
|
|
||||||
|
function isPublicPath(reqPath) {
|
||||||
|
if (PUBLIC_PATHS.has(reqPath)) return true
|
||||||
|
if (reqPath.startsWith('/assets/')) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
function authGuard(req, res, next) {
|
function authGuard(req, res, next) {
|
||||||
if (req.path === '/login' || req.path.startsWith('/api/login')) {
|
if (req.path === '/login' || req.path.startsWith('/api/login')) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isPublicPath(req.path)) {
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
if (isAuthenticated(req)) {
|
if (isAuthenticated(req)) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,14 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#0f3460" />
|
<meta name="theme-color" content="#0f3460" />
|
||||||
<meta name="apple-mobile-web-app-title" content="道德经" />
|
<meta name="apple-mobile-web-app-title" content="道德经" />
|
||||||
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||||
|
<link rel="shortcut icon" href="/favicon.ico" />
|
||||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<link rel="icon" href="/favicon.png" type="image/png" sizes="512x512" />
|
<link rel="icon" href="/favicon.png" type="image/png" sizes="512x512" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
|
<meta name="msapplication-TileColor" content="#0f3460" />
|
||||||
|
<meta name="msapplication-TileImage" content="/apple-touch-icon.png" />
|
||||||
<title>登录 · DAO DE JING</title>
|
<title>登录 · DAO DE JING</title>
|
||||||
<style>
|
<style>
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
|
|||||||
Reference in New Issue
Block a user