Compare commits
	
		
			3 Commits
		
	
	
		
			8abfdb2fc0
			...
			6e6bb8cad2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6e6bb8cad2 | |||
| d5d74cd9a3 | |||
| 986472d76d | 
							
								
								
									
										16
									
								
								app.vue
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								app.vue
									
									
									
									
									
								
							@@ -6,11 +6,11 @@
 | 
				
			|||||||
    nav.container.mx-auto.flex.text-gray-400.text-lg.font-bold
 | 
					    nav.container.mx-auto.flex.text-gray-400.text-lg.font-bold
 | 
				
			||||||
      template(v-for="item in navbar")
 | 
					      template(v-for="item in navbar")
 | 
				
			||||||
        NuxtLink.px-4.py-6.transition-all.duration-150(
 | 
					        NuxtLink.px-4.py-6.transition-all.duration-150(
 | 
				
			||||||
          class="hover:text-gray-800",
 | 
					          class="text-gray-500 hover:text-dark-800",
 | 
				
			||||||
          :class="{ 'text-black': item.active }",
 | 
					          :class="{ 'text-dark-800': item.active }",
 | 
				
			||||||
          :to="item.path"
 | 
					          :to="item.path"
 | 
				
			||||||
        ) {{ item.title }}
 | 
					        ) {{ item.title }}
 | 
				
			||||||
      NuxtLink.px-4.py-6.ml-auto(to="/account" v-if="!account.padding") {{ account.online ? account.name : '登录' }}
 | 
					      NuxtLink.px-4.py-6.ml-auto.text-gray-500(to="/account" v-if="!account.padding") {{ account.online ? account.name : '登录' }}
 | 
				
			||||||
  NuxtPage
 | 
					  NuxtPage
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,6 +71,16 @@ onMounted(() => {
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
 | 
					/** 去除链接默认下划线 */
 | 
				
			||||||
 | 
					a {
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** 全局字体 */
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
					  font-family: 'Noto Sans SC', 'Microsoft YaHei', sans-serif;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 全局滚动条取消空间占用, 防止渲染时闪烁 */
 | 
					/** 全局滚动条取消空间占用, 防止渲染时闪烁 */
 | 
				
			||||||
::-webkit-scrollbar {
 | 
					::-webkit-scrollbar {
 | 
				
			||||||
  display: none;
 | 
					  display: none;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,7 @@
 | 
				
			|||||||
import { defineNuxtConfig } from 'nuxt/config'
 | 
					import { defineNuxtConfig } from 'nuxt/config'
 | 
				
			||||||
import { PugExtractor } from 'vite-plugin-windicss'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
 | 
					 | 
				
			||||||
export default defineNuxtConfig({
 | 
					export default defineNuxtConfig({
 | 
				
			||||||
  modules: ['nuxt-windicss'],
 | 
					  modules: ['@unocss/nuxt'],
 | 
				
			||||||
  app: {
 | 
					  app: {
 | 
				
			||||||
    head: {
 | 
					    head: {
 | 
				
			||||||
      title: 'satori',
 | 
					      title: 'satori',
 | 
				
			||||||
@@ -12,16 +10,6 @@ export default defineNuxtConfig({
 | 
				
			|||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  windicss: {
 | 
					 | 
				
			||||||
    config: {
 | 
					 | 
				
			||||||
      extract: {
 | 
					 | 
				
			||||||
        extractors: [{
 | 
					 | 
				
			||||||
          extractor: PugExtractor,
 | 
					 | 
				
			||||||
          extensions: ['vue', 'pug'],
 | 
					 | 
				
			||||||
        }]
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  nitro: {
 | 
					  nitro: {
 | 
				
			||||||
    storage: {
 | 
					    storage: {
 | 
				
			||||||
      blog: {
 | 
					      blog: {
 | 
				
			||||||
@@ -51,5 +39,6 @@ export default defineNuxtConfig({
 | 
				
			|||||||
        clientPort: 3000
 | 
					        clientPort: 3000
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
 | 
					  compatibilityDate: '2025-02-25'
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
@@ -8,11 +8,13 @@
 | 
				
			|||||||
    "update": "ssh satori 'cd satori; git pull; npm i; npm run build; pm2 restart satori'"
 | 
					    "update": "ssh satori 'cd satori; git pull; npm i; npm run build; pm2 restart satori'"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@unocss/extractor-pug": "^66.0.0",
 | 
				
			||||||
 | 
					    "@unocss/nuxt": "^66.0.0",
 | 
				
			||||||
    "@vue/compiler-sfc": "^3.3.4",
 | 
					    "@vue/compiler-sfc": "^3.3.4",
 | 
				
			||||||
    "nuxt": "3.7.4",
 | 
					    "nuxt": "3.15.4",
 | 
				
			||||||
    "nuxt-windicss": "^2.6.0",
 | 
					 | 
				
			||||||
    "pug": "^3.0.2",
 | 
					    "pug": "^3.0.2",
 | 
				
			||||||
    "vite-plugin-windicss": "^1.9.3"
 | 
					    "pug-plain-loader": "^1.1.0",
 | 
				
			||||||
 | 
					    "unocss": "^66.0.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@formkit/auto-animate": "^0.8.0",
 | 
					    "@formkit/auto-animate": "^0.8.0",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,7 +56,7 @@ div.container.mx-auto.py-24.flex.gap-8.w-full(
 | 
				
			|||||||
              span.mr-4 {{ content }}
 | 
					              span.mr-4 {{ content }}
 | 
				
			||||||
              time.text-xs(:datetime="updatedAt || createdAt") {{ date }}
 | 
					              time.text-xs(:datetime="updatedAt || createdAt") {{ date }}
 | 
				
			||||||
  // 右侧栏(aside)
 | 
					  // 右侧栏(aside)
 | 
				
			||||||
  aside.w-64.py-2.flex.flex-col.gap-8(class="<sm:hidden")
 | 
					  aside.w-64.py-2.flex.flex-col.gap-8
 | 
				
			||||||
    div(v-if="!tagPending && tags.length")
 | 
					    div(v-if="!tagPending && tags.length")
 | 
				
			||||||
      span.font-bold # TAG
 | 
					      span.font-bold # TAG
 | 
				
			||||||
      ul.flex.flex-wrap.gap-2.py-1
 | 
					      ul.flex.flex-wrap.gap-2.py-1
 | 
				
			||||||
@@ -68,7 +68,7 @@ div.container.mx-auto.py-24.flex.gap-8.w-full(
 | 
				
			|||||||
      span.font-bold # 归档
 | 
					      span.font-bold # 归档
 | 
				
			||||||
      template(v-for="item in data.filter(x => x.title)" :key="item.id")
 | 
					      template(v-for="item in data.filter(x => x.title)" :key="item.id")
 | 
				
			||||||
        NuxtLink.block.truncate(
 | 
					        NuxtLink.block.truncate(
 | 
				
			||||||
          class="hover:text-pink-500"
 | 
					          class="text-dark-600 hover:text-pink-500 visited:text-gray-500"
 | 
				
			||||||
          :to="`/blog/${item.id}`"
 | 
					          :to="`/blog/${item.id}`"
 | 
				
			||||||
        ) {{ item.title }}
 | 
					        ) {{ item.title }}
 | 
				
			||||||
  // 弹出层编辑器(编辑日志)
 | 
					  // 弹出层编辑器(编辑日志)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
      NuxtLink(:to="`/gallery/${item.id}`")
 | 
					      NuxtLink(:to="`/gallery/${item.id}`")
 | 
				
			||||||
        //img.w-full(:src="`${item.image}?w=${WIDTH}`" loading="lazy")
 | 
					        //img.w-full(:src="`${item.image}?w=${WIDTH}`" loading="lazy")
 | 
				
			||||||
        img.w-full(:src="item.image" loading="lazy")
 | 
					        img.w-full(:src="item.image+'@w480.webp'" loading="lazy")
 | 
				
			||||||
        .absolute.top-0.left-0.right-0.text-white.overflow-hidden.break-words.pb-4(
 | 
					        .absolute.top-0.left-0.right-0.text-white.overflow-hidden.break-words.pb-4(
 | 
				
			||||||
          style="background: linear-gradient(to top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.25))"
 | 
					          style="background: linear-gradient(to top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.25))"
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<template lang="pug">
 | 
					<template lang="pug">
 | 
				
			||||||
main.container.mx-auto.h-screen.flex.justify-center.align-center.items-center
 | 
					main.container.mx-auto.h-screen.flex.justify-center.align-center.items-center
 | 
				
			||||||
    div.text-red-100.text-8xl.cursor-pointer satori
 | 
					    div.text-red-100.text-8xl.cursor-pointer.ma3 satori
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
import fs from 'fs'
 | 
					import fs from 'fs'
 | 
				
			||||||
import path from 'path'
 | 
					import path from 'path'
 | 
				
			||||||
import { getQuery } from 'h3'
 | 
					 | 
				
			||||||
import webp from 'webp-converter'
 | 
					import webp from 'webp-converter'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 如果当前文件夹是 .output 则使用上一级路径
 | 
					// 如果当前文件夹是 .output 则使用上一级路径
 | 
				
			||||||
@@ -17,31 +16,42 @@ export default defineEventHandler(async event => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // 处理 GET 请求
 | 
					    // 处理 GET 请求
 | 
				
			||||||
    if (event.node.req.method === 'GET') {
 | 
					    if (event.node.req.method === 'GET') {
 | 
				
			||||||
        const file_name = decodeURI(event.context.params.id)
 | 
					        // 正则提取文件名后缀 1ac7eb2155fa09851c4eeca14.png@w320.webp
 | 
				
			||||||
        const file_path = path.resolve(dirPath, file_name)
 | 
					        const filename = decodeURI(event.context.params.id)
 | 
				
			||||||
 | 
					        const match = filename.match(/(.*)@w(\d+)\.(\w+)/)
 | 
				
			||||||
        // 检查文件是否存在
 | 
					 | 
				
			||||||
        if (!fs.existsSync(file_path)) {
 | 
					 | 
				
			||||||
            event.node.res.statusCode = 404
 | 
					 | 
				
			||||||
            return { error: 'file not found' }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 浏览器缓存一个月
 | 
					 | 
				
			||||||
        event.node.res.setHeader('Cache-Control', 'max-age=2629746,immutable')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 是否请求缩略图(如果文件不存在则创建)
 | 
					 | 
				
			||||||
        const { w } = getQuery(event)
 | 
					 | 
				
			||||||
        if (w) {
 | 
					 | 
				
			||||||
            const tmp_path = path.resolve(tmpPath, file_name + `?w=${w}`)
 | 
					 | 
				
			||||||
            if (!fs.existsSync(tmp_path)) {
 | 
					 | 
				
			||||||
                const origin = file_path.replace(/\.thumbnail$/, '')
 | 
					 | 
				
			||||||
                await webp.cwebp(origin, tmp_path, `-lossless -resize ${w} 0`)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return fs.readFileSync(tmp_path)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 请求原图
 | 
					        // 请求原图
 | 
				
			||||||
        return fs.readFileSync(file_path)
 | 
					        if (!match) {
 | 
				
			||||||
 | 
					            event.node.res.setHeader('Cache-Control', 'max-age=2629746,immutable')
 | 
				
			||||||
 | 
					            return fs.readFileSync(path.resolve(dirPath, filename))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const [name, origin, w, format] = match
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 返回缩略图(如果已经生成)
 | 
				
			||||||
 | 
					        if (fs.existsSync(path.resolve(tmpPath, name))) {
 | 
				
			||||||
 | 
					            event.node.res.setHeader('Cache-Control', 'max-age=2629746,immutable')
 | 
				
			||||||
 | 
					            return fs.readFileSync(path.resolve(tmpPath, name))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 生成缩略图(如果源文件存在)
 | 
				
			||||||
 | 
					        if (fs.existsSync(path.resolve(dirPath, origin))) {
 | 
				
			||||||
 | 
					            await webp.cwebp(path.resolve(dirPath, origin), path.resolve(tmpPath, name) , `-lossless -resize ${w} 0`)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // 如果缩略图比原图文件大则替换为原图
 | 
				
			||||||
 | 
					            const originSize = fs.statSync(path.resolve(dirPath, origin)).size
 | 
				
			||||||
 | 
					            const thumbSize = fs.statSync(path.resolve(tmpPath, name)).size
 | 
				
			||||||
 | 
					            if (thumbSize > originSize) {
 | 
				
			||||||
 | 
					                fs.unlinkSync(path.resolve(tmpPath, name))
 | 
				
			||||||
 | 
					                fs.copyFileSync(path.resolve(dirPath, origin), path.resolve(tmpPath, name))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            event.node.res.setHeader('Cache-Control', 'max-age=2629746,immutable')
 | 
				
			||||||
 | 
					            return fs.readFileSync(path.resolve(tmpPath, name))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        event.node.res.statusCode = 404
 | 
				
			||||||
 | 
					        return { error: 'file not found' }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 处理 DELETE 请求
 | 
					    // 处理 DELETE 请求
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ if (!fs.existsSync(dirPath)) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineEventHandler(async event => {
 | 
					export default defineEventHandler(async event => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 处理 GET 请求(读取image中的图片文件列表)
 | 
					    // 处理 GET 请求(读取image中的图片文件列表)
 | 
				
			||||||
    if (event.node.req.method === 'GET') {
 | 
					    if (event.node.req.method === 'GET') {
 | 
				
			||||||
        const files = await fs.promises.readdir(dirPath)
 | 
					        const files = await fs.promises.readdir(dirPath)
 | 
				
			||||||
@@ -30,6 +31,7 @@ export default defineEventHandler(async event => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return { list }
 | 
					        return { list }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 处理 POST 请求(FromData上传图片)
 | 
					    // 处理 POST 请求(FromData上传图片)
 | 
				
			||||||
    if (event.node.req.method === 'POST') {
 | 
					    if (event.node.req.method === 'POST') {
 | 
				
			||||||
        const form = formidable({ multiples: true, uploadDir: dirPath })
 | 
					        const form = formidable({ multiples: true, uploadDir: dirPath })
 | 
				
			||||||
@@ -55,4 +57,5 @@ export default defineEventHandler(async event => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return { list }
 | 
					        return { list }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
							
								
								
									
										17
									
								
								uno.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								uno.config.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import { defineConfig, presetWind3, presetWebFonts } from 'unocss'
 | 
				
			||||||
 | 
					import extractorPug from '@unocss/extractor-pug'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineConfig({
 | 
				
			||||||
 | 
					  presets: [
 | 
				
			||||||
 | 
					    presetWind3(),
 | 
				
			||||||
 | 
					    presetWebFonts({
 | 
				
			||||||
 | 
					      fonts: {
 | 
				
			||||||
 | 
					        sans: 'Roboto',
 | 
				
			||||||
 | 
					        mono: ['Fira Code', 'Fira Mono:400,700']
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  extractors: [
 | 
				
			||||||
 | 
					    extractorPug(),
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
		Reference in New Issue
	
	Block a user