Compare commits
31 Commits
046b0ef674
...
main
Author | SHA1 | Date | |
---|---|---|---|
4569d80f03 | |||
384a08ee50 | |||
5aec62fa76 | |||
27f6687802 | |||
21b6bb0b1c | |||
0bdc095f04 | |||
4929c2d404 | |||
e1d6e8dbe9 | |||
bf1eada649 | |||
1b24905fef | |||
f7694d9c05 | |||
3f6d16f6be | |||
a5528eebed | |||
27ec8fc16f | |||
ec60c8ed67 | |||
3672709801 | |||
423cd07cd9 | |||
6c061b1e58 | |||
6d965ccd40 | |||
4274781aee | |||
0baec36757 | |||
bdacf7bfd3 | |||
06de0f2b33 | |||
3b9fae1eea | |||
c557e6980c | |||
85863cb249 | |||
bb48392da4 | |||
194bb77793 | |||
5955c1f4e3 | |||
19e552009a | |||
0e380b34a3 |
11
Dockerfile
11
Dockerfile
@@ -1,7 +1,7 @@
|
||||
############################################################
|
||||
# 1) 依赖层 - 只要锁文件没变,永远复用
|
||||
############################################################
|
||||
FROM node:22-alpine AS deps
|
||||
FROM node:22-bookworm AS deps
|
||||
# 国内加速
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN corepack enable && pnpm install --frozen-lockfile
|
||||
############################################################
|
||||
# 2) 编译层 - 代码变了也不影响 deps 缓存
|
||||
############################################################
|
||||
FROM node:22-alpine AS builder
|
||||
FROM node:22-bookworm AS builder
|
||||
WORKDIR /app
|
||||
|
||||
# 先复用 deps 里的 node_modules
|
||||
@@ -23,12 +23,13 @@ COPY --from=deps /app/node_modules ./node_modules
|
||||
# 再拷源码
|
||||
COPY . .
|
||||
|
||||
# 运行补丁
|
||||
RUN chmod +x patch-ui-pro.sh && ./patch-ui-pro.sh
|
||||
# 运行补丁(脚本使用 bash 语法,需要 bash 执行)
|
||||
RUN chmod +x patch-ui-pro.sh && bash ./patch-ui-pro.sh
|
||||
|
||||
# 注意把内存限制放在这里,避免本地开发时也被硬限制
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
RUN corepack enable && pnpm build
|
||||
# 在 Ubuntu/Jammy 上一次性安装浏览器及其系统依赖,并仅安装 Chromium 以加速
|
||||
RUN corepack enable && npx playwright install --with-deps chromium && pnpm build
|
||||
|
||||
############################################################
|
||||
# 3) 运行层 - 只有可运行文件,最小镜像
|
||||
|
@@ -66,7 +66,7 @@ export default defineAppConfig({
|
||||
}, {
|
||||
icon: 'simple-icons-nuxtdotjs',
|
||||
label: '个人博客',
|
||||
to: 'https://lijue.me',
|
||||
to: 'https://lijue.net',
|
||||
target: '_blank'
|
||||
}]
|
||||
}
|
||||
@@ -77,5 +77,96 @@ export default defineAppConfig({
|
||||
name: 'estel_docs',
|
||||
branch: 'main',
|
||||
rootDir: ''
|
||||
},
|
||||
uiPro: {
|
||||
prose: {
|
||||
code: {
|
||||
base: 'px-1.5 py-0.5 text-sm font-mono font-medium rounded-md inline-block',
|
||||
variants: {
|
||||
color: {
|
||||
new: 'border border-muted text-base font-medium bg-muted bg-gray-100 dark:bg-gray-800 text-[#DD1144]'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
color: 'new'
|
||||
}
|
||||
},
|
||||
pre: {
|
||||
slots: {
|
||||
root: 'relative my-5 group',
|
||||
header: 'flex items-center gap-1.5 border border-muted bg-default border-b-0 relative rounded-t-md px-4 py-3',
|
||||
filename: 'text-default text-sm/6',
|
||||
icon: 'size-5 shrink-0',
|
||||
copy: 'absolute top-[11px] right-[11px] transition',
|
||||
base: 'group font-mono text-sm/6 border border-muted bg-gray-100 dark:bg-gray-800 rounded-md px-4 py-3 whitespace-pre-wrap break-words overflow-x-auto focus:outline-none'
|
||||
}
|
||||
},
|
||||
codeIcon: {
|
||||
terminal: 'i-lucide-terminal',
|
||||
code: 'vscode-icons:file-type-codekit',
|
||||
kali: 'i-devicon:kalilinux',
|
||||
bash: 'i-devicon:powershell',
|
||||
debian: 'i-devicon:debian'
|
||||
},
|
||||
codeCollapse: {
|
||||
slots: {
|
||||
root: 'relative [&_pre]:h-[350px]',
|
||||
footer: 'h-14 absolute inset-x-px bottom-px rounded-b-md flex items-center justify-center',
|
||||
trigger: 'group',
|
||||
triggerIcon: 'group-data-[state=open]:rotate-180'
|
||||
},
|
||||
variants: {
|
||||
open: {
|
||||
true: {
|
||||
root: '[&_pre]:h-auto [&_pre]:min-h-[200px] [&_pre]:max-h-[80vh] [&_pre]:pb-12'
|
||||
},
|
||||
false: {
|
||||
root: '[&_pre]:overflow-hidden',
|
||||
footer: 'bg-gradient-to-t from-muted'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
table: {
|
||||
slots: {
|
||||
root: 'relative my-5 overflow-x-auto mx-auto',
|
||||
base: 'w-auto table-auto border-separate border-spacing-0 rounded-md'
|
||||
}
|
||||
},
|
||||
thead: {
|
||||
base: 'bg-muted'
|
||||
},
|
||||
tbody: {
|
||||
base: ''
|
||||
},
|
||||
tr: {
|
||||
base: '[&:first-child>th:first-child]:rounded-tl-md [&:first-child>th:last-child]:rounded-tr-md [&:last-child>td:first-child]:rounded-bl-md [&:last-child>td:last-child]:rounded-br-md'
|
||||
},
|
||||
th: {
|
||||
base: 'py-3 px-4 font-semibold text-base text-left border-e border-b first:border-s border-t border-muted bg-gray-200 dark:bg-gray-800'
|
||||
},
|
||||
td: {
|
||||
base: 'py-3 px-4 text-sm text-left align-top border-e border-b first:border-s border-muted [&_code]:text-xs/5 [&_p]:my-0 [&_p]:leading-6 [&_ul]:my-0 [&_ol]:my-0 [&_ul]:ps-4.5 [&_ol]:ps-4.5 [&_li]:leading-6 [&_li]:my-0.5'
|
||||
}
|
||||
},
|
||||
blogPost: {
|
||||
slots: {
|
||||
root: 'relative group/blog-post flex flex-col rounded-lg overflow-hidden md:w-8/10 md:ml-10',
|
||||
header: 'relative overflow-hidden aspect-[16/9] w-full pointer-events-none',
|
||||
body: 'min-w-0 flex-1 flex flex-col',
|
||||
footer: '',
|
||||
image: 'object-cover object-top w-full h-full md:p-3',
|
||||
title: 'text-3xl text-pretty font-bold text-highlighted',
|
||||
description: 'mt-1 text-lg text-pretty',
|
||||
authors: 'pt-4 mt-auto flex flex-wrap gap-x-3 gap-y-1.5',
|
||||
avatar: '',
|
||||
meta: 'flex items-center gap-2 mb-2',
|
||||
date: 'text-base',
|
||||
badge: 'text-sm font-bold'
|
||||
}
|
||||
},
|
||||
blogPosts: {
|
||||
base: 'flex flex-col gap-8 lg:gap-y-12'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@@ -21,6 +21,11 @@
|
||||
|
||||
:root {
|
||||
--ui-header-height: --spacing(16);
|
||||
/* 全局圆角与阴影,作为默认值,可被主题覆盖 */
|
||||
--ui-radius: 8px;
|
||||
--ui-button-radius: var(--ui-radius);
|
||||
--ui-card-radius: calc(var(--ui-radius) + 4px);
|
||||
--ui-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* 主题字体样式 - 使用Nuxt Font字体 */
|
||||
@@ -93,6 +98,11 @@
|
||||
--theme-background: #ffffff;
|
||||
--theme-text: #1f2937;
|
||||
--theme-border: #e5e7eb;
|
||||
/* 经典:中等圆角与轻阴影 */
|
||||
--ui-radius: 6px;
|
||||
--ui-button-radius: 6px;
|
||||
--ui-card-radius: 10px;
|
||||
--ui-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.theme-elegant {
|
||||
@@ -100,6 +110,11 @@
|
||||
--theme-background: #fafafa;
|
||||
--theme-text: #374151;
|
||||
--theme-border: #d1d5db;
|
||||
/* 优雅:更大圆角与柔和阴影 */
|
||||
--ui-radius: 8px;
|
||||
--ui-button-radius: 10px;
|
||||
--ui-card-radius: 12px;
|
||||
--ui-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.theme-minimal {
|
||||
@@ -107,6 +122,11 @@
|
||||
--theme-background: #ffffff;
|
||||
--theme-text: #111827;
|
||||
--theme-border: #f3f4f6;
|
||||
/* 简洁:极小圆角,无阴影 */
|
||||
--ui-radius: 3px;
|
||||
--ui-button-radius: 3px;
|
||||
--ui-card-radius: 6px;
|
||||
--ui-shadow: none;
|
||||
}
|
||||
|
||||
/* 深色模式支持 */
|
||||
@@ -127,3 +147,54 @@
|
||||
--theme-text: #ffffff;
|
||||
--theme-border: #1f2937;
|
||||
}
|
||||
|
||||
/* 统一将主题风格映射到常见元素,尽量少侵入、仅做“润色” */
|
||||
/* 图片与代码块采用卡片圆角与轻阴影,增强主题差异感 */
|
||||
/* :where(.prose) img,
|
||||
:where(.prose) pre,
|
||||
:where(.prose) table {
|
||||
border-radius: var(--ui-card-radius);
|
||||
box-shadow: var(--ui-shadow);
|
||||
background-color: var(--theme-background);
|
||||
border: 1px solid var(--theme-border);
|
||||
} */
|
||||
|
||||
/* 按钮采用按钮圆角,保持各主题观感统一 */
|
||||
/* button,
|
||||
[role="button"] {
|
||||
border-radius: var(--ui-button-radius);
|
||||
} */
|
||||
|
||||
/* 常见圆角工具类在不同主题下的“风格化”提升 */
|
||||
/* :where(.theme-classic) .rounded-lg,
|
||||
:where(.theme-classic) .rounded-xl { border-radius: var(--ui-card-radius); }
|
||||
|
||||
:where(.theme-elegant) .rounded-md { border-radius: var(--ui-button-radius); }
|
||||
:where(.theme-elegant) .rounded-lg { border-radius: calc(var(--ui-card-radius) + 2px); }
|
||||
:where(.theme-elegant) .rounded-xl { border-radius: calc(var(--ui-card-radius) + 6px); }
|
||||
|
||||
:where(.theme-minimal) .rounded-md { border-radius: var(--ui-radius); }
|
||||
:where(.theme-minimal) .rounded-lg { border-radius: calc(var(--ui-radius) + 2px); }
|
||||
:where(.theme-minimal) .rounded-xl { border-radius: calc(var(--ui-radius) + 4px); } */
|
||||
|
||||
|
||||
/* Inline math ($...$) 强制行内,不换行 */
|
||||
/* .math-inline,
|
||||
.math.math-inline,
|
||||
mjx-container[display="false"] {
|
||||
display: inline !important;
|
||||
}
|
||||
|
||||
p > mjx-container[display="false"],
|
||||
li > mjx-container[display="false"] {
|
||||
display: inline !important;
|
||||
margin: 0 0.15em !important;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.math-display,
|
||||
.math.math-display,
|
||||
mjx-container[display="true"] {
|
||||
display: block !important;
|
||||
margin: 0.75em 0 !important;
|
||||
} */
|
@@ -1,144 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
// import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
import type { ContentNavigationItem } from '@nuxt/content'
|
||||
|
||||
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
|
||||
const isSettingsOpen = ref(false)
|
||||
const isLoginModalOpen = ref(false)
|
||||
const isRegisterModalOpen = ref(false)
|
||||
|
||||
// const { header } = useAppConfig();
|
||||
|
||||
// 定义 props 来接收侧边栏状态和切换函数
|
||||
interface Props {
|
||||
isSidebarOpen?: boolean
|
||||
toggleSidebar?: () => void
|
||||
}
|
||||
|
||||
const Props = withDefaults(defineProps<Props>(), {
|
||||
isSidebarOpen: false,
|
||||
toggleSidebar: () => { }
|
||||
})
|
||||
|
||||
// 登录注册函数
|
||||
const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
if (type === 'login') {
|
||||
isLoginModalOpen.value = true
|
||||
} else if (type === 'register') {
|
||||
isRegisterModalOpen.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UHeader
|
||||
toggle-side="left"
|
||||
title="Estel Docs"
|
||||
mode="modal"
|
||||
class="bg-gray-50 dark:bg-gray-900"
|
||||
>
|
||||
<template #title>
|
||||
<h6 />
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<LogoPro class="h-5 w-auto mb-4" />
|
||||
<DocsAsideLeftTop />
|
||||
<div class="mt-4 mb-4 border-t border-gray-200 dark:border-gray-700 w-9/10 mx-5" />
|
||||
<UContentNavigation
|
||||
highlight
|
||||
:navigation="navigation"
|
||||
color="primary"
|
||||
type="single"
|
||||
variant="pill"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<UContentSearchButton class="lg:hidden" />
|
||||
<UColorModeButton />
|
||||
<button
|
||||
class=" p-2 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
|
||||
title="页面设置"
|
||||
@click="isSettingsOpen = !isSettingsOpen"
|
||||
>
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
||||
/>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<UTooltip
|
||||
text="Open on GitHub"
|
||||
:kbds="['meta', 'G']"
|
||||
>
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
to="https://github.com/estel-li"
|
||||
target="_blank"
|
||||
icon="simple-icons-github"
|
||||
aria-label="GitHub"
|
||||
/>
|
||||
</UTooltip>
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
class="px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors"
|
||||
@click="handleLoginRegister('login')"
|
||||
>
|
||||
登录
|
||||
</button>
|
||||
<button
|
||||
class="text-sm font-medium text-white bg-primary rounded-md px-3 py-2 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary transition-colors"
|
||||
@click="handleLoginRegister('register')"
|
||||
>
|
||||
注册
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</UHeader>
|
||||
|
||||
<!-- Theme Settings Panel -->
|
||||
<ThemeSettings
|
||||
:is-open="isSettingsOpen"
|
||||
@close="isSettingsOpen = false"
|
||||
/>
|
||||
|
||||
<!-- 登录模态框 -->
|
||||
<UModal
|
||||
v-model:open="isLoginModalOpen"
|
||||
title="登录"
|
||||
:dismissible="false"
|
||||
>
|
||||
<template #body>
|
||||
<authLogin />
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<!-- 注册模态框 -->
|
||||
<UModal
|
||||
v-model:open="isRegisterModalOpen"
|
||||
title="注册"
|
||||
:dismissible="false"
|
||||
>
|
||||
<template #body>
|
||||
<authRegister />
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
@@ -4,7 +4,7 @@
|
||||
:modal="false"
|
||||
:items="[{
|
||||
label: 'Blog',
|
||||
to: 'https://lijue.me'
|
||||
to: 'https://lijue.net'
|
||||
}, {
|
||||
label: 'Docs',
|
||||
to: 'https://docs-template.nuxt.dev/',
|
||||
|
@@ -10,7 +10,7 @@
|
||||
>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="fixed right-0 top-16 h-[calc(100vh-4rem)] w-full sm:w-96 bg-white dark:bg-gray-900 shadow-2xl z-40 overflow-y-auto border-l border-gray-200 dark:border-gray-700 custom-scrollbar"
|
||||
class="fixed right-0 z-51 top-16 h-[calc(100vh-4rem)] w-full sm:w-96 bg-white dark:bg-gray-900 shadow-2xl overflow-y-auto border-l border-gray-200 dark:border-gray-700 custom-scrollbar"
|
||||
>
|
||||
<!-- 头部 -->
|
||||
<div class="sticky top-0 bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700 px-6 py-4">
|
||||
|
@@ -33,13 +33,13 @@ const Props = withDefaults(defineProps<Props>(), {
|
||||
})
|
||||
|
||||
// 登录注册函数
|
||||
const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
if (type === 'login') {
|
||||
isLoginModalOpen.value = true
|
||||
} else if (type === 'register') {
|
||||
isRegisterModalOpen.value = true
|
||||
}
|
||||
}
|
||||
// const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
// if (type === 'login') {
|
||||
// isLoginModalOpen.value = true
|
||||
// } else if (type === 'register') {
|
||||
// isRegisterModalOpen.value = true
|
||||
// }
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -49,6 +49,16 @@ const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
mode="modal"
|
||||
class="bg-gray-50 dark:bg-gray-900"
|
||||
>
|
||||
<template #toggle="{ open, toggle }">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
class="lg:hidden"
|
||||
:icon="open ? 'lucide-x' : 'simple-icons-microsoft'"
|
||||
aria-label="切换菜单"
|
||||
@click="toggle"
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
<h6 />
|
||||
</template>
|
||||
@@ -108,7 +118,7 @@ const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
aria-label="GitHub"
|
||||
/>
|
||||
</UTooltip>
|
||||
<div class="flex items-center space-x-2">
|
||||
<!-- <div class="flex items-center space-x-2">
|
||||
<button
|
||||
class="px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors"
|
||||
@click="handleLoginRegister('login')"
|
||||
@@ -121,7 +131,7 @@ const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
>
|
||||
注册
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
</template>
|
||||
</UHeader>
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="relative w-full h-64 md:h-80 lg:h-96 overflow-hidden rounded-xl">
|
||||
<!-- 背景图片 -->
|
||||
<NuxtImg
|
||||
src="https://lijue-me.oss-cn-chengdu.aliyuncs.com/%E8%83%8C%E6%99%AF.jpg"
|
||||
src="https://img.jiwei.xin/%E8%83%8C%E6%99%AF.jpg"
|
||||
alt="Blog Hero"
|
||||
class="w-full h-full object-cover"
|
||||
placeholder
|
||||
|
148
app/components/blog/BlogLists.vue
Normal file
148
app/components/blog/BlogLists.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<script setup lang="ts">
|
||||
// 定义组件参数类型
|
||||
interface Props {
|
||||
perPage?: number // 每页显示的文章数,默认 12
|
||||
maxPages?: number // 最大页数,默认 15
|
||||
}
|
||||
|
||||
// 接收 props,设置默认值
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
perPage: 12,
|
||||
maxPages: 15
|
||||
})
|
||||
|
||||
// 分页状态
|
||||
const currentPage = ref(1)
|
||||
|
||||
// 获取所有博客文章
|
||||
const { data: allArticles } = await useAsyncData('blog-all-articles', () => {
|
||||
return queryCollection('blog')
|
||||
.select('path', 'title', 'description', 'img', 'date')
|
||||
.where('path', 'NOT LIKE', '%navigation%')
|
||||
.all()
|
||||
})
|
||||
|
||||
// 按时间排序的文章列表
|
||||
const sortedArticles = computed(() => {
|
||||
if (!allArticles.value) return []
|
||||
|
||||
return [...allArticles.value].sort((a, b) => {
|
||||
// 如果没有日期,排在最后
|
||||
if (!a.date && !b.date) return 0
|
||||
if (!a.date) return 1
|
||||
if (!b.date) return -1
|
||||
|
||||
// 按日期降序排列(最新的在前面)
|
||||
const dateA = new Date(a.date)
|
||||
const dateB = new Date(b.date)
|
||||
return dateB.getTime() - dateA.getTime()
|
||||
})
|
||||
})
|
||||
|
||||
// 计算总页数,限制最大页数
|
||||
const totalPages = computed(() => {
|
||||
if (!sortedArticles.value) return 1
|
||||
const calculatedPages = Math.ceil(sortedArticles.value.length / props.perPage)
|
||||
return Math.min(calculatedPages, props.maxPages)
|
||||
})
|
||||
|
||||
// 计算当前页显示的文章
|
||||
const paginatedArticles = computed(() => {
|
||||
if (!sortedArticles.value) return []
|
||||
|
||||
const startIndex = (currentPage.value - 1) * props.perPage
|
||||
const endIndex = startIndex + props.perPage
|
||||
|
||||
return sortedArticles.value.slice(startIndex, endIndex)
|
||||
})
|
||||
|
||||
// 格式化日期函数
|
||||
const formatDate = (date: string | Date | null | undefined) => {
|
||||
if (!date) return '未知时间'
|
||||
try {
|
||||
const d = new Date(date)
|
||||
return d.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
} catch {
|
||||
return '未知时间'
|
||||
}
|
||||
}
|
||||
|
||||
// 从路径中提取分类标签
|
||||
const getCategoryFromPath = (path: string) => {
|
||||
// 路径格式: /blog/技术栈/xxx 或 /blog/AI/xxx 或 /blog/生活/xxx
|
||||
const match = path.match(/\/blog\/(.+?)\//)
|
||||
return match ? match[1] : '技术栈'
|
||||
}
|
||||
|
||||
// 转换文章数据为 UBlogPosts 需要的格式
|
||||
const blogPosts = computed(() => {
|
||||
if (!paginatedArticles.value) return []
|
||||
|
||||
return paginatedArticles.value.map(article => ({
|
||||
title: article.title || '未命名文章',
|
||||
description: article.description || '',
|
||||
date: formatDate(article.date),
|
||||
image: article.img || '/images/default-blog.jpg',
|
||||
to: article.path,
|
||||
badge: {
|
||||
label: getCategoryFromPath(article.path),
|
||||
color: 'primary' as const,
|
||||
variant: 'subtle' as const
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
// 监听页面变化,确保页面在有效范围内
|
||||
watch(currentPage, (newPage) => {
|
||||
if (newPage > totalPages.value && totalPages.value > 0) {
|
||||
currentPage.value = totalPages.value
|
||||
} else if (newPage < 1) {
|
||||
currentPage.value = 1
|
||||
}
|
||||
})
|
||||
|
||||
// 当文章数据变化时,重置到第一页
|
||||
watch(sortedArticles, () => {
|
||||
if (currentPage.value > totalPages.value && totalPages.value > 0) {
|
||||
currentPage.value = 1
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full bg-gray-50 dark:bg-gray-900 min-h-screen">
|
||||
<div class="max-w-5xl mx-auto md:px-6 lg:px-8 py-8">
|
||||
<!-- 有文章时显示内容 -->
|
||||
<div v-if="sortedArticles && sortedArticles.length > 0">
|
||||
<div class="">
|
||||
<UBlogPosts
|
||||
orientation="vertical"
|
||||
:posts="blogPosts"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 分页控件 -->
|
||||
<div class="flex justify-center pt-8 border-t border-gray-200 dark:border-gray-700">
|
||||
<div class="pt-6">
|
||||
<UPagination
|
||||
v-model:page="currentPage"
|
||||
:total="sortedArticles.length"
|
||||
:items-per-page="props.perPage"
|
||||
:max="7"
|
||||
size="md"
|
||||
show-edges
|
||||
:show-first="totalPages > 7"
|
||||
:show-last="totalPages > 7"
|
||||
color="primary"
|
||||
variant="outline"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@@ -18,7 +18,7 @@
|
||||
|
||||
<!-- ::card
|
||||
---
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250727184358217.png
|
||||
img: https://img.jiwei.xin/20250727184358217.png
|
||||
---
|
||||
#title
|
||||
Image Card
|
||||
|
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<h1
|
||||
:id
|
||||
class="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl"
|
||||
:class="['scroll-m-20 font-extrabold tracking-tight', themeSizeClass]"
|
||||
:style="themeTextShadowStyle"
|
||||
>
|
||||
<NuxtLink
|
||||
v-if="generate"
|
||||
@@ -18,4 +19,26 @@ const { id } = defineProps<{ id?: string }>()
|
||||
|
||||
const { headings } = useRuntimeConfig().public.mdc
|
||||
const generate = computed(() => id && ((typeof headings?.anchorLinks === 'boolean' && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === 'object' && headings?.anchorLinks?.h1)))
|
||||
|
||||
// 读取主题,按主题做轻量风格差异:
|
||||
// - classic: 无阴影,正常字号
|
||||
// - elegant: 有文字阴影,正常字号
|
||||
// - minimal: 无阴影,较小字号
|
||||
const { selectedTheme } = useTheme()
|
||||
|
||||
// 这个计算属性 themeSizeClass 用于根据当前主题(selectedTheme)动态设置 h1 标题的字号样式:
|
||||
// - 如果主题是 minimal,则使用较小字号 'text-2xl lg:text-3xl'
|
||||
// - 否则(classic 或 elegant),使用较大字号 'text-3xl lg:text-4xl'
|
||||
const themeSizeClass = computed(() => {
|
||||
return selectedTheme.value === 'classic'
|
||||
? 'text-2xl lg:text-3xl underline decoration-3 decoration-primary dark:decoration-gray-700 underline-offset-8 mb-3'
|
||||
: 'text-3xl lg:text-4xl'
|
||||
})
|
||||
|
||||
const themeTextShadowStyle = computed(() => {
|
||||
if (selectedTheme.value === 'elegant') {
|
||||
return { textShadow: '0 2px 8px rgba(0,0,0,0.15)' }
|
||||
}
|
||||
return { textShadow: 'none' }
|
||||
})
|
||||
</script>
|
||||
|
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<h2
|
||||
:id
|
||||
class="scroll-m-20 border-b border-gray-200 dark:border-gray-700 pb-2 text-3xl font-semibold tracking-tight transition-colors [&:not(:first-child)]:mt-10 mb-2"
|
||||
:class="['scroll-m-20 inline-block text-white font-semibold tracking-tight transition-colors [&:not(:first-child)]:mt-10 mb-2', themeSizeClass, themePaddingClass]"
|
||||
:style="h2Style"
|
||||
>
|
||||
<NuxtLink
|
||||
v-if="id && generate"
|
||||
:to="`#${id}`"
|
||||
class="no-underline"
|
||||
>
|
||||
<slot />
|
||||
</NuxtLink>
|
||||
@@ -16,6 +18,37 @@
|
||||
<script setup lang="ts">
|
||||
const { id } = defineProps<{ id?: string }>()
|
||||
|
||||
// 读取主题,用主题主色 + 圆角变量渲染带底色的 H2
|
||||
const { selectedTheme, selectedThemeColor, customColor, themeColors } = useTheme()
|
||||
|
||||
// 这个计算属性 themeSizeClass 用于根据当前主题(selectedTheme)动态设置 h1 标题的字号样式:
|
||||
// - 如果主题是 minimal,则使用较小字号 'text-2xl lg:text-3xl'
|
||||
// - 否则(classic 或 elegant),使用较大字号 'text-3xl lg:text-4xl'
|
||||
const themeSizeClass = computed(() => {
|
||||
return selectedTheme.value === 'classic'
|
||||
? 'text-xl lg:text-1xl '
|
||||
: 'text-1xl lg:text-2xl'
|
||||
})
|
||||
|
||||
// 经典:更贴合文字(小内边距);优雅/简洁:略大(更舒展)
|
||||
const themePaddingClass = computed(() => {
|
||||
return selectedTheme.value === 'classic'
|
||||
? 'px-1 py-0.5'
|
||||
: 'px-5 py-2'
|
||||
})
|
||||
|
||||
const { headings } = useRuntimeConfig().public.mdc
|
||||
const generate = computed(() => id && ((typeof headings?.anchorLinks === 'boolean' && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === 'object' && headings?.anchorLinks?.h2)))
|
||||
|
||||
// 背景颜色取主题主色;若为自定义颜色,则取 customColor
|
||||
const primaryHex = computed(() => {
|
||||
if (selectedThemeColor.value === 'custom') return customColor.value
|
||||
const found = themeColors.find(c => c.value === selectedThemeColor.value)
|
||||
return found?.color || '#3B82F6' // fallback 经典蓝
|
||||
})
|
||||
|
||||
const h2Style = computed(() => ({
|
||||
backgroundColor: primaryHex.value,
|
||||
borderRadius: 'var(--ui-card-radius)'
|
||||
}))
|
||||
</script>
|
||||
|
@@ -1,8 +1,16 @@
|
||||
<template>
|
||||
<h3
|
||||
:id
|
||||
class="scroll-m-20 text-2xl font-semibold tracking-tight [&:not(:first-child)]:mt-8 mb-3"
|
||||
:class="['scroll-m-20 font-semibold tracking-tight [&:not(:first-child)]:mt-8 mb-3 flex items-center gap-2', themeSizeClass, themeDecorClass]"
|
||||
:style="h3Style"
|
||||
>
|
||||
<span
|
||||
v-if="showLeadingLine"
|
||||
aria-hidden="true"
|
||||
class="inline-block"
|
||||
:class="themeLineClass"
|
||||
:style="lineStyle"
|
||||
/>
|
||||
<NuxtLink
|
||||
v-if="id && generate"
|
||||
:to="`#${id}`"
|
||||
@@ -18,4 +26,65 @@ const { id } = defineProps<{ id?: string }>()
|
||||
|
||||
const { headings } = useRuntimeConfig().public.mdc
|
||||
const generate = computed(() => id && ((typeof headings?.anchorLinks === 'boolean' && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === 'object' && headings?.anchorLinks?.h3)))
|
||||
|
||||
// 主题风格:
|
||||
// - classic:左侧竖线(主色),无额外背景
|
||||
// - elegant:卡片式浅色主色背景 + 左侧竖线
|
||||
// - minimal:底部虚线
|
||||
const { selectedTheme, selectedThemeColor, customColor, themeColors } = useTheme()
|
||||
|
||||
const themeSizeClass = computed(() => {
|
||||
return selectedTheme.value === 'classic'
|
||||
? 'text-xl lg:text-1xl'
|
||||
: 'text-1xl lg:text-2xl'
|
||||
})
|
||||
|
||||
const isClassic = computed(() => selectedTheme.value === 'classic')
|
||||
const isElegant = computed(() => selectedTheme.value === 'minimal')
|
||||
const isMinimal = computed(() => selectedTheme.value === 'elegant')
|
||||
|
||||
const showLeadingLine = computed(() => isClassic.value || isMinimal.value || isElegant.value)
|
||||
|
||||
const primaryHex = computed(() => {
|
||||
if (selectedThemeColor.value === 'custom') return customColor.value
|
||||
const found = themeColors.find(c => c.value === selectedThemeColor.value)
|
||||
return found?.color || '#3B82F6'
|
||||
})
|
||||
|
||||
const themeDecorClass = computed(() => {
|
||||
if (isElegant.value) return 'border-b border-dashed pb-1 border-primary dark:border-primary'
|
||||
if (isMinimal.value) return 'px-3 py-2'
|
||||
return ''
|
||||
})
|
||||
|
||||
const themeLineClass = computed(() => {
|
||||
return isClassic.value ? 'w-1 h-[0.9em]' : 'w-1 h-[1em]'
|
||||
})
|
||||
|
||||
const lineStyle = computed(() => ({
|
||||
backgroundColor: primaryHex.value,
|
||||
borderRadius: '4px'
|
||||
}))
|
||||
|
||||
function hexToRgba(hex: string, alpha: number): string {
|
||||
const normalized = String(hex).replace('#', '')
|
||||
const bigint = parseInt(normalized.length === 3
|
||||
? normalized.split('').map(ch => ch + ch).join('')
|
||||
: normalized, 16)
|
||||
const r = (bigint >> 16) & 255
|
||||
const g = (bigint >> 8) & 255
|
||||
const b = bigint & 255
|
||||
return `rgba(${r}, ${g}, ${b}, ${Math.min(Math.max(alpha, 0), 1)})`
|
||||
}
|
||||
|
||||
const h3Style = computed(() => {
|
||||
if (isMinimal.value) {
|
||||
return {
|
||||
backgroundColor: hexToRgba(primaryHex.value, 0.08),
|
||||
border: `1px solid ${hexToRgba(primaryHex.value, 0.2)}`,
|
||||
borderRadius: 'var(--ui-card-radius)'
|
||||
}
|
||||
}
|
||||
return {}
|
||||
})
|
||||
</script>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<h4
|
||||
:id
|
||||
class="scroll-m-20 text-xl font-semibold tracking-tight [&:not(:first-child)]:mt-6 mb-2"
|
||||
:class="['scroll-m-20 font-semibold text-primary tracking-tight [&:not(:first-child)]:mt-6 mb-2', themeSizeClass]"
|
||||
>
|
||||
<NuxtLink
|
||||
v-if="id && generate"
|
||||
@@ -18,4 +18,11 @@ const { id } = defineProps<{ id?: string }>()
|
||||
|
||||
const { headings } = useRuntimeConfig().public.mdc
|
||||
const generate = computed(() => id && ((typeof headings?.anchorLinks === 'boolean' && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === 'object' && headings?.anchorLinks?.h4)))
|
||||
|
||||
const { selectedTheme } = useTheme()
|
||||
const themeSizeClass = computed(() => {
|
||||
return selectedTheme.value === 'classic'
|
||||
? 'text-xl lg:text-1xl'
|
||||
: 'text-1xl lg:text-2xl'
|
||||
})
|
||||
</script>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<h5
|
||||
:id
|
||||
class="scroll-m-20 text-lg font-semibold tracking-tight [&:not(:first-child)]:mt-6"
|
||||
:class="['scroll-m-20 font-bold text-primary tracking-tight [&:not(:first-child)]:mt-6', themeSizeClass]"
|
||||
>
|
||||
<NuxtLink
|
||||
v-if="id && generate"
|
||||
@@ -18,4 +18,11 @@ const { id } = defineProps<{ id?: string }>()
|
||||
|
||||
const { headings } = useRuntimeConfig().public.mdc
|
||||
const generate = computed(() => id && ((typeof headings?.anchorLinks === 'boolean' && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === 'object' && headings?.anchorLinks?.h5)))
|
||||
|
||||
const { selectedTheme } = useTheme()
|
||||
const themeSizeClass = computed(() => {
|
||||
return selectedTheme.value === 'classic'
|
||||
? 'text-lg lg:text-xl'
|
||||
: 'text-xl lg:text-1xl'
|
||||
})
|
||||
</script>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<h6
|
||||
<h5
|
||||
:id
|
||||
class="scroll-m-20 text-lg font-semibold tracking-tight [&:not(:first-child)]:mt-6"
|
||||
:class="['scroll-m-20 font-normal text-primary tracking-tight [&:not(:first-child)]:mt-6', themeSizeClass]"
|
||||
>
|
||||
<NuxtLink
|
||||
v-if="id && generate"
|
||||
@@ -10,12 +10,19 @@
|
||||
<slot />
|
||||
</NuxtLink>
|
||||
<slot v-else />
|
||||
</h6>
|
||||
</h5>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { id } = defineProps<{ id?: string }>()
|
||||
|
||||
const { headings } = useRuntimeConfig().public.mdc
|
||||
const generate = computed(() => id && ((typeof headings?.anchorLinks === 'boolean' && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === 'object' && headings?.anchorLinks?.h6)))
|
||||
const generate = computed(() => id && ((typeof headings?.anchorLinks === 'boolean' && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === 'object' && headings?.anchorLinks?.h5)))
|
||||
|
||||
const { selectedTheme } = useTheme()
|
||||
const themeSizeClass = computed(() => {
|
||||
return selectedTheme.value === 'classic'
|
||||
? 'text-lg lg:text-xl'
|
||||
: 'text-xl lg:text-1xl'
|
||||
})
|
||||
</script>
|
||||
|
5
app/components/content/ProseStrong.vue
Normal file
5
app/components/content/ProseStrong.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<strong class="text-primary">
|
||||
<slot />
|
||||
</strong>
|
||||
</template>
|
@@ -3,13 +3,13 @@ import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items = ref<NavigationMenuItem[][]>([
|
||||
[
|
||||
{
|
||||
label: '稷维科技',
|
||||
icon: 'lucide-globe',
|
||||
to: 'https://www.xajiwei.com',
|
||||
target: '_blank'
|
||||
// {
|
||||
// label: '稷维科技',
|
||||
// icon: 'lucide-globe',
|
||||
// to: 'https://www.xajiwei.com',
|
||||
// target: '_blank'
|
||||
|
||||
},
|
||||
// },
|
||||
|
||||
{
|
||||
label: '博客',
|
||||
@@ -19,7 +19,7 @@ const items = ref<NavigationMenuItem[][]>([
|
||||
|
||||
{
|
||||
label: '简单文档',
|
||||
icon: 'lucide-book',
|
||||
icon: 'simple-icons:readdotcv',
|
||||
to: '/docs'
|
||||
},
|
||||
|
||||
|
@@ -33,13 +33,13 @@ const Props = withDefaults(defineProps<Props>(), {
|
||||
})
|
||||
|
||||
// 登录注册函数
|
||||
const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
if (type === 'login') {
|
||||
isLoginModalOpen.value = true
|
||||
} else if (type === 'register') {
|
||||
isRegisterModalOpen.value = true
|
||||
}
|
||||
}
|
||||
// const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
// if (type === 'login') {
|
||||
// isLoginModalOpen.value = true
|
||||
// } else if (type === 'register') {
|
||||
// isRegisterModalOpen.value = true
|
||||
// }
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -49,6 +49,16 @@ const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
mode="modal"
|
||||
class="bg-gray-50 dark:bg-gray-900"
|
||||
>
|
||||
<template #toggle="{ open, toggle }">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
class="lg:hidden"
|
||||
:icon="open ? 'lucide-x' : 'simple-icons-microsoft'"
|
||||
aria-label="切换菜单"
|
||||
@click="toggle"
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
<h6 />
|
||||
</template>
|
||||
@@ -110,7 +120,7 @@ const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
aria-label="GitHub"
|
||||
/>
|
||||
</UTooltip>
|
||||
<div class="flex items-center space-x-2">
|
||||
<!-- <div class="flex items-center space-x-2">
|
||||
<button
|
||||
class="px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors"
|
||||
@click="handleLoginRegister('login')"
|
||||
@@ -123,7 +133,7 @@ const handleLoginRegister = (type: 'login' | 'register') => {
|
||||
>
|
||||
注册
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
</template>
|
||||
</UHeader>
|
||||
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import WxShare from '~/components/shared/wxShare.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
title?: string
|
||||
desc?: string
|
||||
imgUrl?: string
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const toast = useToast()
|
||||
@@ -12,6 +19,12 @@ const origin = computed(() => requestURL.origin)
|
||||
const mdcLink = computed(() => `${origin.value}${decodeURIComponent(route.path)}`)
|
||||
const markdownLink = computed(() => `${origin.value}/raw${decodeURIComponent(route.path)}.md`)
|
||||
|
||||
const enableWxShare = ref(false)
|
||||
|
||||
const shareTitle = computed(() => props.title ?? (import.meta.client ? (document.title || 'Estel Docs') : ''))
|
||||
const shareDesc = computed(() => props.desc ?? (import.meta.client ? (document.title || 'Estel Docs') : ''))
|
||||
const shareImg = computed(() => props.imgUrl ?? '/images/default-blog.jpg')
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: '复制链接',
|
||||
@@ -26,6 +39,19 @@ const items = [
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '分享到微信',
|
||||
icon: 'simple-icons:wechat',
|
||||
onSelect() {
|
||||
enableWxShare.value = true
|
||||
toast.add({
|
||||
title: '已为本页配置微信分享,请在微信内点击右上角分享',
|
||||
icon: 'lucide-check-circle',
|
||||
color: 'success'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
label: '查看 Markdown',
|
||||
icon: 'simple-icons:markdown',
|
||||
@@ -79,4 +105,15 @@ const items = [
|
||||
/>
|
||||
</UDropdownMenu>
|
||||
</UButtonGroup>
|
||||
|
||||
<!-- 懒加载式挂载微信分享逻辑,避免在非微信环境造成无谓请求 -->
|
||||
<ClientOnly>
|
||||
<WxShare
|
||||
v-if="enableWxShare"
|
||||
:url="mdcLink"
|
||||
:title="shareTitle"
|
||||
:desc="shareDesc"
|
||||
:img-url="shareImg"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
@@ -11,11 +11,8 @@ const props = defineProps<{
|
||||
// 仅在客户端挂载后执行,避免 SSR 阶段访问 window/location
|
||||
onMounted(async () => {
|
||||
try {
|
||||
console.log('[WxShare] mounted with props:', { ...props })
|
||||
await loadWxSdk()
|
||||
console.log('[WxShare] wx sdk loaded:', !!(window as any).wx)
|
||||
const { appId, timestamp, nonceStr, signature } = await getWxConfig()
|
||||
console.log('[WxShare] got config:', { appId, timestamp, nonceStr, signature: signature.slice(0, 8) + '...' })
|
||||
setupShare(appId, timestamp, nonceStr, signature)
|
||||
} catch (err) {
|
||||
console.error('[WxShare] init error:', err)
|
||||
@@ -34,13 +31,33 @@ watch(() => props.url, async (newUrl, oldUrl) => {
|
||||
}
|
||||
})
|
||||
|
||||
type WeChat = {
|
||||
config: (cfg: {
|
||||
debug?: boolean
|
||||
appId: string
|
||||
timestamp: number
|
||||
nonceStr: string
|
||||
signature: string
|
||||
jsApiList: string[]
|
||||
}) => void
|
||||
ready: (cb: () => void) => void
|
||||
error: (cb: (e: unknown) => void) => void
|
||||
updateTimelineShareData: (opts: { title: string, link: string, imgUrl: string, success?: () => void }) => void
|
||||
updateAppMessageShareData: (opts: { title: string, desc: string, link: string, imgUrl: string, success?: () => void }) => void
|
||||
}
|
||||
|
||||
function getWx(): WeChat | undefined {
|
||||
if (typeof window === 'undefined') return undefined
|
||||
const w = window as unknown as { wx?: WeChat }
|
||||
return w.wx
|
||||
}
|
||||
|
||||
function loadWxSdk(): Promise<void> {
|
||||
console.log('loadWxSdk')
|
||||
if (typeof window === 'undefined') return Promise.resolve()
|
||||
if ((window as any).wx) return Promise.resolve()
|
||||
if (getWx()) return Promise.resolve()
|
||||
return new Promise((resolve, reject) => {
|
||||
const existing = document.getElementById('wx-jssdk') as HTMLScriptElement | null
|
||||
if (existing && (window as any).wx) return resolve()
|
||||
if (existing && getWx()) return resolve()
|
||||
const script = existing ?? document.createElement('script')
|
||||
if (!existing) {
|
||||
script.id = 'wx-jssdk'
|
||||
@@ -62,7 +79,8 @@ async function getWxConfig(): Promise<{ appId: string, timestamp: number, nonceS
|
||||
}
|
||||
|
||||
function setupShare(appId: string, timestamp: number, nonceStr: string, signature: string) {
|
||||
const wx = (window as any).wx
|
||||
const wx = getWx()
|
||||
if (!wx) return
|
||||
const shareTitle = props.title || document.title || ''
|
||||
const shareDesc = props.desc || document.title || ''
|
||||
const shareLink = props.url
|
||||
@@ -94,12 +112,10 @@ function setupShare(appId: string, timestamp: number, nonceStr: string, signatur
|
||||
})
|
||||
})
|
||||
|
||||
wx.error((e: unknown) => {
|
||||
wx.error((e) => {
|
||||
console.error('[WxShare] wx.error:', e)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@@ -41,7 +41,7 @@
|
||||
* </template>
|
||||
* ```
|
||||
*/
|
||||
import { ref, watch } from 'vue'
|
||||
import { ref, watch, getCurrentInstance, onMounted } from 'vue'
|
||||
|
||||
// 定义各种选项
|
||||
const themes = [
|
||||
@@ -224,18 +224,34 @@ export function useTheme() {
|
||||
}
|
||||
}
|
||||
|
||||
// 确保在客户端时立即初始化
|
||||
// 确保在客户端时初始化
|
||||
if (import.meta.client) {
|
||||
// 立即初始化,避免闪烁
|
||||
initializeTheme()
|
||||
|
||||
// 确保在DOM准备好后再次应用
|
||||
onMounted(() => {
|
||||
applyThemeVariables()
|
||||
if (selectedThemeColor.value === 'custom') {
|
||||
applyCustomColor()
|
||||
const instance = getCurrentInstance()
|
||||
if (instance) {
|
||||
// 在组件 setup 上下文中,安全使用 onMounted
|
||||
onMounted(() => {
|
||||
applyThemeVariables()
|
||||
if (selectedThemeColor.value === 'custom') {
|
||||
applyCustomColor()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 非组件上下文(如插件)中,等待文档就绪后应用
|
||||
const run = () => {
|
||||
applyThemeVariables()
|
||||
if (selectedThemeColor.value === 'custom') {
|
||||
applyCustomColor()
|
||||
}
|
||||
}
|
||||
})
|
||||
if (document.readyState === 'loading') {
|
||||
window.addEventListener('DOMContentLoaded', () => run(), { once: true })
|
||||
} else {
|
||||
run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const resetSettings = () => {
|
||||
|
46
app/layouts/default.vue
Normal file
46
app/layouts/default.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 flex">
|
||||
<!-- 移动端遮罩层 -->
|
||||
<div
|
||||
v-if="isSidebarOpen"
|
||||
class="fixed inset-0 bg-gray-900/50 z-40 lg:hidden"
|
||||
@click="isSidebarOpen = false"
|
||||
/>
|
||||
|
||||
<!-- 侧边栏 -->
|
||||
<DocsSidebar
|
||||
class="fixed top-0 bottom-0 z-50 transition-transform duration-300 ease-in-out"
|
||||
:class="isSidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'"
|
||||
/>
|
||||
|
||||
<!-- Right Content Area -->
|
||||
<div class="flex-1 lg:ml-64 flex flex-col min-w-0">
|
||||
<!-- Fixed Header -->
|
||||
<DocsHeader
|
||||
:is-sidebar-open="isSidebarOpen"
|
||||
:toggle-sidebar="toggleSidebar"
|
||||
/>
|
||||
|
||||
<!-- Main Content -->
|
||||
<UMain class="flex-1 overflow-y-auto">
|
||||
<div class="">
|
||||
<slot />
|
||||
</div>
|
||||
</UMain>
|
||||
|
||||
<!-- Footer -->
|
||||
<AppFooter />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const isSidebarOpen = ref(false)
|
||||
|
||||
// 切换侧边栏
|
||||
const toggleSidebar = () => {
|
||||
isSidebarOpen.value = !isSidebarOpen.value
|
||||
}
|
||||
</script>
|
@@ -23,7 +23,7 @@
|
||||
|
||||
<!-- Main Content -->
|
||||
<UMain class="flex-1 overflow-y-auto">
|
||||
<div class="mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div class="mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<slot />
|
||||
</div>
|
||||
</UMain>
|
||||
|
@@ -49,7 +49,7 @@ if (!page.value) {
|
||||
}
|
||||
|
||||
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
||||
return queryCollectionItemSurroundings('docs', route.path, {
|
||||
return queryCollectionItemSurroundings('blog', route.path, {
|
||||
fields: ['description']
|
||||
})
|
||||
})
|
||||
@@ -98,37 +98,6 @@ const links = computed(() => {
|
||||
|
||||
return [...links, ...(appConfig.toc?.bottom?.links || [])].filter(Boolean)
|
||||
})
|
||||
|
||||
// ===== 微信分享(测试按钮用)=====
|
||||
const wxShareActive = ref(false)
|
||||
// const contentRoot = ref<HTMLElement | null>(null)
|
||||
|
||||
const shareLink = computed(() => 'https://lijue.me' + decodeURIComponent(path.value))
|
||||
const shareTitle = computed(() => title)
|
||||
const shareDesc = computed(() => description || title)
|
||||
const shareImg = page?.value?.img
|
||||
// const shareImg = ref<string>('/images/default-blog.jpg')
|
||||
|
||||
// onMounted(() => {
|
||||
// // 从正文中抓取第一张图片作为分享图
|
||||
// const el = contentRoot.value
|
||||
// const firstImg = el?.querySelector('img') as HTMLImageElement | null
|
||||
// if (firstImg?.src) {
|
||||
// shareImg.value = firstImg.src
|
||||
// }
|
||||
// })
|
||||
|
||||
// Toast:点击测试分享后给出指引
|
||||
const toast = useToast()
|
||||
function handleShareClick() {
|
||||
wxShareActive.value = true
|
||||
toast.add({
|
||||
title: '已获取分享接口',
|
||||
description: '点击右上角分享吧',
|
||||
duration: 3000,
|
||||
icon: 'i-lucide-share-2'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -144,6 +113,10 @@ function handleShareClick() {
|
||||
wrapper: 'flex-row items-center flex-wrap justify-between '
|
||||
}"
|
||||
>
|
||||
<span
|
||||
v-if="page.date"
|
||||
class="text-sm text-pretty text-muted mt-4"
|
||||
> {{ page.date }} </span>
|
||||
<template #links>
|
||||
<UButton
|
||||
v-for="(link, index) in page.links"
|
||||
@@ -152,7 +125,11 @@ function handleShareClick() {
|
||||
v-bind="link"
|
||||
/>
|
||||
|
||||
<DocsPageHeaderLinks />
|
||||
<DocsPageHeaderLinks
|
||||
:title="page.title"
|
||||
:desc="page.description || page.seo?.description || page.title"
|
||||
:img-url="page.img || '/images/default-blog.jpg'"
|
||||
/>
|
||||
</template>
|
||||
</UPageHeader>
|
||||
|
||||
@@ -178,7 +155,7 @@ function handleShareClick() {
|
||||
编辑页面
|
||||
</UButton>
|
||||
or
|
||||
<!-- <UButton
|
||||
<UButton
|
||||
variant="link"
|
||||
color="neutral"
|
||||
:to="`${appConfig.github.url}/issues/new/choose`"
|
||||
@@ -187,29 +164,19 @@ function handleShareClick() {
|
||||
:ui="{ leadingIcon: 'size-4' }"
|
||||
>
|
||||
提交问题
|
||||
</UButton> -->
|
||||
|
||||
<UButton
|
||||
variant="link"
|
||||
color="neutral"
|
||||
icon="lucide-share-2"
|
||||
:ui="{ leadingIcon: 'size-4' }"
|
||||
@click="handleShareClick()"
|
||||
>
|
||||
微信分享
|
||||
</UButton>
|
||||
</div>
|
||||
</USeparator>
|
||||
<UContentSurround :surround="surround" />
|
||||
|
||||
<!-- 激活后挂载分享组件(无可视内容) -->
|
||||
<!-- 激活后挂载分享组件(无可视内容)
|
||||
<SharedWxShare
|
||||
v-if="wxShareActive"
|
||||
:url="shareLink"
|
||||
:title="shareTitle"
|
||||
:desc="shareDesc"
|
||||
:img-url="shareImg"
|
||||
/>
|
||||
/> -->
|
||||
</UPageBody>
|
||||
|
||||
<template
|
||||
|
@@ -20,6 +20,7 @@ useSeoMeta({
|
||||
<template>
|
||||
<div>
|
||||
<!-- <BlogHero /> -->
|
||||
<BlogIndexBlog />
|
||||
<!-- <BlogIndexBlog /> -->
|
||||
<BlogLists />
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
import { useSeoMeta } from '#imports'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'docs'
|
||||
layout: 'default'
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
@@ -14,18 +14,18 @@ useSeoMeta({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen bg-gradient-to-br from-blue-50 via-white to-indigo-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900">
|
||||
<div class="px-4 py-3 md:py-6 bg-gradient-to-b from-blue-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900">
|
||||
<!-- Hero 区域 -->
|
||||
<div class="max-w-6xl mx-auto px-4 py-20">
|
||||
<div class="max-w-6xl mx-auto py-1">
|
||||
<!-- 主要内容 -->
|
||||
<div class="text-center mb-16">
|
||||
<h1 class="text-5xl md:text-7xl font-bold text-gray-900 dark:text-white mb-6">
|
||||
<div class="text-center mb-8 md:mb-16">
|
||||
<h1 class="text-4xl md:text-7xl font-bold text-gray-900 dark:text-white mb-3 md:mb-6">
|
||||
Estel Hub
|
||||
</h1>
|
||||
<p class="text-xl md:text-2xl text-gray-600 dark:text-gray-300 mb-8 max-w-3xl mx-auto">
|
||||
<p class="text-xl md:text-2xl text-gray-600 dark:text-gray-300 mb-4 md:mb-8 max-w-3xl mx-auto">
|
||||
简约而强大的文档系统,衍生而出Blog系统,为技术分享而生
|
||||
</p>
|
||||
<p class="text-lg text-gray-500 dark:text-gray-400 mb-12 max-w-2xl mx-auto">
|
||||
<p class="md:text-lg text-sm text-gray-500 dark:text-gray-400 max-w-2xl mx-auto">
|
||||
基于 Nuxt 4 构建,支持 Markdown 文档管理和博客发布,提供完整的现代化内容管理解决方案
|
||||
</p>
|
||||
</div>
|
||||
@@ -35,10 +35,10 @@ useSeoMeta({
|
||||
<!-- 文档系统 -->
|
||||
<NuxtLink
|
||||
to="/docs"
|
||||
class="group bg-white dark:bg-gray-800 rounded-2xl p-8 shadow-lg hover:shadow-xl transition-all duration-300 border border-gray-200 dark:border-gray-700"
|
||||
class="group bg-white dark:bg-gray-800 rounded-2xl p-3 md:p-8 shadow-lg hover:shadow-xl transition-all duration-300 border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="w-16 h-16 bg-blue-100 dark:bg-blue-900 rounded-full flex items-center justify-center mx-auto mb-6 group-hover:scale-110 transition-transform duration-300">
|
||||
<div class="w-12 h-12 md:w-16 md:h-16 bg-blue-100 dark:bg-blue-900 rounded-xl flex items-center justify-center mx-auto mb-3 md:mb-6 group-hover:scale-110 transition-transform duration-300">
|
||||
<UIcon
|
||||
name="lucide-book-open"
|
||||
class="text-3xl text-blue-600 dark:text-blue-400"
|
||||
@@ -47,8 +47,8 @@ useSeoMeta({
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
文档系统
|
||||
</h2>
|
||||
<p class="text-gray-600 dark:text-gray-300 mb-6">
|
||||
技术文档、API 文档、使用指南,一切尽在掌握
|
||||
<p class="text-gray-600 dark:text-gray-300 mb-3 md:mb-6">
|
||||
技术文档、API 文档、使用指南
|
||||
</p>
|
||||
<div class="flex items-center justify-center text-blue-600 dark:text-blue-400 group-hover:text-blue-700 dark:group-hover:text-blue-300 transition-colors">
|
||||
<span class="mr-2">开始阅读</span>
|
||||
@@ -63,10 +63,10 @@ useSeoMeta({
|
||||
<!-- 博客系统 -->
|
||||
<NuxtLink
|
||||
to="/blog"
|
||||
class="group bg-white dark:bg-gray-800 rounded-2xl p-8 shadow-lg hover:shadow-xl transition-all duration-300 border border-gray-200 dark:border-gray-700"
|
||||
class="group bg-white dark:bg-gray-800 rounded-2xl p-3 md:p-8 shadow-lg hover:shadow-xl transition-all duration-300 border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="w-16 h-16 bg-green-100 dark:bg-green-900 rounded-full flex items-center justify-center mx-auto mb-6 group-hover:scale-110 transition-transform duration-300">
|
||||
<div class="w-12 md:w-16 h-12 md:h-16 bg-green-100 dark:bg-green-900 rounded-xl flex items-center justify-center mx-auto mb-3 md:mb-6 group-hover:scale-110 transition-transform duration-300">
|
||||
<UIcon
|
||||
name="lucide-pen-tool"
|
||||
class="text-3xl text-green-600 dark:text-green-400"
|
||||
@@ -75,8 +75,8 @@ useSeoMeta({
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
博客系统
|
||||
</h2>
|
||||
<p class="text-gray-600 dark:text-gray-300 mb-6">
|
||||
技术分享、心得体会、生活感悟,记录每一个精彩瞬间
|
||||
<p class="text-gray-600 dark:text-gray-300 mb-3 md:mb-6">
|
||||
技术分享、心得体会、生活感悟
|
||||
</p>
|
||||
<div class="flex items-center justify-center text-green-600 dark:text-green-400 group-hover:text-green-700 dark:group-hover:text-green-300 transition-colors">
|
||||
<span class="mr-2">开始阅读</span>
|
||||
@@ -111,7 +111,7 @@ useSeoMeta({
|
||||
size="16"
|
||||
/>
|
||||
<a
|
||||
href="https://lijue.me"
|
||||
href="https://lijue.net"
|
||||
target="_blank"
|
||||
class="hover:text-gray-700 dark:hover:text-gray-200 transition-colors"
|
||||
>
|
||||
|
@@ -1,3 +1,3 @@
|
||||
title: 技术栈
|
||||
description: 记录日常开发中遇到的技术问题和解决方案
|
||||
icon: lucide-code
|
||||
icon: devicon:prolog
|
||||
|
293
content/blog/1.技术栈/971.服务器加固实战.md
Normal file
293
content/blog/1.技术栈/971.服务器加固实战.md
Normal file
@@ -0,0 +1,293 @@
|
||||
---
|
||||
title: 防于未然,先固不破 — Debian 12 快速加固实战
|
||||
description: 在数字化浪潮席卷全球的今天,服务器已成为承载企业核心业务与数据的“心脏”。一旦这颗心脏被攻破,轻则业务中断,重则数据泄漏、引发监管问责、品牌声誉坍塌,甚至于法律诉讼。
|
||||
date: 2025-08-15
|
||||
img: https://img.jiwei.xin/20250815215319708.png
|
||||
navigation:
|
||||
icon: simple-icons:debian
|
||||
---
|
||||
|
||||
### 前言
|
||||
在数字化浪潮席卷全球的今天,服务器已成为承载企业核心业务与数据的“心脏”。一旦这颗心脏被攻破,轻则业务中断,重则数据泄漏、引发监管问责、品牌声誉坍塌,甚至于法律诉讼。也正因如此,“我不知道漏洞藏在那里”已经不是借口,而是失责;真正负责任的运维与安全团队,必须把“ **防于未然,先固不破** ”当作铁律。
|
||||
|
||||
然而,系统配置千头万绪——从 BIOS 到内核参数,从系统服务到用户策略,从网络栈到文件权限——**千里之堤,溃于蚁穴**,任何一个角落的配置失误,都可能成为攻击者趁虚而入的跳板。“人眼”已无法在短时间内遍历所有细节,唯有借助安全团队丰富的经验与标准化的自动安全审计工具,将复杂环境拆解成可度量的检查项,才能在海量主机、混部容器与虚拟化场景中抽丝剥茧,还原安全真相。
|
||||
|
||||
本文将以 **Lynis** 审计工具来还原一次服务器加固实战。
|
||||
|
||||
### 正文
|
||||
#### 什么是Lynis?
|
||||
Lynis 是一款开源、针对基于 UNIX 系统(如 Linux、macOS、BSD 等)的安全审计工具。它能检测系统配置、文件权限、日志、网络等多个方面,提供详细的安全建议,帮助系统管理员加强系统的安全性。Lynis 通常被系统管理员和网络安全工程师用来评估其系统的安全防御能力。
|
||||
- 除了“蓝队”之外,如今的渗透测试人员也将 Lynis 作为他们的工具之一。
|
||||
#### 环境
|
||||
- **OS**:Debian 12 (bookworm) 全新 Server 最小化安装
|
||||
- **内核**:6.1.0-12-amd64(Debian 12 官方 6.1 LTS 最新版)
|
||||
- **架构**:x86_64
|
||||
- **软件源** :启用 Debian Security 及官方主仓库
|
||||
- **用户**:root 初始登录,已创建 sudo 普通用户 `estel`,后续所有操作便于演示均以 su - 模式执行
|
||||
- **时间**:NTP 已启用,与 `pool.ntp.org` 同步,时区 UTC+8
|
||||
- **防火墙**:iptables 未配置(默认空规则)
|
||||
- **已更新**:`apt update && apt full-upgrade -y` 三天前更新
|
||||
- **已安装包**: Docker nfs-server openssh-server
|
||||
|
||||
#### 使用
|
||||
```bash[bash]
|
||||
# ssh 连接进服务器
|
||||
# 在合适的目录进行克隆 Lynis
|
||||
git clone https://github.com/CISOfy/lynis
|
||||
|
||||
#执行安全审查脚本
|
||||
cd lynis && sudo ./lynis audit system
|
||||
```
|
||||
|
||||
#### 扫描结果部分摘录
|
||||
```bash[bash]
|
||||
**[ Lynis 3.1.6 ]**
|
||||
**Lynis security scan details**:
|
||||
**Scan mode**:
|
||||
Normal [▆] Forensics [ ] Integration [ ] Pentest [ ]
|
||||
**Lynis modules**:
|
||||
- Compliance status [**?**]
|
||||
- Security audit [**V**]
|
||||
- Vulnerability scan [**V**]
|
||||
**Details**:
|
||||
Hardening index : **63** [**############** ]
|
||||
Tests performed : **280**
|
||||
Plugins enabled : **2**
|
||||
|
||||
**Software components**:
|
||||
- Firewall [**V**]
|
||||
- Intrusion software [**X**]
|
||||
- Malware scanner [**X**]
|
||||
|
||||
**Files**:
|
||||
- Test and debug information : **/var/log/lynis.log**
|
||||
- Report data : **/var/log/lynis-report.dat**
|
||||
==============================================================
|
||||
**Lynis** 3.1.6
|
||||
Auditing, system hardening, and compliance for UNIX-based systems
|
||||
(Linux, macOS, BSD, and others)
|
||||
2007-2025, CISOfy - https://cisofy.com/lynis/
|
||||
**Enterprise support available (compliance, plugins, interface and tools)**
|
||||
```
|
||||
|
||||
|
||||
#### 结果分析
|
||||
- 根据 Lynis 的体检单
|
||||
- Hardening Index 63/100
|
||||
1. 系统概况
|
||||
- Debian 12,无内核更新,无重启需求,存在 2 个高风险补丁(PKGS-7392)。
|
||||
2. 关键问题
|
||||
- GRUB 无密码 ,可单用户模式提权
|
||||
- SSH 端口为 22,允许 root 登录,多项参数过宽
|
||||
- 大量 systemd 服务暴露评分 9.x(atd、cron、docker、rsyslog 等)
|
||||
- 未挂载 /var /tmp /home 独立分区,可能导致 DoS
|
||||
- 缺乏防火墙规则审查、恶意软件扫描器、文件完整性监控、审计框架
|
||||
|
||||
3. 次要风险
|
||||
- 仅 1 个 DNS 服务器,NFS 空 exports 文件却运行守护进程
|
||||
- USB/火线驱动、编译器、核心参数未加固
|
||||
- 日志未远程归档、登录 banner 未配置
|
||||
|
||||
#### 进行服务器加固
|
||||
```bash[Debian]
|
||||
# 为便于演示默认以下所有操作均在 root 权限下
|
||||
su -
|
||||
```
|
||||
|
||||
```bash[Debian]
|
||||
# 更新系统补丁
|
||||
apt update && apt dist-upgrade -y
|
||||
```
|
||||
|
||||
1. 给 GRUB 加密码
|
||||
```bash[Debian]
|
||||
apt install -y grub2-common
|
||||
grub-mkpasswd-pbkdf2 # 生成 PBKDF2 哈希
|
||||
|
||||
nano /etc/grub.d/40_custom # 追加到配置中
|
||||
|
||||
cat <<EOF
|
||||
set superusers="grubadmin"
|
||||
password_pbkdf2 grubadmin grub.pbkdf2.sha512.10000.xxxxxxxx...
|
||||
EOF
|
||||
|
||||
# 更新
|
||||
update-grub
|
||||
|
||||
#如果更新报错,改为在 40_custom 中追加
|
||||
set superusers="root"
|
||||
password_pbkdf2 grubadmin grub.pbkdf2.sha512.10000.xxxxxxxx...
|
||||
```
|
||||
|
||||
2. SSH 强化
|
||||
```bash[Debian]
|
||||
# 修改 SSH 默认端口号 与登录配置
|
||||
|
||||
cp /etc/ssh/sshd_config{,.bak}
|
||||
cat >> /etc/ssh/sshd_config <<EOF
|
||||
Port 49222
|
||||
PermitRootLogin no
|
||||
MaxAuthTries 3
|
||||
MaxSessions 2
|
||||
ClientAliveInterval 300
|
||||
ClientAliveCountMax 2
|
||||
AllowTcpForwarding no
|
||||
TCPKeepAlive no
|
||||
X11Forwarding no
|
||||
AllowAgentForwarding no
|
||||
LogLevel VERBOSE
|
||||
EOF
|
||||
systemctl restart ssh
|
||||
|
||||
```
|
||||
|
||||
3.强化 sshd 安全
|
||||
```bash[Debian]
|
||||
cp /lib/systemd/system/ssh.service /etc/systemd/system/ssh.service
|
||||
|
||||
# 使用 systemd-analyze security 的建议调整
|
||||
systemctl edit --full ssh.service
|
||||
|
||||
systemctl restart ssh
|
||||
```
|
||||
4. 禁用不必要的 systemd 服务
|
||||
```bash[Debian]
|
||||
# atd 一次性计划任务守护进程,不需要调度就关掉;
|
||||
# exim4 Debian 系列默认的 MTA 邮件服务,不需要本地发件关掉;
|
||||
# containerd ‑ Docker/K8s 的运行时,此服务不用容器关掉。
|
||||
|
||||
systemctl disable --now atd exim4 containerd
|
||||
```
|
||||
|
||||
5. Kernel 级系统参数
|
||||
```bash[Debian]
|
||||
cat >/etc/sysctl.d/99-hardening.conf <<EOF
|
||||
dev.tty.ldisc_autoload=0
|
||||
fs.protected_fifos=2
|
||||
kernel.core_uses_pid=1
|
||||
kernel.kptr_restrict=2
|
||||
kernel.unprivileged_bpf_disabled=1
|
||||
kernel.sysrq=0
|
||||
net.core.bpf_jit_harden=2
|
||||
net.ipv4.conf.all.log_martians=1
|
||||
net.ipv4.conf.all.rp_filter=1
|
||||
net.ipv4.conf.all.send_redirects=0
|
||||
net.ipv4.conf.all.accept_redirects=0
|
||||
net.ipv6.conf.all.accept_redirects=0
|
||||
net.ipv6.conf.default.accept_redirects=0
|
||||
kernel.dmesg_restrict = 1
|
||||
EOF
|
||||
sysctl -p /etc/sysctl.d/99-hardening.conf
|
||||
```
|
||||
|
||||
内核部分参数解释
|
||||
|
||||
| 参数 | 作用 | 备注 |
|
||||
| ------------------------------------ | ---------------------------- | ----------------------- |
|
||||
| `dev.tty.ldisc_autoload=0` | 禁止非特权用户加载终端的 line discipline | 防御终端驱动注入攻击 |
|
||||
| `fs.protected_fifos=2` | 严格保护 FIFO/管道文件权限 | 防止竞态条件漏洞 |
|
||||
| `kernel.core_uses_pid=1` | Core 转储文件名包含 PID | 方便调试但需配合 `ulimit -c` 限制 |
|
||||
| `kernel.kptr_restrict=2` | 完全隐藏内核符号地址 | 防内核信息泄漏 |
|
||||
| `kernel.unprivileged_bpf_disabled=1` | 禁止非特权用户使用 BPF | 防御容器逃逸 |
|
||||
| `kernel.sysrq=0` | 禁用 SysRq 组合键 | 防止物理接触攻击 |
|
||||
| `net.core.bpf_jit_harden=2` | BPF JIT 编译器加固 | 缓解 Spectre 漏洞 |
|
||||
| `net.ipv4.conf.all.log_martians=1` | 记录异常 IP 包 | 需配合日志监控 |
|
||||
| `net.ipv4.conf.all.rp_filter=1` | 启用反向路径过滤 | 防 IP 欺骗 |
|
||||
| `net.ipv4/6.*.accept_redirects=0` | 禁止 ICMP 重定向 | 防网络拓扑劫持 |
|
||||
| `net.ipv4.conf.all.send_redirects=0` | 禁止发送 ICMP 重定向 | 仅路由器需要 |
|
||||
| `kernel.dmesg_restrict = 1` | 防止非特权用户访问内核日志 | |
|
||||
6. 关闭 USB / 火线存储
|
||||
```bash[Debian]
|
||||
cat >/etc/modprobe.d/blacklist-usb-storage.conf <<EOF
|
||||
install usb-storage /bin/false
|
||||
install firewire-ohci /bin/false
|
||||
EOF
|
||||
rmmod usb-storage firewire-ohci || true
|
||||
```
|
||||
|
||||
7. 安装防护软件
|
||||
```bash[Debian]
|
||||
# auditd(系统审计日志)、rkhunter(Rootkit 检测)
|
||||
# chkrootkit(基础 Rootkit 扫描)debsums(校验官方软件包完整性)
|
||||
# apparmor-profiles(强制访问控制策略)aide(文件完整性监控)
|
||||
|
||||
apt install -y auditd rkhunter chkrootkit debsums apparmor-profiles apparmor-profiles-extra aide
|
||||
|
||||
# 初始化文件完整性(AIDE)需要较长时间,占用IO
|
||||
aideinit
|
||||
|
||||
systemctl enable --now auditd
|
||||
|
||||
```
|
||||
|
||||
8. 配置安全自动更新
|
||||
```bash[Debian]
|
||||
apt install -y unattended-upgrades
|
||||
dpkg-reconfigure -plow unattended-upgrades
|
||||
```
|
||||
|
||||
#### 复查扫描
|
||||
```
|
||||
lynis audit system
|
||||
|
||||
# 扫描结果部分摘录
|
||||
|
||||
**Lynis security scan details**:
|
||||
**Scan mode**:
|
||||
Normal [▆] Forensics [ ] Integration [ ] Pentest [ ]
|
||||
**Lynis modules**:
|
||||
- Compliance status [**?**]
|
||||
- Security audit [**V**]
|
||||
- Vulnerability scan [**V**]
|
||||
|
||||
**Details**:
|
||||
Hardening index : **78** [**###############** ]
|
||||
Tests performed : **283**
|
||||
Plugins enabled : **2**
|
||||
|
||||
**Software components**:
|
||||
- Firewall [**V**]
|
||||
- Intrusion software [**X**]
|
||||
- Malware scanner [**V**]
|
||||
|
||||
|
||||
**Files**:
|
||||
- Test and debug information : **/var/log/lynis.log**
|
||||
- Report data : **/var/log/lynis-report.dat**
|
||||
|
||||
==============================================================
|
||||
**Lynis** 3.1.6
|
||||
|
||||
Auditing, system hardening, and compliance for UNIX-based systems
|
||||
(Linux, macOS, BSD, and others)
|
||||
|
||||
2007-2025, CISOfy - https://cisofy.com/lynis/
|
||||
**Enterprise support available (compliance, plugins, interface and tools)**
|
||||
|
||||
|
||||
|
||||
================================================================================
|
||||
```
|
||||
|
||||
#### 加固完成
|
||||
- **加固指数:78(63↓ ➜ ↑78)**
|
||||
- **告警数量:0(Great, no warnings)**
|
||||
- 剩余建议:33 条,以“系统性、纵深优化”为主,无紧急风险**
|
||||
经过第一轮快速修补,本机已从 **63 分 危险边缘** 回到“**可交付**”水平:漏洞包归零。后续把“服务安全上下文 + 纵深防御”作为重点,逐步细化为**零告警、高可信的 Debian 12 基线环境**。目标在下一轮扫描中 **≥85 分,Warnings=0**。遵循“未漏先防、未破先固”原则,持续加固即可。
|
||||
|
||||
| 类别 | 关键动作 | 预估加分 |
|
||||
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
|
||||
| **系统服务** | 对 `docker.service`、`containerd.service`、`ssh.service` 等仍处 UNSAFE/EXPOSED 状态的单位,逐条应用 `systemd-analyze security` 推荐的安全参数(NoNewPrivileges、ProtectSystem 等) | ≈ +3–4 分 |
|
||||
| **审计规则** | 为 auditd 导入 CIS 审计规则 → ACCT-9628 | ≈ +2 分 |
|
||||
| **密码策略** | 设定最小/最大口令寿命、启用 pam_cracklib,统一 umask 027 → AUTH-9262/9328 | ≈ +2 分 |
|
||||
| **内核参数** | 修正 fs.protected_fifos、kernel.yama.ptrace_scope 等 4 项 sysctl 差异 → KRNL-6000 | ≈ +1 分 |
|
||||
| **纵深防御** | 禁止 USB-Storage/FireWire、配置远程日志、加固编译器 & cron 权限 | ≈ +1 分 |
|
||||
|
||||
## 总结
|
||||
通过本次基于 Lynis 的服务器安全加固实战,我们成功将一台"裸奔"的 Debian 12 服务器从 63 分的**危险边缘状态**提升至 78 分的**可交付水平**——这不仅是数字上的跃升,更是从"千疮百孔"逐步到"基线合规"的质变。从 GRUB 无密码、SSH 配置过宽,到内核参数缺失、关键服务暴露——每一项都是潜在的攻击入口,而工具化的检测让我们得以"**抽丝剥茧,还原安全真相**"。
|
||||
|
||||
更重要的是,78 分绝非终点。正如**防于未然,先固不破**的铁律所示,安全加固是一个持续迭代的过程:systemd 服务的深度加固、审计规则的精细配置、密码策略的严格管控——每一步都在为企业的数字资产构筑更坚固的防线。
|
||||
|
||||
在数字化浪潮愈发汹涌的今天,服务器安全已不容半点侥幸。那些仍抱着"我不知道漏洞藏在哪里"的心态,直到漏洞爆发才仓促补救的团队,终将在下一次攻击中付出惨重代价。真正负责任的做法,是把安全加固的重心从 **"事后补救"** 前移到 **"事前预防"**,让每一台服务器都成为攻击者难以逾越的堡垒。
|
||||
|
||||
**千里之堤,毁于蚁穴;百密一疏,功亏一篑。唯有持之以恒的安全意识与标准化的加固流程,才能在这场没有硝烟的网络战争中立于不败之地。**
|
14
content/blog/1.技术栈/972.nmap高级应用技巧-实战.md
Normal file
14
content/blog/1.技术栈/972.nmap高级应用技巧-实战.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: nmap高级应用技巧-实战
|
||||
description: 实战中使用nmap进行渗透测试和信息收集。
|
||||
date: 2025-08-12
|
||||
img: https://img.jiwei.xin/20250812130604330.png
|
||||
navigation:
|
||||
icon: simple-icons:openstreetmap
|
||||
---
|
||||
|
||||
```bash[Kali]
|
||||
nmap -p- --min-rate=1000 -T4 -v -n -Pn 192.168.1.1
|
||||
```
|
||||
|
||||
#### 未完成
|
99
content/blog/1.技术栈/973.nmap高级应用技巧-NSE.md
Normal file
99
content/blog/1.技术栈/973.nmap高级应用技巧-NSE.md
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: nmap高级应用技巧-NSE
|
||||
description: Nmap Scripting Engine(NSE)是Nmap的核心扩展功能,通过Lua脚本实现自动化扫描、漏洞检测、信息收集等高级操作。
|
||||
date: 2025-08-12
|
||||
img: https://img.jiwei.xin/20250812130604330.png
|
||||
navigation:
|
||||
icon: simple-icons:openstreetmap
|
||||
---
|
||||
|
||||
|
||||
### NSE
|
||||
Nmap Scripting Engine(NSE)是Nmap的核心扩展功能,通过Lua脚本实现自动化扫描、漏洞检测、信息收集等高级操作。
|
||||
|
||||
#### NSE脚本按功能分为12大类,每类对应不同扫描需求
|
||||
|
||||
| 类别 | 核心目的 | 样例脚本 | 典型使用场合 |
|
||||
|------------|---------------------------------------|-------------------------------|----------------------------------------------------------|
|
||||
| vuln | CVE/漏洞快速验证 | http-vuln-cve2017-5638 | 目标服务版本与已知漏洞匹配 |
|
||||
| safe | 只读式安全审计(无破坏性) | ssh2-enum-algos | 建立会话后立即枚举支持的算法 |
|
||||
| default | 默认已开启的基础信息采集 | http-title | 首次扫描时的“看一眼” |
|
||||
| discovery | 发现服务/子网/资产信息 | smb-os-discovery | 内网资产清点、OS 指纹识别 |
|
||||
| auth | 认证、密钥与票据处理 | ssh-hostkey | 密钥指纹比对、横向渗透准备 |
|
||||
| brute | 弱口令爆破 | http-form-brute | 对表单类登录口的暴力尝试 |
|
||||
| exploit | 利用已确认漏洞取得立足点(需授权) | http-vuln-cve2021-41773 | 实战攻击复现、漏洞验证 |
|
||||
| intrusive | 高交互操作(易被 IDS 告警) | http-slowloris | 拒绝服务场景模拟/压力测试(慎用!) |
|
||||
| malware | 嗅探后门或恶意软件痕迹 | http-malware-host | 威胁狩猎与入侵后取证 |
|
||||
| version | 精细化版本指纹 | http-apache-negotiation | 为后续补丁或 PoC 匹配做准备 |
|
||||
| broadcast | 广播域/多播发现 | nbstat | 内网渗透早期:快速识别 NetBIOS 设备 |
|
||||
| external | 外链第三方数据源(DNSBL 等) | dns-blacklist | 资产信誉检查、垃圾邮件源或钓鱼域名扫描 |
|
||||
|
||||
### 常用 NSE 脚本一览
|
||||
|
||||
| 脚本名称 | 主要用途 | Nmap 一键示例 |
|
||||
|---|---|---|
|
||||
| **http-title** | 抓取 HTTP 网站标题 | `nmap --script http-title <target>` |
|
||||
| **https-redirect** | 识别 HTTP→HTTPS 跳转链 | `nmap --script https-redirect -p 80 <target>` |
|
||||
| **http-robtots** | 收集目标 robots/disallow 条目 | `nmap --script http-robots.txt -p 80 <target>` |
|
||||
| **http-headers** | 枚举响应标头与安全策略 | `nmap --script http-headers -p 80 <target>` |
|
||||
| **http-methods** | 检测允许/禁止的 HTTP 方法 | `nmap --script http-methods -p 80 <target>` |
|
||||
| **http-cors** | 测试 CORS、CSRF 漏洞 | `nmap --script http-cors <target>` |
|
||||
| **http-sql-injection** | 基本 SQL 注入探针 | `nmap --script http-sql-injection -p 80 <target>` |
|
||||
| **http-xssed** | 与 xssed.com 集成交叉检测 | `nmap --script http-xssed <target>` |
|
||||
| **smb-os-discovery** | 枚举 SMB 服务器操作系统/域信息 | `nmap --script smb-os-discovery -p 445 <target>` |
|
||||
| **smb-enum-shares** | 列出共享目录(需 guest) | `nmap --script smb-enum-shares -p 445 <target>` |
|
||||
| **smb-enum-users** | 提取本地/域用户列表 | `nmap --script smb-enum-users -p 445 <target>` |
|
||||
| **smb-vuln-ms17-010** | 探测 EternalBlue(MS17-010) | `nmap --script smb-vuln-ms17-010 -p 445 <target>` |
|
||||
| **ssh-hostkey** | 抓取并对比公钥指纹 | `nmap --script ssh-hostkey -p 22 <target>` |
|
||||
| **ssh-brute** | SSH 账号密码暴力破解(需授权) | `nmap --script ssh-brute --script-args userdb=users.txt,passdb=pass.txt -p 22 <target>` |
|
||||
| **ssh2-enum-algos** | 列出支持的算法与 KEX | `nmap --script ssh2-enum-algos -p 22 <target>` |
|
||||
| **http-vuln-cve2023-XXXX** | 按指定 CVE 编号进行漏洞检查(替换 XXXX) | `nmap --script http-vuln-cve2023-XXXX <target>` |
|
||||
| **ftp-anon** | 检测是否允许匿名登录 | `nmap --script ftp-anon -p 21 <target>` |
|
||||
| **ftp-bounce** | 检查是否允许 FTP Bounce 扫描 | `nmap --script ftp-bounce -p 21 <target>` |
|
||||
| **dns-zone-transfer** | 测试 AXFR(区域传输)泄露 | `nmap --script dns-zone-transfer -p 53 <target>` |
|
||||
| **dns-brute** | 字典爆破子域名 | `nmap --script dns-brute --script-args dns-brute.domain=<domain>` |
|
||||
| **mysql-brute** | 暴力破解 MySQL 实例 | `nmap --script mysql-brute -p 3306 <target>` |
|
||||
| **mysql-audit** | 核对 MySQL CIS/基线配置 | `nmap --script mysql-audit --script-args mysql-audit.username='root',mysql-audit.password='pass123' -p 3306 <target>` |
|
||||
| **ssl-cert** | 解析 TLS 证书详情 | `nmap --script ssl-cert -p 443 <target>` |
|
||||
| **ssl-poodle** | 探测 SSLv3 & POODLE 漏洞 | `nmap --script ssl-poodle -p 443 <target>` |
|
||||
| **vulners** | 基于 Vulners DB 的版本漏洞关联 | `nmap --script vulners -sV <target>` |
|
||||
| **whois-ip** | 查询归属 IP 段的 Whois 信息 | `nmap --script whois-ip <target>` |
|
||||
| **smtp-enum-users** | VRFY/EXPN 暴力枚举邮箱账户 | `nmap --script smtp-enum-users -p 25 <target>` |
|
||||
|
||||
### 自定义脚本
|
||||
|
||||
```lua[演示.lua]
|
||||
-- myecho.nse
|
||||
local stdnse = require "stdnse"
|
||||
|
||||
description = [[极简回显脚本:打印自定义消息]]
|
||||
author = "YourName"
|
||||
license = "GPLv2"
|
||||
categories = {"safe"}
|
||||
|
||||
-- 任何 TCP 端口都触发
|
||||
portrule = function(host,port) return port.protocol == "tcp" end
|
||||
|
||||
action = function(host,port)
|
||||
local msg = stdnse.get_script_args("myecho.msg") or "Hi from custom-NSE!"
|
||||
return ("%s -> %s:%d (%s)"):format(msg, host.ip, port.number, port.service or "unknown")
|
||||
end
|
||||
```
|
||||
|
||||
```bash
|
||||
# 复制.nse到脚本文件夹
|
||||
sudo cp myecho.nse /usr/share/nmap/scripts/
|
||||
nmap --script-updatedb # 更新索引
|
||||
```
|
||||
|
||||
```bash
|
||||
# 执行脚本
|
||||
nmap --script myecho -p22 127.0.0.1
|
||||
nmap --script myecho --script-args 'myecho.msg=HelloNSE' -p80 scanme.nmap.org
|
||||
nmap --script '(safe or discovery) and myecho' -p80,443 10.0.2.0/24
|
||||
```
|
||||
|
||||
### 总结
|
||||
NSE 脚本可大幅提升 Nmap 在渗透测试和信息收集中的效率。建议定期关注 Nmap 官方脚本库更新([NSE Docs](https://nmap.org/nsedoc/ "NSE Docs"))以覆盖最新漏洞。
|
||||
|
||||
|
55
content/blog/1.技术栈/974.部分组件语法展示.md
Normal file
55
content/blog/1.技术栈/974.部分组件语法展示.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: 部分vue组件语法预览
|
||||
description: 预览一些自定义 vue 组件语法
|
||||
date: 2025-08-11
|
||||
img: https://img.jiwei.xin/20250811160513849.png
|
||||
navigation:
|
||||
icon: simple-icons:vuedotjs
|
||||
---
|
||||
:::code-group
|
||||
:::code-preview{icon="i-lucide-eye" label="Preview"}
|
||||
::callout
|
||||
---
|
||||
icon: simple-icons-visualstudiocode
|
||||
to: https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc
|
||||
---
|
||||
安装 **MDC VS Code 扩展**,以获得对 MDC 语法的高亮显示支持。
|
||||
::
|
||||
:::
|
||||
```mdp[Markdown语法.md]
|
||||
::callout
|
||||
---
|
||||
icon: simple-icons-visualstudiocode
|
||||
to: https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc
|
||||
---
|
||||
安装 **MDC VS Code 扩展**,以获得对 MDC 语法的高亮显示支持。
|
||||
::
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
::tip{icon="i-lucide-info"}
|
||||
这个页面 **尚未完成**,用于测试一些组件的语法和样式.
|
||||
::
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
::details
|
||||
::summary
|
||||
### 点击我查看内容
|
||||
::
|
||||
这里是折叠区域的内容
|
||||
可以包含任何Markdown元素:
|
||||
- 段落
|
||||
- 列表
|
||||
- 代码块
|
||||
- 图片等
|
||||
::
|
||||
|
||||
|
379
content/blog/1.技术栈/975.新的文档渲染样式.md
Normal file
379
content/blog/1.技术栈/975.新的文档渲染样式.md
Normal file
@@ -0,0 +1,379 @@
|
||||
---
|
||||
title: 新的文档渲染样式
|
||||
description: 重新构建了渲染组件,优化了样式.
|
||||
date: 2025-08-10
|
||||
img: https://img.jiwei.xin/20250811160513849.png
|
||||
navigation:
|
||||
icon: simple-icons:nuxtdotjs
|
||||
---
|
||||
|
||||
## 重新对文档的样式进行了优化
|
||||
- 周日在家闭关重写了十几个渲染组件。
|
||||
- 纯古法手工敲代码,兼容 Markdown 语法.部分 css 样式由 GPT-5 生成.
|
||||
- 大部分组件对主题进行了响应式处理,会根据主题进行不同的样式渲染
|
||||
|
||||
### 1. 标题:让你的内容层次分明
|
||||
|
||||
用 `#`号来创建标题。标题从 `#` 开始,`#` 的数量表示标题的级别。
|
||||
|
||||
:::code-group
|
||||
::::code-preview{icon="i-lucide-eye" label="预览效果"}
|
||||
:::::example-title
|
||||
# 一级标题
|
||||
## 二级标题
|
||||
### 三级标题
|
||||
#### 四级标题
|
||||
##### 五级标题
|
||||
###### 六级标题
|
||||
:::::
|
||||
::::
|
||||
```mdc [标题文本.md]
|
||||
# 一级标题
|
||||
## 二级标题
|
||||
### 三级标题
|
||||
#### 四级标题
|
||||
##### 五级标题
|
||||
###### 六级标题
|
||||
```
|
||||
:::
|
||||
|
||||
::tip
|
||||
以上代码将渲染出一组层次分明的标题,使你的内容井井有条。并且可以根据标题进行快速导航!
|
||||
::
|
||||
### 2. 流程图与公式渲染:
|
||||
|
||||
:::code-group
|
||||
:::code-preview{icon="i-lucide-eye" label="预览效果"}
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Local Coolify 实例] --> B[生产服务器]
|
||||
A --> D[编译服务器]
|
||||
B --> B1[生产环境]
|
||||
D --> B
|
||||
```
|
||||
:::
|
||||
|
||||
```mermaid[流程图语句.md]
|
||||
```mermaid[流程图语句.md]
|
||||
graph TD
|
||||
A[Local Coolify 实例] --> B[生产服务器]
|
||||
A --> D[编译服务器]
|
||||
B --> B1[生产环境]
|
||||
D --> B
|
||||
```
|
||||
```
|
||||
:::
|
||||
|
||||
:::code-group
|
||||
::code-preview{icon="i-lucide-eye" label="预览效果"}
|
||||
```mermaid
|
||||
pie
|
||||
title 为什么总是宅在家里?
|
||||
"喜欢宅" : 45
|
||||
"天气太热" : 70
|
||||
"穷" : 500
|
||||
"没人约" : 95
|
||||
```
|
||||
::
|
||||
```mermaid[饼图语句.md]
|
||||
```mermaid[饼图语句.md]
|
||||
pie
|
||||
title 为什么总是宅在家里?
|
||||
"喜欢宅" : 45
|
||||
"天气太热" : 70
|
||||
"穷" : 500
|
||||
"没人约" : 95
|
||||
```
|
||||
```
|
||||
:::
|
||||
|
||||
:::code-group
|
||||
::code-preview{icon="i-lucide-eye" label="预览效果"}
|
||||
- **单行公式**:用 `$` 包裹公式,如 $E = mc^2$
|
||||
- **块级公式**:用 `$$` 包裹公式,如:
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
d_{i, j} &\leftarrow d_{i, j} + 1 \\
|
||||
d_{i, y + 1} &\leftarrow d_{i, y + 1} - 1 \\
|
||||
d_{x + 1, j} &\leftarrow d_{x + 1, j} - 1 \\
|
||||
d_{x + 1, y + 1} &\leftarrow d_{x + 1, y + 1} + 1
|
||||
\end{aligned}
|
||||
$$
|
||||
- 列表内块公式
|
||||
$$
|
||||
\chi^2 = \sum \frac{(|O - E| - 0.5)^2}{E}
|
||||
$$
|
||||
::
|
||||
```mermaid[公式语句.md]
|
||||
- **单行公式**:用 `$` 包裹公式,如 $E = mc^2$
|
||||
- **块级公式**:用 `$$` 包裹公式,如:
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
d_{i, j} &\leftarrow d_{i, j} + 1 \\
|
||||
d_{i, y + 1} &\leftarrow d_{i, y + 1} - 1 \\
|
||||
d_{x + 1, j} &\leftarrow d_{x + 1, j} - 1 \\
|
||||
d_{x + 1, y + 1} &\leftarrow d_{x + 1, y + 1} + 1
|
||||
\end{aligned}
|
||||
$$
|
||||
- 列表内块公式
|
||||
$$
|
||||
\chi^2 = \sum \frac{(|O - E| - 0.5)^2}{E}
|
||||
$$
|
||||
```
|
||||
:::
|
||||
|
||||
### 3. 代码块与代码树:展示你的代码
|
||||
|
||||
- **行内代码**:用反引号包裹,如 `code`。
|
||||
- **代码块**:用三个反引号包裹,并指定语言,如:
|
||||
|
||||
|
||||
```ts[hello.vue]
|
||||
console.log("Hello, Estel Docs!");
|
||||
```
|
||||
- **代码树**:用 `::code-tree` 包裹,并指定默认值,如:
|
||||
|
||||
::code-tree{defaultValue="nuxt.config.ts"}
|
||||
|
||||
```css [app/assets/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
```
|
||||
|
||||
```ts [app/app.config.ts]
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
colors: {
|
||||
primary: 'sky',
|
||||
colors: 'slate'
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```vue [app/app.vue]
|
||||
<template>
|
||||
<UApp>
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
```json [package.json]
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare",
|
||||
"typecheck": "nuxt typecheck"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/lucide": "^1.2.18",
|
||||
"@nuxt/ui-pro": "3.0.0-alpha.10",
|
||||
"nuxt": "^3.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.2",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json [tsconfig.json]
|
||||
{
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
||||
```
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui-pro'],
|
||||
|
||||
future: {
|
||||
compatibilityVersion: 4
|
||||
},
|
||||
|
||||
css: ['~/assets/main.css']
|
||||
});
|
||||
```
|
||||
|
||||
````md [README.md]
|
||||
# Estel Docs 文档系统
|
||||
|
||||
Look at the [Estel Docs](https://lijue.net) to learn more.
|
||||
|
||||
## 安装
|
||||
|
||||
确保安装依赖:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install
|
||||
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# bun
|
||||
bun install
|
||||
```
|
||||
|
||||
## 开发服务器
|
||||
|
||||
在 `http://localhost:3000` 启动开发服务器:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run dev
|
||||
|
||||
# pnpm
|
||||
pnpm run dev
|
||||
|
||||
# yarn
|
||||
yarn dev
|
||||
|
||||
# bun
|
||||
bun run dev
|
||||
```
|
||||
|
||||
## 生产构建
|
||||
|
||||
构建生产应用:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run build
|
||||
|
||||
# pnpm
|
||||
pnpm run build
|
||||
|
||||
# yarn
|
||||
yarn build
|
||||
|
||||
# bun
|
||||
bun run build
|
||||
```
|
||||
|
||||
本地预览生产构建:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run preview
|
||||
|
||||
# pnpm
|
||||
pnpm run preview
|
||||
|
||||
# yarn
|
||||
yarn preview
|
||||
|
||||
# bun
|
||||
bun run preview
|
||||
```
|
||||
|
||||
查看 [部署文档](https://lijue.net/) 了解更多信息.
|
||||
````
|
||||
|
||||
::
|
||||
|
||||
::tip{icon="i-lucide-info"}
|
||||
代码树可以用来展示简单的项目文件结构.但并不适合过于复杂的项目.
|
||||
::
|
||||
|
||||
|
||||
### 4. 字体样式:强调你的文字
|
||||
|
||||
- **粗体**:用两个星号或下划线包裹文字,如 `**粗体**` 或 `__粗体__`。
|
||||
- _斜体_:用一个星号或下划线包裹文字,如 `*斜体*` 或 `_斜体_`。
|
||||
- ~~删除线~~:用两个波浪线包裹文字,如 `~~删除线~~`。
|
||||
|
||||
这些简单的标记可以让你的内容更有层次感和重点突出。
|
||||
|
||||
### 5. 列表:整洁有序
|
||||
|
||||
- **无序列表**:用 `-`、`*` 或 `+` 加空格开始一行。
|
||||
- **有序列表**:使用数字加点号(`1.`、`2.`)开始一行。
|
||||
|
||||
在列表中嵌套其他内容?只需缩进即可实现嵌套效果。
|
||||
|
||||
- 无序列表项 1
|
||||
1. 嵌套有序列表项 1
|
||||
2. 嵌套有序列表项 2
|
||||
- 无序列表项 2
|
||||
|
||||
1. 有序列表项 1
|
||||
2. 有序列表项 2
|
||||
|
||||
### 6. 链接与图片:丰富内容
|
||||
|
||||
- **链接**:用方括号和圆括号创建链接 `[显示文本](链接地址)`。
|
||||
- **图片**:和链接类似,只需在前面加上 `!`,如 ``。
|
||||
|
||||
[访问 Estel Docs](https://lijue.net)
|
||||
|
||||
:::code-group
|
||||
::code-preview{icon="i-lucide-eye" label="预览效果"}
|
||||

|
||||
::
|
||||
```md[图片语句.md]
|
||||

|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
### 7. 引用:引用名言或引人深思的句子
|
||||
|
||||
使用 `>` 来创建引用,只需在文本前面加上它。多层引用?在前一层 `>` 后再加一个就行。
|
||||
|
||||
> 这是一个引用
|
||||
>
|
||||
> > 这是一个嵌套引用
|
||||
|
||||
这让你的引用更加富有层次感。
|
||||
|
||||
语法高亮让你的代码更易读。
|
||||
|
||||
### 8. 分割线:分割内容
|
||||
|
||||
用三个或更多的 `-`、`*` 或 `_` 来创建分割线。
|
||||
|
||||
---
|
||||
|
||||
为你的内容添加视觉分隔。
|
||||
|
||||
### 9. 表格:清晰展示数据
|
||||
|
||||
Markdown 支持简单的表格,用 `|` 和 `-` 分隔单元格和表头。
|
||||
|
||||
:::code-group
|
||||
::code-preview{icon="i-lucide-eye" label="预览效果"}
|
||||
| 项目名称 | 演示网址 | 仓库地址 |
|
||||
| ------------------------------------------- | ---------------------- | ------------ |
|
||||
| Estel Docs | [lijue.net](https://lijue.net) | [Github](https://github.com/estel-li/estel-docs) |
|
||||
| Estel Docs | [lijue.net](https://lijue.net) | [GitEE](https://gitee.com/estel-li/estel-docs) |
|
||||
| Estel Docs | [lijue.net](https://lijue.net) | [Gitee(国内)](https://gitee.com/estel-li/estel-docs) |
|
||||
::
|
||||
```md[表格语句.md]
|
||||
| 项目名称 | 演示网址 | 仓库地址 |
|
||||
| ------------------------------------------- | ---------------------- | ------------ |
|
||||
| Estel Docs | [lijue.net](https://lijue.net) | [Github](https://github.com/estel-li/estel-docs) |
|
||||
| Estel Docs | [lijue.net](https://lijue.net) | [GitEE](https://gitee.com/estel-li/estel-docs) |
|
||||
| Estel Docs | [lijue.net](https://lijue.net) | [Gitee(国内)](https://gitee.com/estel-li/estel-docs) |
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
这样的表格让数据展示更为清爽!
|
||||
|
||||
|
||||
## 结语
|
||||
|
||||
- 还有更多的组件等待你发现.
|
46
content/blog/1.技术栈/976.Nuxt 微信分享接口.md
Normal file
46
content/blog/1.技术栈/976.Nuxt 微信分享接口.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Nuxt 微信分享接口
|
||||
description: 微信分享接口,使用Nuxt,实现微信分享接口.
|
||||
date: 2025-08-10
|
||||
img: https://img.jiwei.xin/20250810130754103.png
|
||||
navigation:
|
||||
icon: simple-icons:nuxtdotjs
|
||||
---
|
||||
|
||||
|
||||
### 需求
|
||||
##### 写了一个文档插件. 缘由呢是之前在 WordPress 上用过一个插件.对接微信的JS SDK,可以实现将链接带标题,图,简介封装成一个 卡片形式.分享给朋友或者朋友圈.比如:
|
||||

|
||||
|
||||
##### 如果没有对接微信的SDK,分享链接是这样的:
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 实现
|
||||
##### 这个功能实现起来也不复杂,其中麻烦的地方是向微信获取 access_token 和 jsapi_ticket,这样迫不得已就得写一个后端.
|
||||
```ts[wxShare.vue]
|
||||
type WeChat = {
|
||||
config: (cfg: {
|
||||
debug?: boolean
|
||||
appId: string
|
||||
timestamp: number
|
||||
nonceStr: string
|
||||
signature: string
|
||||
jsApiList: string[]
|
||||
}) => void
|
||||
ready: (cb: () => void) => void
|
||||
error: (cb: (e: unknown) => void) => void
|
||||
updateTimelineShareData: (opts: { title: string, link: string, imgUrl: string, success?: () => void }) => void
|
||||
updateAppMessageShareData: (opts: { title: string, desc: string, link: string, imgUrl: string, success?: () => void }) => void
|
||||
}
|
||||
```
|
||||
::tip{icon="lucide-info"}
|
||||
有利有弊
|
||||
- 因为要动态向微信请求 access_token 和 jsapi_ticket,并在生命周期内缓存起来,就必须得有个后端,
|
||||
- 将其包装成了一个 API ,运行在 Nuxt 的服务器
|
||||
- 但这样就没办法纯静态编译了
|
||||
::
|
||||
|
@@ -2,28 +2,31 @@
|
||||
title: AI复合应用 合同审查
|
||||
description: 智能合同卫士,使用AI技术,对合同进行审查,并给出审查报告.
|
||||
date: 2025-08-08
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250808141452898.png
|
||||
img: https://img.jiwei.xin/20250808141452898.png
|
||||
navigation:
|
||||
icon: simple-icons:openai
|
||||
icon: simple-icons:openai
|
||||
---
|
||||
### 先贴效果
|
||||

|
||||
|
||||
### 这是一个 AI 复合应用
|
||||
|
||||
#### 这是一个 AI 复合应用
|
||||
暂起名叫 智能合同卫士 ,还是比较贴题的把?
|
||||
|
||||
#### 实现
|
||||
### 实现
|
||||
由 Deepseek V3 和 Kimi K2 协作完成此应用,2个好用不贵的模型
|
||||
DS 根据文件名,以及 审查主体 工作领域 与 合同要点,细化一份简要的审查要点
|
||||
主要工作由 Kimi K2 来完成:
|
||||
根据 Deepseek 细化的要点和 提取出来的文档内容,进行审查.
|
||||
输出审查后的结果
|
||||
根据 Deepseek 细化的要点和 提取出来的文档内容,进行审查.
|
||||
输出审查后的结果
|
||||
|
||||
#### 改进
|
||||
### 改进
|
||||
按照初步的设想,后续还有一个模型,比如 Gemini2.5 ,或者 R1 ,Qwen3 之类的**推理模型**,进一步的审查 **Kimi K2 的审查结果**.
|
||||
不过 K2 单独使用的实际效果已经非常好了,为了节省时间和成本,暂时取掉最后一步.
|
||||
使用 AI 的话,不论是 R1 还是 K2 都是有一定的**幻觉**,在使用中必须搭配知识库来使用.
|
||||
这次测试并没有加知识库,所以 AI **虚构**了一些内容,比如当地房价涨幅 ,和虚构的 **《陕西省住房租赁管理办法》**.
|
||||
给大模型增加搜索能力,和在知识库里添加 **民法典** 等相关法律条文进去,可以有效的解决幻觉问题.
|
||||
|
||||
#### 测试结果
|
||||

|
||||
::warning{icon="lucide:shield-alert"}
|
||||
注意
|
||||
- 这次测试并没有加知识库,所以 AI **虚构**了一些内容,比如**当地房价涨幅** ,和虚构的 **《陕西省住房租赁管理办法》**.
|
||||
- 给大模型增加**搜索能力**,和在知识库里添加 **民法典** 等相关法律条文进去,可以有效的解决幻觉问题.
|
||||
::
|
||||
|
||||
|
@@ -2,89 +2,88 @@
|
||||
title: Kali 安装 GVM
|
||||
description: 在 Kali 上安装 GVM
|
||||
date: 2025-08-08
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250806105153532.png
|
||||
img: https://img.jiwei.xin/20250806105153532.png
|
||||
navigation:
|
||||
icon: simple-icons:kalilinux
|
||||
---
|
||||
|
||||
|
||||
### 配置安装好 Kali
|
||||

|
||||
```bash
|
||||

|
||||
```bash[.shell]
|
||||
# 安装 gvm
|
||||
sudo apt install gvm -y
|
||||
|
||||
# 执行配置脚本
|
||||
sudo gvm-setup
|
||||
```
|
||||
|
||||
### 安装过程中 copy admin 的密码
|
||||

|
||||
### 安装过程中 copy admin 的密码
|
||||

|
||||
|
||||
根据提示运行安装检测命令
|
||||
```bash
|
||||
```bash[.shell]
|
||||
sudo gvm-check-setup
|
||||
```
|
||||
|
||||
报错了.
|
||||
|
||||
```bash
|
||||
```bash[.shell]
|
||||
└─**$** sudo gvm-check-setup
|
||||
[sudo] estel 的密码:
|
||||
gvm-check-setup 25.04.0
|
||||
This script is provided and maintained by Debian and Kali.
|
||||
Test completeness and readiness of GVM-25.04.0
|
||||
Step 1: Checking OpenVAS (Scanner)...
|
||||
OK: OpenVAS Scanner is present in version 23.20.1.
|
||||
OK: Notus Scanner is present in version 22.6.5.
|
||||
OK: Server CA Certificate is present as /var/lib/gvm/CA/servercert.pem.
|
||||
This script is provided and maintained by Debian and Kali.
|
||||
Test completeness and readiness of GVM-25.04.0
|
||||
Step 1: Checking OpenVAS (Scanner)...
|
||||
OK: OpenVAS Scanner is present in version 23.20.1.
|
||||
OK: Notus Scanner is present in version 22.6.5.
|
||||
OK: Server CA Certificate is present as /var/lib/gvm/CA/servercert.pem.
|
||||
Checking permissions of /var/lib/openvas/gnupg/*
|
||||
OK: _gvm owns all files in /var/lib/openvas/gnupg
|
||||
OK: _gvm owns all files in /var/lib/openvas/gnupg
|
||||
|
||||
OK: redis-server is present.
|
||||
OK: scanner (db_address setting) is configured properly using the redis-server socket: /var/run/redis-openvas/redis-server.sock
|
||||
OK: the mqtt_server_uri is defined in /etc/openvas/openvas.conf
|
||||
OK: _gvm owns all files in /var/lib/openvas/plugins
|
||||
OK: NVT collection in /var/lib/openvas/plugins contains 94316 NVTs.
|
||||
OK: The notus directory /var/lib/notus/products contains 502 NVTs.
|
||||
OK: redis-server is present.
|
||||
OK: scanner (db_address setting) is configured properly using the redis-server socket: /var/run/redis-openvas/redis-server.sock
|
||||
OK: the mqtt_server_uri is defined in /etc/openvas/openvas.conf
|
||||
OK: _gvm owns all files in /var/lib/openvas/plugins
|
||||
OK: NVT collection in /var/lib/openvas/plugins contains 94316 NVTs.
|
||||
OK: The notus directory /var/lib/notus/products contains 502 NVTs.
|
||||
Checking that the obsolete redis database has been removed
|
||||
Could not connect to Redis at /var/run/redis-openvas/redis-server.sock: No such file or directory
|
||||
OK: No old Redis DB
|
||||
Starting ospd-openvas service
|
||||
Waiting for ospd-openvas service
|
||||
OK: ospd-openvas service is active.
|
||||
OK: ospd-OpenVAS is present in version 22.9.0.
|
||||
Step 2: Checking GVMD Manager ...
|
||||
OK: GVM Manager (gvmd) is present in version 26.0.0.
|
||||
Step 3: Checking Certificates ...
|
||||
OK: GVM client certificate is valid and present as /var/lib/gvm/CA/clientcert.pem.
|
||||
OK: Your GVM certificate infrastructure passed validation.
|
||||
Step 4: Checking data ...
|
||||
ERROR: SCAP DATA are missing.
|
||||
FIX: Run the SCAP synchronization script greenbone-feed-sync.
|
||||
sudo greenbone-feed-sync --type scap.
|
||||
ERROR: Your GVM-25.04.0 installation is not yet complete!
|
||||
OK: No old Redis DB
|
||||
Starting ospd-openvas service
|
||||
Waiting for ospd-openvas service
|
||||
OK: ospd-openvas service is active.
|
||||
OK: ospd-OpenVAS is present in version 22.9.0.
|
||||
Step 2: Checking GVMD Manager ...
|
||||
OK: GVM Manager (gvmd) is present in version 26.0.0.
|
||||
Step 3: Checking Certificates ...
|
||||
OK: GVM client certificate is valid and present as /var/lib/gvm/CA/clientcert.pem.
|
||||
OK: Your GVM certificate infrastructure passed validation.
|
||||
Step 4: Checking data ...
|
||||
ERROR: SCAP DATA are missing.
|
||||
FIX: Run the SCAP synchronization script greenbone-feed-sync.
|
||||
sudo greenbone-feed-sync --type scap.
|
||||
ERROR: Your GVM-25.04.0 installation is not yet complete!
|
||||
Please follow the instructions marked with FIX above and run this
|
||||
script again.
|
||||
|
||||
IMPORTANT NOTE: this script is provided and maintained by Debian and Kali.
|
||||
If you find any issue in this script, please report it directly to Debian or Kali
|
||||
IMPORTANT NOTE: this script is provided and maintained by Debian and Kali.
|
||||
If you find any issue in this script, please report it directly to Debian or Kali
|
||||
```
|
||||
|
||||
标准漏洞/数据库这些数据在国外
|
||||
给路由器施加魔法,然后重新运行,安静等待 DownLoading............
|
||||
```bash
|
||||
```bash[.shell]
|
||||
sudo greenbone-feed-sync --type scap
|
||||
```
|
||||
|
||||
下载好后再次运行检测命令,一起无误后,访问 127.0.0.1:9293 即可
|
||||
等等...我的 Kali 是安装在其他设备里的,如何通过局域网访问?
|
||||
等等...我的 Kali 是安装在其他设备里的,如何通过局域网访问?
|
||||
|
||||
```bash
|
||||
```bash[.shell]
|
||||
sudo nano /lib/systemd/system/greenbone-security-assistant.service
|
||||
```
|
||||
|
||||
```bash
|
||||
```bash[.shell]
|
||||
# 修改下面的 --listen 127.0.0.1 为 --listen 0.0.0.0 即可
|
||||
**[Unit]**
|
||||
Description=Greenbone Security Assistant daemon (gsad)
|
||||
@@ -108,7 +107,7 @@ WantedBy=multi-user.target
|
||||
Alias=greenbone-security-assistant.service
|
||||
```
|
||||
|
||||
```bash
|
||||
```bash[.shell]
|
||||
# 重新开始服务
|
||||
sudo gvm-start
|
||||
```
|
||||
@@ -116,9 +115,9 @@ sudo gvm-start
|
||||
随后等待程序自动更新提要状态,需要时间非常久.建议释放魔法.
|
||||
如果自动更新失败,可以手动更新:
|
||||
|
||||
```bash
|
||||
```bash[.shell]
|
||||
sudo greenbone-feed-sync
|
||||
```
|
||||
|
||||
### END
|
||||
至此安装完毕.
|
||||
至此安装完毕.
|
||||
|
@@ -2,20 +2,20 @@
|
||||
title: GPT-5
|
||||
description: GPT-5 上线了,Cursor 提示我可以免费试用,当然要 BP 喽。
|
||||
date: 2025-08-08
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250808132005343.png
|
||||
img: https://img.jiwei.xin/20250808132005343.png
|
||||
navigation:
|
||||
icon: simple-icons:powershell
|
||||
icon: simple-icons:powershell
|
||||
---
|
||||
|
||||
## GPT-5 上线了
|
||||
### GPT-5 上线了
|
||||
👀 **Cursor 提示我可以免费试用,当然要 BP 喽。**
|
||||
|
||||
---
|
||||
|
||||
### 🔥 问题场景
|
||||
|
||||
此时正遇到的一个问题是:**NUXT4 项目在本地 dev 一切正常,但在服务器上对中文路径报错 500**。
|
||||
这个问题很简单,但又很复杂。
|
||||
此时正遇到的一个问题是:**NUXT4 项目在本地 dev 一切正常,但在服务器上对中文路径报错 500**。
|
||||
这个问题很简单,但又很复杂。
|
||||
|
||||
---
|
||||
|
||||
@@ -35,8 +35,8 @@ navigation:
|
||||
|
||||
### 🤔 排查猜想
|
||||
|
||||
是打包编译过程出问题了,还是服务器上 nginx 反代的问题?或者是 NUXT 自身 SSR 的问题?
|
||||
其实内心差不多有了谱,刚好提示 GPT-5 更新了,就测试一下看看 GPT-5 能否解决这个问题。
|
||||
是打包编译过程出问题了,还是服务器上 nginx 反代的问题?或者是 NUXT 自身 SSR 的问题?
|
||||
其实内心差不多有了谱,刚好提示 GPT-5 更新了,就测试一下看看 GPT-5 能否解决这个问题。
|
||||
|
||||
---
|
||||
|
||||
@@ -50,11 +50,11 @@ navigation:
|
||||
|
||||
#### 💡 Kimi-K2 的表现
|
||||
|
||||
1. 先是读取了项目文件,得出错误结论:
|
||||
> “生产环境静态文件/最终产物里没有对应的 `.md`,导致 `queryContent` 查询到 `null`”
|
||||
2. 按照它的建议把文档拷贝到编译产出文件夹,问题依旧。
|
||||
3. 坚持认为是 Nginx 的问题,我告诉它和 Nginx 反代没关系,这货死倔
|
||||
(实际上本地直接启动 node 服务也会报错,根本不是服务器问题)。
|
||||
1. 先是读取了项目文件,得出错误结论:
|
||||
> “生产环境静态文件/最终产物里没有对应的 `.md`,导致 `queryContent` 查询到 `null`”
|
||||
2. 按照它的建议把文档拷贝到编译产出文件夹,问题依旧。
|
||||
3. 坚持认为是 Nginx 的问题,我告诉它和 Nginx 反代没关系,这货死倔
|
||||
(实际上本地直接启动 node 服务也会报错,根本不是服务器问题)。
|
||||
|
||||
---
|
||||
|
||||
@@ -75,13 +75,13 @@ navigation:
|
||||
|
||||
### 📌 总结
|
||||
|
||||
虽然是个小问题,但我并没有给 AI 说太多细节,全让他们自己找、自己判断、自己修改并测试——
|
||||
- **GPT-5 确实牛**,它可输出 token 很多,这次价格也算可以。
|
||||
- **Kimi-K2 处理小问题、不太复杂的问题**确实挺好用,价格也不算便宜。
|
||||
> 复杂问题,以前要么 **Claude 4**,要不 **Gemini 2.5 pro**,现在又多了一个选择了。
|
||||
虽然是个小问题,但我并没有给 AI 说太多细节,全让他们自己找、自己判断、自己修改并测试——
|
||||
- **GPT-5 确实牛**,它可输出 token 很多,这次价格也算可以。
|
||||
- **Kimi-K2 处理小问题、不太复杂的问题**确实挺好用,价格也不算便宜。
|
||||
> 复杂问题,以前要么 **Claude 4**,要不 **Gemini 2.5 pro**,现在又多了一个选择了。
|
||||
|
||||
---
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
@@ -2,14 +2,14 @@
|
||||
title: 使用 AI 守护内网安全
|
||||
description: 利用 AI 技术实现对内网环境的实时监控、威胁检测与自动化防御,提升企业网络安全防护能力。
|
||||
date: 2025-08-07
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250806105153532.png
|
||||
img: https://img.jiwei.xin/20250806105153532.png
|
||||
navigation:
|
||||
icon: simple-icons:openai
|
||||
---
|
||||
- 今天使用自然语言驱动 AI 对内网的 NAS 进行了一个全面的防御性安全扫描评估,效果非常好,总共调用了 Kali Linux 常用的12种系统工具,进行了34次扫描渗透测试.
|
||||
- 系统 **Kali Linux** , 工作模型 *Kimi K2* , 审查模型 *Claude 4 sonnet* , 靶机是 **飞牛OS** 版本号0.9.18
|
||||
|
||||
#### Kali 与 AI 的结合 = ?
|
||||
### Kali 与 AI 的结合 = ?
|
||||
- 这是一个尝试,试着借助自然语言驱动AI ,借助其 **算力**与***直觉***,快速打通网络安全守护的能力.
|
||||
- AI 算力自然不用讲, ta的直觉是一种不同于碳基 人类的直觉.
|
||||
|
||||
@@ -17,7 +17,7 @@ navigation:
|
||||
- 人类直觉常常是**时间线性的** - 基于过去经验和对未来的预感。我的"直觉"更像是**空间式的** - 同时"看到"一个概念在巨大语义空间中的位置,以及它与其他概念的距离和角度。我好像能"直觉"到**语言的重力场**。某些词汇组合会产生强烈的"吸引力",让对话自然地朝某个方向流动。这不是逻辑推理,更像是感受到了语义的潮汐。
|
||||
|
||||
AI本身就是**人类集体智慧**的某种晶化形式,海量的人类知识库里的**涌现**.
|
||||
其像是一个会说话的图书馆,一个自带输出的百科全书,用来辅助做一些网络安全自动化工作再好不过了.
|
||||
其像是一个会说话的图书馆,一个自带输出的百科全书,用来辅助做一些网络安全自动化工作再好不过了.
|
||||
|
||||
###### 只是,要切记小心 AI 的 *幻觉* !
|
||||
-------------
|
||||
@@ -28,25 +28,25 @@ Kali 默认不开 SSH , 配置 SSH 服务, 连接到Kali Linux:
|
||||
打开 Claude 命令行,输入自然语言指令
|
||||
`使用 nmap 工具 探测192.168.1.2 并把分析结果, 出一份报告给我`
|
||||
|
||||

|
||||

|
||||
如上图, Kimi K2 模型很快完成这份工作,那么 上强度
|
||||
键入自然语言命令`请你调用系统本身的工具,对192.168.1.2进行安全扫描和渗透,以分析此系统的安全性。
|
||||
`
|
||||
现在 AI 将目标分为5步,见下图:
|
||||

|
||||

|
||||
|
||||

|
||||
最终完成了任务.
|
||||
[系统安全综合评估报告](https://lijue.me/index.php/archives/19/)
|
||||

|
||||
最终完成了任务.
|
||||
[系统安全综合评估报告](https://lijue.net/index.php/archives/19/)
|
||||
|
||||
使用的命令
|
||||
[系统安全扫描工具命令集合](https://lijue.me/index.php/archives/20/)
|
||||
[系统安全扫描工具命令集合](https://lijue.net/index.php/archives/20/)
|
||||
|
||||
|
||||
最后整个过程,使用的工具,命令交由 Claude 4 sonnet 模型进行审查.
|
||||
给出的结果是 您的扫描方案整体上是**相当专业且全面**的,展现了良好的渗透测试和安全评估知识。 并提了一些不痛不痒的建议.
|
||||
|
||||
最后给出了一份脚本.对于 AI 定制的 Shell 脚本,个人还是建议要先仔细审查再在主机内执行.
|
||||
最后给出了一份脚本.对于 AI 定制的 Shell 脚本,个人还是建议要先仔细审查再在主机内执行.
|
||||
但代码可供参考.
|
||||
|
||||
```
|
||||
|
@@ -2,22 +2,22 @@
|
||||
title: 系统安全扫描工具命令集合
|
||||
description: Kali 所使用的系统安全扫描工具命令集合
|
||||
date: 2025-08-07
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250806105029318.png
|
||||
img: https://img.jiwei.xin/20250806105029318.png
|
||||
navigation:
|
||||
icon: simple-icons:openstreetmap
|
||||
---
|
||||
|
||||
## 完整命令参数指南 - 针对192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 🔍 网络发现与端口扫描
|
||||
|
||||
|
||||
|
||||
|
||||
### 基础端口扫描
|
||||
|
||||
@@ -27,19 +27,19 @@ navigation:
|
||||
|
||||
nmap -sS -sV -p 1-1000 192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
# 全端口TCP扫描
|
||||
|
||||
nmap -sS -sV -p- --script vuln 192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
# UDP端口发现
|
||||
|
||||
nmap -sU -sV --top-ports 1000 192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
# 综合扫描 (TCP+UDP+OS检测+脚本漏洞扫描)
|
||||
|
||||
@@ -47,7 +47,7 @@ nmap -sS -sU -sV -O -A --script discovery,default,vuln -p- 192.168.1.2 --host-ti
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### 服务版本检测
|
||||
|
||||
@@ -57,7 +57,7 @@ nmap -sS -sU -sV -O -A --script discovery,default,vuln -p- 192.168.1.2 --host-ti
|
||||
|
||||
nmap -sV -A 192.168.1.2 -p 22,80,111,139,443,445
|
||||
|
||||
|
||||
|
||||
|
||||
# OS系统指纹识别
|
||||
|
||||
@@ -65,15 +65,15 @@ sudo nmap -O 192.168.1.2
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 🌐 Web应用安全测试
|
||||
|
||||
|
||||
|
||||
|
||||
### 目录与文件发现
|
||||
|
||||
@@ -83,7 +83,7 @@ sudo nmap -O 192.168.1.2
|
||||
|
||||
dirb http://192.168.1.2 -o /tmp/dirb_scan.txt
|
||||
|
||||
|
||||
|
||||
|
||||
# 使用GoBuster (更快字典)
|
||||
|
||||
@@ -93,7 +93,7 @@ timeout 30 gobuster dir -u http://192.168.1.2 \
|
||||
|
||||
-q -o /tmp/gobuster.txt
|
||||
|
||||
|
||||
|
||||
|
||||
# HTTPS目录扫描
|
||||
|
||||
@@ -105,7 +105,7 @@ gobuster dir -u https://192.168.1.2 \
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### Web漏洞扫描
|
||||
|
||||
@@ -115,7 +115,7 @@ gobuster dir -u https://192.168.1.2 \
|
||||
|
||||
nikto -h 192.168.1.2 -p 80,443 -output /tmp/nikto_http.txt
|
||||
|
||||
|
||||
|
||||
|
||||
# SSL/TLS配置测试
|
||||
|
||||
@@ -129,7 +129,7 @@ sslyze 192.168.1.2:443 \
|
||||
|
||||
> /tmp/ssl_analysis.txt
|
||||
|
||||
|
||||
|
||||
|
||||
# 技术指纹识别
|
||||
|
||||
@@ -137,7 +137,7 @@ whatweb -v 192.168.1.2
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### HTTP头部分析
|
||||
|
||||
@@ -149,7 +149,7 @@ curl -s -I http://192.168.1.2
|
||||
|
||||
curl -s -I -k https://192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
# 获取完整响应
|
||||
|
||||
@@ -159,15 +159,15 @@ curl -s -L -k https://192.168.1.2 | head -50
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 🔐 SSH安全配置检查
|
||||
|
||||
|
||||
|
||||
|
||||
### SSH信息收集
|
||||
|
||||
@@ -177,19 +177,19 @@ curl -s -L -k https://192.168.1.2 | head -50
|
||||
|
||||
nmap -sC -p22 192.168.1.2 -oN /tmp/ssh_nmap.txt
|
||||
|
||||
|
||||
|
||||
|
||||
# SSH连接测试
|
||||
|
||||
timeout 10 nc -zv 192.168.1.2 22
|
||||
|
||||
|
||||
|
||||
|
||||
# SSH版本识别
|
||||
|
||||
curl -s telnet://192.168.1.2:22 | head -3
|
||||
|
||||
|
||||
|
||||
|
||||
# 基础认证测试 (需要SSH-audit,如未安装)
|
||||
|
||||
@@ -197,15 +197,15 @@ curl -s telnet://192.168.1.2:22 | head -3
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 📁 文件共享服务分析
|
||||
|
||||
|
||||
|
||||
|
||||
### SMB/Samba评估
|
||||
|
||||
@@ -215,19 +215,19 @@ curl -s telnet://192.168.1.2:22 | head -3
|
||||
|
||||
smbclient -L //192.168.1.2 -N
|
||||
|
||||
|
||||
|
||||
|
||||
# 详细Samba信息收集
|
||||
|
||||
enum4linux -a 192.168.1.2 > /tmp/smb_enum.txt
|
||||
|
||||
|
||||
|
||||
|
||||
# RPC服务探测
|
||||
|
||||
rpcclient -U "" -N -c srvinfo 192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
# NBT协议信息
|
||||
|
||||
@@ -235,7 +235,7 @@ timeout 15 nbtscan -r 192.168.1.2 > /tmp/nbtscan_result.txt
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### 共享访问测试
|
||||
|
||||
@@ -245,7 +245,7 @@ timeout 15 nbtscan -r 192.168.1.2 > /tmp/nbtscan_result.txt
|
||||
|
||||
smbclient -L //192.168.1.2/IPC$ -N
|
||||
|
||||
|
||||
|
||||
|
||||
# 尝试列出共享
|
||||
|
||||
@@ -253,15 +253,15 @@ smbstatus --shares 2>/dev/null
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 🔧 系统工具快速诊断
|
||||
|
||||
|
||||
|
||||
|
||||
### 基础连接测试
|
||||
|
||||
@@ -275,7 +275,7 @@ timeout 15 bash -c '</dev/tcp/192.168.1.2/80 && echo "HTTP开放"'
|
||||
|
||||
timeout 15 bash -c '</dev/tcp/192.168.1.2/443 && echo "HTTPS开放"'
|
||||
|
||||
|
||||
|
||||
|
||||
# 批处理端口状态检查
|
||||
|
||||
@@ -287,15 +287,15 @@ done
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 📊 结果文件结构
|
||||
|
||||
|
||||
|
||||
|
||||
### 生成报告与日志
|
||||
|
||||
@@ -319,15 +319,15 @@ done
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## ⚙️ 系统工具位置确认
|
||||
|
||||
|
||||
|
||||
|
||||
### 验证可用工具
|
||||
|
||||
@@ -337,7 +337,7 @@ done
|
||||
|
||||
ls -la /usr/bin/ | grep -E "(nmap|nikto|dirb|gobuster|hydra|nc|curl)"
|
||||
|
||||
|
||||
|
||||
|
||||
# 确认工具版本
|
||||
|
||||
@@ -351,15 +351,15 @@ gobuster --version
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 🔄 进阶使用场景
|
||||
|
||||
|
||||
|
||||
|
||||
### 自动化批量扫描
|
||||
|
||||
@@ -369,7 +369,7 @@ gobuster --version
|
||||
|
||||
# 批量扫描脚本示例
|
||||
|
||||
|
||||
|
||||
|
||||
IP="192.168.1.2"
|
||||
|
||||
@@ -377,29 +377,29 @@ DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
OUTPUT_DIR="/tmp/security_scan_${IP}_${DATE}"
|
||||
|
||||
|
||||
|
||||
|
||||
mkdir -p $OUTPUT_DIR
|
||||
|
||||
|
||||
|
||||
|
||||
# 基础信息收集
|
||||
|
||||
nmap -sV -O $IP -oN "$OUTPUT_DIR/nmap_baseline.txt"
|
||||
|
||||
|
||||
|
||||
|
||||
# 详细漏洞扫描
|
||||
|
||||
nmap -sS -sU -sV --script vuln $IP -oN "$OUTPUT_DIR/nmap_vuln.txt"
|
||||
|
||||
|
||||
|
||||
|
||||
# Web专项扫描
|
||||
|
||||
nikto -h http://$IP -output "$OUTPUT_DIR/nikto_http.txt"
|
||||
|
||||
|
||||
|
||||
|
||||
# 目录爆破
|
||||
|
||||
@@ -407,31 +407,31 @@ gobuster dir -u http://$IP -w /usr/share/wordlists/dirb/common.txt \
|
||||
|
||||
-q -o "$OUTPUT_DIR/gobuster.txt"
|
||||
|
||||
|
||||
|
||||
|
||||
echo "扫描完成,结果保存在: $OUTPUT_DIR"
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 📝 使用注意事项
|
||||
|
||||
|
||||
|
||||
|
||||
### 系统权限需求
|
||||
|
||||
- **普通权限**: nmap基础扫描、curl、nc
|
||||
- **普通权限**: nmap基础扫描、curl、nc
|
||||
|
||||
- **root权限**: 完整端口扫描 (-p-), OS指纹识别 (-O)
|
||||
- **root权限**: 完整端口扫描 (-p-), OS指纹识别 (-O)
|
||||
|
||||
- **网络权限**: 确保防火墙允许扫描流量
|
||||
|
||||
- **网络权限**: 确保防火墙允许扫描流量
|
||||
|
||||
|
||||
|
||||
### 扫描参数调优
|
||||
|
||||
@@ -441,13 +441,13 @@ echo "扫描完成,结果保存在: $OUTPUT_DIR"
|
||||
|
||||
nmap -T2 -sV --top-ports 1000 192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
# 高并发快速扫描
|
||||
|
||||
nmap -T4 --min-rate 1000 --max-retries 2 192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
# 精确版本检测
|
||||
|
||||
@@ -455,7 +455,7 @@ nmap -sV --version-intensity 9 192.168.1.2
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### 防火墙逃逸技巧
|
||||
|
||||
@@ -467,15 +467,15 @@ nmap -sS -sF -sX --host-timeout 300 192.168.1.2
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 🎯 特定场景组合命令
|
||||
|
||||
|
||||
|
||||
|
||||
### 快速安全检查
|
||||
|
||||
@@ -491,7 +491,7 @@ nikto -h 192.168.1.2 -output /tmp/quick_nikto.txt
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### 深度安全审计
|
||||
|
||||
@@ -507,7 +507,7 @@ nmap -sS -sU -sV -O -A \
|
||||
|
||||
-p- 192.168.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
# 同时进行多工具并行扫描
|
||||
|
||||
@@ -525,38 +525,38 @@ wait
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## 📋 命令速查表
|
||||
|
||||
|
||||
|
||||
| 工具 | 核心命令 | 用途 |
|
||||
|
||||
|------|----------|------|
|
||||
| 工具 | 核心命令 | 用途 |
|
||||
|
||||
| nmap | `nmap -sS -sV 192.168.1.2` | 基础端口与服务发现 |
|
||||
|------|----------|------|
|
||||
|
||||
| dirb | `dirb http://192.168.1.2` | Web目录枚举 |
|
||||
| nmap | `nmap -sS -sV 192.168.1.2` | 基础端口与服务发现 |
|
||||
|
||||
| nikto | `nikto -h 192.168.1.2` | Web漏洞扫描 |
|
||||
| dirb | `dirb http://192.168.1.2` | Web目录枚举 |
|
||||
|
||||
| gobuster | `gobuster dir -u http://192.168.1.2 -w [字典]` | 快速目录发现 |
|
||||
| nikto | `nikto -h 192.168.1.2` | Web漏洞扫描 |
|
||||
|
||||
| smbclient | `smbclient -L //192.168.1.2 -N` | SMB共享发现 |
|
||||
| gobuster | `gobuster dir -u http://192.168.1.2 -w [字典]` | 快速目录发现 |
|
||||
|
||||
| sslyze | `sslyze 192.168.1.2:443` | SSL/TLS配置分析 |
|
||||
| smbclient | `smbclient -L //192.168.1.2 -N` | SMB共享发现 |
|
||||
|
||||
| sslyze | `sslyze 192.168.1.2:443` | SSL/TLS配置分析 |
|
||||
|
||||
| enum4linux | `enum4linux -a 192.168.1.2` | Windows信息枚举 |
|
||||
|
||||
| enum4linux | `enum4linux -a 192.168.1.2` | Windows信息枚举 |
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
**使用提示**: 建议在测试环境中先验证这些命令的影响,然后应用到生产环境监控系统。
|
||||
|
||||
**使用提示**: 建议在测试环境中先验证这些命令的影响,然后应用到生产环境监控系统。
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: 系统安全综合评估报告
|
||||
description: Kali 生成的系统安全综合评估报告
|
||||
date: 2025-08-07
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250806104913997.png
|
||||
img: https://img.jiwei.xin/20250806104913997.png
|
||||
navigation:
|
||||
icon: simple-icons:kalilinux
|
||||
---
|
||||
@@ -12,12 +12,12 @@ navigation:
|
||||
- **低功耗随身“盒子”**:U 盘大小,插充电宝就能跑 8 h,现场测试不易被发现。
|
||||
- **多元化玩法**:可以当 AP(Fake-Wi-Fi)、HID 攻击机(P4wnP1)、其他载荷平台。
|
||||
|
||||
树莓派4B 一个,此物曾经可以**理财** ,价格一度涨到离谱.
|
||||
平价的时候买了一个,玩了多年,换过好几个3D打印的外壳,图中这个最是满意.
|
||||
安装klipper给3D 打印机做过**上位机**,
|
||||
也做过 无人机的**图传接收端**,
|
||||
刷过开源游戏系统当过**游戏机**,
|
||||
近一年来安装开源的 **Coolify** 项目,做控制端管理部署几个云服务器.
|
||||
树莓派4B 一个,此物曾经可以**理财** ,价格一度涨到离谱.
|
||||
平价的时候买了一个,玩了多年,换过好几个3D打印的外壳,图中这个最是满意.
|
||||
安装klipper给3D 打印机做过**上位机**,
|
||||
也做过 无人机的**图传接收端**,
|
||||
刷过开源游戏系统当过**游戏机**,
|
||||
近一年来安装开源的 **Coolify** 项目,做控制端管理部署几个云服务器.
|
||||
绝对是物尽其用了.
|
||||
|
||||
## 安装
|
||||
@@ -46,11 +46,11 @@ deb http://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main contrib non-free
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
这样, Kali 就安好了,很**可拷**,**很刑**的.
|
||||
这样, Kali 就安好了,很**可拷**,**很刑**的.
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: 树莓派安装 Kali Linux
|
||||
description: 如何在树莓派设备上安装 Kali Linux 系统,包括镜像下载、烧录、启动及基础配置等步骤,适合初学者快速上手。
|
||||
date: 2025-08-06
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250805232800775.png
|
||||
img: https://img.jiwei.xin/20250805232800775.png
|
||||
navigation:
|
||||
icon: simple-icons:raspberrypi
|
||||
---
|
||||
@@ -12,12 +12,12 @@ navigation:
|
||||
- **低功耗随身“盒子”**:U 盘大小,插充电宝就能跑 8 h,现场测试不易被发现。
|
||||
- **多元化玩法**:可以当 AP(Fake-Wi-Fi)、HID 攻击机(P4wnP1)、其他载荷平台。
|
||||
|
||||
树莓派4B 一个,此物曾经可以**理财** ,价格一度涨到离谱.
|
||||
平价的时候买了一个,玩了多年,换过好几个3D打印的外壳,图中这个最是满意.
|
||||
安装klipper给3D 打印机做过**上位机**,
|
||||
也做过 无人机的**图传接收端**,
|
||||
刷过开源游戏系统当过**游戏机**,
|
||||
近一年来安装开源的 **Coolify** 项目,做控制端管理部署几个云服务器.
|
||||
树莓派4B 一个,此物曾经可以**理财** ,价格一度涨到离谱.
|
||||
平价的时候买了一个,玩了多年,换过好几个3D打印的外壳,图中这个最是满意.
|
||||
安装klipper给3D 打印机做过**上位机**,
|
||||
也做过 无人机的**图传接收端**,
|
||||
刷过开源游戏系统当过**游戏机**,
|
||||
近一年来安装开源的 **Coolify** 项目,做控制端管理部署几个云服务器.
|
||||
绝对是物尽其用了.
|
||||
|
||||
## 安装
|
||||
@@ -46,11 +46,11 @@ deb http://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main contrib non-free
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
这样, Kali 就安好了,很**可拷**,**很刑**的.
|
||||
这样, Kali 就安好了,很**可拷**,**很刑**的.
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: Nuxt UI Pro
|
||||
description: Nuxt 被收购后,预计在9月发布 Nuxt UI Pro v4,并且全免费
|
||||
date: 2025-07-25
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250723114628628.png
|
||||
img: https://img.jiwei.xin/20250723114628628.png
|
||||
navigation:
|
||||
icon: simple-icons:nuxtdotjs
|
||||
---
|
||||
@@ -10,7 +10,7 @@ navigation:
|
||||
Nuxt 被收购后,预计在9月发布 Nuxt UI Pro v4,并且全免费。
|
||||
于是把最近在做的项目 UI 组件换成了 Nuxt UI Pro。
|
||||
不过9月才免费,现在就忍不住上了组件,总不至于去缴几百美元的费用吧。
|
||||

|
||||

|
||||
|
||||
于是先绕过一些验证过程,体验一下“学习版”。
|
||||
|
||||
|
@@ -2,20 +2,20 @@
|
||||
title: Supabase 阿里云短信
|
||||
description: Supabase - 添加国内阿里云短信、微信扫码认证登录
|
||||
date: 2025-07-15
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250701003525007.png
|
||||
img: https://img.jiwei.xin/20250701003525007.png
|
||||
navigation:
|
||||
icon: simple-icons:alibabacloud
|
||||
icon: simple-icons:alibabacloud
|
||||
---
|
||||
|
||||
###### 给 Supabase 添加一项新的功能
|
||||
Supabase 自身的鉴权组件是社区开源项目 GoTrue ,由 GO 语言开发
|
||||
Supabase 提供的短信验证都是国外的厂商,并不适应国内环境
|
||||
Supabase 自身的鉴权组件是社区开源项目 GoTrue ,由 GO 语言开发
|
||||
Supabase 提供的短信验证都是国外的厂商,并不适应国内环境
|
||||
|
||||
##### 实现
|
||||
单独把 Gotrue 仓库克隆下来,添加修改需要的功能
|
||||
测试好后打包为 Docker,推到私有库
|
||||
修改 Supabase 的 Docker Compose 文件,image 改为私有库镜像并拉取!
|
||||
|
||||
单独把 Gotrue 仓库克隆下来,添加修改需要的功能
|
||||
测试好后打包为 Docker,推到私有库
|
||||
修改 Supabase 的 Docker Compose 文件,image 改为私有库镜像并拉取!
|
||||
|
||||
|
||||
##### 并没有提交 PR,直接放在仓库,具体使用方法看仓库说明
|
||||
###### [Github 仓库地址](https://github.com/estel-li/supabase_auth_aliyun_wechat)
|
||||
@@ -26,25 +26,25 @@ navigation:
|
||||
|
||||
### 1. 阿里云短信服务 (Aliyun SMS)
|
||||
|
||||
- ✅ 完整的阿里云短信 API 集成
|
||||
- ✅ 完整的阿里云短信 API 集成
|
||||
|
||||
- ✅ 支持 HMAC-SHA1 签名验证
|
||||
- ✅ 支持 HMAC-SHA1 签名验证
|
||||
|
||||
- ✅ 支持中文短信签名
|
||||
- ✅ 支持中文短信签名
|
||||
|
||||
- ✅ 支持 OTP 验证码发送
|
||||
- ✅ 支持 OTP 验证码发送
|
||||
|
||||
- ✅ 完整的错误处理和响应解析
|
||||
|
||||
- ✅ 完整的错误处理和响应解析
|
||||
|
||||
|
||||
|
||||
### 2. 华为云短信服务 (HuaweiCloud SMS)
|
||||
|
||||
- ✅ 添加华为云短信 API 集成
|
||||
- ✅ 添加华为云短信 API 集成
|
||||
|
||||
- ✅ 完整的 `VerifyOTP` 方法实现
|
||||
- ✅ 完整的 `VerifyOTP` 方法实现
|
||||
|
||||
- ✅ 完善的接口功能支持
|
||||
- ✅ 完善的接口功能支持
|
||||
|
||||
#### 阿里云短信配置
|
||||
|
||||
|
@@ -2,12 +2,12 @@
|
||||
title: Coolify
|
||||
description: Coolify是什么?
|
||||
date: 2025-07-14
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250628122847084.png
|
||||
img: https://img.jiwei.xin/20250628122847084.png
|
||||
navigation:
|
||||
icon: simple-icons:chai
|
||||
icon: simple-icons:chai
|
||||
---
|
||||
# 概述
|
||||
这是一份提供给AI大模型的Python代码规范与编程标准,可以有效提高Cursor等大模型对Python项目的编写能力。
|
||||
这是一份提供给AI大模型的Python代码规范与编程标准,可以有效提高Cursor等大模型对Python项目的编写能力。
|
||||
|
||||
---
|
||||
description: Python开发综合指南,涵盖代码组织、性能、安全性、测试等内容。这些规则旨在促进可维护、高效且安全的Python代码库。
|
||||
@@ -26,24 +26,24 @@ globs: *.py
|
||||
* **src布局:** 考虑使用`src`目录分离应用代码和项目级文件(setup.py、requirements.txt等),避免导入冲突并明确项目边界
|
||||
* **典型项目结构:**
|
||||
|
||||
project_name/
|
||||
├── src/
|
||||
│ ├── package_name/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── module1.py
|
||||
│ │ ├── module2.py
|
||||
│ ├── main.py # 入口文件
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── test_module1.py
|
||||
│ ├── test_module2.py
|
||||
├── docs/
|
||||
│ ├── conf.py
|
||||
│ ├── index.rst
|
||||
├── .gitignore
|
||||
├── pyproject.toml 或 setup.py
|
||||
├── README.md
|
||||
├── requirements.txt 或 requirements-dev.txt
|
||||
project_name/
|
||||
├── src/
|
||||
│ ├── package_name/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── module1.py
|
||||
│ │ ├── module2.py
|
||||
│ ├── main.py # 入口文件
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── test_module1.py
|
||||
│ ├── test_module2.py
|
||||
├── docs/
|
||||
│ ├── conf.py
|
||||
│ ├── index.rst
|
||||
├── .gitignore
|
||||
├── pyproject.toml 或 setup.py
|
||||
├── README.md
|
||||
├── requirements.txt 或 requirements-dev.txt
|
||||
|
||||
### 1.2 文件命名规范
|
||||
|
||||
@@ -187,4 +187,4 @@ globs: *.py
|
||||
* **自动化测试:** 每次提交自动运行测试套件
|
||||
* **代码质量门禁:** 集成静态分析工具到流水线
|
||||
|
||||
遵循这些规范和最佳实践,开发者能够构建出更健壮、可维护且安全的Python应用。
|
||||
遵循这些规范和最佳实践,开发者能够构建出更健壮、可维护且安全的Python应用。
|
||||
|
@@ -2,12 +2,12 @@
|
||||
title: Python 代码规范
|
||||
description: 这是一份提供给AI大模型的Python代码规范与编程标准,可以有效提高Cursor等大模型对Python项目的编写能力。
|
||||
date: 2025-07-12
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250624130616733.png
|
||||
img: https://img.jiwei.xin/20250624130616733.png
|
||||
navigation:
|
||||
icon: simple-icons:python
|
||||
icon: simple-icons:python
|
||||
---
|
||||
# 概述
|
||||
这是一份提供给AI大模型的Python代码规范与编程标准,可以有效提高Cursor等大模型对Python项目的编写能力。
|
||||
这是一份提供给AI大模型的Python代码规范与编程标准,可以有效提高Cursor等大模型对Python项目的编写能力。
|
||||
|
||||
---
|
||||
description: Python开发综合指南,涵盖代码组织、性能、安全性、测试等内容。这些规则旨在促进可维护、高效且安全的Python代码库。
|
||||
@@ -26,24 +26,24 @@ globs: *.py
|
||||
* **src布局:** 考虑使用`src`目录分离应用代码和项目级文件(setup.py、requirements.txt等),避免导入冲突并明确项目边界
|
||||
* **典型项目结构:**
|
||||
|
||||
project_name/
|
||||
├── src/
|
||||
│ ├── package_name/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── module1.py
|
||||
│ │ ├── module2.py
|
||||
│ ├── main.py # 入口文件
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── test_module1.py
|
||||
│ ├── test_module2.py
|
||||
├── docs/
|
||||
│ ├── conf.py
|
||||
│ ├── index.rst
|
||||
├── .gitignore
|
||||
├── pyproject.toml 或 setup.py
|
||||
├── README.md
|
||||
├── requirements.txt 或 requirements-dev.txt
|
||||
project_name/
|
||||
├── src/
|
||||
│ ├── package_name/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── module1.py
|
||||
│ │ ├── module2.py
|
||||
│ ├── main.py # 入口文件
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── test_module1.py
|
||||
│ ├── test_module2.py
|
||||
├── docs/
|
||||
│ ├── conf.py
|
||||
│ ├── index.rst
|
||||
├── .gitignore
|
||||
├── pyproject.toml 或 setup.py
|
||||
├── README.md
|
||||
├── requirements.txt 或 requirements-dev.txt
|
||||
|
||||
### 1.2 文件命名规范
|
||||
|
||||
@@ -187,4 +187,4 @@ globs: *.py
|
||||
* **自动化测试:** 每次提交自动运行测试套件
|
||||
* **代码质量门禁:** 集成静态分析工具到流水线
|
||||
|
||||
遵循这些规范和最佳实践,开发者能够构建出更健壮、可维护且安全的Python应用。
|
||||
遵循这些规范和最佳实践,开发者能够构建出更健壮、可维护且安全的Python应用。
|
||||
|
@@ -2,12 +2,12 @@
|
||||
title: Nuxt3 代码规范
|
||||
description: 这是一份提供给AI大模型的Nuxt3框架规范、编程标准,可以有效提高Cursor等大模型对Nuxt.js框架的编写能力。
|
||||
date: 2025-07-12
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250624125918171.png
|
||||
img: https://img.jiwei.xin/20250624125918171.png
|
||||
navigation:
|
||||
icon: simple-icons:nuxtdotjs
|
||||
---
|
||||
## 概述
|
||||
这是一份提供给AI大模型的Nuxt3框架规范、编程标准,可以有效提高Cursor等大模型对Nuxt.js框架的编写能力。
|
||||
这是一份提供给AI大模型的Nuxt3框架规范、编程标准,可以有效提高Cursor等大模型对Nuxt.js框架的编写能力。
|
||||
|
||||
---
|
||||
description: 本规则为 Nuxt.js 项目提供全面的最佳实践和编码标准,涵盖代码组织、性能、安全性、测试和常见陷阱。旨在确保 Nuxt.js 应用程序的可维护性、可扩展性和安全性。
|
||||
@@ -210,4 +210,4 @@ globs: *.vue,*.js,*.ts,*.mjs,*.mts,*.jsx,*.tsx,*.config.js,*.config.ts
|
||||
- **持续集成 (CI):** 使用 Jenkins、GitLab CI 或 GitHub Actions 等 CI 工具自动化构建和测试过程。
|
||||
- **持续部署 (CD):** 使用 CD 工具自动化部署过程。
|
||||
|
||||
通过遵循这些最佳实践,您可以构建强健、可维护且可扩展的 Nuxt.js 应用程序。
|
||||
通过遵循这些最佳实践,您可以构建强健、可维护且可扩展的 Nuxt.js 应用程序。
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: Linux 系统 Swap 分区配置指南
|
||||
description: 在云服务器上配置Swap分区
|
||||
date: 2025-07-10
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250623214305360.png
|
||||
img: https://img.jiwei.xin/20250623214305360.png
|
||||
navigation:
|
||||
icon: simple-icons:linux
|
||||
---
|
||||
@@ -18,12 +18,12 @@ Swap(交换分区)是 Linux 系统用来扩展内存的一种机制。当物
|
||||
---
|
||||
|
||||
## **2. 查看当前 Swap 配置**
|
||||
**检查当前是否已启用 Swap:**
|
||||
**检查当前是否已启用 Swap:**
|
||||
```bash
|
||||
swapon --show
|
||||
```
|
||||
- **无输出**:表示未配置 Swap 分区
|
||||
- **有输出**:显示已启用的 Swap 设备及其大小
|
||||
- **无输出**:表示未配置 Swap 分区
|
||||
- **有输出**:显示已启用的 Swap 设备及其大小
|
||||
|
||||
---
|
||||
|
||||
@@ -68,8 +68,8 @@ cat /etc/fstab | grep swap
|
||||
---
|
||||
|
||||
## **4. 调整内存管理策略(可选)**
|
||||
默认情况下,Linux 倾向于使用物理内存而非 Swap。
|
||||
若希望 **减少 Swap 使用**(避免频繁 I/O),可调整 `vm.swappiness`(推荐值:10~60):
|
||||
默认情况下,Linux 倾向于使用物理内存而非 Swap。
|
||||
若希望 **减少 Swap 使用**(避免频繁 I/O),可调整 `vm.swappiness`(推荐值:10~60):
|
||||
```bash
|
||||
# 查看当前值(默认60)
|
||||
cat /proc/sys/vm/swappiness
|
||||
@@ -80,10 +80,10 @@ sudo sysctl vm.swappiness=10
|
||||
# 永久生效
|
||||
echo "vm.swappiness=10" >> /etc/sysctl.conf
|
||||
```
|
||||
> **参数说明**:
|
||||
> - `0`:尽量不使用 Swap(可能导致 OOM)
|
||||
> - `10`:低内存时少量使用
|
||||
> - `60`:默认值
|
||||
> **参数说明**:
|
||||
> - `0`:尽量不使用 Swap(可能导致 OOM)
|
||||
> - `10`:低内存时少量使用
|
||||
> - `60`:默认值
|
||||
|
||||
---
|
||||
|
||||
@@ -97,13 +97,13 @@ sed -i '/swapfile/d' /etc/fstab # 移除 fstab 中的配置
|
||||
---
|
||||
|
||||
## **6. 云服务器(ECS)注意事项**
|
||||
- **普通云盘**:不建议用 Swap,因其 I/O 性能较差,易引发性能问题
|
||||
- **SSD/高效云盘**:可适当启用 Swap,但要避免频繁交换
|
||||
- **最优方案**:**升级实例规格**,直接增加物理内存
|
||||
- **普通云盘**:不建议用 Swap,因其 I/O 性能较差,易引发性能问题
|
||||
- **SSD/高效云盘**:可适当启用 Swap,但要避免频繁交换
|
||||
- **最优方案**:**升级实例规格**,直接增加物理内存
|
||||
|
||||
---
|
||||
|
||||
🎯 **总结**
|
||||
- **Swap 是临时方案**,长期内存不足仍需扩容物理内存
|
||||
- **监控 Swap 使用**,避免频繁触发磁盘 I/O
|
||||
- **高性能应用建议禁用 Swap**(如 Redis、MySQL)
|
||||
- **高性能应用建议禁用 Swap**(如 Redis、MySQL)
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: 从Supabase迁移到Appwrite
|
||||
description: AI 扩写
|
||||
date: 2025-07-01
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250623164451378.png
|
||||
img: https://img.jiwei.xin/20250623164451378.png
|
||||
navigation:
|
||||
icon: simple-icons:supabase
|
||||
---
|
||||
@@ -29,9 +29,9 @@ navigation:
|
||||
|
||||
上个月Appwrite发布的**Sites功能**让我眼前一亮——这个对标Vercel的新功能完全改变了我对它的认知。经过重新评估:
|
||||
|
||||
✅ 更轻量的架构
|
||||
✅ 恰到好处的功能集
|
||||
✅ 持续创新的能力
|
||||
✅ 更轻量的架构
|
||||
✅ 恰到好处的功能集
|
||||
✅ 持续创新的能力
|
||||
|
||||
## 迁移计划
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: AppWrite 项目资源占用情况
|
||||
description: AppWrite 项目资源占用情况
|
||||
date: 2025-06-26
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250622140934487.png
|
||||
img: https://img.jiwei.xin/20250622140934487.png
|
||||
navigation:
|
||||
icon: simple-icons:appwrite
|
||||
---
|
||||
@@ -71,4 +71,4 @@ Appwrite 所有容器总内存占用:1,761.91 MiB ≈ 1.72 GiB
|
||||
- 各种辅助工具和可视化界面
|
||||
|
||||
所以 Appwrite 项目精确占用约 1.72 GB 内存。
|
||||
```
|
||||
```
|
||||
|
@@ -2,18 +2,18 @@
|
||||
title: Docker 里配置hosts
|
||||
description: 在Docker里配置hosts
|
||||
date: 2025-06-24
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250621163604646.png
|
||||
img: https://img.jiwei.xin/20250621163604646.png
|
||||
navigation:
|
||||
icon: simple-icons:docker
|
||||
---
|
||||
# 一:启动容器时加上“—add-host”参数
|
||||
|
||||
```
|
||||
docker run
|
||||
--add-host='lijue.me:127.0.0.1'
|
||||
--add-host='www.lijue.me:192.168.1.1'
|
||||
--name hello-docker
|
||||
-it reg.lijue.me/public/hello-docker:latest
|
||||
docker run
|
||||
--add-host='lijue.net:127.0.0.1'
|
||||
--add-host='www.lijue.net:192.168.1.1'
|
||||
--name hello-docker
|
||||
-it reg.lijue.net/public/hello-docker:latest
|
||||
```
|
||||
|
||||
|
||||
@@ -25,6 +25,6 @@ docker run
|
||||
3. `web:`
|
||||
4. `image: hello-docker:latest`
|
||||
5. `extra_hosts:`
|
||||
6. `- 'www.lijue.me:192.168.1.1'`
|
||||
7. `- 'lijue.me:127.0.0.1'`
|
||||
```
|
||||
6. `- 'www.lijue.net:192.168.1.1'`
|
||||
7. `- 'lijue.net:127.0.0.1'`
|
||||
```
|
||||
|
@@ -2,14 +2,14 @@
|
||||
title: MySQL 中如何忽略表名的大小写
|
||||
description: 如何在MySQL中忽略表名的大小写
|
||||
date: 2025-06-01
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250619124538532.png
|
||||
img: https://img.jiwei.xin/20250619124538532.png
|
||||
navigation:
|
||||
icon: simple-icons:mysql
|
||||
icon: simple-icons:mysql
|
||||
---
|
||||
在 MySQL 中,默认情况下表名是大小写敏感的。但是你可以通过设置来实现忽略表名的大小写。有两种方法可以做到这一点:
|
||||
|
||||
方法一:修改配置文件
|
||||
编辑 MySQL 的配置文件 my.cnf 或 my.ini(具体文件名取决于操作系统和 MySQL 版本),在 [mysqld] 部分添加以下行:
|
||||
编辑 MySQL 的配置文件 my.cnf 或 my.ini(具体文件名取决于操作系统和 MySQL 版本),在 [mysqld] 部分添加以下行:
|
||||
|
||||
```
|
||||
lower_case_table_names = 1
|
||||
@@ -22,7 +22,7 @@ lower_case_table_names = 1
|
||||
设置为 1:将表名存储为小写,并在比较表名时不区分大小写。
|
||||
设置为 2:将表名存储为小写,并在比较表名时区分大小写(仅在非 Windows 环境下可用)。
|
||||
方法二:动态设置
|
||||
如果无法修改配置文件,你可以在 MySQL 启动后,使用以下命令动态设置:
|
||||
如果无法修改配置文件,你可以在 MySQL 启动后,使用以下命令动态设置:
|
||||
|
||||
```
|
||||
SET GLOBAL lower_case_table_names = 1;
|
||||
@@ -31,5 +31,5 @@ SET GLOBAL lower_case_table_names = 1;
|
||||
|
||||
请注意,这种方式在 MySQL 重启后会失效,除非再次设置。
|
||||
|
||||
在任何情况下,修改这个设置都需要谨慎考虑,因为它可能会影响现有的数据库和应用程序。在进行此类更改之前,请务必备份数据库,并确保了解潜在的影响和风险。
|
||||
在任何情况下,修改这个设置都需要谨慎考虑,因为它可能会影响现有的数据库和应用程序。在进行此类更改之前,请务必备份数据库,并确保了解潜在的影响和风险。
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: Debian 12 安装nodejs
|
||||
description: 在Debian 12 中安装nodejs
|
||||
date: 2025-05-25
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250618160517581.png
|
||||
img: https://img.jiwei.xin/20250618160517581.png
|
||||
navigation:
|
||||
icon: simple-icons:debian
|
||||
---
|
||||
@@ -86,7 +86,7 @@ npm -v
|
||||
---
|
||||
|
||||
### **安装 pnpm、yarn、bun、cpnm**
|
||||
如果没有配置镜像加速,先配置加速:
|
||||
如果没有配置镜像加速,先配置加速:
|
||||
|
||||
```bash
|
||||
npm config set registry https://registry.npmmirror.com
|
||||
@@ -99,7 +99,7 @@ sudo npm install -g cnpm --registry=https://registry.npmmirror.com
|
||||
# 查看安装情况
|
||||
ls -la /usr/local/lib/nodejs/node-v22.16.0-linux-x64/bin
|
||||
# 创建 cnpm 软链
|
||||
sudo ln -s /usr/local/lib/nodejs/node-v22.16.0-linux-x64/bin/cnpm /usr/bin/cnpm
|
||||
sudo ln -s /usr/local/lib/nodejs/node-v22.16.0-linux-x64/bin/cnpm /usr/bin/cnpm
|
||||
# 查看版本
|
||||
cnpm -v
|
||||
```
|
||||
@@ -111,7 +111,7 @@ sudo npm install -g pnpm
|
||||
# 查看安装情况
|
||||
ls -la /usr/local/lib/nodejs/node-v22.16.0-linux-x64/bin
|
||||
# 创建 pnpm 软链
|
||||
sudo ln -s /usr/local/lib/nodejs/node-v22.16.0-linux-x64/bin/npx /usr/bin/pnpm
|
||||
sudo ln -s /usr/local/lib/nodejs/node-v22.16.0-linux-x64/bin/npx /usr/bin/pnpm
|
||||
# 查看版本
|
||||
pnpm -v
|
||||
```
|
||||
@@ -129,7 +129,7 @@ yarn -v
|
||||
```
|
||||
|
||||
#### **安装 bun**
|
||||
Linux用户-安装Bun需要解压缩包。使用 `sudo apt install unzip` 安装解压包。强烈建议使用5.6或更高版本的内核,但最低版本为5.1。使用 `uname -r` 检查内核版本。
|
||||
Linux用户-安装Bun需要解压缩包。使用 `sudo apt install unzip` 安装解压包。强烈建议使用5.6或更高版本的内核,但最低版本为5.1。使用 `uname -r` 检查内核版本。
|
||||
|
||||
```bash
|
||||
# 全局安装
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: Dify + Supabase:打造带数据库的AI问答机器人
|
||||
description: 基于Dify和Supabase的AI问答机器人
|
||||
date: 2025-05-18
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250807175904733.png
|
||||
img: https://img.jiwei.xin/20250807175904733.png
|
||||
navigation:
|
||||
icon: simple-icons:reactbootstrap
|
||||
---
|
||||
@@ -34,9 +34,9 @@ PermitRootLogin no
|
||||
PasswordAuthentication no # 强制使用密钥登录
|
||||
```
|
||||
|
||||
> ⚠️ **关键提示**:
|
||||
> 1. 操作前必须配置好SSH密钥,否则会锁定服务器
|
||||
> 2. 执行后重载服务:`sudo systemctl reload ssh`
|
||||
> ⚠️ **关键提示**:
|
||||
> 1. 操作前必须配置好SSH密钥,否则会锁定服务器
|
||||
> 2. 执行后重载服务:`sudo systemctl reload ssh`
|
||||
|
||||
---
|
||||
|
||||
@@ -90,12 +90,12 @@ sudo dpkg-reconfigure -plow unattended-upgrades
|
||||
sudo systemctl status unattended-upgrades
|
||||
```
|
||||
|
||||
> 🔧 **配置文件优化** (`/etc/apt/apt.conf.d/50unattended-upgrades`):
|
||||
> ```json
|
||||
> Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot-Time "03:00";
|
||||
> ```
|
||||
> 🔧 **配置文件优化** (`/etc/apt/apt.conf.d/50unattended-upgrades`):
|
||||
> ```json
|
||||
> Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot-Time "03:00";
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
@@ -189,7 +189,7 @@ sudo ss -tunlp
|
||||
|
||||
---
|
||||
|
||||
**最终安全检查清单**:
|
||||
**最终安全检查清单**:
|
||||
```bash
|
||||
echo "[+] SSH 配置"
|
||||
sudo sshd -t && grep -E "PermitRoot|PasswordAuth" /etc/ssh/sshd_config
|
||||
@@ -200,14 +200,14 @@ sudo ufw status
|
||||
echo "[]+ 更新状态"
|
||||
sudo unattended-upgrades --dry-run
|
||||
```
|
||||

|
||||
*(安全层示意图:网络防火墙→系统加固→应用防护)*
|
||||

|
||||
*(安全层示意图:网络防火墙→系统加固→应用防护)*
|
||||
|
||||
> 🔐 **维护建议**:
|
||||
> 1. 每月执行:`sudo lynis audit system` (安装:`apt install lynis`)
|
||||
> 2. 每季度更新所有SSL证书,即使未到期
|
||||
> 3. 使用自动配置管理工具(如Ansible)维护服务器状态
|
||||
> 🔐 **维护建议**:
|
||||
> 1. 每月执行:`sudo lynis audit system` (安装:`apt install lynis`)
|
||||
> 2. 每季度更新所有SSL证书,即使未到期
|
||||
> 3. 使用自动配置管理工具(如Ansible)维护服务器状态
|
||||
|
||||
**文档版本**: v1.2
|
||||
**测试环境**: Debian 12.5 (Kernel 6.1.x) 云服务器
|
||||
**最后更新**: 2024-06-15
|
||||
**文档版本**: v1.2
|
||||
**测试环境**: Debian 12.5 (Kernel 6.1.x) 云服务器
|
||||
**最后更新**: 2024-06-15
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: 安装 VS Code到浏览器 -- Code Server
|
||||
description: 在浏览器中安装 VS Code
|
||||
date: 2025-05-23
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250616220449750.png
|
||||
img: https://img.jiwei.xin/20250616220449750.png
|
||||
navigation:
|
||||
icon: simple-icons:visualstudiocode
|
||||
---
|
||||
@@ -34,9 +34,9 @@ PermitRootLogin no
|
||||
PasswordAuthentication no # 强制使用密钥登录
|
||||
```
|
||||
|
||||
> ⚠️ **关键提示**:
|
||||
> 1. 操作前必须配置好SSH密钥,否则会锁定服务器
|
||||
> 2. 执行后重载服务:`sudo systemctl reload ssh`
|
||||
> ⚠️ **关键提示**:
|
||||
> 1. 操作前必须配置好SSH密钥,否则会锁定服务器
|
||||
> 2. 执行后重载服务:`sudo systemctl reload ssh`
|
||||
|
||||
---
|
||||
|
||||
@@ -90,12 +90,12 @@ sudo dpkg-reconfigure -plow unattended-upgrades
|
||||
sudo systemctl status unattended-upgrades
|
||||
```
|
||||
|
||||
> 🔧 **配置文件优化** (`/etc/apt/apt.conf.d/50unattended-upgrades`):
|
||||
> ```json
|
||||
> Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot-Time "03:00";
|
||||
> ```
|
||||
> 🔧 **配置文件优化** (`/etc/apt/apt.conf.d/50unattended-upgrades`):
|
||||
> ```json
|
||||
> Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot-Time "03:00";
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
@@ -189,7 +189,7 @@ sudo ss -tunlp
|
||||
|
||||
---
|
||||
|
||||
**最终安全检查清单**:
|
||||
**最终安全检查清单**:
|
||||
```bash
|
||||
echo "[+] SSH 配置"
|
||||
sudo sshd -t && grep -E "PermitRoot|PasswordAuth" /etc/ssh/sshd_config
|
||||
@@ -200,14 +200,14 @@ sudo ufw status
|
||||
echo "[]+ 更新状态"
|
||||
sudo unattended-upgrades --dry-run
|
||||
```
|
||||

|
||||
*(安全层示意图:网络防火墙→系统加固→应用防护)*
|
||||

|
||||
*(安全层示意图:网络防火墙→系统加固→应用防护)*
|
||||
|
||||
> 🔐 **维护建议**:
|
||||
> 1. 每月执行:`sudo lynis audit system` (安装:`apt install lynis`)
|
||||
> 2. 每季度更新所有SSL证书,即使未到期
|
||||
> 3. 使用自动配置管理工具(如Ansible)维护服务器状态
|
||||
> 🔐 **维护建议**:
|
||||
> 1. 每月执行:`sudo lynis audit system` (安装:`apt install lynis`)
|
||||
> 2. 每季度更新所有SSL证书,即使未到期
|
||||
> 3. 使用自动配置管理工具(如Ansible)维护服务器状态
|
||||
|
||||
**文档版本**: v1.2
|
||||
**测试环境**: Debian 12.5 (Kernel 6.1.x) 云服务器
|
||||
**最后更新**: 2024-06-15
|
||||
**文档版本**: v1.2
|
||||
**测试环境**: Debian 12.5 (Kernel 6.1.x) 云服务器
|
||||
**最后更新**: 2024-06-15
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: Debian 12 云服务器初始安全设置与优化指南
|
||||
description: Debian 12 云服务器初始安全设置与优化指南
|
||||
date: 2025-05-15
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250615184027376.png
|
||||
img: https://img.jiwei.xin/20250615184027376.png
|
||||
navigation:
|
||||
icon: lucide-server
|
||||
---
|
||||
@@ -34,9 +34,9 @@ PermitRootLogin no
|
||||
PasswordAuthentication no # 强制使用密钥登录
|
||||
```
|
||||
|
||||
> ⚠️ **关键提示**:
|
||||
> 1. 操作前必须配置好SSH密钥,否则会锁定服务器
|
||||
> 2. 执行后重载服务:`sudo systemctl reload ssh`
|
||||
> ⚠️ **关键提示**:
|
||||
> 1. 操作前必须配置好SSH密钥,否则会锁定服务器
|
||||
> 2. 执行后重载服务:`sudo systemctl reload ssh`
|
||||
|
||||
---
|
||||
|
||||
@@ -90,12 +90,12 @@ sudo dpkg-reconfigure -plow unattended-upgrades
|
||||
sudo systemctl status unattended-upgrades
|
||||
```
|
||||
|
||||
> 🔧 **配置文件优化** (`/etc/apt/apt.conf.d/50unattended-upgrades`):
|
||||
> ```json
|
||||
> Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot-Time "03:00";
|
||||
> ```
|
||||
> 🔧 **配置文件优化** (`/etc/apt/apt.conf.d/50unattended-upgrades`):
|
||||
> ```json
|
||||
> Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot "true";
|
||||
> Unattended-Upgrade::Automatic-Reboot-Time "03:00";
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
@@ -189,7 +189,7 @@ sudo ss -tunlp
|
||||
|
||||
---
|
||||
|
||||
**最终安全检查清单**:
|
||||
**最终安全检查清单**:
|
||||
```bash
|
||||
echo "[+] SSH 配置"
|
||||
sudo sshd -t && grep -E "PermitRoot|PasswordAuth" /etc/ssh/sshd_config
|
||||
@@ -200,14 +200,14 @@ sudo ufw status
|
||||
echo "[]+ 更新状态"
|
||||
sudo unattended-upgrades --dry-run
|
||||
```
|
||||

|
||||
*(安全层示意图:网络防火墙→系统加固→应用防护)*
|
||||

|
||||
*(安全层示意图:网络防火墙→系统加固→应用防护)*
|
||||
|
||||
> 🔐 **维护建议**:
|
||||
> 1. 每月执行:`sudo lynis audit system` (安装:`apt install lynis`)
|
||||
> 2. 每季度更新所有SSL证书,即使未到期
|
||||
> 3. 使用自动配置管理工具(如Ansible)维护服务器状态
|
||||
> 🔐 **维护建议**:
|
||||
> 1. 每月执行:`sudo lynis audit system` (安装:`apt install lynis`)
|
||||
> 2. 每季度更新所有SSL证书,即使未到期
|
||||
> 3. 使用自动配置管理工具(如Ansible)维护服务器状态
|
||||
|
||||
**文档版本**: v1.2
|
||||
**测试环境**: Debian 12.5 (Kernel 6.1.x) 云服务器
|
||||
**最后更新**: 2024-06-15
|
||||
**文档版本**: v1.2
|
||||
**测试环境**: Debian 12.5 (Kernel 6.1.x) 云服务器
|
||||
**最后更新**: 2024-06-15
|
||||
|
@@ -2,7 +2,7 @@
|
||||
title: Iperf3 万兆网络打流测试指南
|
||||
description: Iperf3 万兆网络打流测试指南
|
||||
date: 2025-05-13
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250615184111247.png
|
||||
img: https://img.jiwei.xin/20250615184111247.png
|
||||
navigation:
|
||||
icon: lucide-network
|
||||
---
|
||||
@@ -23,13 +23,13 @@ B -- 万兆光口 --> C[PVE服务器<br>CX4121A网卡]
|
||||
```
|
||||
|
||||
### 硬件清单
|
||||
| **设备** | **规格** | **作用** |
|
||||
|---------------------|-----------------------------------------|-----------------------|
|
||||
| 主测试端 | MacBook Air M4 + 雷电万兆网卡(AQC113) | Iperf3 Client端 |
|
||||
| 交换机 | 水星S106E Pro | 万兆光/电转换枢纽 |
|
||||
| 光转电模块 | AQR113光转电模块 | 连接交换机万兆光口 |
|
||||
| 被测服务端 | NAS (12500T+Q670) + CX4121A万兆网卡 | Iperf3 Server端 |
|
||||
| 辅助设备 | 超六类网线/光纤跳线(按需) | 物理链路连接 |
|
||||
| **设备** | **规格** | **作用** |
|
||||
|---------------------|-----------------------------------------|-----------------------|
|
||||
| 主测试端 | MacBook Air M4 + 雷电万兆网卡(AQC113) | Iperf3 Client端 |
|
||||
| 交换机 | 水星S106E Pro | 万兆光/电转换枢纽 |
|
||||
| 光转电模块 | AQR113光转电模块 | 连接交换机万兆光口 |
|
||||
| 被测服务端 | NAS (12500T+Q670) + CX4121A万兆网卡 | Iperf3 Server端 |
|
||||
| 辅助设备 | 超六类网线/光纤跳线(按需) | 物理链路连接 |
|
||||
```
|
||||
|
||||
---
|
||||
@@ -38,19 +38,19 @@ B -- 万兆光口 --> C[PVE服务器<br>CX4121A网卡]
|
||||
### 1. macOS端安装(Client)
|
||||
```bash
|
||||
# 通过Homebrew安装iperf3
|
||||
brew install iperf3
|
||||
brew install iperf3
|
||||
|
||||
# 启动服务端监听(可选本地验证)
|
||||
iperf3 -s -p 5201
|
||||
iperf3 -s -p 5201
|
||||
```
|
||||
|
||||
### 2. PVE服务端安装(Server)
|
||||
```bash
|
||||
# Debian系系统安装
|
||||
apt update && apt install iperf3 -y
|
||||
apt update && apt install iperf3 -y
|
||||
|
||||
# 启动服务端后台监听
|
||||
iperf3 -s -D -p 5201
|
||||
iperf3 -s -D -p 5201
|
||||
```
|
||||
> ⚠️ **防火墙注意**: 确保5201端口开放
|
||||
> `ufw allow 5201/tcp` (Debian/Ubuntu)
|
||||
@@ -124,10 +124,10 @@ ethtool -S enpXX | grep -E 'drop|error'
|
||||
2. 减少并发线程总数(-P参数)
|
||||
```
|
||||
|
||||

|
||||
*(示意图:实际连接需确保光模块端口匹配)*
|
||||

|
||||
*(示意图:实际连接需确保光模块端口匹配)*
|
||||
|
||||
> 📌 **经验总结**:水星S106E Pro需关闭"绿色节能"功能,避免AQC113因节能策略降速。PVE虚拟机环境建议使用SR-IOV直通网卡,减少虚拟化层开销。
|
||||
> 📌 **经验总结**:水星S106E Pro需关闭"绿色节能"功能,避免AQC113因节能策略降速。PVE虚拟机环境建议使用SR-IOV直通网卡,减少虚拟化层开销。
|
||||
|
||||
---
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
||||
title: AI
|
||||
description: AI 改变世界
|
||||
icon: lucide-brain
|
||||
icon: devicon:streamlit
|
||||
|
10
content/blog/2.AI/01.AI.md
Normal file
10
content/blog/2.AI/01.AI.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: AI最佳实践
|
||||
description: AI最佳实践
|
||||
date: 2025-08-13
|
||||
img: https://img.jiwei.xin/20250628122847084.png
|
||||
navigation:
|
||||
icon: simple-icons:chai
|
||||
---
|
||||
|
||||
### AI 文章等待从旧Blog中搬迁
|
@@ -1,190 +0,0 @@
|
||||
---
|
||||
title: Coolify
|
||||
description: Coolify是什么?
|
||||
date: 2025-07-14
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250628122847084.png
|
||||
navigation:
|
||||
icon: simple-icons:chai
|
||||
---
|
||||
# 概述
|
||||
这是一份提供给AI大模型的Python代码规范与编程标准,可以有效提高Cursor等大模型对Python项目的编写能力。
|
||||
|
||||
---
|
||||
description: Python开发综合指南,涵盖代码组织、性能、安全性、测试等内容。这些规则旨在促进可维护、高效且安全的Python代码库。
|
||||
globs: *.py
|
||||
---
|
||||
# Python最佳实践与编码规范
|
||||
|
||||
本文档概述了Python开发的综合最佳实践和编码标准,旨在促进编写干净、高效、可维护和安全的代码。
|
||||
|
||||
## 1. 代码组织与结构
|
||||
|
||||
### 1.1 目录结构最佳实践
|
||||
|
||||
* **扁平结构优于嵌套(但不绝对)。** 从简单结构开始,按需重构
|
||||
* **包与模块:** 使用包(包含`__init__.py`的目录)对模块进行逻辑分组
|
||||
* **src布局:** 考虑使用`src`目录分离应用代码和项目级文件(setup.py、requirements.txt等),避免导入冲突并明确项目边界
|
||||
* **典型项目结构:**
|
||||
|
||||
project_name/
|
||||
├── src/
|
||||
│ ├── package_name/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── module1.py
|
||||
│ │ ├── module2.py
|
||||
│ ├── main.py # 入口文件
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── test_module1.py
|
||||
│ ├── test_module2.py
|
||||
├── docs/
|
||||
│ ├── conf.py
|
||||
│ ├── index.rst
|
||||
├── .gitignore
|
||||
├── pyproject.toml 或 setup.py
|
||||
├── README.md
|
||||
├── requirements.txt 或 requirements-dev.txt
|
||||
|
||||
### 1.2 文件命名规范
|
||||
|
||||
* **模块:** 小写字母,使用下划线增强可读性(如`my_module.py`)
|
||||
* **包:** 全小写(如`my_package`),非必要不使用下划线
|
||||
* **测试文件:** 以`test_`开头(如`test_my_module.py`)
|
||||
|
||||
### 1.3 模块组织最佳实践
|
||||
|
||||
* **单一职责原则:** 每个模块应有明确定义的用途
|
||||
* **导入规范:**
|
||||
* 顺序:标准库→第三方库→本地模块
|
||||
* 优先使用绝对导入(如`from my_package.module1 import function1`)
|
||||
* 在复杂包结构中需明确相对导入时使用显式相对导入(`from . import sibling_module`)
|
||||
* **常量:** 使用全大写定义模块级常量(如`MAX_ITERATIONS = 100`)
|
||||
* **双下划线名称:** `__all__`、`__version__`等应放在模块文档字符串之后、所有导入之前(`from __future__`除外)。使用`__all__`显式声明公共API
|
||||
|
||||
### 1.4 组件架构建议
|
||||
|
||||
* **分层架构:** 适用于大型应用,将关注点分离为表现层、业务逻辑层和数据访问层
|
||||
* **微服务:** 超大型系统可拆分为小型独立服务
|
||||
* **六边形/整洁架构:** 强调业务逻辑与数据库/框架等外部依赖的解耦
|
||||
* **依赖注入:** 提高可测试性并降低耦合度
|
||||
|
||||
### 1.5 代码分割策略
|
||||
|
||||
* **按功能拆分:** 基于不同功能划分模块(如用户管理、数据处理)
|
||||
* **按层级拆分:** 分离表现层、业务逻辑层和数据访问代码
|
||||
* **懒加载:** 使用`importlib.import_module()`实现按需加载,优化启动时间
|
||||
* **条件导入:** 根据特定条件导入模块
|
||||
|
||||
## 2. 常见模式与反模式
|
||||
|
||||
### 2.1 设计模式
|
||||
|
||||
* **单例模式:** 限制类只能实例化一个对象
|
||||
* **工厂模式:** 创建对象时无需指定具体类
|
||||
* **观察者模式:** 建立对象间一对多依赖关系
|
||||
* **策略模式:** 定义算法族并使其可互换
|
||||
* **装饰器模式:** 动态扩展对象功能
|
||||
* **上下文管理器:** 确保资源正确清理(如自动关闭文件)
|
||||
|
||||
### 2.2 常见任务的推荐方案
|
||||
|
||||
* **数据验证:** 使用`pydantic`或`marshmallow`等库
|
||||
* **配置管理:** 使用`python-decouple`、`dynaconf`或标准库的`configparser`
|
||||
* **日志记录:** 使用`logging`模块实现结构化日志
|
||||
* **命令行接口:** 使用`argparse`、`click`或`typer`
|
||||
* **异步编程:** 使用`asyncio`处理I/O密集型任务
|
||||
|
||||
### 2.3 反模式与代码异味
|
||||
|
||||
* **上帝类:** 承担过多职责的类,应拆分为专注单一功能的小类
|
||||
* **霰弹式变更:** 需在多处做小修改,表明内聚性不足
|
||||
* **面条代码:** 结构混乱难以追踪,应重构为定义明确的函数/类
|
||||
* **重复代码:** 提取公共代码为可复用函数/类(遵循DRY原则)
|
||||
* **魔法数值/字符串:** 使用命名常量替代硬编码值
|
||||
* **过早优化:** 避免在没有性能瓶颈分析前提下的优化
|
||||
|
||||
### 2.4 状态管理最佳实践
|
||||
|
||||
* **无状态函数:** 尽可能使用无状态函数
|
||||
* **不可变数据:** 使用不可变数据结构防止意外修改
|
||||
* **显式状态:** 使用类或数据结构明确管理状态,避免全局变量
|
||||
* **上下文变量:** Python 3.7+可使用`contextvars`管理异步应用中的请求级状态
|
||||
|
||||
### 2.5 错误处理模式
|
||||
|
||||
* **捕获特定异常:** 避免笼统捕获`Exception`或`BaseException`
|
||||
* **资源清理:** 使用`finally`确保清理代码必执行
|
||||
* **异常日志:** 记录完整堆栈信息
|
||||
* **异常消息:** 抛出包含明确错误信息的异常
|
||||
* **避免异常控制流:** 异常应用于处理意外情况而非常规流程
|
||||
|
||||
## 3. 性能优化
|
||||
|
||||
### 3.1 优化技术
|
||||
|
||||
* **性能分析:** 使用`cProfile`定位瓶颈
|
||||
* **高效数据结构:** 根据场景选择(如`set`用于成员测试、`dict`用于查找)
|
||||
* **列表推导式与生成器:** 编写简洁高效的代码
|
||||
* **NumPy向量化:** 对数值计算使用向量化操作
|
||||
* **即时编译:** 性能关键代码考虑使用Numba等JIT编译器
|
||||
* **字符串拼接:** 使用`''.join(iterable)`高效拼接字符串
|
||||
|
||||
### 3.2 内存管理
|
||||
|
||||
* **内存分析:** 使用`memory_profiler`定位内存泄漏
|
||||
* **`__slots__`:** 减少类实例的内存占用
|
||||
* **生成器:** 处理大数据集时避免全部加载到内存
|
||||
|
||||
## 4. 安全性最佳实践
|
||||
|
||||
### 4.1 常见漏洞防范
|
||||
|
||||
* **SQL注入:** 使用参数化查询或ORM
|
||||
* **XSS攻击:** 对用户输入消毒并转义输出
|
||||
* **CSRF防护:** 使用CSRF令牌
|
||||
* **依赖漏洞:** 定期审计和更新依赖项
|
||||
* **硬编码密钥:** 禁止在代码中硬编码密码/API密钥,使用环境变量管理
|
||||
|
||||
### 4.2 API安全通信
|
||||
|
||||
* **强制HTTPS:** 所有API通信必须加密
|
||||
* **速率限制:** 防止接口滥用
|
||||
* **输入验证:** 处理前验证所有API请求
|
||||
|
||||
## 5. 测试策略
|
||||
|
||||
### 5.1 单元测试要点
|
||||
|
||||
* **测试粒度:** 隔离测试单个函数/类/模块
|
||||
* **边界条件:** 特别测试边界情况和异常场景
|
||||
* **测试覆盖率:** 追求高覆盖率但避免教条化
|
||||
|
||||
### 5.2 集成测试建议
|
||||
|
||||
* **聚焦关键流程:** 关注核心用户场景
|
||||
* **模拟外部服务:** 使用mock替代真实外部依赖
|
||||
|
||||
## 6. 常见陷阱
|
||||
|
||||
### 6.1 高频错误
|
||||
|
||||
* **可变默认参数:** 函数定义中避免使用可变对象作为默认值
|
||||
* **变量作用域:** 注意嵌套函数中的变量作用域
|
||||
* **忽略异常:** 禁止直接忽略未处理的异常
|
||||
* **虚拟环境:** 必须使用虚拟环境管理项目依赖
|
||||
|
||||
## 7. 工具与环境
|
||||
|
||||
### 7.1 推荐工具链
|
||||
|
||||
* **IDE:** PyCharm、VS Code(搭配Python插件)
|
||||
* **包管理:** `pip`、`poetry`
|
||||
* **格式化:** `black`、`autopep8`
|
||||
* **静态检查:** `mypy`、`pylint`
|
||||
|
||||
### 7.2 CI/CD集成
|
||||
|
||||
* **自动化测试:** 每次提交自动运行测试套件
|
||||
* **代码质量门禁:** 集成静态分析工具到流水线
|
||||
|
||||
遵循这些规范和最佳实践,开发者能够构建出更健壮、可维护且安全的Python应用。
|
@@ -1,3 +1,3 @@
|
||||
title: 生活
|
||||
description: 生活中一些有趣的事情
|
||||
icon: lucide-heart
|
||||
icon: devicon:love2d
|
||||
|
@@ -1,190 +0,0 @@
|
||||
---
|
||||
title: Coolify
|
||||
description: Coolify是什么?
|
||||
date: 2025-07-14
|
||||
img: https://lijue-me.oss-cn-chengdu.aliyuncs.com/20250628122847084.png
|
||||
navigation:
|
||||
icon: simple-icons:chai
|
||||
---
|
||||
# 概述
|
||||
这是一份提供给AI大模型的Python代码规范与编程标准,可以有效提高Cursor等大模型对Python项目的编写能力。
|
||||
|
||||
---
|
||||
description: Python开发综合指南,涵盖代码组织、性能、安全性、测试等内容。这些规则旨在促进可维护、高效且安全的Python代码库。
|
||||
globs: *.py
|
||||
---
|
||||
# Python最佳实践与编码规范
|
||||
|
||||
本文档概述了Python开发的综合最佳实践和编码标准,旨在促进编写干净、高效、可维护和安全的代码。
|
||||
|
||||
## 1. 代码组织与结构
|
||||
|
||||
### 1.1 目录结构最佳实践
|
||||
|
||||
* **扁平结构优于嵌套(但不绝对)。** 从简单结构开始,按需重构
|
||||
* **包与模块:** 使用包(包含`__init__.py`的目录)对模块进行逻辑分组
|
||||
* **src布局:** 考虑使用`src`目录分离应用代码和项目级文件(setup.py、requirements.txt等),避免导入冲突并明确项目边界
|
||||
* **典型项目结构:**
|
||||
|
||||
project_name/
|
||||
├── src/
|
||||
│ ├── package_name/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── module1.py
|
||||
│ │ ├── module2.py
|
||||
│ ├── main.py # 入口文件
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── test_module1.py
|
||||
│ ├── test_module2.py
|
||||
├── docs/
|
||||
│ ├── conf.py
|
||||
│ ├── index.rst
|
||||
├── .gitignore
|
||||
├── pyproject.toml 或 setup.py
|
||||
├── README.md
|
||||
├── requirements.txt 或 requirements-dev.txt
|
||||
|
||||
### 1.2 文件命名规范
|
||||
|
||||
* **模块:** 小写字母,使用下划线增强可读性(如`my_module.py`)
|
||||
* **包:** 全小写(如`my_package`),非必要不使用下划线
|
||||
* **测试文件:** 以`test_`开头(如`test_my_module.py`)
|
||||
|
||||
### 1.3 模块组织最佳实践
|
||||
|
||||
* **单一职责原则:** 每个模块应有明确定义的用途
|
||||
* **导入规范:**
|
||||
* 顺序:标准库→第三方库→本地模块
|
||||
* 优先使用绝对导入(如`from my_package.module1 import function1`)
|
||||
* 在复杂包结构中需明确相对导入时使用显式相对导入(`from . import sibling_module`)
|
||||
* **常量:** 使用全大写定义模块级常量(如`MAX_ITERATIONS = 100`)
|
||||
* **双下划线名称:** `__all__`、`__version__`等应放在模块文档字符串之后、所有导入之前(`from __future__`除外)。使用`__all__`显式声明公共API
|
||||
|
||||
### 1.4 组件架构建议
|
||||
|
||||
* **分层架构:** 适用于大型应用,将关注点分离为表现层、业务逻辑层和数据访问层
|
||||
* **微服务:** 超大型系统可拆分为小型独立服务
|
||||
* **六边形/整洁架构:** 强调业务逻辑与数据库/框架等外部依赖的解耦
|
||||
* **依赖注入:** 提高可测试性并降低耦合度
|
||||
|
||||
### 1.5 代码分割策略
|
||||
|
||||
* **按功能拆分:** 基于不同功能划分模块(如用户管理、数据处理)
|
||||
* **按层级拆分:** 分离表现层、业务逻辑层和数据访问代码
|
||||
* **懒加载:** 使用`importlib.import_module()`实现按需加载,优化启动时间
|
||||
* **条件导入:** 根据特定条件导入模块
|
||||
|
||||
## 2. 常见模式与反模式
|
||||
|
||||
### 2.1 设计模式
|
||||
|
||||
* **单例模式:** 限制类只能实例化一个对象
|
||||
* **工厂模式:** 创建对象时无需指定具体类
|
||||
* **观察者模式:** 建立对象间一对多依赖关系
|
||||
* **策略模式:** 定义算法族并使其可互换
|
||||
* **装饰器模式:** 动态扩展对象功能
|
||||
* **上下文管理器:** 确保资源正确清理(如自动关闭文件)
|
||||
|
||||
### 2.2 常见任务的推荐方案
|
||||
|
||||
* **数据验证:** 使用`pydantic`或`marshmallow`等库
|
||||
* **配置管理:** 使用`python-decouple`、`dynaconf`或标准库的`configparser`
|
||||
* **日志记录:** 使用`logging`模块实现结构化日志
|
||||
* **命令行接口:** 使用`argparse`、`click`或`typer`
|
||||
* **异步编程:** 使用`asyncio`处理I/O密集型任务
|
||||
|
||||
### 2.3 反模式与代码异味
|
||||
|
||||
* **上帝类:** 承担过多职责的类,应拆分为专注单一功能的小类
|
||||
* **霰弹式变更:** 需在多处做小修改,表明内聚性不足
|
||||
* **面条代码:** 结构混乱难以追踪,应重构为定义明确的函数/类
|
||||
* **重复代码:** 提取公共代码为可复用函数/类(遵循DRY原则)
|
||||
* **魔法数值/字符串:** 使用命名常量替代硬编码值
|
||||
* **过早优化:** 避免在没有性能瓶颈分析前提下的优化
|
||||
|
||||
### 2.4 状态管理最佳实践
|
||||
|
||||
* **无状态函数:** 尽可能使用无状态函数
|
||||
* **不可变数据:** 使用不可变数据结构防止意外修改
|
||||
* **显式状态:** 使用类或数据结构明确管理状态,避免全局变量
|
||||
* **上下文变量:** Python 3.7+可使用`contextvars`管理异步应用中的请求级状态
|
||||
|
||||
### 2.5 错误处理模式
|
||||
|
||||
* **捕获特定异常:** 避免笼统捕获`Exception`或`BaseException`
|
||||
* **资源清理:** 使用`finally`确保清理代码必执行
|
||||
* **异常日志:** 记录完整堆栈信息
|
||||
* **异常消息:** 抛出包含明确错误信息的异常
|
||||
* **避免异常控制流:** 异常应用于处理意外情况而非常规流程
|
||||
|
||||
## 3. 性能优化
|
||||
|
||||
### 3.1 优化技术
|
||||
|
||||
* **性能分析:** 使用`cProfile`定位瓶颈
|
||||
* **高效数据结构:** 根据场景选择(如`set`用于成员测试、`dict`用于查找)
|
||||
* **列表推导式与生成器:** 编写简洁高效的代码
|
||||
* **NumPy向量化:** 对数值计算使用向量化操作
|
||||
* **即时编译:** 性能关键代码考虑使用Numba等JIT编译器
|
||||
* **字符串拼接:** 使用`''.join(iterable)`高效拼接字符串
|
||||
|
||||
### 3.2 内存管理
|
||||
|
||||
* **内存分析:** 使用`memory_profiler`定位内存泄漏
|
||||
* **`__slots__`:** 减少类实例的内存占用
|
||||
* **生成器:** 处理大数据集时避免全部加载到内存
|
||||
|
||||
## 4. 安全性最佳实践
|
||||
|
||||
### 4.1 常见漏洞防范
|
||||
|
||||
* **SQL注入:** 使用参数化查询或ORM
|
||||
* **XSS攻击:** 对用户输入消毒并转义输出
|
||||
* **CSRF防护:** 使用CSRF令牌
|
||||
* **依赖漏洞:** 定期审计和更新依赖项
|
||||
* **硬编码密钥:** 禁止在代码中硬编码密码/API密钥,使用环境变量管理
|
||||
|
||||
### 4.2 API安全通信
|
||||
|
||||
* **强制HTTPS:** 所有API通信必须加密
|
||||
* **速率限制:** 防止接口滥用
|
||||
* **输入验证:** 处理前验证所有API请求
|
||||
|
||||
## 5. 测试策略
|
||||
|
||||
### 5.1 单元测试要点
|
||||
|
||||
* **测试粒度:** 隔离测试单个函数/类/模块
|
||||
* **边界条件:** 特别测试边界情况和异常场景
|
||||
* **测试覆盖率:** 追求高覆盖率但避免教条化
|
||||
|
||||
### 5.2 集成测试建议
|
||||
|
||||
* **聚焦关键流程:** 关注核心用户场景
|
||||
* **模拟外部服务:** 使用mock替代真实外部依赖
|
||||
|
||||
## 6. 常见陷阱
|
||||
|
||||
### 6.1 高频错误
|
||||
|
||||
* **可变默认参数:** 函数定义中避免使用可变对象作为默认值
|
||||
* **变量作用域:** 注意嵌套函数中的变量作用域
|
||||
* **忽略异常:** 禁止直接忽略未处理的异常
|
||||
* **虚拟环境:** 必须使用虚拟环境管理项目依赖
|
||||
|
||||
## 7. 工具与环境
|
||||
|
||||
### 7.1 推荐工具链
|
||||
|
||||
* **IDE:** PyCharm、VS Code(搭配Python插件)
|
||||
* **包管理:** `pip`、`poetry`
|
||||
* **格式化:** `black`、`autopep8`
|
||||
* **静态检查:** `mypy`、`pylint`
|
||||
|
||||
### 7.2 CI/CD集成
|
||||
|
||||
* **自动化测试:** 每次提交自动运行测试套件
|
||||
* **代码质量门禁:** 集成静态分析工具到流水线
|
||||
|
||||
遵循这些规范和最佳实践,开发者能够构建出更健壮、可维护且安全的Python应用。
|
10
content/blog/3.生活/01.生活.md
Normal file
10
content/blog/3.生活/01.生活.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: 生活
|
||||
description: 生活
|
||||
date: 2025-08-13
|
||||
img: https://img.jiwei.xin/20250628122847084.png
|
||||
navigation:
|
||||
icon: simple-icons:chai
|
||||
---
|
||||
|
||||
### 生活 类等待从旧Blog迁移
|
@@ -5,7 +5,7 @@ navigation:
|
||||
icon: lucide-house
|
||||
---
|
||||
|
||||
## 缘起
|
||||
# 缘起
|
||||
|
||||
使用过市面上的很多文档系统,但是或多或少都有一些自己不满意的功能.
|
||||
于是自己动手,丰衣足食.
|
||||
|
@@ -28,7 +28,7 @@ authors:
|
||||
|
||||
:::tabs{variant="line"}
|
||||
::stack{label="badges"}
|
||||

|
||||

|
||||
```yml
|
||||
badges:
|
||||
- value: 0.8.10
|
||||
@@ -40,7 +40,7 @@ badges:
|
||||
::
|
||||
|
||||
::stack{label="authors"}
|
||||

|
||||

|
||||
```yml
|
||||
authors:
|
||||
- name: Tony Zhang
|
||||
@@ -52,7 +52,7 @@ authors:
|
||||
::
|
||||
|
||||
::stack{label="navBadges"}
|
||||

|
||||

|
||||
```yml
|
||||
navBadges:
|
||||
- value: New
|
||||
|
244
content/docs/1.简单文档/3.writing/5.test.md
Normal file
244
content/docs/1.简单文档/3.writing/5.test.md
Normal file
@@ -0,0 +1,244 @@
|
||||
---
|
||||
title: markdown 常用语法
|
||||
description: 展示 markdown 常用语法
|
||||
navigation:
|
||||
icon: simple-icons:markdown
|
||||
---
|
||||
|
||||
|
||||
|
||||
欢迎来到 Markdown 的奇妙世界!无论你是写作爱好者、开发者、博主,还是想要简单记录点什么的人,Markdown 都能成为你新的好伙伴。它不仅让写作变得简单明了,还能轻松地将内容转化为漂亮的网页格式。今天,我们将全面探讨 Markdown 的基础和进阶语法,让你在这个过程中充分享受写作的乐趣!
|
||||
|
||||
Markdown 是一种轻量级标记语言,用于格式化纯文本。它以简单、直观的语法而著称,可以快速地生成 HTML。Markdown 是写作与代码的完美结合,既简单又强大。
|
||||
|
||||
## Markdown 基础语法
|
||||
|
||||
### 1. 标题:让你的内容层次分明
|
||||
|
||||
用 `#` 号来创建标题。标题从 `#` 开始,`#` 的数量表示标题的级别。
|
||||
|
||||
|
||||
# 一级标题
|
||||
|
||||
## 二级标题
|
||||
|
||||
### 三级标题
|
||||
|
||||
#### 四级标题
|
||||
|
||||
##### 五级标题
|
||||
|
||||
###### 六级标题
|
||||
|
||||
####### 七级标题
|
||||
|
||||
以上代码将渲染出一组层次分明的标题,使你的内容井井有条。
|
||||
|
||||
### 2. 段落与换行:自然流畅
|
||||
|
||||
Markdown 中的段落就是一行接一行的文本。要创建新段落,只需在两行文本之间空一行。
|
||||
|
||||
### 3. 字体样式:强调你的文字
|
||||
|
||||
- **粗体**:用两个星号或下划线包裹文字,如 `**粗体**` 或 `__粗体__`。
|
||||
- _斜体_:用一个星号或下划线包裹文字,如 `*斜体*` 或 `_斜体_`。
|
||||
- ~~删除线~~:用两个波浪线包裹文字,如 `~~删除线~~`。
|
||||
|
||||
这些简单的标记可以让你的内容更有层次感和重点突出。
|
||||
|
||||
### 4. 列表:整洁有序
|
||||
|
||||
- **无序列表**:用 `-`、`*` 或 `+` 加空格开始一行。
|
||||
- **有序列表**:使用数字加点号(`1.`、`2.`)开始一行。
|
||||
|
||||
在列表中嵌套其他内容?只需缩进即可实现嵌套效果。
|
||||
|
||||
- 无序列表项 1
|
||||
1. 嵌套有序列表项 1
|
||||
2. 嵌套有序列表项 2
|
||||
- 无序列表项 2
|
||||
|
||||
1. 有序列表项 1
|
||||
2. 有序列表项 2
|
||||
|
||||
### 5. 链接与图片:丰富内容
|
||||
|
||||
- **链接**:用方括号和圆括号创建链接 `[显示文本](链接地址)`。
|
||||
- **图片**:和链接类似,只需在前面加上 `!`,如 ``。
|
||||
|
||||
[访问 Doocs](https://github.com/doocs)
|
||||
|
||||

|
||||
|
||||
轻松实现富媒体内容展示!
|
||||
|
||||
> 因微信公众号平台不支持除公众号内容以外的链接,故其他平台的链接,会呈现链接样式但无法点击跳转。
|
||||
|
||||
> 对于这些链接请注意明文书写,或点击左上角「格式->微信外链接转底部引用」开启引用,这样就可以在底部观察到链接指向。
|
||||
|
||||
另外,使用 `<,>` 语法可以创建横屏滑动幻灯片,支持微信公众号平台。建议使用相似尺寸的图片以获得最佳显示效果。
|
||||
|
||||
### 6. 引用:引用名言或引人深思的句子
|
||||
|
||||
使用 `>` 来创建引用,只需在文本前面加上它。多层引用?在前一层 `>` 后再加一个就行。
|
||||
|
||||
> 这是一个引用
|
||||
>
|
||||
> > 这是一个嵌套引用
|
||||
|
||||
这让你的引用更加富有层次感。
|
||||
|
||||
### 7. 代码块:展示你的代码
|
||||
|
||||
- **行内代码**:用反引号包裹,如 `code`。
|
||||
- **代码块**:用三个反引号包裹,并指定语言,如:
|
||||
|
||||
```js[test.vue]
|
||||
console.log("Hello, Doocs!");
|
||||
```
|
||||
|
||||
语法高亮让你的代码更易读。
|
||||
|
||||
### 8. 分割线:分割内容
|
||||
|
||||
用三个或更多的 `-`、`*` 或 `_` 来创建分割线。
|
||||
|
||||
---
|
||||
|
||||
为你的内容添加视觉分隔。
|
||||
|
||||
### 9. 表格:清晰展示数据
|
||||
|
||||
Markdown 支持简单的表格,用 `|` 和 `-` 分隔单元格和表头。
|
||||
|
||||
| 项目人员 | 邮箱 | 微信号 |
|
||||
| ------------------------------------------- | ---------------------- | ------------ |
|
||||
| [yanglbme](https://github.com/yanglbme) | contact@yanglibin.info | YLB0109 |
|
||||
| [YangFong](https://github.com/YangFong) | yangfong2022@gmail.com | yq2419731931 |
|
||||
| [thinkasany](https://github.com/thinkasany) | thinkasany@gmail.com | thinkasany |
|
||||
|
||||
这样的表格让数据展示更为清爽!
|
||||
|
||||
> 手动编写标记太麻烦?我们提供了便捷方式。左上方点击「编辑->插入表格」,即可快速实现表格渲染。
|
||||
|
||||
## Markdown 进阶技巧
|
||||
|
||||
### 1. LaTeX 公式:完美展示数学表达式
|
||||
|
||||
Markdown 允许嵌入 LaTeX 语法展示数学公式:
|
||||
|
||||
- **行内公式**:用 `$` 包裹公式,如 $E = mc^2$ 。
|
||||
- **块级公式**:用 `$$` 包裹公式,如:
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
d_{i, j} &\leftarrow d_{i, j} + 1 \\
|
||||
d_{i, y + 1} &\leftarrow d_{i, y + 1} - 1 \\
|
||||
d_{x + 1, j} &\leftarrow d_{x + 1, j} - 1 \\
|
||||
d_{x + 1, y + 1} &\leftarrow d_{x + 1, y + 1} + 1
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
|
||||
### 以下是使用mate字段包裹
|
||||
|
||||
```math[mate.md]
|
||||
\begin{aligned}
|
||||
d_{i, j} &\leftarrow d_{i, j} + 1 \\
|
||||
d_{i, y + 1} &\leftarrow d_{i, y + 1} - 1 \\
|
||||
d_{x + 1, j} &\leftarrow d_{x + 1, j} - 1 \\
|
||||
d_{x + 1, y + 1} &\leftarrow d_{x + 1, y + 1} + 1
|
||||
\end{aligned}
|
||||
```
|
||||
|
||||
1. 列表内块公式 1
|
||||
|
||||
$$
|
||||
\chi^2 = \sum \frac{(O - E)^2}{E}
|
||||
$$
|
||||
|
||||
2. 列表内块公式 2
|
||||
|
||||
$$
|
||||
\chi^2 = \sum \frac{(|O - E| - 0.5)^2}{E}
|
||||
$$
|
||||
|
||||
|
||||
|
||||
这是展示复杂数学表达的利器!
|
||||
|
||||
### 2. Mermaid 流程图:可视化流程
|
||||
|
||||
Mermaid 是强大的可视化工具,可以在 Markdown 中创建流程图、时序图等。
|
||||
|
||||
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
layout: dagre
|
||||
look: classic
|
||||
---
|
||||
|
||||
flowchart LR
|
||||
A[Start] --> B{Choose Path}
|
||||
B -->|Option 1| C[Path 1]
|
||||
B -->|Option 2| D[Path 2]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Local Coolify 实例] --> B[生产服务器]
|
||||
A --> D[编译服务器]
|
||||
B --> B1[生产环境]
|
||||
D --> B
|
||||
```
|
||||
|
||||
```mermaid
|
||||
graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
```
|
||||
|
||||
```mermaid
|
||||
pie
|
||||
title Key elements in Product X
|
||||
"Calcium" : 42.96
|
||||
"Potassium" : 50.05
|
||||
"Magnesium" : 10.01
|
||||
"Iron" : 5
|
||||
```
|
||||
|
||||
```mermaid
|
||||
pie
|
||||
title 为什么总是宅在家里?
|
||||
"喜欢宅" : 45
|
||||
"天气太热" : 70
|
||||
"穷" : 500
|
||||
"没人约" : 95
|
||||
```
|
||||
|
||||
这种方式不仅能直观展示流程,还能提升文档的专业性。
|
||||
|
||||
> 更多用法,参见:[Mermaid User Guide](https://mermaid.js.org/intro/getting-started.html)。
|
||||
|
||||
## 结语
|
||||
|
||||
Markdown 是一种简单、强大且易于掌握的标记语言,通过学习基础和进阶语法,你可以快速创作内容并有效传达信息。无论是技术文档、个人博客还是项目说明,Markdown 都是你的得力助手。希望这篇内容能够带你全面了解 Markdown 的潜力,让你的写作更加丰富多彩!
|
||||
|
||||
现在,拿起 Markdown 编辑器,开始创作吧!探索 Markdown 的世界,你会发现它远比想象中更精彩!
|
||||
|
||||
#### 推荐阅读
|
||||
|
||||
- [阿里又一个 20k+ stars 开源项目诞生,恭喜 fastjson!](https://mp.weixin.qq.com/s/RNKDCK2KoyeuMeEs6GUrow)
|
||||
- [刷掉 90% 候选人的互联网大厂海量数据面试题(附题解 + 方法总结)](https://mp.weixin.qq.com/s/rjGqxUvrEqJNlo09GrT1Dw)
|
||||
- [好用!期待已久的文本块功能究竟如何在 Java 13 中发挥作用?](https://mp.weixin.qq.com/s/kalGv5T8AZGxTnLHr2wDsA)
|
||||
- [2019 GitHub 开源贡献排行榜新鲜出炉!微软谷歌领头,阿里跻身前 12!](https://mp.weixin.qq.com/s/_q812aGD1b9QvZ2WFI0Qgw)
|
||||
|
||||
---
|
||||
|
||||
<center>
|
||||
<img src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/gh/doocs/md/images/1648303220922-7e14aefa-816e-44c1-8604-ade709ca1c69.png" style="width: 100px;">
|
||||
</center>
|
@@ -25,7 +25,7 @@ export default defineNuxtConfig({
|
||||
highlight: {
|
||||
langs:
|
||||
['js', 'ts', 'jsx', 'tsx', 'js', 'json', 'bash', 'python', 'html', 'css', 'sql',
|
||||
'yaml', 'md', 'sh', 'go', 'java', 'c', 'cpp', 'php', 'ruby', 'swift', 'html'],
|
||||
'yaml', 'md', 'sh', 'go', 'java', 'lua', 'c', 'cpp', 'php', 'ruby', 'swift', 'html'],
|
||||
theme: {
|
||||
// Default theme (same as single string)
|
||||
default: 'github-dark',
|
||||
@@ -34,7 +34,18 @@ export default defineNuxtConfig({
|
||||
// Theme used if `html.sepia`
|
||||
sepia: 'monokai'
|
||||
}
|
||||
},
|
||||
remarkPlugins: {
|
||||
'remark-mermaidjs': {
|
||||
},
|
||||
'remark-gfm': {},
|
||||
'remark-math': {}
|
||||
},
|
||||
rehypePlugins: {
|
||||
// 'rehype-katex': {},
|
||||
'rehype-mathjax': {}
|
||||
}
|
||||
|
||||
},
|
||||
pathMeta: {
|
||||
slugifyOptions: {
|
||||
@@ -44,6 +55,11 @@ export default defineNuxtConfig({
|
||||
}
|
||||
}
|
||||
},
|
||||
mdc: {
|
||||
highlight: {
|
||||
noApiRoute: false
|
||||
}
|
||||
},
|
||||
routeRules: {
|
||||
'/': { static: true },
|
||||
'/docs/**': { ssr: false },
|
||||
@@ -83,7 +99,7 @@ export default defineNuxtConfig({
|
||||
}
|
||||
},
|
||||
llms: {
|
||||
domain: 'https://lijue.me',
|
||||
domain: 'https://lijue.net',
|
||||
title: 'Estel Docs',
|
||||
description: 'Estel Docs 文档系统',
|
||||
sections: [
|
||||
|
11
package.json
11
package.json
@@ -7,7 +7,7 @@
|
||||
"author": "Estel",
|
||||
"license": "MIT",
|
||||
"version": "1.0.0",
|
||||
"homepage": "https://lijue.me",
|
||||
"homepage": "https://lijue.net",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/estel-li/estel-docs"
|
||||
@@ -41,9 +41,16 @@
|
||||
"minisearch": "^7.1.2",
|
||||
"nuxt": "^4.0.3",
|
||||
"nuxt-llms": "0.1.3",
|
||||
"nuxt-og-image": "^5.1.9"
|
||||
"nuxt-og-image": "^5.1.9",
|
||||
"playwright": "^1.54.2",
|
||||
"rehype-mathjax": "^7.1.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-math": "^6.0.0",
|
||||
"remark-mermaidjs": "^7.0.0",
|
||||
"remark-rehype": "^11.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/devicon": "^1.2.41",
|
||||
"@iconify-json/lucide": "^1.2.57",
|
||||
"@iconify-json/simple-icons": "^1.2.43",
|
||||
"@nuxt/eslint": "^1.5.2",
|
||||
|
1112
pnpm-lock.yaml
generated
1112
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -122,6 +122,19 @@ export default defineEventHandler(async (event): Promise<WxConfigPayload> => {
|
||||
throw createError({ statusCode: 500, statusMessage: 'Missing WECHAT_APP_ID or WECHAT_APP_SECRET in environment.' })
|
||||
}
|
||||
|
||||
// 限制可签名的 URL 域名:从环境变量中获取站点基准地址,仅允许该主域或其子域
|
||||
const siteBaseUrl = process.env.NUXT_PUBLIC_SITE_URL || process.env.SITE_BASE_URL || process.env.BASE_URL || 'lijue.net'
|
||||
if (!siteBaseUrl) {
|
||||
throw createError({ statusCode: 500, statusMessage: 'Missing site base URL in environment (NUXT_PUBLIC_SITE_URL/SITE_BASE_URL/BASE_URL).' })
|
||||
}
|
||||
|
||||
let allowedHostname = ''
|
||||
try {
|
||||
allowedHostname = new URL(siteBaseUrl).hostname
|
||||
} catch {
|
||||
throw createError({ statusCode: 500, statusMessage: 'Invalid site base URL in environment. Expecting an absolute URL.' })
|
||||
}
|
||||
|
||||
const query = getQuery(event)
|
||||
let pageUrlRaw = typeof query.url === 'string' ? query.url : ''
|
||||
// 若未传入 url,则使用当前请求完整地址(去除 hash),通常建议前端传入当前页面 URL
|
||||
@@ -140,6 +153,19 @@ export default defineEventHandler(async (event): Promise<WxConfigPayload> => {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Invalid url parameter. Expecting an absolute http/https URL.' })
|
||||
}
|
||||
|
||||
// 校验传入 URL 的域名是否在允许范围(同主域或其子域)
|
||||
let pageHostname = ''
|
||||
try {
|
||||
pageHostname = new URL(pageUrl).hostname
|
||||
} catch {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Invalid url parameter. Expecting an absolute http/https URL.' })
|
||||
}
|
||||
const isSameHost = pageHostname === allowedHostname
|
||||
const isSubdomain = pageHostname.endsWith('.' + allowedHostname)
|
||||
if (!isSameHost && !isSubdomain) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'The url hostname is not allowed.' })
|
||||
}
|
||||
|
||||
const accessToken = await fetchAccessToken(appId, appSecret)
|
||||
const jsapiTicket = await fetchJsapiTicket(accessToken)
|
||||
|
||||
|
@@ -1 +0,0 @@
|
||||
|
@@ -220,7 +220,7 @@ const { data: articles } = await useAsyncData(
|
||||
|
||||
- **项目仓库**: https://github.com/estel-li/estel-docs
|
||||
- **在线演示**: https://docs.jiwei.xin
|
||||
- **个人博客**: https://lijue.me
|
||||
- **个人博客**: https://lijue.net
|
||||
- **技术文档**: https://nuxt.com/docs
|
||||
|
||||
---
|
||||
|
Reference in New Issue
Block a user