在没有 Docker 的情况下部署 Discourse

我的解决方案的一些细节:

  1. Nginx 配置:
    在反向代理 (haproxy) 之后,您需要将 /var/discourse/config/nginx-config-sample.conf 复制到 /etc/nginx/sites-enabled/discourse.conf 或类似的目录,然后替换 listen <yourreverseproxyport>(如果需要),并将 server_name 中的 _ 替换为主机名(subdmain.example.com)。其他任何内容都不应更改。
  2. 页面未显示(“哎呀”消息)和电子邮件管理员注册不工作:
    Magick 是导致首次启动后管理员注册页面未显示页面的原因。我遵循了日志中指示的文件:/var/www/discourse/lib/letter_avatar.rb:112,在第 112 行,有两个 magick 命令确实没有响应。convert 响应了,所以我将该行中的 magick 替换为 convert。在进行这些更正后,又记录了一个错误。尝试使用文件中的给定命令进行相同的过程,magick 命令不起作用,并且 convert 失败。注意:magick --version 是 7.x,而 convert --version 是 6.x。原因是:我首先使用 apt 安装了 imagemagick,然后从源代码安装了 magick 7。存在一些冲突,magick 告诉 convert 命令已过时。所以我只用 magick 7 重新运行了我的脚本。这立即解决了问题,我可以看到等待了数天的全新页面,电子邮件也正常工作了!Magick 确实很神奇。
  3. 混合内容问题仍然存在,但网站运行正常。
    注意:在 puma.rb 中,行 bind ENV.fetch("PUMA_BIND", "tcp://#{ENV['PUMA_BIND_ALL'] ? '' : '127.0.0.1:'}3000") 必须像这样(修正了第一个帖子。)
  4. 编辑“3.”,关于强制 discourse 使用 https:我不知道确切原因,但我的浏览器不再显示“混合内容”警告(也许是因为缓存更新),所以现在一切都很好。我只需要完成我的脚本。
3 个赞

已确认可以正常工作!!!
ImageMagick v7 解决了 Ooops 问题!
在我测试过的范围内,它完全可以正常工作。
我将测试其余功能并尽快报告。

1 个赞

我为测试所做的是在运行 puma 时设置环境变量 PUMA_BIND

关于 ImageMagick,不知何故 Discourse 在 webUI 中执行图像转换失败,当我上传大图片时,它很友好地拒绝转换……我仍在调试这个问题。

调试有进展吗?我这边正在处理 magick。
对于一些大约 50kb 的图片,浏览器会弹出一个窗口显示:
timeout -k 40.0 20 magick gif:/tmp/RackMultipart20250927-23598-xrrp6e.gif -auto-orient -background white -interlace none -flatten -debug all -quality 90 jpg:/tmp/image20250927-23598-9ujq3d.jpg 并且没有图片加载。
如果图片尺寸更大,则不会弹出窗口,但加载轮会无限循环转动,没有任何结果。/var/www/discourse/log 中没有记录任何错误。

对我来说也是同样的问题 :joy::sob:

我唯一能让它运行的方法是使用 brew 仓库中的 imagemagick。

但是,当图片超过 3MB 时它就会失败……也许我设置了一个非常严格的策略配置。

试试看!!!

我正在测试 docker 安装,但我认为或者说我非常愚蠢地将 nginx、unicorn、redis、postgresql 和其他东西部署在同一个容器中……这完全没有意义。而且没有针对大型部署的基础设施文档……我在 IT 行业工作了 20 多年,我只看到使用这种基础设施在不久的将来会出现问题。

更不用说“Docker 空间吞噬者”(就像 Dormammu 一样)了 :rofl::rofl:

此问题已通过以下解决方案解决:

调试 1:bundle db:create 输出,首次启动时也在日志中:


未知 OID 21096:无法识别“embeddings”的类型。它将被视为字符串。
pngquant 工作进程:“pngquant”未找到;请提供正确的二进制文件或禁用此工作进程(通过参数 --no-pngquant 或选项 :pngquant => false
oxipng 工作进程:“oxipng”未找到;请提供正确的二进制文件或禁用此工作进程(通过参数 --no-oxipng 或选项 :oxipng => false
jhead 工作进程:“jhead”未找到,“jpegtran”未找到;请提供正确的二进制文件或禁用此工作进程(通过参数 --no-jhead 或选项 :jhead => false
jpegoptim 工作进程:“jpegoptim”未找到;请提供正确的二进制文件或禁用此工作进程(通过参数 --no-jpegoptim 或选项 :jpegoptim => false

调试 2:某些 magick 命令处理文件时输出:

此图像格式没有解码委托
此最后的问题在此处提及:here

解决方案在此处:here(安装带有格式插件的 magick):

t=$(mktemp) && \
wget 'https://dist.1-2.dev/imei.sh' -qO "$t" && \
bash "$t" && \
rm "$t"

在此之后,我的上传大小可以达到 518KB。之前不行。这仅适用于图像。所有其他文档、音频、视频上传都可以正常工作。

剩余问题的临时解决方案:
我查看了管理员设置、discourse.conf、nginx/sites-enables/discourse.conf、git 文件夹。最后在 AdminPanel/Parameters/Files 中,我禁用了“Composer media optimization image enabled”,然后一切正常。我可以上传任何图像。

是的,IMEI 几乎和使用 brew 的解决方案一样,但我确信它不会占用大约 2GB 的磁盘空间 :rofl::rofl::sob::sob::sob::face_with_symbols_on_mouth::face_with_symbols_on_mouth::face_with_symbols_on_mouth:

我将检查您关于上传最大文件大小的解决方案。

我也在为“免费”发送电子邮件而苦苦寻找解决方案(我处于早期阶段,不想签约 mailtrap/mailgun/etc…)

为什么不安装邮件服务器?我有一个自己的,并且编写了安装脚本。如果你有兴趣,请告诉我。

好的!请私信我说明指示或 git 仓库!:grinning_face_with_smiling_eyes:

我多年前就不再使用自托管邮件服务器了……

执行 rake assets:precompile 时,提示 No such file or directory - brotli,请使用包管理器安装它。

2 个赞

我发现它抛出了这个错误:

bundler: failed to load command: puma (/home/mry/.rbenv/versions/3.4.6/bin/puma)
/home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/puma-7.0.4/lib/puma/cluster.rb:472:in `Puma::Cluster#run': undefined method `wait_readable' for nil (NoMethodError)

            if read.wait_readable([0, @next_check - Time.now].max)
                   ^^^^^^^^^^^^^^
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/puma-7.0.4/lib/puma/launcher.rb:202:in `Puma::Launcher#run'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/puma-7.0.4/lib/puma/cli.rb:73:in `Puma::CLI#run'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/puma-7.0.4/bin/puma:10:in `<top (required)>'
        from /home/mry/.rbenv/versions/3.4.6/bin/puma:25:in `Kernel#load'
        from /home/mry/.rbenv/versions/3.4.6/bin/puma:25:in `<top (required)>'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli/exec.rb:59:in `Kernel.load'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli/exec.rb:59:in `Bundler::CLI::Exec#kernel_load'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli/exec.rb:23:in `Bundler::CLI::Exec#run'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli.rb:452:in `Bundler::CLI#exec'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/vendor/thor/lib/thor/command.rb:28:in `Bundler::Thor::Command#run'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `Bundler::Thor::Invocation#invoke_command'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/vendor/thor/lib/thor.rb:538:in `Bundler::Thor.dispatch'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli.rb:35:in `Bundler::CLI.dispatch'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/vendor/thor/lib/thor/base.rb:584:in `Bundler::Thor::Base::ClassMethods#start'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli.rb:29:in `Bundler::CLI.start'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/exe/bundle:28:in `block in <top (required)>'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/friendly_errors.rb:117:in `Bundler.with_friendly_errors'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/exe/bundle:20:in `<top (required)>'
        from /home/mry/.rbenv/versions/3.4.6/bin/bundle:25:in `Kernel#load'
        from /home/mry/.rbenv/versions/3.4.6/bin/bundle:25:in `<main>'

然后 Puma 会重启。我该怎么做才能阻止这种情况发生?

编辑:这似乎是 Puma 自 Ruby 3.4.0 以来的一个问题 (Puma 的问题)。通过禁用 YJIT 解决了。

然后使用 3.3.7。这是我使用的版本。

我太懒了,不想重新下载 gem,只需禁用 YJIT 并继续使用 3.4.6。

大家好,
这是我在 Debian 12 容器(我使用的是 LXC)上,无需 Docker,分三步完成完整安装的脚本。
使用 if 块来仅运行你需要的部分。

  1. 设置你的参数,并以 root 身份使用所需参数运行脚本。注意:脚本会在“### MANUAL”部分之前的退出点终止。
  2. 你必须手动在容器终端中执行这些命令。我在我的非特权 LXC 容器中未能使其自动运行成功。(默认用户为 root)
  3. 然后运行其余部分。
    我多次测试过,它确实可以工作,但在适配发布后,可能仍存在一些 bug。
install_script
#!/bin/bash
########## 在 Debian 12 上无需 Docker 安装 Discourse 的脚本 ##################
#- 最后测试时间:2025 年 12 月
############################## 请以 root 身份运行,最好在 LXC 容器中!!!!!!!
#- 讨论来源:https://meta.discourse.org/t/deploy-discourse-without-docker/351194
##############################################################################
# 手动参数
certbot_contact_email=hello@domain.tld
certbot_renew_port=9785
nginx_discourse_port=8080
DISCOURSE_DEVELOPER_EMAILS='emailaddress'
DISCOURSE_SMTP_ADDRESS=smtp_server_address
DISCOURSE_SMTP_PORT=465
DISCOURSE_SMTP_USER_NAME=xxx
DISCOURSE_SMTP_PASSWORD=xxx
DISCOURSE_SMTP_DOMAIN=xxx
DISCOURSE_NOTIFICATION_EMAIL=no-reply-xxxx
DISCOURSE_MAXMIND_ACCOUNT_ID=50
# 手动参数结束

shopt -s expand_aliases
source /etc/bash.bashrc
container=$1
cont_haproxy=$2
pool=$3
lxc_image=$4
hostname=$5
db_password=$6
maxmind_license_key=$7
DISCOURSE_HOSTNAME=$hostname
DISCOURSE_MAXMIND_LICENSE_KEY=$maxmind_license_key

### 安装 ImageMagick 7 及其插件
# https://github.com/SoftCreatR/imei/?tab=readme-ov-file
if [ 1 == 1 ]; then
t=$(mktemp) && \
wget 'https://dist.1-2.dev/imei.sh' -qO "$t" && \
bash "$t" && \
rm "$t"
fi

### 安装基础组件 ###
if [ 1 == 1 ]; then
apt install -y apt-utils postgresql nginx libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-static redis
systemctl enable --now postgresql redis-server nginx
useradd -m -s /bin/bash discourse
usermod -aG sudo discourse
passwd -d discourse
# RVM (RUBY)
apt install -y ruby-full # 必需
su - discourse -c 'curl -sSL https://get.rvm.io | bash'
su - discourse -c 'source /home/discourse/.bashrc'
su - discourse -c 'source /home/discourse/.rvm/scripts/rvm' # rvm 安装后必需
su - discourse -c 'rvm install 3.3.7' # 截至今天将安装 node 3.3.7
source /home/discourse/.rvm/scripts/rvm
# fi
# 配置 PostgreSQL
apt update && apt upgrade -y
apt autoremove -y

# 你必须接受升级
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
apt install -y postgresql-18-pgvector
fi
alias mydb="sudo -u postgres psql discourse -c "
if [ 1 == 1 ]; then
su - discourse -c 'sudo -u postgres createuser -s discourse'
# fi
su - discourse -c 'sudo -u postgres createdb discourse'
su - discourse -c 'sudo -u postgres psql -c "ALTER USER discourse WITH ENCRYPTED PASSWORD '\''password'\'';"'
alias mydb="sudo -Hiu discourse psql -c "
su - discourse -c 'psql -c "GRANT CONNECT ON DATABASE discourse TO root;"'
su - discourse -c 'psql -c "CREATE EXTENSION pg_trgm;CREATE EXTENSION hstore;"'
su - discourse -c 'psql -c "ALTER DATABASE discourse OWNER TO discourse;"'
su - discourse -c 'psql -c "CREATE EXTENSION unaccent;CREATE EXTENSION plpgsql;"'
su - discourse -c 'psql -c "CREATE EXTENSION vector;"'
echo "OK"

### 从 GitHub 安装 Discourse
cd /var/www/
git clone https://github.com/discourse/discourse
git config --global --add safe.directory /var/www/discourse
# exit
### 对 bundle、ruby 等是必需的,但对 Discourse 本身并非必需
tee -a /home/discourse/.bashrc > /dev/null <<EOT
export RAILS_ENV=production
export UNICORN_SIDEKIQ_MAX_RSS=800
export UNICORN_WORKERS=8
export UNICORN_SIDEKIQS=1
export PUMA_SIDEKIQ_MAX_RSS=800
export PUMA_WORKERS=8
export PUMA_SIDEKIQS=1
export RUBY_GC_HEAP_GROWTH_MAX_SLOTS=40000
export RUBY_GC_HEAP_INIT_SLOTS=400000
export RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=1.5

export DISCOURSE_HOSTNAME=$hostname
export DISCOURSE_DEVELOPER_EMAILS=$DISCOURSE_DEVELOPER_EMAILS
export DISCOURSE_SMTP_ADDRESS=$DISCOURSE_SMTP_ADDRESS
export DISCOURSE_SMTP_PORT=$DISCOURSE_SMTP_PORT
export DISCOURSE_SMTP_USER_NAME=$DISCOURSE_SMTP_USER_NAME
export DISCOURSE_SMTP_PASSWORD=$DISCOURSE_SMTP_PASSWORD
export DISCOURSE_SMTP_DOMAIN=$DISCOURSE_SMTP_DOMAIN
export DISCOURSE_NOTIFICATION_EMAIL=$DISCOURSE_NOTIFICATION_EMAIL
export DISCOURSE_MAXMIND_ACCOUNT_ID=$DISCOURSE_MAXMIND_ACCOUNT_ID
export DISCOURSE_MAXMIND_LICENSE_KEY=$maxmind_license_key

export DISCOURSE_ENABLE_CORS=true
export DISCOURSE_MAX_REQS_PER_IP_MODE=none
export DISCOURSE_MAX_REQS_PER_IP_PER_MINUTE=20000
export DISCOURSE_MAX_REQS_PER_IP_PER_10_SECONDS=5000
export DISCOURSE_MAX_ASSET_REQS_PER_IP_PER_10_SECONDS=20000
export DISCOURSE_MAX_REQS_RATE_LIMIT_ON_PRIVATE=false
export DISCOURSE_MAX_USER_API_REQS_PER_MINUTE=200
export DISCOURSE_MAX_USER_API_REQS_PER_DAY=28800
export DISCOURSE_MAX_ADMIN_API_REQS_PER_MINUTE=600
export DISCOURSE_MAX_DATA_EXPLORER_API_REQ_MODE=none
EOT
cd /var/www/discourse
git stash
git pull
git checkout tests-passed 
cd plugins
for plugin in *
do
echo "ok"
    echo $plugin; cd ${plugin}; git pull; cd ..
done
# fi
chown -R discourse:discourse /var/www/discourse/     
mkdir -p /var/www/discourse/public
chown -R discourse:www-data /var/www/discourse/public
tee -a /var/www/discourse/config/discourse.conf > /dev/null <<EOT
max_data_explorer_api_req_mode = 'none'
max_user_api_reqs_per_day = '28800'
hostname = '$hostname'
redis_host = 'localhost'
smtp_user_name = $$DISCOURSE_SMTP_USER_NAME
db_password = '$db_password'
smtp_address = '$DISCOURSE_SMTP_ADDRESS'
db_socket = ''
max_reqs_per_ip_per_10_seconds = '5000'
max_asset_reqs_per_ip_per_10_seconds = '20000'
max_reqs_rate_limit_on_private = 'false'
developer_emails = '$DISCOURSE_DEVELOPER_EMAILS'
max_user_api_reqs_per_minute = '200'
maxmind_license_key = '$maxmind_license_key'
smtp_port = '$DISCOURSE_SMTP_PORT'
maxmind_account_id = '$DISCOURSE_MAXMIND_ACCOUNT_ID'
smtp_password = '$DISCOURSE_SMTP_PASSWORD'
max_reqs_per_ip_per_minute = '20000'
notification_email = '$DISCOURSE_NOTIFICATION_EMAIL'
db_host = 'localhost'
enable_cors = 'true'
db_port = ''
max_reqs_per_ip_mode = 'none'
smtp_domain = '$DISCOURSE_SMTP_DOMAIN'
#max_admin_api_reqs_per_minute = '600'
smtp_force_tls='true'
EOT

cd /var/www/discourse && sed -i '/gem "rails_multisite"/i gem "rails"' Gemfile
cd /var/www/discourse && sed -i '/gem "image_optim"/i gem "image_optim_pack"' Gemfile
su - discourse -c 'cd /var/www/discourse && bundle install'
# fi

### PNPM (NODEJS)
su - discourse -c 'curl -fsSL https://get.pnpm.io/install.sh' | su - discourse -c 'sh -'
su - discourse -c 'source /home/discourse/.bashrc'
source /home/discourse/.rvm/scripts/rvm
su - discourse -c 'sed -i "s/\~> 3.3/3.3.7/" /var/www/discourse/Gemfile'
chown -R discourse:discourse /var/www/discourse/Gemfile Gemfile.lock
apt-get -yqq install brotli # 是否需要????????????
# fi
su - discourse -c 'cd /var/www/discourse && bin/rails db:create'
# fi
su - discourse -c 'source /home/discourse/.rvm/scripts/rvm'
tee -a .bashrc > /dev/null <<EOT
# pnpm
export PNPM_HOME="/home/discourse/.local/share/pnpm"
case ":\$PATH:" in
  *":\$PNPM_HOME:"*) ;;
  *) export PATH="\$PNPM_HOME:\$PATH" ;;
esac
# pnpm end
EOT
which pnpm
source .bashrc
pnpm env use --global lts # 截至今天将安装 node 22
cd /var/www/discourse && pnpm i
chown -R discourse:discourse .
chown -R discourse:www-data public
exit

#################################
######### 手动操作 ################
export PATH="$PATH:/home/discourse/.rvm/bin"
rvm install "ruby-3.3.7" # 以 root 身份安装
cd /var/www/discourse
bundle install
bundle exec rake db:migrate
bundle exec rake themes:update
bundle exec rake assets:precompile
######### 手动操作结束 #################
##################################

fi
### puma.rb
#cd /var/www/discourse
cp /var/www/discourse/config/puma.rb /var/www/discourse/config/puma.rb_original

tee /var/www/discourse/config/puma.rb > /dev/null <<EOT
# frozen_string_literal: true

require "fileutils"

discourse_path = File.expand_path(File.expand_path(File.dirname(__FILE__)) + "/../")

enable_logstash_logger = ENV["ENABLE_LOGSTASH_LOGGER"] == "1"
puma_stderr_path = "#{discourse_path}/log/puma.stderr.log"
puma_stdout_path = "#{discourse_path}/log/puma.stdout.log"

# 如果启用则加载 logstash 日志记录器
if enable_logstash_logger
  require_relative "../lib/discourse_logstash_logger"
  FileUtils.touch(puma_stderr_path) if !File.exist?(puma_stderr_path)
  # 注意:你可能需要调整 Puma 的日志记录器初始化
  log_formatter = proc do |severity, time, progname, msg|
    event = {
      "@timestamp" => Time.now.utc,
      "message" => msg,
      "severity" => severity,
      "type" => "puma"
    }
    "#{event.to_json}\n"
  end
else
  stdout_redirect puma_stdout_path, puma_stderr_path, true
end

# 工作进程(进程)数量
workers ENV.fetch("PUMA_WORKERS", 8).to_i

# 设置目录
directory discourse_path

# 绑定到指定地址和端口
bind ENV.fetch("PUMA_BIND", "tcp://#{ENV['PUMA_BIND_ALL'] ? '' : '127.0.0.1:'}3000")

# PID 文件位置
FileUtils.mkdir_p("#{discourse_path}/tmp/pids")
pidfile ENV.fetch("PUMA_PID_PATH", "#{discourse_path}/tmp/pids/puma.pid")

# 状态文件 - 由 pumactl 使用
state_path "#{discourse_path}/tmp/pids/puma.state"

# 特定环境的配置
if ENV["RAILS_ENV"] == "production"
  # 生产环境超时
  worker_timeout 30
else
  # 开发环境超时
  worker_timeout ENV.fetch("PUMA_TIMEOUT", 60).to_i
end

# 预加载应用
preload_app!

# 处理工作进程启动和关闭
before_fork do
  Discourse.preload_rails!
  Discourse.before_fork

  # 监督进程检查
  supervisor_pid = ENV["PUMA_SUPERVISOR_PID"].to_i
  if supervisor_pid > 0
    Thread.new do
      loop do
        unless File.exist?("/proc/#{supervisor_pid}")
          puts "Kill self supervisor is gone"
          Process.kill "TERM", Process.pid
        end
        sleep 2
      end
    end
  end

  # Sidekiq 工作进程
  sidekiqs = ENV["PUMA_SIDEKIQS"].to_i
  if sidekiqs > 0
    puts "starting #{sidekiqs} supervised sidekiqs"

    require "demon/sidekiq"
    Demon::Sidekiq.after_fork { DiscourseEvent.trigger(:sidekiq_fork_started) }
    Demon::Sidekiq.start(sidekiqs)

    if Discourse.enable_sidekiq_logging?
      Signal.trap("USR1") do
        # 延迟 Sidekiq 日志重新打开
        sleep 1
        Demon::Sidekiq.kill("USR2")
      end
    end
  end

  # 邮件同步守护进程
  if ENV["DISCOURSE_ENABLE_EMAIL_SYNC_DEMON"] == "true"
    puts "starting up EmailSync demon"
    Demon::EmailSync.start(1)
  end

  # 插件守护进程
  DiscoursePluginRegistry.demon_processes.each do |demon_class|
    puts "starting #{demon_class.prefix} demon"
    demon_class.start(1)
  end

  # 守护进程监控线程
  Thread.new do
    loop do
      begin
        sleep 60

        if sidekiqs > 0
          Demon::Sidekiq.ensure_running
          Demon::Sidekiq.heartbeat_check
          Demon::Sidekiq.rss_memory_check
        end

        if ENV["DISCOURSE_ENABLE_EMAIL_SYNC_DEMON"] == "true"
          Demon::EmailSync.ensure_running
          Demon::EmailSync.check_email_sync_heartbeat
        end

        DiscoursePluginRegistry.demon_processes.each(&:ensure_running)
      rescue => e
        Rails.logger.warn("Error in demon processes heartbeat check: #{e}\n#{e.backtrace.join("\n")}")
      end
    end
  end

  # 关闭 Redis 连接
  Discourse.redis.close
end

on_worker_boot do
  DiscourseEvent.trigger(:web_fork_started)
  Discourse.after_fork
end

# 工作进程超时处理
worker_timeout 30

# 底层工作进程选项
threads 8, 32
EOT

### Certbot
apt install -y certbot
certbot certonly --non-interactive --agree-tos --renew-with-new-domains  --standalone --email $certbot_contact_email --rsa-key-size 4096 --http-01-port $certbot_renew_port -d $hostname
# fi

### Nginx
cp /var/www/discourse/config/nginx.sample.conf /etc/nginx/sites-enabled/discourse.conf
sed -i "s/80/$nginx_discourse_port/" /etc/nginx/sites-enabled/discourse.conf
sed -i "s/server_name _/server_name $hostname/" /etc/nginx/sites-enabled/discourse.conf
mkdir -p /var/nginx/cache
systemctl enable nginx
systemctl restart nginx
systemctl status nginx

### Systemd 服务自动启动
sudo tee /etc/systemd/system/discourse.service > /dev/null <<EOT
[Unit]
Description=Discourse with Puma Server
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=discourse
Group=discourse
WorkingDirectory=/var/www/discourse
# 在此服务运行前需要执行 `rvm 3.3.7 --default`
ExecStart=/usr/bin/bash -lc '/home/discourse/.rvm/gems/ruby-3.3.7/bin/puma -C config/puma.rb'
ExecReload=/usr/bin/bash -lc '/home/discourse/.rvm/gems/ruby-3.3.7/bin/pumactl restart'
# 下方需要这一行
Environment=RAILS_ENV=production
# 重启配置
Restart=always
RestartSec=5s

# 基本安全措施
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=read-only

[Install]
WantedBy=multi-user.target
EOT

systemctl enable discourse
systemctl start discourse

# 或手动启动
# puma -C config/puma.rb

### 设置
echo "最后的手动设置:"
echo ""
echo "进入 Admin > files 并添加所需的文件扩展名 - 视频、音频和文档
echo "强制 HTTPS:https://domain.com/admin/config/security"