最近几个月用Obsidian让我有点头痛,因为以下的问题让我开始找适合自己使用习惯的新软件和服务:
-
每次需要快速更改内容的时候总是会反复去调整Markdown,在调整的过程中并不美观,有时候我只想写一些简易的文字内容;而且需要用鼠标点击切换才会显示为预览/阅读模式,太累了!
-
保存方案是使用WebDAV通过内网穿透存到家里的All in boom里面,我选择的是基于Wireguard的易有云,两年期不算很贵(买的时候似乎是¥199两年)但是我在切换代理的时候就需要通过浏览器访问认证页面,认证保留时间只有24h,也即每天都需要登录一次,才能打开笔记并同步回我的存储,太麻烦了!
-
我需要使用AI进行内容优化,万一也需要写代码什么的呢?于是买了一个月Notion。Notion对于纯文字内容的优化能力是很强的,但是价格放在那里,能少花钱就少花钱不是?Obsidian插件的话又不够快捷好用, 我除了Ctrl+C/Ctrl+V和截图之外的快捷键一向是不想去记的 如果能支持我用鼠标选中唤醒那不是更快更好吗?
部署AFFiNE
访问项目的页面AFFiNE往下即可看到Self Hosted相关内容,你只需要安装docker和docker compose即可。
在首次部署时,会自动生成AFFiNE的环境配置文件,默认路径在
/root/.affine/self-host/config/affine.js
这个文件中往下找,修改这段,记得末尾的括号要前后匹配,不要漏了
AFFiNE.use('copilot', {
openai: {
baseURL: 'https://API代理网址/v1',
apiKey: '你的Key',
},
# 后面的fal api可以不管,如果你只需要对话功能
# 末尾的括号解注释
})
然后重启整个应用,你就可以在浏览器中访问你搭建的AFFiNE服务,使用AI帮忙编辑你的笔记了。(这里选择让AI帮忙翻译一下)
翻译的消耗量,在这里也简单统计下(用的是去年玩的一个代理站,额度还剩好多没消耗完,放着也是浪费 )
同样,你也可以选择调用本地的AI服务,最好用one-api转换为通用统一的,因为我尝试把供应换成claude就报错了。没有多尝试,有兴趣的可以多试试其它模型。
我在Issue里看了一下,后面开发团队说是会慢慢测试支持各种模型,拭目以待吧。
虽然自建是通过浏览器访问编辑,但是AFFiNE支持批量导入MarkDown、导入HTML和Notion。
如果你自建完成,登录准备编辑后发现一直有个让你开启云同步的红色提示,那么就开启它,没关系的,你并不会自动创建一个云账号担心你的内容被同步上去。实在不放心的话,你就在你的服务器上做一个限制吧。
一定要创建并开启云空间同步,你的笔记才能保存在自建服务器上哦~
在我优化+测试的过程中,我发现了如果按照默认情况来配置AFFiNE,用户会遇到AI请求限制的问题,以及工作区创建限制。 有点鸡贼,我一边改数据库一边恼火。
考察了一下Issue,AFFiNE使用的数据库是 PostgreSQL 解决各类使用限制的方案如下:
进入你的PostgreSQL容器
docker exec -it affine_postgres psql -U affine
执行select命令,你就可以看到权限对应的ID和说明。别忘记了末尾有分号。
affine=# select id, feature, configs from features;
---
id | feature | configs
----+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | copilot | {}
2 | early_access | {"whitelist":["@toeverything.info"]}
3 | early_access | {"whitelist":[]}
4 | unlimited_workspace | {}
5 | unlimited_copilot | {}
6 | ai_early_access | {}
7 | administrator | {}
8 | free_plan_v1 | {"name":"Free","blobLimit":10485760,"storageQuota":10737418240,"historyPeriod":604800000,"memberLimit":3}
9 | pro_plan_v1 | {"name":"Pro","blobLimit":104857600,"storageQuota":107374182400,"historyPeriod":2592000000,"memberLimit":10}
10 | restricted_plan_v1 | {"name":"Restricted","blobLimit":1048576,"storageQuota":10485760,"historyPeriod":2592000000,"memberLimit":10}
11 | free_plan_v1 | {"name":"Free","blobLimit":104857600,"storageQuota":10737418240,"historyPeriod":604800000,"memberLimit":3}
12 | free_plan_v1 | {"name":"Free","blobLimit":10485760,"businessBlobLimit":104857600,"storageQuota":10737418240,"historyPeriod":604800000,"memberLimit":3}
13 | free_plan_v1 | {"name":"Free","blobLimit":10485760,"businessBlobLimit":104857600,"storageQuota":10737418240,"historyPeriod":604800000,"memberLimit":3,"copilotActionLimit":10}
14 | pro_plan_v1 | {"name":"Pro","blobLimit":104857600,"storageQuota":107374182400,"historyPeriod":2592000000,"memberLimit":10,"copilotActionLimit":10}
15 | restricted_plan_v1 | {"name":"Restricted","blobLimit":1048576,"storageQuota":10485760,"historyPeriod":2592000000,"memberLimit":10,"copilotActionLimit":10}
16 | lifetime_pro_plan_v1 | {"name":"Lifetime Pro","blobLimit":104857600,"storageQuota":1099511627776,"historyPeriod":2592000000,"memberLimit":10,"copilotActionLimit":10}
(16 rows)
那么为了解除AI请求限制、创建工作区限制和协作人员数量限制,你可以选择在权限表里添加序号为"4、5、16"来提权。
insert into user_features (id,user_id,feature_id,reason,activated) values (3,'3eae2420-77a6-45b1-b2ec-d2be829752a8',4,'AI request unlimited','t');
---
# 执行这个命令,你就能看到所有用户对应的权限了
select * from user_features;
---
# reason这个项可以随便写,因为是记录权限描述的项,所以我很随意的复制粘贴了
id | user_id | feature_id | reason | created_at | expired_at | activated
----+--------------------------------------+------------+------------+----------------------------+------------+-----------
2 | 3eae2420-77a6-45b1-b2ec-d2be829752a8 | 7 | Admin user | 2024-09-11 09:19:02.565+00 | | t
1 | 3eae2420-77a6-45b1-b2ec-d2be829752a8 | 16 | sign up | 2024-09-11 09:19:02.556+00 | | t
3 | 3eae2420-77a6-45b1-b2ec-d2be829752a8 | 4 | sign up | 2024-09-11 09:28:04.837+00 | | t
4 | 3eae2420-77a6-45b1-b2ec-d2be829752a8 | 5 | sign up | 2024-09-11 09:28:22.998+00 | | t
(4 rows)
依照刚才的操作,现在你已经可以无限次请求AI了
那么成员数如何修改呢?
# 更新权限表里的描述
# 修改memberLimit
affine=# update features set configs = '{"name":"Lifetime Pro","blobLimit":104857600,"storageQuota":1099511627776,"historyPeriod":2592000000,"memberLimit":1000,"copilotActionLimit":10}' where id = 16;
所有的操作完成后,重启PostgreSQL即可
docker restart affine_postgres
成功咯!
接下来就是如何处理网页版无法复制粘贴内容的问题
你在实现了基础的部署后,会发现操作内容时,只能从电脑粘贴内容到AFFiNE里,却无法从AFFiNE拷贝内容到剪贴板上。
这是因为AFFiNE前端代码里面指定了必须要通过HTTPS来实现完整的复制粘贴功能【我也不知道他们怎么想的】那么这里就有两种方案了
- 使用Docker-reverse-proxy这个项目来实现反向代理【依照文档里的步骤来即可,很简单】,避开申请证书的步骤,但是仍需要使用域名来访问,你可能需要加静态解析
- DDNS/Nginx或其他服务,将AFFiNE的端口代理出来。这就可能需要你有一个自己的域名,或者稍微折腾下DDNS了。
如果已经实现了内网穿透,而你不想使用域名访问,只想使用HTTPS+IP,那么可以用nginx如下步骤实现正常的同步、复制粘贴。
生成证书和key【这里选择APT安装nginx了,懒得用Docker把配置文件挂在主机上】
openssl genrsa -out /etc/nginx/ssl.key 2048
openssl req -new -key /etc/nginx/ssl.key -out /etc/nginx/ssl.csr
openssl x509 -req -days 1460 -in /etc/nginx/ssl.csr -signkey /etc/nginx/ssl.key -out /etc/nginx/ssl.crt
# Nginx配置
ssl_certificate /etc/nginx/ssl.crt;
ssl_certificate_key /etc/nginx/ssl.key;
ssl_session_timeout 30m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
允许WebSocket通信【不然你会发现访问的时候一直无法载入】
location / {
proxy_pass http://127.0.0.1:3010;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}