继续提交
2
mengyanote-frontend/.env.development
Normal file
@@ -0,0 +1,2 @@
|
||||
# 开发环境配置 - 用于本地开发 (npm run dev)
|
||||
VITE_API_BASE=http://192.168.1.233:2424
|
||||
8
mengyanote-frontend/.env.production
Normal file
@@ -0,0 +1,8 @@
|
||||
# 生产环境配置 - 用于部署到服务器 (npm run build 或 npm run build:server)
|
||||
#
|
||||
# 配置说明:
|
||||
# 1. 前后端分离部署(不同域名/端口):填写后端完整URL,如 http://your-backend.com:2424
|
||||
# 2. 前后端同域名部署(Nginx代理):留空或填 "" 使用相对路径
|
||||
#
|
||||
# 当前配置:分离部署模式(修改为你的后端地址)
|
||||
VITE_API_BASE=https://note.api.shumengya.top
|
||||
@@ -1,31 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
echo ============== 开始执行构建 ==============
|
||||
:: 关键:添加 call 确保 npm 执行完后继续执行后续命令
|
||||
call npm run build
|
||||
|
||||
:: 检查构建是否成功
|
||||
if %errorlevel% neq 0 (
|
||||
echo 【错误】构建失败!请查看上面的错误信息
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ============== 构建成功,准备启动服务器 ==============
|
||||
echo 正在进入 dist 目录...
|
||||
cd dist || (
|
||||
echo 【错误】找不到 dist 目录!构建可能未生成该目录
|
||||
echo 当前目录:%cd% (请确认该目录下是否有 dist 文件夹)
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ============== 服务器启动中(端口 8080) ==============
|
||||
echo 提示:服务器启动后会一直运行,访问 http://localhost:8080 查看
|
||||
echo 若要停止服务器,请按 Ctrl + C,然后按 Y 确认
|
||||
python -m http.server 8080
|
||||
|
||||
:: 服务器停止后执行
|
||||
echo ============== 服务器已停止 ==============
|
||||
pause
|
||||
20
mengyanote-frontend/package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "markdown-to-web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"github-markdown-css": "^5.7.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
@@ -24,6 +25,7 @@
|
||||
"@types/react": "^19.1.13",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
"@vitejs/plugin-react": "^5.0.3",
|
||||
"baseline-browser-mapping": "^2.9.9",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
@@ -1576,9 +1578,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.8.tgz",
|
||||
"integrity": "sha512-be0PUaPsQX/gPWWgFsdD+GFzaoig5PXaUC1xLkQiYdDnANU8sMnHoQd8JhbJQuvTWrWLyeFN9Imb5Qtfvr4RrQ==",
|
||||
"version": "2.9.9",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.9.tgz",
|
||||
"integrity": "sha512-V8fbOCSeOFvlDj7LLChUcqbZrdKD9RU/VR260piF1790vT0mfLSwGc/Qzxv3IqiTukOpNtItePa0HBpMAj7MDg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -2444,6 +2446,18 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/github-markdown-css": {
|
||||
"version": "5.8.1",
|
||||
"resolved": "https://registry.npmjs.org/github-markdown-css/-/github-markdown-css-5.8.1.tgz",
|
||||
"integrity": "sha512-8G+PFvqigBQSWLQjyzgpa2ThD9bo7+kDsriUIidGcRhXgmcaAWUIpCZf8DavJgc+xifjbCG+GvMyWr0XMXmc7g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "npm run generate-data && vite",
|
||||
"build": "npm run generate-data && vite build",
|
||||
"generate-data": "node scripts/generateData.js",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build:local": "vite build --mode production.local",
|
||||
"build:server": "vite build --mode production",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"github-markdown-css": "^5.7.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
@@ -27,6 +29,7 @@
|
||||
"@types/react": "^19.1.13",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
"@vitejs/plugin-react": "^5.0.3",
|
||||
"baseline-browser-mapping": "^2.9.9",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
|
||||
BIN
mengyanote-frontend/public/LXGWWenKaiMono-Medium.ttf
Normal file
BIN
mengyanote-frontend/public/MapleMono-CN-ExtraBold.ttf
Normal file
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"totalFiles": 246,
|
||||
"totalFolders": 53,
|
||||
"generatedAt": "2025-11-29T11:17:51.031Z",
|
||||
"sourceDirectory": "E:\\React\\markdown-to-web\\public\\mengyanote"
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"promptDelete": false,
|
||||
"readableLineLength": false,
|
||||
"strictLineBreaks": false,
|
||||
"showLineNumber": false,
|
||||
"rightToLeft": false,
|
||||
"spellcheck": false
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"cssTheme": "GitHub Theme",
|
||||
"theme": "moonstone",
|
||||
"monospaceFontFamily": "Maple Mono NF CN",
|
||||
"baseFontSize": 18,
|
||||
"interfaceFontFamily": "Maple Mono NF CN",
|
||||
"textFontFamily": "霞鹜文楷,Maple Mono NF CN",
|
||||
"nativeMenus": false,
|
||||
"accentColor": "#fec834"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
[
|
||||
"obsidian-livesync",
|
||||
"obsidian-style-settings",
|
||||
"obsidian-hover-editor"
|
||||
]
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": false,
|
||||
"backlink": true,
|
||||
"canvas": false,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"properties": false,
|
||||
"page-preview": true,
|
||||
"daily-notes": false,
|
||||
"templates": false,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": false,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": false,
|
||||
"webviewer": false,
|
||||
"footnotes": false,
|
||||
"bases": false
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"collapse-filter": true,
|
||||
"search": "",
|
||||
"showTags": false,
|
||||
"showAttachments": false,
|
||||
"hideUnresolved": false,
|
||||
"showOrphans": true,
|
||||
"collapse-color-groups": true,
|
||||
"colorGroups": [],
|
||||
"collapse-display": true,
|
||||
"showArrow": false,
|
||||
"textFadeMultiplier": 0,
|
||||
"nodeSizeMultiplier": 1,
|
||||
"lineSizeMultiplier": 1,
|
||||
"collapse-forces": true,
|
||||
"centerStrength": 0.518713248970312,
|
||||
"repelStrength": 10,
|
||||
"linkStrength": 1,
|
||||
"linkDistance": 250,
|
||||
"scale": 1,
|
||||
"close": true
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"id": "obsidian-hover-editor",
|
||||
"name": "Hover Editor",
|
||||
"version": "0.11.26",
|
||||
"minAppVersion": "1.5.8",
|
||||
"description": "Transform the Page Preview hover popover into a fully working editor instance",
|
||||
"author": "NothingIsLost",
|
||||
"authorUrl": "https://github.com/nothingislost",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
@@ -1,582 +0,0 @@
|
||||
/* @settings
|
||||
|
||||
name: Hover Editor
|
||||
id: hover-editor
|
||||
settings:
|
||||
-
|
||||
id: titlebar-heading
|
||||
title: Title bar
|
||||
type: heading
|
||||
level: 1
|
||||
collapsed: true
|
||||
-
|
||||
id: titlebar-heading
|
||||
title: Title bar background/foreground
|
||||
type: heading
|
||||
level: 2
|
||||
collapsed: true
|
||||
-
|
||||
id: he-title-bar-active-bg
|
||||
title: Active unpinned title bar background color
|
||||
type: variable-themed-color
|
||||
format: hex
|
||||
default-light: '#'
|
||||
default-dark: '#'
|
||||
-
|
||||
id: he-title-bar-inactive-bg
|
||||
title: Inactive unpinned title bar background color
|
||||
type: variable-themed-color
|
||||
format: hex
|
||||
default-light: '#'
|
||||
default-dark: '#'
|
||||
-
|
||||
id: he-title-bar-active-pinned-bg
|
||||
title: Active pinned title bar background color
|
||||
type: variable-themed-color
|
||||
format: hex
|
||||
default-light: '#'
|
||||
default-dark: '#'
|
||||
-
|
||||
id: he-title-bar-inactive-pinned-bg
|
||||
title: Inactive pinned title bar background color
|
||||
type: variable-themed-color
|
||||
format: hex
|
||||
default-light: '#'
|
||||
default-dark: '#'
|
||||
-
|
||||
id: he-title-bar-active-fg
|
||||
title: Active title bar foreground color
|
||||
type: variable-themed-color
|
||||
format: hex
|
||||
default-light: '#'
|
||||
default-dark: '#'
|
||||
-
|
||||
id: he-title-bar-inactive-fg
|
||||
title: Inactive title bar foreground color
|
||||
type: variable-themed-color
|
||||
format: hex
|
||||
default-light: '#'
|
||||
default-dark: '#'
|
||||
-
|
||||
id: titlebar-action-heading
|
||||
title: Title bar icons
|
||||
type: heading
|
||||
level: 2
|
||||
collapsed: true
|
||||
-
|
||||
id: he-title-bar-inactive-action
|
||||
title: Title bar inactive icon color
|
||||
type: variable-themed-color
|
||||
format: hex
|
||||
default-light: '#'
|
||||
default-dark: '#'
|
||||
-
|
||||
id: he-title-bar-active-action
|
||||
title: Titlebar active icon color
|
||||
type: variable-themed-color
|
||||
format: hex
|
||||
default-light: '#'
|
||||
default-dark: '#'
|
||||
-
|
||||
id: titlebar-text-heading
|
||||
title: Title bar text
|
||||
type: heading
|
||||
level: 2
|
||||
collapsed: true
|
||||
-
|
||||
id: he-title-bar-font-size
|
||||
title: Title bar Font size
|
||||
type: variable-text
|
||||
description: Accepts any CSS font-size value
|
||||
default: 15px
|
||||
-
|
||||
id: titlebar-height-heading
|
||||
title: Title bar height
|
||||
type: heading
|
||||
level: 2
|
||||
collapsed: true
|
||||
-
|
||||
id: he-title-bar-height
|
||||
title: Title bar height
|
||||
type: variable-text
|
||||
description: Accepts any CSS font-size value
|
||||
default: 28px
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* general styling */
|
||||
--he-popover-opacity-while-dragging: 0.8;
|
||||
--he-popover-border-radius: 6px;
|
||||
--he-popover-header-transition-speed: 0.3s;
|
||||
--he-popover-snap-to-edge-transition-speed: 0.3s;
|
||||
/* resize handle sizing */
|
||||
--he-resize-handle-side-size: 12px;
|
||||
--he-resize-handle-corner-size: 18px;
|
||||
/* view header height */
|
||||
--he-view-header-height: 36px;
|
||||
}
|
||||
|
||||
body {
|
||||
--he-text-on-accent-inactive: var(--text-on-accent); /* couldn't find a good variable that worked across themes */
|
||||
--he-text-on-accent-active: #fff;
|
||||
/* z-index layer settings, probably not a good idea to mess with these */
|
||||
--he-popover-layer-inactive: calc(var(--layer-slides) - 4);
|
||||
--he-popover-layer-active: calc(var(--he-popover-layer-inactive) + 1);
|
||||
--he-popover-layer-new: calc(var(--he-popover-layer-inactive) + 2);
|
||||
--he-leaf-drag-overlay: calc(var(--he-popover-layer-inactive) + 3);
|
||||
/* calculated variables, do not modify */
|
||||
--he-resize-handle-side-offset: calc((var(--he-resize-handle-side-size) - 3px) * -1);
|
||||
--he-resize-handle-corner-offset: calc((var(--he-resize-handle-corner-size) / 2) * -1);
|
||||
--he-resize-handle-side-length: calc(100% - var(--he-resize-handle-corner-size));
|
||||
/* title bar colors */
|
||||
--he-title-bar-active-bg: var(--interactive-accent);
|
||||
--he-title-bar-inactive-bg: #777777;
|
||||
--he-title-bar-inactive-pinned-bg: #777777;
|
||||
--he-title-bar-active-pinned-bg: var(--interactive-accent);
|
||||
|
||||
--he-title-bar-active-fg: var(--he-text-on-accent-active);
|
||||
--he-title-bar-inactive-fg: var(--he-text-on-accent-inactive);
|
||||
/* title bar action/icon colors */
|
||||
--he-title-bar-inactive-action: var(--he-text-on-accent-inactive);
|
||||
--he-title-bar-active-action: var(--he-text-on-accent-active);
|
||||
/* titlebar sizing */
|
||||
--he-title-bar-height: 28px;
|
||||
--he-title-bar-font-size: 15px;
|
||||
}
|
||||
|
||||
.popover.hover-editor .workspace-leaf,
|
||||
.popover.hover-editor .workspace-split {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
Obsidian 1.6 sets a different background for non-root splits,
|
||||
then uses primary as an override at root. Since hover editors
|
||||
don't live in a root split, we have to copy the override:
|
||||
*/
|
||||
.popover.hover-editor .workspace-split .view-content {
|
||||
background-color: var(--background-primary);
|
||||
}
|
||||
|
||||
.popover.hover-editor {
|
||||
min-height: unset;
|
||||
max-height: unset;
|
||||
/* touch action none fixes dragging and resizing on mobile */
|
||||
touch-action: none;
|
||||
/* this is set to allow the drag/resize handles to overflow the popover frame */
|
||||
overflow: visible;
|
||||
border: none;
|
||||
padding: 0;
|
||||
z-index: var(--he-popover-layer-inactive);
|
||||
border-radius: var(--he-popover-border-radius);
|
||||
|
||||
/* Prevent snagging on titlebar */
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.popover.hover-editor .markdown-preview-view {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-active {
|
||||
z-index: var(--he-popover-layer-active);
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-new {
|
||||
z-index: var(--he-popover-layer-new);
|
||||
}
|
||||
|
||||
/* Drag/link overlay needs to overlay popups */
|
||||
.workspace-fake-target-overlay,
|
||||
.workspace-drop-overlay {
|
||||
z-index: var(--he-leaf-drag-overlay);
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle {
|
||||
position: absolute;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle.top {
|
||||
top: var(--he-resize-handle-side-offset);
|
||||
height: var(--he-resize-handle-side-size);
|
||||
left: calc(var(--he-resize-handle-corner-offset) * -1);
|
||||
width: var(--he-resize-handle-side-length);
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle.left {
|
||||
height: var(--he-resize-handle-side-length);
|
||||
left: var(--he-resize-handle-side-offset);
|
||||
top: calc(var(--he-resize-handle-corner-offset) * -1);
|
||||
width: var(--he-resize-handle-side-size);
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle.right {
|
||||
height: var(--he-resize-handle-side-length);
|
||||
right: var(--he-resize-handle-side-offset);
|
||||
top: calc(var(--he-resize-handle-corner-offset) * -1);
|
||||
width: var(--he-resize-handle-side-size);
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle.bottom {
|
||||
bottom: var(--he-resize-handle-side-offset);
|
||||
height: var(--he-resize-handle-side-size);
|
||||
left: calc(var(--he-resize-handle-corner-offset) * -1);
|
||||
width: var(--he-resize-handle-side-length);
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle.bottom-left {
|
||||
bottom: var(--he-resize-handle-corner-offset);
|
||||
height: var(--he-resize-handle-corner-size);
|
||||
left: var(--he-resize-handle-corner-offset);
|
||||
width: var(--he-resize-handle-corner-size);
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle.bottom-right {
|
||||
bottom: var(--he-resize-handle-corner-offset);
|
||||
height: var(--he-resize-handle-corner-size);
|
||||
right: var(--he-resize-handle-corner-offset);
|
||||
width: var(--he-resize-handle-corner-size);
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle.top-left {
|
||||
top: var(--he-resize-handle-corner-offset);
|
||||
height: var(--he-resize-handle-corner-size);
|
||||
left: var(--he-resize-handle-corner-offset);
|
||||
width: var(--he-resize-handle-corner-size);
|
||||
}
|
||||
|
||||
.popover.hover-editor .resize-handle.top-right {
|
||||
top: var(--he-resize-handle-corner-offset);
|
||||
height: var(--he-resize-handle-corner-size);
|
||||
right: var(--he-resize-handle-corner-offset);
|
||||
width: var(--he-resize-handle-corner-size);
|
||||
}
|
||||
|
||||
/* body.is-dragging-popover .tooltip {
|
||||
opacity: 0;
|
||||
} */
|
||||
|
||||
.popover-header-icon {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.mod-pin-popover > svg {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.mod-pin-popover.is-active > svg {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.popover-action,
|
||||
.popover-header-icon {
|
||||
margin: 0 8px;
|
||||
cursor: pointer;
|
||||
color: var(--he-title-bar-inactive-action);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.popover-action.is-active,
|
||||
.mod-pin-popover.is-active {
|
||||
color: var(--he-title-bar-active-action);
|
||||
}
|
||||
|
||||
.popover-action:hover,
|
||||
.popover-header-icon:hover {
|
||||
color: var(--he-title-bar-active-action);
|
||||
}
|
||||
|
||||
.popover-action.is-active svg,
|
||||
.mod-pin-popover.is-active svg {
|
||||
}
|
||||
|
||||
.mod-pin-popover.is-active > svg {
|
||||
transform: unset;
|
||||
}
|
||||
|
||||
.popover.hover-editor .workspace-leaf-content[data-type="empty"] .view-header {
|
||||
/* ensures that minimal theme doesn't hide the popover header */
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.popover.hover-editor .workspace-split > .workspace-leaf:last-child > .workspace-leaf-resize-handle {
|
||||
/* this hides the leaf resize handles that touch the edge of the popover */
|
||||
/* without this the leaf resize handles conflict with the popover resize handles */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-dragging {
|
||||
opacity: var(--he-popover-opacity-while-dragging);
|
||||
}
|
||||
|
||||
.popover.hover-editor:is(.snap-to-viewport, .snap-to-left, .snap-to-right) .resize-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popover.hover-editor.snap-to-right .resize-handle.left,
|
||||
.popover.hover-editor.snap-to-left .resize-handle.right {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-dragging.snap-to-left,
|
||||
.popover.hover-editor.is-dragging.snap-to-right,
|
||||
.popover.hover-editor.is-dragging.snap-to-viewport {
|
||||
transition: width var(--he-popover-snap-to-edge-transition-speed),
|
||||
height var(--he-popover-snap-to-edge-transition-speed), top var(--he-popover-snap-to-edge-transition-speed),
|
||||
left var(--he-popover-snap-to-edge-transition-speed);
|
||||
}
|
||||
|
||||
.hover-popover.is-dragging.snap-to-left::after,
|
||||
.hover-popover.is-dragging.snap-to-right::after,
|
||||
.hover-popover.is-dragging.snap-to-viewport::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
border-radius: var(--he-popover-border-radius);
|
||||
box-shadow: inset 0px 0px 0px 4px var(--interactive-accent);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.popover.hover-editor.snap-to-left {
|
||||
max-height: unset !important;
|
||||
}
|
||||
|
||||
.popover.hover-editor.snap-to-right {
|
||||
right: 0 !important;
|
||||
max-height: unset !important;
|
||||
}
|
||||
|
||||
.popover.hover-editor.snap-to-viewport {
|
||||
max-height: unset !important;
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
.popover.hover-editor .popover-titlebar {
|
||||
display: flex;
|
||||
height: var(--he-title-bar-height);
|
||||
width: 100%;
|
||||
background-color: var(--he-title-bar-inactive-bg);
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-active .popover-titlebar {
|
||||
background-color: var(--he-title-bar-active-bg);
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-pinned.is-pinned .popover-titlebar {
|
||||
background-color: var(--he-title-bar-inactive-pinned-bg);
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-pinned.is-pinned.is-active .popover-titlebar {
|
||||
background-color: var(--he-title-bar-active-pinned-bg);
|
||||
}
|
||||
|
||||
.popover.hover-editor .popover-titlebar .popover-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.popover.hover-editor > .popover-content {
|
||||
margin: 0;
|
||||
border-radius: var(--he-popover-border-radius);
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.popover.hover-popover.hover-editor .pdf-toolbar:not(.pdf-findbar.mod-hidden) {
|
||||
/* Show PDF toolbar in hover editor */
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.popover.hover-editor .popover-titlebar .popover-title {
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
transition: all 0.3s;
|
||||
align-self: center;
|
||||
font-size: var(--he-title-bar-font-size);
|
||||
font-weight: 500;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
color: var(--he-title-bar-inactive-fg);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-active .popover-title {
|
||||
color: var(--he-title-bar-active-fg);
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-active .popover-title:after {
|
||||
background: linear-gradient(to right, transparent, var(--he-title-bar-active-bg));
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-pinned.is-pinned.is-active .popover-title:after {
|
||||
background: linear-gradient(to right, transparent, var(--he-title-bar-active-pinned-bg));
|
||||
}
|
||||
|
||||
.popover.hover-editor.is-pinned.is-pinned .popover-title:after {
|
||||
background: linear-gradient(to right, transparent, var(--he-title-bar-inactive-pinned-bg));
|
||||
}
|
||||
|
||||
.popover.hover-editor .popover-title:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, transparent, var(--he-title-bar-inactive-bg));
|
||||
}
|
||||
|
||||
.popover.hover-editor .mod-show-navbar svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.popover.hover-editor > .popover-content > .workspace-split {
|
||||
height: calc(100% - var(--he-title-bar-height));
|
||||
}
|
||||
|
||||
.popover.hover-editor .view-header {
|
||||
border-top: none;
|
||||
transition: all var(--he-popover-header-transition-speed);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Restore 1.5.x view header icons */
|
||||
.view-header .view-header-icon {
|
||||
display: none;
|
||||
padding: var(--size-2-2);
|
||||
margin-right: var(--size-2-3);
|
||||
color: var(--text-muted);
|
||||
align-self: center;
|
||||
cursor: grab;
|
||||
}
|
||||
.view-header .view-header-icon:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.popover.hover-editor .view-header .view-header-icon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.popover.hover-editor.show-navbar:not(.is-minimized) .popover-title {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.popover.hover-editor:not(.show-navbar) .view-header {
|
||||
height: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.popover.hover-editor.show-navbar .view-header {
|
||||
/* theme devs: if you want to change the header height, you must do so by setting the --he-view-header-height variable */
|
||||
/* if you don't use the variable, you will break internal measurement logic */
|
||||
height: var(--he-view-header-height);
|
||||
overflow: unset;
|
||||
}
|
||||
|
||||
.popover.hover-editor:not(.show-navbar) .view-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.popover.hover-editor .workspace-leaf-content[data-type="image"] .view-content {
|
||||
padding: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.popover.hover-editor .workspace-leaf-content[data-type="image"] img {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
body .popover.hover-editor .view-content {
|
||||
/* theme devs: if you want to change the header height, you must do so by setting the --he-view-header-height variable */
|
||||
/* if you don't use the variable, you will break internal measurement logic */
|
||||
height: calc(100% - var(--he-view-header-height));
|
||||
}
|
||||
|
||||
/* start: zoomable images feature */
|
||||
|
||||
.popover.hover-editor.image-zoom .view-content .image-embed:active {
|
||||
aspect-ratio: unset;
|
||||
cursor: zoom-out;
|
||||
display: block;
|
||||
z-index: 200;
|
||||
position: fixed;
|
||||
max-height: calc(100% + 1px);
|
||||
max-width: 100%;
|
||||
height: calc(100% + 1px);
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
margin: -0.5px auto 0;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
/* extra specificity to override some community theme styles that cause issues */
|
||||
.popover.hover-editor.image-zoom .view-content .image-embed img:active {
|
||||
top: 50%;
|
||||
z-index: 99;
|
||||
transform: translateY(-50%);
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
width: calc(100% - 20px);
|
||||
height: unset;
|
||||
max-height: 95vh;
|
||||
object-fit: contain;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
opacity: 1;
|
||||
max-width: unset;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.popover.hover-editor.image-zoom .view-content .image-embed:active:after {
|
||||
background-color: var(--background-primary);
|
||||
opacity: 0.9;
|
||||
content: " ";
|
||||
height: calc(100% + 1px);
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 1px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.popover.hover-editor.image-zoom .view-content img {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
/* extra specificity to override some community theme styles that cause issues */
|
||||
.popover.hover-editor.image-zoom .workspace-leaf-content[data-type="image"] img {
|
||||
cursor: zoom-in;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
object-fit: contain;
|
||||
height: unset;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
opacity: 1;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
/* end: zoomable images feature */
|
||||
@@ -1,158 +0,0 @@
|
||||
{
|
||||
"remoteType": "",
|
||||
"useCustomRequestHandler": false,
|
||||
"couchDB_URI": "",
|
||||
"couchDB_USER": "",
|
||||
"couchDB_PASSWORD": "",
|
||||
"couchDB_DBNAME": "",
|
||||
"liveSync": false,
|
||||
"syncOnSave": true,
|
||||
"syncOnStart": true,
|
||||
"savingDelay": 200,
|
||||
"lessInformationInLog": false,
|
||||
"gcDelay": 0,
|
||||
"versionUpFlash": "",
|
||||
"minimumChunkSize": 20,
|
||||
"longLineThreshold": 250,
|
||||
"showVerboseLog": false,
|
||||
"suspendFileWatching": false,
|
||||
"trashInsteadDelete": true,
|
||||
"periodicReplication": true,
|
||||
"periodicReplicationInterval": 60,
|
||||
"syncOnFileOpen": true,
|
||||
"encrypt": false,
|
||||
"passphrase": "",
|
||||
"usePathObfuscation": false,
|
||||
"doNotDeleteFolder": false,
|
||||
"resolveConflictsByNewerFile": false,
|
||||
"batchSave": true,
|
||||
"batchSaveMinimumDelay": 5,
|
||||
"batchSaveMaximumDelay": 60,
|
||||
"deviceAndVaultName": "",
|
||||
"usePluginSettings": false,
|
||||
"showOwnPlugins": false,
|
||||
"showStatusOnEditor": true,
|
||||
"showStatusOnStatusbar": true,
|
||||
"showOnlyIconsOnEditor": false,
|
||||
"hideFileWarningNotice": false,
|
||||
"usePluginSync": false,
|
||||
"autoSweepPlugins": false,
|
||||
"autoSweepPluginsPeriodic": false,
|
||||
"notifyPluginOrSettingUpdated": false,
|
||||
"checkIntegrityOnSave": false,
|
||||
"batch_size": 25,
|
||||
"batches_limit": 25,
|
||||
"useHistory": true,
|
||||
"disableRequestURI": true,
|
||||
"skipOlderFilesOnSync": true,
|
||||
"checkConflictOnlyOnOpen": false,
|
||||
"showMergeDialogOnlyOnActive": false,
|
||||
"syncInternalFiles": false,
|
||||
"syncInternalFilesBeforeReplication": false,
|
||||
"syncInternalFilesIgnorePatterns": "\\/node_modules\\/, \\/\\.git\\/, \\/obsidian-livesync\\/",
|
||||
"syncInternalFilesTargetPatterns": "",
|
||||
"syncInternalFilesInterval": 60,
|
||||
"additionalSuffixOfDatabaseName": "421f688ab570b23d",
|
||||
"ignoreVersionCheck": false,
|
||||
"lastReadUpdates": 25,
|
||||
"deleteMetadataOfDeletedFiles": false,
|
||||
"syncIgnoreRegEx": "",
|
||||
"syncOnlyRegEx": "",
|
||||
"customChunkSize": 60,
|
||||
"readChunksOnline": true,
|
||||
"watchInternalFileChanges": true,
|
||||
"automaticallyDeleteMetadataOfDeletedFiles": 0,
|
||||
"disableMarkdownAutoMerge": false,
|
||||
"writeDocumentsIfConflicted": false,
|
||||
"useDynamicIterationCount": false,
|
||||
"syncAfterMerge": true,
|
||||
"configPassphraseStore": "",
|
||||
"encryptedPassphrase": "",
|
||||
"encryptedCouchDBConnection": "%$Bh465+EFQMTElwFFSDGASKI1SLpAExbeorlbZRkteweG3W2aZo9y25x4kbHgwa+9MNvE7ALlSWzLpSh7vriIQ8CZBeJeOgrv8b2DWVhDFOo42t0hScnVRlzhmaXERAzgvlBmMW9DxhV3oNPFDM3Q2leCEpAVGRhOhVJrp68emgdQOYQ5xVRWVAAwxvp/sWL1O6nerkxFH2hvA+AUPp05alJ9tycmVWPUKwclnoDknBfW5k9Ft90nPpcX9EgAO6IRMlp8X5bD0a26g1ti0ImFiA7Qi/1kl+Ymi7rDtzQEODtqhs5BPDHf/7oTU/MBWywlRiNOw22341Fvi+EvJb4QqZPFLUfwlrXCq81CTCrhV59kcl+s+IrWr8Wroo/cbbifyLjQ9IDYCeUHwe4lbWI0fGgjUJUAbWiRDAU8stifNwHON/PNyFd2/FR+e8PETbmWMxH7vuHjEPs8INUEvKiMOVZ7k0jwWM5xro8zGCYOYVmd5pInOXmHjypr3StPtXvgS2eOizsPZRrMsKlJ8gkcgHK32gOcBGwWVZ2iUkaP9vl6DztLB/uf2zgjTnlNk8MCtE4SAXSag1ipu/V6LVTzGmAkZT0VdG9TxzvDIaNHWhvvk1PekzagIU08xiAyh2Tin+hP6YJEujJ7S9jOn1//PuayuGFeD0KExmj7jxnrVvgN4QtrtO0b8/EqRni5qtzRVRslDpqhFuw=",
|
||||
"permitEmptyPassphrase": false,
|
||||
"useIndexedDBAdapter": true,
|
||||
"useTimeouts": false,
|
||||
"writeLogToTheFile": false,
|
||||
"doNotPaceReplication": false,
|
||||
"hashCacheMaxCount": 300,
|
||||
"hashCacheMaxAmount": 50,
|
||||
"concurrencyOfReadChunksOnline": 30,
|
||||
"minimumIntervalOfReadChunksOnline": 25,
|
||||
"hashAlg": "xxhash64",
|
||||
"suspendParseReplicationResult": false,
|
||||
"doNotSuspendOnFetching": false,
|
||||
"useIgnoreFiles": false,
|
||||
"ignoreFiles": ".gitignore",
|
||||
"syncOnEditorSave": true,
|
||||
"pluginSyncExtendedSetting": {},
|
||||
"syncMaxSizeInMB": 50,
|
||||
"settingSyncFile": "",
|
||||
"writeCredentialsForSettingSync": false,
|
||||
"notifyAllSettingSyncFile": false,
|
||||
"isConfigured": true,
|
||||
"settingVersion": 10,
|
||||
"enableCompression": false,
|
||||
"accessKey": "",
|
||||
"bucket": "",
|
||||
"endpoint": "",
|
||||
"region": "",
|
||||
"secretKey": "",
|
||||
"useEden": false,
|
||||
"maxChunksInEden": 10,
|
||||
"maxTotalLengthInEden": 1024,
|
||||
"maxAgeInEden": 10,
|
||||
"disableCheckingConfigMismatch": false,
|
||||
"displayLanguage": "zh",
|
||||
"enableChunkSplitterV2": false,
|
||||
"disableWorkerForGeneratingChunks": false,
|
||||
"processSmallFilesInUIThread": false,
|
||||
"notifyThresholdOfRemoteStorageSize": 0,
|
||||
"usePluginSyncV2": true,
|
||||
"usePluginEtc": false,
|
||||
"handleFilenameCaseSensitive": false,
|
||||
"doNotUseFixedRevisionForChunks": true,
|
||||
"showLongerLogInsideEditor": false,
|
||||
"sendChunksBulk": false,
|
||||
"sendChunksBulkMaxSize": 1,
|
||||
"useSegmenter": false,
|
||||
"useAdvancedMode": false,
|
||||
"usePowerUserMode": false,
|
||||
"useEdgeCaseMode": false,
|
||||
"enableDebugTools": false,
|
||||
"suppressNotifyHiddenFilesChange": false,
|
||||
"syncMinimumInterval": 2000,
|
||||
"P2P_Enabled": false,
|
||||
"P2P_AutoAccepting": 0,
|
||||
"P2P_AppID": "self-hosted-livesync",
|
||||
"P2P_roomID": "",
|
||||
"P2P_passphrase": "",
|
||||
"P2P_relays": "wss://exp-relay.vrtmrz.net/",
|
||||
"P2P_AutoBroadcast": false,
|
||||
"P2P_AutoStart": false,
|
||||
"P2P_AutoSyncPeers": "",
|
||||
"P2P_AutoWatchPeers": "",
|
||||
"P2P_SyncOnReplication": "",
|
||||
"P2P_RebuildFrom": "",
|
||||
"P2P_AutoAcceptingPeers": "",
|
||||
"P2P_AutoDenyingPeers": "",
|
||||
"P2P_IsHeadless": false,
|
||||
"doctorProcessedVersion": "0.25.0",
|
||||
"bucketCustomHeaders": "",
|
||||
"couchDB_CustomHeaders": "",
|
||||
"useJWT": false,
|
||||
"jwtAlgorithm": "",
|
||||
"jwtKey": "",
|
||||
"jwtKid": "",
|
||||
"jwtSub": "",
|
||||
"jwtExpDuration": 5,
|
||||
"useRequestAPI": false,
|
||||
"bucketPrefix": "",
|
||||
"chunkSplitterVersion": "",
|
||||
"E2EEAlgorithm": "",
|
||||
"processSizeMismatchedFiles": false,
|
||||
"forcePathStyle": true,
|
||||
"configPassphrase": "",
|
||||
"preset": "",
|
||||
"syncMode": "ONEVENTS",
|
||||
"dummy": 0
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.25.16",
|
||||
"minAppVersion": "0.9.12",
|
||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"author": "vorotamoroz",
|
||||
"authorUrl": "https://github.com/vrtmrz",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
@@ -1,472 +0,0 @@
|
||||
.added {
|
||||
color: var(--text-on-accent);
|
||||
background-color: var(--text-accent);
|
||||
}
|
||||
|
||||
.normal {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.deleted {
|
||||
color: var(--text-on-accent);
|
||||
background-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.conflict-dev-name {
|
||||
display: inline-block;
|
||||
min-width: 5em;
|
||||
}
|
||||
|
||||
.op-scrollable {
|
||||
overflow-y: scroll;
|
||||
/* min-height: 280px; */
|
||||
max-height: 280px;
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
}
|
||||
|
||||
.op-pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.op-warn {
|
||||
border: 1px solid salmon;
|
||||
padding: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.op-warn::before {
|
||||
content: "Warning";
|
||||
font-weight: bold;
|
||||
color: salmon;
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.op-warn-info {
|
||||
border: 1px solid rgb(255, 209, 81);
|
||||
padding: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.op-warn-info::before {
|
||||
content: "Notice";
|
||||
font-weight: bold;
|
||||
color: rgb(255, 209, 81);
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.syncstatusbar {
|
||||
-webkit-filter: grayscale(100%);
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
.tcenter {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sls-plugins-wrap {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
max-height: 50vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.sls-plugins-tbl {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
width: 100%;
|
||||
max-height: 80%;
|
||||
}
|
||||
|
||||
.divider th {
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.sls-header-button {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.sls-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:root {
|
||||
--sls-log-text: "";
|
||||
}
|
||||
|
||||
.sls-troubleshoot-preview {
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
.sls-troubleshoot-preview img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.sls-setting-tab {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.sls-setting-menu-btn {
|
||||
color: var(--text-normal);
|
||||
background-color: var(--background-secondary-alt);
|
||||
border-radius: 4px 4px 0 0;
|
||||
padding: 6px 10px;
|
||||
cursor: pointer;
|
||||
margin-right: 12px;
|
||||
font-family: "Inter", sans-serif;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.sls-setting-label.selected {
|
||||
/* order: 1; */
|
||||
flex-grow: 1;
|
||||
/* width: 100%; */
|
||||
}
|
||||
|
||||
.sls-setting-tab:hover~div.sls-setting-menu-btn,
|
||||
.sls-setting-label.selected .sls-setting-tab:checked~div.sls-setting-menu-btn {
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
}
|
||||
|
||||
.sls-setting-menu-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: rgba(var(--background-primary), 0.3);
|
||||
backdrop-filter: blur(4px);
|
||||
border-radius: 4px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.sls-setting-menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
--sls-col-transparent: transparent;
|
||||
--sls-col-warn: rgba(var(--background-modifier-error-rgb), 0.1);
|
||||
--sls-col-warn-stripe1: var(--sls-col-transparent);
|
||||
--sls-col-warn-stripe2: var(--sls-col-warn);
|
||||
}
|
||||
|
||||
.sls-setting-menu-buttons {
|
||||
border: 1px solid var(--sls-col-warn);
|
||||
padding: 2px;
|
||||
margin: 1px;
|
||||
border-radius: 4px;
|
||||
background-image: linear-gradient(-45deg,
|
||||
var(--sls-col-warn-stripe1) 25%, var(--sls-col-warn-stripe2) 25%, var(--sls-col-warn-stripe2) 50%,
|
||||
var(--sls-col-warn-stripe1) 50%, var(--sls-col-warn-stripe1) 75%, var(--sls-col-warn-stripe2) 75%, var(--sls-col-warn-stripe2));
|
||||
background-size: 30px 30px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
padding: 0.5em 0.25em;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* transition: background-position 1s; */
|
||||
animation: sls-scroll-warn 1s linear 0s infinite;
|
||||
}
|
||||
|
||||
@keyframes sls-scroll-warn {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 30px 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sls-setting-menu-buttons label {
|
||||
margin-right: auto;
|
||||
flex-grow: 1;
|
||||
color: var(--text-warning);
|
||||
}
|
||||
|
||||
.sls-setting-label {
|
||||
flex-grow: 1;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.setting-collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sls-plugins-tbl-buttons {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sls-plugins-tbl-buttons button {
|
||||
flex-grow: 0;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
.sls-plugins-tbl-device-head {
|
||||
background-color: var(--background-secondary-alt);
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.op-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.op-flex input {
|
||||
display: inline-flex;
|
||||
flex-grow: 1;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.op-info {
|
||||
display: inline-flex;
|
||||
flex-grow: 1;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
width: 100%;
|
||||
margin-bottom: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.history-added {
|
||||
color: var(--text-on-accent);
|
||||
background-color: var(--text-accent);
|
||||
}
|
||||
|
||||
.history-normal {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.history-deleted {
|
||||
color: var(--text-on-accent);
|
||||
background-color: var(--text-muted);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.ob-btn-config-fix label {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.ob-btn-config-info {
|
||||
border: 1px solid salmon;
|
||||
padding: 2px;
|
||||
margin: 1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ob-btn-config-head {
|
||||
padding: 2px;
|
||||
margin: 1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.isWizard .wizardHidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sls-setting:not(.isWizard) .wizardOnly {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sls-item-dirty::before {
|
||||
content: "✏";
|
||||
}
|
||||
|
||||
.sls-item-dirty-help::after {
|
||||
content: " ❓";
|
||||
}
|
||||
|
||||
.sls-item-invalid-value {
|
||||
background-color: rgba(var(--background-modifier-error-rgb), 0.3) !important;
|
||||
}
|
||||
|
||||
.sls-setting-disabled input[type=text],
|
||||
.sls-setting-disabled input[type=number],
|
||||
.sls-setting-disabled input[type=password] {
|
||||
filter: brightness(80%);
|
||||
color: var(--text-muted);
|
||||
|
||||
}
|
||||
|
||||
.sls-setting-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.sls-setting-obsolete {
|
||||
background-image: linear-gradient(-45deg,
|
||||
var(--sls-col-warn-stripe1) 25%, var(--sls-col-warn-stripe2) 25%, var(--sls-col-warn-stripe2) 50%,
|
||||
var(--sls-col-warn-stripe1) 50%, var(--sls-col-warn-stripe1) 75%, var(--sls-col-warn-stripe2) 75%, var(--sls-col-warn-stripe2));
|
||||
background-image: linear-gradient(-45deg,
|
||||
transparent 25%, rgba(var(--background-secondary), 0.1) 25%, rgba(var(--background-secondary), 0.1) 50%, transparent 50%, transparent 75%, rgba(var(--background-secondary), 0.1) 75%, rgba(var(--background-secondary), 0.1));
|
||||
background-size: 60px 60px;
|
||||
}
|
||||
|
||||
.password-input>.setting-item-control>input {
|
||||
-webkit-text-security: disc;
|
||||
}
|
||||
|
||||
span.ls-mark-cr::after {
|
||||
user-select: none;
|
||||
content: "↲";
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.deleted span.ls-mark-cr::after {
|
||||
color: var(--text-on-accent);
|
||||
}
|
||||
|
||||
.ls-imgdiff-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ls-imgdiff-wrap .overlay {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ls-imgdiff-wrap .overlay .img-base {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ls-imgdiff-wrap .overlay .img-overlay {
|
||||
-webkit-filter: invert(100%) opacity(50%);
|
||||
filter: invert(100%) opacity(50%);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
animation: ls-blink-diff 0.5s cubic-bezier(0.4, 0, 1, 1) infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes ls-blink-diff {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.livesync-status {
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
height: auto;
|
||||
min-height: 1em;
|
||||
position: absolute;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
padding-right: 16px;
|
||||
top: var(--header-height);
|
||||
z-index: calc(var(--layer-cover) + 1);
|
||||
|
||||
font-variant-numeric: tabular-nums;
|
||||
tab-size: 4;
|
||||
text-align: right;
|
||||
white-space: pre-wrap;
|
||||
display: inline-block;
|
||||
color: var(--text-normal);
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
div.workspace-leaf-content[data-type=bases] .livesync-status {
|
||||
top: calc(var(--bases-header-height) + var(--header-height));
|
||||
padding: 5px;
|
||||
padding-right:18px;
|
||||
}
|
||||
|
||||
.is-mobile div.workspace-leaf-content[data-type=bases] .livesync-status {
|
||||
top: calc(var(--bases-header-height) + var(--view-header-height));
|
||||
padding: 6px;
|
||||
padding-right:18px;
|
||||
}
|
||||
|
||||
.livesync-status div {
|
||||
opacity: 0.6;
|
||||
-webkit-filter: grayscale(100%);
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
.livesync-status .livesync-status-loghistory {
|
||||
text-align: left;
|
||||
opacity: 0.4;
|
||||
|
||||
}
|
||||
|
||||
.livesync-status div.livesync-status-messagearea {
|
||||
opacity: 0.6;
|
||||
color: var(--text-on-accent);
|
||||
background: var(--background-modifier-error);
|
||||
-webkit-filter: unset;
|
||||
filter: unset;
|
||||
}
|
||||
|
||||
|
||||
.menu-setting-poweruser-disabled .sls-setting-poweruser {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-setting-advanced-disabled .sls-setting-advanced {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-setting-edgecase-disabled .sls-setting-edgecase {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sls-setting-panel-title {
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.sls-setting-panel-title {
|
||||
top: 2em;
|
||||
background-color: rgba(var(--background-primary), 0.3);
|
||||
backdrop-filter: blur(4px);
|
||||
border-radius: 30%;
|
||||
}
|
||||
|
||||
.sls-dialogue-note-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sls-dialogue-note-countdown {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.sls-qr {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
.sls-keypair pre {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"Components@@CTA-BTN-enable": true,
|
||||
"Components@@file-names-untrim": true,
|
||||
"Components@@folder-font-bold": true,
|
||||
"Components@@colorful-folder": true,
|
||||
"Components@@file-icon-remove": false,
|
||||
"Components@@outline-enhanced": true,
|
||||
"Components@@new-tab-btn-select": "new-tab-btn-default",
|
||||
"Components@@immersive-canvas": true,
|
||||
"Components@@scrollbar-hide": true,
|
||||
"Mobile@@drawer-phone-full-width": true,
|
||||
"Mobile@@card-layout-pad-open": true,
|
||||
"Plugin@@colorful-checkbox": true,
|
||||
"Editor@@italic-color@@light": "#000000",
|
||||
"Editor@@text-highlight-bg@@light": "#000000",
|
||||
"Editor@@inline-code-normal@@light": "#000000",
|
||||
"Editor@@link-color@@light": "#000000",
|
||||
"Editor@@link-color-hover@@light": "#000000",
|
||||
"Editor@@h1-weight": 900,
|
||||
"Editor@@h2-weight": 850,
|
||||
"Editor@@h3-weight": 800,
|
||||
"Editor@@h4-weight": 750,
|
||||
"Editor@@h5-weight": 700,
|
||||
"Editor@@h6-weight": 650,
|
||||
"Editor@@h6-divider-on": false,
|
||||
"Editor@@h5-divider-on": false,
|
||||
"Editor@@h4-divider-on": false,
|
||||
"Editor@@h3-divider-on": false,
|
||||
"Editor@@h2-divider-on": false,
|
||||
"Editor@@h1-divider-on": false,
|
||||
"Editor@@collapse-icon-restore": false,
|
||||
"Editor@@inline-title-divider-remove": true,
|
||||
"Editor@@text-align-justify": false,
|
||||
"Editor@@bold-color@@dark": "#000000",
|
||||
"Editor@@bold-color@@light": "#000000",
|
||||
"Editor@@editor-grid-background-pattren": true,
|
||||
"Editor@@border-focus-mode": false,
|
||||
"Editor@@line-hover-indicator": true,
|
||||
"Appearance-light@@card-layout-open-light": true
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"id": "obsidian-style-settings",
|
||||
"name": "Style Settings",
|
||||
"version": "1.0.9",
|
||||
"minAppVersion": "0.11.5",
|
||||
"description": "Offers controls for adjusting theme, plugin, and snippet CSS variables.",
|
||||
"author": "mgmeyers",
|
||||
"authorUrl": "https://github.com/mgmeyers/obsidian-style-settings",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"id": "templater-obsidian",
|
||||
"name": "Templater",
|
||||
"version": "2.14.1",
|
||||
"description": "Create and use templates",
|
||||
"minAppVersion": "1.5.0",
|
||||
"author": "SilentVoid",
|
||||
"authorUrl": "https://github.com/SilentVoid13",
|
||||
"helpUrl": "https://silentvoid13.github.io/Templater/",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
.templater_search {
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.templater_div {
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.templater_div > .setting-item {
|
||||
border-top: none !important;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.templater_div > .setting-item > .setting-item-control {
|
||||
justify-content: space-around;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.templater_div
|
||||
> .setting-item
|
||||
> .setting-item-control
|
||||
> .setting-editor-extra-setting-button {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.templater_donating {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.templater_title {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.templater_template {
|
||||
align-self: center;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.templater_cmd {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.templater_div2 > .setting-item {
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.templater-prompt-div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.templater-prompt-form {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.templater-prompt-input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.templater-button-div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
textarea.templater-prompt-input {
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
textarea.templater-prompt-input:focus {
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .templater-command-bg {
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
background-color: var(--background-primary-alt);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command {
|
||||
font-size: 0.85em;
|
||||
font-family: var(--font-monospace);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.cm-s-obsidian .templater-inline .cm-templater-command {
|
||||
background-color: var(--background-primary-alt);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-templater-opening-tag {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-templater-closing-tag {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-templater-interpolation-tag {
|
||||
color: var(--code-property, #008bff);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-templater-execution-tag {
|
||||
color: var(--code-function, #c0d700);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-keyword {
|
||||
color: var(--code-keyword, #00a7aa);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-atom {
|
||||
color: var(--code-normal, #f39b35);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-value,
|
||||
.cm-s-obsidian .cm-templater-command.cm-number,
|
||||
.cm-s-obsidian .cm-templater-command.cm-type {
|
||||
color: var(--code-value, #a06fca);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-def,
|
||||
.cm-s-obsidian .cm-templater-command.cm-type.cm-def {
|
||||
color: var(--code-normal, var(--text-normal));
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-property,
|
||||
.cm-s-obsidian .cm-templater-command.cm-property.cm-def,
|
||||
.cm-s-obsidian .cm-templater-command.cm-attribute {
|
||||
color: var(--code-function, #98e342);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-variable,
|
||||
.cm-s-obsidian .cm-templater-command.cm-variable-2,
|
||||
.cm-s-obsidian .cm-templater-command.cm-variable-3,
|
||||
.cm-s-obsidian .cm-templater-command.cm-meta {
|
||||
color: var(--code-property, #d4d4d4);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-callee,
|
||||
.cm-s-obsidian .cm-templater-command.cm-operator,
|
||||
.cm-s-obsidian .cm-templater-command.cm-qualifier,
|
||||
.cm-s-obsidian .cm-templater-command.cm-builtin {
|
||||
color: var(--code-operator, #fc4384);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-tag {
|
||||
color: var(--code-tag, #fc4384);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-comment,
|
||||
.cm-s-obsidian .cm-templater-command.cm-comment.cm-tag,
|
||||
.cm-s-obsidian .cm-templater-command.cm-comment.cm-attribute {
|
||||
color: var(--code-comment, #696d70);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-string,
|
||||
.cm-s-obsidian .cm-templater-command.cm-string-2 {
|
||||
color: var(--code-string, #e6db74);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-header,
|
||||
.cm-s-obsidian .cm-templater-command.cm-hr {
|
||||
color: var(--code-keyword, #da7dae);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-link {
|
||||
color: var(--code-normal, #696d70);
|
||||
}
|
||||
|
||||
.cm-s-obsidian .cm-templater-command.cm-error {
|
||||
border-bottom: 1px solid #c42412;
|
||||
}
|
||||
|
||||
.CodeMirror-hints {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
list-style: none;
|
||||
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
|
||||
-webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 3px;
|
||||
border: 1px solid silver;
|
||||
|
||||
background: white;
|
||||
font-size: 90%;
|
||||
font-family: monospace;
|
||||
|
||||
max-height: 20em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.CodeMirror-hint {
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
border-radius: 2px;
|
||||
white-space: pre;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
li.CodeMirror-hint-active {
|
||||
background: #08f;
|
||||
color: white;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "Border",
|
||||
"version": "1.12.20",
|
||||
"minAppVersion": "0.16.0",
|
||||
"author": "Akifyss",
|
||||
"authorUrl": "https://github.com/Akifyss"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "GitHub Theme",
|
||||
"version": "1.1.6",
|
||||
"minAppVersion": "1.0.0",
|
||||
"author": "@krios2146",
|
||||
"authorUrl": "https://github.com/krios2146"
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": "Minimal",
|
||||
"version": "8.0.4",
|
||||
"minAppVersion": "1.9.0",
|
||||
"author": "@kepano",
|
||||
"authorUrl": "https://twitter.com/kepano",
|
||||
"fundingUrl": "https://www.buymeacoffee.com/kepano"
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
{
|
||||
"main": {
|
||||
"id": "0d497f6739ef6df9",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "306ec49a61f877d3",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "a93d6d3811397e7b",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "Docker/优秀好用的Docker镜像/Gitea-私有化仓库部署.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "Gitea-私有化仓库部署"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
},
|
||||
"left": {
|
||||
"id": "dd92142da425da5d",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "18712eeca302c86a",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "f1c17e2d203acce4",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "file-explorer",
|
||||
"state": {
|
||||
"sortOrder": "alphabetical",
|
||||
"autoReveal": false
|
||||
},
|
||||
"icon": "lucide-folder-closed",
|
||||
"title": "文件列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "0d80743f19037256",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "NSSM",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabeticalReverse"
|
||||
},
|
||||
"icon": "lucide-search",
|
||||
"title": "搜索"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "29759c335a3c0889",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "bookmarks",
|
||||
"state": {},
|
||||
"icon": "lucide-bookmark",
|
||||
"title": "书签"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
},
|
||||
"right": {
|
||||
"id": "a00fc03fc4170f30",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "553118af57cf253c",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "330eb3c82c280cae",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "树萌芽的小本本/树萌芽已部署网站(不定时持续更新).md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
"showSearch": false,
|
||||
"searchQuery": "",
|
||||
"backlinkCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-coming-in",
|
||||
"title": "树萌芽已部署网站(不定时持续更新) 的反向链接列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "a11bb24aba14d458",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-going-out",
|
||||
"title": "出链"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "66a43be089eb76fa",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "tag",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"useHierarchy": true,
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-tags",
|
||||
"title": "标签"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300,
|
||||
"collapsed": true
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"switcher:打开快速切换": false,
|
||||
"command-palette:打开命令面板": false,
|
||||
"obsidian-livesync:Show Log": false,
|
||||
"obsidian-livesync:Replicate": false,
|
||||
"obsidian-livesync:P2P Replicator": false,
|
||||
"obsidian-livesync:Show Customization sync": false
|
||||
}
|
||||
},
|
||||
"active": "a93d6d3811397e7b",
|
||||
"lastOpenFiles": [
|
||||
"编程语言/Java/JavaSpring标准项目架构.md",
|
||||
"树萌芽的小本本/树萌芽已部署网站(不定时持续更新).md",
|
||||
"Linux/ADB/ADB应用启动命令.md",
|
||||
"Linux/ADB/ADB常用命令.md",
|
||||
"树萌芽的小本本/重要信息记录.md",
|
||||
"树萌芽の小想法/革命后的理想.md",
|
||||
"内网穿透/Wireguard/Wireguard基础命令.md",
|
||||
"编程语言/Golang/Golang标准代码架构.md",
|
||||
"编程语言/前端/React标准项目架构.md",
|
||||
"编程语言/前端/Vue标准项目架构.md",
|
||||
"编程语言/前端/纯静态网页的强大功能与应用.md",
|
||||
"编程语言/前端/JavaScript趣味题/JavaScript趣味题_128.md",
|
||||
"编程语言/前端/React项目初始化教程.md",
|
||||
"编程语言/前端/OpenList美化代码.md",
|
||||
"编程语言/前端/css注入代码合集.md",
|
||||
"编程语言/前端/Vue项目初始化教程.md",
|
||||
"编程语言/前端/代码片段/代码片段-特殊HelloWorld输出.md",
|
||||
"编程语言/前端/前端html导入css和js方法.md",
|
||||
"编程语言/前端/代码片段/代码片段-标准HelloWorld输出.md",
|
||||
"编程语言/前端/代码片段",
|
||||
"编程语言/前端/JavaScript趣味题/JavaScript趣味题_28.md",
|
||||
"编程语言/前端/React打包成Windows和Android软件方案.md",
|
||||
"编程语言/前端/JavaScript趣味题/JavaScript趣味题_18.md",
|
||||
"编程语言/前端/nodejs的markdown库.md",
|
||||
"实习求职/面试经历/27双非本一腾讯IEG游戏安全后台实习面经.md",
|
||||
"编程语言/前端/企业级标准React项目架构.md",
|
||||
"编程语言/前端/JavaScript趣味题",
|
||||
"实习求职/面试经历",
|
||||
"编程语言/前端",
|
||||
"编程语言/前端&HTML&CSS&JS/React项目初始化教程.md",
|
||||
"数据库/SQLite",
|
||||
"数据库/MongoDB",
|
||||
"数据库/MySQL",
|
||||
"内网穿透/Wireguard/配置/wgs-alyxg.conf",
|
||||
"内网穿透/Wireguard/配置/wgs-alycd.conf",
|
||||
"内网穿透/Wireguard/配置/wgc-win.conf",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/5-04.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/5-03.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/5-02.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/5-01.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/2-01.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/1-10.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/1-09.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/1-08.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/1-07.png",
|
||||
"Minecraft/基岩版服务器/NukkitLearn/images/1-06.png"
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 371 KiB |
|
Before Width: | Height: | Size: 4.9 MiB |
|
Before Width: | Height: | Size: 5.1 MiB |
|
Before Width: | Height: | Size: 4.8 MiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 924 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 4.8 MiB |
|
Before Width: | Height: | Size: 257 KiB |
@@ -1,126 +0,0 @@
|
||||
以下是你提到的英语词汇及其翻译和音标。我已经为其添加了音标,并将其排版成HTML和CSS格式。
|
||||
|
||||
|
||||
|
||||
### 英语词汇及翻译
|
||||
|
||||
|
||||
|
||||
| 英文词汇 | 翻译 | 音标 |
|
||||
| :------------------------------------------------ | :----------------------- | :------------------------------------------------- |
|
||||
| hands | 手 | /hændz/ |
|
||||
| butterflies in the stomach | 肚子里的蝴蝶 | /ˈbʌtərflaɪz ɪn ðə ˈstʌmək/ |
|
||||
| muscle tension | 肌肉紧张 | /ˈmʌsl ˈtɛnʃən/ |
|
||||
| credit | 学分 / 信用 | /ˈkrɛdɪt/ |
|
||||
| major | 主修 / 专业 | /ˈmeɪdʒər/ |
|
||||
| major in | 主修 | /ˈmeɪdʒər ɪn/ |
|
||||
| dropout | 辍学 | /ˈdrɔpˌaʊt/ |
|
||||
| trigger | 诱因 / 扳机 | /ˈtrɪɡər/ |
|
||||
| resilience | 弹性 | /rɪˈzɪljəns/ |
|
||||
| cyberlove | 网恋 | /ˈsaɪbərləv/ |
|
||||
| long-distance relationship | 异地恋 | /lɔŋ ˈdɪstəns rɪˌleɪʃənˈʃɪp/ |
|
||||
| date | 日期 / 约会 | /deɪt/ |
|
||||
| lovesick | 恋爱中的病 | /ˈlʌvˌsɪk/ |
|
||||
| zodiac | 星座 | /ˈzoʊ.dɪ.æk/ |
|
||||
| aquaman | 水行侠 | /ˈækwəˌmæn/ |
|
||||
| aquarium | 水族馆 | /əˈkwɛərɪəm/ |
|
||||
| pilgrim | 朝圣者 | /ˈpɪlɡrɪm/ |
|
||||
| soul | 灵魂 | /soʊl/ |
|
||||
| bar | 酒吧 | /bɑr/ |
|
||||
| soap bar | 香皂 | /soʊp bɑr/ |
|
||||
| behind the bars | 在监狱里 | /bɪˈhaɪnd ðə bɑrz/ |
|
||||
| enjoy each other's company | 享受彼此的陪伴 | /ɪnˈdʒɔɪ iʧ ˈʌðərz ˈkʌmpəni/ |
|
||||
| crush | 暗恋 | /krʌʃ/ |
|
||||
| have a crush on | 对某人有好感 | /hæv ə krʌʃ ɑn/ |
|
||||
| idol | 偶像 | /ˈaɪdəl/ |
|
||||
| icon | 图标 / 偶像 | /ˈaɪˌkɒn/ |
|
||||
| ideal | 理想 | /aɪˈdɪəl/ |
|
||||
| idea | 主意 | /aɪˈdɪə/ |
|
||||
| idiot | 傻瓜 | /ˈɪdiət/ |
|
||||
| marketing campaign | 营销活动 | /ˈmɑrkɪtɪŋ kæmˈpeɪn/ |
|
||||
| being in love implies that you can fall out of it | 恋爱意味着你可能会失去爱 | /ˈbiːɪŋ ɪn lʌv ɪmˈplaɪz ðæt juː kæn fɔl aʊt ʌv ɪt/ |
|
||||
| -wise | 方面的 | /waɪz/ |
|
||||
| family-wise | 从家庭的角度 | /ˈfæmɪli waɪz/ |
|
||||
| better off | 过得更好 | /ˈbɛtər ɔf/ |
|
||||
| big old kiss | 大大的吻 | /bɪɡ oʊld kɪs/ |
|
||||
| vulnerable | 脆弱的 | /ˈvʌlnərəbəl/ |
|
||||
|
||||
|
||||
|
||||
### HTML/CSS 排版美化
|
||||
|
||||
|
||||
|
||||
```html
|
||||
html<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>英语词汇及翻译</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #007BFF;
|
||||
color: white;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
tr:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>英语词汇及翻译</h1>
|
||||
<table>
|
||||
<tr>
|
||||
<th>英文词汇</th>
|
||||
<th>翻译</th>
|
||||
<th>音标</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>hands</td>
|
||||
<td>手</td>
|
||||
<td>/hændz/</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>butterflies in the stomach</td>
|
||||
<td>肚子里的蝴蝶</td>
|
||||
<td>/ˈbʌtərflaɪz ɪn ðə ˈstʌmək/</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>muscle tension</td>
|
||||
<td>肌肉紧张</td>
|
||||
<td>/ˈmʌsl ˈtɛnʃən/</td>
|
||||
</tr>
|
||||
<!-- 可以继续添加其他词汇 -->
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
html
|
||||
```
|
||||
|
||||
|
||||
|
||||
你可以将上述HTML代码复制到文本编辑器中,然后另存为`.html`文件,以在浏览器中打开并查看效果。如果有其他需求或需要进一步的帮助,请告知我!
|
||||
|
Before Width: | Height: | Size: 4.7 MiB |
@@ -1,61 +0,0 @@
|
||||
timeline:
|
||||
9.16 一面
|
||||
9.18 二面
|
||||
9.22 三面
|
||||
9.23 HR面
|
||||
9.24 录用评估
|
||||
9.26 Offer
|
||||
|
||||
腾讯一面(1h)
|
||||
|
||||
1. 介绍实习需求(K8S 和 Casbin RBAC 相关)
|
||||
2. 为啥初创实习两个月离职
|
||||
3. Go 为什么支持高并发
|
||||
4. GMP模型原理
|
||||
5. Goroutine Work-Stealing 的目的
|
||||
6. P的角色的作用,如果在M上维护Goroutine队列有什么不好
|
||||
7. GMP对CPU密集型任务能提高并发么
|
||||
8. IO操作需要CPU么,什么时候需要,磁盘IO和网络IO的区别
|
||||
9. Channel的作用和底层实现
|
||||
10. Channel的缓冲区在用户态还是内核态
|
||||
11. Goroutine阻塞等待的时候由谁来唤醒,需要额外的goroutine来遍历所有的channel么
|
||||
12. M上的G0是干嘛的
|
||||
13. 介绍select/poll/epoll
|
||||
14. 网络IO的流程
|
||||
15. 了解过Go Runtime么
|
||||
|
||||
算法:求两个数的最大公约数
|
||||
|
||||
腾讯二面(1h)
|
||||
|
||||
1. 介绍实习需求,最有挑战的部分
|
||||
2. RocksDB了解么,说一下LsmTree
|
||||
3. 详细介绍一下Raft协议
|
||||
4. Raft协议和Paxos协议的区别,有哪些优化
|
||||
5. 介绍一下React Agent
|
||||
6. LangChain 和 LangGraph 的区别
|
||||
7. Agent 和 LLM 的区别
|
||||
8. Function Call 和 MCP 的区别
|
||||
9. RPC的全流程
|
||||
10. 负载均衡算法有哪些
|
||||
11. 介绍一致性Hash算法,服务扩缩容之后有什么影响
|
||||
12. 网络编程
|
||||
13. 介绍一下TCP和UDP
|
||||
14. 介绍一下HTTP各个版本及实现
|
||||
|
||||
算法:
|
||||
|
||||
1. 编辑距离
|
||||
2. 两两交换链表中的节点
|
||||
|
||||
腾讯三面(30min)
|
||||
|
||||
1. 介绍实习,你做了什么
|
||||
2. 介绍项目
|
||||
3. 实习时长,到岗时间,推HR面
|
||||
|
||||
腾讯HR面(15min)
|
||||
|
||||
1. 离职原因
|
||||
2. 实习时长,到岗时间
|
||||
3. 聊聊天
|
||||
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 493 KiB |
|
Before Width: | Height: | Size: 107 KiB |
@@ -1,195 +0,0 @@
|
||||
|
||||
```toml
|
||||
#=============================================================================
|
||||
#===================================基础设置===================================
|
||||
#=============================================================================
|
||||
|
||||
serverAddr = "47.108.90.0"
|
||||
serverPort = 7000
|
||||
|
||||
auth.method = "token"
|
||||
auth.token = "smy"
|
||||
|
||||
webServer.addr = "0.0.0.0"
|
||||
webServer.port = 7400
|
||||
webServer.user = "shumengya"
|
||||
webServer.password = "tyh@19900420"
|
||||
webServer.pprofEnable = false
|
||||
|
||||
#下面两个二选一
|
||||
transport.protocol = "kcp"
|
||||
#transport.protocol = "quic"
|
||||
|
||||
# 日志配置
|
||||
log.to = "console"
|
||||
log.level = "info"
|
||||
|
||||
|
||||
#=============================================================================
|
||||
#===================================Http服务===================================
|
||||
#=============================================================================
|
||||
|
||||
# 萌芽盘-openlist
|
||||
[[proxies]]
|
||||
name = "openlist"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 5244
|
||||
customDomains = ["openlist.shumengya.top","pan.shumengya.top"]
|
||||
|
||||
|
||||
#大萌芽1panel面板-1panel
|
||||
[[proxies]]
|
||||
name = "1panel"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 19132
|
||||
customDomains = ["1panel.shumengya.top"]
|
||||
|
||||
|
||||
#Obsidian笔记同步-couchdb
|
||||
[[proxies]]
|
||||
name = "couchdb"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 5984
|
||||
customDomains = ["note.shumengya.top"]
|
||||
|
||||
#大萌芽frp客户端-frpc
|
||||
[[proxies]]
|
||||
name = "frpc"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 7400
|
||||
customDomains = ["frpc.shumengya.top"]
|
||||
|
||||
#萌芽文件快传-filecodebox
|
||||
[[proxies]]
|
||||
name = "filecodebox"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 12345
|
||||
customDomains = ["file.shumengya.top","send.shumengya.top"]
|
||||
|
||||
#萌芽图床-lsky-pro
|
||||
[[proxies]]
|
||||
name = "lsky-pro"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 8089
|
||||
customDomains = ["image.shumengya.top","img.shumengya.top"]
|
||||
|
||||
#在线代码编辑器-codeserver
|
||||
[[proxies]]
|
||||
name = "codeserver"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 8888
|
||||
customDomains = ["code.shumengya.top"]
|
||||
|
||||
#60sAPI接口集合-60sapi
|
||||
[[proxies]]
|
||||
name = "60s-API"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 4399
|
||||
customDomains = ["60s.api.shumengya.top"]
|
||||
|
||||
#社交媒体视频ai总结-bilinote
|
||||
[[proxies]]
|
||||
name = "bilinote"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 3015
|
||||
customDomains = ["bilinote.shumengya.top"]
|
||||
|
||||
#萌芽git仓库-gitea
|
||||
[[proxies]]
|
||||
name = "gitea"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 8989
|
||||
customDomains = ["repo.shumengya.top"]
|
||||
|
||||
#Docker可视化面板-DPanel
|
||||
[[proxies]]
|
||||
name = "dpanel"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 8800
|
||||
customDomains = ["dpanel.shumengya.top"]
|
||||
|
||||
#萌芽通知-ntfy
|
||||
[[proxies]]
|
||||
name = "ntfy"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 82
|
||||
customDomains = ["ntfy.shumengya.top"]
|
||||
|
||||
#萌芽大内网管理后台-openwrt
|
||||
[[proxies]]
|
||||
name = "openwrt"
|
||||
type = "http"
|
||||
localIP = "192.168.1.1"
|
||||
localPort = 80
|
||||
customDomains = ["openwrt.shumengya.top"]
|
||||
|
||||
#=========================================================================
|
||||
#===================================TCP服务=================================
|
||||
#=========================================================================
|
||||
|
||||
#萌芽农场远程控制台
|
||||
[[proxies]]
|
||||
name = "mengyafarm-config"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 7071
|
||||
remotePort = 6060
|
||||
|
||||
#mysql数据库
|
||||
[[proxies]]
|
||||
name = "mysql"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 3306
|
||||
remotePort = 3307 # frps 上的映射端口
|
||||
|
||||
#MongoDB数据库
|
||||
[[proxies]]
|
||||
name = "mongodb"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 27017
|
||||
remotePort = 27018
|
||||
|
||||
#Postgres数据库
|
||||
[[proxies]]
|
||||
name = "postgres"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 5432
|
||||
remotePort = 5433
|
||||
|
||||
#大萌芽-Debian11 SSH连接
|
||||
[[proxies]]
|
||||
name = "bigmengya-ssh"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 22
|
||||
remotePort = 9022
|
||||
|
||||
#Redis数据库
|
||||
[[proxies]]
|
||||
name = "redis"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 6379
|
||||
remotePort = 6380
|
||||
|
||||
|
||||
#=========================================================================
|
||||
#===================================UDP服务=================================
|
||||
#=========================================================================
|
||||
|
||||
```
|
||||
@@ -1,73 +0,0 @@
|
||||
|
||||
---
|
||||
|
||||
## 基础:QUIC 与 KCP 本身的区别(协议层面)
|
||||
|
||||
要理解 FRP 下这两种模式的差别,首先必须认识 QUIC 与 KCP 两种协议在 UDP 之上的设计差异。下面从几个维度来对比。
|
||||
|
||||
|维度|KCP|QUIC|
|
||||
|---|---|---|
|
||||
|协议定位 / 设计目标|一个可靠的 UDP 层协议(“可靠 UDP + ARQ + 拥塞控制 + 重传策略”),强调低延迟、对弱网络适应性较好。 ([腾讯云](https://cloud.tencent.com/developer/article/1964393?utm_source=chatgpt.com "KCP协议:从TCP到UDP家族QUIC/KCP/ENET-腾讯云开发 ..."))|更现代的传输层协议,最初由 Google 设计,后来演化成为 HTTP/3 的底层传输(即:QUIC + TLS + 多路复用)的一部分。它在 UDP 之上集成了连接管理、多路复用、拥塞控制、0-RTT、TLS 加密等机制。 ([CSDN博客](https://blog.csdn.net/qq_36541069/article/details/132143729?utm_source=chatgpt.com "所得杂记:KCP,QUIC,MQTT - CSDN博客"))|
|
||||
|连接建立 / 握手延迟|KCP 在使用上一般是比较“轻”的握手(因为只是建立会话 ID 等),延迟比较低|QUIC 支持 0-RTT、较快握手(因为集成 TLS)等机制,从连接建立角度更有优势|
|
||||
|多路复用 / 多流支持|传统 KCP 是面向“单流”(一个会话对应一个流式数据)。要实现真正的多路复用,需要在上层自己设计;否则每条独立连接就是一个 KCP 实例|QUIC 原生支持多流(multiple streams)和流内优先级,能在一个连接上同时承载多个逻辑数据流,减少队头阻塞(Head-of-line blocking)的问题|
|
||||
|拥塞控制 / 拥塞算法|KCP 自己有拥塞控制与窗口机制,也可以调节参数去“弱化”拥塞控制以换取低延迟。 ([腾讯云](https://cloud.tencent.com/developer/article/1964393?utm_source=chatgpt.com "KCP协议:从TCP到UDP家族QUIC/KCP/ENET-腾讯云开发 ..."))|QUIC 的拥塞控制受到现代研究和 TCP 的启发,有较为成熟的拥塞控制与流量控制算法;它也能动态适应网络变化|
|
||||
|丢包 / 重传机制|KCP 使用选择性重传 (Selective Retransmission)、快速重传、延迟 ACK 调节等策略来减少重传滞后和延迟惩罚。 ([腾讯云](https://cloud.tencent.com/developer/article/1964393?utm_source=chatgpt.com "KCP协议:从TCP到UDP家族QUIC/KCP/ENET-腾讯云开发 ..."))|QUIC 也具备细粒度的 ACK / 重传机制,有更复杂的 ACK / 确认策略,集成了更现代的重传算法|
|
||||
|加密 / 安全|KCP 本身通常是“裸协议”或可选加密(上层或 FRP 在其上做加密)|QUIC 本身设计是**加密为默认**(TLS 集成在协议里),所有控制面与数据面都加密,更能防止中间人干扰、流量识别等问题|
|
||||
|开销 / 资源占用|相对轻量;因为协议栈简单,控制开销、头部开销较低|更复杂,协议处理开销更大(比如加密、握手、流管理、ACK 处理等)|
|
||||
|在极端场景 / 性能瓶颈下表现|在高延迟 + 大包 / 大数据量 + 丢包环境下,KCP 性能可能下降。比如有测试指出在**高延迟 + 发送超过 MTU 的数据**时,KCP 的延迟会显著恶化。 ([GitHub](https://github.com/xtaci/kcp-go/issues/204?utm_source=chatgpt.com "对比TCP/QUIC/KCP的测试结果 · Issue #204 · xtaci/kcp-go - GitHub"))|在这些情况下,QUIC 的设计通常更稳健一些,更能处理大流量、多个流、多并发场景|
|
||||
|
||||
从这些对比来看,QUIC 在协议设计上更“现代”、更全面、更关注安全性与多路复用,而 KCP 更加轻量、灵活,适合对实时性、低延迟要求比较高的场景。
|
||||
|
||||
不过,协议设计优劣并不是绝对的,具体在 FRP 的应用场景下,还要考虑实现、参数调优、网络环境等因素。
|
||||
|
||||
---
|
||||
|
||||
## 在 FRP 中:QUIC 模式 vs KCP 模式的区别与表现
|
||||
|
||||
FRP 支持将底层通信协议从默认的 TCP / HTTPTransport 切换成 KCP 或 QUIC(即 `transport.protocol = "kcp"` 或 `"quic"`)模式。官方文档里对此有说明。 ([gofrp.org](https://gofrp.org/en/docs/features/common/network/network/?utm_source=chatgpt.com "Communication Security and Optimization | frp"))
|
||||
|
||||
下面是两种模式在 FRP 中常见的差别、优缺点、使用注意。
|
||||
|
||||
### 优点 / 劣势对比
|
||||
|
||||
|特性 / 维度|FRP + KCP 模式|FRP + QUIC 模式|
|
||||
|---|---|---|
|
||||
|延迟表现 / 实时响应|通常优于 TCP,尤其在丢包 / 抖动较大、网络不稳定的环境下。很多用户用 KCP 模式来加速远程桌面、SSH 等。 ([CSDN博客](https://blog.csdn.net/MENGHUANBEIKE/article/details/100793876?utm_source=chatgpt.com "利用frp内网穿透kcp模式做跳板,加速流畅访问远程桌面3389"))|在连接建立、短连接场景以及多流场景中,QUIC 有优势;也能在某些网络被封、被干扰的情况下更抗阻断性(因为包头更难识别)|
|
||||
|抗网络干扰 / 被封锁风险|KCP 尽管在 UDP 上传输,但协议本身特征可能比较容易被识别;在被运营商主动干扰 UDP 流量的环境中可能被限速或封锁|QUIC 本身带加密和混淆特性,更难被中间设备识别,某些用户用 QUIC 模式来规避“被运营商阻断 FRP TCP 连接”的情况。比如有一篇博客指出,TCP 模式在某些校园网或被 ISP 限制时连接失败,而改用 QUIC 协议就能恢复连接。 ([ZRHan's Blog](https://blog.zrhan.top/2024/07/14/%E7%94%A8quic%E5%8D%8F%E8%AE%AE%E8%A7%A3%E5%86%B3frp%E8%A2%AB%E8%BF%90%E8%90%A5%E5%95%86%E9%98%BB%E6%96%AD%E7%9A%84%E9%97%AE%E9%A2%98/?utm_source=chatgpt.com "用quic协议解决frp被运营商阻断的问题 - ZRHan's Blog"))|
|
||||
|带宽开销 / 效率|KCP 为了追求低延迟,会在一定程度上牺牲一些带宽效率(可能会有额外重传、冗余、控制包开销等) ([gofrp.org](https://gofrp.org/en/docs/features/common/network/network/?utm_source=chatgpt.com "Communication Security and Optimization \| frp"))|QUIC 在设计上对带宽利用率考虑较多,控制与重传机制更复杂,会在多流、大连接场景下更高效|
|
||||
|多代理 / 多连接 / 并发|KCP 模式下,如果你在一个 FRP 实例下跑很多代理 / 多条连接,每条连接都是一条 KCP,会有一定管理开销|QUIC 的多流 / 多路复用设计可以在一个连接上承载多个代理或多个数据流,更理想地减少连接数和开销|
|
||||
|实现稳定性 / Bug 风险|KCP 在 FRP 中被使用较久、成熟度较高,用户社区案例较多,参数调优经验丰富|QUIC 是相对较新、更复杂的模式,在 FRP 中可能还存在一些 bug 或限制;例如,有 issue 提到 “无法单独运行在 QUIC 模式” 的问题——无论是否配置 bind_port 字段,frps 总会监听 TCP 端口(这个 issue 在 FRP 的 issue 列表里被关闭为“不计划修复”)([GitHub](https://github.com/fatedier/frp/issues/3225?utm_source=chatgpt.com "无法单独运行在quic模式 · Issue #3225 · fatedier/frp · GitHub"))|
|
||||
|参数调优空间 / 灵活性|KCP 的很多参数(MTU、窗口、nodelay、重传策略等)都可以调,灵活性高;但需要手动调优以适应网络环境|QUIC 的很多机制是在协议内部做的(如流控、拥塞控制、加密层),对用户的参数调控空间可能较少(或复杂)|
|
||||
|适用场景|需要低延迟或在弱网络环境里工作的服务(如 SSH、RDP、交互式服务)|多流服务、HTTP(s) 代理、长连接传输、多代理复用、抗干扰场合等可能更适合|
|
||||
|
||||
综合来看:如果你现在主要是用 FRP 来做一些 SSH、远程桌面、交互性强的服务,或者网络环境不稳定/丢包率高,KCP 模式可能是更稳定、可控的选择。而如果你需要让 FRP 在更复杂的网络环境下更具抗封锁性、更现代化、更高效,QUIC 模式是一个很有吸引力的选项。
|
||||
|
||||
### 在 FRP 上使用时的注意与限制
|
||||
|
||||
- 要让 QUIC / KCP 模式生效,你需要在 frps 和 frpc 两边都正确配置对应的端口(`quicBindPort` / `kcpBindPort` 等)与 `transport.protocol = "quic"` 或 `"kcp"`。否则默认仍用 TCP。 ([格态随记](https://blog.gotab.cn/archives/RvdYvu2g?utm_source=chatgpt.com "Frp:服务端 frps.toml 和 frpc.toml 客户端配置文件详解"))
|
||||
|
||||
- 防火墙 / UDP 端口必须开放 — 因为 KCP / QUIC 都是在 UDP 上运行的,无法简单依赖 TCP。
|
||||
|
||||
- 参数调优很关键:KCP 有很多参数(如 `nodelay`、`interval`、窗口大小、重传策略等)需要根据网络环境调整,否则可能效果不好或发生延迟爆炸。
|
||||
|
||||
- QUIC 模式在 FRP 中可能还不够成熟 / 存在一些边界 bug。例如前面提到的 “无法单独运行在 QUIC 模式” 问题就是一个。 ([GitHub](https://github.com/fatedier/frp/issues/3225?utm_source=chatgpt.com "无法单独运行在quic模式 · Issue #3225 · fatedier/frp · GitHub"))
|
||||
|
||||
- 在高带宽 / 长距离 /大流量场景下,KCP 模式可能表现不如预期,因为协议本身在大包 / 高延迟 + 数据量大时延迟会膨胀。实测中有用户发现,在延迟较高 + 发送大于 MTU 的数据场景下,QUIC 表现优于 KCP。 ([GitHub](https://github.com/xtaci/kcp-go/issues/204?utm_source=chatgpt.com "对比TCP/QUIC/KCP的测试结果 · Issue #204 · xtaci/kcp-go - GitHub"))
|
||||
|
||||
- 加密开销:如果你还在 FRP 上启用 TLS / 加密,QUIC 本身就有加密,叠加可能有额外开销;KCP 则通常是裸或可选加密,开销可能较低。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 总结 & 建议
|
||||
|
||||
- **优势倾向**
|
||||
- 若你更注重“稳定 + 可控 + 低延迟 + 在弱网环境下表现好”,KCP 是一个比较稳妥的选择(尤其已有成熟经验)
|
||||
- 若你更希望未来扩展性强、支持多流、抗干扰 / 抗封锁能力更好、连接更现代化,QUIC 是更有前景的选择
|
||||
|
||||
- **实用建议**
|
||||
1. 在你的网络环境下做对比测试:部署一个用 KCP,一个用 QUIC,测一下延迟、丢包率、吞吐量、稳定性,哪一个更符合你的场景就用哪个。
|
||||
2. 若你目前已有稳定的 KCP 环境且能满足需求,不必急于切换;只有在遇到 TCP 被封锁 / 性能瓶颈 / 抗干扰需求时,再考虑 QUIC。
|
||||
3. 在启用 QUIC(或 KCP)模式时,务必留一个“回退”方案(例如保留 TCP 模式),以防 QUIC 模式在某些网络环境下连接失败 / 不稳定。
|
||||
4. 多关注 FRP 的版本更新、issue 列表,特别是关于 QUIC 模式的 bug 或兼容性问题。
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
|
||||
---
|
||||
|
||||
## 1) frps(公网服务器)配置
|
||||
|
||||
编辑 `frps.toml`,让 frps 监听一个 HTTP CONNECT 复用端口(你希望是 8022 就设 8022):
|
||||
|
||||
```toml
|
||||
bindPort = 7000
|
||||
|
||||
# 关键:tcpmux 复用入口端口
|
||||
tcpmuxHTTPConnectPort = 8022
|
||||
```
|
||||
|
||||
重启 frps
|
||||
这个 8022 **不是直接 SSH 端口**,而是“HTTP CONNECT 复用入口”
|
||||
|
||||
---
|
||||
|
||||
## 2) 两台内网主机的 frpc 配置
|
||||
|
||||
### 主机 A(big.smy.top)
|
||||
|
||||
`frpc.toml`:
|
||||
|
||||
```toml
|
||||
serverAddr = "你的公网IP"
|
||||
serverPort = 7000
|
||||
|
||||
[[proxies]]
|
||||
name = "ssh_big"
|
||||
type = "tcpmux"
|
||||
multiplexer = "httpconnect"
|
||||
customDomains = ["big.smy.top"]
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 22
|
||||
```
|
||||
|
||||
### 主机 B(small.smy.top)
|
||||
|
||||
`frpc.toml`:
|
||||
|
||||
```toml
|
||||
serverAddr = "你的公网IP"
|
||||
serverPort = 7000
|
||||
|
||||
[[proxies]]
|
||||
name = "ssh_small"
|
||||
type = "tcpmux"
|
||||
multiplexer = "httpconnect"
|
||||
customDomains = ["small.smy.top"]
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 22
|
||||
```
|
||||
|
||||
`customDomains` 就是用来区分路由的域名键
|
||||
|
||||
---
|
||||
|
||||
## 3) DNS 解析
|
||||
|
||||
把
|
||||
|
||||
- `big.smy.top`
|
||||
- `small.smy.top`
|
||||
|
||||
|
||||
都 **A 记录解析到同一台公网 frps 的 IP**。
|
||||
|
||||
---
|
||||
|
||||
## 4) 连接方式
|
||||
|
||||
因为 tcpmux 依赖 **HTTP CONNECT** 来告诉 frps 目标域名,所以你不能这样直连:
|
||||
|
||||
```bash
|
||||
ssh big.smy.top -p 8022 # ❌ 这样 frps 不知道该转给谁
|
||||
```
|
||||
|
||||
要用 ProxyCommand(官方示例用 socat):
|
||||
|
||||
```bash
|
||||
ssh -o 'ProxyCommand=socat - PROXY:公网IP:%h:%p,proxyport=8022' test@big.smy.top
|
||||
ssh -o 'ProxyCommand=socat - PROXY:公网IP:%h:%p,proxyport=8022' test@small.smy.top
|
||||
```
|
||||
|
||||
frps 会根据 `%h`(也就是 big / small 域名)分流到对应主机
|
||||
|
||||
---
|
||||
|
||||
## 5) 更舒服的用法:写到你的 `~/.ssh/config`
|
||||
|
||||
```sshconfig
|
||||
Host big
|
||||
HostName big.smy.top
|
||||
User test
|
||||
ProxyCommand socat - PROXY:公网IP:%h:%p,proxyport=8022
|
||||
|
||||
Host small
|
||||
HostName small.smy.top
|
||||
User test
|
||||
ProxyCommand socat - PROXY:公网IP:%h:%p,proxyport=8022
|
||||
```
|
||||
|
||||
以后直接:
|
||||
|
||||
```bash
|
||||
ssh big
|
||||
ssh small
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
因为 SSH 本身无法携带“我要去哪个域名后端”的信息给 frps
|
||||
tcpmux 是目前 frp 官方唯一的“同端口分流 SSH”方案
|
||||
|
||||
---
|
||||
@@ -1,42 +0,0 @@
|
||||
# JavaScript趣味题 #128
|
||||
|
||||
> 导出时间: 2025/8/29 14:32:49
|
||||
|
||||
## 题目
|
||||
|
||||
输出什么?
|
||||
|
||||
## 代码
|
||||
|
||||
```javascript
|
||||
const name = "Lydia Hallie";
|
||||
const age = 21;
|
||||
|
||||
console.log(Number.isNaN(name));
|
||||
console.log(Number.isNaN(age));
|
||||
|
||||
console.log(isNaN(name));
|
||||
console.log(isNaN(age));
|
||||
```
|
||||
|
||||
## 选项
|
||||
|
||||
A. A: `true` `false` `true` `false`
|
||||
B. B: `true` `false` `false` `false`
|
||||
C. C: `false` `false` `true` `false` ✅
|
||||
D. D: `false` `true` `false` `true`
|
||||
|
||||
## 正确答案
|
||||
|
||||
**C**
|
||||
|
||||
## 答案解析
|
||||
|
||||
通过方法 `Number.isNaN`,你可以检测你传递的值是否为 _数字值_ 并且是否等价于 `NaN`。`name` 不是一个数字值,因此 `Number.isNaN(name)` 返回 `false`。`age` 是一个数字值,但它不等价于 `NaN`,因此 `Number.isNaN(age)` 返回 `false`.
|
||||
|
||||
通过方法 `isNaN`,你可以检测你传递的值是否一个 number。`name` 不是一个 `number`,因此 `isNaN(name)` 返回 `true`. `age` 是一个 `number` 因此 `isNaN(age)` 返回 `false`.
|
||||
|
||||
---
|
||||
|
||||
*本题目来源于JavaScript趣味题集合*
|
||||
*导出工具: JavaScript趣味题网页版*
|
||||
@@ -1,46 +0,0 @@
|
||||
# JavaScript趣味题 #18
|
||||
|
||||
> 导出时间: 2025/8/29 14:53:34
|
||||
|
||||
## 题目
|
||||
|
||||
输出是什么?
|
||||
|
||||
## 代码
|
||||
|
||||
```javascript
|
||||
function checkAge(data) {
|
||||
if (data === { age: 18 }) {
|
||||
console.log('You are an adult!')
|
||||
} else if (data == { age: 18 }) {
|
||||
console.log('You are still an adult.')
|
||||
} else {
|
||||
console.log(`Hmm.. You don't have an age I guess`)
|
||||
}
|
||||
}
|
||||
|
||||
checkAge({ age: 18 })
|
||||
```
|
||||
|
||||
## 选项
|
||||
|
||||
A. A: `You are an adult!`
|
||||
B. B: `You are still an adult.`
|
||||
C. C: `Hmm.. You don't have an age I guess` ✅
|
||||
|
||||
## 正确答案
|
||||
|
||||
**C**
|
||||
|
||||
## 答案解析
|
||||
|
||||
在测试相等性时,基本类型通过它们的值(value)进行比较,而对象通过它们的引用(reference)进行比较。JavaScript 检查对象是否具有对内存中相同位置的引用。
|
||||
|
||||
题目中我们正在比较的两个对象不是同一个引用:作为参数传递的对象引用的内存位置,与用于判断相等的对象所引用的内存位置并不同。
|
||||
|
||||
这也是 `{ age: 18 } === { age: 18 }` 和 `{ age: 18 } == { age: 18 }` 都返回 `false` 的原因。
|
||||
|
||||
---
|
||||
|
||||
*本题目来源于JavaScript趣味题集合*
|
||||
*导出工具: JavaScript趣味题网页版*
|
||||
@@ -1,39 +0,0 @@
|
||||
# JavaScript趣味题 #28
|
||||
|
||||
> 导出时间: 2025/8/29 14:28:13
|
||||
|
||||
## 题目
|
||||
|
||||
输出是什么?
|
||||
|
||||
## 代码
|
||||
|
||||
```javascript
|
||||
String.prototype.giveLydiaPizza = () => {
|
||||
return 'Just give Lydia pizza already!'
|
||||
}
|
||||
|
||||
const name = 'Lydia'
|
||||
|
||||
name.giveLydiaPizza()
|
||||
```
|
||||
|
||||
## 选项
|
||||
|
||||
A. A: `"Just give Lydia pizza already!"` ✅
|
||||
B. B: `TypeError: not a function`
|
||||
C. C: `SyntaxError`
|
||||
D. D: `undefined`
|
||||
|
||||
## 正确答案
|
||||
|
||||
**A**
|
||||
|
||||
## 答案解析
|
||||
|
||||
`String` 是内置的构造函数,我们可以向它添加属性。我只是在它的原型中添加了一个方法。基本类型字符串被自动转换为字符串对象,由字符串原型函数生成。因此,所有 string(string 对象) 都可以访问该方法!
|
||||
|
||||
---
|
||||
|
||||
*本题目来源于JavaScript趣味题集合*
|
||||
*导出工具: JavaScript趣味题网页版*
|
||||
@@ -1,60 +0,0 @@
|
||||
游戏玩家数据储存在普通JSON文件和MongoDB中各有其适用场景,二者的优缺点对比可结合游戏数据的特性(如数据量、读写频率、结构灵活性、并发需求等)展开分析:
|
||||
|
||||
一、普通JSON文件储存
|
||||
|
||||
普通JSON文件储存指将玩家数据以JSON格式写入本地文件(单文件或多文件,如按玩家ID分文件),依赖文件系统直接读写。
|
||||
|
||||
优点
|
||||
|
||||
1. 简单轻量,开发成本低
|
||||
无需部署数据库服务,直接通过编程语言的文件操作API(如Python的 json 模块、Node.js的 fs 模块)即可读写,适合小型团队或开发初期快速验证功能,学习成本几乎为0。
|
||||
2. 资源占用少
|
||||
仅依赖文件系统,不消耗额外的数据库进程资源(如内存、CPU),适合设备性能有限的场景(如单机小游戏、轻量网页游戏)。
|
||||
3. 数据直观可见
|
||||
JSON文件为文本格式,可直接用编辑器打开查看或修改,便于开发调试(如临时修改玩家数据测试功能)。
|
||||
|
||||
缺点
|
||||
|
||||
1. 读写效率低,不适合大数据量
|
||||
- 每次读写需加载整个JSON文件到内存解析(或写入时覆盖整个文件),当玩家数据量增大(如10万+玩家),单文件体积膨胀,读写耗时会急剧增加(例如加载100MB的JSON文件可能需要数百毫秒)。
|
||||
- 若按玩家ID分文件,虽能缓解单文件压力,但批量查询(如排行榜、全服统计)需遍历所有文件,效率极低。
|
||||
2. 并发安全问题突出
|
||||
多玩家同时操作时(如多人在线游戏的并发读写),文件系统缺乏原生锁机制,易出现“写覆盖”(如A玩家写入时B玩家同时写入,导致其中一方数据丢失)或数据损坏(如写入中断导致JSON格式错误)。
|
||||
3. 结构扩展性差
|
||||
若玩家数据结构迭代(如新增“宠物属性”“公会信息”),需手动处理旧JSON文件的格式兼容(如遍历所有文件添加新字段),操作繁琐且易出错。
|
||||
4. 缺乏数据一致性保障
|
||||
无事务支持,例如玩家“扣金币+加道具”的原子操作,若中途程序崩溃,可能导致金币已扣但道具未加,数据不一致。
|
||||
|
||||
二、MongoDB储存
|
||||
|
||||
MongoDB是文档型NoSQL数据库,以BSON(JSON的二进制扩展)格式存储数据,天然适配半结构化的玩家数据。
|
||||
|
||||
优点
|
||||
|
||||
1. 高效读写与查询能力
|
||||
- 支持索引(如按玩家ID、等级建索引),可快速定位单玩家数据(毫秒级),解决JSON文件全量解析的性能问题。
|
||||
- 支持复杂查询(如“等级>50且在线的玩家”“道具列表包含某物品的玩家”),适合游戏中的排行榜、好友列表、任务统计等场景。
|
||||
2. 良好的并发与一致性
|
||||
- 内置多版本并发控制(MVCC),支持高并发读写( thousands of TPS),避免JSON文件的并发冲突。
|
||||
- 支持事务(MongoDB 4.0+),可保证“扣钱+加道具”“升级+解锁技能”等操作的原子性,防止数据不一致。
|
||||
3. 灵活扩展,适配数据增长
|
||||
- 玩家数据量随用户增长时,可通过“分片”横向扩展(将数据分布到多台服务器),支持百万级甚至亿级玩家数据。
|
||||
- 文档结构动态灵活,新增字段无需修改表结构,旧数据可兼容(查询时自动忽略不存在的字段),适配游戏版本迭代。
|
||||
4. 完善的数据管理能力
|
||||
提供备份( mongodump )、恢复( mongorestore )、监控(MongoDB Atlas)等工具,支持数据压缩、过期索引(如清理离线玩家缓存),适合长期运行的在线游戏。
|
||||
|
||||
缺点
|
||||
|
||||
1. 运维与学习成本高
|
||||
需要部署、维护数据库服务(如集群配置、索引优化、故障排查),对小型团队可能增加运维压力;开发人员需学习MongoDB的查询语法(如 find 、 aggregate )、索引设计等,门槛高于JSON文件。
|
||||
2. 资源占用较高
|
||||
相比JSON文件,MongoDB需要额外的内存(缓存索引和热数据)、CPU(处理查询)和磁盘空间(数据文件+日志),不适合资源受限的极简场景(如嵌入式小游戏)。
|
||||
3. 过度设计风险
|
||||
对于单机游戏、玩家数极少(如几百人)且无复杂查询的场景,MongoDB的优势无法体现,反而因配置复杂降低开发效率。
|
||||
|
||||
总结:适用场景
|
||||
|
||||
- 普通JSON文件:适合单人游戏、小型休闲游戏、开发测试阶段,或数据量极小(<1万玩家)、读写频率低、无复杂查询的场景。
|
||||
- MongoDB:适合多人在线游戏(MMO、MOBA等)、中大型游戏,或玩家数多(>1万)、需高频读写、复杂查询、结构迭代频繁的场景。
|
||||
|
||||
实际开发中,也可根据规模过渡:初期用JSON文件快速上线,待用户增长后迁移至MongoDB,利用其扩展性支撑业务。
|
||||
@@ -1,118 +0,0 @@
|
||||
除了索引之外,MongoDB 在 CRUD 操作优化上还有多种有效策略,尤其在处理海量数据时更为关键。以下是综合各技术文档和实践总结的核心优化方法:
|
||||
|
||||
---
|
||||
|
||||
### 🔄 一、批量操作优化
|
||||
1. **使用 `bulkWrite` 替代单条操作**
|
||||
通过批量写入减少网络开销和事务开销。基于 `ReplaceOneModel` 启用 `upsert`,实现存在更新、不存在插入的高效操作:
|
||||
```java
|
||||
public void batchSave(List<?> dataList) {
|
||||
List<ReplaceOneModel<Document>> bulkOps = dataList.stream()
|
||||
.map(item -> {
|
||||
Document doc = convertToDocument(item); // 转换为Document
|
||||
return new ReplaceOneModel<>(
|
||||
Filters.eq("_id", doc.get("_id")),
|
||||
doc,
|
||||
new UpdateOptions().upsert(true)
|
||||
);
|
||||
}).collect(Collectors.toList());
|
||||
collection.bulkWrite(bulkOps); // 批量执行
|
||||
}
|
||||
```
|
||||
此方法将多条操作合并为一次请求,写入性能提升显著。
|
||||
|
||||
---
|
||||
|
||||
### 📖 二、分页查询优化
|
||||
1. **避免精确 `count`**
|
||||
当数据量超大时,`count` 可能极慢。采用阈值法:若跳过 `MAX_PAGE_COUNT`(如1万条)后仍有数据,则返回阈值提示“数据超过1万条”,避免全表扫描:
|
||||
```java
|
||||
private long approxCount(MongoTemplate mongoTemplate, Query query) {
|
||||
query = query.with(PageRequest.of(MAX_PAGE_COUNT, 1));
|
||||
return mongoTemplate.find(query, Entity.class).isEmpty()
|
||||
? mongoTemplate.count(query)
|
||||
: MAX_PAGE_COUNT;
|
||||
}
|
||||
。
|
||||
```
|
||||
|
||||
2. **用条件替代 `skip`**
|
||||
深度分页时(如第100页),避免 `skip(9900)`。改为记录上一页末尾的排序字段值(如时间戳),作为下一页查询条件:
|
||||
```sql
|
||||
-- 原查询:db.collection.find().sort({time:-1}).skip(9900).limit(100)
|
||||
-- 优化后:
|
||||
db.collection.find({ time: { $lt: lastPageEndTime } })
|
||||
.sort({ time: -1 })
|
||||
.limit(100);
|
||||
```
|
||||
此方法将 **O(N)** 的跳过操作转为 **O(1)** 的条件过滤。
|
||||
|
||||
---
|
||||
|
||||
### 📤 三、全量导出优化
|
||||
1. **字段投影减少数据传输**
|
||||
仅查询必要字段,降低网络与内存开销:
|
||||
```java
|
||||
Query query = new Query();
|
||||
query.fields().include("_id").include("name"); // 只返回_id和name。
|
||||
```
|
||||
|
||||
2. **流式处理替代全量加载**
|
||||
使用 `stream()` 逐条处理数据,避免 `findAll` 导致内存溢出:
|
||||
```java
|
||||
try (CloseableIterator<Entity> iter = mongoTemplate.stream(query, Entity.class)) {
|
||||
while (iter.hasNext()) {
|
||||
process(iter.next()); // 单条处理
|
||||
}
|
||||
}
|
||||
```
|
||||
相比分页查询,流式处理无 `skip` 开销,且内存占用恒定。
|
||||
|
||||
---
|
||||
|
||||
### 🧩 四、文档设计优化
|
||||
1. **打破第三范式**
|
||||
- **冗余字段**:将高频查询的关联字段(如部门名称)冗余到主文档,避免联表查询。
|
||||
- **内嵌设计**:一对多关系直接嵌套子文档(如订单内嵌商品列表),提升读取效率。但需注意文档大小限制(16MB)。
|
||||
- **引用设计**:多对多关系使用ID数组而非完整嵌套,避免文档膨胀:
|
||||
```json
|
||||
// 学生文档
|
||||
{
|
||||
"_id": "s001",
|
||||
"name": "Alice",
|
||||
"teachers": ["t01", "t02"] // 仅存储ID
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ⚙️ 五、分片集群优化
|
||||
1. **碎片整理(MongoDB <7.0)**
|
||||
在早期版本中,分片集合可能因频繁写入产生碎片化小数据块,导致CRUD延迟。通过 `configureCollectionBalancing` 命令合并数据块,但需注意:
|
||||
- 整理期间可能短暂阻塞元数据更新。
|
||||
- MongoDB 7.0+ 已支持自动合并,通常无需手动操作。
|
||||
|
||||
2. **负载均衡窗口设置**
|
||||
在业务低峰期触发负载均衡器迁移数据块,减少对CRUD的影响。
|
||||
|
||||
---
|
||||
|
||||
### 💎 六、其他关键技巧
|
||||
1. **合理控制事务范围**:短事务减少锁竞争。
|
||||
2. **写入确认级别调整**:对非关键数据使用 `w:0`(无确认),提升写入速度(牺牲一致性)。
|
||||
3. **TTL索引自动清理**:为临时数据(如日志)设置过期时间,减少存储压力。
|
||||
|
||||
---
|
||||
|
||||
### 💎 优化方法效果对比表
|
||||
| **优化方向** | **适用场景** | **性能提升效果** | **实现复杂度** |
|
||||
|--------------------|--------------------------|----------------------|--------------|
|
||||
| **批量写入** | 数据导入、批量更新 | ⭐⭐⭐⭐⭐ (极高) | ⭐⭐ (中等) |
|
||||
| **流式导出** | 大数据量导出 | ⭐⭐⭐⭐ (避免OOM) | ⭐ (简单) |
|
||||
| **条件分页** | 深度分页(>100页) | ⭐⭐⭐⭐ (O(1) 跳转) | ⭐⭐ (中等) |
|
||||
| **冗余字段** | 高频关联查询 | ⭐⭐⭐ (减少联表) | ⭐⭐⭐ (较高) |
|
||||
| **分片碎片整理** | MongoDB 6.0以下分片集群 | ⭐⭐ (减少延迟) | ⭐⭐⭐⭐ (复杂) |
|
||||
|
||||
---
|
||||
|
||||
> 💡 **实践建议**:优先从**批量操作**和**分页策略**入手,这两类优化代码改动小且收益显著。海量数据场景下,**流式处理+字段投影**是导出标配方案。分片集群升级到 MongoDB 7.0+ 可减少运维负担。
|
||||
@@ -1,123 +0,0 @@
|
||||
|
||||
接下来我们来 **一步步给 MongoDB 添加用户账户(角色管理)**。
|
||||
|
||||
假设你希望创建一个:
|
||||
- 用户名:`shumengya`
|
||||
- 密码:`shumengya520`
|
||||
- 权限:对整个数据库系统有管理权限(root 权限)
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🧭 步骤 1:进入 MongoDB Shell
|
||||
|
||||
MongoDB 7.x 之后使用的是新客户端 `mongosh`:
|
||||
|
||||
```bash
|
||||
mongosh
|
||||
```
|
||||
|
||||
如果提示 `command not found`,可以执行:
|
||||
|
||||
```bash
|
||||
mongo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧩 步骤 2:切换到 `admin` 数据库
|
||||
|
||||
(MongoDB 的用户管理都存在 `admin` 数据库里)
|
||||
|
||||
```javascript
|
||||
use admin
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧰 步骤 3:创建管理员用户
|
||||
|
||||
运行下面的命令创建一个超级管理员账号:
|
||||
|
||||
```javascript
|
||||
db.createUser({
|
||||
user: "shumengya",
|
||||
pwd: "shumengya520",
|
||||
roles: [ { role: "root", db: "admin" } ]
|
||||
})
|
||||
```
|
||||
|
||||
✅ 成功后会输出:
|
||||
|
||||
```
|
||||
Successfully added user: {
|
||||
"user" : "shumengya",
|
||||
"roles" : [ { "role" : "root", "db" : "admin" } ]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 步骤 4:启用身份验证
|
||||
|
||||
编辑 MongoDB 的配置文件:
|
||||
|
||||
```bash
|
||||
sudo nano /etc/mongod.conf
|
||||
```
|
||||
|
||||
找到或添加以下部分(注意缩进):
|
||||
|
||||
```yaml
|
||||
security:
|
||||
authorization: enabled
|
||||
```
|
||||
|
||||
保存退出后,重启 MongoDB:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart mongod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 步骤 5:验证登录是否成功
|
||||
|
||||
```bash
|
||||
mongosh -u shumengya -p shumengya520 --authenticationDatabase admin
|
||||
```
|
||||
|
||||
如果看到类似:
|
||||
|
||||
```
|
||||
test>
|
||||
```
|
||||
|
||||
说明登录成功 ✅
|
||||
|
||||
---
|
||||
|
||||
## 💡 可选:为特定数据库创建普通用户
|
||||
|
||||
比如你有个业务数据库叫 `farmgame`,可以创建普通读写用户:
|
||||
|
||||
```javascript
|
||||
use farmgame
|
||||
db.createUser({
|
||||
user: "gameuser",
|
||||
pwd: "gamepass123",
|
||||
roles: [ { role: "readWrite", db: "farmgame" } ]
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧱 总结
|
||||
|
||||
|操作|命令或说明|
|
||||
|---|---|
|
||||
|创建管理员|`db.createUser({user: "shumengya", pwd: "...", roles: [{role: "root", db: "admin"}]})`|
|
||||
|开启认证|`/etc/mongod.conf` → `security.authorization: enabled`|
|
||||
|登录验证|`mongosh -u shumengya -p shumengya520 --authenticationDatabase admin`|
|
||||
|
||||
---
|
||||
@@ -1,85 +0,0 @@
|
||||
|
||||
**索引的核心作用:**
|
||||
|
||||
1. **大幅减少查询需要扫描的文档数量:** 没有索引时,MongoDB 必须执行集合扫描(`COLLSCAN`),即检查集合中的*每一个*文档。有了合适的索引,MongoDB 可以使用索引扫描(`IXSCAN`)快速定位到包含所需数据的文档位置。
|
||||
2. **加速排序:** 如果排序字段包含在索引中,MongoDB 可以直接利用索引中已经排好的顺序返回结果,避免昂贵的在内存中排序。
|
||||
3. **支持高效的数据去重:** `$group` 聚合阶段的分组操作可以利用索引。
|
||||
4. **实现覆盖查询:** 如果查询只需要返回索引中包含的字段,MongoDB 可以*完全*从索引中获取结果,无需去读取实际的文档数据,速度极快。
|
||||
|
||||
**MongoDB 支持的索引类型:**
|
||||
|
||||
1. **单字段索引:**
|
||||
* 最基本的索引类型,在单个字段上创建。
|
||||
* 示例:`db.collection.createIndex({ name: 1 })` (1 表示升序,-1 表示降序。对于纯等值查询,顺序通常不重要;对于排序查询,顺序很重要)。
|
||||
|
||||
2. **复合索引:**
|
||||
* 在多个字段上创建的索引。
|
||||
* 字段的顺序**极其重要**。它决定了索引如何组织和哪些查询模式可以利用该索引(最左前缀原则)。
|
||||
* 示例:`db.collection.createIndex({ status: 1, order_date: -1 })`。这个索引可以高效支持:
|
||||
* 只查询 `status` 的查询
|
||||
* 同时查询 `status` 和 `order_date` 的查询
|
||||
* 查询 `status` 并按 `order_date` 排序的查询
|
||||
* 但不支持只查询 `order_date` 的查询(不符合最左前缀)。
|
||||
|
||||
3. **多键索引:**
|
||||
* 当索引字段是数组时,MongoDB 会自动为数组中的每个元素创建索引条目。
|
||||
* 用于高效查询数组字段中的元素。
|
||||
* 示例:索引 `db.collection.createIndex({ tags: 1 })` 可以高效支持查询 `db.collection.find({ tags: "mongodb" })`。
|
||||
|
||||
4. **地理空间索引:**
|
||||
* **2dsphere:** 用于查询存储为 GeoJSON 对象或传统坐标对的地理空间数据(地球球面几何)。支持邻近查询(`$near`)、包含查询(`$geoWithin`)、相交查询(`$geoIntersects`)等。
|
||||
* **2d:** 用于在二维平面上(如地图游戏)查询存储为传统坐标对的数据。主要用于平面几何计算。
|
||||
|
||||
5. **文本索引:**
|
||||
* 支持对字符串或字符串数组字段的内容进行文本搜索。
|
||||
* 支持词干提取、停用词过滤等基本文本处理功能。
|
||||
* 示例:`db.collection.createIndex({ description: "text" })`,然后使用 `$text` 操作符进行搜索。
|
||||
|
||||
6. **哈希索引:**
|
||||
* 对字段值进行哈希运算,并在哈希值上建立索引。
|
||||
* 主要用途是为**分片键**提供更均匀的数据分布(使用`hashed`分片策略时)。
|
||||
* 只支持等值匹配查询,不支持范围查询、排序或其他操作。
|
||||
* 示例:`db.collection.createIndex({ _id: "hashed" })`。
|
||||
|
||||
7. **通配符索引:**
|
||||
* 可以索引文档中未知或任意字段。适用于模式动态变化的场景。
|
||||
* 示例:
|
||||
* `db.collection.createIndex({ "userMetadata.$**": 1 })` 索引 `userMetadata` 子文档中的所有字段。
|
||||
* `db.collection.createIndex({ "$**": 1 })` 索引文档中的所有字段(谨慎使用,开销大)。
|
||||
|
||||
8. **唯一索引:**
|
||||
* 强制索引字段的值在整个集合中是唯一的(`_id` 字段默认就有唯一索引)。
|
||||
* 可以用于单字段或复合字段。
|
||||
* 示例:`db.collection.createIndex({ email: 1 }, { unique: true })`。
|
||||
|
||||
9. **TTL 索引:**
|
||||
* 一种特殊的单字段索引,用于在指定时间后或在指定时间点自动从集合中删除文档。字段必须是日期类型或包含日期元素的数组。
|
||||
* 适用于会话数据、日志、临时数据等。
|
||||
* 示例:`db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })` (文档在 `createdAt` 时间之后 3600 秒/1 小时被删除)。
|
||||
|
||||
10. **稀疏索引:**
|
||||
* 只包含具有索引字段的文档的条目。即使索引字段值为 `null`,也会包含在内;但如果文档*完全缺失*该索引字段,则不会被索引。
|
||||
* 节省空间,提高索引效率(当字段在大部分文档中缺失时)。
|
||||
* 示例:`db.collection.createIndex({ optionalField: 1 }, { sparse: true })`。
|
||||
|
||||
**如何管理和使用索引:**
|
||||
|
||||
1. **创建索引:** 使用 `db.collection.createIndex()` 方法。
|
||||
* 示例:`db.products.createIndex({ category: 1, price: -1 })`
|
||||
2. **查看索引:** 使用 `db.collection.getIndexes()` 方法。
|
||||
3. **删除索引:** 使用 `db.collection.dropIndex()` 或 `db.collection.dropIndexes()` 方法。
|
||||
4. **分析查询性能:** 使用 `explain()` 方法查看查询的执行计划,确认是否使用了索引(`IXSCAN`)以及使用了哪个索引。这是优化查询的关键步骤。
|
||||
* 示例:`db.orders.find({ status: "A", amount: { $gt: 100 } }).sort({ order_date: -1 }).explain("executionStats")`
|
||||
5. **索引管理最佳实践:**
|
||||
* **为常用查询模式创建索引:** 分析你的应用最常见的查询(`find`, `sort`, `aggregate`中的`$match`, `$group`, `$sort`等阶段),为这些查询涉及的字段创建合适的索引(通常是复合索引)。
|
||||
* **遵循最左前缀原则:** 设计复合索引时,将最常用于过滤或排序的字段放在左边。
|
||||
* **考虑选择性:** 选择性高的字段(如唯一值多的字段)放在复合索引的前面通常更有效。
|
||||
* **使用覆盖查询:** 尽量让查询只返回索引中包含的字段。
|
||||
* **监控索引使用率:** MongoDB Profiler 或 Atlas 性能监控可以查看索引的使用情况。使用率低的索引应考虑删除,因为它们会消耗写性能和存储空间。
|
||||
* **权衡读写性能:** 索引会加速读操作,但会减慢写操作(插入、更新、删除),因为写操作需要维护索引。不要过度索引。
|
||||
* **后台创建大索引:** 在大型集合上创建索引可能耗时很长并阻塞操作。使用 `{ background: true }` 选项可以在后台创建索引(虽然仍可能有性能影响,但不会完全阻塞读写)。
|
||||
* **合理使用内存:** 确保有足够的内存将常用索引(或其活跃部分)保存在内存中,避免频繁的磁盘 I/O。
|
||||
|
||||
**总结:**
|
||||
|
||||
MongoDB 提供了极其丰富和强大的索引类型(单字段、复合、多键、地理空间、文本、哈希、通配符、唯一、TTL、稀疏)来满足各种查询场景的优化需求。**创建和管理合适的索引是提升 MongoDB 查询性能最关键的手段。** 务必结合你的具体查询模式,使用 `explain()` 分析执行计划,遵循索引设计的最佳实践(特别是复合索引的最左前缀原则),并持续监控和调整索引策略。
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
|
||||
## 1️⃣ 数值类型(Numeric)
|
||||
|
||||
| 类型分类 | 数据类型 | 说明 |
|
||||
| ---- | --------------------------------------------------------- | ------------- |
|
||||
| 整数型 | `TINYINT`、`SMALLINT`、`MEDIUMINT`、`INT`/`INTEGER`、`BIGINT` | 存储不同范围的整数 |
|
||||
| 精确小数 | `DECIMAL(M,D)`、`NUMERIC` | 高精度定点数(常用于金额) |
|
||||
| 浮点数 | `FLOAT`、`DOUBLE`/`REAL` | 近似数值(科学计算) |
|
||||
| 位类型 | `BIT(M)` | 存储二进制位 |
|
||||
| | | |
|
||||
| | | |
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 日期与时间类型(Date & Time)
|
||||
|
||||
|数据类型|格式|说明|
|
||||
|---|---|---|
|
||||
|`DATE`|YYYY-MM-DD|日期|
|
||||
|`DATETIME(fsp)`|YYYY-MM-DD HH:MM:SS|日期 + 时间|
|
||||
|`TIMESTAMP(fsp)`|YYYY-MM-DD HH:MM:SS|时间戳(支持自动更新)|
|
||||
|`TIME`|HH:MM:SS|时间或时间间隔|
|
||||
|`YEAR`|YYYY|年份(2 位或 4 位)|
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 字符串与二进制类型(String & Binary)
|
||||
|
||||
|类型分类|数据类型|说明|
|
||||
|---|---|---|
|
||||
|定长字符串|`CHAR(M)`|定长,最大 255|
|
||||
|变长字符串|`VARCHAR(M)`|可变长度,最大 65535(受行大小限制)|
|
||||
|定长二进制|`BINARY(M)`|定长二进制|
|
||||
|变长二进制|`VARBINARY(M)`|变长二进制|
|
||||
|文本|`TINYTEXT`、`TEXT`、`MEDIUMTEXT`、`LONGTEXT`|存储大文本|
|
||||
|二进制大对象|`TINYBLOB`、`BLOB`、`MEDIUMBLOB`、`LONGBLOB`|存储二进制数据|
|
||||
|枚举|`ENUM('a','b',...)`|单选固定集合|
|
||||
|集合|`SET('a','b',...)`|多选固定集合|
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ 空间数据类型(Spatial)
|
||||
|
||||
|数据类型|说明|
|
||||
|---|---|
|
||||
|`GEOMETRY`|任意几何对象|
|
||||
|`POINT`|点|
|
||||
|`LINESTRING`|线|
|
||||
|`POLYGON`|多边形|
|
||||
|`MULTIPOINT`、`MULTILINESTRING`、`MULTIPOLYGON`、`GEOMETRYCOLLECTION`|组合空间对象|
|
||||
|
||||
---
|
||||
|
||||
## 5️⃣ JSON 类型
|
||||
|
||||
|数据类型|说明|
|
||||
|---|---|
|
||||
|`JSON`|存储 JSON 文档,支持索引与函数操作|
|
||||
|
||||
---
|
||||
@@ -1,339 +0,0 @@
|
||||
|
||||
# NSSM 使用教程(Windows 下的 systemctl 替代方案)
|
||||
> 适用场景:把 **控制台程序**(如 frpc、openlist、各种 exe/bat/cmd)
|
||||
> 做成 **开机自启 + 后台运行 + 崩溃自动重启 + 日志管理** 的 Windows 服务。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
1. NSSM 是什么
|
||||
2. 下载与准备
|
||||
3. 安装服务(GUI)
|
||||
4. 安装服务(纯命令行)
|
||||
5. 服务启停与状态查看
|
||||
6. 修改服务配置(GUI/命令行)
|
||||
7. 崩溃自动重启策略
|
||||
8. 日志捕获与轮转
|
||||
9. 权限、依赖与进程设置(可选)
|
||||
10. 卸载服务
|
||||
11. 常见问题与排错
|
||||
12. frpc/openlist 成品脚本示例
|
||||
|
||||
---
|
||||
|
||||
## 1. NSSM 是什么
|
||||
NSSM(Non-Sucking Service Manager)是一个 Windows Service Wrapper,能:
|
||||
- 把任何控制台程序包装成 Windows 服务
|
||||
- 进程退出/崩溃时自动重启
|
||||
- 捕获 stdout/stderr 写日志并支持轮转
|
||||
|
||||
---
|
||||
|
||||
## 2. 下载与准备
|
||||
1. 下载 NSSM 压缩包,解压
|
||||
2. 根据系统位数选择:
|
||||
- 64 位:`win64\nssm.exe`
|
||||
- 32 位:`win32\nssm.exe`
|
||||
3. 放到固定目录,例如:
|
||||
```
|
||||
|
||||
C:\tools\nssm\nssm.exe
|
||||
|
||||
````
|
||||
4. (推荐)将 `C:\tools\nssm` 加到系统 PATH
|
||||
|
||||
> **注意**:后续所有安装/启动/删除服务命令,都建议在 **管理员 CMD/PowerShell** 中执行。
|
||||
|
||||
---
|
||||
|
||||
## 3. 安装服务(GUI 最常用)
|
||||
管理员 CMD:
|
||||
|
||||
```bat
|
||||
nssm install frpc
|
||||
````
|
||||
|
||||
弹出 GUI 后在 **Application** 页填写:
|
||||
|
||||
- **Path**:程序路径
|
||||
例:`E:\控制台软件\frpc\frpc.exe`
|
||||
|
||||
- **Startup directory**:工作目录
|
||||
例:`E:\控制台软件\frpc`
|
||||
|
||||
- **Arguments**:启动参数
|
||||
例:`-c E:\控制台软件\frpc\frpc.ini`
|
||||
|
||||
|
||||
填完点 **Install service**。
|
||||
|
||||
---
|
||||
|
||||
## 4. 安装服务(纯命令行)
|
||||
|
||||
适合脚本化部署:
|
||||
|
||||
```bat
|
||||
nssm install frpc "E:\控制台软件\frpc\frpc.exe" "-c E:\控制台软件\frpc\frpc.ini"
|
||||
```
|
||||
|
||||
然后补充工作目录等:
|
||||
|
||||
```bat
|
||||
nssm set frpc AppDirectory "E:\控制台软件\frpc"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 服务启停与状态查看
|
||||
|
||||
```bat
|
||||
nssm start frpc
|
||||
nssm stop frpc
|
||||
nssm restart frpc
|
||||
nssm status frpc
|
||||
```
|
||||
|
||||
常见 `status` 结果:
|
||||
|
||||
- `SERVICE_RUNNING`:运行中
|
||||
|
||||
- `SERVICE_STOPPED`:已停止
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 6. 修改服务配置(GUI / 命令行)
|
||||
|
||||
### 6.1 GUI 编辑
|
||||
|
||||
```bat
|
||||
nssm edit frpc
|
||||
```
|
||||
|
||||
### 6.2 命令行修改(set/get/reset/dump)
|
||||
|
||||
通用格式:
|
||||
|
||||
```bat
|
||||
nssm set <服务名> <参数名> <值>
|
||||
nssm get <服务名> <参数名>
|
||||
nssm reset <服务名> <参数名>
|
||||
nssm dump <服务名>
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```bat
|
||||
nssm set frpc AppDirectory "E:\控制台软件\frpc"
|
||||
nssm get frpc AppDirectory
|
||||
nssm dump frpc
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 崩溃自动重启策略(两层保险)
|
||||
|
||||
### 7.1 NSSM 自带 Exit actions
|
||||
|
||||
GUI:**Exit actions** 标签页
|
||||
|
||||
- **AppExit Default** = `Restart`
|
||||
|
||||
- **AppRestartDelay** = `5000`(毫秒)
|
||||
|
||||
- **AppThrottle** = `1500`(默认,一般不用改)
|
||||
|
||||
|
||||
命令行等价:
|
||||
|
||||
```bat
|
||||
nssm set frpc AppExit Default Restart
|
||||
nssm set frpc AppRestartDelay 5000
|
||||
nssm set frpc AppThrottle 1500
|
||||
```
|
||||
|
||||
### 7.2 Windows 服务“恢复”策略(推荐开启)
|
||||
|
||||
`Win + R → services.msc`
|
||||
找到服务 → 属性 → **恢复**:
|
||||
|
||||
- 第一次失败:重新启动服务
|
||||
|
||||
- 第二次失败:重新启动服务
|
||||
|
||||
- 后续失败:重新启动服务
|
||||
|
||||
- 延迟:5~10 秒
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 8. 日志捕获与轮转
|
||||
|
||||
### 8.1 捕获 stdout / stderr
|
||||
|
||||
GUI:**I/O** 页
|
||||
|
||||
- **Output (stdout)**:`C:\logs\frpc.out.log`
|
||||
|
||||
- **Error (stderr)**:`C:\logs\frpc.err.log`
|
||||
|
||||
|
||||
命令行:
|
||||
|
||||
```bat
|
||||
nssm set frpc AppStdout "C:\logs\frpc.out.log"
|
||||
nssm set frpc AppStderr "C:\logs\frpc.err.log"
|
||||
```
|
||||
|
||||
### 8.2 日志轮转
|
||||
|
||||
GUI:**File rotation** 页
|
||||
|
||||
- 勾 `Rotate while service is running`
|
||||
|
||||
- `Rotate files bigger than X bytes`(如 50MB)
|
||||
|
||||
- 可选勾 `Replace existing files`
|
||||
|
||||
|
||||
命令行示例(轮转阈值 50MB):
|
||||
|
||||
```bat
|
||||
nssm set frpc AppRotateFiles 1
|
||||
nssm set frpc AppRotateOnline 1
|
||||
nssm set frpc AppRotateBytes 52428800
|
||||
nssm set frpc AppRotateSeconds 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 权限、依赖与进程设置(可选)
|
||||
|
||||
### 9.1 用指定账户跑服务
|
||||
|
||||
GUI:**Log on** 页
|
||||
默认 `LocalSystem` 已够用。
|
||||
需要访问网络共享时才换普通用户。
|
||||
|
||||
命令行:
|
||||
|
||||
```bat
|
||||
nssm set frpc ObjectName ".\someuser" "password"
|
||||
```
|
||||
|
||||
### 9.2 设置依赖(等某服务起来再启动)
|
||||
|
||||
GUI:**Dependencies** 页
|
||||
命令行:
|
||||
|
||||
```bat
|
||||
nssm set frpc DependOnService Tcpip
|
||||
```
|
||||
|
||||
### 9.3 设置优先级/CPU 亲和性
|
||||
|
||||
GUI:**Process** 页
|
||||
命令行:
|
||||
|
||||
```bat
|
||||
nssm set frpc AppPriority NORMAL_PRIORITY_CLASS
|
||||
nssm set frpc AppAffinity All
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 卸载服务(安全删除)
|
||||
|
||||
先停再删:
|
||||
|
||||
```bat
|
||||
nssm stop frpc
|
||||
nssm remove frpc confirm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 常见问题与排错
|
||||
|
||||
### 11.1 `Can't open service! OpenService(): 拒绝访问`
|
||||
|
||||
原因:**没有管理员权限控制服务**
|
||||
|
||||
解决:
|
||||
|
||||
1. 关闭当前 CMD
|
||||
|
||||
2. 以管理员身份打开 CMD
|
||||
|
||||
3. 再执行:
|
||||
|
||||
|
||||
```bat
|
||||
nssm start frpc
|
||||
```
|
||||
|
||||
### 11.2 服务启动后立刻停止
|
||||
|
||||
常见原因:**Path / Arguments / Startup directory 错**
|
||||
|
||||
排查:
|
||||
|
||||
```bat
|
||||
nssm edit frpc
|
||||
nssm dump frpc
|
||||
```
|
||||
|
||||
### 11.3 日志没内容
|
||||
|
||||
原因:
|
||||
|
||||
- 没设置 `AppStdout/AppStderr`
|
||||
|
||||
- 目录没权限
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 12. frpc / openlist 成品脚本示例
|
||||
|
||||
### 12.1 frpc
|
||||
|
||||
```bat
|
||||
nssm install frpc "E:\控制台软件\frpc\frpc.exe" "-c \"E:\控制台软件\frpc\frpc.ini\""
|
||||
nssm set frpc AppDirectory "E:\控制台软件\frpc"
|
||||
nssm set frpc AppStdout "C:\logs\frpc.out.log"
|
||||
nssm set frpc AppStderr "C:\logs\frpc.err.log"
|
||||
nssm set frpc AppExit Default Restart
|
||||
nssm set frpc AppRestartDelay 5000
|
||||
nssm start frpc
|
||||
```
|
||||
|
||||
### 12.2 openlist(按你实际参数改)
|
||||
|
||||
```bat
|
||||
nssm install openlist "E:\控制台软件\openlist\openlist.exe" "server --config E:\控制台软件\openlist\config.yaml"
|
||||
nssm set openlist AppDirectory "E:\控制台软件\openlist"
|
||||
nssm set openlist AppStdout "C:\logs\openlist.out.log"
|
||||
nssm set openlist AppStderr "C:\logs\openlist.err.log"
|
||||
nssm set openlist AppExit Default Restart
|
||||
nssm set openlist AppRestartDelay 5000
|
||||
nssm start openlist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 速查表
|
||||
|
||||
```bat
|
||||
nssm install <name> "<exe>" "<args>"
|
||||
nssm edit <name>
|
||||
nssm start|stop|restart <name>
|
||||
nssm status <name>
|
||||
nssm set/get/reset <name> <key> [value]
|
||||
nssm dump <name>
|
||||
nssm remove <name> confirm
|
||||
```
|
||||
|
||||
---
|
||||
@@ -1,61 +0,0 @@
|
||||
Nacos 是阿里巴巴开源的 **动态服务发现、配置管理与服务治理平台**,旨在简化微服务架构的构建、交付和管理。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 核心功能
|
||||
|
||||
| 功能 | 主要作用 |
|
||||
|------|----------|
|
||||
| 🎯 **服务注册与发现** | 服务提供者注册自身信息,服务消费者动态发现和调用其他服务。 |
|
||||
| ⚙️ **动态配置管理** | 集中管理所有环境的配置,支持实时推送变更,无需重启应用。 |
|
||||
| 🔍 **服务健康监测** | 定期检查服务实例健康状况,自动隔离不健康实例,保障系统稳定性。 |
|
||||
| 🚦 **动态DNS与流量管理** | 支持权重路由,助力灰度发布、蓝绿部署等高级流量管理策略。 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 服务发现与健康检查
|
||||
|
||||
- **服务注册**:实例启动时向 Nacos 注册自身元数据(服务名、IP、端口等)。
|
||||
- **服务发现**:消费者通过服务名查询健康实例,实现通信而无需关心具体地址。
|
||||
- **健康检查**:支持客户端上报与服务端主动探测,自动剔除不健康实例,保障可靠性。
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 动态配置管理
|
||||
|
||||
- **集中化管理**:统一存储数据库连接、开关、限流阈值等配置。
|
||||
- **动态刷新**:配置变更实时推送,应用可在运行中直接生效(热更新)。
|
||||
- **版本控制与回滚**:支持历史版本,一键回滚,降低变更风险。
|
||||
|
||||
---
|
||||
|
||||
## 🚦 动态DNS与服务治理
|
||||
|
||||
- **动态DNS服务**:支持权重路由、流量控制和简易内网 DNS 解析。
|
||||
- **服务与元数据管理**:统一管理服务描述、依赖关系、健康状态、流量及安全策略。
|
||||
|
||||
---
|
||||
|
||||
## 💡 主要应用场景
|
||||
|
||||
- **数据库配置集中化管理**:提升安全性与合规性。
|
||||
- **限流与降级开关**:结合 Sentinel 等组件实现运行时动态调整。
|
||||
- **多环境与多数据中心**:基于 Namespace 与 Group 实现隔离与灵活部署。
|
||||
|
||||
---
|
||||
|
||||
## 📊 与其他组件对比
|
||||
|
||||
- ✅ **功能更全面**:Nacos = 服务发现 + 配置管理;Eureka 仅支持服务发现。
|
||||
- ✅ **健康检查机制更强**:优于 Eureka 心跳检测。
|
||||
- ✅ **社区与生态活跃**:Eureka 已停止维护,而 Nacos 持续迭代。
|
||||
- ✅ **多数据中心支持优越**:原生支持多数据中心部署。
|
||||
|
||||
---
|
||||
|
||||
## 📚 总结
|
||||
|
||||
Nacos 集 **服务发现、配置管理、服务治理** 于一体,
|
||||
极大简化了微服务架构复杂性,提升了 **开发效率**、**可维护性** 和 **系统弹性**。
|
||||
|
||||
---
|
||||
@@ -1,230 +0,0 @@
|
||||
```html
|
||||
<!-- 默认自定义头部 -->
|
||||
<style>
|
||||
/*隐藏底部openlsit版权信息*/
|
||||
.footer span,.footer a:nth-of-type(1){
|
||||
display:none;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
|
||||
```html
|
||||
<!-- 默认自定义头部 -->
|
||||
<!-- 引入 polyfill -->
|
||||
<script src="https://polyfill.alicdn.com/v3/polyfill.min.js?features=String.prototype.replaceAll"></script>
|
||||
|
||||
<!-- 引入字体 -->
|
||||
<link rel="stylesheet" href="https://npm.elemecdn.com/lxgw-wenkai-webfont@1.1.0/lxgwwenkai-regular.css" />
|
||||
|
||||
<style>
|
||||
/*Logo图片*/
|
||||
.header-left img { filter: drop-shadow(0px 0px 20px rgb(9, 187, 189)); filter: brightness(1.5); }
|
||||
|
||||
|
||||
/* 背景设置 */
|
||||
.hope-ui-dark, .hope-ui-light {
|
||||
background-image: url('https://pan.lcxm.site/d/%E7%81%B5%E5%88%9B%E7%9B%98-%E4%B8%BB%E7%9B%AE%E5%BD%95/%E7%81%B5%E5%88%9B%E7%9B%98%E7%B3%BB%E7%BB%9F%E6%96%87%E4%BB%B6/background4.png?sign=q5pQCi5TXFxY1f9ULk-Y2y4pHeIc7yulTy2e8ai_GJI=:0') !important;
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
/*主列表白天模式透明*/
|
||||
.obj-box.hope-stack.hope-c-dhzjXW.hope-c-PJLV.hope-c-PJLV-igScBhH-css {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background-color: rgba(255, 255, 255, 0.3) !important;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
/*主列表夜间模式透明*/
|
||||
.obj-box.hope-stack.hope-c-dhzjXW.hope-c-PJLV.hope-c-PJLV-iigjoxS-css {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background-color: rgba(0, 0, 0, 0.3) !important;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
/*readme白天模式透明*/
|
||||
.hope-c-PJLV.hope-c-PJLV-ikSuVsl-css {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background-color: rgba(255, 255, 255, 0.3) !important;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
/*readme夜间模式透明*/
|
||||
.hope-c-PJLV.hope-c-PJLV-iiuDLME-css {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background-color: rgba(0, 0, 0, 0.3) !important;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
|
||||
/*顶部*/
|
||||
#root > .header {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background: rgba(255, 255, 255, 0); /* 透明背景色 */
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
|
||||
/*导航条*/
|
||||
/*白天模式*/
|
||||
.hope-ui-light .body > .nav {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
border-radius: var(--hope-radii-xl);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
/*夜间模式*/
|
||||
.hope-ui-dark .body > .nav {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: var(--hope-radii-xl);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
|
||||
/*隐藏导航条遮罩*/
|
||||
.body > .nav::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*右上视图切换菜单*/
|
||||
/*白天模式*/
|
||||
.hope-ui-light .hope-c-PJLV-iSMXDf-css {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background: rgba(255, 255, 255, 0.3); /* 透明背景色 */
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
/*夜间模式*/
|
||||
.hope-ui-dark .hope-c-PJLV-iSMXDf-css {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background: rgba(0, 0, 0, 0.3); /* 透明背景色 */
|
||||
border-radius: var(--hope-radii-xl);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
}
|
||||
|
||||
/*右下角侧边栏按钮透明 第一个是白天 第二个是夜间*/
|
||||
.hope-ui-light .hope-c-PJLV-ijgzmFG-css {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background-color: rgba(255, 255, 255, 0.3) !important;
|
||||
}
|
||||
.hope-ui-dark .hope-c-PJLV-ijgzmFG-css {
|
||||
backdrop-filter: blur(10px); /* 毛玻璃效果的强度 */
|
||||
-webkit-backdrop-filter: blur(10px); /* 为 Safari 浏览器添加兼容性 */
|
||||
background-color: rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
/*白天模式代码块透明*/
|
||||
.hope-ui-light pre {
|
||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
/*夜间模式代码块透明*/
|
||||
.hope-ui-dark pre {
|
||||
background-color: rgba(255, 255, 255, 0) !important;
|
||||
}
|
||||
|
||||
/*底部CSS,.App .table这三个一起的*/
|
||||
dibu {
|
||||
border-top: 0px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
.App {
|
||||
min-height: 85vh;
|
||||
}
|
||||
.table {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
|
||||
/*全局字体*/
|
||||
* {
|
||||
font-family: LXGW WenKai;
|
||||
}
|
||||
* {
|
||||
font-weight: bold;
|
||||
}
|
||||
body {
|
||||
font-family: LXGW WenKai;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
|
||||
```html
|
||||
<!-- 延迟加载 -->
|
||||
<div id="customize" style="display: none;">
|
||||
<div>
|
||||
<br />
|
||||
<center class="dibu">
|
||||
<div style=" line-height: 20px;font-size: 20pt;font-weight: bold;">
|
||||
<span>
|
||||
"
|
||||
<span style="color: rgb(154, 216, 166); font-weight: bold;" id="hitokoto">
|
||||
<a href="#" id="hitokoto_text">
|
||||
"灵创新媒实验室欢迎您!"
|
||||
</a>
|
||||
</span> "
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 底部链接 -->
|
||||
<div style="font-size: 13px; font-weight: bold;">
|
||||
<span class="nav-item">
|
||||
<a class="nav-link" href="mailto:xiaoming@xmdblog.com" target="_blank">
|
||||
<i class="fa-duotone fa-envelope-open" style="color:#409EFF" aria-hidden="true">
|
||||
</i>
|
||||
邮箱 |
|
||||
</a>
|
||||
</span>
|
||||
<span class="nav-item">
|
||||
<a class="nav-link" href="pan.xmdblog.com" target="_blank">
|
||||
<i class="fa fa-cloud-download" style="color:#409EFF;" aria-hidden="true">
|
||||
</i>
|
||||
云盘 |
|
||||
</a>
|
||||
</span>
|
||||
<!--后台入口-->
|
||||
<span class="nav-item">
|
||||
<a class="nav-link" href="/@manage" target="_blank">
|
||||
<i class="fa-solid fa-folder-gear" style="color:#409EFF;" aria-hidden="true">
|
||||
</i>
|
||||
管理 |
|
||||
</a>
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<!--添加备案信息-->
|
||||
<span class="nav-item">
|
||||
<a class="nav-link" href="https://beian.miit.gov.cn/#/Integrated/index" target="_blank">
|
||||
<i class="fa-solid fa-shield-check" style="color:#409EFF;" aria-hidden="true">
|
||||
</i>
|
||||
沪ICP备2024050492号-1
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</center>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<!--延迟加载范围到这里结束-->
|
||||
</div>
|
||||
|
||||
<!-- 延迟加载JS -->
|
||||
<script>
|
||||
let interval = setInterval(() => {
|
||||
if (document.querySelector(".footer")) {
|
||||
document.querySelector("#customize").style.display = "";
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 200);
|
||||
</script>
|
||||
```
|
||||
|
||||
```html
|
||||
<script type="text/javascript"> function show_runtime() { window.setTimeout("show_runtime()", 1000); X = new Date("3/30/2025 12:12:12"); /*开始时间*/ Y = new Date(); T = (Y.getTime() - X.getTime()); M = 24 * 60 * 60 * 1000; a = T / M; A = Math.floor(a); b = (a - A) * 24; B = Math.floor(b); c = (b - B) * 60; C = Math.floor((b - B) * 60); D = Math.floor((c - C) * 60); runtime_span.innerHTML = "本站已运行 " + A + " 天 " + B + " 小时 " + C + " 分 " + D + " 秒 " } show_runtime(); </script>
|
||||
```
|
||||
@@ -1,6 +0,0 @@
|
||||
```php
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
$this->getServer()->getLogger()->info(TextFormat::GREEN . "[刷矿机] 插件激活成功!");
|
||||
```
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
# 在阅读本教程前,务必阅读本Readme
|
||||
|
||||
目前由官方承认的几个宣传贴
|
||||
|
||||
https://www.minebbs.com/threads/dreamserver-nukkit-nukkit.2912/#post-13183
|
||||
|
||||
https://www.mcplugin.cn/thread-1259-1-1.html
|
||||
|
||||
https://www.mcbbs.net/forum.php?mod=viewthread&tid=927009&page=1#pid15854025
|
||||
|
||||
https://www.mcbbs.net/forum.php?mod=viewthread&tid=930861&mobile=2
|
||||
|
||||
# 史上最详细的Nukkit教程
|
||||
#### Learning Nukkit Whoever you are!
|
||||
|
||||
|
||||
- [中文(简体)](README.md)
|
||||
- [English](README_en.md)
|
||||
|
||||
#### 说明
|
||||
|
||||
您可以在这里作为一本书观看教程: http://noyark.net/
|
||||
|
||||
本教程遵循[发现者小组公约](LICENSE)
|
||||
|
||||
#### 友情项目
|
||||
|
||||
[基于Java开发的SCPSL服务端](https://github.com/jsmod2-java-c/JSmod2-Core)
|
||||
|
||||
#### 关于主要作者
|
||||
|
||||
事实上作者上已经退坑了,但是不影响写教程,只是有几点还请悉知:
|
||||
- 不要问如何搭建服务器;
|
||||
- 不要PM任何有关人员定制插件;
|
||||
- 除了写这个教程,作者不会参与任何和Minecraft有关的活动和交易;
|
||||
- 本教程需要一定的Java基础,请先做好预习功课,并且作者不会回答任何基础的问题;
|
||||
- 作者目前是高中学生,因此时间并不充裕,更新速度请大家海涵;
|
||||
- 如有任何疏漏或Bug的存在,还请指正;
|
||||
|
||||
#### 作者的扯皮
|
||||
|
||||
Nukkit核心作为一个服务端开发框架,虽然性能优越,吸引了大批的开发者加入开发,但教程稀少,学习难度大,使很多小白
|
||||
望而却步,基于目前很多人对于教程的渴望,包括作者在初学的时候只能通过到处询问和看核心源码来了解如何使用一个东西,
|
||||
但并不是所有的人都能安心去看核心源码或者到处询问问题。同时,本教程会重复强调一件事情: 打好基础,本教程已经列出
|
||||
您需要掌握的java基础,什么0基础直接学习nukkit都是骗人的,作者已经见过很多这样的初学者,很少的人通过这种方式
|
||||
学会(无疑是一种懒散的行为),nukkit事实上就是学习一个新的api,并无很大的入门难度,只要您学习了基础,就能很快从
|
||||
这里得到启发,并参与到nukkit的大家庭。
|
||||
|
||||
主作者其实很和蔼,但是对于简单的问题作者由于时间问题是不能回答的,也不能干刷屏之类的事情,再和蔼的人也会把你给
|
||||
屏蔽,当然,作者坚持开放原则,如果有相关问题,可以issues发表您的疑问。
|
||||
|
||||
同时,记得给项目一个star支持一下,并且将这个项目宣传给别人,在支持作者同时,造福他人。您的支持和宣传就是作者写下去的动力,
|
||||
希望这个教程可以帮到您,我们由衷的感谢:)
|
||||
|
||||
--- MagicLu550
|
||||
|
||||
#### 关于本教程
|
||||
|
||||
Nukkit官方说过: Nukkit是一款高性能的核能驱动的Minecraft基岩版服务器,它的
|
||||
速度更快,性能相比PocketMine更高。
|
||||
```
|
||||
Nukkit is nuclear-powered server software for Minecraft Bedrock Edition. It has a few key advantages over other server software:
|
||||
|
||||
Written in Java, Nukkit is faster and more stable.
|
||||
Having a friendly structure, it's easy to contribute to Nukkit's development and rewrite plugins from other platforms into Nukkit plugins.
|
||||
Nukkit is under improvement yet, we welcome contributions.
|
||||
```
|
||||
|
||||
目前,依托Java语言的健壮性,Nukkit形成了强大的生态,并且出现了很多分支,目前使用最广泛的分支是[NukkitX](http://nukkitx.com)
|
||||
|
||||
感谢您能观看我们编写的教程。该教程在编写阶段,更新间隔可能会很长。
|
||||
请谅解。该教程是为了简化目前很多晦涩难懂的教程,使得有过Java语法基础的朋友
|
||||
能方便地了解Nukkit和学习Nukkit. 目前Nukkit的学习难度主要在于其资料过少。
|
||||
我们将会整理和参考其他相关资料来编写该教程,也感谢您参与编写,造福大家
|
||||
|
||||
同时,转发本教程应当附上GitHub原地址,不得任意转发和搬运,以及商业使用。
|
||||
本教程不涉及语法,只涉及Nukkit的各种库的解释以便于开发
|
||||
您所需要掌握的最基本的Java基础体系
|
||||
```
|
||||
--基础部分
|
||||
-- 语法
|
||||
-- 变量定义
|
||||
-- 方法定义
|
||||
-- 基本表达式
|
||||
-- 逻辑表达式
|
||||
-- 算数表达式
|
||||
-- 流程控制
|
||||
-- 条件语句
|
||||
-- 循环语句
|
||||
-- 选择语句
|
||||
-- 面向对象
|
||||
-- 类,对象的概念
|
||||
-- 定义类和声明对象
|
||||
-- 包的概念
|
||||
-- 匿名内部类
|
||||
-- 接口
|
||||
-- 抽象类,抽象方法
|
||||
-- lambda表达式
|
||||
-- 面向对象的三大特征
|
||||
-- 继承
|
||||
-- 封装
|
||||
-- 多态
|
||||
-- 枚举
|
||||
-- 注解
|
||||
-- 类库
|
||||
-- 反射
|
||||
-- 多线程
|
||||
-- 字符串操作
|
||||
-- 数字包装类
|
||||
-- io流
|
||||
-- bio
|
||||
-- nio
|
||||
-- 套接字Socket
|
||||
-- udp
|
||||
-- tcp
|
||||
-- 时间类库
|
||||
-- 系统类库
|
||||
|
||||
```
|
||||
未来,我们会开发Nukkit-d,是对于Nukkit设计思想的整理和重新实现,以解决目前nukkit维护难的问题
|
||||
#### 关于我们
|
||||
|
||||
如果您有兴趣可以随时发送Pr等
|
||||
本教程不定期更新,大概寒假时期更新最快
|
||||
相关意见可以联系843983728@qq.com或者加QQ:843983728
|
||||
|
||||
QQ群: 931210534
|
||||
|
||||

|
||||
|
||||
#### 贡献标准和须知
|
||||
|
||||
1. 教程要求尽量自然,易懂,符合本项目所追求的"人人可以学习Nukkit"的宗旨
|
||||
2. 编写教程可以在参与编写者添加自己的名字,若没有可以自己手动加入,如第一章所同
|
||||
3. 照搬其他教程原文需要得到作者同意,并且在下面注明参考文献。
|
||||
4. 若发现侵权行为,与本项目领导者无关,但我们会积极配合找到侵权者
|
||||
5. 请尽量以第一章为范本编写您的内容。
|
||||
6. 如果有知识缺点,请在Readme里注明这个缺点出处,写在知识缺点里,知识缺点在章节中标注。
|
||||
7. 教程补充首要的是解决知识缺点
|
||||
8. 由于编写人数较少,教程不避免的会有错误,漏洞,不严谨,如果发现,可以发送pullRequest参与修改
|
||||
|
||||
#### 知识缺点:
|
||||
- 关于fallBackPrefix的作用 - 第二章和第四章 [1]
|
||||
|
||||
#### 专栏
|
||||
|
||||
- [填坑专栏](专栏-关于我们常见的那些坑.md)
|
||||
|
||||
- [用指令设置玩家实体大小](章外篇之—-用指令设置玩家实体大小(简单版).md)
|
||||
|
||||
- [多语言解决方案](章外篇之二-多语言解决方案.md)
|
||||
|
||||
#### 目录
|
||||
- [第一部分 基础准备](第一部分前言.md)
|
||||
- [X] [如何搭建开发环境](第一章-如何搭建环境.md)
|
||||
- [X] [插件要素](第二章-插件要素.md)
|
||||
- [X] [如何编写监听器](第三章-如何编写监听器.md)
|
||||
- [X] [如何编写指令](第四章-如何编写命令.md)
|
||||
- [X] [如何使用配置文件](第五章-如何使用配置文件.md)
|
||||
- [X] [如何编写plugin.yml](第六章-如何编写plugin.yml.md)
|
||||
- [X] [PluginBase类](第七章-PluginBase类.md)
|
||||
- [X] [练习案例](第八章-案例玩家进入信息等效果.md)
|
||||
- [第二部分 nukkit的工具和各种事件介绍](第二部分前言.md)
|
||||
- [X] [主要的事件的介绍](第一章-主要的事件介绍.md)
|
||||
- [ ] [事件相关方法](第二章-事件相关方法.md)
|
||||
- [X] [计时器的介绍](第三章-计时器的介绍.md)
|
||||
- [X] [Server类和PluginManager类](第四章-Server类和PluginManager类.md)
|
||||
- [ ] [各种实体类的方法介绍](第五章-各种实体类的方法介绍.md)
|
||||
- [ ] [各种工具类的介绍](第六章-各种工具类的介绍.md)
|
||||
- [X] [如何发送数据包](第七章-如何发送数据包.md)
|
||||
- 第三部分 nukkit相关实例
|
||||
- 第四部分 nukkit原理剖析
|
||||
|
||||
|
||||
#### 专栏状况
|
||||
```diff
|
||||
+ 专题
|
||||
+ 提示信息
|
||||
```
|
||||
@@ -1,146 +0,0 @@
|
||||
|
||||
# 核心思路(快速决策)
|
||||
|
||||
- **Windows(成熟 & 快)**:用 **Electron** + `electron-builder`(或 Electron Forge)把 web 前端放进 WebView,桌面端直接运行/或同时启动本地后端进程。成熟、生态完善、发布流程(安装包、自动更新、签名)有现成方案。
|
||||
- **Windows(瘦体积)**:若关心体积、原生安全,考虑 **Tauri**(Rust 后端,最终二进制更小)。但学习/环境比 Electron 略复杂。
|
||||
- **Android(快 & 直接把现有 web 变原生)**:用 **Capacitor**(Ionic 团队)把生产构建的前端打包成 Android 原生工程,再用 Android Studio 打包签名上传 Play。对保留现有 React 网页最省力。
|
||||
- **Android(PWA 路线,最快上线)**:如果你的 React 是 PWA(有 service worker、HTTPS、manifest),可以做 **Trusted Web Activity (TWA)**,用 Bubblewrap/PWABuilder 生成 Android 包并上架(适合想最小工作量上 Play 的场景)。
|
||||
|
||||
下面给出每条路线的**实战步骤、常见陷阱和示例命令**。
|
||||
|
||||
---
|
||||
|
||||
# 路线 A — 推荐(最快、最成熟):**Electron(Windows) + Capacitor(Android)**
|
||||
|
||||
适合:不想重写、后端可以继续作为远端服务(推荐),要快速得到桌面 + Android 双端。
|
||||
|
||||
## 1) 通用准备(前端)
|
||||
|
||||
1. 把 React 做生产构建(示例用 CRA / Vite):
|
||||
`npm run build`(或 `yarn build`),生成 `dist/` 或 `build/`。
|
||||
2. 确保所有 API 用 HTTPS、CORS、并把生产 API 域配置好(不要在客户端明文放敏感秘钥)。
|
||||
|
||||
|
||||
## 2) Windows:Electron + electron-builder(快速、成熟)
|
||||
|
||||
概念:Electron 启一个 native 窗口并加载你的静态 `build/`。用 `electron-builder` 打包成 installer(NSIS/MSI/portable)并可做代码签名与自动更新。
|
||||
|
||||
快速步骤(最小):
|
||||
|
||||
- 项目根目录加入 `electron` 主进程文件(`main.js`),让它加载 `build/index.html`。
|
||||
- 安装依赖并配置打包:
|
||||
|
||||
|
||||
```bash
|
||||
# 依赖
|
||||
npm install --save-dev electron electron-builder
|
||||
# package.json scripts 示例
|
||||
"scripts": {
|
||||
"build:web": "npm run build", # 你的 React build
|
||||
"start:electron": "electron .",
|
||||
"dist": "npm run build:web && electron-builder"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.yourcompany.app",
|
||||
"files": ["build/**/*", "main.js", "package.json"],
|
||||
"win": {"target":["nsis","portable"]}
|
||||
}
|
||||
```
|
||||
|
||||
- 本地测试:`npm run start:electron`。
|
||||
|
||||
- 生成发布包:`npm run dist`(electron-builder 会产出 `.exe` / `.msi` / installer)。
|
||||
|
||||
|
||||
**如果需要把后端随应用打包(可选但更复杂)**:
|
||||
常见做法是把 Node 后端打成独立 exe(用 `pkg` 或 `nexe`),在 Electron 启动时用 `child_process.spawn()` 启动本地服务,再让前端调用 `http://localhost:xxxx`。这样用户本地拥有完整后端(适合离线或内网部署)。示例工具:`pkg`、`nexe`。注意:安全、端口冲突、自动更新、杀进程清理等需要处理。
|
||||
|
||||
常见注意点:
|
||||
|
||||
- Electron 应用体积通常较大(几十 MB 起);自动更新、签名(Authenticode)建议上线前完成。
|
||||
- 在 CI(如 GitHub Actions)上可以交叉构建,但 Windows 的代码签名通常在 Windows runner 或专门流程中完成。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 3) Android:Capacitor(把 web 打包成原生 APK/AAB)
|
||||
|
||||
概念:Capacitor 将你的 `build/` 内容嵌入到一个原生 Android 项目(WebView),并提供与原生桥接的插件 API。非常适合把现有 web 转 app。
|
||||
|
||||
快速步骤(最小):
|
||||
|
||||
```bash
|
||||
# 在项目根(build 已存在)
|
||||
npm install @capacitor/core @capacitor/cli
|
||||
npx cap init "MyApp" com.example.myapp --web-dir=build
|
||||
npx cap add android
|
||||
# 每次 web 有新 build:
|
||||
npm run build
|
||||
npx cap copy android
|
||||
npx cap open android # 在 Android Studio 中打开,构建、签名、导出 AAB
|
||||
```
|
||||
|
||||
在 Android Studio 中使用 **Build → Generate Signed Bundle / APK** 来生成签名的 AAB 上传到 Play(Play 推荐上传 AAB)。发布与签名细节见 Google Play 签名文档(Play App Signing)。
|
||||
|
||||
常见注意点:
|
||||
|
||||
- 若后端是远端 API,Android 里可直接请求远端;若需要本地 DB,使用 SQLite 或 Capacitor 的 Storage/插件而不是在设备上运行完整 Node 后端。
|
||||
- 调试网络(开发时指向本地 dev server)可用 `npx cap open android` 并在 `capacitor.config.*` 设置 `server.url` 来启用 live reload(但生产请编译静态文件)。
|
||||
|
||||
|
||||
---
|
||||
|
||||
# 路线 B — 可选:**Tauri(Windows 更小) + Capacitor/TWA(Android)**
|
||||
|
||||
- 若你很在意桌面体积与原生安全性,**Tauri** 是比 Electron 更轻量的替代品(用 Rust 来打包、使用系统 WebView),生成的二进制更小且性能好。缺点:本地构建环境(Rust / MSVC)和插件生态比 Electron 小一些。
|
||||
- Android 仍建议用 Capacitor 或 TWA 路线(Tauri 的移动支持还在发展中)。
|
||||
|
||||
|
||||
Tauri 打包基本:`npm run build`(前端)然后 `tauri build`(生成 Windows installer / exe)——详见 Tauri 分发文档(Windows 打包细节、WebView2 要求等)。
|
||||
|
||||
---
|
||||
|
||||
# 路线 C — 直接 PWA → Play(最快上架 Android)
|
||||
|
||||
如果你的 React 已经是 PWA(manifest + SW),通过 **TWA**(Trusted Web Activity)把 PWA 裹进一个最小的 Android 应用是最快的上 Play 方式:用 **Bubblewrap** / PWABuilder 生成项目,然后签名上传 Play。适合不需要本地原生 API,仅需离线缓存与简单推送的场景。
|
||||
|
||||
---
|
||||
|
||||
# 后端打包 vs 远端服务的建议(关键设计决策)
|
||||
|
||||
- **优先把后端作为远端服务**(部署到云/内网)——理由:升级、补丁、数据库统一管理、安全容易;前端变体(桌面/Android/PWA)都指向同一 API,开发运维简单。
|
||||
- 仅在必须(离线、内网隔离)时,把后端打包到桌面:用 `pkg` / `nexe` 做 Node -> exe,再由 Electron 启动子进程(`child_process.spawn`)。Android 上嵌入 Node 很麻烦(可选 nodejs-mobile,但生态少),因此若后端要内嵌,桌面比手机更现实。
|
||||
|
||||
|
||||
---
|
||||
|
||||
# 上线前必须做的成熟化步骤(checklist)
|
||||
|
||||
1. **安全**:HTTPS、CSP、Key/Secret 不放前端;XSS/CSRF/认证策略。
|
||||
2. **签名与发布**:Windows 用 Authenticode 证书签名;Android 用 Play App Signing(上传签名/上传 key)。
|
||||
3. **自动更新**:Electron 有 `electron-updater`/electron-builder 集成;考虑 OTA 策略。
|
||||
4. **CI/CD**:用 GitHub Actions / Azure / CircleCI 自动构建 web、electron dist、Android AAB(包含签名步骤)。
|
||||
5. **测试**:多架构/多机型测试、模拟断网、升级回滚测试。
|
||||
6. **体积优化**:压缩静态资源、剔除 dev 依赖、考虑 Tauri 以减小 exe 体积。
|
||||
|
||||
|
||||
---
|
||||
|
||||
# 推荐组合(按你想要的结果选)
|
||||
|
||||
- **最快上手 + 最成熟**:Electron(Windows) + Capacitor(Android),后端保留云端。
|
||||
- **追求二进制最瘦**:Tauri(Windows) + Capacitor/TWA(Android)。
|
||||
- **只想最快上线到 Play(不做原生功能)**:PWA → TWA(Bubblewrap/PWABuilder)。
|
||||
|
||||
|
||||
---
|
||||
|
||||
# 我可以直接帮你做的事(选一项即可回复)
|
||||
|
||||
1. 给你一份针对你 current React 项目的 **具体脚本(package.json)+ Electron 主进程示例 + electron-builder 配置**(Windows 一键打包脚本)。
|
||||
2. 给你一份 **Capacitor 打包到 Android 的 step-by-step 脚本 & Android Studio 签名操作流水线**(含 AAB 签名命令示例)。
|
||||
3. 或者把 **把后端打包为 exe 的示例(pkg/nexe)并在 Electron 中 spawn 的最小示例** 发给你。
|
||||
|
||||
告诉我你要哪一项(例如“给我 Electron + 打包脚本”),我立刻把完整代码/命令/CI 配置直接生成给你(无需你再提供额外信息即可开始)。
|
||||
|
||||
---
|
||||
@@ -1,79 +0,0 @@
|
||||
|
||||
---
|
||||
|
||||
# ✅ **方式一:使用 Vite 初始化 React 项目(推荐,最快最轻量)**
|
||||
|
||||
### **1. 安装 Node.js(必须)**
|
||||
|
||||
确保 Node 版本 ≥ 18
|
||||
检查:
|
||||
```bash
|
||||
node -v
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **2. 使用 Vite 创建 React 项目**
|
||||
|
||||
```bash
|
||||
npm create vite@latest my-react-app -- --template react
|
||||
```
|
||||
如果你想要 TypeScript 版本:
|
||||
```bash
|
||||
npm create vite@latest my-react-app -- --template react-ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **3. 安装依赖**
|
||||
|
||||
```bash
|
||||
cd my-react-app
|
||||
npm install
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **4. 启动开发环境**
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
浏览器访问提示的 localhost 地址即可。
|
||||
|
||||
---
|
||||
|
||||
# ✅ **方式二:使用 Create-React-App(传统方案,不推荐但能用)**
|
||||
|
||||
(CRA 已多年未更新,官方推荐迁移到 Vite)
|
||||
|
||||
### 1. 创建项目
|
||||
|
||||
```bash
|
||||
npx create-react-app my-react-app
|
||||
```
|
||||
|
||||
### 2. 启动项目
|
||||
|
||||
```bash
|
||||
cd my-react-app
|
||||
npm start
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# ✅ **方式三:使用 Next.js(如果你准备做大一点的项目)**
|
||||
|
||||
如果你想做:
|
||||
✔ SEO
|
||||
✔ 服务端渲染
|
||||
✔ 大型项目结构
|
||||
✔ API 路由
|
||||
|
||||
推荐 Next.js:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest
|
||||
```
|
||||
|
||||
---
|
||||
@@ -1,15 +0,0 @@
|
||||
**TCP连接建立过程:**
|
||||
首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源。Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了。
|
||||
|
||||
**TCP连接断开过程:**
|
||||
假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。",Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
|
||||
|
||||
**为什么要三次握手?**
|
||||
|
||||
在只有两次"握手"的情形下,假设Client想跟Server建立连接,但是却因为中途连接请求的数据报丢失了,故Client端不得不重新发送一遍;这个时候Server端仅收到一个连接请求,因此可以正常的建立连接。但是,有时候Client端重新发送请求不是因为数据报丢失了,而是有可能数据传输过程因为网络并发量很大在某结点被阻塞了,这种情形下Server端将先后收到2次请求,并持续等待两个Client请求向他发送数据...问题就在这里,Cient端实际上只有一次请求,而Server端却有2个响应,极端的情况可能由于Client端多次重新发送请求数据而导致Server端最后建立了N多个响应在等待,因而造成极大的资源浪费!所以,"三次握手"很有必要!
|
||||
|
||||
**为什么要四次挥手?**
|
||||
|
||||
试想一下,假如现在你是客户端你想断开跟Server的所有连接该怎么做?第一步,你自己先停止向Server端发送数据,并等待Server的回复。但事情还没有完,虽然你自身不往Server发送数据了,但是因为你们之前已经建立好平等的连接了,所以此时他也有主动权向你发送数据;故Server端还得终止主动向你发送数据,并等待你的确认。其实,说白了就是保证双方的一个合约的完整执行!
|
||||
|
||||
使用TCP的协议:FTP(文件传输协议)、Telnet(远程登录协议)、SMTP(简单邮件传输协议)、POP3(和SMTP相对,用于接收邮件)、HTTP协议等。
|
||||
@@ -1,43 +0,0 @@
|
||||
# 🌐 TCP 连接与断开总结
|
||||
|
||||
## 🔗 TCP 连接建立(三次握手)
|
||||
1. **Client → Server**:发送 `SYN`(请求建立连接)。
|
||||
2. **Server → Client**:回复 `SYN + ACK`(同意并确认请求),并分配资源。
|
||||
3. **Client → Server**:再发送 `ACK`(确认收到),也分配资源。
|
||||
|
||||
✅ **完成三次握手,连接建立成功。**
|
||||
|
||||
---
|
||||
|
||||
## 🔒 TCP 连接断开(四次挥手)
|
||||
1. **Client → Server**:发送 `FIN`(我没有数据要发了)。
|
||||
2. **Server → Client**:回复 `ACK`(收到请求,但可能还有数据要发),此时 Client 进入 `FIN_WAIT` 状态。
|
||||
3. **Server → Client**:当数据发送完成,发送 `FIN`(我也准备好关闭了)。
|
||||
4. **Client → Server**:回复 `ACK`,并进入 `TIME_WAIT` 状态,等待 2MSL,确保 Server 收到确认。
|
||||
|
||||
✅ **完成四次挥手,连接安全关闭。**
|
||||
|
||||
---
|
||||
|
||||
## ❓ 为什么要三次握手?
|
||||
- **两次握手的风险**:若请求报文滞留在网络中,Server 可能会收到多次请求,建立多个无效连接,造成资源浪费。
|
||||
- **三次握手的好处**:避免 **重复连接** 的问题,确保 **双方通信能力正常**。
|
||||
|
||||
---
|
||||
|
||||
## ❓ 为什么要四次挥手?
|
||||
- 关闭连接需要 **双向确认**:
|
||||
- Client 不再发送数据,但 **Server 可能还有数据要发**。
|
||||
- 所以必须分为两步:
|
||||
- **Client 先关闭发送通道**。
|
||||
- **Server 再关闭自己的发送通道**。
|
||||
- 保证 **双方通信的完整性**,不会丢数据。
|
||||
|
||||
---
|
||||
|
||||
## 📌 使用 TCP 的常见协议
|
||||
- 📂 **FTP**(文件传输协议)
|
||||
- 🖥️ **Telnet**(远程登录协议)
|
||||
- 📧 **SMTP**(简单邮件传输协议)
|
||||
- 📩 **POP3**(接收邮件协议)
|
||||
- 🌍 **HTTP/HTTPS**(超文本传输协议)
|
||||
@@ -1,324 +0,0 @@
|
||||
|
||||
## 1. 环境准备
|
||||
|
||||
### 1.1 安装Node.js
|
||||
- 访问 [Node.js官网](https://nodejs.org/) 下载LTS版本
|
||||
- 验证安装:
|
||||
```bash
|
||||
node --version
|
||||
npm --version
|
||||
```
|
||||
|
||||
### 1.2 安装Vue CLI(可选,推荐使用Vite)
|
||||
```bash
|
||||
npm install -g @vue/cli
|
||||
# 或
|
||||
yarn global add @vue/cli
|
||||
```
|
||||
|
||||
## 2. 使用Vite创建Vue项目(推荐方式)
|
||||
|
||||
### 2.1 创建项目
|
||||
```bash
|
||||
# 使用npm
|
||||
npm create vue@latest
|
||||
|
||||
# 或使用yarn
|
||||
yarn create vue
|
||||
|
||||
# 或使用pnpm
|
||||
pnpm create vue
|
||||
```
|
||||
|
||||
### 2.2 交互式配置
|
||||
执行命令后,你会看到以下选项:
|
||||
```
|
||||
✔ Project name: … <your-project-name>
|
||||
✔ Add TypeScript? … No / Yes
|
||||
✔ Add JSX Support? … No / Yes
|
||||
✔ Add Vue Router for Single Page Application development? … No / Yes
|
||||
✔ Add Pinia for state management? … No / Yes
|
||||
✔ Add Vitest for Unit Testing? … No / Yes
|
||||
✔ Add an End-to-End Testing Solution? … No / Cypress / Playwright
|
||||
✔ Add ESLint for code quality? … No / Yes
|
||||
✔ Add Prettier for code formatting? … No / Yes
|
||||
```
|
||||
|
||||
### 2.3 进入项目并运行
|
||||
```bash
|
||||
cd <project-name>
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 3. 使用Vue CLI创建项目(传统方式)
|
||||
|
||||
### 3.1 创建项目
|
||||
```bash
|
||||
vue create my-project
|
||||
```
|
||||
|
||||
### 3.2 选择预设
|
||||
```
|
||||
? Please pick a preset:
|
||||
Default ([Vue 2] babel, eslint)
|
||||
Default (Vue 3) ([Vue 3] babel, eslint)
|
||||
❯ Manually select features
|
||||
```
|
||||
|
||||
### 3.3 选择功能
|
||||
```
|
||||
? Check the features needed for your project:
|
||||
◉ Babel
|
||||
◯ TypeScript
|
||||
◯ Progressive Web App (PWA) Support
|
||||
❯◉ Router
|
||||
◉ Vuex
|
||||
◉ CSS Pre-processors
|
||||
◉ Linter / Formatter
|
||||
◯ Unit Testing
|
||||
◯ E2E Testing
|
||||
```
|
||||
|
||||
## 4. 项目结构说明
|
||||
|
||||
使用Vite创建的典型项目结构:
|
||||
```
|
||||
my-vue-project/
|
||||
├── node_modules/
|
||||
├── public/
|
||||
│ └── favicon.ico
|
||||
├── src/
|
||||
│ ├── assets/
|
||||
│ │ └── logo.svg
|
||||
│ ├── components/
|
||||
│ │ └── HelloWorld.vue
|
||||
│ ├── router/
|
||||
│ │ └── index.js
|
||||
│ ├── stores/
|
||||
│ │ └── counter.js
|
||||
│ ├── views/
|
||||
│ │ ├── AboutView.vue
|
||||
│ │ └── HomeView.vue
|
||||
│ ├── App.vue
|
||||
│ └── main.js
|
||||
├── .gitignore
|
||||
├── index.html
|
||||
├── package.json
|
||||
├── README.md
|
||||
└── vite.config.js
|
||||
```
|
||||
|
||||
## 5. 基础配置示例
|
||||
|
||||
### 5.1 修改vite.config.js
|
||||
```javascript
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
open: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 5.2 配置ESLint + Prettier
|
||||
`.eslintrc.cjs`:
|
||||
```javascript
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
browser: true,
|
||||
es2021: true
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-essential',
|
||||
'@vue/eslint-config-prettier'
|
||||
],
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`.prettierrc`:
|
||||
```json
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 80,
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 2
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 安装常用依赖
|
||||
|
||||
```bash
|
||||
# 状态管理
|
||||
npm install pinia
|
||||
|
||||
# UI框架(按需选择)
|
||||
npm install element-plus
|
||||
# 或
|
||||
npm install ant-design-vue
|
||||
# 或
|
||||
npm install vuetify
|
||||
|
||||
# HTTP客户端
|
||||
npm install axios
|
||||
|
||||
# 路由
|
||||
npm install vue-router@4
|
||||
|
||||
# 日期处理
|
||||
npm install dayjs
|
||||
|
||||
# 工具函数
|
||||
npm install lodash-es
|
||||
```
|
||||
|
||||
## 7. 创建第一个组件
|
||||
|
||||
`src/components/HelloWorld.vue`:
|
||||
```vue
|
||||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<button @click="count++">Count is: {{ count }}</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
msg: String
|
||||
})
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hello {
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 8. 配置路由
|
||||
|
||||
`src/router/index.js`:
|
||||
```javascript
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
component: () => import('../views/AboutView.vue')
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
```
|
||||
|
||||
## 9. 启动和构建
|
||||
|
||||
```bash
|
||||
# 开发模式
|
||||
npm run dev
|
||||
|
||||
# 生产构建
|
||||
npm run build
|
||||
|
||||
# 预览生产构建
|
||||
npm run preview
|
||||
|
||||
# 代码检查
|
||||
npm run lint
|
||||
```
|
||||
|
||||
## 10. 最佳实践建议
|
||||
|
||||
### 10.1 项目组织
|
||||
```
|
||||
src/
|
||||
├── api/ # API接口
|
||||
├── assets/ # 静态资源
|
||||
├── components/ # 公共组件
|
||||
├── composables/ # 组合式函数
|
||||
├── directives/ # 自定义指令
|
||||
├── router/ # 路由配置
|
||||
├── stores/ # 状态管理
|
||||
├── styles/ # 全局样式
|
||||
├── utils/ # 工具函数
|
||||
└── views/ # 页面组件
|
||||
```
|
||||
|
||||
### 10.2 Git提交规范
|
||||
```bash
|
||||
# 安装commitlint
|
||||
npm install @commitlint/config-conventional @commitlint/cli -D
|
||||
|
||||
# 创建.commitlintrc.js
|
||||
echo "module.exports = { extends: ['@commitlint/config-conventional'] }" > .commitlintrc.js
|
||||
```
|
||||
|
||||
### 10.3 环境变量配置
|
||||
创建`.env`文件:
|
||||
```env
|
||||
VITE_API_BASE_URL=https://api.example.com
|
||||
VITE_APP_TITLE=My Vue App
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 安装缓慢?
|
||||
- 使用淘宝镜像:`npm config set registry https://registry.npmmirror.com`
|
||||
- 使用pnpm:`npm install -g pnpm`
|
||||
|
||||
### Q: 端口被占用?
|
||||
```javascript
|
||||
// vite.config.js
|
||||
server: {
|
||||
port: 3000 // 修改端口号
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 需要支持旧浏览器?
|
||||
```javascript
|
||||
// vite.config.js
|
||||
import legacy from '@vitejs/plugin-legacy'
|
||||
|
||||
export default {
|
||||
plugins: [
|
||||
legacy({
|
||||
targets: ['defaults', 'not IE 11']
|
||||
})
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
theme: jekyll-theme-minimal
|
||||
@@ -1,408 +0,0 @@
|
||||
# CSS 效果字典 — 注入代码合集
|
||||
|
||||
> 本文档收集了 *30+* 个常用且有趣的 CSS 注入片段。每个片段都包含:
|
||||
> - **名称**(用途)
|
||||
> - **代码**(可直接复制到控制台或写入 `<style>`)
|
||||
> - **快速用法**(如何注入/如何移除)
|
||||
|
||||
---
|
||||
|
||||
## 使用说明(快速上手)
|
||||
- **在控制台临时注入**:把下面的 CSS 放到一个 `<style>` 标签里再 append 到 `document.head`。例如:
|
||||
```js
|
||||
const s = document.createElement('style');
|
||||
s.textContent = `/* 把你要的 CSS 放在这里 */`;
|
||||
document.head.appendChild(s);
|
||||
// 移除: s.remove();
|
||||
```
|
||||
- **做成书签脚本(Bookmarklet)**:把单行 JS 从 `javascript:(function(){...})();` 开始保存为书签。
|
||||
- **长期使用**:推荐用 Stylus(仅 CSS) 或 Tampermonkey(JS 注入)管理。
|
||||
|
||||
---
|
||||
|
||||
# 目录
|
||||
1. 纯色背景
|
||||
2. 渐变背景
|
||||
3. 大图背景覆盖
|
||||
4. 模糊背景(玻璃效果)
|
||||
5. 彩虹文字动画
|
||||
6. 文字跑马灯
|
||||
7. 所有链接高亮
|
||||
8. 可视化网格(布局调试)
|
||||
9. 居中容器(快速调试页面布局)
|
||||
10. 旋转页面
|
||||
11. 倒色模式
|
||||
12. 灰阶/暗色模式
|
||||
13. 字体切换(Comic Sans 恶搞)
|
||||
14. 打字机效果
|
||||
15. 卡片阴影增强
|
||||
16. 元素悬停放大(hover zoom)
|
||||
17. 图片变成圆形并加边框
|
||||
18. 所有按钮变成圆角并增大
|
||||
19. 表单输入聚焦样式
|
||||
20. 隐藏页面元素(广告清理器)
|
||||
21. 打印友好(去除背景和广告)
|
||||
22. 代码高亮(简单样式)
|
||||
23. CSS 变量主题切换器(light/dark)
|
||||
24. 霓虹发光边框
|
||||
25. 浮动提示(tooltip 基础样式)
|
||||
26. 闪烁光标(输入框样式)
|
||||
27. 页面文字渐隐渐显(fade)
|
||||
28. 反色/漫画滤镜
|
||||
29. 模拟夜光(色温调整)
|
||||
30. 页面水印
|
||||
|
||||
---
|
||||
|
||||
## 1. 纯色背景
|
||||
**用途**:快速更换页面背景色
|
||||
|
||||
```css
|
||||
body { background: #f0f8ff !important; }
|
||||
```
|
||||
**用法**:把 CSS 放进 `<style>`。移除 style 节点即可。
|
||||
|
||||
---
|
||||
|
||||
## 2. 渐变背景
|
||||
**用途**:让页面有现代渐变效果
|
||||
|
||||
```css
|
||||
body {
|
||||
background: linear-gradient(135deg, #ff9a9e 0%, #fad0c4 50%, #fbc2eb 100%) !important;
|
||||
background-attachment: fixed !important;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 大图背景覆盖
|
||||
**用途**:把页面背景替换为在线图片
|
||||
|
||||
```css
|
||||
body {
|
||||
background-image: url('https://picsum.photos/1600/900') !important;
|
||||
background-size: cover !important;
|
||||
background-position: center center !important;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 模糊背景(玻璃效果)
|
||||
**用途**:制造“毛玻璃”或模糊背景层
|
||||
|
||||
```css
|
||||
body::before{
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
backdrop-filter: blur(8px) saturate(120%);
|
||||
-webkit-backdrop-filter: blur(8px) saturate(120%);
|
||||
pointer-events: none;
|
||||
z-index: 99998;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 彩虹文字动画
|
||||
**用途**:让页面文字颜色循环变化
|
||||
|
||||
```css
|
||||
@keyframes rainbow { 0%{color:#ff0000}20%{color:#ff7f00}40%{color:#ffff00}60%{color:#00ff00}80%{color:#0000ff}100%{color:#8b00ff} }
|
||||
*{ animation: rainbow 4s linear infinite !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 文字跑马灯
|
||||
**用途**:让段落文字像跑马灯一样滚动(适合短文本)
|
||||
|
||||
```css
|
||||
.marqueeize{
|
||||
display:inline-block;
|
||||
white-space:nowrap;
|
||||
animation: marquee 10s linear infinite;
|
||||
}
|
||||
@keyframes marquee{ 0%{transform:translateX(100%)} 100%{transform:translateX(-100%)} }
|
||||
|
||||
/* 使用方法(在控制台把选择的元素加上类):
|
||||
document.querySelectorAll('p, h1, h2, li').forEach(el=>el.classList.add('marqueeize'));
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 所有链接高亮
|
||||
**用途**:在调研或扒站时一眼看出页面中所有链接
|
||||
|
||||
```css
|
||||
a { outline: 2px dashed #ffb86b !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 可视化网格(布局调试)
|
||||
**用途**:显示 12 列网格帮助布局调试
|
||||
|
||||
```css
|
||||
.grid-debug::before{
|
||||
content: "";
|
||||
position: fixed; inset:0; pointer-events:none; z-index:99999;
|
||||
background-image: linear-gradient(transparent 0 0), linear-gradient(transparent 0 0);
|
||||
}
|
||||
/* 更强定制可把背景替换成重复线条或者半透明列 */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 居中容器(快速调试页面布局)
|
||||
**用途**:临时把 body 内主要容器居中并限定宽度
|
||||
|
||||
```css
|
||||
body > * { max-width: 1000px; margin: 0 auto !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 旋转页面
|
||||
**用途**:把页面旋转 180°(搞怪/演示)
|
||||
|
||||
```css
|
||||
html{ transform: rotate(180deg) !important; transform-origin: center center; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 倒色模式
|
||||
**用途**:整体颜色反转(相当于拍照底片效果)
|
||||
|
||||
```css
|
||||
html{ filter: invert(100%) hue-rotate(180deg) !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 灰阶 / 暗色模式
|
||||
**用途**:将页面转为黑白,或更接近暗色(用于阅读)
|
||||
|
||||
```css
|
||||
html{ filter: grayscale(100%) contrast(90%) !important; }
|
||||
/* 或者暗色模式 */
|
||||
:root { --bg:#0f0f12; --fg:#e6e6e6; }
|
||||
body{ background:var(--bg) !important; color:var(--fg) !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. 字体切换(Comic Sans 恶搞)
|
||||
**用途**:瞬间把页面字体切成 Comic Sans
|
||||
|
||||
```css
|
||||
*{ font-family: 'Comic Sans MS', 'Comic Sans', cursive !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 打字机效果
|
||||
**用途**:给指定元素文字加打字机动画(需要把类加到元素)
|
||||
|
||||
```css
|
||||
.typewriter { overflow:hidden; white-space:nowrap; border-right: .12em solid rgba(255,255,255,.75); animation: typing 3s steps(40,end), blink .7s step-end infinite; }
|
||||
@keyframes typing{ from{ width:0 } to{ width:100% } }
|
||||
@keyframes blink{ 50%{ border-color: transparent } }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. 卡片阴影增强
|
||||
**用途**:让页面中常见卡片组件更醒目
|
||||
|
||||
```css
|
||||
.card, .panel, .box { box-shadow: 0 10px 30px rgba(0,0,0,0.25) !important; border-radius: 12px !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 16. 元素悬停放大(hover zoom)
|
||||
**用途**:鼠标移上去时微放大,做交互提示
|
||||
|
||||
```css
|
||||
* { transition: transform .18s ease-in-out !important; }
|
||||
*:hover { transform: translateZ(0) scale(1.02) !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 17. 图片变成圆形并加边框
|
||||
**用途**:把页面所有图片做成头像样式
|
||||
|
||||
```css
|
||||
img { border-radius: 999px !important; border: 3px solid rgba(255,255,255,0.6) !important; object-fit: cover; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 18. 所有按钮变成圆角并增大
|
||||
**用途**:改善按钮可点击面积
|
||||
|
||||
```css
|
||||
button, input[type=button], input[type=submit], .btn { border-radius: 999px !important; padding: .8em 1.2em !important; font-weight:600; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 19. 表单输入聚焦样式
|
||||
**用途**:给输入框加聚焦高亮
|
||||
|
||||
```css
|
||||
input:focus, textarea:focus, select:focus { outline: 3px solid rgba(66,153,225,0.6) !important; box-shadow: 0 0 0 4px rgba(66,153,225,0.12) !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 20. 隐藏页面元素(广告清理器)
|
||||
**用途**:删除常见广告容器或悬浮元素
|
||||
|
||||
```css
|
||||
iframe, .ad, .ads, [id*='ad'], [class*='ad-'] { display: none !important; visibility: hidden !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 21. 打印友好(去除背景和广告)
|
||||
**用途**:在打印前快速隐藏不必要的元素
|
||||
|
||||
```css
|
||||
@media print {
|
||||
body { background: white !important; color: black !important; }
|
||||
iframe, .ad, nav, footer, .sidebar { display: none !important; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 22. 代码高亮(简单样式)
|
||||
**用途**:给 `<code>` / `<pre>` 添加基础美化
|
||||
|
||||
```css
|
||||
pre, code { background:#0b0b0b; color:#e6e6e6; padding:.4em .6em; border-radius:6px; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', 'Helvetica Neue', monospace; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 23. CSS 变量主题切换器(light/dark)
|
||||
**用途**:示范如何用变量快速切换主题
|
||||
|
||||
```css
|
||||
:root{ --bg:#ffffff; --fg:#111111; }
|
||||
[data-theme='dark']{ --bg:#0b0b0f; --fg:#e6e6e6; }
|
||||
body{ background:var(--bg) !important; color:var(--fg) !important; }
|
||||
/* JS 切换: document.documentElement.setAttribute('data-theme','dark'); */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 24. 霓虹发光边框
|
||||
**用途**:给重要元素加炫酷发光边框
|
||||
|
||||
```css
|
||||
.neon { box-shadow: 0 0 8px rgba(255,0,111,0.6), inset 0 0 6px rgba(255,0,111,0.2); border:1px solid rgba(255,0,111,0.5); border-radius:10px; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 25. 浮动提示(tooltip 基础样式)
|
||||
**用途**:给带 `data-tooltip` 的元素显示悬浮提示
|
||||
|
||||
```css
|
||||
[data-tooltip]{ position:relative; }
|
||||
[data-tooltip]:hover::after{ content: attr(data-tooltip); position:absolute; left:50%; transform:translateX(-50%); bottom:120%; white-space:nowrap; padding:.3em .6em; background:#111;color:#fff;border-radius:6px; font-size:12px; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 26. 闪烁光标(输入框样式)
|
||||
**用途**:增强输入框的视觉焦点
|
||||
|
||||
```css
|
||||
input[type='text']{ caret-color: #ff5c7c; }
|
||||
input[type='text']::placeholder{ opacity: .7; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 27. 页面文字渐隐渐显(fade)
|
||||
**用途**:页面加载时做个渐入动画,提升体验
|
||||
|
||||
```css
|
||||
.fade-in *{ opacity:0; transform: translateY(6px); animation: fadein .6s forwards; }
|
||||
@keyframes fadein{ to{ opacity:1; transform:none; } }
|
||||
/* 使用: document.body.classList.add('fade-in') */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 28. 反色 / 漫画滤镜
|
||||
**用途**:把页面变成卡通/漫画风格
|
||||
|
||||
```css
|
||||
html{ filter: contrast(150%) saturate(130%) sepia(10%) !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 29. 模拟夜光(色温调整)
|
||||
**用途**:降低蓝光,适合晚间阅读
|
||||
|
||||
```css
|
||||
html{ filter: sepia(10%) hue-rotate(-20deg) brightness(0.95) !important; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 30. 页面水印
|
||||
**用途**:给页面加浮动水印(仅视觉)
|
||||
|
||||
```css
|
||||
body::after{
|
||||
content: '仅供测试 — 树萌芽';
|
||||
position: fixed; right: 10px; bottom: 10px; opacity: .12; font-size: 14px; pointer-events:none;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 额外:通用注入 / 开关 模板(JS)
|
||||
把下面的 JS 复制到控制台或做成书签,一键注入或移除指定的 CSS:
|
||||
|
||||
```js
|
||||
(function toggleCSS(id, css){
|
||||
let s = document.getElementById(id);
|
||||
if(s){ s.remove(); return 'removed'; }
|
||||
s = document.createElement('style'); s.id = id; s.textContent = css; document.head.appendChild(s); return 'added';
|
||||
})('my-fun-css', 'body{ background: #ffdead !important; }');
|
||||
```
|
||||
|
||||
- 把 `my-fun-css` 改成有意义的 id,把 css 换成任意上面的片段。
|
||||
|
||||
---
|
||||
|
||||
## Tampermonkey 用户脚本模板(自动注入)
|
||||
```js
|
||||
// ==UserScript==
|
||||
// @name Inject Custom CSS
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 0.1
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// ==/UserScript==
|
||||
(function(){
|
||||
'use strict';
|
||||
const css = `/* 把 CSS 放这里 */`;
|
||||
const s = document.createElement('style'); s.textContent = css; document.head.appendChild(s);
|
||||
})();
|
||||
```
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
QQ机器人(napcat)
|
||||
docker run -d \
|
||||
-e NAPCAT_GID=$(id -g) \
|
||||
-e NAPCAT_UID=$(id -u) \
|
||||
-p 3000:3000 \
|
||||
-p 3001:3001 \
|
||||
-p 3002:6099 \
|
||||
--name napcat \
|
||||
--restart=always \
|
||||
mlikiowa/napcat-docker:latest
|
||||
|
||||
微信机器人(gewe)
|
||||
docker run -itd -v /shumengya/bin/wechatbot:/root/temp -p 2531:2531 -p 2532:2532 --privileged=true --name=gewe gewe /usr/sbin/init
|
||||
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
### 使用 yum 安装 systemd(CentOS/RHEL)
|
||||
```
|
||||
yum install systemd
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 使用 apt 安装 systemd(Debian/Ubuntu)
|
||||
```
|
||||
apt install systemd
|
||||
sudo vim /etc/systemd/system/frps.service
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 服务名称,可自定义
|
||||
```
|
||||
Description = frp server
|
||||
After = network.target syslog.target
|
||||
Wants = network.target
|
||||
|
||||
[Service]
|
||||
Type = simple
|
||||
```
|
||||
|
||||
|
||||
### 启动frps的命令,需修改为您的frps的安装路径
|
||||
```
|
||||
ExecStart = /path/to/frps -c /path/to/frps.toml
|
||||
|
||||
[Install]
|
||||
WantedBy = multi-user.target
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 启动frp
|
||||
```
|
||||
sudo systemctl start frps
|
||||
```
|
||||
|
||||
|
||||
### 停止frp
|
||||
```
|
||||
sudo systemctl stop frps
|
||||
```
|
||||
|
||||
|
||||
### 重启frp
|
||||
```
|
||||
sudo systemctl restart frps
|
||||
```
|
||||
|
||||
|
||||
### 查看frp状态
|
||||
```
|
||||
sudo systemctl status frps
|
||||
sudo systemctl enable frps
|
||||
```
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
https://storage.googleapis.com/docscloudstorage/2022.3/UnityDocumentation.zip
|
||||
|
||||
ChatGPT响应: Markdown中的一些常用标识符包括:
|
||||
|
||||
1. **标题**:使用`#`表示不同级别的标题
|
||||
```markdown
|
||||
# 一级标题
|
||||
## 二级标题
|
||||
### 三级标题
|
||||
```
|
||||
|
||||
2. **粗体**:使用`**`或`__`包围文本
|
||||
```markdown
|
||||
**粗体文本** 或 __粗体文本__
|
||||
```
|
||||
|
||||
3. **斜体**:使用`*`或`_`包围文本
|
||||
```markdown
|
||||
*斜体文本* 或 _斜体文本_
|
||||
```
|
||||
|
||||
4. **无序列表**:使用`-`、`*`或`+`
|
||||
```markdown
|
||||
- 项目1
|
||||
* 项目2
|
||||
+ 项目3
|
||||
```
|
||||
|
||||
5. **有序列表**:使用数字加点
|
||||
```markdown
|
||||
1. 第一项
|
||||
2. 第二项
|
||||
```
|
||||
|
||||
6. **链接**:使用`[链接文本](URL)`
|
||||
```markdown
|
||||
[这是一个链接](https://example.com)
|
||||
```
|
||||
|
||||
7. **图片**:使用``
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
8. **代码块**:使用反引号`` ` ``
|
||||
```markdown
|
||||
`行内代码`
|
||||
```
|
||||
|
||||
或使用三个反引号表示多行代码
|
||||
```markdown
|
||||
```
|
||||
多行代码
|
||||
```
|
||||
```
|
||||
|
||||
9. **引用**:使用`>`
|
||||
```markdown
|
||||
> 这是一个引用
|
||||
```
|
||||
|
||||
10. **水平线**:使用三个或以上的`-`、`*`或`_`
|
||||
```markdown
|
||||
---
|
||||
```
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
1. **markdown-it**:
|
||||
|
||||
- **简介**: 一个功能强大且可扩展的 Markdown 解析器,支持插件和自定义规则。
|
||||
- **安装**: 可以通过 npm 安装:`npm install markdown-it`
|
||||
- **使用示例**:
|
||||
```javascript
|
||||
const MarkdownIt = require('markdown-it');
|
||||
const md = new MarkdownIt();
|
||||
const result = md.render('# 你好,Markdown!');
|
||||
console.log(result);
|
||||
```
|
||||
|
||||
2. **marked**:
|
||||
- **简介**: 一个快速、轻量级的 Markdown 解析器,支持自定义渲染器。
|
||||
- **安装**: 使用 npm 安装:`npm install marked`
|
||||
- **使用示例**:
|
||||
```javascript
|
||||
const marked = require('marked');
|
||||
const result = marked('# 你好,Markdown!');
|
||||
console.log(result);
|
||||
```
|
||||
|
||||
3. **remark**:
|
||||
- **简介**: 一个强大的 Markdown 处理库,支持解析、检查和转换 Markdown 文档。
|
||||
- **安装**: 通过 npm 安装:`npm install remark remark-html`
|
||||
- **使用示例**:
|
||||
```javascript
|
||||
const remark = require('remark');
|
||||
const html = require('remark-html');
|
||||
|
||||
remark()
|
||||
.use(html)
|
||||
.process('# 你好,Markdown!', function (err, file) {
|
||||
if (err) throw err;
|
||||
console.log(String(file));
|
||||
});
|
||||
```
|
||||
|
||||
这些库各有优点,选择哪个库取决于您的具体需求,比如性能、可扩展性、支持的特性等。如果你需要简单的解析和渲染,`marked` 和 `markdown-it` 都是不错的选择;如果需要更多的可扩展性和插件支持,`remark` 可能更适合。
|
||||
@@ -1,283 +0,0 @@
|
||||
|
||||
### Linux `systemctl` 命令常用用法总结
|
||||
|
||||
|
||||
#### **一、服务管理(核心功能)**
|
||||
`systemctl` 主要用于管理系统服务(`.service` 文件),支持启动、停止、重启、查看状态、设置开机启动等操作。
|
||||
|
||||
##### 1. 服务状态操作
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 启动服务 | `systemctl start <服务名>` | 启动指定服务(仅临时生效,重启后不保留) |
|
||||
| 停止服务 | `systemctl stop <服务名>` | 停止指定服务 |
|
||||
| 重启服务 | `systemctl restart <服务名>` | 重启服务(若服务未运行则启动) |
|
||||
| 重新加载配置 | `systemctl reload <服务名>` | 重新加载服务配置文件(不重启服务,适用于支持此功能的服务,如 Nginx) |
|
||||
| 平滑重启(优雅重启)| `systemctl try-restart <服务名>` | 仅在服务运行时重启,避免报错 |
|
||||
|
||||
##### 2. 服务状态查询
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 查看状态 | `systemctl status <服务名>` | 显示服务运行状态、日志及依赖关系(按 `Q` 退出) |
|
||||
| 简洁状态 | `systemctl is-active <服务名>` | 仅显示服务是否激活(active/running) |
|
||||
| 查看是否启用开机启动| `systemctl is-enabled <服务名>` | 显示服务是否设置为开机启动(enabled/disabled) |
|
||||
| 列出所有服务 | `systemctl list-units --type=service` | 列出所有运行中的服务 |
|
||||
| 列出所有服务(含未激活)| `systemctl list-units --type=service --all` | 显示所有服务,包括未激活的(状态为 inactive/failed 等) |
|
||||
| 列出失败的服务 | `systemctl --failed --type=service` | 查看启动失败的服务 |
|
||||
|
||||
##### 3. 开机启动管理
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 启用开机启动 | `systemctl enable <服务名>` | 设置服务开机自动启动(永久生效,关联到默认运行目标) |
|
||||
| 禁用开机启动 | `systemctl disable <服务名>` | 取消服务开机启动 |
|
||||
| 临时启用(仅本次启动)| `systemctl enable --now <服务名>` | 立即启动服务并设置开机启动 |
|
||||
| 临时禁用(停止并取消启动)| `systemctl disable --now <服务名>` | 立即停止服务并取消开机启动 |
|
||||
|
||||
##### 4. 服务依赖与依赖关系
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 查看依赖关系 | `systemctl list-dependencies <服务名>` | 显示服务的依赖项(依赖它的服务 + 它依赖的服务) |
|
||||
| 查看直接依赖 | `systemctl list-dependencies --reverse <服务名>` | 仅显示依赖该服务的其他服务 |
|
||||
|
||||
|
||||
#### **二、系统状态与控制**
|
||||
##### 1. 系统基本状态
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 查看系统运行时间 | `systemctl uptime` | 显示系统已运行时间(类似 `uptime` 命令) |
|
||||
| 查看启动耗时 | `systemctl time` | 显示系统启动总耗时及各阶段(内核、用户空间、服务)耗时 |
|
||||
| 查看登录会话 | `systemctl list-sessions` | 列出当前登录的用户会话 |
|
||||
|
||||
##### 2. 电源管理(需管理员权限)
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 关机 | `systemctl poweroff` | 立即关机 |
|
||||
| 重启 | `systemctl reboot` | 立即重启 |
|
||||
| 休眠(挂起到硬盘) | `systemctl hibernate` | 系统进入休眠状态 |
|
||||
| 睡眠(挂起到内存) | `systemctl suspend` | 系统进入睡眠状态(低功耗,断电数据丢失) |
|
||||
| 混合休眠 | `systemctl hybrid-sleep` | 同时休眠到内存和硬盘,结合两者优势 |
|
||||
| 取消挂起/休眠 | `systemctl wakeup` | 唤醒系统(需配合定时器使用) |
|
||||
|
||||
##### 3. 运行目标(替代传统运行级别)
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 查看当前目标 | `systemctl get-default` | 显示默认运行目标(如 `graphical.target` 对应图形界面) |
|
||||
| 切换目标 | `systemctl isolate <目标名>` | 切换到指定目标(如 `multi-user.target` 为无图形多用户模式) |
|
||||
| 设置默认目标 | `systemctl set-default <目标名>` | 设置开机默认目标(需管理员权限) |
|
||||
|
||||
**常用目标对应关系**:
|
||||
- `graphical.target`:图形界面(对应传统运行级别 5)
|
||||
- `multi-user.target`:无图形多用户模式(对应级别 3)
|
||||
- `rescue.target`:单用户救援模式(对应级别 1)
|
||||
- `emergency.target`:紧急模式(无文件系统挂载,仅基本命令)
|
||||
|
||||
|
||||
#### **三、定时器管理(systemd.timer)**
|
||||
`systemctl` 可管理定时任务(替代传统 `cron`),需配合 `.timer` 文件使用。
|
||||
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 列出所有定时器 | `systemctl list-timers` | 显示激活的定时器及其下一次触发时间 |
|
||||
| 查看定时器状态 | `systemctl status <定时器名>` | 显示定时器详细状态及日志 |
|
||||
| 启用/禁用定时器 | `systemctl enable/disable <定时器名>` | 控制定时器是否开机启动 |
|
||||
| 立即触发定时器任务 | `systemctl start <定时器名>` | 强制立即执行定时器关联的服务 |
|
||||
|
||||
|
||||
#### **四、资源控制与限制(高级功能)**
|
||||
可对服务设置 CPU、内存、IO 等资源限制(需在服务文件中配置,或临时生效)。
|
||||
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 设置 CPU 核心限制 | `systemctl set-property <服务名> CPUAffinity=0,1` | 限制服务仅运行在 CPU 0 和 1 核心 |
|
||||
| 设置内存上限 | `systemctl set-property <服务名> MemoryMax=1G` | 限制服务最大内存使用量为 1GB |
|
||||
| 恢复默认资源配置 | `systemctl unset-property <服务名> CPUAffinity MemoryMax` | 清除已设置的资源限制 |
|
||||
|
||||
|
||||
#### **五、日志相关(结合 journalctl)**
|
||||
| 操作 | 命令 | 说明 |
|
||||
|---------------------|---------------------------------------|----------------------------------------------------------------------|
|
||||
| 查看服务日志 | `systemctl status <服务名> -l` | 显示服务状态并附带日志(`-l` 展开完整日志) |
|
||||
| 实时监控日志 | `journalctl -u <服务名> -f` | 实时追踪服务日志(需结合 `journalctl` 命令) |
|
||||
| 查看历史日志 | `journalctl -u <服务名> --since "2025-05-01"` | 查看指定时间后的服务日志 |
|
||||
|
||||
|
||||
#### **六、常用选项与技巧**
|
||||
- **通配符匹配**:
|
||||
可使用通配符管理同类服务,例如:
|
||||
```bash
|
||||
systemctl start nginx*.service # 启动所有以 nginx 开头的服务
|
||||
systemctl stop *.timer # 停止所有定时器服务
|
||||
```
|
||||
|
||||
- **强制操作**:
|
||||
`--force` 用于强制停止/重启服务(可能终止进程):
|
||||
```bash
|
||||
systemctl --force stop <服务名>
|
||||
```
|
||||
|
||||
- **临时生效(不修改配置)**:
|
||||
使用 `--now` 可立即执行操作并关联开机启动(或取消):
|
||||
```bash
|
||||
systemctl enable --now <服务名> # 启动并启用开机启动
|
||||
systemctl disable --now <服务名> # 停止并禁用开机启动
|
||||
```
|
||||
|
||||
- **查看帮助**:
|
||||
```bash
|
||||
systemctl --help # 查看全局帮助
|
||||
systemctl help <子命令> # 查看子命令详细用法(如 systemctl help start)
|
||||
```
|
||||
|
||||
|
||||
#### **七、示例场景**
|
||||
1. **管理 Nginx 服务**:
|
||||
```bash
|
||||
systemctl start nginx # 启动 Nginx
|
||||
systemctl enable nginx # 设置开机启动
|
||||
systemctl status nginx # 查看运行状态
|
||||
systemctl reload nginx # 重新加载配置
|
||||
```
|
||||
|
||||
2. **系统关机与重启**:
|
||||
```bash
|
||||
systemctl reboot # 立即重启
|
||||
systemctl poweroff # 立即关机
|
||||
systemctl suspend # 进入睡眠
|
||||
```
|
||||
|
||||
3. **设置默认运行目标**:
|
||||
```bash
|
||||
systemctl set-default multi-user.target # 设为无图形多用户模式
|
||||
systemctl isolate rescue.target # 进入单用户救援模式
|
||||
```
|
||||
|
||||
|
||||
### 总结
|
||||
`systemctl` 是 systemd 生态的核心工具,覆盖服务管理、系统控制、定时任务、资源限制等功能。熟练掌握其常用命令可高效管理 Linux 系统服务与状态,替代传统 SysVinit 的 `service`、`chkconfig` 等工具。建议结合 `journalctl` 分析服务日志,通过服务文件(`/etc/systemd/system/`)自定义复杂配置。`systemctl` 是 Linux 系统中用于控制 `systemd` 系统和服务管理器的命令。以下是 `systemctl` 命令的一些常用用法总结:
|
||||
|
||||
### 1. 服务管理
|
||||
#### 启动服务
|
||||
```bash
|
||||
systemctl start <服务名>
|
||||
```
|
||||
例如,启动 `httpd` 服务:
|
||||
```bash
|
||||
systemctl start httpd
|
||||
```
|
||||
|
||||
#### 停止服务
|
||||
```bash
|
||||
systemctl stop <服务名>
|
||||
```
|
||||
例如,停止 `httpd` 服务:
|
||||
```bash
|
||||
systemctl stop httpd
|
||||
```
|
||||
|
||||
#### 重启服务
|
||||
```bash
|
||||
systemctl restart <服务名>
|
||||
```
|
||||
例如,重启 `httpd` 服务:
|
||||
```bash
|
||||
systemctl restart httpd
|
||||
```
|
||||
|
||||
#### 重新加载服务配置
|
||||
```bash
|
||||
systemctl reload <服务名>
|
||||
```
|
||||
例如,重新加载 `nginx` 服务配置:
|
||||
```bash
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
#### 查看服务状态
|
||||
```bash
|
||||
systemctl status <服务名>
|
||||
```
|
||||
例如,查看 `httpd` 服务状态:
|
||||
```bash
|
||||
systemctl status httpd
|
||||
```
|
||||
|
||||
### 2. 服务的开机自启管理
|
||||
#### 设置服务开机自启
|
||||
```bash
|
||||
systemctl enable <服务名>
|
||||
```
|
||||
例如,设置 `httpd` 服务开机自启:
|
||||
```bash
|
||||
systemctl enable httpd
|
||||
```
|
||||
|
||||
#### 禁止服务开机自启
|
||||
```bash
|
||||
systemctl disable <服务名>
|
||||
```
|
||||
例如,禁止 `httpd` 服务开机自启:
|
||||
```bash
|
||||
systemctl disable httpd
|
||||
```
|
||||
|
||||
#### 查看服务是否开机自启
|
||||
```bash
|
||||
systemctl is-enabled <服务名>
|
||||
```
|
||||
例如,查看 `httpd` 服务是否开机自启:
|
||||
```bash
|
||||
systemctl is-enabled httpd
|
||||
```
|
||||
|
||||
### 3. 系统管理
|
||||
#### 重启系统
|
||||
```bash
|
||||
systemctl reboot
|
||||
```
|
||||
|
||||
#### 关机
|
||||
```bash
|
||||
systemctl poweroff
|
||||
```
|
||||
|
||||
#### 挂起系统
|
||||
```bash
|
||||
systemctl suspend
|
||||
```
|
||||
|
||||
#### 休眠系统
|
||||
```bash
|
||||
systemctl hibernate
|
||||
```
|
||||
|
||||
#### 混合休眠(挂起并休眠)
|
||||
```bash
|
||||
systemctl hybrid-sleep
|
||||
```
|
||||
|
||||
### 4. 查看系统状态
|
||||
#### 查看系统当前运行级别
|
||||
```bash
|
||||
systemctl get-default
|
||||
```
|
||||
|
||||
#### 设置系统默认运行级别
|
||||
```bash
|
||||
systemctl set-default <目标运行级别>
|
||||
```
|
||||
例如,设置系统默认运行级别为多用户模式:
|
||||
```bash
|
||||
systemctl set-default multi-user.target
|
||||
```
|
||||
|
||||
### 5. 查看服务列表
|
||||
#### 查看所有已激活的服务
|
||||
```bash
|
||||
systemctl list-units --type=service --state=active
|
||||
```
|
||||
|
||||
#### 查看所有服务(包括未激活的)
|
||||
```bash
|
||||
systemctl list-units --type=service --all
|
||||
```
|
||||
|
||||
这些是 `systemctl` 命令的一些常用用法,掌握这些用法可以帮助你有效地管理 Linux 系统中的服务和系统状态。
|
||||
@@ -1,12 +0,0 @@
|
||||
# 填坑专栏
|
||||
#### 这里整理一些容易出现的坑,这里会有解决方案.
|
||||
##### 大家可以把自己平时的坑写进去,写上解决方案
|
||||
##### 或者等待别人写上解决方案
|
||||
|
||||
1. ClassCastException
|
||||
这种是类转换问题的锅,可以查看下
|
||||
2. EventException: null
|
||||
3. Main类名称指错的问题
|
||||
4. ClassNotFoundException的问题
|
||||
5. NullPointerException的常见问题
|
||||
6. 忘记注册监听器的坑
|
||||
@@ -1,3 +0,0 @@
|
||||
```javascript
|
||||
console.log("Hello, World!");
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
```javascript
|
||||
//各种奇奇怪怪的方法
|
||||
// 方法1: 使用Unicode转义
|
||||
console.log('\u0048\u0065\u006c\u006c\u006f\u002c\u0020\u0057\u006f\u0072\u006c\u0064\u0021');
|
||||
|
||||
// 方法2: 使用Array和join
|
||||
console.log(['Hello', 'World!'].join(', '));
|
||||
|
||||
// 方法3: 使用定时器
|
||||
setTimeout(() => console.log('Hello, World!'), 0);
|
||||
|
||||
// 方法4: 使用Promise
|
||||
Promise.resolve('Hello, World!').then(console.log);
|
||||
|
||||
// 方法5: 使用函数式编程
|
||||
[].concat('Hello, World!').map(console.log);
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
---
|
||||
|
||||
## 一、开源替代方案 /工具清单
|
||||
|
||||
下面是目前社区里比较流行 /有实用性的开源方案:
|
||||
|
||||
|名称|语言 / 技术栈|支持协议 /功能|特点 /亮点|可能缺点 /注意|
|
||||
|---|---|---|---|---|
|
||||
|**rathole**|Rust|TCP / HTTP /TLS|轻量、高性能,内存占用低,支持热重载、服务令牌认证等。被设计为 FRP / ngrok 的替代品。 ([GitHub](https://github.com/rathole-org/rathole?utm_source=chatgpt.com "GitHub - rathole-org/rathole: A lightweight and high-performance ..."))|文档 /社区可能不如 FRP 那么成熟;一些边缘功能可能欠缺|
|
||||
|**NSmartProxy**|.NET Core / C#|TCP / HTTP 反向代理 /穿透|跨平台支持好,用异步 I/O 架构驱动穿透代理。 ([GitHub](https://github.com/tmoonlight/NSmartProxy?utm_source=chatgpt.com "GitHub - tmoonlight/NSmartProxy: NSmartProxy是一款开源的内网穿透工具。采用.NET CORE ..."))|在 Linux /非 .NET 平台上的稳定性 /优化要测试;社区规模可能偏小|
|
||||
|**Tunnelmole**|Node.js / TypeScript|HTTP / HTTPS 隧道|100% 开源、可自托管、方便给本地 Web 服务生成公开 URL。 ([tunnelmole.com](https://tunnelmole.com/?utm_source=chatgpt.com "Tunnelmole - A free and open source tunneling tool"))|主要面向 HTTP/HTTPS,游戏 /通用 TCP 服务可能支持不够好;对于高并发 /大流量性能需要评估|
|
||||
|**Reverse Proxy Tool(基于 Netty 的)**|Java / Netty|支持任意 TCP 上层协议和 HTTP|支持 HTTP 升级、SSL 验证、协议透传等功能。 ([Gitee](https://gitee.com/codebanks/reverse-proxy-tool?utm_source=chatgpt.com "Reverse Proxy Tool: Reverse Proxy Tool : 基于Netty实现的 ..."))|Java 程序可能资源占用较高;配置可能稍复杂;性能需要根据 JVM 优化|
|
||||
|**nps / NPS** / **lanproxy** / **holer**|Go /其他|TCP / HTTP /UDP 穿透、反向代理|在「开源内网穿透工具」合集 /对比文章里经常被提及。 ([laoliang.net](https://www.laoliang.net/jsjh/technology/14975.html?utm_source=chatgpt.com "归纳几种免费开源的内网穿透工具(frp、holer、nps、lanproxy)"))|各工具在稳定性、协议支持深度、社区维护度上有差别;需要逐个评估|
|
||||
|**reverse-tunnel(snsi nfu 的项目)**|(未知 /社区项目)|公网 → 私网 隧道 /映射|提供反向隧道映射能力,据社区介绍可以做安全隧道。 ([CSDN.blog](https://blog.csdn.net/gitblog_01080/article/details/141481391?utm_source=chatgpt.com "反向隧道工具 reverse-tunnel 使用指南-CSDN博客"))|项目可能成熟度不高,文档 /示例可能不足;功能边界需自己测试|
|
||||
|**tiny-frpc**|Go / 精简 FRP 客户端|frp 协议子集|体积小、适合资源紧张的嵌入式 /小设备使用。 ([博客园](https://www.cnblogs.com/tang863/p/18274521?utm_source=chatgpt.com "tiny-frpc: frp 反向代理精简版客户端 - AmbiaCode - 博客园"))|只是 frpc 的子集,用于客户端,不是完整替代;功能限制较多|
|
||||
|
||||
---
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
|
||||
---
|
||||
|
||||
## 1) 本地文件(Local)
|
||||
|
||||
**说明**:把资源放在自己服务器 / 项目目录,通过相对或绝对路径引用(`/assets/`、`./`、`../` 等)。
|
||||
|
||||
**示例**
|
||||
|
||||
```html
|
||||
<!-- CSS(放 head) -->
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
|
||||
<!-- JS(放 body 末尾或 head + defer) -->
|
||||
<script src="/js/app.js" defer></script>
|
||||
```
|
||||
|
||||
**优点**
|
||||
|
||||
- 完全可控:部署、版本、更新由自己管理。
|
||||
- 离线/内网可用(不依赖外部网络)。
|
||||
- 容易与构建流程(hash、cache-bust)集成。
|
||||
|
||||
**缺点**
|
||||
|
||||
- 需要自己承担带宽与缓存策略。
|
||||
- 同一资源不会被不同站点共享缓存(除非使用统一 CDN)。
|
||||
- 初次请求可能比公共 CDN 略慢(取决于服务器配置/地域)。
|
||||
|
||||
---
|
||||
|
||||
## 2) 网页链接 / CDN(Remote)
|
||||
|
||||
**说明**:引用第三方 URL(例如公用 CDN、第三方库托管),常用于库(jQuery、Bootstrap)、字体、图标或通用资源。
|
||||
|
||||
**示例**
|
||||
|
||||
```html
|
||||
<!-- CSS(CDN) -->
|
||||
<link rel="stylesheet" href="https://cdn.example.com/bootstrap/5.0.0/bootstrap.min.css">
|
||||
|
||||
<!-- JS(CDN + SRI + crossorigin) -->
|
||||
<script src="https://cdn.example.com/jquery/3.7.0/jquery.min.js"
|
||||
integrity="sha384-abc123..."
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<!-- 分析类脚本可用 async -->
|
||||
<script src="https://cdn.example.com/analytics.js" async></script>
|
||||
```
|
||||
|
||||
**优点**
|
||||
|
||||
- 全球分发、通常延迟更低、CDN 节点可被多站点共享缓存。
|
||||
- 节省自家带宽;外部资源常有高可用性。
|
||||
- 快速集成常用库(省去打包/托管工作)。
|
||||
|
||||
**缺点**
|
||||
|
||||
- 依赖第三方可用性与隐私/合规(跨域、GDPR、内网限制)。
|
||||
- 安全风险:若 CDN 被劫持会影响站点(可用 SRI + crossorigin 缓解)。
|
||||
- 访问受限时(企业内网、无外网环境)会失败。
|
||||
|
||||
---
|
||||
|
||||
## 对比速览(关键点)
|
||||
|
||||
- 控制权:本地 ✅✅ | CDN ✅(较弱)
|
||||
- 性能(首屏/全球):CDN ✅ | 本地 视服务器而定
|
||||
- 可用性(离线/内网):本地 ✅ | CDN ❌
|
||||
- 缓存共享:CDN ✅ | 本地 ❌
|
||||
- 安全(篡改风险):本地 ✅ | CDN 需 SRI+cors
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践(简短清单)
|
||||
|
||||
- **CSS**:放 `<head>`;关键 CSS 可内联;非关键可 `preload` + onload 切换。
|
||||
- **JS**:外部脚本优先用 `defer`(保持顺序),第三方可用 `async`(若相互独立)。
|
||||
- **版本管理**:本地资源使用 hash(`app.abc123.js`)或 querystring(`?v=20251030`)做 cache-bust。
|
||||
- **安全**:使用 CDN 时为第三方静态资源加 `integrity` + `crossorigin`。
|
||||
- **回退/兼容**:必要时对重要外部资源提供本地回退(先尝试 CDN,失败再加载本地)。
|
||||
- **隐私合规**:评估第三方脚本会不会传输用户数据,必要时加载前征得同意。
|
||||
|
||||
|
||||
---
|
||||
@@ -1,4 +0,0 @@
|
||||
# 春日限定:
|
||||
赴一场自然与生机的约会 春回大地时,万物挣脱冬日的沉寂,以最鲜活的姿态铺满视野。无论是枝头抽芽的新绿、田间绽放的繁花,还是拂面而来的暖风,都在诉说着季节的温柔絮语。
|
||||
---
|
||||
## 一、春日限定的自然画卷 春日的美,藏在每一处细腻的景致里。清晨的林间,露珠挂在嫩绿的叶片上,折射着柔和的晨光;午后的花海,各色花朵竞相开放,蝴蝶在花丛中翩跹起舞;傍晚的河畔,夕阳为水面镀上金边,归鸟的鸣啼伴着流水声,构成一幅宁静而治愈的画面。  *图注:清晨的林间,露珠与新绿交织出春日的清新气息* --- ## 二、解锁春日的正确打开方式 1. 踏青寻芳:走进公园、郊外或山林,沉浸式感受草木生长的力量,呼吸带着花香的新鲜空气。 2. 野餐小聚:约上好友,带着美食与餐布,在草坪上享受阳光与陪伴,记录惬意时光。 3. 绿植养护:在家中摆放几盆绿植或鲜花,让春日的生机延伸到室内,点缀日常空间。  *图注:在洒满阳光的草坪上,与好友共度轻松惬意的野餐时刻* --- ## 三、春日里的生活感悟 春天是充满希望的季节,它教会我们接纳变化、拥抱新生。就像树木在经历寒冬后重新发芽,生活也会在坚持与等待中迎来转机。不妨趁着春日正好,放慢脚步,感受身边的美好,为自己注入前行的力量。 --- 要不要我帮你生成**不同主题(如旅行、美食、读书)的带图片Markdown文章模板**?你只需告诉我具体主题,我就能快速适配内容和图片搭配。
|
||||
@@ -1,77 +0,0 @@
|
||||
要使得某个 `.sh` 脚本能够在系统中全局执行,你可以将该脚本放在 **系统的 `PATH` 路径中**,使其在任何地方都能被执行。常见的目录有:
|
||||
|
||||
### 1. **`/usr/local/bin/`**
|
||||
这是推荐的目录,因为它是用来放置系统管理员手动安装或编译的软件及脚本的。脚本放在这个目录中,可以确保它对所有用户可用,并且不会与系统自带的命令冲突。
|
||||
|
||||
步骤:
|
||||
1. 将你的 `.sh` 脚本复制到 `/usr/local/bin/` 目录下。
|
||||
|
||||
```bash
|
||||
sudo cp your_script.sh /usr/local/bin/
|
||||
```
|
||||
|
||||
2. 赋予脚本执行权限:
|
||||
|
||||
```bash
|
||||
sudo chmod +x /usr/local/bin/your_script.sh
|
||||
```
|
||||
|
||||
3. 确保脚本能被执行,可以通过命令行直接调用(例如 `your_script.sh`),无需指定路径。
|
||||
|
||||
### 2. **`/usr/bin/`**
|
||||
这个目录通常用于系统范围内的二进制文件,和 `/usr/local/bin/` 类似。但是,建议将自定义脚本放在 `/usr/local/bin/` 目录下,因为 `/usr/bin/` 通常用于通过包管理器安装的系统级命令。
|
||||
|
||||
步骤:
|
||||
1. 将脚本复制到 `/usr/bin/` 目录:
|
||||
|
||||
```bash
|
||||
sudo cp your_script.sh /usr/bin/
|
||||
```
|
||||
|
||||
2. 赋予执行权限:
|
||||
|
||||
```bash
|
||||
sudo chmod +x /usr/bin/your_script.sh
|
||||
```
|
||||
|
||||
### 3. **`/bin/`**(不推荐)
|
||||
`/bin/` 目录通常用于存放系统核心命令和工具,普通用户通常不建议将自定义脚本放在这个目录中,除非它们确实是系统必需的命令。通常会将脚本放置在 `/usr/local/bin/` 或 `/usr/bin/` 中。
|
||||
|
||||
### 4. **用户特定的目录(例如 `/home/username/bin/`)**
|
||||
如果你只想为某个特定用户设置全局脚本,而不是所有用户可以访问的脚本,你可以将脚本放在该用户的 `~/bin/` 目录下(如果该目录不存在,可以自己创建)。确保将 `~/bin/` 添加到用户的 `PATH` 环境变量中。
|
||||
|
||||
步骤:
|
||||
1. 创建 `~/bin/` 目录(如果没有的话):
|
||||
|
||||
```bash
|
||||
mkdir -p ~/bin
|
||||
```
|
||||
|
||||
2. 将脚本复制到 `~/bin/` 目录:
|
||||
|
||||
```bash
|
||||
cp your_script.sh ~/bin/
|
||||
```
|
||||
|
||||
3. 赋予执行权限:
|
||||
|
||||
```bash
|
||||
chmod +x ~/bin/your_script.sh
|
||||
```
|
||||
|
||||
4. 编辑 `~/.bashrc` 或 `~/.profile`,将 `~/bin/` 添加到 `PATH`:
|
||||
|
||||
```bash
|
||||
export PATH=$PATH:~/bin
|
||||
```
|
||||
|
||||
5. 使 `~/.bashrc` 变更生效:
|
||||
|
||||
```bash
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### 总结
|
||||
- **最推荐的目录:** `/usr/local/bin/`,适用于系统范围内的自定义脚本。
|
||||
- **其他可选目录:** `/usr/bin/`(不推荐),如果需要放系统级命令可以选择该目录。
|
||||
- **特定用户的脚本:** 用户自己的 `~/bin/` 目录。
|
||||
@@ -1,388 +0,0 @@
|
||||
derp(Docker安装):
|
||||
|
||||
```bash
|
||||
#Docker容器一键化启动
|
||||
#默认开启tcp端口33445 udp端口3478
|
||||
docker run --restart always \
|
||||
--name derper1 \
|
||||
-p 33445:33445 \
|
||||
-p 3478:3478/udp \
|
||||
-v /shumengya/ssl/derp1/:/app/certs \
|
||||
-e DERP_CERT_MODE=manual \
|
||||
-e DERP_ADDR=:33445 \
|
||||
-e DERP_DOMAIN=derp1.shumengya.top \
|
||||
-e DERP_CERT_FILE=/app/certs/derp1.shumengya.top.crt \
|
||||
-e DERP_KEY_FILE=/app/certs/derp1.shumengya.top.key \
|
||||
-d ghcr.io/yangchuansheng/derper:latest
|
||||
|
||||
docker run --restart always \
|
||||
--name derper2 \
|
||||
-p 33445:33445 \
|
||||
-p 3478:3478/udp \
|
||||
-v /shumengya/ssl/derp2/:/app/certs \
|
||||
-e DERP_CERT_MODE=manual \
|
||||
-e DERP_ADDR=:33445 \
|
||||
-e DERP_DOMAIN=derp2.shumengya.top \
|
||||
-e DERP_CERT_FILE=/app/certs/derp2.shumengya.top.crt \
|
||||
-e DERP_KEY_FILE=/app/certs/derp2.shumengya.top.key \
|
||||
-d ghcr.io/yangchuansheng/derper:latest
|
||||
|
||||
docker run --restart always \
|
||||
--name derper3 \
|
||||
-p 33445:33445 \
|
||||
-p 3478:3478/udp \
|
||||
-v /shumengya/ssl/derp3/:/app/certs \
|
||||
-e DERP_CERT_MODE=manual \
|
||||
-e DERP_ADDR=:33445 \
|
||||
-e DERP_DOMAIN=derp3.shumengya.top \
|
||||
-e DERP_CERT_FILE=/app/certs/derp3.shumengya.top.crt \
|
||||
-e DERP_KEY_FILE=/app/certs/derp3.shumengya.top.key \
|
||||
-d ghcr.io/yangchuansheng/derper:latest
|
||||
|
||||
```
|
||||
|
||||
```bash
|
||||
#默认使用Let's Encrypt签发ssl证书
|
||||
#默认需要关闭nginx占用80端口来签发证书
|
||||
curl -s https://get.acme.sh | sh
|
||||
|
||||
source ~/.bashrc
|
||||
|
||||
acme.sh --issue --standalone -d 你的域名
|
||||
#安装依赖
|
||||
apt update && apt install -y socat
|
||||
yum install -y socat
|
||||
dnf install -y socat
|
||||
|
||||
acme.sh --register-account -m 你的邮箱地址
|
||||
```
|
||||
|
||||
|
||||
headscale(直接部署systemctl运行):
|
||||
|
||||
headscale默认放行:9090~9092 和50443
|
||||
|
||||
配置一个域名:
|
||||
headscale.shumengya.top
|
||||
下载headscale:[https://github.com/juanfont/headscale](headscale)
|
||||
下载headscale-ui:[https://github.com/juanfont/headscale](headscale-ui)
|
||||
|
||||
新建文件夹(用来放相关文件):
|
||||
mkdir -p shumengya/headscale
|
||||
|
||||
安装headscale:
|
||||
dpkg -i headscale_0.26.1_linux_amd64.deb
|
||||
|
||||
修改headscale配置
|
||||
```yaml
|
||||
---
|
||||
# headscale 会按照以下顺序查找名为 `config.yaml` (或 `config.json`) 的配置文件:
|
||||
#
|
||||
# - `/etc/headscale`
|
||||
# - `~/.headscale`
|
||||
# - 当前工作目录
|
||||
|
||||
# 客户端将要连接的 URL。
|
||||
# 通常应为一个域名,例如:
|
||||
#
|
||||
# https://myheadscale.example.com:443
|
||||
#
|
||||
server_url: http://127.0.0.1:9091
|
||||
|
||||
# 服务器监听/绑定的地址
|
||||
#
|
||||
# 生产环境建议:
|
||||
listen_addr: 0.0.0.0:9092
|
||||
#listen_addr: 127.0.0.1:8080
|
||||
|
||||
# /metrics 和 /debug 的监听地址,你可能希望
|
||||
# 将此端点限制在内网
|
||||
metrics_listen_addr: 127.0.0.1:9090
|
||||
|
||||
# gRPC 的监听地址。
|
||||
# gRPC 用于通过 CLI 远程控制 headscale 服务器
|
||||
# 注意:只有在使用有效证书时,远程访问才可用。
|
||||
#
|
||||
# 生产环境建议:
|
||||
grpc_listen_addr: 0.0.0.0:50443
|
||||
#grpc_listen_addr: 127.0.0.1:50443
|
||||
|
||||
# 是否允许 gRPC 管理接口以 **不安全** 模式运行。
|
||||
# 不推荐启用,因为流量将不加密。只有在你完全理解风险时才启用。
|
||||
grpc_allow_insecure: false
|
||||
|
||||
# Noise 部分包含 TS2021 Noise 协议的特定配置
|
||||
noise:
|
||||
# Noise 私钥用于在 headscale 和 Tailscale 客户端之间
|
||||
# 使用新的基于 Noise 的协议加密流量。缺少的密钥将会自动生成。
|
||||
private_key_path: /var/lib/headscale/noise_private.key
|
||||
|
||||
# 为 tailnet 分配地址的 IP 前缀列表。
|
||||
# 每个前缀由 IPv4 或 IPv6 地址 + 前缀长度组成,以斜杠分隔。
|
||||
# 必须在 Tailscale 客户端支持的范围内
|
||||
# 即 100.64.0.0/10 和 fd7a:115c:a1e0::/48 的子网。
|
||||
# 否则会导致异常问题。
|
||||
prefixes:
|
||||
v4: 100.64.0.0/10
|
||||
#v6: fd7a:115c:a1e0::/48
|
||||
|
||||
# 节点 IP 分配策略,可选:
|
||||
# - sequential(默认):按顺序分配下一个可用 IP
|
||||
# - random:使用伪随机生成器分配可用 IP
|
||||
allocation: sequential
|
||||
|
||||
# DERP 是 Tailscale 在无法建立直连时使用的中继系统。
|
||||
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
|
||||
#
|
||||
# headscale 需要一个 DERP 服务器列表供客户端使用。
|
||||
derp:
|
||||
server:
|
||||
# 启用后,将运行内置 DERP 服务器并合并到 DERP 配置中
|
||||
# 上面的 server_url 必须使用 https,DERP 需要 TLS
|
||||
enabled: false
|
||||
|
||||
# 内置 DERP 服务器的区域 ID。
|
||||
# 如果与外部配置的区域 ID 冲突,则本地 DERP 优先。
|
||||
region_id: 999
|
||||
|
||||
# 区域代码和名称会在 Tailscale UI 中显示
|
||||
region_code: "headscale"
|
||||
region_name: "Headscale Embedded DERP"
|
||||
|
||||
# 在指定地址上监听 UDP 端口以支持 STUN 连接(帮助 NAT 穿透)。
|
||||
# 如果启用内置 DERP,必须定义 stun_listen_addr。
|
||||
#
|
||||
# 更多详情可参考:https://tailscale.com/blog/how-tailscale-works/
|
||||
stun_listen_addr: "0.0.0.0:3478"
|
||||
|
||||
# 用于加密 headscale DERP 与 Tailscale 客户端之间流量的私钥。
|
||||
# 缺少时会自动生成。
|
||||
private_key_path: /var/lib/headscale/derp_server_private.key
|
||||
|
||||
# 此标志用于控制是否自动将内置 DERP 写入 DERP map。
|
||||
# 若设为 false,则需通过 DERP.paths 手动配置。
|
||||
automatically_add_embedded_derp_region: true
|
||||
|
||||
# 为了更稳定的连接(特别是 Exit-Node + DNS 不可用时),
|
||||
# 可以选择性地将公网 IPv4/IPv6 地址写入 Derp-Map:
|
||||
ipv4: 1.2.3.4
|
||||
ipv6: 2001:db8::1
|
||||
|
||||
# 外部可用的 DERP map 列表(JSON 格式)
|
||||
urls:
|
||||
# - https://controlplane.tailscale.com/derpmap/default
|
||||
#- https://你的域名/web/derp.json
|
||||
|
||||
# 本地可用的 DERP map 文件(YAML 格式)
|
||||
#
|
||||
# 对于自建 DERP 服务器很有用:
|
||||
# https://tailscale.com/kb/1118/custom-derp-servers/
|
||||
#
|
||||
# paths:
|
||||
# - /etc/headscale/derp-example.yaml
|
||||
paths: []
|
||||
|
||||
# 若启用,将定期刷新上述 DERP 源并更新 derpmap
|
||||
auto_update_enabled: true
|
||||
|
||||
# 检查 DERP 更新的间隔
|
||||
update_frequency: 24h
|
||||
|
||||
# 禁用启动时自动检查 headscale 更新
|
||||
disable_check_updates: false
|
||||
|
||||
# 非活跃临时节点在多久后被删除?
|
||||
ephemeral_node_inactivity_timeout: 30m
|
||||
|
||||
database:
|
||||
# 数据库类型,可选:sqlite, postgres
|
||||
# 注意:强烈不推荐使用 Postgres,仅为遗留原因保留。
|
||||
# 所有新开发和优化都基于 SQLite。
|
||||
type: sqlite
|
||||
|
||||
# 启用调试模式。需要 log.level 设置为 "debug" 或 "trace"。
|
||||
debug: false
|
||||
|
||||
# GORM 配置
|
||||
gorm:
|
||||
# 启用预编译语句。
|
||||
prepare_stmt: true
|
||||
|
||||
# 启用参数化查询。
|
||||
parameterized_queries: true
|
||||
|
||||
# 跳过 “record not found” 错误日志。
|
||||
skip_err_record_not_found: true
|
||||
|
||||
# 慢查询阈值(毫秒)
|
||||
slow_threshold: 1000
|
||||
|
||||
# SQLite 配置
|
||||
sqlite:
|
||||
path: /var/lib/headscale/db.sqlite
|
||||
|
||||
# 启用 WAL 模式。推荐生产环境启用。
|
||||
# https://www.sqlite.org/wal.html
|
||||
write_ahead_log: true
|
||||
|
||||
# WAL 文件在达到多少帧时自动 checkpoint。
|
||||
# 设为 0 可禁用自动 checkpoint。
|
||||
wal_autocheckpoint: 1000
|
||||
|
||||
# # Postgres 配置(不推荐,仅遗留支持)
|
||||
# postgres:
|
||||
# # 使用 Unix socket 时,设置 host 为 socket 路径,port 留空。
|
||||
# host: localhost
|
||||
# port: 5432
|
||||
# name: headscale
|
||||
# user: foo
|
||||
# pass: bar
|
||||
# max_open_conns: 10
|
||||
# max_idle_conns: 10
|
||||
# conn_max_idle_time_secs: 3600
|
||||
#
|
||||
# # SSL 配置,参考官方文档
|
||||
# ssl: false
|
||||
|
||||
### TLS 配置
|
||||
#
|
||||
## Let's Encrypt / ACME
|
||||
#
|
||||
# headscale 支持使用 Let's Encrypt 自动申请 TLS 证书
|
||||
#
|
||||
# ACME 目录 URL
|
||||
acme_url: https://acme-v02.api.letsencrypt.org/directory
|
||||
|
||||
# ACME 注册邮箱
|
||||
acme_email: ""
|
||||
|
||||
# 要申请 TLS 证书的域名
|
||||
tls_letsencrypt_hostname: ""
|
||||
|
||||
# 存储证书和元数据的路径
|
||||
# 生产环境建议:
|
||||
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
|
||||
|
||||
# ACME 挑战类型,目前支持:
|
||||
# HTTP-01 或 TLS-ALPN-01
|
||||
# 详见 docs/ref/tls.md
|
||||
tls_letsencrypt_challenge_type: HTTP-01
|
||||
# 使用 HTTP-01 时,letsencrypt 必须监听:
|
||||
# :http = 80 端口
|
||||
tls_letsencrypt_listen: ":http"
|
||||
|
||||
## 使用已有证书
|
||||
tls_cert_path: ""
|
||||
tls_key_path: ""
|
||||
|
||||
log:
|
||||
# 日志输出格式:text 或 json
|
||||
format: text
|
||||
# 日志级别:info, debug, trace
|
||||
level: info
|
||||
|
||||
## 策略
|
||||
# headscale 支持 Tailscale 的 ACL 策略
|
||||
# 文档:https://tailscale.com/kb/1018/acls/
|
||||
policy:
|
||||
# 模式:file 或 database
|
||||
mode: file
|
||||
# 若为 file 模式,指定 HuJSON 文件路径
|
||||
path: ""
|
||||
|
||||
## DNS
|
||||
#
|
||||
# headscale 支持 Tailscale 的 DNS 配置和 MagicDNS。
|
||||
# 文档:
|
||||
# https://tailscale.com/kb/1054/dns/
|
||||
# https://tailscale.com/kb/1081/magicdns/
|
||||
# https://tailscale.com/blog/2021-09-private-dns-with-magicdns/
|
||||
#
|
||||
# 注意:要使 DNS 配置生效,客户端必须启用 `--accept-dns=true`。
|
||||
# 否则将不生效。
|
||||
dns:
|
||||
# 是否启用 MagicDNS
|
||||
magic_dns: true
|
||||
|
||||
# MagicDNS 的基础域名,必须与 server_url 域名不同。
|
||||
base_domain: example.com
|
||||
|
||||
# 是否覆盖本地 DNS 设置
|
||||
override_local_dns: false
|
||||
|
||||
# 全局 DNS 服务器列表
|
||||
nameservers:
|
||||
global:
|
||||
- 1.1.1.1
|
||||
- 1.0.0.1
|
||||
- 2606:4700:4700::1111
|
||||
- 2606:4700:4700::1001
|
||||
# NextDNS 示例
|
||||
# - https://dns.nextdns.io/abc123
|
||||
|
||||
# Split DNS 示例(为特定域使用不同 DNS)
|
||||
split: {}
|
||||
|
||||
# 自定义搜索域
|
||||
search_domains: []
|
||||
|
||||
# 额外 DNS 记录(支持 A/AAAA)
|
||||
extra_records: []
|
||||
# - name: "grafana.myvpn.example.com"
|
||||
# type: "A"
|
||||
# value: "100.64.0.3"
|
||||
|
||||
# CLI 使用的 Unix socket(无认证)
|
||||
# 生产环境建议自定义
|
||||
unix_socket: /var/run/headscale/headscale.sock
|
||||
unix_socket_permission: "0770"
|
||||
|
||||
# OpenID Connect 配置(实验功能)
|
||||
# oidc:
|
||||
# only_start_if_oidc_is_available: true
|
||||
# issuer: "https://your-oidc.issuer.com/path"
|
||||
# client_id: "your-oidc-client-id"
|
||||
# client_secret: "your-oidc-client-secret"
|
||||
# client_secret_path: "${CREDENTIALS_DIRECTORY}/oidc_client_secret"
|
||||
# expiry: 180d
|
||||
# use_expiry_from_token: false
|
||||
# scope: ["openid", "profile", "email", "custom"]
|
||||
# extra_params:
|
||||
# domain_hint: example.com
|
||||
# allowed_domains:
|
||||
# - example.com
|
||||
# allowed_groups:
|
||||
# - /headscale
|
||||
# allowed_users:
|
||||
# - alice@example.com
|
||||
# pkce:
|
||||
# enabled: false
|
||||
# method: S256
|
||||
|
||||
# Logtail 配置
|
||||
# Logtail 是 Tailscale 的日志与审计系统
|
||||
logtail:
|
||||
# 启用后,客户端将日志发送到 Tailscale 服务器
|
||||
enabled: false
|
||||
|
||||
# 启用后,设备将优先使用随机端口进行 WireGuard 流量
|
||||
# 以避免某些防火墙兼容性问题。
|
||||
# 详见:https://tailscale.com/kb/1181/firewalls/
|
||||
randomize_client_port: false
|
||||
|
||||
|
||||
```
|
||||
|
||||
设置反向代理:
|
||||
公网域名->127.0.0.1:9092
|
||||
```txt
|
||||
location /web {
|
||||
index index.html;
|
||||
alias /shumengya/headscale/web;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
views:
|
||||
- type: table
|
||||
name: 表格
|
||||
@@ -1,10 +0,0 @@
|
||||
CVM 通常指 云服务器(Cloud Virtual Machine),是云计算服务中的一种基础计算资源。它通过虚拟化技术,将物理服务器分割成多个独立的虚拟服务器,用户可像使用本地服务器一样远程管理和使用,无需自行购买和维护硬件设备。
|
||||
|
||||
其核心特点包括:
|
||||
|
||||
- 弹性伸缩:可根据业务需求灵活调整 CPU、内存、存储等配置。
|
||||
- 按需付费:按使用时长或资源配置计费,降低初期投入成本。
|
||||
- 高可用性:依托云服务商的基础设施,通常具备多副本存储、故障自动迁移等能力。
|
||||
- 易管理性:通过云平台控制台可便捷进行开关机、重装系统、配置网络等操作。
|
||||
|
||||
在实际应用中,CVM 广泛用于搭建网站、部署应用程序、运行数据库、进行大数据分析等场景。不同云服务商(如腾讯云、阿里云等)对其的具体命名可能略有差异,但核心功能一致。
|
||||
@@ -1,57 +0,0 @@
|
||||
IT 行业中,**A 端、B 端、C 端**,是按照业务服务对象的不同来划分
|
||||
|
||||
---
|
||||
|
||||
## 1. C 端(Customer/Consumer)
|
||||
|
||||
- **对象**:普通个人用户,即消费者。
|
||||
- **特点**:
|
||||
- 用户量大,但单个用户的付费能力相对有限。
|
||||
- 对产品的体验、易用性、趣味性和性价比要求较高。
|
||||
- 市场竞争激烈,获客成本高,用户流失率也较高。
|
||||
- **典型产品**:
|
||||
- 微信、抖音、美团、淘宝等面向大众的互联网应用。
|
||||
- 常见的互联网 App 和电商平台,主要服务于个人消费者。
|
||||
|
||||
---
|
||||
|
||||
## 2. B 端(Business)
|
||||
|
||||
- **对象**:企业或组织客户。
|
||||
- **特点**:
|
||||
- 用户数量相对少,但单个客户的价值很高。
|
||||
- 注重功能的完整性、效率、稳定性、安全性和售后支持。
|
||||
- 产品通常要和企业的业务流程深度结合,销售模式更偏向“解决方案”。
|
||||
- **典型产品**:
|
||||
- 企业管理系统(ERP、CRM、OA)、云计算服务(阿里云、腾讯云)、SaaS 工具(钉钉、飞书)。
|
||||
- 企业采购、供应链、财务软件等。
|
||||
|
||||
---
|
||||
|
||||
## 3. A 端(Administration/Government)
|
||||
|
||||
- **对象**:政府机构或事业单位。
|
||||
- **特点**:
|
||||
- 强调合规性、稳定性和数据安全。
|
||||
- 客户对服务的专业性、长期支持能力和安全保密性要求极高。
|
||||
- 项目型特征明显,常涉及“政务信息化”“智慧城市”等建设。
|
||||
- **典型产品**:
|
||||
- 政务大数据平台、智慧城市解决方案、公安/税务/教育信息化系统。
|
||||
- 例如国家的社保系统、政府政务服务平台。
|
||||
|
||||
---
|
||||
|
||||
## 4. 三者的区别总结
|
||||
|
||||
- **C 端**:面对大众 → 重视用户体验和规模效应。
|
||||
- **B 端**:面对企业 → 重视效率、功能深度、可持续合作。
|
||||
- **A 端**:面对政府 → 重视合规、安全、稳定、长期合作。
|
||||
|
||||
可以简单理解为:
|
||||
|
||||
- **C 端是“零售”**,靠规模和用户体验取胜。
|
||||
- **B 端是“批发”**,客户少但客单价高,需要深度定制。
|
||||
- **A 端是“定制工程”**,政策导向明显,更看重安全和合规。
|
||||
|
||||
---
|
||||
有没有面向D端-开发者developer?
|
||||
@@ -1 +0,0 @@
|
||||
微控制单元(Microcontroller Unit):这是电子工程领域的专业术语,又称“单片机”。它是一种将中央处理器(CPU)、存储器、输入/输出接口等功能集成在一块芯片上的微型计算机,广泛应用于智能家居、汽车电子、工业控制等领域。
|
||||
@@ -1,11 +0,0 @@
|
||||
MQ(Message Queue,消息队列),是一种基于“队列”模型的中间件,核心作用是实现跨服务的异步通信、解耦和流量削峰。
|
||||
|
||||
简单来说,MQ就像“快递驿站”:发送消息的一方(生产者)把消息放到“驿站”(队列),不需要等接收方(消费者)立即处理;接收方空闲时再从“驿站”取消息处理,双方无需直接通信,也不用等待对方响应。
|
||||
|
||||
MQ的核心价值(对应之前高QPS、异步场景)
|
||||
|
||||
1. 削峰填谷:比如直播间送礼峰值时,大量请求先涌入MQ队列,消费端按数据库/服务的承载能力匀速处理,避免直接打垮后端;
|
||||
2. 解耦服务:送礼场景中,“扣余额”和“发送通知”无需强绑定,扣完余额后扔一条消息到MQ,通知服务自己消费处理,后续新增“积分统计”功能,只需新增一个消费端即可,不用修改核心业务代码;
|
||||
3. 异步提速:用户点击送礼后,后端只需完成“扣余额+发MQ消息”就立即返回成功,后续的“主播收益更新、消息推送”等耗时操作由MQ异步触发,提升接口响应速度。
|
||||
|
||||
常见的MQ产品有RocketMQ、Kafka、RabbitMQ等,在高并发场景(如电商大促、直播、秒杀)中是核心中间件。
|
||||
@@ -1,5 +0,0 @@
|
||||
POI(Poor Obfuscation Implementation),是Apache基金会提供的一套Java开源API库,核心作用是实现对Microsoft Office格式文件(如Excel、Word、PPT)的读写、编辑和生成。
|
||||
|
||||
简单来说,POI就是Java程序操作Office文件的“工具包”——比如你需要在系统中导出Excel报表(如直播间送礼统计、用户消费明细)、读取Excel中的批量数据(如批量导入用户信息),都可以通过POI来实现。
|
||||
|
||||
它之所以在报表处理场景中常用,是因为能直接操作Office原生格式,支持复杂的单元格样式(合并单元格、公式、图表),且兼容性强,几乎能覆盖所有Office文件操作需求。之前提到的“POI处理报表”,就是指用这套库完成Excel报表的生成、解析等核心操作。
|
||||
@@ -1,14 +0,0 @@
|
||||
QPS(Queries Per Second),即每秒查询率,是衡量服务器在单位时间内处理请求能力的核心性能指标,直接反映系统的并发处理能力。
|
||||
|
||||
简单来说,QPS就像“每秒能接待多少位客人”,比如一个接口的QPS为100,意味着它每秒最多能稳定处理100次用户的访问请求。
|
||||
|
||||
QPS的关键细节说明
|
||||
|
||||
1. 统计范围:通常针对单个接口、单个服务节点或整个系统,不同范围的QPS数值代表的意义不同(如“登录接口QPS”“支付服务QPS”)。
|
||||
2. 与并发量的关系:QPS ≈ 并发量 / 平均响应时间。例如,若系统平均响应时间为0.1秒,并发量为50,那么QPS约为500(50/0.1)。
|
||||
3. 实际应用场景:
|
||||
- 日常运维:通过监控QPS判断系统负载是否正常(如峰值QPS是否超过阈值)。
|
||||
- 架构设计:根据业务预估QPS(如电商大促预估10万QPS),决定服务器集群规模、缓存策略等。
|
||||
- 性能优化:若QPS不足(如请求排队、超时),需通过扩容、优化代码、增加缓存等方式提升。
|
||||
|
||||
例如,普通小型网站日常QPS可能仅几十到几百,而大型电商平台促销峰值QPS可达到几十万甚至上百万。
|
||||
@@ -1,55 +0,0 @@
|
||||
# 什么是 SaaS(Software as a Service)?
|
||||
|
||||
**一句话**:SaaS 就是把软件放在厂商的云端,通过互联网提供给你用,你按月/年订阅即可,无需自己买服务器、装软件、打补丁。
|
||||
|
||||
## 它和传统软件/其他“_aaS_”的区别
|
||||
|
||||
- **传统本地部署**:你买许可证+买服务器+自己维护 → 成本高、上线慢。
|
||||
- **SaaS**:厂商托管一切(基础设施、应用、更新、备份),你用浏览器/APP直接用。
|
||||
- **IaaS**(基础设施即服务):租云上的“硬件”(算力/存储/网络)。
|
||||
- **PaaS**(平台即服务):租开发与运行平台(数据库/函数/中间件)。
|
||||
- **SaaS**:直接是“成品应用”(如CRM、协作工具)。
|
||||
|
||||
## 关键特性
|
||||
|
||||
- 订阅计费(按用户/按用量/套餐分级)
|
||||
- 通过浏览器或移动端访问,零安装
|
||||
- 自动更新与弹性扩容
|
||||
- 多租户架构(共享一套系统,数据逻辑隔离)
|
||||
- 开放接口(API/SSO/SCIM)便于与现有系统集成
|
||||
- 明确的**SLA**(可用性、支持时效)
|
||||
|
||||
## 优点 & 潜在不足
|
||||
|
||||
**优点**
|
||||
|
||||
- 低启动成本,快速上线
|
||||
- 省运维与升级,持续获得新功能
|
||||
- 按需扩缩,适合中小团队和快速试错
|
||||
|
||||
**潜在不足**
|
||||
|
||||
- 深度定制受限(更多是“配置”而非“改代码”)
|
||||
- 数据合规/数据驻留要求需确认
|
||||
- 依赖网络与厂商,存在一定“供应商锁定”
|
||||
- 长期订阅成本需要核算
|
||||
|
||||
## 常见场景与例子
|
||||
|
||||
- 协作与沟通:Slack、Zoom、Notion
|
||||
- 客户与销售:Salesforce、HubSpot
|
||||
- 电商与支付:Shopify、Stripe(偏API型SaaS)
|
||||
- 人事财务:Workday、QuickBooks Online
|
||||
|
||||
## 选型时的速查清单
|
||||
|
||||
1. **合规与安全**:是否支持你所在行业/地区的合规(如 ISO 27001、SOC 2、GDPR、数据驻留选项)。
|
||||
2. **SLA 与可靠性**:可用性承诺、备份与灾备、支持响应时效。
|
||||
3. **集成**:是否支持 SSO/SCIM、现有系统的数据同步与 API 能力。
|
||||
4. **可配置度**:工作流、权限、报表、低代码/自动化能力到什么程度。
|
||||
5. **数据进出**:导入/导出、数据可携带与停用后的数据处理。
|
||||
6. **成本模型**:按用户/用量/功能包的组合,三年总拥有成本(TCO)。
|
||||
|
||||
## 一个小比喻
|
||||
|
||||
把软件当“自来水”:SaaS 负责建厂、铺管、净水和维护,你拧开水龙头就能用水(功能),按量付费。
|
||||
@@ -1,8 +0,0 @@
|
||||
“pwn” 是网络安全领域的常用术语,核心指 通过利用程序漏洞(如缓冲区溢出、内存泄漏等)获取目标系统的控制权,比如执行任意代码、提升权限甚至完全掌控设备。
|
||||
|
||||
它起源于“own”(意为“拥有、控制”)的拼写变体,早期在黑客社区和安全竞赛(如CTF)中流行。常见场景包括:
|
||||
|
||||
- 对存在漏洞的软件、服务器进行攻击;
|
||||
- CTF竞赛中的“pwn题”,要求参赛者找出并利用程序漏洞解决问题。
|
||||
|
||||
简单说,pwn的本质就是“找到漏洞并借此控制目标”。
|
||||