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
 | 
			
		||||
      template(v-for="item in navbar")
 | 
			
		||||
        NuxtLink.px-4.py-6.transition-all.duration-150(
 | 
			
		||||
          class="hover:text-gray-800",
 | 
			
		||||
          :class="{ 'text-black': item.active }",
 | 
			
		||||
          class="text-gray-500 hover:text-dark-800",
 | 
			
		||||
          :class="{ 'text-dark-800': item.active }",
 | 
			
		||||
          :to="item.path"
 | 
			
		||||
        ) {{ 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
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -71,6 +71,16 @@ onMounted(() => {
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
/** 去除链接默认下划线 */
 | 
			
		||||
a {
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 全局字体 */
 | 
			
		||||
body {
 | 
			
		||||
  font-family: 'Noto Sans SC', 'Microsoft YaHei', sans-serif;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 全局滚动条取消空间占用, 防止渲染时闪烁 */
 | 
			
		||||
::-webkit-scrollbar {
 | 
			
		||||
  display: none;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
import { defineNuxtConfig } from 'nuxt/config'
 | 
			
		||||
import { PugExtractor } from 'vite-plugin-windicss'
 | 
			
		||||
 | 
			
		||||
// https://nuxt.com/docs/api/configuration/nuxt-config
 | 
			
		||||
export default defineNuxtConfig({
 | 
			
		||||
  modules: ['nuxt-windicss'],
 | 
			
		||||
  modules: ['@unocss/nuxt'],
 | 
			
		||||
  app: {
 | 
			
		||||
    head: {
 | 
			
		||||
      title: 'satori',
 | 
			
		||||
@@ -12,16 +10,6 @@ export default defineNuxtConfig({
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  windicss: {
 | 
			
		||||
    config: {
 | 
			
		||||
      extract: {
 | 
			
		||||
        extractors: [{
 | 
			
		||||
          extractor: PugExtractor,
 | 
			
		||||
          extensions: ['vue', 'pug'],
 | 
			
		||||
        }]
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  nitro: {
 | 
			
		||||
    storage: {
 | 
			
		||||
      blog: {
 | 
			
		||||
@@ -51,5 +39,6 @@ export default defineNuxtConfig({
 | 
			
		||||
        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'"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@unocss/extractor-pug": "^66.0.0",
 | 
			
		||||
    "@unocss/nuxt": "^66.0.0",
 | 
			
		||||
    "@vue/compiler-sfc": "^3.3.4",
 | 
			
		||||
    "nuxt": "3.7.4",
 | 
			
		||||
    "nuxt-windicss": "^2.6.0",
 | 
			
		||||
    "nuxt": "3.15.4",
 | 
			
		||||
    "pug": "^3.0.2",
 | 
			
		||||
    "vite-plugin-windicss": "^1.9.3"
 | 
			
		||||
    "pug-plain-loader": "^1.1.0",
 | 
			
		||||
    "unocss": "^66.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@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 }}
 | 
			
		||||
              time.text-xs(:datetime="updatedAt || createdAt") {{ date }}
 | 
			
		||||
  // 右侧栏(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")
 | 
			
		||||
      span.font-bold # TAG
 | 
			
		||||
      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 # 归档
 | 
			
		||||
      template(v-for="item in data.filter(x => x.title)" :key="item.id")
 | 
			
		||||
        NuxtLink.block.truncate(
 | 
			
		||||
          class="hover:text-pink-500"
 | 
			
		||||
          class="text-dark-600 hover:text-pink-500 visited:text-gray-500"
 | 
			
		||||
          :to="`/blog/${item.id}`"
 | 
			
		||||
        ) {{ item.title }}
 | 
			
		||||
  // 弹出层编辑器(编辑日志)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
    )
 | 
			
		||||
      NuxtLink(:to="`/gallery/${item.id}`")
 | 
			
		||||
        //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(
 | 
			
		||||
          style="background: linear-gradient(to top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.25))"
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
<template lang="pug">
 | 
			
		||||
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>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import fs from 'fs'
 | 
			
		||||
import path from 'path'
 | 
			
		||||
import { getQuery } from 'h3'
 | 
			
		||||
import webp from 'webp-converter'
 | 
			
		||||
 | 
			
		||||
// 如果当前文件夹是 .output 则使用上一级路径
 | 
			
		||||
@@ -17,31 +16,42 @@ export default defineEventHandler(async event => {
 | 
			
		||||
 | 
			
		||||
    // 处理 GET 请求
 | 
			
		||||
    if (event.node.req.method === 'GET') {
 | 
			
		||||
        const file_name = decodeURI(event.context.params.id)
 | 
			
		||||
        const file_path = path.resolve(dirPath, file_name)
 | 
			
		||||
 | 
			
		||||
        // 检查文件是否存在
 | 
			
		||||
        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)
 | 
			
		||||
        }
 | 
			
		||||
        // 正则提取文件名后缀 1ac7eb2155fa09851c4eeca14.png@w320.webp
 | 
			
		||||
        const filename = decodeURI(event.context.params.id)
 | 
			
		||||
        const match = filename.match(/(.*)@w(\d+)\.(\w+)/)
 | 
			
		||||
 | 
			
		||||
        // 请求原图
 | 
			
		||||
        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 请求
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ if (!fs.existsSync(dirPath)) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default defineEventHandler(async event => {
 | 
			
		||||
 | 
			
		||||
    // 处理 GET 请求(读取image中的图片文件列表)
 | 
			
		||||
    if (event.node.req.method === 'GET') {
 | 
			
		||||
        const files = await fs.promises.readdir(dirPath)
 | 
			
		||||
@@ -30,6 +31,7 @@ export default defineEventHandler(async event => {
 | 
			
		||||
 | 
			
		||||
        return { list }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 处理 POST 请求(FromData上传图片)
 | 
			
		||||
    if (event.node.req.method === 'POST') {
 | 
			
		||||
        const form = formidable({ multiples: true, uploadDir: dirPath })
 | 
			
		||||
@@ -55,4 +57,5 @@ export default defineEventHandler(async event => {
 | 
			
		||||
 | 
			
		||||
        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