From fac02ac341309786b6211fd8fcf0b5b988ad5db4 Mon Sep 17 00:00:00 2001 From: imblowsnow <74449531+imblowsnow@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:35:48 +0800 Subject: [PATCH] feat: add discourse (#1847) --- apps/discourse/README.md | 58 ++++++++++++ apps/discourse/data.yml | 19 ++++ apps/discourse/latest/data.yml | 111 +++++++++++++++++++++++ apps/discourse/latest/docker-compose.yml | 68 ++++++++++++++ apps/discourse/latest/scripts/upgrade.sh | 11 +++ apps/discourse/logo.png | Bin 0 -> 10769 bytes 6 files changed, 267 insertions(+) create mode 100644 apps/discourse/README.md create mode 100644 apps/discourse/data.yml create mode 100644 apps/discourse/latest/data.yml create mode 100644 apps/discourse/latest/docker-compose.yml create mode 100644 apps/discourse/latest/scripts/upgrade.sh create mode 100644 apps/discourse/logo.png diff --git a/apps/discourse/README.md b/apps/discourse/README.md new file mode 100644 index 00000000..3dfada7d --- /dev/null +++ b/apps/discourse/README.md @@ -0,0 +1,58 @@ +# 注意事项 +- 同一个存储卷,只会在首次初始化管理员账号 +- 使用端口访问的话,不会显示图标,修改为域名即可 + +# 配置 +## 设置语言 +/admin/site_settings/category/required + +default locale 设置为 简体中文 + +## 设置SMTP +手动编辑 docker-compose,修改 smtp相关配置 + + +## 插件安装 +```shell +cd /opt/bitnami/discourse +# 安装插件 PLUGIN_REPO_URL 替换为插件地址 +sudo RAILS_ENV=production bundle exec rake plugin:install repo=PLUGIN_REPO_URL +# 最后,预编译新资源以供插件使用: +sudo RAILS_ENV=production bundle exec rake assets:precompile +``` + +## 插件卸载 +```shell +cd /opt/bitnami/discourse/plugins +# 删除插件目录 PLUGIN-DIR 替换为插件目录 +rm -rf PLUGIN-DIR +# 最后,预编译新资源 +sudo RAILS_ENV=production bundle exec rake assets:precompile +``` + +# Discourse + +Discourse是一个现代化的讨论平台,提供了以下令人印象深刻的功能: + +## 主要功能: + +- **无缝滚动对话**:摒弃传统的分页,实现即时加载,用户可以无限滚动阅读更多内容。 +- **实时聊天**:创建社区成员之间的非正式聊天渠道,建立关系,并将聊天信息引用到主题中以持续讨论。 +- **个性化体验**:自定义侧边栏和用户偏好设置,允许社区成员根据个人需求调整体验。 +- **简洁且有上下文**:Discourse是一个简单的平面论坛,回复按页面顺序排列,通过展开帖子上下文和引用来展示完整对话。 +- **移动优先设计**:为高分辨率触摸设备设计,内置移动布局,支持iOS和Android应用。 +- **自动扩展链接**:粘贴链接即可自动扩展,提供来自Wikipedia、YouTube等数百个流行网站的额外上下文和信息。 +- **单点登录**:与现有网站的登录系统集成,实现简单、强大的单点登录。 +- **信任系统**:随着成员逐渐成为受信任的常客,他们将获得帮助维护社区的能力。 +- **社区管理**:让社区自行抑制垃圾邮件和危险内容,友好解决争议。 +- **垃圾邮件屏蔽**:内置Akismet垃圾邮件保护和启发式规则,包括新用户沙盒、用户标志阻止和标准nofollow。 +- **社交登录**:轻松添加Google、Facebook、Twitter、Discord和GitHub等常见社交登录。 +- **主题摘要**:使用摘要按钮将长主题压缩为最有趣和最受欢迎的帖子。 +- **徽章系统**:通过包含的徽章集鼓励积极的社区行为,或添加自定义徽章。 +- **表情符号**:提供可搜索的标准表情符号列表,可选择四种不同的表情符号集或定义自定义表情符号。 +- **通过电子邮件回复**:当用户不活跃在网站上时,通知将自动通过电子邮件发送,并可从任何地方、任何设备通过电子邮件回复。 +- **双因素认证**:使用免费的Android或iOS认证应用程序增强账户安全性。 +- **管理仪表板**:社区健康指标最相关和基本的指标只需点击即可访问。 +- **全面API**:屏幕上看到的任何内容,也可以通过API调用完成。 +- **100%开源**:完全开放的代码,可以完全信任地将Discourse集成到你的站点中。 +- **一键更新**:通过一键式网络更新流程,在仪表板上自动通知新版本。 diff --git a/apps/discourse/data.yml b/apps/discourse/data.yml new file mode 100644 index 00000000..d0b14293 --- /dev/null +++ b/apps/discourse/data.yml @@ -0,0 +1,19 @@ +name: Discourse +tags: + - 社区 +title: 开源社区系统 +description: 社区讨论的平台。免费、开放、简单。 +additionalProperties: + key: discourse + name: Discourse + tags: + - WebSite + shortDescZh: 社区讨论的平台。免费、开放、简单。 + shortDescEn: A platform for community discussion. Free, open, simple. + type: website + crossVersionUpdate: false + limit: 0 + recommend: 0 + website: https://www.discourse.org + github: https://github.com/discourse/discourse + document: https://hub.docker.com/r/bitnami/discourse diff --git a/apps/discourse/latest/data.yml b/apps/discourse/latest/data.yml new file mode 100644 index 00000000..c596a044 --- /dev/null +++ b/apps/discourse/latest/data.yml @@ -0,0 +1,111 @@ +additionalProperties: + formFields: + - default: "" + envKey: DISCOURSE_HOST + labelEn: HOST + labelZh: 访问域名 + required: true + type: text + - default: 40030 + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: HTTP 端口 + required: true + rule: paramPort + type: number + - default: "" + envKey: DISCOURSE_EMAIL + labelEn: email + labelZh: 管理员邮箱 + required: true + type: text + - default: "" + envKey: DISCOURSE_PASSWORD + labelEn: password(must length > 10) + labelZh: 管理员密码(长度必须>10) + required: true + type: password + rule: paramCommon + # - default: "" + # envKey: DISCOURSE_SMTP_HOST + # labelEn: SMTP HOST + # labelZh: SMTP服务器地址 + # required: true + # type: text + # - default: 0 + # envKey: DISCOURSE_SMTP_PORT_NUMBER + # labelEn: SMTP PORT + # labelZh: SMTP服务器端口 + # required: true + # type: number + # - default: "" + # envKey: DISCOURSE_SMTP_USER + # labelEn: SMTP USER + # labelZh: SMTP服务器账号 + # required: true + # type: text + # - default: "" + # envKey: DISCOURSE_SMTP_PASSWORD + # labelEn: SMTP PASSWORD + # labelZh: SMTP服务器密码 + # required: true + # type: password + # - default: "" + # envKey: DISCOURSE_SMTP_PROTOCOL + # labelEn: SMTP PROTOCOL + # labelZh: SMTP服务器加密协议 + # type: select + # values: + # - label: 无 + # value: "" + # - label: TLS + # value: "tls" + # - label: SSL + # value: "ssl" + - default: "" + envKey: PANEL_DB_HOST + key: postgresql + labelEn: postgresql Database Service + labelZh: postgresql数据库服务 + required: true + type: service + - default: de + envKey: PANEL_DB_NAME + labelEn: Database + labelZh: 数据库名 + random: true + required: true + rule: paramCommon + type: text + - default: de + envKey: PANEL_DB_USER + labelEn: User + labelZh: 数据库用户 + random: true + required: true + rule: paramCommon + type: text + - default: de + envKey: PANEL_DB_USER_PASSWORD + labelEn: Password + labelZh: 数据库用户密码 + random: true + required: true + rule: paramComplexity + type: password + - default: "" + envKey: PANEL_REDIS_HOST #docker-compose 文件中的参数 + key: redis #依赖应用的 key , 例如 mysql + labelEn: Redis Service #英文的label + labelZh: redis 服务 #中文的label + required: true #是否必填 + type: service #如果需要依赖其他应用,例如数据库,使用此 type + - default: de + envKey: PANEL_REDIS_ROOT_PASSWORD + labelEn: Password + labelZh: redis 密码 + random: true + required: true + rule: paramComplexity + type: password + diff --git a/apps/discourse/latest/docker-compose.yml b/apps/discourse/latest/docker-compose.yml new file mode 100644 index 00000000..5584746c --- /dev/null +++ b/apps/discourse/latest/docker-compose.yml @@ -0,0 +1,68 @@ +services: + discourse: + container_name: ${CONTAINER_NAME} + image: bitnami/discourse + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:3000" + volumes: + - discourse_data:/bitnami/discourse + environment: + #ALLOW_EMPTY_PASSWORD: true + DISCOURSE_HOST: ${DISCOURSE_HOST} + DISCOURSE_USERNAME: admin + DISCOURSE_PASSWORD: ${DISCOURSE_PASSWORD} + DISCOURSE_EMAIL: ${DISCOURSE_EMAIL} + DISCOURSE_DATABASE_HOST: ${PANEL_DB_HOST} + DISCOURSE_DATABASE_PORT_NUMBER: ${PANEL_DB_PORT} + DISCOURSE_DATABASE_USER: ${PANEL_DB_USER} + DISCOURSE_DATABASE_PASSWORD: ${PANEL_DB_USER_PASSWORD} + DISCOURSE_DATABASE_NAME: ${PANEL_DB_NAME} + POSTGRESQL_CLIENT_POSTGRES_USER: ${PANEL_DB_USER} + POSTGRESQL_CLIENT_POSTGRES_PASSWORD: ${PANEL_DB_USER_PASSWORD} + POSTGRESQL_CLIENT_CREATE_DATABASE_NAME: ${PANEL_DB_NAME} + POSTGRESQL_CLIENT_CREATE_DATABASE_EXTENSIONS: hstore,pg_trgm + DISCOURSE_REDIS_HOST: ${PANEL_REDIS_HOST} + DISCOURSE_REDIS_PASSWORD: ${PANEL_REDIS_ROOT_PASSWORD} + DISCOURSE_SMTP_HOST: test + DISCOURSE_SMTP_PORT_NUMBER: 0 + DISCOURSE_SMTP_USER: test + DISCOURSE_SMTP_PASSWORD: test + DISCOURSE_SMTP_PROTOCOL: + labels: + createdBy: "Apps" + sidekiq: + container_name: ${CONTAINER_NAME}-sidekiq + image: bitnami/discourse + restart: always + networks: + - 1panel-network + depends_on: + - discourse + volumes: + - discourse_data:/bitnami/discourse + command: /opt/bitnami/scripts/discourse-sidekiq/run.sh + environment: + DISCOURSE_HOST: ${DISCOURSE_HOST} + DISCOURSE_DATABASE_HOST: ${PANEL_DB_HOST} + DISCOURSE_DATABASE_PORT_NUMBER: ${PANEL_DB_PORT} + DISCOURSE_DATABASE_USER: ${PANEL_DB_USER} + DISCOURSE_DATABASE_PASSWORD: ${PANEL_DB_USER_PASSWORD} + DISCOURSE_DATABASE_NAME: ${PANEL_DB_NAME} + DISCOURSE_REDIS_HOST: ${PANEL_REDIS_HOST} + DISCOURSE_REDIS_PASSWORD: ${PANEL_REDIS_ROOT_PASSWORD} +# DISCOURSE_SMTP_HOST: test +# DISCOURSE_SMTP_PORT_NUMBER: 0 +# DISCOURSE_SMTP_USER: test +# DISCOURSE_SMTP_PASSWORD: test +# DISCOURSE_SMTP_PROTOCOL: + labels: + createdBy: "Apps" +volumes: + discourse_data: + +networks: + 1panel-network: + external: true diff --git a/apps/discourse/latest/scripts/upgrade.sh b/apps/discourse/latest/scripts/upgrade.sh new file mode 100644 index 00000000..bffdf6b1 --- /dev/null +++ b/apps/discourse/latest/scripts/upgrade.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [[ -f ./.env ]]; then + if grep -q "DISCOURSE_SKIP_BOOTSTRAP" ./.env; then + echo "DISCOURSE_SKIP_BOOTSTRAP 已存在" + else + echo 'DISCOURSE_SKIP_BOOTSTRAP="yes"' >> ./.env + fi +else + echo ".env 文件不存在" +fi diff --git a/apps/discourse/logo.png b/apps/discourse/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..25a51ab1eb2c6528249f2219302ec1b7b2d9e743 GIT binary patch literal 10769 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/Cb8HT7 zed`t?-%7Q8nmhTEBjGn82mwn!r5fKk*;7kC-TxHudvg!f{5@gs-`6AO1B&F&U>Ao$ zGz6~;fD^z06vx)YrlB}13q|HI99=$4n+m?dkA&qK|1`UN%Jje7Hxgzhd_=gD@VW`# zIr6(KU8|AL?!LC;N93t# zxUCYdzyEI^7cE~;UNj=YW_wCcdIJJ*|3goJCxSQuyvafdU}hfnhZ#{Zl}o?ZugKD` zmUwdY?DF5+@eIP}4x(g_tiC`O?|FhpG6_j-Pji2F9m zmzp!0v`3E~{q&@_m;g8ddj+FD{u3PYuhA@?!HYQSxqD}#Hzk4U{vHUJ zl7cQvK0qh7`qiiEmwHV8GOGW-Ci3TPB=Wl;qPssVws5!cYbq3r5wSs0V%`76;8g+i zr|dsUvEs|nNYp&|UljZ2H|kI*K{|ff8@mvYj^J~e{Ik8Jwas#As ziCQ;rOq@ov8}YeBJjMSB{oq$16R9u4mnU*WyeFdTmU^7+uc{U&fp?w!J|Y5q;piUV zQS$jbL5oFsPfzsu;57lb62R-NkvMD&$FViToXfy3?EW>uO7R4wsv;&^;|uR zAV=)slO_`J6HVwc`ym9py<6++uR2YH*RgsL@9FV-fu@P8GQVU z$x?egfQ!Lv0uUWQ62PDOslZKnI97H310mQIR}@oC&7{}N)<`F--SpkdcUF7uy5;EciWnb4Q4HHb*F3o&Sj(H3Cv(JS1QbDhD% z8T;lNevMAt)vMjg zrHl96;rko{!N2;mDD~|9gFG7ZYoREk^(0>iUK0SrkKECOt_EQCP;&pg`173gGwIwr z7dvFw9iLuI^aNiFUK4;U%jnT76d%OL0Yyami5az|pA^|OS^DYMiTs)PdhgSC{)53t zm`nBa$lj2i3noj>h0m{vpA%Qk81Os zWP$&bO!&?G9sCx~AmMF;@2a1*7%!G@>Eb+t=kr(9h$&s^W>KC*T=+r zpw4##f2P%%r6>4e@R|Vnt8^>%!v1G@4ZfecJIc*D*_OQMfDHE4Sm9ZZ^nY>#_`iwd|mz`Uf%f(5K$81H<|9cz#9(WZ4ZhHt}SKkhEayBgXaM;2^5#Fb}*dWPs z<-AiiqKhQX(6xt#B5249=)AmG^A0Q=CTxY1G`i0uc4BcXKZZaOjQ@&*B%A~ATTw)h zi{ZPx0zNCxYGgbAMjX(<8#TCH+YsrT*)OBp_kPkW{u3hpwY4y3TrD1V<6fEq5`g@P{zID!yQ)Hxx?|*@eu*+#PcMKuC09)5h;7_F$I{HTd>4emy?*;%i^bAQPwb__D*|B11Hv%q z7b%c)DA5WEhhOhV7=vOE(L0Q+Hc$)_6Yc2X2M1X-Unr3+T&#d$VtL?P1YgpTC@R&z zcJVopog{IjsyXc{3KbLKKWi(Iby~|E*gdl#-lI7F&T(z;J;!k&4ky6^OgKMJSWTjM zmjv=JXr6^X=Tr71htPf0YJ}YWAee0azY*PhweRDpYy6xC;)>v^dLlF^G)YftW8gIb zgj=l$>O)5j&+ z2ELQ0A+GXjwce>Gw6V|x;GrvqxPiZH-z`@0@dn(DRmIS zuDt^xzZ#1E_YFtCKaLcqarPZL8vTa6hyiy$hR~bthcRz3{S@)$jd%|ZJXz!e6v{Mv&ZJcSMc zTp_S2$_PDqo&X6z{luGV0z1%r5c$LO#CZxG0%)~RMVHTFGI1~6jW|!CLjbK1T;r=| zxwJ9!`b|Yg4$#UVI(&-dBZI+k!PUH;LWcla5xBu)F`Gx|N%AH-1kh?AA8?o(KcC-R zNOlqcpYHAcg*wnuP!(mS>*&^&Muz|{2NdUIj#=IjZt;GicOi5Lprs+=yj-c6&F9^* z+mh%IK#M}HcF5LeFJ;G0OQAymUIcHAbKXB{Rn=$BW^*4s0hb#cRe%>D;=9Zj)0TG; z@ef~UfBxl0hX6bXt%;)^vzX+iW`p@^{b097bO@k{a9Oz%l@vx-Dh9)St2~=gLs8zO z%zKGlR+e5Wm{`SRTBw@jsWc8R(PunpQ