CrossBox 是一个 All-in-One 的,可自行架设的,的通信套件,包含了及时通信,邮件,文件存储等等组件。CrossBox 致力于为托管/电子邮件提供商提供一体化的、100%自托管的通信套件。它允许服务提供商利用现有的基础设施,为客户提供通常仅在 Gmail、Outlook 和其他大型 SaaS 平台上可用的通信功能(并且完全符合 GDPR 规定)。
最值得注意的功能就是 CrossBox Cluster。它使服务提供商能够为不同服务器上的所有客户提供单一的入口点。其中包括统一的 Webmail URL,统一的 IMAP,SMTP,POP3 主机名以及统一的 MX。在更复杂的线上环境中,提供商还可以选择构建高可用性,负载均衡和地理分布式设置 - 使得它可以轻松实现 100%可用性,水平扩展以及来自世界不同地区的用户获得快速访问。
CrossBox 集成了超过 40 多个工具和功能,包括了邮件,即时通信,音频和视频通话,会议,屏幕分享,文件存储分享,等等。可以从官方页面的介绍中了解更多。或者直接试用线上版本 进行体验。
CrossBox 的 White-label Add-on 也可以让服务提供商自定义应用的名字,Logo,主题颜色,登录地址,[[webmail]] 域名,IMAP/SMTP/POP3 hostnames,MX 等等细节。更甚至服务提供商可以利用 在线的应用构建程序 直接生成 Android/iOS 的应用。
CrossBox 还提供了 [[DirectAdmin]],[[cPanel]],[[Plesk]] 控制面板的集成。服务提供商一键接入即可,无需任何的修改。1
现在 EV Hosting 也上线了自定义域名邮箱服务,如果你想要一个可以自己控制的域名邮箱,欢迎订购使用,现在使用 EV_MAIL_INIT 可以享受年付 5 折优惠。
很偶然的机会在 Twitter 上看到有人分享了读书笔记,于是就加入了待看书单。
大卫-鲁宾斯坦 David Rubenstein 是彭博一个备受欢迎的财经访谈节目主持人。出于对历史的热爱,他还主持了一档历史节目。作为亿万富翁,他是著名的私募股权公司凯雷集团(The Carlyle Group)的联合创始人。他曾是职业律师,并担任过政府官员。
这是作者以主持人身份采访了一批当今大师级投资者后写的。试图总结出这批人的投资风格、思维模式、行为方式和人生态度等多方面共性。 而作者的经历也体现出富翁们的共性:高度的职业精神。
作者写作本书的目的是要向几十年前的《金钱大师》(The Money Masters)致敬。当年那本经典著作总结了 9 位大师的投资之道。而本书的重点并不是投资的干货,而是努力观察 23 位现在很活跃的投资家共同的特点。
在阅读的过程中,我脑海中一直会跳跃出 [[Naval Ravikant]] ,[[芒格]] 等等人的形象,好的投资人都有一些共同的技能和特点,我一直都认为好的投资一定是做对了什么,而这些投资家看待事情的角度,做对事情的过程,以及内在的思维模式,往往就隐藏着他自己的思考。他们所拥有的「技能」,「思想」,「习惯」,都是值得我们普通人去学习和借鉴的。在书中可以看到的是,这些投资家大多数来自于普通的家庭,往往并不是金融领域的,并且可能还遭遇过很多的失败。但是他们从来不因此而放弃,也从来不跟随市场,他们愿意承担自己的做出的选择所带来的后果,并且愿意承担错误的后果,能够及时地纠正自己的错误,谦虚低调。
最后我想说的是,投资是一门手艺,投资的经验是不可代替的,我们不应该期望通过阅读成功投资者的采访来成为更好的投资者,就像我们不应该期望通过阅读 NBA 球员的采访来成为更好的篮球运动员一样。但是我们要看到成功者身上独特的闪光点:
对投资感兴趣的人。
在使用 [[ChatGPT]] 的时候总是对它一个字一个字的出结果感到焦急,虽然也知道 AI 生成内容的时候确实是一个字一个字计算出来的。OpenAI 使用这样的一个打字机效果也确实符合这个使用场景。但是当我想要自己去实现这样的效果的时候就突然遇到了我的知识盲区,观察 Chrome DevTools,我原本还以为是用 Web Socket 实现的,但是观察了一番发现并没有 Web Socket 的连接。再观察 https://chat.openai.com/backend-api/conversation
接口,发现 content-type: text/event-stream; charset=utf-8
,于是就有了这篇文章。
Server-sent Events (SSE) 是一种服务器推送技术,利用该技术可以让服务器通过 HTTP 连接向客户端推送通知,消息,事件。SSE 通常用于向浏览器客户端发送消息更新或连续数据流,通过称为 EventSource 的 JavaScript API 来增强本机跨浏览器流媒体,客户端通过请求特定 URL 来接收 Event Stream。 SSE 的 media type 为 text /event-stream
。
当我们开发需要数据实时更新的项目时,通常有一个问题,就是「如何从服务端向客户端发送消息/更新」,通常情况下有三种处理方式:
客户端以固定间隔向服务端轮询查询更新。这个技术不是很新,实现比较简单。但这种技术只能算作准实时。
Websocket 是一个流行的技术,用来提供客户端和服务端的双向数据传输。
Websocket 不是基于 HTTP 协议的,所以需要额外的安装和集成,开发和实现难度稍微比 Client Polling 复杂一些。
Server-Sent Events 是一个最新的技术,基于 HTTP,提供从服务端到客户端的异步消息通讯。几乎所有的浏览器都支持 SSE,除了 Internet Explorer。
SEE 使得服务器可以不依赖任何 polling 或 long-polling 的机制来发送消息给客户端。
GET /api/v1/live-stream
Accept: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
text/event-stream
表示客户端会从服务端等待事件流。no-cache
表示禁止缓存。
这个请求会开启一个长连接,服务端可以将实时的内容发送给客户端。Events 发送的内容是 UTF-8 编码的文本内容。
Microsoft 提供了 fetch-event-source 这个库来实现了 POST 请求的 EventSource。
Hestia Control Panel ([[HestiaCP]]) 是一个免费开源的 Web 服务器控制面板,它提供了一个易于使用的 Web 界面来管理 Web 服务器和网站。Hestia CP 支持多种 Linux 发行版,包括 Ubuntu、Debian、CentOS 等,可以安装和配置 Apache 或 Nginx、PHP、MySQL 等常见的 Web 服务器和数据库软件。Hestia CP 还提供了一些额外的功能,如邮件服务器、防火墙、文件备份和恢复等。
ionCube Loader 是一个 PHP 扩展程序,用于解密和执行使用 ionCube 编码技术加密的 PHP 脚本。它通常用于保护商业 PHP 应用程序的源代码,以防止未经授权的访问和复制。我之前在安装 Clientexec 的时候短暂地接触过。之前在 lnmp 和 [[aapanel]] 上都是手动安装的,基本步骤也相差不错,下载 ionCube,然后修改 PHP 配置,在配置里面将 ionCube 扩展的本地路径配置上。
首先访问 ionCube Loader 官网,然后根据自己的系统下载对应版本的二进制。
解压之后可以得到很多 .so
文件。
cp ioncube_loader_lin_7.4.so /usr/lib/php/20190902
echo zend_extension=ioncube_loader_lin_7.4.so > /etc/php/7.4/fpm/conf.d/00-ioncube.ini
echo zend_extension=ioncube_loader_lin_7.4.so > /etc/php/7.4/cli/conf.d/00-ioncube.ini
配置 8.1
echo zend_extension=ioncube_loader_lin_8.1.so > /etc/php/8.1/fpm/conf.d/00-ioncube.ini
echo zend_extension=ioncube_loader_lin_8.1.so > /etc/php/8.1/cli/conf.d/00-ioncube.ini
重启
service php7.4-fpm restart
验证
php7.4 -v
可以在 /usr/lib/php
目录下看到三个日期的目录,上面提到了一个 20190902 的目录。如果要配置其他 PHP 的版本,就需要用到其他的目录。
这三个数字分别代表 PHP 的版本号。20190902 代表 PHP 7.4,20200930 代表 PHP 8.0,而 20220829 代表 PHP 8.1。这些数字用于指定 PHP 扩展或应用程序所需的最低 PHP 版本。如果一个应用程序需要 PHP 7.4 或更高版本,则可以使用 20190902,如果需要 PHP 8.0 或更高版本,则可以使用 20200930,如果需要 PHP 8.1 或更高版本,则可以使用 20220829。
要注意的是 ionCube 是不提供 8.0 版本的,所以如果要求 8.0 以上,那么就需要配置 8.1 的。
之前的一篇文章介绍过如何使用 Docker 自建 [[Chatwoot]],但是最近调用 API 的时候总是发现问题。在调用最普通的接口的时候,按照要求在 Header 中传了 api_access_token
,但是接口返回 401 或者是
{"errors":["You need to sign in or sign up before continuing."]}
简单的查询了一下之后,发现问题出现在 Nginx 上,Nginx 默认情况下不允许带下划线的 Header,所以当请求到 Nginx,然后转发到后台 Chatwoot 的时候这个 api_access_token
就丢了。所以一直出现 401 和需要登录的状况。
解决办法非常容易,在 Nginx 的配置 server
块中增加如下的配置
underscores_in_headers on;
然后 Nginx 配置 reload 即可,因为我使用 [[HestiaCP]] 控制面板,所以后台修改一下配置模板即可。
之前的时候,有一台小主机,在上面安装了 [[Proxmox VE]],然后在其中安装了 [[iKuai]] 和 [[OpenWrt]] 作为软路由使用。现在已经不需要再将其作为软路由代理使用,所以今天就拿出来整理一下,正好放在家里面作为一个 Linux 小服务器,跑一些小一点的程序,然后顺便挂载一个硬盘作为一个小型的媒体服务器。
因为之前在 Proxmox VE 上安装过很多次的系统,这里就不展开,把一些重要的配置和截图放在下面。
在创建虚拟机之前,需要到 Ubuntu Server 官网 下载最新的 ISO 镜像,然后把镜像上传到 ISO Images 中:
之后就可以开始创建 Ubuntu Server。具体的步骤如下。
首先第一步设置节点的名字(Name)
然后第二步选择需要挂载的镜像。
第三步配置 BIOS,保持默认即可。
第四步,选择磁盘,这里个地方可以根据自己的需要调整虚拟磁盘大小。
第五步,设置 CPU 核心,默认是不能超过物理 CPU 的数量的。
第六步,设置网络设备,我这边默认有一个 Linux 网桥(vmbr0),默认即可。
之后点击下一步,确认自己的配置,然后点击完成虚拟机的创建。
之后就可以开启虚拟机,第一次会使用设置 ISO 启动虚拟机,然后进入 Ubuntu Server 的安装界面。
第一次启动虚拟机之后会自动进入安装的程序,安装的过程比较简单,使用键盘选择,确认即可,基本上会分成如下几步:
einverne.info
,那么在 Ubuntu Server 中解析 webserver
的时候会首先尝试去解析 webserver.einverne.info
等待安装程序安装完成之后就可以通过 IP 地址和端口,用户名和密码登录到 Ubuntu Server。
ssh username@ip
CoinPayments 是全球第一个加密货币支付网关,成立于 2013 年 8 月加拿大,CEO 是 Alex Alexandrov。商家可以借助 CoinPayments 来接受加密货币的支付订单。CoinPayments 接受非常多的加密货币,不仅支持流行的比特币(BTC),以太坊(ETH),还支持非常多的小众代币。CoinPayments 还提供了大量的购物车插件,以及定制的支付解决方案和商家工具。
CoinPayments 收取 0.5% 的固定佣金,相较于 Stripe 的 2~3%,以及信用卡更高的佣金,CoinPayments 非常有优势,但目前的问题就在于
CoinPayments 并没有将自己的业务限制在支付工具上,用户也可以通过 CoinPayments 构建一个在线商店,直接通过 CoinPayments 销售商品并接受加密货币支付,但看起来目前做得并不成功。
用户可以使用的另一个独特功能是 PaybyName 服务,它可以简化任何加密货币交易者或投资者的生活。通过创建一个帐户,用户可以获得一个用于发送或接收任所支持加密货币的地址。
CoinPayments 的功能:
CoinPayments 对于所有支付的订单收取 0.5% 的交易手续费。2
对于存入钱包的,每个月前 15000 美元免费,之后收取 0.5%。
CoinPayments 不收取任何提现的手续费,但是如果要转账到其他钱包,根据不同的网络,需要支付不同的网络费用(Network Fees)。
更多的网络费用可以在这里 查看。
CoinPayments 的注册非常简单,只需要一个邮箱即可,但是 [[KYC]] (Know your customer) 的过程稍微有一些复杂。
注册使用 CoinPayments 的步骤:
[[KYC]] 是 CoinPayments 验证注册用户身份的一种方式,在 KYC 的过程中至少需要准备:
然后按照网站给出的流程注册即可,但是需要注意的是,验证网站不能上传截图或者带反光的照片,在拍摄的时候要特别注意,否则可能验证失败。失败之后可以重新拍摄,重新提交进行验证。
身份验证完成之后就可以在 CoinPayments 后台访问设置,商家工具等等。
在注册后,您需要在您的 CoinPayments 账户中添加您要接受的加密货币。这涉及到为每种币种生成一个加密货币地址,客户可以向该地址发送付款,需要注意的是 USDT TRC-20 网络的地址需要提前向该地址发送 0.1 TRX 进行激活。
集成到您的网站:CoinPayments 提供了许多 API 和插件,可帮助您将其支付处理器集成到您的网站上。根据您的技术水平和需求,您可以选择使用 CoinPayments 提供的现成插件或自己编写代码。
接受付款:一旦 CoinPayments 已经被成功集成到您的网站上,您就可以开始接受加密货币付款了。当客户发送付款时,CoinPayments 会将资金保留在您的账户中,直到您将其提取到您的钱包中。
需要注意的是,使用 CoinPayments 需要您了解一些基本的数字货币概念,例如加密货币地址、钱包、Gas Fee 等等。如果您对此还不熟悉,建议您先学习一些相关基础知识。
最后,您可以从 CoinPayments 账户中提取您接受的加密货币。提取的方式取决于您的需求和偏好,您可以选择将资金提取到您的加密货币钱包或将其转换为法定货币后提取到您的银行账户。
在 KYC 的过程中,你会看到 CoinPayments 要求用户选择是什么类型的账户,这个账户类型选择之后就不能修改了,除非联系人工客服。
Non-Incorporated 和 Incorporated 的区别在于
CoinPayments 提供了一套完整的 API 接入方式,具体可查看这里。当用户完成支付时,也可以通过 IPN,Web Callback 的方式来通知系统。
如果觉得文章内容对您有帮助,可以点击下方的支付按钮,支付 1 USDT 表示您的支持。
之前在搭建 Mailcow 邮件服务器的时候简单的了解到了 Sieve 这个可以用来编程过滤邮件的语言。刚好现在要充分利用起 Mailcow,所以系统地学习一下 Sieve 这个邮件过滤编程语言。
Sieve 是由 RFC 5528 定义的一门专门用来处理电子邮件的语言。它被设计不仅可以用于邮件客户端的邮件过滤,也可以在邮件服务器端进行过滤。设计它的目的在于扩展性,且独立于邮件架构和操作系统。 它适合运行在不允许用户执行程序的邮件服务器上运行,例如在 IMAP 服务器上。因为 Sieve 中没有变量,没有循环,也不运行调用外部的 Shell。
Sieve 没有特别复杂的结构,只是包含一组命令,比如 discard
, if
, fileinto
等等
require ["fileinto", "reject"];
# Daffy Duck is a good friend of mine.
if address :is "from" "daffy.duck@example.com"
{
fileinto "friends";
}
# Reject mails from the hunting enthusiasts at example.com.
if header :contains "list-id" "<duck-hunting.example.com>"
{
reject "No violence, please";
}
# The command "keep" is executed automatically, if no other action is taken.
第一行脚本(require 命令)告诉 Sieve 解释器将使用可选的命令文件。然后是两个过滤规则。第一个过滤规则将所有来自 “ daffy.duck@example.com” 的邮件存储到名为“friends”的邮箱中。第二个规则拒绝头部包含字符串“
如果脚本中没有匹配的条件,则应用默认操作,即隐式“保留”命令。该命令将邮件存储在默认邮箱中,通常是 INBOX。
Sieve 有两种注释写法
# Everything after # character will be ignored.
/* this is a bracketed (C-style) comment. */
和地址比较,From:
, To:
, Sender:
还有三个可选的参数可以用来比较
:localpart
,@
符号前面的部分:domain
,@
符号后面的部分:all
,全部# The two test below are equivalent;
# The first variant is clearer and probably also more efficient.
if address :is :domain "to" "example.com"
{
fileinto "examplecom";
}
if address :matches :all "to" "*@example.com"
{
fileinto "examplecom";
}
一个邮件地址通常是 "FirstName LastName" <localpart@domain>
这样组成的。
比较 Header 中其他字段。
# File mails with a Spamassassin score of 4.0 or more
# into the "junk" folder.
if header :contains "x-spam-level" "****"
{
fileinto "junk";
}
Sieve 提供了三种比较方法:
:is
,比较两个字符串完全相等:contains
,是否包含:matches
,使用通配符 ?
来匹配一个未知字符,使用*
来匹配零个或多个未知字符# Reject all messages that contain the string "viagra"in the Subject.
if header :contains "subject" "viagra"
{
reject "go away!";
}
# Silently discard all messages sent from the tax man
elsif address :matches :domain "from" "*hmrc.gov.uk"
{
discard;
}
匹配列表:
# A mail to any of the recipients in the list of strings is filed to the folder "friends".
if address :is "from" ["daffy.duck@example.com", "porky.pig@example.com", "speedy.gonzales@example.com"]
{
fileinto "friends";
}
如果要表达,from 或 sender 是某邮箱的时候,做什么
# Check if either the "from" or the "sender" header is from Porky.
if address :is ["from", "sender"] "porky.pig@example.com"
{
fileinto "friends";
}
如果要组合表达
# Match "from" or the "sender" file with any of Daffy, Porky or Speedy.
if address :is ["from", "sender"] ["daffy.duck@example.com", "porky.pig@example.com", "speedy.gonzales@example.com"]
{
fileinto "friends";
}
allof
测试列表,如果列表中的每一个都是 true,则返回 true,逻辑上的 andanyof
测试列表,只要其中一个满足,则返回 true,逻辑上的 or# This test checks against Spamassassin's header fields:
# If the spam level ls 4 or more and the Subject contains too
# many illegal characters, then silently discard the mail.
if allof (header :contains "X-Spam-Level" "****",
header :contains "X-Spam-Report" "FROM_ILLEGAL_CHARS")
{
discard;
}
# Discard mails that do not have a Date: or From: header field
# or mails that are sent from the marketing department at example.com.
elsif anyof (not exists ["from", "date"],
header :contains "from" "marketing@example.com") {
discard;
}
可以使用 size
来检测
# Delete messages greater than half a MB
if size :over 500K
{
discard;
}
# Also delete small mails, under 1k
if size :under 1k
{
discard;
}
一个简单的 Sieve 过滤器的例子是将所有来自特定发件人的邮件自动转发到另一个邮箱。下面是一个示例 Sieve 脚本:
if header :contains "From" "example@example.com" {
redirect "another@example.com";
}
这个脚本将检查邮件的发件人是否是”example@example.com”,如果是,则将邮件重定向到”another@example.com”。
在 Mailcow 中可以通过如下的路径设置 Sieve 过滤器。
Configuration -> Mail Setup -> Filters -> Add filter
另外如果有人想要创建自己的自定义域名邮箱,欢迎到 EV Hosting 订购使用。
在之前的文章中就提到过 Raycast,前不久看到 [[Raycast]] 快速跟进了 OpenAI,现在推出了 Raycast AI,我没有想到的是,Raycast 的使用场景可以如此完美地和 AI 结合在一起。
Raycast 代替了如下我过去常常做的事情:
选中文本,然后 Cmd+Space,translate 即可。
直接使用 Improve Writing,并将润色过的文本直接粘贴回 Obsidian。
如果在网上看到一段非常长的文章,可以直接选中,然后让 Raycast 总结。
可以看到的是上面的所有的操作,大部分都是 Raycast AI 默认自己定义的,但是 Raycast AI 更强大的一点在于它可以自己创建 AI Command(prompt),也就是说可以利用 OpenAI 的上下文对话的能力,将一些固定的模式写道 Raycast 中,然后下次使用的时候就可以直接输入几个字母触发了。也可以利用 Raycast 自己的快捷键来一键呼出。
Raycast 本身已经非常强大了,但是有一些贴心的功能他没有展示出来,需要用户自己去发现,下面就是一些使用的小 Tip。
如果还没有排到 Raycast AI,还可以试试 macOS 上的 MacGPT。
Raycast AI 将会进入 Raycast Pro 的套餐,每个月 8 美元,目前可以使用 RAYFRIENDS10 来获取 10% 的折扣。
在介绍什么是向量数据库之前先来了解一下数据库的种类。
图上从左往右依次是 Key-Value 数据库([[Redis]],[[HBase]]),文档数据库([[MongoDB]],[[Cosmos DB]]),[[图数据库]]([[图数据库 Neo4j]],[[图数据库 Nebula Graph]]),向量数据库。
向量数据库就是用来存储,检索,分析向量的数据库。
向量数据库是一种专门用于存储和查询向量数据的数据库,其中向量数据指的是由数字组成的向量。向量数据库通常使用高效的相似度搜索算法,例如余弦相似度或欧几里得距离,来快速查询与目标向量最相似的向量。向量数据库在机器学习、计算机视觉、自然语言处理等领域中得到广泛应用。
The vectors are usually generated by applying some kind of transformation or embedding function to the raw data, such as text, images, audio, video, and others.
解决两个问题:
在机器学习领域,通常使用一组数值来表示一个物体的不同特征。比如我们去搜索图片的时候,数据库中存储和对比的并不是图片,而是去搜索算法提取的图片特征。
向量数据库具有以下特点:
目前比较流行的向量数据库包括:
相关的开源项目:
ChatGPT Embedding 后的内容相似度查询是用 Cosine 算法
可学习的代码:
欧氏距离是计算两个点之间最短直线距离的方法。
$d(x,y) = d(y,x) = \sqrt{\sum_{i=1}^{n}(x_i-y_i)^2}$
其中 $x=(x_1, x_2, …, a_n)$ 和 $y=(y_1, y_2, …, y_n)$ 是 N 维欧式空间中的点。
两个向量内积距离计算公式
$\mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^{n} a_i b_i$
其中,$n$ 是向量的维数,$a_i$ 和 $b_i$ 分别是向量 $\mathbf{a}$ 和 $\mathbf{b}$ 在第 $i$ 个维度上的分量。
内积更适合计算向量的方向而不是大小。如果要使用点积计算向量相似度,必需对向量作归一化处理,处理后点积与余弦相似度等价。
二维
[[chatdoc]] 项目