基于 Astro Cactus 主题 + 1Panel + 子域名架构的完整部署指南
📋 目录
架构概述
域名结构
| 域名 | 用途 | 对应内容 |
|---|---|---|
updn.pub |
主站首页 | /dist/index.html |
blog.updn.pub |
博客文章 | /dist/posts/ |
about.updn.pub |
关于页面 | /dist/about/ |
note.updn.pub |
动态/笔记 | /dist/notes/ |
技术栈
- 框架: Astro 5.x
- 主题: Astro Cactus
- 样式: Tailwind CSS 4.x
- 面板: 1Panel
- Web服务: OpenResty (Nginx)
- 部署方式: 静态文件托管
设计理念
- 单项目多子域名: 一个 Astro 项目,四个子域名各自指向不同内容
- 性能优先: 纯静态文件,Nginx 直接托管
- 统一维护: 换主题只需改一处,所有子站同步更新
环境准备
1. 清理旧环境(可选)
# 停止并删除不需要的 Docker 容器
docker stop <container_name>
docker rm <container_name>
# 清理无用镜像
docker image prune
2. 安装 Node.js 22.x
# 添加 NodeSource 仓库
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -
# 安装 Node.js
apt-get install -y nodejs
# 验证安装
node -v # 应显示 v22.x.x
npm -v # 应显示 10.x.x
3. 安装 pnpm
# 全局安装 pnpm
npm install -g pnpm
# 验证安装
pnpm -v # 应显示 10.x.x
项目部署
1. 创建项目目录
mkdir -p /opt/updn-sites
cd /opt/updn-sites
2. 克隆 Astro Cactus 主题
git clone https://github.com/chrismwilliams/astro-theme-cactus.git cactus
cd cactus
3. 安装依赖
pnpm install
4. 修改站点配置
编辑 /opt/updn-sites/cactus/src/site.config.ts:
import type { AstroExpressiveCodeOptions } from "astro-expressive-code";
import type { SiteConfig } from "@/types";
export const siteConfig: SiteConfig = {
author: "AK",
date: {
locale: "zh-CN",
options: {
day: "numeric",
month: "short",
year: "numeric",
},
},
description: "Game not over.",
lang: "zh-CN",
ogLocale: "zh_CN",
title: "Project Laser",
url: "https://updn.pub",
};
// 导航菜单 - 使用绝对 URL 指向子域名
export const menuLinks: { path: string; title: string }[] = [
{ path: "https://updn.pub", title: "首页" },
{ path: "https://about.updn.pub", title: "关于" },
{ path: "https://blog.updn.pub", title: "博客" },
{ path: "https://note.updn.pub", title: "动态" },
];
// 代码块配置(保持默认)
export const expressiveCodeOptions: AstroExpressiveCodeOptions = {
styleOverrides: {
borderRadius: "4px",
codeFontFamily:
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
codeFontSize: "0.875rem",
codeLineHeight: "1.7142857rem",
codePaddingInline: "1rem",
frames: {
frameBoxShadowCssValue: "none",
},
uiLineHeight: "inherit",
},
themeCssSelector(theme, { styleVariants }) {
if (styleVariants.length >= 2) {
const baseTheme = styleVariants[0]?.theme;
const altTheme = styleVariants.find((v) => v.theme.type !== baseTheme?.type)?.theme;
if (theme === baseTheme || theme === altTheme) return `[data-theme='${theme.type}']`;
}
return `[data-theme="${theme.name}"]`;
},
themes: ["dracula", "github-light"],
useThemedScrollbars: false,
};
5. 首次构建
cd /opt/updn-sites/cactus
pnpm build
构建成功后,静态文件输出到 /opt/updn-sites/cactus/dist/ 目录。
站点配置
DNS 解析配置
在域名管理后台添加以下 A 记录(以 updn.pub 为例):
| 主机记录 | 记录类型 | 记录值 |
|---|---|---|
@ |
A | 服务器IP |
blog |
A | 服务器IP |
about |
A | 服务器IP |
note |
A | 服务器IP |
或使用泛解析:
| 主机记录 | 记录类型 | 记录值 |
|---|---|---|
@ |
A | 服务器IP |
* |
A | 服务器IP |
SSL 证书
建议申请泛域名证书 *.updn.pub,可通过 1Panel 的证书管理功能申请 Let’s Encrypt 免费证书。
1Panel 网站配置
1. 创建四个静态网站
在 1Panel 面板中:网站 → 创建 → 静态网站
依次创建:
| 主域名 | 代号 |
|---|---|
updn.pub |
updn.pub |
blog.updn.pub |
blog.updn.pub |
about.updn.pub |
about.updn.pub |
note.updn.pub |
note.updn.pub |
2. 配置 HTTPS
每个站点:配置 → HTTPS → 启用并选择证书
3. 部署静态文件
1Panel 创建的站点目录结构:
/opt/1panel/apps/openresty/openresty/www/sites/
├── updn.pub/
│ ├── index/ ← 静态文件放这里
│ ├── log/
│ └── ssl/
├── blog.updn.pub/
│ ├── index/
│ ├── log/
│ └── ssl/
├── about.updn.pub/
│ └── ...
└── note.updn.pub/
└── ...
复制构建产物到各站点:
# 主站
cp -r /opt/updn-sites/cactus/dist/* /opt/1panel/apps/openresty/openresty/www/sites/updn.pub/index/
# 博客
cp -r /opt/updn-sites/cactus/dist/* /opt/1panel/apps/openresty/openresty/www/sites/blog.updn.pub/index/
# 关于
cp -r /opt/updn-sites/cactus/dist/* /opt/1panel/apps/openresty/openresty/www/sites/about.updn.pub/index/
# 动态
cp -r /opt/updn-sites/cactus/dist/* /opt/1panel/apps/openresty/openresty/www/sites/note.updn.pub/index/
Nginx 配置详解
主站 updn.pub
无需额外配置,使用 1Panel 默认生成的配置即可。
博客 blog.updn.pub
进入 1Panel → 网站 → blog.updn.pub → 配置文件,修改为:
server {
listen 80;
listen 443 ssl http2;
server_name blog.updn.pub;
index index.php index.html index.htm default.php default.htm default.html;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
access_log /www/sites/blog.updn.pub/log/access.log main;
error_log /www/sites/blog.updn.pub/log/error.log;
location ^~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
root /www/sites/blog.updn.pub/index;
# 关键配置:访问根路径时显示 posts 内容
location = / {
try_files /posts/index.html =404;
}
error_page 404 /404.html;
if ($scheme = http) {
return 301 https://$host$request_uri;
}
ssl_certificate /www/sites/blog.updn.pub/ssl/fullchain.pem;
ssl_certificate_key /www/sites/blog.updn.pub/ssl/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header X-Forwarded-Proto https;
add_header Strict-Transport-Security "max-age=31536000";
}
关于 about.updn.pub
# 在 root 行后添加:
location = / {
try_files /about/index.html =404;
}
动态 note.updn.pub
# 在 root 行后添加:
location = / {
try_files /notes/index.html =404;
}
修改后点击「保存并重载」
部署脚本
创建一键部署脚本 /opt/updn-sites/deploy.sh:
#!/bin/bash
# 🚀 Project Laser 一键部署脚本
set -e
echo "📦 开始构建..."
cd /opt/updn-sites/cactus
pnpm build
echo "🚀 部署到所有站点..."
cp -r dist/* /opt/1panel/apps/openresty/openresty/www/sites/updn.pub/index/
cp -r dist/* /opt/1panel/apps/openresty/openresty/www/sites/blog.updn.pub/index/
cp -r dist/* /opt/1panel/apps/openresty/openresty/www/sites/about.updn.pub/index/
cp -r dist/* /opt/1panel/apps/openresty/openresty/www/sites/note.updn.pub/index/
echo "✅ 部署完成!"
echo " - https://updn.pub"
echo " - https://blog.updn.pub"
echo " - https://about.updn.pub"
echo " - https://note.updn.pub"
添加执行权限:
chmod +x /opt/updn-sites/deploy.sh
使用方法:
/opt/updn-sites/deploy.sh
日常维护
添加博客文章
在 /opt/updn-sites/cactus/src/content/post/ 目录下创建 .md 文件:
---
title: "文章标题"
description: "文章描述"
publishDate: "2026-01-12"
tags: ["标签1", "标签2"]
---
正文内容...
添加动态/笔记
在 /opt/updn-sites/cactus/src/content/note/ 目录下创建 .md 文件:
---
title: "动态标题"
publishDate: "2026-01-12T12:00:00Z"
---
动态内容...
注意: Note 的 publishDate 需要完整的 ISO 8601 格式(带时间)。
部署更新
修改内容后执行:
/opt/updn-sites/deploy.sh
查看语法参考
保留的示例文件:
cat /opt/updn-sites/cactus/src/content/post/markdown-elements/index.md
目录结构总览
/opt/
├── updn-sites/
│ ├── cactus/ # Astro 项目
│ │ ├── src/
│ │ │ ├── content/
│ │ │ │ ├── post/ # 博客文章
│ │ │ │ │ ├── hello-world.md
│ │ │ │ │ └── markdown-elements/
│ │ │ │ └── note/ # 动态/笔记
│ │ │ │ └── welcome.md
│ │ │ ├── pages/
│ │ │ │ └── about.astro # 关于页面
│ │ │ └── site.config.ts # 站点配置
│ │ ├── dist/ # 构建输出
│ │ ├── astro.config.ts
│ │ └── package.json
│ └── deploy.sh # 部署脚本
│
├── 1panel/
│ └── apps/
│ └── openresty/
│ └── openresty/
│ └── www/
│ └── sites/
│ ├── updn.pub/
│ │ ├── index/ # 静态文件
│ │ ├── log/
│ │ └── ssl/
│ ├── blog.updn.pub/
│ ├── about.updn.pub/
│ └── note.updn.pub/
│
└── bots/ # TG Bot(可选)
├── common.env
└── astropub_bot/
├── .env
└── astropub_bot.py
常见问题
Q: 访问显示 404
排查步骤:
- 检查文件是否已复制到
index/目录 - 检查 Nginx 配置是否正确
- 检查是否点击了「保存并重载」
Q: 子域名无法访问
排查步骤:
- 检查 DNS 解析是否生效:
dig blog.updn.pub +short - 检查 SSL 证书是否配置
- 检查 1Panel 站点状态是否「已启动」
Q: 构建报错 Invalid datetime
Note 的 publishDate 需要完整格式:
# ❌ 错误
publishDate: "2026-01-12"
# ✅ 正确
publishDate: "2026-01-12T12:00:00Z"
Q: 菜单跳转不正确
确保 site.config.ts 中的 menuLinks 使用完整的 URL:
{ path: "https://blog.updn.pub", title: "博客" }
版本信息
| 组件 | 版本 |
|---|---|
| Ubuntu | 22.04 LTS |
| Node.js | 22.x |
| pnpm | 10.x |
| Astro | 5.x |
| Astro Cactus | 6.x |
| 1Panel | 1.10.x |
| OpenResty | 1.21.x |
文档版本: 1.0 | 更新日期: 2026-01-12