|
| 1 | +--- |
| 2 | +title: 私有知识库和笔记工具 Affine 搭建, 修复导入 Markdown 图片丢失的问题 |
| 3 | +date: 2025-11-12 |
| 4 | +description: "私有知识库和笔记工具 Affine 搭建, 修复导入 Markdown 图片丢失的问题" |
| 5 | +layout: post |
| 6 | + |
| 7 | +tags: |
| 8 | + - Wiki |
| 9 | + - Notion |
| 10 | + - Affine |
| 11 | +categories: |
| 12 | + - SelfHost |
| 13 | +lightgallery: true |
| 14 | + |
| 15 | +toc: |
| 16 | + auto: true |
| 17 | +--- |
| 18 | + |
| 19 | +# 私有知识库和笔记工具 Affine 搭建, 修复导入 Markdown 图片丢失的问题 |
| 20 | + |
| 21 | +## 前言 |
| 22 | + |
| 23 | +一直以来我使用 Obsidian 作为我的知识库管理和编辑工具,配合自建的图床和 Nextcloud 的同步,我几乎可以在任何地方随意维护我的知识库,后面因为 Obsidian 的 Vim 模式用起来不顺手,在中文之间移动有卡顿的感觉,所以索性编辑器直接就用了 `Vim` ,但我一直苦恼一个问题,就是我的知识库一直以 Markdown 源文件的方式在 Nextcloud 的同步下,我虽然可以随时打开 Obsidian 查看,但是在移动端查看不方便,以及每次想查询的时候还得打开客户端,我本人是很不喜欢客户端的,我希望能在 web 上就可以查看,当然 Nextcloud 也能在网页端查看渲染的 Markdown 文件,但是效果也差强人意,正好刷 Koala 聊编程的时候看到了 Affine,其主打的 Notion 替代品的概念吸引了我,虽然我也用 Notion, 但用的并不多,而且 Notion 无法迁移我的本地知识库,导入的 Markdown 格式各种问题,抱着试试看的心态部署了一下,虽说官方提供了完整的 `docker-compose`配置文件,但实际配置下来还是遇到了一些问题,在此权当记录一下。 |
| 24 | + |
| 25 | +## 安装 affine 服务端 |
| 26 | + |
| 27 | +### Docker compose 安装服务端 |
| 28 | + |
| 29 | +还是使用 `dokcer compose`部署,下载最新版本 `docker-compose.yaml`文件。 |
| 30 | + |
| 31 | +```shellscript |
| 32 | +wget -O docker-compose.yml https://github.com/toeverything/affine/releases/latest/download/docker-compose.yml |
| 33 | +``` |
| 34 | + |
| 35 | +  下载配置模板修改配置 |
| 36 | + |
| 37 | +```shellscript |
| 38 | +wget -O .env https://github.com/toeverything/affine/releases/latest/download/default.env.example |
| 39 | +``` |
| 40 | + |
| 41 | +下面是我的配置 |
| 42 | + |
| 43 | +``` |
| 44 | +# 数据库挂载目录 |
| 45 | +DB_DATA_LOCATION=./postgres |
| 46 | +# 上传的附件目录 |
| 47 | +UPLOAD_LOCATION=/mnt/media/affine |
| 48 | +# 配置文件目录 |
| 49 | +CONFIG_LOCATION=./config |
| 50 | +# 数据库配置 |
| 51 | +DB_USERNAME=affine |
| 52 | +DB_PASSWORD=**** |
| 53 | +DB_DATABASE=affine |
| 54 | +# select a revision to deploy, available values: stable, beta, canary |
| 55 | +# 部署的版本:稳定版 |
| 56 | +AFFINE_REVISION=stable |
| 57 | +
|
| 58 | +# set the host for the server for outgoing links |
| 59 | +# AFFINE_SERVER_HTTPS=true |
| 60 | +# AFFINE_SERVER_HOST=affine.yourdomain.com |
| 61 | +# or |
| 62 | +# 这里是我的自定义的内网域名,如果放到公网改成你自己的域名,如果内网IP访问则去掉这行 |
| 63 | +AFFINE_SERVER_EXTERNAL_URL=https://wiki.linkzz.hm |
| 64 | +``` |
| 65 | + |
| 66 | +运行 |
| 67 | + |
| 68 | +```shellscript |
| 69 | +docker compose up -d |
| 70 | +``` |
| 71 | + |
| 72 | +正常运行之后打开 http://{youip}:3010 进行管理员配置 |
| 73 | + |
| 74 | +### 反向代理 |
| 75 | + |
| 76 | +内网部署服务众多,我不想记忆每个服务的端口,所以我选择使用内网域名来进行反代,下面是 `Nginx` 反代的配置: |
| 77 | + |
| 78 | +```nginx |
| 79 | +map $http_upgrade $connection_upgrade { |
| 80 | + default upgrade; |
| 81 | + '' close; |
| 82 | +} |
| 83 | +upstream wiki-server { |
| 84 | + server 192.168.5.128:3010; |
| 85 | +} |
| 86 | +server { |
| 87 | + listen 443 ssl; |
| 88 | + server_name wiki.linkzz.hm; |
| 89 | + ssl_certificate ssl/linkzz.hm/_wildcard.linkzz.hm.pem; |
| 90 | + ssl_certificate_key ssl/linkzz.hm/_wildcard.linkzz.hm-key.pem; |
| 91 | +
|
| 92 | + ssl_session_timeout 5m; |
| 93 | + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; |
| 94 | + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; |
| 95 | + ssl_prefer_server_ciphers on; |
| 96 | +
|
| 97 | + location / { |
| 98 | + proxy_pass http://wiki-server; |
| 99 | + proxy_set_header Host $host:$server_port; |
| 100 | + proxy_set_header X-Real-IP $remote_addr; |
| 101 | + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| 102 | + proxy_set_header X-Forwarded-Proto https; |
| 103 | + proxy_set_header Upgrade $http_upgrade; |
| 104 | + proxy_set_header Connection $connection_upgrade; |
| 105 | + proxy_cache_bypass $http_upgrade; |
| 106 | + proxy_http_version 1.1; |
| 107 | + } |
| 108 | +
|
| 109 | + error_page 404 /404.html; |
| 110 | + location = /40x.html { |
| 111 | + } |
| 112 | +
|
| 113 | + error_page 500 502 503 504 /50x.html; |
| 114 | + location = /50x.html { |
| 115 | + } |
| 116 | +} |
| 117 | +
|
| 118 | +server { |
| 119 | + listen 80; |
| 120 | + server_name wiki.linkzz.hm; |
| 121 | + rewrite ^(.*)$ https://${server_name}$1 permanent; |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +## 解决导入时图片跨域问题 |
| 126 | + |
| 127 | +我部署的版本是 `0.25.0` 这个版本导入 Markdown 文件时其他格式都能很好保留,唯独图片存在跨域的问题,看了下控制台,Markdown 中的外链图片会通过官方的 Cloudflare worker 处理,这个worker 默认只处理官方host 的 Referer, 即 <https://app.affine.pro/> ,而我们部署的版本浏览器会自动加上这个域名的Referer,所以图片导入是无法使用的。 |
| 128 | + |
| 129 | +### 部署自己的图片处理 worker |
| 130 | + |
| 131 | +好在官方已经开源了这个 worker |
| 132 | + |
| 133 | +[GitHub - toeverything/affine-workers](https://github.com/toeverything/affine-workers) |
| 134 | + |
| 135 | +我们可以自己部署自己的版本来解决这个问题,Fork 一下官方源码,修改以下地方 |
| 136 | + |
| 137 | +```diff |
| 138 | +modified packages/utils/src/headers.ts |
| 139 | +@@ -5,6 +5,7 @@ const ALLOW_ORIGIN: OriginRule[] = [ |
| 140 | + 'https://app.affine.pro', |
| 141 | + 'https://insider.affine.pro', |
| 142 | + 'https://affine.fail', |
| 143 | ++ 'https://wiki.linkzz.hm', |
| 144 | + 'https://try-blocksuite.vercel.app', |
| 145 | + /https?:\/\/localhost(:\d+)/, |
| 146 | + /https:\/\/.*?-toeverything\.vercel\.app$/, |
| 147 | +modified packages/worker/src/index.ts |
| 148 | +@@ -2,7 +2,7 @@ import { DomainRouterBuilder, domainRoutersHandler, type Env } from '@affine/uti |
| 149 | + |
| 150 | + import { AFFiNEWorker } from './affine.js'; |
| 151 | + |
| 152 | +-const WORKER_DOMAIN = 'affine-worker.toeverything.workers.dev'; |
| 153 | +# 这个地址需要改为你的worker 访问地址 |
| 154 | ++const WORKER_DOMAIN = 'affine-worker.***.workers.dev'; |
| 155 | + |
| 156 | + const affine = AFFiNEWorker(); |
| 157 | + |
| 158 | +modified packages/worker/wrangler.toml |
| 159 | +@@ -4,5 +4,19 @@ compatibility_date = "2023-11-21" |
| 160 | + workers_dev = true |
| 161 | + minify = true |
| 162 | + |
| 163 | +-[limits] |
| 164 | +-cpu_ms = 500 |
| 165 | ++# [limits] |
| 166 | ++# cpu_ms = 500 |
| 167 | ++[observability] |
| 168 | ++enabled = false |
| 169 | ++head_sampling_rate = 1 |
| 170 | ++ |
| 171 | ++[observability.logs] |
| 172 | ++enabled = true |
| 173 | ++head_sampling_rate = 1 |
| 174 | ++persist = true |
| 175 | ++invocation_logs = true |
| 176 | ++ |
| 177 | ++[observability.traces] |
| 178 | ++enabled = false |
| 179 | ++persist = true |
| 180 | ++head_sampling_rate = 1 |
| 181 | +``` |
| 182 | + |
| 183 | +observability 相关的配置是记录worker日志的,可以去掉 |
| 184 | + |
| 185 | +使用 `wrangler`或者在 Cloudflare 控制台关联仓库部署一下,下面是 wrangler 的部署步骤: |
| 186 | + |
| 187 | +安装 [wrangler](https://developers.cloudflare.com/workers/wrangler/) |
| 188 | + |
| 189 | +```shellscript |
| 190 | +npm install -g wrangler |
| 191 | +``` |
| 192 | + |
| 193 | +安装 [pnpm](https://pnpm.io/installation) |
| 194 | + |
| 195 | +```shellscript |
| 196 | + npm install -g pnpm |
| 197 | +``` |
| 198 | + |
| 199 | +安装依赖 |
| 200 | + |
| 201 | +```shellscript |
| 202 | +pnpm install |
| 203 | +``` |
| 204 | + |
| 205 | +登录授权 wrangler |
| 206 | + |
| 207 | +```shellscript |
| 208 | +wrangler login |
| 209 | +``` |
| 210 | + |
| 211 | +部署 worker |
| 212 | + |
| 213 | +```shellscript |
| 214 | +pnpm run deploy |
| 215 | +``` |
| 216 | + |
| 217 | +### |
| 218 | + |
| 219 | +### 应用自己的worker |
| 220 | + |
| 221 | +目前使用的这个版本 worker 的地址是硬编码在前端的,无法使用配置项替换,改官方源码还要编译构建,只能自己改下镜像替换一下制品,目前 `0.25.0`使用这个方法是可行的。 |
| 222 | + |
| 223 | +创建 Dockerfile 文件, 下面的地址需要替换为你部署好的 worker 访问地址。 |
| 224 | + |
| 225 | +```docker |
| 226 | +FROM ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable} |
| 227 | +
|
| 228 | +RUN sed -i 's|https://affine-worker.toeverything.workers.dev|https://affine-worker.***.workers.dev|g' /app/static/js/index.*.js |
| 229 | +``` |
| 230 | + |
| 231 | +然后修改 `docker-compose.yml` 文件 |
| 232 | + |
| 233 | +```yaml |
| 234 | +name: affine |
| 235 | +services: |
| 236 | + affine: |
| 237 | + # 使用 Local Dockerfile 构建镜像 |
| 238 | + #image: ghcr.io/toeverything/affine:${AFFINE_REVISION:-stable} |
| 239 | + build: . |
| 240 | + container_name: affine_server |
| 241 | +``` |
| 242 | +
|
| 243 | +最后重启之后运行容器 |
| 244 | +
|
| 245 | +```shellscript |
| 246 | +docker compose down && docker compose up -d --build |
| 247 | +``` |
| 248 | + |
| 249 | +没问题的话就可以试试效果了 |
| 250 | + |
| 251 | + |
| 252 | + |
| 253 | +以上是我的博客的一篇文章导入的效果,可以看到他成功下载了我图床里面的图片并导入到了内置的存储之中。 |
| 254 | + |
| 255 | +## 结语 |
| 256 | + |
| 257 | +目前使用下来,Affine 还是比较丝滑的,目前这个还不是完整版的 Affine,因为AI 功能尚未配置,不过用作个人知识库倒是足够了,我先深度使用一段时间来看看,后续等 AI 功能稳定了再考虑配置。 |
0 commit comments