我相信透明带来的安全,所以在这里把技术架构通通告诉大家,方便新功能的开发、发现现有的问题,如果你也想建一个这样的网站也可以作为参考。
可以通过 功能更新 CHANGE LOG 页面看到,我们在 Discourse 的基础上添加了许多功能。下面将事无巨细地交代:
单一源站:Discourse
在一台配置较高的服务器上:
pacman -S docker git
systemctl enable --now docker
cd /var/
git clone https://github.com/discourse/discourse_docker.git discourse
cd discourse
./launcher rebuild app
多个反向代理服务器
源站位于高可靠性机器上,但是网络对于中国不是最优的,所以配置反向代理,采用多台优质回国线路、但是配置普通的机器加速:
https://xjtu.app/t/topic/4330/6
配置文件
/etc/systemd/system/caddy.service
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
User=caddy
Group=caddy
Environment="ASSUME_NO_MOVING_GC_UNSAFE_RISK_IT_WITH=go1.20"
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
/etc/caddy/Caddyfile.normal
{
#auto_https off
}
xjtu.app:443, xjtu.app:443, xjtu.love:443 {
import /etc/caddy/hsts-xjtu
@default {
not path /xjtumen-custom-api/* /xjtumen-g/* /xjtumen-res/* /ip
}
reverse_proxy @default unix//var/discourse/shared/standalone/nginx.http.sock
reverse_proxy /xjtumen-custom-api/* 127.0.0.1:7010
# reverse_proxy /xjtumen-res/* https://res.xjtu.app
# handle /xjtumen-res/* {
# uri strip_prefix /xjtumen-res
# reverse_proxy /* https://res.xjtu.app
# }
import pass-ip-to-backend
route /ip {
rate_limit {remote.ip} 10r/m
}
redir /xjtumen-g /xjtumen-g/ 308
route /xjtumen-g/ {
rate_limit {remote.ip} 60r/m
}
handle /xjtumen-g/* {
uri strip_prefix /xjtumen-g
root /xjtumen-g/* /var/g/
file_server {
browse
hide .git
}
}
log {
output file /var/log/caddy/xjtu
}
}
import /etc/caddy/common
/etc/caddy/Caddyfile.rebuild
xjtu.app:443, xjtu.app:443, xjtu.love:443 {
import /etc/caddy/hsts-xjtu
respond "交大門正在更新,预计 10min 内完成。Thanks for your patience!"
#root * /usr/share/nginx/xjtu-men-maintainence/
#root * /var/www/xjtu.app
#file_server
#rewrite / index.html
log {
output file /var/log/caddy/xjtu-rebuild
}
}
import /etc/caddy/common
/etc/caddy/common
www.xjtu.app:443, www.xjtu.app:443, www.xjtu.love:443 {
import /etc/caddy/hsts-xjtu
redir https://xjtu.app{uri} 301
}
/etc/caddy/hsts-xjtu
tls /etc/caddy/ssl/xjtu.app.cer /etc/caddy/ssl/xjtu.app.key
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Xss-Protection "1; mode=block"
X-Content-Type-Options "nosniff"
Content-Security-Policy "upgrade-insecure-requests"
Referrer-Policy "strict-origin-when-cross-origin"
Cache-Control "public, max-age=15, must-revalidate"
Permissions-Policy interest-cohort=()
Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'self'; camera 'none'; encrypted-media 'none'; fullscreen 'self'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture *; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none'"
}
/etc/caddy/pass-ip-to-backend
# https://caddyserver.com/docs/modules/http
reverse_proxy /ip 127.0.0.1:7001 {
header_up X-Real-IP {http.request.header.CF-Connecting-IP}
header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
header_up REMOTE_HOST {http.request.remote.host}
header_up REQ_HOST {http.request.host}
header_up REQ_HOSTPORT {http.request.hostport}
header_up REMOTE_PORT {http.request.remote.port}
header_up DURATION_MS {http.request.duration_ms}
header_up UUID {http.request.uuid}
header_up SCEME {http.request.scheme}
header_up TLS_VER {http.request.tls.version}
header_up TLS_CIPHER {http.request.tls.cipher_suite}
header_up TLS_PROTO {http.request.tls.proto}
header_up PROTO {http.request.proto}
header_up TLS_RESUMED {http.request.tls.resumed}
header_up TLS_SERVER_NAME {http.request.tls.server_name}
header_up REQ_URI {http.request.uri}
header_up REQ_METHOD {http.request.method}
header_up REQ_ORIG_METHOD {http.request.orig_method}
header_up RES_CACHE_CONTROL {http.response.header.cache-control}
header_up RES_CONTENT_LEN {http.response.header.content-length}
header_up RES_CONTENT_TYPE {http.response.header.content-type}
header_up RES_DATE http.response.header.date}
header_up RES_CSP {http.response.header.content-security-policy}
}
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
~/go/bin/xcaddy build --with github.com/xjtu-men/caddy-ratelimit@latest --with github.com/mholt/caddy-l4 --with github.com/quic-go/quic-go@v0.37.4
systemctl enable --now caddy
插件
一些插件在私有 GitHub 仓库里,例如 discourse-multiple-hostnames
插件:
git clone https://github_pat_<redacted>@github.com/xjtu-men/discourse-multiple-hostnames.git
以下是插件列表:
-
docker_manager
允许在网页端进行管理,包括调用 docker 更新命令,注意:可能会失败,而且失败后有一定概率损坏浏览器缓存,并需要命令行 rebuild app。建议不使用热更新。 -
discourse-follow
Follow 其他用户 -
discourse-apple-add-to-homescreen
针对苹果用户的浏览器显示“添加到主屏幕”提示。Android 的提示(PWA)集成在 Core 里。 -
discourse-activity-pub
理论上允许和 Mastodon 互联,但没有成功过。 -
header-category-dropdown
允许在最上栏显示下拉菜单,暂时没用到。 -
discourse-post-voting
在话题内,对后续的回复进行 Up/Down Voting -
discourse-topic-voting
发表投票/调查话题,允许多选/单选。 -
discourse-teambuild
类似于让門友做任务,暂时没用到 -
discourse-solved
将回答中的某个回答设为“已解决”,适合于提问帖 -
discourse-data-explorer
在网页端调用数据库查询,仅限管理员使用 -
discourse-whos-online
查看当前在线情况,仅限管理员使用 -
discourse-calendar
日历支持 -
discourse-math
支持 Latex 数学公式,单行/多行。可选后端:MathJAX / KaTex -
discourse-reactions
用更多 emoji 对话题作出回应,默认只有 -
discourse-ai
众多 AI 功能 -
discourse-perspective-api
调用 Google Perspective API 分析帖文是否违反社区规范 -
discourse-github
和 GitHub 联动,例如 issues 和 pull request -
discourse-user-notes
用于 mod 对用户进行备注 -
discourse-encrypt
端到端聊天加密 -
discourse-automation
执行自动化任务,当前尚未使用,后续准备用来更新 关于 西安交大导师信息 类 的 https://xjtu.app/tag/empty 标签 -
discourse-templates
允许设置话题的模板 -
discourse-assign
允许将话题分配给某些人,适合于问答场景。 -
discourse-ai-topic-summary
使用 AI 对长话题进行总结 -
discourse-user-network-vis
可视化社交关系 -
discourse-nationalflags
国旗扩展 -
discourse-telegram-notifications
同步到 Telegram 消息 -
discourse-word-cloud
字数统计 -
discourse-chatbot
主页上悬浮的 AIBot 和可以私聊/私信的 AIBot -
discourse-layouts
自定义页面布局 -
discourse-locations
支持定位功能,目前想不到应用场景,暂未启用 -
discourse-topic-list-previews
启用 Discourse 默认的一行行帖子的用户界面,实现类似于小红书之类软件的瀑布流效果(列数随浏览器宽度自动调整,移动端只有一列)。但由于全屏幕帖子数量少,以及未知原因造成的偶尔出现从主题返回主页慢的问题,因而只在 redditish 和 Material Design 这两个主题启用(FKBPro 自带主题内容预览功能,也可一试),可以在用户偏好里设置。
-
discourse-multiple-hostnames
允许 Discourse 用多域名访问(注意app.yml
里只能配置一个域名,它将作为 canonical name 用户)搜索引擎检索,因而可以看成是反向代理域名白名单列表
-
discourse-anonymous-post
允许匿名回复/发贴,无需登录即可使用,只需点击右上的回复按钮。 -
discourse-autobot
从其他消息源自动拉取更新并发帖。 -
discourse-shadowban
残忍的 Shadowban,仅发帖人可见自己的帖子,其他人/未登录者看不到,仅用于短时间应对极端刷屏用户。 -
retort
自选 eomji 回应回复,来自上交维护的 ShuiyuanSJTU/retort,fork 自早已 EOF 的 gdpelican/retort。
服务器状态
root@archlinuxDO ~ # df -h
Filesystem Size Used Avail Use% Mounted on
dev 978M 0 978M 0% /dev
run 987M 676K 986M 1% /run
/dev/vda1 59G 44G 13G 78% /
tmpfs 987M 1.1M 985M 1% /dev/shm
tmpfs 987M 4.8M 982M 1% /tmp
overlay 59G 44G 13G 78% /var/lib/docker/overlay2/cfceca63649b674ce9a0706ebb64ecbd6b58389a73f3981226ff9387c04be342/merged
root@archlinuxDO ~ # free -h
total used free shared buff/cache available
Mem: 1.9Gi 1.7Gi 127Mi 35Mi 303Mi 220Mi
Swap: 4.0Gi 2.4Gi 1.6Gi
root@archlinuxDO ~ # uptime
21:57:44 up 3 days, 7:32, 2 users, load average: 0.34, 0.45, 0.39
Theme (Component)
Theme
不同的主题不仅颜色、界面不同,而且启用的 Theme Component 不同,如果你不喜欢某种功能,可以尝试不同的主题。在不同的设备上也可以选择不同的主题。
- DiscourseDefault
默认和推荐主题,主题的话题列表是单列。
无话题内容预览。
有显示额外信息的侧边栏,如话题类别,热门 tag,常用链接等。另外 Material Design 也有该侧边栏。如果不喜欢可以换用其他主题。移动设备上没有侧边栏。
-
FKB Pro Theme
采用类似 Win11 的圆角设计,有话题图文预览。无侧边栏。 -
graceful
也有柔和的边角设计,但不建议移动端使用,因为边缘间隙比较大。无侧边栏。 -
Material Design Theme
Material Design 风格,移动端倒是感觉不到,有话题图文预览,采用砖块/窗口式帖子列表,可自动根据页面缩放自动调节列数,有侧边栏。 -
redditish
Reddit 风格,有话题图文预览。侧边栏仅显示最近的话题。
Theme Component
被移除的 TC
https://github.com/discourse/discourse-category-badge-styles.git
https://github.com/VaperinaDEV/custom-embedded-replies.git
https://github.com/discourse/discourse-colorful-categories.git
https://github.com/xjtu-men/discourse-gray-filter
https://github.com/xjtu-men/discourse-hide-ip-info.git
https://github.com/Lhcfl/discourse-larger-long-image.git
https://github.com/fokx/discourse-marquee.git
https://github.com/xjtumen/discourse-matomo-analytics
https://github.com/literatecomputing/discourse-qrcode-wrap-theme-component.git
https://github.com/discourse/discourse-thin-header-theme-component.git
https://github.com/discourse/Discourse-Tiles-image-gallery.git
https://github.com/Arkshine/discourse-chat-sidebar.git
https://github.com/VaperinaDEV/discourse-stickers.git
被禁用的 TC
https://github.com/discourse/discourse-category-icons.git
https://github.com/xjtumen/discourse-custom-header-links.git
https://github.com/awesomerobot/discourse-button-styles.git
https://github.com/Arkshine/discourse-fireworks/tree/mobile-support-and-settings
https://github.com/discourse/discourse-gated-topics-in-category.git
https://github.com/discourse/discourse-icon-header-links.git
所有 TC
-
Anonymous Post
和插件配合实现不登录发帖/回复。 -
Colorful categories
根据不同的类别改变界面的颜色,不改变顶栏颜色,因为屏幕对比度太高、有长条色块容易晃眼睛。 -
Custom Header Links
在顶栏插入链接,如评课社区
和学习资料
。 -
Icon Header Links
顶栏添加图标超链接 -
Dark-Light Toggle
允许手动切换亮暗色主题,一般是不需要的,可以自动识别浏览器的亮暗状态。 -
DiscoTOC
自动添加目录,无需任何人工操作 -
discourse-buttons
定制按钮颜色和形状如圆角 -
Discourse Clickable Topic
话题的整个区域都可点击,不仅仅是标题文字 -
discourse-gifs
编辑器 GIPHY / Tenor 表情包 GIF -
discourse-right-sidebar-blocks
侧边栏 -
PDF previews
页面内嵌入 pdf,无需下载,无需新标签页打开 -
Play G Widget
game widget -
QR Code Inserter
编辑器插入 QR Code(quick-response code,二维码),可以是到任意 URL,或者是本当前页面(url 参数为空时) -
Showcased Categories
侧边栏展示两个类别 -
Table Builder
编辑器插入表格 -
Tag Cloud
可视化所有的 tag -
Text Highlighter and Coloring Composer Button (BBCODE)
编辑器支持 BBCode,以及快速高亮文本 -
Tiles - Gallery Component
最优地放置多图片 -
Topic Excerpts
显示文本预览 -
Topic Thumbnails
显示图片预览 -
Topic List Previews
图文预览 -
Topic List Item Click Animation
话题点击动画 -
xjtu.men custom theme-component
用于自定义一些 css 和 js,例如允许复制代码块内容,代码块支持 Intel One Mono 字体,调整顶栏项目的顺序 -
Brand Header Theme Component
在当前的顶栏上面在加一行,类似菜单 -
Category Headers theme component
在每个类别的主页添加自定义内容 -
Discourse Docs Card Filter
允许将某些类别设置成文档的效果 -
discourse-homepage-feature-component
主页显示 featured topic -
discourse-category-icons
category icon -
discourse-tc-character-count
character count in editor -
Category Badge Styles
category badge set to none to show icon only -
discourse-header-submenus
show submenu above normal header -
discourse-gated-topics-in-category
提示未登录用户只有登录才能查看内容 -
discourse-reply-template-component
使用模板新建话题/回复/私信
https://meta.discourse.org/t/reply-template/162373
默认主题:
https://github.com/OsamaSayegh/discourse-tab-bar-theme
加载了的组件:
配色
用户有很多配色方案可以选择,默认是 OpenAI-Partial
。参考了 OpenAI Developer Forum。OpenAI 的配色十分好看,我们想要模仿,即是可以用 console 调出 :root
的color_definitions.css
,可惜没法完全一样。
OpenAI-Partial-Dark Palette
"OpenAI-Partial-Dark": {
"primary": "ffffff",
"secondary": "262727",
"tertiary": "2ec27e",
"quaternary": "b9b4ec",
"header_background": "111111",
"header_primary": "dddddd",
"highlight": "b496ff",
"danger": "f17173",
"success": "09cf09",
"love": "fa6c8d",
"selected": "183d31",
"hover": "2d2e31"
}
OpenAI-Partial Palette
"OpenAI-Partial": {
"primary": "202123",
"secondary": "ffffff",
"tertiary": "10a37f",
"quaternary": "715fde",
"header_background": "ffffff",
"header_primary": "333333",
"highlight": "482da8",
"danger": "ef4146",
"success": "009900",
"love": "fa6c8d",
"selected": "e6f3f3",
"hover": "f7f7f8"
}
在线配置
网页端可以改不少配置选项,比较重要的有:
- base font 以及针对 code blocks 使用 Intel One Mono
- 固定左边栏类别的顺序,调整移动设备上顶栏按钮的顺序和桌面/移动端可见性
- 调整“提醒注册”的 banner 对已登录用户不可见
- 中文帖名和内容的长度,用户名长度限制,保留的用户名及 regex
- 不同 Trust Level 实现的条件、允许的功能和 rate limit (incl. API key)
- max image size kb, max attachment size kb, 需要./launcher enter app 改 nginx 的配置
- AI 模型选择和服务网址
存在的问题
- Discourse docker 容器里面使用
nginx
做服务器,监听到 unix socket,再用宿主机caddy
反代,存在性能损失(unix socket 很高效,可以忽略)和不必要的内存占用 - nftables 和 docker 冲突,
没有配置防火墙已采用云服务商防火墙