前言
这次使用 Astro + Cloudflare Pages 的方案重新部署了自己的博客,同时使用 Remark42 + fly.io 部署了博客评论系统,这里写一篇文章做一些简单的记录。
Astro 博客主体
使用的主题是 AstroPaper,从 github 上 Use this template
来创建自己的 blog repo,本地预览环境使用 pnpm run dev
即可启动。更改完一些自定义设置后使用 Cloudflare Pages 进行静态部署。
除了一些自定义的配置外,目前我主要对博客进行了如下两处小修改。
更改博客主题配色
参考 PaperMod 更换了一下博客主题配色,更改 global.css
中的 html[data-theme="light"]
和 html[data-theme="dark"]
即可。
/* in global.css */
:root,
html[data-theme="light"] {
--background: rgb(245, 245, 245);
--foreground: rgb(31, 31, 31);
--accent: #3560ab;
--muted: #e6e6e6;
--border: #ece9e9;
}
html[data-theme="dark"] {
--background: rgb(29, 30, 32);
--foreground: #eaedf3;
--accent: #1ad9d9;
--muted: rgb(46, 46, 51);
--border: #3b4655;
}
更换博客字体
原本打算使用 FiraCode Nerd Font Mono
+ LXGW WenKai Screen
,但感觉 LXGW WenKai Screen
对于博客内容显示来说表现欠佳,所以暂时没有启用。
为了防止 cf 加载 FireCode Font 资源失败,所以在本地也放了一份备用。
/* import "LXGW WenKai Screen" */
@import url("https://cdn.staticfile.org/lxgw-wenkai-screen-webfont/1.6.0/lxgwwenkaiscreen.css");
/* import "FiraCode Nerd Font Mono" */
@layer base {
@font-face {
font-family: "FiraCode Nerd Font Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
url("/fonts/FiraCode-Regular.woff2") format("woff2");
}
然后更改 --font-sans
:
:root,
html[data-theme="light"] {
--font-sans: "FiraCode Nerd Font Mono", Inter, ui-sans-serif, system-ui, sans-serif;
}
html[data-theme="dark"] {
--font-sans: "FiraCode Nerd Font Mono", sans-serif;
}
Cloudflare Pages 部署
感觉这里没啥可说的,在 Pages 页面选中你的 github repo 直接按照默认配置部署即可。创建了一个 deploy 分支,推送到这个分支时才会部署到生产环境,也就是主域名,然后日常 dev 在 main 分支,关闭了预览部署。
Remark42 评论系统
Remark42 部署
原主题没有对评论系统做任何集成,因此需要自己解决博客评论系统问题。我采用的方案是 Remark42 + SaaS 服务,这里的 SaaS 服务目前使用的是 fly.io。
fly.io 之前一直都是有每个月的 5 刀免费额度,不过今年没有了,需要绑定一张卡才能后续使用。 fly.io 的部署需要在本地安装他们的 CLI,安装完成后,使用 fly auth login
完成登录。
编写 Remark42 配置文件 fly.toml
:
app = 'se-remark42-01'
primary_region = 'hkg'
[build]
image = 'umputun/remark42:latest'
[env]
ADMIN_SHARED_ID = ''
REMARK_URL = 'https://remark42.silente.dev/'
# REMARK_URL = 'https://se-remark42-01.fly.dev/'
SITE = 'silente.dev'
[[mounts]]
source = 'remark42_data_01'
destination = '/srv/var'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = 'off'
auto_start_machines = true
min_machines_running = 1
processes = ['app']
[[vm]]
size = 'shared-cpu-1x'
配置文件需要改动的地方是:
app
: 应用唯一名称REMARK_URL
: remark42 服务的 domain,fly.io 提供自定义域名服务,可以先部署一次然后在他们的 dashboard 中进行添加;如果想直接使用 fly.io 自己的域名则可以参考注释中的格式:${app}.fly.dev
SITE
: 站点名称,可以随便设置ADMIN_SHARED_ID
: 管理员 ID,这里可以先不设置,后续配置完成后再使用fly secrets
设置
配置完成后,使用 flyctl launch
部署服务,这样应该就能从 fly.io 的 Dashboard 上看到 Remark42 服务了。
接下来进行其他环境变量的配置,创建一个 prod.env
文件,配置以下变量:
SECRET=xxx
AUTH_GITHUB_CID=xxx
AUTH_GITHUB_CSEC=xxx
AUTH_ANON=true
ADMIN_SHARED_ID=xxxx
SECRET
用来生成 JWT,所以手滚键盘输出一个即可;AUTH_GITHUB_CID
和 AUTH_GITHUB_CSEC
则用来配置 Github 认证登录,这里可以去参考 Remark42 官方指南获取这两个变量值,其他的认证方式同理配置。至于 ADMIN_SHARED_ID
这个值需要等到你配置好认证方式并登录后,点开你的头像即可看到 ID。
在每次更新 prod.env
时,使用 fly secrets import < prod.env
将配置变量导入 fly secrets 中,然后使用 fly deploy
触发重新部署。
至于 Remark42 部署在 fly.io 上目前的费用的话差不多 1 天 0.1 刀。
集成评论系统
创建一个 Remark.astro
组件
---
declare global {
interface Window {
remark_config: {
host: string;
site_id: string;
components: string[];
max_shown_comments: number;
theme: string;
locale: string;
show_email_subscription: boolean;
simple_view: boolean;
};
REMARK42: {
createInstance: (config: {
node: HTMLElement;
host: string;
site_id: string;
max_shown_comments: number;
theme: string;
locale: string;
show_email_subscription: boolean;
simple_view: boolean;
}) => Remark42Instance;
ready: boolean;
};
}
// Type for Remark42 instance
interface Remark42Instance {
destroy: () => void;
}
}
export interface Props {
noMarginTop?: boolean;
}
---
<div class="comments" class="mx-auto w-full px-4 pb-12">
<div id="remark42" class="mx-auto max-w-3xl"></div>
</div>
<script>
// Type for Remark42 instance
interface Remark42Instance {
destroy: () => void;
}
let remark42Instance: Remark42Instance | null = null;
// Configuration for Remark42
window.remark_config = {
host: 'https://remark42.silente.dev',
site_id: 'silente.dev',
components: ['embed', 'counter'],
max_shown_comments: 100,
theme: localStorage.getItem('theme') || 'light',
locale: 'en',
show_email_subscription: false,
simple_view: true
};
// Initialize Remark42 instance
function initRemark42() {
if (window.REMARK42) {
// Destroy previous instance if exists
if (remark42Instance) {
remark42Instance.destroy();
}
// Create new instance
const node = document.getElementById('remark42');
if (node) {
remark42Instance = window.REMARK42.createInstance({
node,
host: window.remark_config.host,
site_id: window.remark_config.site_id,
max_shown_comments: window.remark_config.max_shown_comments,
theme: window.remark_config.theme,
locale: window.remark_config.locale,
show_email_subscription: window.remark_config.show_email_subscription,
simple_view: window.remark_config.simple_view
});
}
}
}
// Load Remark42 scripts
(function(components, doc) {
for (let i = 0; i < components.length; i++) {
const script = doc.createElement('script') as HTMLScriptElement;
const extension = 'noModule' in script ? '.mjs' : '.js';
if ('noModule' in script) {
script.type = 'module';
} else {
(script as HTMLScriptElement).async = true;
(script as HTMLScriptElement).defer = true;
}
script.src = `${window.remark_config.host}/web/${components[i]}${extension}`;
(doc.head || doc.body).appendChild(script);
}
})(window.remark_config.components || ['embed'], document);
// Initialize when Remark42 is ready or wait for it to be ready
document.addEventListener('DOMContentLoaded', () => {
if (window.REMARK42) {
initRemark42();
} else {
window.addEventListener('REMARK42::ready', () => {
initRemark42();
});
}
});
// Handle Astro page transitions
document.addEventListener('astro:page-load', () => {
initRemark42();
});
// Clean up before page transitions
document.addEventListener('astro:before-swap', () => {
if (remark42Instance) {
remark42Instance.destroy();
}
});
</script>
在文章组件中加入并放在合适的位置,这里根据 Astro 主题不同放置的位置也不同,对于 AstroPapaer 来说我放在了 PostDetails.astro
中 <Footer />
的下方。