本页内容

  • 先决条件
  • 步骤 1 - 配置防火墙
  • 步骤 2 - 安装 Docker 与 Docker Compose
  • 步骤 3 - 安装 Nginx
  • 步骤 4 - 安装 SSL
  • 步骤 5 - 下载并配置 Standard Notes
  • 步骤 6 - 启动 Standard Notes 服务器
  • 步骤 7 - 安装 Standard Notes Web 应用(安装 Git、安装 NodeJS、安装 Yarn、安装 PM2 包管理器、安装 Web 应用)
  • 步骤 8 - 配置 Nginx
  • 步骤 9 - 配置 SELinux
  • 步骤 10 - 使用 Standard Notes
  • 步骤 11 - 启用付费功能
  • 步骤 12 - 配置服务器以支持文件上传
  • 步骤 13 - 升级 Standard Notes 服务器与 Web 应用
  • 总结
  • 安装 Git
  • 安装 NodeJS
  • 安装 Yarn
  • 安装 PM2 包管理器
  • 安装 Web 应用

Standard Notes 是一款开源且完全加密的笔记应用。它提供免费和付费两种方案,并同时提供云端托管以及在自有服务器上托管的选择。你可以使用自己的服务器在不同设备之间同步存储的笔记。Standard Notes 为所有桌面操作系统和移动平台都提供了客户端应用。

本教程将教你如何在 Rocky Linux 9 机器上自托管 Standard Notes 服务器。你还将学习如何在服务器上免费激活付费方案,以及如何启用文件上传功能。

先决条件

  • 一台运行 Rocky Linux 9 的服务器,至少配备 2 GB 内存。
  • 一个具有 sudo 权限的非 root 用户。
  • 一个指向该服务器的完整域名(FQDN)。在本教程中,我们将使用域名 standardnotes.example.com。你还需要另外两个域名,一个用于文件服务器,另一个用于 Web 应用。我们将使用 snotes-files.example.com 作为文件服务器域名,使用 snotes-web.example.com 作为 Web 应用域名。
  • 系统已更新。 $ sudo dnf update
  • 系统所需的几个软件包。 $ sudo dnf install -y wget nano unzip yum-utils policycoreutils-python-utils 其中部分软件包可能已安装在你的系统上。

一台运行 Rocky Linux 9 的服务器,至少配备 2 GB 内存。

一个具有 sudo 权限的非 root 用户。

一个指向该服务器的完整域名(FQDN)。在本教程中,我们将使用域名 standardnotes.example.com。你还需要另外两个域名,一个用于文件服务器,另一个用于 Web 应用。我们将使用 snotes-files.example.com 作为文件服务器域名,使用 snotes-web.example.com 作为 Web 应用域名。

系统已更新。

$ sudo dnf update

系统所需的一些软件包。

$ sudo dnf install -y wget nano unzip yum-utils policycoreutils-python-utils

其中部分软件包可能已安装在你的系统上。

步骤 1 - 配置防火墙

在安装任何软件包之前,第一步是配置防火墙以开放 HTTP 和 HTTPS 端口。Rocky Linux 使用 Firewalld 防火墙。请检查防火墙的状态。

$ sudo firewall-cmd --state
running

防火墙以不同的区域(zone)工作,public 区域是我们将使用的默认区域。列出防火墙中所有已激活的服务和端口。

$ sudo firewall-cmd --zone=public --list-all

它应当显示如下输出。

public
  target: default
  icmp-block-inversion: no
  interfaces: enp1s0
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 
  protocols: 
  forward: yes
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

在防火墙中开放 HTTP 和 HTTPS 端口。

$ sudo firewall-cmd --zone=public --add-service=http
$ sudo firewall-cmd --zone=public --add-service=https

再次检查防火墙的状态。

$ sudo firewall-cmd --zone=public --list-all

你应该会看到类似的输出。

public
  target: default
  icmp-block-inversion: no
  interfaces: enp1s0
  sources: 
  services: cockpit dhcpv6-client http https ssh
  ports: 
  protocols: 
  forward: yes
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

使所有更改永久生效,并重新加载防火墙以应用更改。

$ sudo firewall-cmd --runtime-to-permanent
$ sudo firewall-cmd --reload

步骤 2 - 安装 Docker 与 Docker Compose

安装官方 Docker 仓库。

$ sudo dnf config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

安装 Docker。

$ sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

启用并运行 Docker 守护进程。

$ sudo systemctl enable docker --now

检查 Docker 服务的状态。

$ sudo systemctl status docker
? docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: disabled)
     Active: active (running) since Tue 2024-04-16 23:35:00 UTC; 5s ago
TriggeredBy: ? docker.socket
       Docs: https://docs.docker.com
   Main PID: 23273 (dockerd)
      Tasks: 9
     Memory: 34.0M
        CPU: 176ms
     CGroup: /system.slice/docker.service
             ??23273 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Apr 16 23:35:00 standard-notes systemd[1]: Starting Docker Application Container Engine...
Apr 16 23:35:00 standard-notes dockerd[23273]: time="2024-04-16T23:35:00.050964934Z" level=info msg="Starting up"
Apr 16 23:35:00 standard-notes dockerd[23273]: time="2024-04-16T23:35:00.083526973Z" level=info msg="Loading containers: start."
Apr 16 23:35:00 standard-notes dockerd[23273]: time="2024-04-16T23:35:00.654481705Z" level=info msg="Firewalld: interface docker0 already part of docker zone, returning"
Apr 16 23:35:00 standard-notes dockerd[23273]: time="2024-04-16T23:35:00.772647844Z" level=info msg="Loading containers: done."
Apr 16 23:35:00 standard-notes dockerd[23273]: time="2024-04-16T23:35:00.793814339Z" level=info msg="Docker daemon" commit=60b9add containerd-snapshotter=false storage-driver=overlay2 version=26.0.1
Apr 16 23:35:00 standard-notes dockerd[23273]: time="2024-04-16T23:35:00.793962347Z" level=info msg="Daemon has completed initialization"
Apr 16 23:35:00 standard-notes dockerd[23273]: time="2024-04-16T23:35:00.827030005Z" level=info msg="API listen on /run/docker.sock"
Apr 16 23:35:00 standard-notes systemd[1]: Started Docker Application Container Engine.

将你的系统用户加入 Docker 组,以避免每次运行 Docker 命令都需要使用 sudo。

$ sudo usermod -aG docker $(whoami)

注销后再次登录服务器以使更改生效。

验证更改是否生效。

$ groups
navjot wheel docker

步骤 3 - 安装 Nginx

Rocky Linux 9 自带较旧版本的 Nginx。你需要使用官方 Nginx 仓库来安装最新版本。

创建并打开文件 /etc/yum.repos.d/nginx.repo 进行编辑。

$ sudo nano /etc/yum.repos.d/nginx.repo

在其中粘贴以下代码。

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

完成后,按 Ctrl + X 保存文件,并在提示时输入 Y 确认。我们将使用 Nginx 的 mainline(主线)版本,因此在上述文件中通过 mainline 区块下的 enabled=1 启用了 mainline 仓库,而不是 stable 仓库。你也可以选择其中任意一个。

安装 Nginx。

$ sudo dnf install nginx -y

验证安装结果。

$ nginx -v
nginx version: nginx/1.25.5

启用并启动 Nginx 服务。

$ sudo systemctl enable nginx --now

检查服务的状态。

$ sudo systemctl status nginx
? nginx.service - nginx - high performance web server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: disabled)
     Active: active (running) since Tue 2024-04-16 23:41:23 UTC; 6s ago
       Docs: http://nginx.org/en/docs/
    Process: 4886 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
   Main PID: 4887 (nginx)
      Tasks: 3 (limit: 10819)
     Memory: 3.1M
        CPU: 11ms
     CGroup: /system.slice/nginx.service
             ??4887 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
             ??4888 "nginx: worker process"
             ??4889 "nginx: worker process"

Apr 16 23:41:23 standard-notes systemd[1]: Starting nginx - high performance web server...
Apr 16 23:41:23 standard-notes systemd[1]: Started nginx - high performance web server.

步骤 4 - 安装 SSL

我们需要安装 Certbot 来生成 SSL 证书。我们将使用 Snapd 包管理器来完成。由于 Rocky Linux 默认未附带 Snapd,因此需要先安装 Snapd。它需要 EPEL(企业版 Linux 附加软件包)仓库才能工作。

安装 EPEL 仓库。

$ sudo dnf install epel-release -y

安装 Snapd 软件包。

$ sudo dnf install snapd -y

启用并启动 Snap 服务。

$ sudo systemctl enable snapd --now

安装 Snap core 软件包,并确保你的 Snapd 版本是最新的。

$ sudo snap install core && sudo snap refresh core

创建 Snapd 工作所需的必要软链接。

$ sudo ln -s /var/lib/snapd/snap /snap
$ echo 'export PATH=$PATH:/var/lib/snapd/snap/bin' | sudo tee -a /etc/profile.d/snapd.sh

安装 Certbot。

$ sudo snap install --classic certbot

使用以下命令,通过创建指向 /usr/bin 目录的符号链接,确保 Certbot 命令可以被执行。

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

检查 Certbot 版本。

$ certbot --version
certbot 2.10.0

运行以下命令来生成 SSL 证书。

$ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m 
[email protected]
 -d standardnotes.example.com

上述命令会将证书下载到服务器上的 /etc/letsencrypt/live/standardnotes.example.com 目录。

我们需要对 Files 子域名执行相同操作。

$ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m 
[email protected]
 -d snotes-files.example.com

最后,再为 Web 应用执行一次。

$ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m 
[email protected]
 -d snotes-web.example.com

生成 Diffie-Hellman 组证书。

$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096

检查 Certbot 续期调度服务。

$ systemctl list-timers

你会发现 snap.certbot.renew.service 是计划运行的服务之一。

NEXT                        LEFT          LAST                        PASSED      UNIT                        ACTIVATES                   ----------------------------------------------------------------------------------------------------------------------------------  
Wed 2024-04-17 00:00:00 UTC 5min left   Tue 2024-04-16 23:29:44 UTC 24min ago    logrotate.timer              logrotate.service
Wed 2024-04-17 00:31:26 UTC 36min left  -                           -            dnf-makecache.timer          dnf-makecache.service
Wed 2024-04-17 11:00:00 UTC 11h left    -                           -            snap.certbot.renew.timer     snap.certbot.renew.service

对该流程进行一次空跑(dry run),以检查 SSL 续期是否正常工作。

$ sudo certbot renew --dry-run

如果你没有看到任何错误,说明一切就绪。你的证书将自动续期。

步骤 5 - 下载并配置 Standard Notes

确保你位于系统的主目录中。

$ cd ~

为 Standard Notes 服务器创建一个工作目录,并切换到该目录。

$ mkdir standardnotes
$ cd ~/standardnotes

从 Standard Notes 的 GitHub 仓库下载环境变量示例文件。

$ wget https://raw.githubusercontent.com/standardnotes/server/main/.env.sample

从下载的示例文件创建实际使用的环境变量文件。

$ cp .env.sample .env

你需要三把用于配置 .env 文件的认证与加密密钥。运行以下命令生成三把独立的密钥,并复制它们的输出。

$ openssl rand -hex 32

你还需要一个强壮的数据库密码。可以运行以下命令生成一个安全的数据库密码。

$ openssl rand -hex 12

打开环境变量文件进行编辑。

$ nano .env

选择一个强壮的数据库密码,并将其填入 DB_PASSWORD 变量。

######
# DB #
######

DB_HOST=db
DB_PORT=3306
DB_USERNAME=std_notes_user
DB_PASSWORD=changeme
DB_DATABASE=standard_notes_db
DB_TYPE=mysql

#########
# CACHE #
#########

REDIS_PORT=6379
REDIS_HOST=cache
CACHE_TYPE=redis

########
# KEYS #
########

AUTH_JWT_SECRET=
AUTH_SERVER_ENCRYPTION_SERVER_KEY=
VALET_TOKEN_SECRET=

添加之前生成的密钥,并将它们分别填入 KEYS 区块中的三个变量。

默认情况下,用户注册是开启的。如果你想在 Standard Notes 服务器上禁用公开的用户注册,可以在环境变量文件中添加以下变量。不过,请在创建好你的第一个账户之后再执行此操作。如果你在最开始就使用该设置,你将无法自己使用该应用。

AUTH_SERVER_DISABLE_USER_REGISTRATION=true

添加以下变量以定义你的 Standard Notes 文件 URL。

PUBLIC_FILES_SERVER_URL=https://snotes-files.example.com

完整的 .env 文件应如下所示。

######
# DB #
######

DB_HOST=db
DB_PORT=3306
DB_USERNAME=std_notes_user
DB_PASSWORD=<YourDBPassword>
DB_DATABASE=standard_notes_db
DB_TYPE=mysql

#########
# CACHE #
#########

REDIS_PORT=6379
REDIS_HOST=cache
CACHE_TYPE=redis

########
# KEYS #
########

AUTH_JWT_SECRET=52b354ad5d8f11c995b4eed33870a1a7afbbc84b8da61e0b583660b075d0390c
AUTH_SERVER_ENCRYPTION_SERVER_KEY=fce4fe78972c3407be47127aee57d67e86b84afb628020b7ab6999edb3c905f1
VALET_TOKEN_SECRET=50aad911a283f252713139667b6475ac105280a220e009e05f733cf785630ce5

AUTH_SERVER_DISABLE_USER_REGISTRATION=true
PUBLIC_FILES_SERVER_URL=https://snotes-files.example.com

完成后按 Ctrl + X 保存文件,并在提示时输入 Y 确认。

从 GitHub 仓库下载 Localstack 引导脚本。

$ wget https://raw.githubusercontent.com/standardnotes/server/main/docker/localstack_bootstrap.sh

为该脚本赋予可执行权限。

$ chmod +x localstack_bootstrap.sh

从 GitHub 仓库下载 Standard Notes 服务器的 Docker Compose 文件。

$ wget https://raw.githubusercontent.com/standardnotes/server/main/docker-compose.example.yml

从下载的示例 Compose 文件创建实际使用的 Docker Compose 文件。

$ cp docker-compose.example.yml docker-compose.yml

打开该文件进行编辑。

$ nano docker-compose.yml

将 MYSQL_ROOT_PASSWORD 和 MYSQL_PASSWORD 的值修改为你之前在 .env 文件中设置的数据库密码。你的 Docker Compose 文件应如下所示。

services:
  server:
    image: standardnotes/server
    env_file: .env
    container_name: server_self_hosted
    restart: unless-stopped
    ports:
      - 3000:3000
      - 3125:3104
    volumes:
      - ./logs:/var/lib/server/logs
      - ./uploads:/opt/server/packages/files/dist/uploads
    networks:
      - standardnotes_self_hosted

  localstack:
    image: localstack/localstack:3.0
    container_name: localstack_self_hosted
    expose:
      - 4566
    restart: unless-stopped
    environment:
      - SERVICES=sns,sqs
      - HOSTNAME_EXTERNAL=localstack
      - LS_LOG=warn
    volumes:
      - ./localstack_bootstrap.sh:/etc/localstack/init/ready.d/localstack_bootstrap.sh
    networks:
      - standardnotes_self_hosted

  db:
    image: mysql:8
    container_name: db_self_hosted
    environment:
      - MYSQL_DATABASE=standard_notes_db
      - MYSQL_USER=std_notes_user
      - MYSQL_ROOT_PASSWORD=<YourDBPassword>
      - MYSQL_PASSWORD=<YourDBPassword>
    expose:
      - 3306
    restart: unless-stopped
    command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
    volumes:
      - ./data/mysql:/var/lib/mysql
      - ./data/import:/docker-entrypoint-initdb.d
    networks:
      - standardnotes_self_hosted

  cache:
    image: redis:6.0-alpine
    container_name: cache_self_hosted
    volumes:
      - ./data/redis/:/data
    expose:
      - 6379
    restart: unless-stopped
    networks:
      - standardnotes_self_hosted

networks:
  standardnotes_self_hosted:
    name: standardnotes_self_hosted

完成后按 Ctrl + X 保存文件,并在提示时输入 Y 确认。至此,Standard Notes 服务器的下载与配置就完成了。

步骤 6 - 启动 Standard Notes 服务器

运行以下命令启动服务器。

$ docker compose up -d

该命令需要几分钟才能完成。在此期间,过程会拉取所有相关的 Docker 镜像,并为所有服务创建容器。它还会填充数据库并执行相应的迁移。CPU 使用率可能会在几分钟内飙升,但很快就会降下来。

你可以在 logs 目录中找到 Standard Notes 服务器的日志。可以使用以下命令查看进程日志。

$ tail -f logs/*.log

按 Ctrl + C 退出日志查看。你可以使用以下任意命令检查正在运行的容器状态。

$ docker compose ps
OR
$ docker ps

你将收到类似的输出。

NAME                     IMAGE                       COMMAND                  SERVICE      CREATED          STATUS                   PORTS
cache_self_hosted        redis:6.0-alpine            "docker-entrypoint.s…"   cache        16 minutes ago   Up 5 minutes             6379/tcp
db_self_hosted           mysql:8                     "docker-entrypoint.s…"   db           16 minutes ago   Up 5 minutes             3306/tcp, 33060/tcp
localstack_self_hosted   localstack/localstack:3.0   "docker-entrypoint.sh"   localstack   16 minutes ago   Up 5 minutes (healthy)   4510-4559/tcp, 4566/tcp, 5678/tcp
server_self_hosted       standardnotes/server        "docker-entrypoint.sh"   server       16 minutes ago   Up 5 minutes             0.0.0.0:3000->3000/tcp, :::3000->3000/tcp, 0.0.0.0:3125->3104/tcp, :::3125->3104/tcp

你可以使用以下命令检查服务器的健康状态。

$ curl http://localhost:3000/healthcheck
OK

Standard Notes 默认使用 3000 端口。如果你在 .env 文件中配置了不同的端口,请在上面的命令中相应更新。

步骤 7 - 安装 Standard Notes Web 应用

你可以使用现有的 Standard Notes Web 应用,也可以自行托管。在本教程中,我们将自托管 Standard Notes Web 应用。

安装 Git

在继续之前,我们需要安装一些必要的组件,其中之一就是 Git。安装 Git。

$ sudo dnf install git -y

安装 NodeJS

下一步是安装 NodeJS。我们将为此安装 Node 18.x LTS 版本。执行以下命令来安装 NodeJS。

$ curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -
$ sudo dnf install -y nodejs

确认安装结果。

$ node --version
v18.20.2

安装 Yarn

我们还需要 Yarn 包管理器。使用 Node 安装它。

$ sudo npm install --global yarn

安装 PM2 包管理器

我们需要 PM2 来将 Standard Notes Web 应用作为后台服务运行。

$ sudo npm install --global pm2

安装 Web 应用

为 Web 应用创建一个目录,并切换到该目录。

$ cd ~
$ mkdir snotes-app
$ cd ~/snotes-app

从 GitHub 下载 Standard Notes 应用。

$ git clone https://github.com/standardnotes/app.git .

使用 Yarn 安装依赖。可以忽略任何警告。

$ yarn install

为 Web 应用构建所需的软件包。

$ yarn build:web

切换到 web 目录。

$ cd packages/web

使用 PM2 启动 Web 应用。

$ pm2 start "yarn start" --name api

你可以使用以下命令检查服务的状态。

$ pm2 status
[PM2] Starting /usr/bin/bash in fork_mode (1 instance)
[PM2] Done.
????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
? id ? name   ? namespace   ? version ? mode    ? pid      ? uptime ? ?    ? status    ? cpu      ? mem      ? user     ? watching ?
????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
? 0  ? api    ? default     ? N/A     ? fork    ? 15090    ? 0s     ? 0    ? online    ? 0%       ? 26.2mb   ? navjot   ? disabled ?
????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

应用将被启动,并可通过 3001 端口访问。我们将利用这一信息为 Web 应用创建 Nginx 配置。

PM2 不会在系统重启时自动重启应用。为此,我们需要生成一个启动脚本。

$ pm2 startup

你应该会看到类似的输出。

[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u navjot --hp /home/navjot

运行输出中提供的命令。

$ sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u navjot --hp /home/navjot

这将生成并启用一个 systemd 服务文件。

保存该进程,使其能够在重启时自动启动。

$ pm2 save

步骤 8 - 配置 Nginx

打开文件 /etc/nginx/nginx.conf 进行编辑。

$ sudo nano /etc/nginx/nginx.conf

在 include /etc/nginx/conf.d/*.conf; 这一行之前添加以下一行。

server_names_hash_bucket_size 64;

完成后按 Ctrl + X 保存文件,并在提示时输入 Y 确认。

创建并打开文件 /etc/nginx/conf.d/standardnotes.conf 进行编辑。

$ sudo nano /etc/nginx/conf.d/standardnotes.conf

在其中粘贴以下代码。请将 standardnotes.example.com 替换为你的域名。我们将 client_max_body_size 的值设置为 50MB。你可以根据自己的需求进行修改。

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  http2 on;
  http3 on;
  quic_retry on;

  server_name standardnotes.example.com;

  client_max_body_size 50M;

  access_log  /var/log/nginx/standardnotes.access.log;
  error_log   /var/log/nginx/standardnotes.error.log;

  ssl_certificate      /etc/letsencrypt/live/standardnotes.example.com/fullchain.pem;
  ssl_certificate_key  /etc/letsencrypt/live/standardnotes.example.com/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/standardnotes.example.com/chain.pem;

  ssl_session_timeout  5m;
  ssl_session_cache shared:MozSSL:10m;
  ssl_session_tickets off;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers off;
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
  resolver 8.8.8.8;
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_dhparam /etc/ssl/certs/dhparam.pem;

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_cache off;
  }
}
# enforce HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name  standardnotes.example.com;
    return 301   https://$host$request_uri;
}

完成后按 Ctrl + X 保存文件,并在提示时输入 Y 确认。

上面的配置文件用于主 Standard Notes 应用。接下来,我们需要为 Files 子域名配置另一个文件。

$ sudo nano /etc/nginx/conf.d/files-standardnotes.conf

在其中粘贴以下代码。请将 snotes-files.example.com 替换为你的域名。我们将 client_max_body_size 变量设置为 50MB。你可以根据自己的需求进行修改。

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  http2 on;
  http3 on;
  quic_retry on;

  server_name snotes-files.example.com;

  client_max_body_size 50M;

  access_log  /var/log/nginx/files-standardnotes.access.log;
  error_log   /var/log/nginx/files-standardnotes.error.log;

  ssl_certificate      /etc/letsencrypt/live/snotes-files.example.com/fullchain.pem;
  ssl_certificate_key  /etc/letsencrypt/live/snotes-files.example.com/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/snotes-files.example.com/chain.pem;

  ssl_session_timeout  5m;
  ssl_session_cache shared:MozSSL:10m;
  ssl_session_tickets off;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers off;
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
  resolver 8.8.8.8;
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_dhparam /etc/ssl/certs/dhparam.pem;

  location / {
    proxy_pass http://127.0.0.1:3125;
    proxy_cache off;
  }
}
# enforce HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name  snotes-files.example.com;
    return 301   https://$host$request_uri;
}

最后,我们还需要为 Web 应用创建一个配置文件。

$ sudo nano /etc/nginx/conf.d/web-standardnotes.conf

在其中粘贴以下代码。

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  http2 on;
  http3 on;
  quic_retry on;

  server_name snotes-web.example.com;

  client_max_body_size 50M;

  access_log  /var/log/nginx/standardnotes-web.access.log;
  error_log   /var/log/nginx/standardnotes-web.error.log;

  ssl_certificate      /etc/letsencrypt/live/snotes-web.example.com/fullchain.pem;
  ssl_certificate_key  /etc/letsencrypt/live/snotes-web.example.com/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/snotes-web.example.com/chain.pem;

  ssl_session_timeout  5m;
  ssl_session_cache shared:MozSSL:10m;
  ssl_session_tickets off;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers off;
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
  resolver 8.8.8.8;
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_dhparam /etc/ssl/certs/dhparam.pem;

  location / {
    proxy_pass http://127.0.0.1:3125;
    proxy_cache off;
  }
}
# enforce HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name  snotes-web.example.com;
    return 301   https://$host$request_uri;
}

验证你的 Nginx 配置。

$ sudo nginx -t

重启 Nginx 服务器以使配置文件生效。

$ sudo systemctl restart nginx

步骤 9 - 配置 SELinux

应用策略以允许连接到外部主机。

$ sudo setsebool -P httpd_can_network_connect 1

应用策略以允许 Nginx 访问 PostgreSQL。

$ sudo setsebool -P httpd_can_network_connect_db 1

步骤 10 - 使用 Standard Notes

如果你在浏览器中打开 URL https://standardnotes.example.com,你应该会看到如下输出。

在 Rocky Linux 9 上安装部署 Standard Notes 服务器

这意味着你的服务器已经启动并正常运行。要使用 Standard Notes,你需要使用官方客户端应用。在本教程中,我们将使用其 Web 应用,但桌面端和移动端应用的方法也是相同的。

打开 URL https://snotes-web.example.com 以访问 Web 应用。点击页面左下角的“创建免费账户(Create Free Account)”链接,填写你的电子邮件地址和密码。点击“高级功能(Advanced features)”按钮,取消勾选“自定义同步服务器(Custom sync server)”选项,并在框中填入 URL https://standardnotes.example.com。

在 Rocky Linux 9 上安装部署 Standard Notes 服务器

点击“下一步(Next)”按钮继续。系统会再次要求你输入密码。

在 Rocky Linux 9 上安装部署 Standard Notes 服务器

登录后,你就可以开始创建笔记并使用该应用了。

步骤 11 - 启用付费功能

到目前为止,我们已为 Standard Notes 应用启用了基础功能。不过,你可以激活一些服务器端付费功能,而 Standard Notes 允许你免费这样做。这不会激活客户端的高级功能(如超级笔记 Super Notes 和嵌套标签 Nested tags)。你可以通过购买 Standard Notes 离线(Offline)方案来获取大幅折扣的订阅以激活它们。

你需要从 Standard Notes 目录运行以下命令,以在自托管应用中启用服务器端高级功能。

$ cd ~/standardnotes

$ docker compose exec db sh -c "MYSQL_PWD=\$MYSQL_ROOT_PASSWORD mysql \$MYSQL_DATABASE -e \
      'INSERT INTO user_roles (role_uuid , user_uuid) VALUES ((SELECT uuid FROM roles WHERE name=\"PRO_USER\" ORDER BY version DESC limit 1) ,(SELECT uuid FROM users WHERE email=\"EMAIL@ADDR\")) ON DUPLICATE KEY UPDATE role_uuid = VALUES(role_uuid);' \
    "

$ docker compose exec db sh -c "MYSQL_PWD=\$MYSQL_ROOT_PASSWORD mysql \$MYSQL_DATABASE -e \
      'INSERT INTO user_subscriptions SET uuid=UUID(), plan_name=\"PRO_PLAN\", ends_at=8640000000000000, created_at=0, updated_at=0, user_uuid=(SELECT uuid FROM users WHERE email=\"EMAIL@ADDR\"), subscription_id=1, subscription_type=\"regular\";' \
    "

重新加载 Web 应用,付费功能应该已为你的账户激活。要激活客户端功能,请打开应用偏好设置,选择“通用(General)>> 离线激活(Offline activation)”菜单,并输入你通过购买离线方案获得的激活码。

步骤 12 - 配置服务器以支持文件上传

文件上传是 Standard Notes 的付费功能,不购买离线方案将无法使用。我们已经为文件上传启用了自定义 API URL。但它们仍然无法工作。要让它们正常工作,我们需要为 uploads 目录赋予适当的权限。上传文件存储在 ~/standardnotes/uploads 目录中。运行以下命令更改权限。

$ sudo chmod -R 775 data
$ sudo chmod -R 755 uploads
$ sudo chown -R 1001.1001 uploads

现在,Standard Notes 为每个用户设置的文件上传限制为 0。这意味着除非手动分配配额,否则任何用户都无法上传文件。因此,让文件上传正常工作最后一步是为付费用户账户启用文件配额。我们可以通过在数据库容器内执行 SQL 查询来实现。

登录数据库容器内的 MySQL shell。

$ docker exec -it db-standalone mysql -u std_notes_user -p
Enter password:

进入 MySQL shell 后,让我们查看数据库列表。

mysql > show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
| standard_notes_db  |
+--------------------+
3 rows in set (0.01 sec)

切换到 Standard Notes 数据库。

mysql > use standard_notes_db;

运行以下 SQL 命令,为上面激活的付费用户添加 10GB 的文件配额。

mysql> INSERT INTO subscription_settings(uuid, name, value, created_at, updated_at, user_subscription_uuid) VALUES (UUID(), "FILE_UPLOAD_BYTES_LIMIT", 10737418240, FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), (SELECT us.uuid FROM user_subscriptions us INNER JOIN users u ON us.user_uuid=u.uuid WHERE u.email="
[email protected]
"));

这里的 10737418240 指总字节数,即 10GB。你可以根据需要修改这个数字。

退出 MySQL shell 和数据库容器。

mysql > exit

步骤 13 - 升级 Standard Notes 服务器与 Web 应用

升级 Standard Notes 服务器的第一步是停止并移除现有的容器。

$ cd ~/standardnotes
$ docker compose stop
$ docker compose remove --orphans

从 GitHub 仓库获取最新的 .env、localstack_bootstrap.sh 以及 Docker Compose 文件。进行必要的修改,并用更新后的文件替换你现有的文件。

再次启动服务器容器。

$ docker compose up -d

你更新后的同步服务器现在正在运行。

切换到 Web 应用目录。

$ cd ~/snotes-app

切换到 Web 应用根目录。

$ cd packages/web

停止现有进程。

$ pm2 stop api

切换回主应用目录。

$ cd ~/snotes-app

从 GitHub 仓库拉取最新更改。

$ git pull

重新进入 web 目录。

$ cd packages/web

启动该进程。

$ pm2 start api

你的 Web 应用应该已经更新。

总结

至此,我们在 Rocky Linux 9 机器上安装和配置 Standard Notes 服务器的教程就结束了。如果你有任何问题,请在下方评论区留言。