From 0d7dc93a67146c90890b1fc98fab268bc93f9656 Mon Sep 17 00:00:00 2001 From: Tim Date: Sun, 28 Sep 2025 21:06:52 +0800 Subject: [PATCH 001/105] =?UTF-8?q?fix:=20=E5=88=9D=E6=AD=A5=E8=BD=AC?= =?UTF-8?q?=E7=A7=BB=E4=B8=BAdocker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 94 ++++++++++++++++++ backend/open-isle.env.example | 3 + .../src/main/resources/application.properties | 20 ++-- docker/.env.example | 20 +--- docker/docker-compose.yaml | 97 +++++++++++++++++-- frontend_nuxt/.env.dev.example | 15 +-- frontend_nuxt/.env.example | 24 +---- frontend_nuxt/.env.production.example | 16 +-- frontend_nuxt/.env.staging.example | 20 +--- .../websocket_service.env.example | 2 + 10 files changed, 215 insertions(+), 96 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..00624c513 --- /dev/null +++ b/.env.example @@ -0,0 +1,94 @@ +# === Core Service Ports === +SERVER_PORT=8080 +FRONTEND_PORT=3000 +WEBSOCKET_PORT=8082 +MYSQL_PORT=3306 +REDIS_PORT=6379 +RABBITMQ_PORT=5672 +RABBITMQ_MANAGEMENT_PORT=15672 + +# === OpenSearch Configuration === +OPENSEARCH_PORT=9200 +OPENSEARCH_METRICS_PORT=9600 +OPENSEARCH_DASHBOARDS_PORT=5601 +OPENSEARCH_ENABLED=true +OPENSEARCH_SCHEME=http +OPENSEARCH_USERNAME= +OPENSEARCH_PASSWORD= + +# === Database Configuration === +MYSQL_DATABASE=openisle +MYSQL_ROOT_PASSWORD= +MYSQL_USER= +MYSQL_PASSWORD= +MYSQL_HOST= + +# === Redis Configuration === +REDIS_HOST=redis +REDIS_DATABASE=0 + +# === RabbitMQ Configuration === +RABBITMQ_HOST=rabbitmq +RABBITMQ_USERNAME=openisle +RABBITMQ_PASSWORD=openisle_password + +# === Backend Application Secrets === +JWT_SECRET=change-me-jwt-secret +JWT_REASON_SECRET=change-me-jwt-reason-secret +JWT_RESET_SECRET=change-me-jwt-reset-secret +JWT_INVITE_SECRET=change-me-jwt-invite-secret +JWT_EXPIRATION=2592000000 +PASSWORD_STRENGTH=LOW +POST_PUBLISH_MODE=DIRECT +REGISTER_MODE=WHITELIST +UPLOAD_CHECK_TYPE=true +UPLOAD_MAX_SIZE=5242880 +AVATAR_STYLE=pixel-art-neutral +AVATAR_SIZE=128 +AVATAR_BASE_URL=https://api.dicebear.com/6.x +USER_POSTS_LIMIT=10 +USER_REPLIES_LIMIT=50 +SNIPPET_LENGTH=200 +SEARCH_INDEX_PREFIX=openisle +SEARCH_HIGHLIGHT_FRAGMENT_SIZE=200 +SEARCH_REINDEX_ON_STARTUP=true +SEARCH_REINDEX_BATCH_SIZE=500 +CAPTCHA_ENABLED=false +RECAPTCHA_SECRET_KEY= +CAPTCHA_REGISTER_ENABLED=false +CAPTCHA_LOGIN_ENABLED=false +CAPTCHA_POST_ENABLED=false +CAPTCHA_COMMENT_ENABLED=false +RESEND_API_KEY= +RESEND_FROM_EMAIL= +COS_BASE_URL=https://<你的cos>.cos.accelerate.myqcloud.com +COS_SECRET_ID= +COS_SECRET_KEY= +COS_REGION=ap-guangzhou +COS_BUCKET_NAME= +GOOGLE_CLIENT_ID= +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +DISCORD_CLIENT_ID= +DISCORD_CLIENT_SECRET= +TWITTER_CLIENT_ID= +TWITTER_CLIENT_SECRET= +TELEGRAM_BOT_TOKEN= +OPENAI_API_KEY= +OPENAI_MODEL=gpt-4o +AI_FORMAT_LIMIT=3 +WEBSITE_URL=http://localhost:3000 +WEBPUSH_PUBLIC_KEY= +WEBPUSH_PRIVATE_KEY= +LOG_LEVEL=INFO + +# === Frontend (Nuxt) === +NUXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080 +NUXT_PUBLIC_WEBSOCKET_URL=https://127.0.0.1:8082 +NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 +NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com +NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN +NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779 +NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ +NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135 + diff --git a/backend/open-isle.env.example b/backend/open-isle.env.example index a62ac877f..261c45b17 100644 --- a/backend/open-isle.env.example +++ b/backend/open-isle.env.example @@ -1,3 +1,6 @@ +# 所有环境变量已集中在仓库根目录的 .env.*.example 文件。 +# 此文件保留作参考用途,如需在 Docker 之外手动配置,可按需复制。 + # === Spring Boot === SERVER_PORT=8080 diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 479fc683c..b59065e3f 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -4,7 +4,7 @@ server.port=${SERVER_PORT:8080} # for mysql logging.level.root=${LOG_LEVEL:INFO} logging.level.com.openisle.service.CosImageUploader=DEBUG -spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost:3306/openisle} +spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:3306/${MYSQL_DATABASE} spring.datasource.username=${MYSQL_USER:root} spring.datasource.password=${MYSQL_PASSWORD:password} spring.jpa.hibernate.ddl-auto=update @@ -47,11 +47,11 @@ app.snippet-length=${SNIPPET_LENGTH:200} # OpenSearch integration app.search.enabled=${SEARCH_ENABLED:true} -app.search.host=${SEARCH_HOST:localhost} -app.search.port=${SEARCH_PORT:9200} -app.search.scheme=${SEARCH_SCHEME:http} -app.search.username=${SEARCH_USERNAME:} -app.search.password=${SEARCH_PASSWORD:} +app.search.host=${OPENSEARCH_HOST:localhost} +app.search.port=${OPENSEARCH_PORT:9200} +app.search.scheme=${OPENSEARCH_SCHEME:http} +app.search.username=${OPENSEARCH_USERNAME:} +app.search.password=${OPENSEARCH_PASSWORD:} app.search.index-prefix=${SEARCH_INDEX_PREFIX:openisle} app.search.highlight-fragment-size=${SEARCH_HIGHLIGHT_FRAGMENT_SIZE:${SNIPPET_LENGTH:200}} app.search.reindex-on-startup=${SEARCH_REINDEX_ON_STARTUP:true} @@ -81,15 +81,15 @@ cos.bucket-name=${COS_BUCKET_NAME:} # your image upload services: ... # Google OAuth configuration -google.client-id=${GOOGLE_CLIENT_ID:} +google.client-id=${NUXT_PUBLIC_GOOGLE_CLIENT_ID:} # GitHub OAuth configuration -github.client-id=${GITHUB_CLIENT_ID:} +github.client-id=${NUXT_PUBLIC_GITHUB_CLIENT_ID:} github.client-secret=${GITHUB_CLIENT_SECRET:} # Discord OAuth configuration -discord.client-id=${DISCORD_CLIENT_ID:} +discord.client-id=${NUXT_PUBLIC_DISCORD_CLIENT_ID:} discord.client-secret=${DISCORD_CLIENT_SECRET:} # Twitter OAuth configuration -twitter.client-id=${TWITTER_CLIENT_ID:} +twitter.client-id=${NUXT_PUBLIC_TWITTER_CLIENT_ID:} twitter.client-secret=${TWITTER_CLIENT_SECRET:} # Telegram login configuration telegram.bot-token=${TELEGRAM_BOT_TOKEN:} diff --git a/docker/.env.example b/docker/.env.example index 0ad80a93c..a798793ea 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -1,16 +1,4 @@ -# 前端访问端口 -SERVER_PORT=8080 - -# OpenSearch 配置 -OPENSEARCH_PORT=9200 -OPENSEARCH_METRICS_PORT=9600 -OPENSEARCH_DASHBOARDS_PORT=5601 - -# MySQL 配置 -MYSQL_ROOT_PASSWORD=toor - -# 会覆盖 `open-isle.env` -MYSQL_PORT=3306 -MYSQL_DATABASE=openisle -MYSQL_USER=<数据库用户名> -MYSQL_PASSWORD=<数据库密码> +# 已迁移到仓库根目录的 .env.*.example 文件。 +# 请复制对应环境的示例文件到项目根目录,例如: +# cp ../.env.dev.example ../.env +# docker-compose 将自动读取 ../.env。 diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 50a60557d..58c0e1150 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -5,16 +5,20 @@ services: container_name: openisle-mysql restart: always env_file: - - ../backend/open-isle.env - - ./.env + - ../.env + environment: + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} ports: - - "${MYSQL_PORT}:3306" + - "${MYSQL_PORT:-3306}:3306" volumes: - mysql-data:/var/lib/mysql - ../backend/src/main/resources/db/init:/docker-entrypoint-initdb.d networks: - openisle-network - + # OpenSearch Service opensearch: build: @@ -52,6 +56,35 @@ services: - opensearch restart: unless-stopped + rabbitmq: + image: rabbitmq:3.13-management + container_name: openisle-rabbitmq + restart: unless-stopped + env_file: + - ../.env + environment: + RABBITMQ_DEFAULT_USER: ${RABBITMQ_USERNAME} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD} + ports: + - "${RABBITMQ_PORT:-5672}:5672" + - "${RABBITMQ_MANAGEMENT_PORT:-15672}:15672" + volumes: + - rabbitmq-data:/var/lib/rabbitmq + networks: + - openisle-network + + redis: + image: redis:7 + container_name: openisle-redis + restart: unless-stopped + env_file: + - ../.env + ports: + - "${REDIS_PORT:-6379}:6379" + volumes: + - redis-data:/data + networks: + - openisle-network # Java spring boot service springboot: @@ -59,21 +92,63 @@ services: container_name: openisle-springboot working_dir: /app env_file: - - ../backend/open-isle.env - - ./.env - environment: - - MYSQL_URL=jdbc:mysql://mysql:${MYSQL_PORT}/${MYSQL_DATABASE}?useUnicode=yes&characterEncoding=UTF-8&useInformationSchema=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true + - ../.env ports: - - "${SERVER_PORT}:8080" + - "${SERVER_PORT:-8080}:${SERVER_PORT:-8080}" volumes: - ../backend:/app - maven-repo:/root/.m2 depends_on: - mysql + - redis + - rabbitmq command: mvn clean spring-boot:run -Dmaven.test.skip=true networks: - openisle-network + websocket-service: + image: maven:3.9-eclipse-temurin-17 + container_name: openisle-websocket + working_dir: /app + env_file: + - ../.env + environment: + SERVER_PORT: ${WEBSOCKET_PORT} + RABBITMQ_HOST: ${RABBITMQ_HOST} + RABBITMQ_PORT: ${RABBITMQ_PORT} + RABBITMQ_USERNAME: ${RABBITMQ_USERNAME} + RABBITMQ_PASSWORD: ${RABBITMQ_PASSWORD} + JWT_SECRET: ${JWT_SECRET} + WEBSITE_URL: ${WEBSITE_URL} + ports: + - "${WEBSOCKET_PORT:-8082}:${WEBSOCKET_PORT:-8082}" + volumes: + - ../websocket_service:/app + - websocket-maven-repo:/root/.m2 + depends_on: + - rabbitmq + command: mvn clean spring-boot:run -Dmaven.test.skip=true + networks: + - openisle-network + + frontend: + image: node:20 + container_name: openisle-frontend + working_dir: /app + env_file: + - ../.env + command: sh -c "npm install && npm run dev" + volumes: + - ../frontend_nuxt:/app + - frontend-node-modules:/app/node_modules + ports: + - "${FRONTEND_PORT:-3000}:3000" + depends_on: + - springboot + - websocket-service + networks: + - openisle-network + networks: openisle-network: driver: bridge @@ -81,3 +156,7 @@ networks: volumes: mysql-data: maven-repo: + redis-data: + rabbitmq-data: + websocket-maven-repo: + frontend-node-modules: diff --git a/frontend_nuxt/.env.dev.example b/frontend_nuxt/.env.dev.example index bb0ee2635..9a41ba7af 100644 --- a/frontend_nuxt/.env.dev.example +++ b/frontend_nuxt/.env.dev.example @@ -1,12 +1,3 @@ -; 本地部署后端 -NUXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080 -NUXT_PUBLIC_WEBSOCKET_URL=https://127.0.0.1:8082 -NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 - -NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com -# NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ -; 本地 -NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN -NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779 -NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ -NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135 +# 环境变量已统一迁移至仓库根目录的 .env.*.example 文件。 +# 如需在本地运行 Nuxt,请复制对应的示例文件到项目根目录: +# cp ../.env.dev.example ../.env diff --git a/frontend_nuxt/.env.example b/frontend_nuxt/.env.example index dadb36387..bcbdb7bf6 100644 --- a/frontend_nuxt/.env.example +++ b/frontend_nuxt/.env.example @@ -1,19 +1,5 @@ -; 本地部署后端 -; NUXT_PUBLIC_API_BASE_URL=https://127.0.0.1:8081 -; 预发环境后端 -; NUXT_PUBLIC_API_BASE_URL=https://staging.open-isle.com -; 生产环境后端 -NUXT_PUBLIC_API_BASE_URL=https://open-isle.com - -; 生产环境ws后端 -NUXT_PUBLIC_WEBSOCKET_URL=https://open-isle.com/websocket - -; 预发环境 -; NUXT_PUBLIC_WEBSITE_BASE_URL=https://staging.open-isle.com -; 正式环境/生产环境 -NUXT_PUBLIC_WEBSITE_BASE_URL=https://open-isle.com -NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com -NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ -NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779 -NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ -NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135 +# 环境变量已统一迁移至仓库根目录的 .env.*.example 文件。 +# 根据环境选择对应文件复制至项目根目录: +# cp ../.env.dev.example ../.env +# cp ../.env.staging.example ../.env +# cp ../.env.production.example ../.env diff --git a/frontend_nuxt/.env.production.example b/frontend_nuxt/.env.production.example index a1bff6842..ee3573dd6 100644 --- a/frontend_nuxt/.env.production.example +++ b/frontend_nuxt/.env.production.example @@ -1,13 +1,3 @@ - -; 生产环境后端 -NUXT_PUBLIC_API_BASE_URL=https://www.open-isle.com -; 正式环境/生产环境 -NUXT_PUBLIC_WEBSITE_BASE_URL=https://www.open-isle.com -; 生产环境ws后端 -NUXT_PUBLIC_WEBSOCKET_URL=https://www.open-isle.com/websocket - -NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com -NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ -NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779 -NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ -NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135 +# 环境变量已统一迁移至仓库根目录的 .env.*.example 文件。 +# 如需配置生产环境,请复制并修改对应示例文件: +# cp ../.env.production.example ../.env diff --git a/frontend_nuxt/.env.staging.example b/frontend_nuxt/.env.staging.example index 0cc8ef140..dde477e44 100644 --- a/frontend_nuxt/.env.staging.example +++ b/frontend_nuxt/.env.staging.example @@ -1,17 +1,3 @@ -; 本地部署后端 -; NUXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080 - -; 预发环境后端 -NUXT_PUBLIC_API_BASE_URL=https://staging.open-isle.com - -; 预发环境ws后端 -NUXT_PUBLIC_WEBSOCKET_URL=https://staging.open-isle.com/websocket - -; 预发环境 -NUXT_PUBLIC_WEBSITE_BASE_URL=https://staging.open-isle.com - -NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com -NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ -NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779 -NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ -NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135 +# 环境变量已统一迁移至仓库根目录的 .env.*.example 文件。 +# 如需配置预发环境,请复制并修改对应示例文件: +# cp ../.env.staging.example ../.env diff --git a/websocket_service/websocket_service.env.example b/websocket_service/websocket_service.env.example index 9f6304269..79e51ccdf 100644 --- a/websocket_service/websocket_service.env.example +++ b/websocket_service/websocket_service.env.example @@ -1,3 +1,5 @@ +# 所有环境变量已集中在仓库根目录的 .env.*.example 文件。 +# 如需在独立环境中运行,可参考以下字段: SERVER_PORT= # RabbitMQ 配置 From 39ae8c02cbf4bbcb70f437e52d30c29a4936334a Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 10:29:37 +0800 Subject: [PATCH 002/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9.env.example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 00624c513..197b29a0e 100644 --- a/.env.example +++ b/.env.example @@ -21,7 +21,7 @@ MYSQL_DATABASE=openisle MYSQL_ROOT_PASSWORD= MYSQL_USER= MYSQL_PASSWORD= -MYSQL_HOST= +MYSQL_HOST=localhost # === Redis Configuration === REDIS_HOST=redis @@ -66,12 +66,8 @@ COS_SECRET_ID= COS_SECRET_KEY= COS_REGION=ap-guangzhou COS_BUCKET_NAME= -GOOGLE_CLIENT_ID= -GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= -DISCORD_CLIENT_ID= DISCORD_CLIENT_SECRET= -TWITTER_CLIENT_ID= TWITTER_CLIENT_SECRET= TELEGRAM_BOT_TOKEN= OPENAI_API_KEY= @@ -86,9 +82,16 @@ LOG_LEVEL=INFO NUXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080 NUXT_PUBLIC_WEBSOCKET_URL=https://127.0.0.1:8082 NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 +# 线上 & 本地均可使用 NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com +# 线上 NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN +# 本地 +# NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN +# 线上 & 本地均可使用 NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779 +# 线上 & 本地均可使用 NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ +# 线上 NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135 From 208c8758682955a86419555b2eb020af1a77cccf Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 10:42:17 +0800 Subject: [PATCH 003/105] =?UTF-8?q?fix:=20=E5=8E=BB=E9=99=A4compose?= =?UTF-8?q?=E4=B8=AD=E9=87=8D=E5=A4=8D=E5=A3=B0=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 6 +++--- docker/docker-compose.yaml | 17 +---------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index 197b29a0e..038182d43 100644 --- a/.env.example +++ b/.env.example @@ -28,9 +28,9 @@ REDIS_HOST=redis REDIS_DATABASE=0 # === RabbitMQ Configuration === -RABBITMQ_HOST=rabbitmq -RABBITMQ_USERNAME=openisle -RABBITMQ_PASSWORD=openisle_password +RABBITMQ_HOST=localhost +RABBITMQ_USERNAME=guest +RABBITMQ_PASSWORD=guest # === Backend Application Secrets === JWT_SECRET=change-me-jwt-secret diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 58c0e1150..29c8b432a 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -6,11 +6,6 @@ services: restart: always env_file: - ../.env - environment: - MYSQL_DATABASE: ${MYSQL_DATABASE} - MYSQL_USER: ${MYSQL_USER} - MYSQL_PASSWORD: ${MYSQL_PASSWORD} - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} ports: - "${MYSQL_PORT:-3306}:3306" volumes: @@ -62,9 +57,6 @@ services: restart: unless-stopped env_file: - ../.env - environment: - RABBITMQ_DEFAULT_USER: ${RABBITMQ_USERNAME} - RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD} ports: - "${RABBITMQ_PORT:-5672}:5672" - "${RABBITMQ_MANAGEMENT_PORT:-15672}:15672" @@ -102,6 +94,7 @@ services: - mysql - redis - rabbitmq + - websocket-service command: mvn clean spring-boot:run -Dmaven.test.skip=true networks: - openisle-network @@ -112,14 +105,6 @@ services: working_dir: /app env_file: - ../.env - environment: - SERVER_PORT: ${WEBSOCKET_PORT} - RABBITMQ_HOST: ${RABBITMQ_HOST} - RABBITMQ_PORT: ${RABBITMQ_PORT} - RABBITMQ_USERNAME: ${RABBITMQ_USERNAME} - RABBITMQ_PASSWORD: ${RABBITMQ_PASSWORD} - JWT_SECRET: ${JWT_SECRET} - WEBSITE_URL: ${WEBSITE_URL} ports: - "${WEBSOCKET_PORT:-8082}:${WEBSOCKET_PORT:-8082}" volumes: From a12368602d7a7902af21a6dba2a724953a5f0536 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 10:52:59 +0800 Subject: [PATCH 004/105] =?UTF-8?q?fix:=20=E5=B0=9D=E8=AF=95docker?= =?UTF-8?q?=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 docker/.gitignore diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 000000000..6320cd248 --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1 @@ +data \ No newline at end of file From bb955c98bae229f3313fe002a7f07b0110a9f296 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 15:16:32 +0800 Subject: [PATCH 005/105] =?UTF-8?q?fix:=20=E5=90=8E=E5=8F=B0=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E9=93=BE=E6=8E=A5=E5=90=84=E4=B8=AA=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 2 +- .../src/main/resources/application.properties | 2 +- docker/docker-compose.yaml | 66 +++++++++++++++++-- docker/frontend-service-entrypoint.sh | 62 +++++++++++++++++ docker/frontend-service.Dockerfile | 12 ++++ docker/{DockerFile => opensearch.Dockerfile} | 0 6 files changed, 135 insertions(+), 9 deletions(-) create mode 100755 docker/frontend-service-entrypoint.sh create mode 100644 docker/frontend-service.Dockerfile rename docker/{DockerFile => opensearch.Dockerfile} (100%) diff --git a/.env.example b/.env.example index 038182d43..5a2bbdcb6 100644 --- a/.env.example +++ b/.env.example @@ -28,7 +28,7 @@ REDIS_HOST=redis REDIS_DATABASE=0 # === RabbitMQ Configuration === -RABBITMQ_HOST=localhost +RABBITMQ_HOST=local RABBITMQ_USERNAME=guest RABBITMQ_PASSWORD=guest diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index b59065e3f..4362668b3 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -47,7 +47,7 @@ app.snippet-length=${SNIPPET_LENGTH:200} # OpenSearch integration app.search.enabled=${SEARCH_ENABLED:true} -app.search.host=${OPENSEARCH_HOST:localhost} +app.search.host=${OPENSEARCH_HOST:opensearch} app.search.port=${OPENSEARCH_PORT:9200} app.search.scheme=${OPENSEARCH_SCHEME:http} app.search.username=${OPENSEARCH_USERNAME:} diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 29c8b432a..178a74536 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -13,12 +13,18 @@ services: - ../backend/src/main/resources/db/init:/docker-entrypoint-initdb.d networks: - openisle-network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "-p$MYSQL_ROOT_PASSWORD"] + interval: 5s + timeout: 3s + retries: 30 + start_period: 20s # OpenSearch Service opensearch: build: context: . - dockerfile: Dockerfile + dockerfile: opensearch.Dockerfile container_name: opensearch environment: - cluster.name=os-single @@ -38,6 +44,14 @@ services: - "${OPENSEARCH_PORT:-9200}:9200" - "${OPENSEARCH_METRICS_PORT:-9600}:9600" restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:9200/_cluster/health >/dev/null"] + interval: 10s + timeout: 5s + retries: 30 + start_period: 60s + networks: + - openisle-network dashboards: image: opensearchproject/opensearch-dashboards:3.0.0 @@ -50,6 +64,8 @@ services: depends_on: - opensearch restart: unless-stopped + networks: + - openisle-network rabbitmq: image: rabbitmq:3.13-management @@ -85,16 +101,26 @@ services: working_dir: /app env_file: - ../.env + environment: + - MYSQL_HOST=mysql + - OPENSEARCH_HOST=opensearch + - RABBITMQ_HOST=rabbitmq ports: - "${SERVER_PORT:-8080}:${SERVER_PORT:-8080}" volumes: - ../backend:/app - maven-repo:/root/.m2 depends_on: - - mysql - - redis - - rabbitmq - - websocket-service + mysql: + condition: service_healthy + redis: + condition: service_started + rabbitmq: + condition: service_started + websocket-service: + condition: service_started + opensearch: + condition: service_healthy command: mvn clean spring-boot:run -Dmaven.test.skip=true networks: - openisle-network @@ -116,9 +142,9 @@ services: networks: - openisle-network - frontend: + frontend_dev: image: node:20 - container_name: openisle-frontend + container_name: openisle-frontend-dev working_dir: /app env_file: - ../.env @@ -133,6 +159,30 @@ services: - websocket-service networks: - openisle-network + profiles: + - dev + + frontend_service: + build: + context: .. + dockerfile: frontend-service.Dockerfile + container_name: openisle-frontend-service + working_dir: /app + env_file: + - ../.env + volumes: + - ../frontend_nuxt:/app + - frontend-service-node-modules:/app/node_modules + - frontend-static:/var/www/openisle + ports: + - "${FRONTEND_SERVICE_PORT:-3001}:3000" + depends_on: + - springboot + - websocket-service + networks: + - openisle-network + profiles: + - service networks: openisle-network: @@ -145,3 +195,5 @@ volumes: rabbitmq-data: websocket-maven-repo: frontend-node-modules: + frontend-service-node-modules: + frontend-static: diff --git a/docker/frontend-service-entrypoint.sh b/docker/frontend-service-entrypoint.sh new file mode 100755 index 000000000..6e5485647 --- /dev/null +++ b/docker/frontend-service-entrypoint.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd /app + +echo "👉 Building frontend (Nuxt SSR)..." + +if [ -f .env.production.example ] && [ ! -f .env ]; then + echo "📄 Copying .env.production.example to .env" + cp .env.production.example .env +fi + +npm ci +npm run build + +echo "🧪 Smoke-testing: nuxt generate (artifacts will NOT be used)..." + +SSR_OUTPUT_DIR=".output" +SSR_OUTPUT_BAK=".output-ssr-backup-$$" +GEN_FAIL_MSG="❌ Generate smoke test failed" + +if [ ! -d "${SSR_OUTPUT_DIR}" ]; then + echo "❌ 未发现 ${SSR_OUTPUT_DIR},请先确保 npm run build 成功执行" + exit 1 +fi + +mv "${SSR_OUTPUT_DIR}" "${SSR_OUTPUT_BAK}" + +restore_on_fail() { + if [ -d ".output" ]; then + mv .output ".output-generate-failed-$(date +%s)" || true + fi + mv "${SSR_OUTPUT_BAK}" "${SSR_OUTPUT_DIR}" +} + +trap 'restore_on_fail; echo "${GEN_FAIL_MSG}: unexpected error"; exit 1' ERR + +NUXT_TELEMETRY_DISABLED=1 \ +NITRO_PRERENDER_FAIL_ON_ERROR=1 \ +npx nuxi generate --preset static + +if [ ! -d ".output/public" ]; then + restore_on_fail + echo "${GEN_FAIL_MSG}: .output/public not found" + exit 1 +fi + +rm -rf ".output" +mv "${SSR_OUTPUT_BAK}" "${SSR_OUTPUT_DIR}" +trap - ERR +echo "✅ Generate smoke test passed." + +if [ -d ".output/public" ]; then + mkdir -p /var/www/openisle + rsync -a --delete .output/public/ /var/www/openisle/ +else + echo "❌ 未发现 .output/public;检查 nuxt.config.ts/nitro preset" + exit 1 +fi + +echo "🚀 Starting Nuxt SSR server..." +exec node .output/server/index.mjs diff --git a/docker/frontend-service.Dockerfile b/docker/frontend-service.Dockerfile new file mode 100644 index 000000000..f2846e946 --- /dev/null +++ b/docker/frontend-service.Dockerfile @@ -0,0 +1,12 @@ +FROM node:20 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends rsync \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY docker/frontend-service-entrypoint.sh /usr/local/bin/frontend-service-entrypoint.sh +RUN chmod +x /usr/local/bin/frontend-service-entrypoint.sh + +CMD ["frontend-service-entrypoint.sh"] diff --git a/docker/DockerFile b/docker/opensearch.Dockerfile similarity index 100% rename from docker/DockerFile rename to docker/opensearch.Dockerfile From 06d76438e8a4eb2e9d88fac179c2a33cf110aa28 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 16:04:14 +0800 Subject: [PATCH 006/105] =?UTF-8?q?fix:=20=E5=89=8D=E7=AB=AF=E5=88=9D?= =?UTF-8?q?=E6=AD=A5=E8=B0=83=E9=80=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 10 ++++++---- .../main/java/com/openisle/config/SecurityConfig.java | 2 ++ docker/docker-compose.yaml | 4 ---- frontend_nuxt/nuxt.config.ts | 4 +++- .../openisle/websocket/security/SecurityConfig.java | 2 ++ 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 5a2bbdcb6..887a948b2 100644 --- a/.env.example +++ b/.env.example @@ -15,20 +15,21 @@ OPENSEARCH_ENABLED=true OPENSEARCH_SCHEME=http OPENSEARCH_USERNAME= OPENSEARCH_PASSWORD= +OPENSEARCH_HOST=opensearch # === Database Configuration === MYSQL_DATABASE=openisle MYSQL_ROOT_PASSWORD= MYSQL_USER= MYSQL_PASSWORD= -MYSQL_HOST=localhost +MYSQL_HOST=mysql # === Redis Configuration === REDIS_HOST=redis REDIS_DATABASE=0 # === RabbitMQ Configuration === -RABBITMQ_HOST=local +RABBITMQ_HOST=rabbitmq RABBITMQ_USERNAME=guest RABBITMQ_PASSWORD=guest @@ -79,8 +80,9 @@ WEBPUSH_PRIVATE_KEY= LOG_LEVEL=INFO # === Frontend (Nuxt) === -NUXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080 -NUXT_PUBLIC_WEBSOCKET_URL=https://127.0.0.1:8082 +NUXT_PUBLIC_API_BASE_URL=http://springboot:8080 +NUXT_PUBLIC_API_BASE_URL_SSR=http://localhost:8080 +NUXT_PUBLIC_WEBSOCKET_URL=http://websocket_service:8082 NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 # 线上 & 本地均可使用 NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com diff --git a/backend/src/main/java/com/openisle/config/SecurityConfig.java b/backend/src/main/java/com/openisle/config/SecurityConfig.java index 4bd8e735f..b2d0da85e 100644 --- a/backend/src/main/java/com/openisle/config/SecurityConfig.java +++ b/backend/src/main/java/com/openisle/config/SecurityConfig.java @@ -97,6 +97,8 @@ public class SecurityConfig { "http://localhost:8081", "http://localhost:8082", "http://localhost:3000", + "http://frontend_dev:3000", + "http://frontend_service:3000", "http://localhost:3001", "http://localhost", "http://30.211.97.238:3000", diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 178a74536..2f82594ee 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -101,10 +101,6 @@ services: working_dir: /app env_file: - ../.env - environment: - - MYSQL_HOST=mysql - - OPENSEARCH_HOST=opensearch - - RABBITMQ_HOST=rabbitmq ports: - "${SERVER_PORT:-8080}:${SERVER_PORT:-8080}" volumes: diff --git a/frontend_nuxt/nuxt.config.ts b/frontend_nuxt/nuxt.config.ts index 0c5bea8ac..b2c7784b7 100644 --- a/frontend_nuxt/nuxt.config.ts +++ b/frontend_nuxt/nuxt.config.ts @@ -9,7 +9,9 @@ export default defineNuxtConfig({ modules: ['@nuxt/image'], runtimeConfig: { public: { - apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL || '', + apiBaseUrl: process.server + ? process.env.NUXT_PUBLIC_API_BASE_URL_SSR + : process.env.NUXT_PUBLIC_API_BASE_URL, websocketUrl: process.env.NUXT_PUBLIC_WEBSOCKET_URL || '', websiteBaseUrl: process.env.NUXT_PUBLIC_WEBSITE_BASE_URL || '', googleClientId: process.env.NUXT_PUBLIC_GOOGLE_CLIENT_ID || '', diff --git a/websocket_service/src/main/java/com/openisle/websocket/security/SecurityConfig.java b/websocket_service/src/main/java/com/openisle/websocket/security/SecurityConfig.java index 06b924bd8..6d6da7faa 100644 --- a/websocket_service/src/main/java/com/openisle/websocket/security/SecurityConfig.java +++ b/websocket_service/src/main/java/com/openisle/websocket/security/SecurityConfig.java @@ -37,6 +37,8 @@ public class SecurityConfig { "http://localhost:8081", "http://localhost:8082", "http://localhost:3000", + "http://frontend_dev:3000", + "http://frontend_service:3000", "http://localhost:3001", "http://localhost", "http://30.211.97.238:3000", From 4080f60f60d412a36289b906d4d13ffb59dcee7e Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 16:46:25 +0800 Subject: [PATCH 007/105] =?UTF-8?q?fix:=20rabbitmq=20=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 4 +- docker/docker-compose.yaml | 57 +++++++++++++++++++++++----- docker/rabbitmq/conf/enabled_plugins | 1 + docker/rabbitmq/conf/rabbitmq.conf | 6 +++ docker/rabbitmq/definitions.json | 31 +++++++++++++++ 5 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 docker/rabbitmq/conf/enabled_plugins create mode 100644 docker/rabbitmq/conf/rabbitmq.conf create mode 100644 docker/rabbitmq/definitions.json diff --git a/.env.example b/.env.example index 887a948b2..b89b03aff 100644 --- a/.env.example +++ b/.env.example @@ -30,8 +30,8 @@ REDIS_DATABASE=0 # === RabbitMQ Configuration === RABBITMQ_HOST=rabbitmq -RABBITMQ_USERNAME=guest -RABBITMQ_PASSWORD=guest +RABBITMQ_USERNAME=nagisa +RABBITMQ_PASSWORD=nagisa # === Backend Application Secrets === JWT_SECRET=change-me-jwt-secret diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 2f82594ee..029c654e4 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -71,16 +71,26 @@ services: image: rabbitmq:3.13-management container_name: openisle-rabbitmq restart: unless-stopped - env_file: - - ../.env + environment: + RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_VHOST:-/} ports: - "${RABBITMQ_PORT:-5672}:5672" - "${RABBITMQ_MANAGEMENT_PORT:-15672}:15672" volumes: - rabbitmq-data:/var/lib/rabbitmq + - ./rabbitmq/conf/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro + - ./rabbitmq/conf/enabled_plugins:/etc/rabbitmq/enabled_plugins:ro + - ./rabbitmq/definitions.json:/etc/rabbitmq/definitions.json:ro + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"] + interval: 10s + timeout: 5s + retries: 30 + start_period: 30s networks: - openisle-network + redis: image: redis:7 container_name: openisle-redis @@ -101,6 +111,9 @@ services: working_dir: /app env_file: - ../.env + environment: + SPRING_HEALTH_PATH: ${SPRING_HEALTH_PATH:-/actuator/health} + SERVER_PORT: ${SERVER_PORT:-8080} ports: - "${SERVER_PORT:-8080}:${SERVER_PORT:-8080}" volumes: @@ -117,7 +130,16 @@ services: condition: service_started opensearch: condition: service_healthy - command: mvn clean spring-boot:run -Dmaven.test.skip=true + command: > + sh -c "apt-get update && apt-get install -y --no-install-recommends curl && + mvn clean spring-boot:run -Dmaven.test.skip=true" + # healthcheck: + # test: ["CMD-SHELL", + # "curl -fsS http://127.0.0.1:${SERVER_PORT:-8080}${SPRING_HEALTH_PATH:-/actuator/health} | grep -q '\"status\"\\s*:\\s*\"UP\"'"] + # interval: 10s + # timeout: 5s + # retries: 30 + # start_period: 90s networks: - openisle-network @@ -127,14 +149,27 @@ services: working_dir: /app env_file: - ../.env + environment: + WS_HEALTH_PATH: ${WS_HEALTH_PATH:-/actuator/health} + WEBSOCKET_PORT: ${WEBSOCKET_PORT:-8082} ports: - "${WEBSOCKET_PORT:-8082}:${WEBSOCKET_PORT:-8082}" volumes: - ../websocket_service:/app - websocket-maven-repo:/root/.m2 depends_on: - - rabbitmq - command: mvn clean spring-boot:run -Dmaven.test.skip=true + rabbitmq: + condition: service_healthy + command: > + sh -c "apt-get update && apt-get install -y --no-install-recommends curl && + mvn clean spring-boot:run -Dmaven.test.skip=true" + # healthcheck: + # test: ["CMD-SHELL", + # "curl -fsS http://127.0.0.1:${WEBSOCKET_PORT:-8082}${WS_HEALTH_PATH:-/actuator/health} | grep -q '\"status\"\\s*:\\s*\"UP\"'"] + # interval: 10s + # timeout: 5s + # retries: 30 + # start_period: 90s networks: - openisle-network @@ -151,8 +186,10 @@ services: ports: - "${FRONTEND_PORT:-3000}:3000" depends_on: - - springboot - - websocket-service + springboot: + condition: service_started + websocket-service: + condition: service_started networks: - openisle-network profiles: @@ -173,8 +210,10 @@ services: ports: - "${FRONTEND_SERVICE_PORT:-3001}:3000" depends_on: - - springboot - - websocket-service + springboot: + condition: service_started + websocket-service: + condition: service_started networks: - openisle-network profiles: diff --git a/docker/rabbitmq/conf/enabled_plugins b/docker/rabbitmq/conf/enabled_plugins new file mode 100644 index 000000000..5b44fea2e --- /dev/null +++ b/docker/rabbitmq/conf/enabled_plugins @@ -0,0 +1 @@ +[rabbitmq_management, rabbitmq_prometheus]. diff --git a/docker/rabbitmq/conf/rabbitmq.conf b/docker/rabbitmq/conf/rabbitmq.conf new file mode 100644 index 000000000..7734d03e0 --- /dev/null +++ b/docker/rabbitmq/conf/rabbitmq.conf @@ -0,0 +1,6 @@ +# 管理插件加载 definitions(仅空库时生效) +management.load_definitions = /etc/rabbitmq/definitions.json + +# (可选)禁用管理老式统计采集,转 Prometheus,避免弃用告警 +management_agent.disable_metrics_collector = true +management.disable_stats = true diff --git a/docker/rabbitmq/definitions.json b/docker/rabbitmq/definitions.json new file mode 100644 index 000000000..bf8fc561f --- /dev/null +++ b/docker/rabbitmq/definitions.json @@ -0,0 +1,31 @@ +{ + "users": [ + { "name": "nagisa", "password": "nagisa", "tags": "administrator" } + ], + "vhosts": [{ "name": "/" }], + "permissions": [ + { "user": "nagisa", "vhost": "/", "configure": ".*", "write": ".*", "read": ".*" } + ], + "queues": [ + { "name": "notifications-queue", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-0", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-1", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-2", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-3", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-4", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-5", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-6", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-7", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-8", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-9", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-a", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-b", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-c", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-d", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-e", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} }, + { "name": "notifications-queue-f", "vhost": "/", "durable": true, "auto_delete": false, "arguments": {} } + ], + "exchanges": [], + "bindings": [] + } + \ No newline at end of file From 201af061e4300bdc2596f2f58947d045010f9350 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 17:55:19 +0800 Subject: [PATCH 008/105] =?UTF-8?q?fix:=20=E7=AE=80=E5=8D=95=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 4 ++-- .../java/com/openisle/websocket/security/SecurityConfig.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index b89b03aff..e69054378 100644 --- a/.env.example +++ b/.env.example @@ -80,8 +80,8 @@ WEBPUSH_PRIVATE_KEY= LOG_LEVEL=INFO # === Frontend (Nuxt) === -NUXT_PUBLIC_API_BASE_URL=http://springboot:8080 -NUXT_PUBLIC_API_BASE_URL_SSR=http://localhost:8080 +NUXT_PUBLIC_API_BASE_URL=http://localhost:8080 +NUXT_PUBLIC_API_BASE_URL_SSR=http://springboot:8080 NUXT_PUBLIC_WEBSOCKET_URL=http://websocket_service:8082 NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 # 线上 & 本地均可使用 diff --git a/websocket_service/src/main/java/com/openisle/websocket/security/SecurityConfig.java b/websocket_service/src/main/java/com/openisle/websocket/security/SecurityConfig.java index 6d6da7faa..a0083ea6f 100644 --- a/websocket_service/src/main/java/com/openisle/websocket/security/SecurityConfig.java +++ b/websocket_service/src/main/java/com/openisle/websocket/security/SecurityConfig.java @@ -37,14 +37,14 @@ public class SecurityConfig { "http://localhost:8081", "http://localhost:8082", "http://localhost:3000", - "http://frontend_dev:3000", - "http://frontend_service:3000", "http://localhost:3001", "http://localhost", "http://30.211.97.238:3000", "http://30.211.97.238", "http://192.168.7.98", "http://192.168.7.98:3000", + "http://frontend_dev:3000", + "http://frontend_service:3000", websiteUrl, websiteUrl.replace("://www.", "://") )); From e9878487e8a8bb316d0b437bcc50476c59338624 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 18:08:35 +0800 Subject: [PATCH 009/105] =?UTF-8?q?fix:=20=E5=AE=B9=E5=99=A8=E5=86=85?= =?UTF-8?q?=E6=B5=81=E9=87=8F=E8=BD=AC=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 3 +- docker/docker-compose.yaml | 58 ++++++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index e69054378..0fadfe5a0 100644 --- a/.env.example +++ b/.env.example @@ -81,8 +81,7 @@ LOG_LEVEL=INFO # === Frontend (Nuxt) === NUXT_PUBLIC_API_BASE_URL=http://localhost:8080 -NUXT_PUBLIC_API_BASE_URL_SSR=http://springboot:8080 -NUXT_PUBLIC_WEBSOCKET_URL=http://websocket_service:8082 +NUXT_PUBLIC_WEBSOCKET_URL=http://localhost:8082 NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 # 线上 & 本地均可使用 NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 029c654e4..4e02f0904 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -14,7 +14,17 @@ services: networks: - openisle-network healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "-p$MYSQL_ROOT_PASSWORD"] + test: + [ + "CMD", + "mysqladmin", + "ping", + "-h", + "127.0.0.1", + "-u", + "root", + "-p$MYSQL_ROOT_PASSWORD", + ] interval: 5s timeout: 3s retries: 30 @@ -36,7 +46,7 @@ services: - cluster.blocks.create_index=false ulimits: memlock: { soft: -1, hard: -1 } - nofile: { soft: 65536, hard: 65536 } + nofile: { soft: 65536, hard: 65536 } volumes: - ./data:/usr/share/opensearch/data - ./snapshots:/snapshots @@ -45,7 +55,11 @@ services: - "${OPENSEARCH_METRICS_PORT:-9600}:9600" restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:9200/_cluster/health >/dev/null"] + test: + [ + "CMD-SHELL", + "curl -fsS http://127.0.0.1:9200/_cluster/health >/dev/null", + ] interval: 10s timeout: 5s retries: 30 @@ -65,7 +79,7 @@ services: - opensearch restart: unless-stopped networks: - - openisle-network + - openisle-network rabbitmq: image: rabbitmq:3.13-management @@ -90,7 +104,6 @@ services: networks: - openisle-network - redis: image: redis:7 container_name: openisle-redis @@ -219,6 +232,41 @@ services: profiles: - service + loopback_8080: + image: alpine/socat + container_name: loopback-8080 + # 监听“frontend_dev 容器自身的” 127.0.0.1:8080 → 转发到 springboot:8080 + command: ["-d","-d","-ly","TCP4-LISTEN:8080,bind=127.0.0.1,reuseaddr,fork","TCP4:springboot:8080"] + depends_on: + springboot: + condition: service_started + network_mode: "service:frontend_dev" + profiles: ["dev"] + healthcheck: + test: ["CMD","sh","-c","nc -z 127.0.0.1 8080"] + interval: 5s + timeout: 3s + retries: 20 + start_period: 10s + + loopback_8082: + image: alpine/socat + container_name: loopback-8082 + # 监听 127.0.0.1:8082 → 转发到 websocket-service:8082(WS 纯 TCP 可直接过) + command: ["-d","-d","-ly","TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork","TCP4:websocket-service:8082"] + depends_on: + websocket-service: + condition: service_started + network_mode: "service:frontend_dev" + profiles: ["dev"] + healthcheck: + test: ["CMD","sh","-c","nc -z 127.0.0.1 8082"] + interval: 5s + timeout: 3s + retries: 20 + start_period: 10s + + networks: openisle-network: driver: bridge From 2b6d7c5ab9384d5bd36cc28b1b03f99c65211218 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 18:12:55 +0800 Subject: [PATCH 010/105] =?UTF-8?q?fix:=20=E6=96=B0=E5=A2=9E=E5=A4=9A?= =?UTF-8?q?=E7=A7=8Durl=E4=BE=9B=E5=BC=80=E5=8F=91=E8=80=85=E9=80=89?= =?UTF-8?q?=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 0fadfe5a0..09c6104c2 100644 --- a/.env.example +++ b/.env.example @@ -80,8 +80,15 @@ WEBPUSH_PRIVATE_KEY= LOG_LEVEL=INFO # === Frontend (Nuxt) === -NUXT_PUBLIC_API_BASE_URL=http://localhost:8080 -NUXT_PUBLIC_WEBSOCKET_URL=http://localhost:8082 + +# NUXT_PUBLIC_API_BASE_URL=http://localhost:8080 +NUXT_PUBLIC_API_BASE_URL=https://www.open-isle.com +# NUXT_PUBLIC_API_BASE_URL=https://www.staging.open-isle.com + +# NUXT_PUBLIC_WEBSOCKET_URL=http://localhost:8082 +NUXT_PUBLIC_WEBSOCKET_URL=https://www.open-isle.com +# NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com + NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 # 线上 & 本地均可使用 NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com From b2a29913aa1a561596004c943e684dfe0cc771c3 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 19:11:18 +0800 Subject: [PATCH 011/105] =?UTF-8?q?feat:=20=E7=A7=BB=E5=8A=A8=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/db/init => docker/mysql}/init_script.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {backend/src/main/resources/db/init => docker/mysql}/init_script.sql (100%) diff --git a/backend/src/main/resources/db/init/init_script.sql b/docker/mysql/init_script.sql similarity index 100% rename from backend/src/main/resources/db/init/init_script.sql rename to docker/mysql/init_script.sql From 0cf81136913b0fdc2b19204b9ec1aa82c076b09b Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 19:14:48 +0800 Subject: [PATCH 012/105] =?UTF-8?q?Revert=20"feat:=20=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=BD=8D=E7=BD=AE"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b2a29913aa1a561596004c943e684dfe0cc771c3. --- .../mysql => backend/src/main/resources/db/init}/init_script.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {docker/mysql => backend/src/main/resources/db/init}/init_script.sql (100%) diff --git a/docker/mysql/init_script.sql b/backend/src/main/resources/db/init/init_script.sql similarity index 100% rename from docker/mysql/init_script.sql rename to backend/src/main/resources/db/init/init_script.sql From 35c503eb6cb486f82af2f2110fe7a0dcc90d4676 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 19:26:02 +0800 Subject: [PATCH 013/105] =?UTF-8?q?feat:=20mysql=20=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 14 ++-- .../resources/db/init/00_init_db_and_user.sql | 17 ++++ .../src/main/resources/db/init/01_schema.sql | 57 +++++++++++++ .../main/resources/db/init/02_seed_data.sql | 36 +++++++++ .../main/resources/db/init/init_script.sql | 81 ------------------- docker/docker-compose.yaml | 29 +++++-- 6 files changed, 140 insertions(+), 94 deletions(-) create mode 100644 backend/src/main/resources/db/init/00_init_db_and_user.sql create mode 100644 backend/src/main/resources/db/init/01_schema.sql create mode 100644 backend/src/main/resources/db/init/02_seed_data.sql delete mode 100644 backend/src/main/resources/db/init/init_script.sql diff --git a/.env.example b/.env.example index 09c6104c2..d3466c33d 100644 --- a/.env.example +++ b/.env.example @@ -19,9 +19,9 @@ OPENSEARCH_HOST=opensearch # === Database Configuration === MYSQL_DATABASE=openisle -MYSQL_ROOT_PASSWORD= -MYSQL_USER= -MYSQL_PASSWORD= +MYSQL_ROOT_PASSWORD=openisle +MYSQL_USER=openisle +MYSQL_PASSWORD=openisle MYSQL_HOST=mysql # === Redis Configuration === @@ -81,12 +81,12 @@ LOG_LEVEL=INFO # === Frontend (Nuxt) === -# NUXT_PUBLIC_API_BASE_URL=http://localhost:8080 -NUXT_PUBLIC_API_BASE_URL=https://www.open-isle.com +NUXT_PUBLIC_API_BASE_URL=http://localhost:8080 +# NUXT_PUBLIC_API_BASE_URL=https://www.open-isle.com # NUXT_PUBLIC_API_BASE_URL=https://www.staging.open-isle.com -# NUXT_PUBLIC_WEBSOCKET_URL=http://localhost:8082 -NUXT_PUBLIC_WEBSOCKET_URL=https://www.open-isle.com +NUXT_PUBLIC_WEBSOCKET_URL=http://localhost:8082 +# NUXT_PUBLIC_WEBSOCKET_URL=https://www.open-isle.com # NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 diff --git a/backend/src/main/resources/db/init/00_init_db_and_user.sql b/backend/src/main/resources/db/init/00_init_db_and_user.sql new file mode 100644 index 000000000..bbdceed25 --- /dev/null +++ b/backend/src/main/resources/db/init/00_init_db_and_user.sql @@ -0,0 +1,17 @@ +-- 统一字符集 +SET NAMES utf8mb4; +SET CHARACTER SET utf8mb4; +SET collation_connection = utf8mb4_0900_ai_ci; + +-- 创建数据库(如果不存在) +CREATE DATABASE IF NOT EXISTS `openisle` + CHARACTER SET utf8mb4 + COLLATE utf8mb4_0900_ai_ci; + +-- 创建业务账号(容器会基于环境变量自动创建;此处再兜底 + 幂等) +CREATE USER IF NOT EXISTS 'openisle'@'%' IDENTIFIED BY 'openisle'; +GRANT ALL PRIVILEGES ON `openisle`.* TO 'openisle'@'%'; +FLUSH PRIVILEGES; + +-- 切换到目标库 +USE `openisle`; diff --git a/backend/src/main/resources/db/init/01_schema.sql b/backend/src/main/resources/db/init/01_schema.sql new file mode 100644 index 000000000..0844e5799 --- /dev/null +++ b/backend/src/main/resources/db/init/01_schema.sql @@ -0,0 +1,57 @@ +USE `openisle`; + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- users 表 +CREATE TABLE IF NOT EXISTS `users` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `approved` bit(1) DEFAULT NULL, + `avatar` varchar(255) DEFAULT NULL, + `created_at` datetime(6) DEFAULT NULL, + `display_medal` varchar(255) DEFAULT NULL, + `email` varchar(255) NOT NULL, + `experience` int DEFAULT NULL, + `introduction` text, + `password` varchar(255) NOT NULL, + `password_reset_code` varchar(255) DEFAULT NULL, + `point` int DEFAULT NULL, + `register_reason` text, + `role` varchar(20) DEFAULT 'USER', + `username` varchar(50) NOT NULL, + `verification_code` varchar(255) DEFAULT NULL, + `verified` bit(1) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UK_users_email` (`email`), + UNIQUE KEY `UK_users_username` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- categories 表 +CREATE TABLE IF NOT EXISTS `categories` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `description` text, + `icon` varchar(255) DEFAULT NULL, + `name` varchar(50) NOT NULL, + `small_icon` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UK_categories_name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- tags 表(依赖 users.id 外键) +CREATE TABLE IF NOT EXISTS `tags` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `approved` bit(1) DEFAULT NULL, + `created_at` datetime(6) DEFAULT NULL, + `description` text, + `icon` varchar(255) DEFAULT NULL, + `name` varchar(50) NOT NULL, + `small_icon` varchar(255) DEFAULT NULL, + `creator_id` bigint DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UK_tags_name` (`name`), + KEY `FK_tags_creator` (`creator_id`), + CONSTRAINT `FK_tags_creator` FOREIGN KEY (`creator_id`) REFERENCES `users` (`id`) + ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/backend/src/main/resources/db/init/02_seed_data.sql b/backend/src/main/resources/db/init/02_seed_data.sql new file mode 100644 index 000000000..d217be9a7 --- /dev/null +++ b/backend/src/main/resources/db/init/02_seed_data.sql @@ -0,0 +1,36 @@ +USE `openisle`; +SET FOREIGN_KEY_CHECKS = 0; + +-- 清空并灌入演示数据(幂等处理:先删再插) +DELETE FROM `tags`; +DELETE FROM `categories`; +DELETE FROM `users`; + +-- users(密码已是 bcrypt;账号:admin/user1/user2,明文 123321) +INSERT INTO `users` +(`id`,`approved`,`avatar`,`created_at`,`display_medal`,`email`,`experience`,`introduction`, + `password`,`password_reset_code`,`point`,`register_reason`,`role`,`username`,`verification_code`,`verified`) +VALUES +(1, b'1', '', '2025-09-01 16:08:17.426430', 'PIONEER', 'adminmail@openisle.com', 70, NULL, + '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 110, + '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'ADMIN', 'admin', NULL, b'1'), +(2, b'1', '', '2025-09-03 16:08:17.426430', 'PIONEER', 'usermail2@openisle.com', 70, NULL, + '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 110, + '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'USER', 'user1', NULL, b'1'), +(3, b'1', '', '2025-09-02 17:21:21.617666', 'PIONEER', 'usermail1@openisle.com', 40, NULL, + '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 40, + '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'USER', 'user2', NULL, b'1'); + +-- categories +INSERT INTO `categories` (`id`,`description`,`icon`,`name`,`small_icon`) VALUES +(1,'测试用分类1','1','测试用分类1',NULL), +(2,'测试用分类2','2','测试用分类2',NULL), +(3,'测试用分类3','3','测试用分类3',NULL); + +-- tags(此处不绑定 creator_id,避免外键顺序依赖;如需可填 1/2/3) +INSERT INTO `tags` (`id`,`approved`,`created_at`,`description`,`icon`,`name`,`small_icon`,`creator_id`) VALUES +(1,b'1','2025-09-02 10:51:56.000000','测试用标签1',NULL,'测试用标签1',NULL,NULL), +(2,b'1','2025-09-02 10:51:56.000000','测试用标签2',NULL,'测试用标签2',NULL,NULL), +(3,b'1','2025-09-02 10:51:56.000000','测试用标签3',NULL,'测试用标签3',NULL,NULL); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/backend/src/main/resources/db/init/init_script.sql b/backend/src/main/resources/db/init/init_script.sql deleted file mode 100644 index 3ea4bd2c1..000000000 --- a/backend/src/main/resources/db/init/init_script.sql +++ /dev/null @@ -1,81 +0,0 @@ --- 2025-09-02 --- 本地化开发,初始化脚本 --- 抽奖的时候奖品图片是必须的,把相关代码注释掉即可跳过check - --- 设置字符集和排序规则 -SET NAMES utf8; -SET CHARACTER SET utf8; -SET collation_connection = utf8_general_ci; - --- 创建 users 表(如果不存在) -CREATE TABLE IF NOT EXISTS `users` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `approved` bit(1) DEFAULT NULL, - `avatar` varchar(255) DEFAULT NULL, - `created_at` datetime(6) DEFAULT NULL, - `display_medal` varchar(255) DEFAULT NULL, - `email` varchar(255) NOT NULL, - `experience` int DEFAULT NULL, - `introduction` text, - `password` varchar(255) NOT NULL, - `password_reset_code` varchar(255) DEFAULT NULL, - `point` int DEFAULT NULL, - `register_reason` text, - `role` varchar(20) DEFAULT 'USER', - `username` varchar(50) NOT NULL, - `verification_code` varchar(255) DEFAULT NULL, - `verified` bit(1) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `UK_users_email` (`email`), - UNIQUE KEY `UK_users_username` (`username`) -); - --- 清空users表 -DELETE FROM `users`; --- 插入用户,两个普通用户,一个管理员 --- username:admin/user1/user2 password:123321 -INSERT INTO `users` (`id`, `approved`, `avatar`, `created_at`, `display_medal`, `email`, `experience`, `introduction`, `password`, `password_reset_code`, `point`, `register_reason`, `role`, `username`, `verification_code`, `verified`) VALUES - (1, b'1', '', '2025-09-01 16:08:17.426430', 'PIONEER', 'adminmail@openisle.com', 70, NULL, '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 110, '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'ADMIN', 'admin', NULL, b'1'), - (2, b'1', '', '2025-09-03 16:08:17.426430', 'PIONEER', 'usermail2@openisle.com', 70, NULL, '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 110, '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'USER', 'user1', NULL, b'1'), - (3, b'1', '', '2025-09-02 17:21:21.617666', 'PIONEER', 'usermail1@openisle.com', 40, NULL, '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 40, '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'USER', 'user2', NULL, b'1'); - --- 创建 tags 表(如果不存在) -CREATE TABLE IF NOT EXISTS `tags` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `approved` bit(1) DEFAULT NULL, - `created_at` datetime(6) DEFAULT NULL, - `description` text, - `icon` varchar(255) DEFAULT NULL, - `name` varchar(50) NOT NULL, - `small_icon` varchar(255) DEFAULT NULL, - `creator_id` bigint DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `UK_tags_name` (`name`), - KEY `FK_tags_creator` (`creator_id`), - CONSTRAINT `FK_tags_creator` FOREIGN KEY (`creator_id`) REFERENCES `users` (`id`) -); --- 清空tags表 -DELETE FROM `tags`; --- 插入标签,三个测试用标签 -INSERT INTO `tags` (`id`, `approved`, `created_at`, `description`, `icon`, `name`, `small_icon`, `creator_id`) VALUES - (1, b'1', '2025-09-02 10:51:56.000000', '测试用标签1', NULL, '测试用标签1', NULL, NULL), - (2, b'1', '2025-09-02 10:51:56.000000', '测试用标签2', NULL, '测试用标签2', NULL, NULL), - (3, b'1', '2025-09-02 10:51:56.000000', '测试用标签3', NULL, '测试用标签3', NULL, NULL); - --- 创建 categories 表(如果不存在) -CREATE TABLE IF NOT EXISTS `categories` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `description` text, - `icon` varchar(255) DEFAULT NULL, - `name` varchar(50) NOT NULL, - `small_icon` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `UK_categories_name` (`name`) -); --- 清空categories表 -DELETE FROM `categories`; --- 插入分类,三个测试用分类 -INSERT INTO `categories` (`id`, `description`, `icon`, `name`, `small_icon`) VALUES - (1, '测试用分类1', '1', '测试用分类1', NULL), - (2, '测试用分类2', '2', '测试用分类2', NULL), - (3, '测试用分类3', '3', '测试用分类3', NULL); \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 4e02f0904..0746dcbe6 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -11,6 +11,10 @@ services: volumes: - mysql-data:/var/lib/mysql - ../backend/src/main/resources/db/init:/docker-entrypoint-initdb.d + command: > + --character-set-server=utf8mb4 + --collation-server=utf8mb4_0900_ai_ci + --default-time-zone=+80:00 networks: - openisle-network healthcheck: @@ -236,37 +240,50 @@ services: image: alpine/socat container_name: loopback-8080 # 监听“frontend_dev 容器自身的” 127.0.0.1:8080 → 转发到 springboot:8080 - command: ["-d","-d","-ly","TCP4-LISTEN:8080,bind=127.0.0.1,reuseaddr,fork","TCP4:springboot:8080"] + command: + [ + "-d", + "-d", + "-ly", + "TCP4-LISTEN:8080,bind=127.0.0.1,reuseaddr,fork", + "TCP4:springboot:8080", + ] depends_on: springboot: condition: service_started network_mode: "service:frontend_dev" profiles: ["dev"] healthcheck: - test: ["CMD","sh","-c","nc -z 127.0.0.1 8080"] + test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 8080"] interval: 5s timeout: 3s retries: 20 start_period: 10s - + loopback_8082: image: alpine/socat container_name: loopback-8082 # 监听 127.0.0.1:8082 → 转发到 websocket-service:8082(WS 纯 TCP 可直接过) - command: ["-d","-d","-ly","TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork","TCP4:websocket-service:8082"] + command: + [ + "-d", + "-d", + "-ly", + "TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork", + "TCP4:websocket-service:8082", + ] depends_on: websocket-service: condition: service_started network_mode: "service:frontend_dev" profiles: ["dev"] healthcheck: - test: ["CMD","sh","-c","nc -z 127.0.0.1 8082"] + test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 8082"] interval: 5s timeout: 3s retries: 20 start_period: 10s - networks: openisle-network: driver: bridge From 2f509cc2d815ebcb50cb333206545ac6009a48f7 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 19:27:24 +0800 Subject: [PATCH 014/105] =?UTF-8?q?feat:=20mysql=20=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 0746dcbe6..36f2392b3 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -14,7 +14,7 @@ services: command: > --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_ai_ci - --default-time-zone=+80:00 + --default-time-zone=+08:00 networks: - openisle-network healthcheck: From 3d5cee6e6888db860903a61eaeedd5081d990ce6 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 19:41:04 +0800 Subject: [PATCH 015/105] =?UTF-8?q?feat:=20mysql=20=E4=B9=B1=E7=A0=81?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/init/00_init_db_and_user.sql | 4 --- .../src/main/resources/db/init/01_schema.sql | 5 +--- .../main/resources/db/init/02_seed_data.sql | 22 ++++------------ docker/docker-compose.yaml | 25 +++++++------------ docker/mysql/conf.d/charset.cnf | 10 ++++++++ 5 files changed, 25 insertions(+), 41 deletions(-) create mode 100644 docker/mysql/conf.d/charset.cnf diff --git a/backend/src/main/resources/db/init/00_init_db_and_user.sql b/backend/src/main/resources/db/init/00_init_db_and_user.sql index bbdceed25..d8cf198ef 100644 --- a/backend/src/main/resources/db/init/00_init_db_and_user.sql +++ b/backend/src/main/resources/db/init/00_init_db_and_user.sql @@ -1,17 +1,13 @@ --- 统一字符集 SET NAMES utf8mb4; SET CHARACTER SET utf8mb4; SET collation_connection = utf8mb4_0900_ai_ci; --- 创建数据库(如果不存在) CREATE DATABASE IF NOT EXISTS `openisle` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; --- 创建业务账号(容器会基于环境变量自动创建;此处再兜底 + 幂等) CREATE USER IF NOT EXISTS 'openisle'@'%' IDENTIFIED BY 'openisle'; GRANT ALL PRIVILEGES ON `openisle`.* TO 'openisle'@'%'; FLUSH PRIVILEGES; --- 切换到目标库 USE `openisle`; diff --git a/backend/src/main/resources/db/init/01_schema.sql b/backend/src/main/resources/db/init/01_schema.sql index 0844e5799..87e1e22da 100644 --- a/backend/src/main/resources/db/init/01_schema.sql +++ b/backend/src/main/resources/db/init/01_schema.sql @@ -1,9 +1,8 @@ USE `openisle`; - SET NAMES utf8mb4; + SET FOREIGN_KEY_CHECKS = 0; --- users 表 CREATE TABLE IF NOT EXISTS `users` ( `id` bigint NOT NULL AUTO_INCREMENT, `approved` bit(1) DEFAULT NULL, @@ -26,7 +25,6 @@ CREATE TABLE IF NOT EXISTS `users` ( UNIQUE KEY `UK_users_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; --- categories 表 CREATE TABLE IF NOT EXISTS `categories` ( `id` bigint NOT NULL AUTO_INCREMENT, `description` text, @@ -37,7 +35,6 @@ CREATE TABLE IF NOT EXISTS `categories` ( UNIQUE KEY `UK_categories_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; --- tags 表(依赖 users.id 外键) CREATE TABLE IF NOT EXISTS `tags` ( `id` bigint NOT NULL AUTO_INCREMENT, `approved` bit(1) DEFAULT NULL, diff --git a/backend/src/main/resources/db/init/02_seed_data.sql b/backend/src/main/resources/db/init/02_seed_data.sql index d217be9a7..091635304 100644 --- a/backend/src/main/resources/db/init/02_seed_data.sql +++ b/backend/src/main/resources/db/init/02_seed_data.sql @@ -1,33 +1,21 @@ USE `openisle`; +SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; --- 清空并灌入演示数据(幂等处理:先删再插) DELETE FROM `tags`; DELETE FROM `categories`; DELETE FROM `users`; --- users(密码已是 bcrypt;账号:admin/user1/user2,明文 123321) -INSERT INTO `users` -(`id`,`approved`,`avatar`,`created_at`,`display_medal`,`email`,`experience`,`introduction`, - `password`,`password_reset_code`,`point`,`register_reason`,`role`,`username`,`verification_code`,`verified`) -VALUES -(1, b'1', '', '2025-09-01 16:08:17.426430', 'PIONEER', 'adminmail@openisle.com', 70, NULL, - '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 110, - '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'ADMIN', 'admin', NULL, b'1'), -(2, b'1', '', '2025-09-03 16:08:17.426430', 'PIONEER', 'usermail2@openisle.com', 70, NULL, - '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 110, - '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'USER', 'user1', NULL, b'1'), -(3, b'1', '', '2025-09-02 17:21:21.617666', 'PIONEER', 'usermail1@openisle.com', 40, NULL, - '$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe', NULL, 40, - '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试', 'USER', 'user2', NULL, b'1'); +INSERT INTO `users` (`id`,`approved`,`avatar`,`created_at`,`display_medal`,`email`,`experience`,`introduction`,`password`,`password_reset_code`,`point`,`register_reason`,`role`,`username`,`verification_code`,`verified`) VALUES +(1,b'1','', '2025-09-01 16:08:17.426430','PIONEER','adminmail@openisle.com',70,NULL,'$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe',NULL,110,'测试测试测试……','ADMIN','admin',NULL,b'1'), +(2,b'1','', '2025-09-03 16:08:17.426430','PIONEER','usermail2@openisle.com',70,NULL,'$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe',NULL,110,'测试测试测试……','USER','user1',NULL,b'1'), +(3,b'1','', '2025-09-02 17:21:21.617666','PIONEER','usermail1@openisle.com',40,NULL,'$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe',NULL,40,'测试测试测试……','USER','user2',NULL,b'1'); --- categories INSERT INTO `categories` (`id`,`description`,`icon`,`name`,`small_icon`) VALUES (1,'测试用分类1','1','测试用分类1',NULL), (2,'测试用分类2','2','测试用分类2',NULL), (3,'测试用分类3','3','测试用分类3',NULL); --- tags(此处不绑定 creator_id,避免外键顺序依赖;如需可填 1/2/3) INSERT INTO `tags` (`id`,`approved`,`created_at`,`description`,`icon`,`name`,`small_icon`,`creator_id`) VALUES (1,b'1','2025-09-02 10:51:56.000000','测试用标签1',NULL,'测试用标签1',NULL,NULL), (2,b'1','2025-09-02 10:51:56.000000','测试用标签2',NULL,'测试用标签2',NULL,NULL), diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 36f2392b3..d762df085 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -6,34 +6,27 @@ services: restart: always env_file: - ../.env - ports: - - "${MYSQL_PORT:-3306}:3306" - volumes: - - mysql-data:/var/lib/mysql - - ../backend/src/main/resources/db/init:/docker-entrypoint-initdb.d command: > --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_ai_ci --default-time-zone=+08:00 + --skip-character-set-client-handshake + ports: + - "${MYSQL_PORT:-3306}:3306" + volumes: + - mysql-data:/var/lib/mysql + - ../backend/src/main/resources/db/init:/docker-entrypoint-initdb.d:ro + - ./mysql/conf.d:/etc/mysql/conf.d:ro networks: - openisle-network healthcheck: - test: - [ - "CMD", - "mysqladmin", - "ping", - "-h", - "127.0.0.1", - "-u", - "root", - "-p$MYSQL_ROOT_PASSWORD", - ] + test: ["CMD","mysqladmin","ping","-h","127.0.0.1","-u","root","-p$MYSQL_ROOT_PASSWORD"] interval: 5s timeout: 3s retries: 30 start_period: 20s + # OpenSearch Service opensearch: build: diff --git a/docker/mysql/conf.d/charset.cnf b/docker/mysql/conf.d/charset.cnf new file mode 100644 index 000000000..2464cce02 --- /dev/null +++ b/docker/mysql/conf.d/charset.cnf @@ -0,0 +1,10 @@ +[mysqld] +character-set-server = utf8mb4 +collation-server = utf8mb4_0900_ai_ci +skip-character-set-client-handshake + +[client] +default-character-set = utf8mb4 + +[mysql] +default-character-set = utf8mb4 From 73790d1992d9ff9675ccc6c9f8c8aa30f6727259 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 19:42:54 +0800 Subject: [PATCH 016/105] =?UTF-8?q?feat:=20healthy.=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index d762df085..195b1e15d 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -143,13 +143,6 @@ services: command: > sh -c "apt-get update && apt-get install -y --no-install-recommends curl && mvn clean spring-boot:run -Dmaven.test.skip=true" - # healthcheck: - # test: ["CMD-SHELL", - # "curl -fsS http://127.0.0.1:${SERVER_PORT:-8080}${SPRING_HEALTH_PATH:-/actuator/health} | grep -q '\"status\"\\s*:\\s*\"UP\"'"] - # interval: 10s - # timeout: 5s - # retries: 30 - # start_period: 90s networks: - openisle-network @@ -173,13 +166,6 @@ services: command: > sh -c "apt-get update && apt-get install -y --no-install-recommends curl && mvn clean spring-boot:run -Dmaven.test.skip=true" - # healthcheck: - # test: ["CMD-SHELL", - # "curl -fsS http://127.0.0.1:${WEBSOCKET_PORT:-8082}${WS_HEALTH_PATH:-/actuator/health} | grep -q '\"status\"\\s*:\\s*\"UP\"'"] - # interval: 10s - # timeout: 5s - # retries: 30 - # start_period: 90s networks: - openisle-network From 287d52df10abcd425f362ab1f582b4317931108f Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 19:47:55 +0800 Subject: [PATCH 017/105] =?UTF-8?q?feat:=20healthy.=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 195b1e15d..d5ee840e8 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -137,12 +137,22 @@ services: rabbitmq: condition: service_started websocket-service: - condition: service_started + condition: service_healthy opensearch: condition: service_healthy command: > sh -c "apt-get update && apt-get install -y --no-install-recommends curl && mvn clean spring-boot:run -Dmaven.test.skip=true" + healthcheck: + test: + [ + "CMD-SHELL", + "curl -fsS http://127.0.0.1:${SERVER_PORT:-8080}${SPRING_HEALTH_PATH:-/actuator/health} || exit 1", + ] + interval: 10s + timeout: 5s + retries: 30 + start_period: 60s networks: - openisle-network @@ -166,6 +176,16 @@ services: command: > sh -c "apt-get update && apt-get install -y --no-install-recommends curl && mvn clean spring-boot:run -Dmaven.test.skip=true" + healthcheck: + test: + [ + "CMD-SHELL", + "curl -fsS http://127.0.0.1:${WEBSOCKET_PORT:-8082}${WS_HEALTH_PATH:-/actuator/health} || exit 1", + ] + interval: 10s + timeout: 5s + retries: 30 + start_period: 60s networks: - openisle-network @@ -183,9 +203,9 @@ services: - "${FRONTEND_PORT:-3000}:3000" depends_on: springboot: - condition: service_started + condition: service_healthy websocket-service: - condition: service_started + condition: service_healthy networks: - openisle-network profiles: @@ -207,9 +227,9 @@ services: - "${FRONTEND_SERVICE_PORT:-3001}:3000" depends_on: springboot: - condition: service_started + condition: service_healthy websocket-service: - condition: service_started + condition: service_healthy networks: - openisle-network profiles: @@ -229,7 +249,7 @@ services: ] depends_on: springboot: - condition: service_started + condition: service_healthy network_mode: "service:frontend_dev" profiles: ["dev"] healthcheck: @@ -253,7 +273,7 @@ services: ] depends_on: websocket-service: - condition: service_started + condition: service_healthy network_mode: "service:frontend_dev" profiles: ["dev"] healthcheck: From fffd335ebb7d603c2e6e92b850e1feb92b047f87 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 29 Sep 2025 19:54:37 +0800 Subject: [PATCH 018/105] =?UTF-8?q?fix:=20=E4=B8=A4=E4=B8=AAspringboot?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8E=A2=E6=B4=BB=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application.properties | 3 +++ websocket_service/src/main/resources/application.properties | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 4362668b3..81032349b 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -129,3 +129,6 @@ springdoc.info.description=OpenIsle Open API Documentation springdoc.info.version=0.0.1 springdoc.info.scheme=Bearer springdoc.info.header=Authorization + +management.endpoints.web.exposure.include=health,info +management.endpoint.health.probes.enabled=true \ No newline at end of file diff --git a/websocket_service/src/main/resources/application.properties b/websocket_service/src/main/resources/application.properties index 0bce91d31..87659cc08 100644 --- a/websocket_service/src/main/resources/application.properties +++ b/websocket_service/src/main/resources/application.properties @@ -19,4 +19,7 @@ logging.level.org.springframework.messaging=${MESSAGING_LOG_LEVEL:DEBUG} logging.level.org.springframework.web.socket=${WEBSOCKET_LOG_LEVEL:DEBUG} # 网站 URL 配置 -app.website-url=${WEBSITE_URL:https://www.open-isle.com} \ No newline at end of file +app.website-url=${WEBSITE_URL:https://www.open-isle.com} + +management.endpoints.web.exposure.include=health,info +management.endpoint.health.probes.enabled=true \ No newline at end of file From f872a3241007f68510cb32f914dbaeae79509c94 Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Mon, 29 Sep 2025 21:47:12 +0900 Subject: [PATCH 019/105] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=201.=20=E6=96=87=E5=AD=97=E5=8F=98=E4=B8=BA?= =?UTF-8?q?=E7=99=BD=E8=89=B2=202.=20=E6=8C=89=E9=92=AE=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E5=92=8C=E5=85=B6=E4=BB=96=E6=8C=89=E9=92=AE=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/components/AvatarCropper.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend_nuxt/components/AvatarCropper.vue b/frontend_nuxt/components/AvatarCropper.vue index 2cf260fd7..538ebe5cb 100644 --- a/frontend_nuxt/components/AvatarCropper.vue +++ b/frontend_nuxt/components/AvatarCropper.vue @@ -119,7 +119,7 @@ export default { .cropper-btn { padding: 6px 12px; - border-radius: 4px; + border-radius: 10px; color: var(--primary-color); border: none; background: transparent; @@ -128,7 +128,7 @@ export default { .cropper-btn.primary { background: var(--primary-color); - color: var(--text-color); + color: #ffff; border-color: var(--primary-color); } From 5971700e8a32cb2323fc1e609e80e4de893273b2 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 00:33:05 +0800 Subject: [PATCH 020/105] =?UTF-8?q?fix:=20=E6=96=B0=E5=A2=9E=E4=BE=9D?= =?UTF-8?q?=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/pom.xml | 4 ++++ websocket_service/pom.xml | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/pom.xml b/backend/pom.xml index a0f1c9b7e..1908a5c1b 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -132,6 +132,10 @@ springdoc-openapi-starter-webmvc-api 2.2.0 + + org.springframework.boot + spring-boot-starter-actuator + org.opensearch.client diff --git a/websocket_service/pom.xml b/websocket_service/pom.xml index 37d29413c..4091615b2 100644 --- a/websocket_service/pom.xml +++ b/websocket_service/pom.xml @@ -51,10 +51,10 @@ lombok true - - - - + + org.springframework.boot + spring-boot-starter-actuator + org.springframework.boot spring-boot-starter-test From 24fe90cfc6bdcdadd5d72d44ebda4e96027fb27c Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 00:47:18 +0800 Subject: [PATCH 021/105] fix: change port --- websocket_service/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websocket_service/src/main/resources/application.properties b/websocket_service/src/main/resources/application.properties index 87659cc08..bd7de2ef9 100644 --- a/websocket_service/src/main/resources/application.properties +++ b/websocket_service/src/main/resources/application.properties @@ -1,4 +1,4 @@ -server.port=${SERVER_PORT:8082} +server.port=${WEBSOCKET_PORT:8082} # 服务器配置 spring.application.name=websocket-service From ca26b931dad7cd15954c948503aca315f0011832 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 01:19:13 +0800 Subject: [PATCH 022/105] fix: use server port --- docker/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index d5ee840e8..1dfd9b1af 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -165,6 +165,7 @@ services: environment: WS_HEALTH_PATH: ${WS_HEALTH_PATH:-/actuator/health} WEBSOCKET_PORT: ${WEBSOCKET_PORT:-8082} + SERVER_PORT: ${WEBSOCKET_PORT:-8082} ports: - "${WEBSOCKET_PORT:-8082}:${WEBSOCKET_PORT:-8082}" volumes: From f8a2ee6ee95c38a183cd799087c863553a61aa4f Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 01:45:47 +0800 Subject: [PATCH 023/105] fix: use server port --- backend/src/main/java/com/openisle/config/SecurityConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/main/java/com/openisle/config/SecurityConfig.java b/backend/src/main/java/com/openisle/config/SecurityConfig.java index b2d0da85e..4f9782cb3 100644 --- a/backend/src/main/java/com/openisle/config/SecurityConfig.java +++ b/backend/src/main/java/com/openisle/config/SecurityConfig.java @@ -179,6 +179,8 @@ public class SecurityConfig { .permitAll() .requestMatchers(HttpMethod.POST, "/api/point-goods") .permitAll() + .requestMatchers("/actuator/**") + .permitAll() .requestMatchers(HttpMethod.POST, "/api/categories/**") .hasAuthority("ADMIN") .requestMatchers(HttpMethod.POST, "/api/tags/**") From 28618c745239bb5f9bfd32e44536c86577c6ab14 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 10:22:39 +0800 Subject: [PATCH 024/105] =?UTF-8?q?fix:=20springboot=20healthy=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/openisle/config/SecurityConfig.java | 1 + backend/src/main/resources/db/init/02_seed_data.sql | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/openisle/config/SecurityConfig.java b/backend/src/main/java/com/openisle/config/SecurityConfig.java index 4f9782cb3..6a5dc8fb2 100644 --- a/backend/src/main/java/com/openisle/config/SecurityConfig.java +++ b/backend/src/main/java/com/openisle/config/SecurityConfig.java @@ -234,6 +234,7 @@ public class SecurityConfig { uri.startsWith("/api/channels") || uri.startsWith("/api/sitemap.xml") || uri.startsWith("/api/medals") || + uri.startsWith("/actuator") || uri.startsWith("/api/rss")); if (authHeader != null && authHeader.startsWith("Bearer ")) { diff --git a/backend/src/main/resources/db/init/02_seed_data.sql b/backend/src/main/resources/db/init/02_seed_data.sql index 091635304..53562803c 100644 --- a/backend/src/main/resources/db/init/02_seed_data.sql +++ b/backend/src/main/resources/db/init/02_seed_data.sql @@ -12,9 +12,9 @@ INSERT INTO `users` (`id`,`approved`,`avatar`,`created_at`,`display_medal`,`emai (3,b'1','', '2025-09-02 17:21:21.617666','PIONEER','usermail1@openisle.com',40,NULL,'$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe',NULL,40,'测试测试测试……','USER','user2',NULL,b'1'); INSERT INTO `categories` (`id`,`description`,`icon`,`name`,`small_icon`) VALUES -(1,'测试用分类1','1','测试用分类1',NULL), -(2,'测试用分类2','2','测试用分类2',NULL), -(3,'测试用分类3','3','测试用分类3',NULL); +(1,'测试用分类1','star','测试用分类1',NULL), +(2,'测试用分类2','star','测试用分类2',NULL), +(3,'测试用分类3','star','测试用分类3',NULL); INSERT INTO `tags` (`id`,`approved`,`created_at`,`description`,`icon`,`name`,`small_icon`,`creator_id`) VALUES (1,b'1','2025-09-02 10:51:56.000000','测试用标签1',NULL,'测试用标签1',NULL,NULL), From 7ce5de7f7c3c5d416095e1ffac05f561caaecdca Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 10:45:31 +0800 Subject: [PATCH 025/105] =?UTF-8?q?fix:=20=E8=87=AA=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/db/init/02_seed_data.sql | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/src/main/resources/db/init/02_seed_data.sql b/backend/src/main/resources/db/init/02_seed_data.sql index 53562803c..f3f375b7a 100644 --- a/backend/src/main/resources/db/init/02_seed_data.sql +++ b/backend/src/main/resources/db/init/02_seed_data.sql @@ -6,10 +6,12 @@ DELETE FROM `tags`; DELETE FROM `categories`; DELETE FROM `users`; -INSERT INTO `users` (`id`,`approved`,`avatar`,`created_at`,`display_medal`,`email`,`experience`,`introduction`,`password`,`password_reset_code`,`point`,`register_reason`,`role`,`username`,`verification_code`,`verified`) VALUES -(1,b'1','', '2025-09-01 16:08:17.426430','PIONEER','adminmail@openisle.com',70,NULL,'$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe',NULL,110,'测试测试测试……','ADMIN','admin',NULL,b'1'), -(2,b'1','', '2025-09-03 16:08:17.426430','PIONEER','usermail2@openisle.com',70,NULL,'$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe',NULL,110,'测试测试测试……','USER','user1',NULL,b'1'), -(3,b'1','', '2025-09-02 17:21:21.617666','PIONEER','usermail1@openisle.com',40,NULL,'$2a$10$dux.NXwW09cCsdZ05BgcnOtxVqqjcmnbj3.8xcxGl/iiIlv06y7Oe',NULL,40,'测试测试测试……','USER','user2',NULL,b'1'); +-- 插入用户,两个普通用户,一个管理员 +-- username:admin/user1/user2 password:123456 +INSERT INTO `users` (`id`, `approved`, `avatar`, `created_at`, `display_medal`, `email`, `experience`, `introduction`, `password`, `password_reset_code`, `point`, `register_reason`, `role`, `username`, `verification_code`, `verified`) VALUES +(1, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-01 16:08:17.426430', 'PIONEER', 'adminmail@openisle.com', 70, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 110, '测试测试测试……', 'ADMIN', 'admin', NULL, b'1'), +(2, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-03 16:08:17.426430', 'PIONEER', 'usermail2@openisle.com', 70, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 110, '测试测试测试……', 'USER', 'user1', NULL, b'1'), +(3, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-02 17:21:21.617666', 'PIONEER', 'usermail1@openisle.com', 40, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 40, '测试测试测试……', 'USER', 'user2', NULL, b'1'); INSERT INTO `categories` (`id`,`description`,`icon`,`name`,`small_icon`) VALUES (1,'测试用分类1','star','测试用分类1',NULL), From d00dbbbd03db0344e4e682f359eb8965d02c2bef Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 13:51:43 +0800 Subject: [PATCH 026/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E7=94=9F=E4=BA=A7=E6=9E=84=E5=BB=BA=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.properties | 2 +- docker/docker-compose.yaml | 25 +++----- docker/frontend-service-entrypoint.sh | 62 ------------------- docker/frontend-service.Dockerfile | 45 +++++++++++--- 4 files changed, 47 insertions(+), 87 deletions(-) delete mode 100755 docker/frontend-service-entrypoint.sh diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 81032349b..6d7c5bbe5 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -4,7 +4,7 @@ server.port=${SERVER_PORT:8080} # for mysql logging.level.root=${LOG_LEVEL:INFO} logging.level.com.openisle.service.CosImageUploader=DEBUG -spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:3306/${MYSQL_DATABASE} +spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE} spring.datasource.username=${MYSQL_USER:root} spring.datasource.password=${MYSQL_PASSWORD:password} spring.jpa.hibernate.ddl-auto=update diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 1dfd9b1af..9366e165a 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -212,29 +212,24 @@ services: profiles: - dev - frontend_service: + frontend_service: build: - context: .. - dockerfile: frontend-service.Dockerfile - container_name: openisle-frontend-service - working_dir: /app + context: .. # 仓库根目录(包含 frontend_nuxt/) + dockerfile: docker/frontend-service.Dockerfile + args: + # 由 .env(或 --env-file)提供,默认 staging;正式环境脚本会传 production + NUXT_ENV: ${NUXT_ENV:-staging} + container_name: openisle-frontend env_file: - - ../.env - volumes: - - ../frontend_nuxt:/app - - frontend-service-node-modules:/app/node_modules - - frontend-static:/var/www/openisle + - ../.env # 或者在脚本里指定 .env.staging / .env.production ports: - - "${FRONTEND_SERVICE_PORT:-3001}:3000" + - "${FRONTEND_PORT:-3000}:3000" depends_on: springboot: condition: service_healthy websocket-service: condition: service_healthy - networks: - - openisle-network - profiles: - - service + restart: unless-stopped loopback_8080: image: alpine/socat diff --git a/docker/frontend-service-entrypoint.sh b/docker/frontend-service-entrypoint.sh deleted file mode 100755 index 6e5485647..000000000 --- a/docker/frontend-service-entrypoint.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -cd /app - -echo "👉 Building frontend (Nuxt SSR)..." - -if [ -f .env.production.example ] && [ ! -f .env ]; then - echo "📄 Copying .env.production.example to .env" - cp .env.production.example .env -fi - -npm ci -npm run build - -echo "🧪 Smoke-testing: nuxt generate (artifacts will NOT be used)..." - -SSR_OUTPUT_DIR=".output" -SSR_OUTPUT_BAK=".output-ssr-backup-$$" -GEN_FAIL_MSG="❌ Generate smoke test failed" - -if [ ! -d "${SSR_OUTPUT_DIR}" ]; then - echo "❌ 未发现 ${SSR_OUTPUT_DIR},请先确保 npm run build 成功执行" - exit 1 -fi - -mv "${SSR_OUTPUT_DIR}" "${SSR_OUTPUT_BAK}" - -restore_on_fail() { - if [ -d ".output" ]; then - mv .output ".output-generate-failed-$(date +%s)" || true - fi - mv "${SSR_OUTPUT_BAK}" "${SSR_OUTPUT_DIR}" -} - -trap 'restore_on_fail; echo "${GEN_FAIL_MSG}: unexpected error"; exit 1' ERR - -NUXT_TELEMETRY_DISABLED=1 \ -NITRO_PRERENDER_FAIL_ON_ERROR=1 \ -npx nuxi generate --preset static - -if [ ! -d ".output/public" ]; then - restore_on_fail - echo "${GEN_FAIL_MSG}: .output/public not found" - exit 1 -fi - -rm -rf ".output" -mv "${SSR_OUTPUT_BAK}" "${SSR_OUTPUT_DIR}" -trap - ERR -echo "✅ Generate smoke test passed." - -if [ -d ".output/public" ]; then - mkdir -p /var/www/openisle - rsync -a --delete .output/public/ /var/www/openisle/ -else - echo "❌ 未发现 .output/public;检查 nuxt.config.ts/nitro preset" - exit 1 -fi - -echo "🚀 Starting Nuxt SSR server..." -exec node .output/server/index.mjs diff --git a/docker/frontend-service.Dockerfile b/docker/frontend-service.Dockerfile index f2846e946..132e185e0 100644 --- a/docker/frontend-service.Dockerfile +++ b/docker/frontend-service.Dockerfile @@ -1,12 +1,39 @@ -FROM node:20 - -RUN apt-get update \ - && apt-get install -y --no-install-recommends rsync \ - && rm -rf /var/lib/apt/lists/* - +# ==== builder ==== +FROM node:20-bullseye AS builder WORKDIR /app -COPY docker/frontend-service-entrypoint.sh /usr/local/bin/frontend-service-entrypoint.sh -RUN chmod +x /usr/local/bin/frontend-service-entrypoint.sh +# 通过构建参数选择环境:staging / production(默认 staging) +ARG NUXT_ENV=staging +ENV NODE_ENV=production \ + NUXT_TELEMETRY_DISABLED=1 -CMD ["frontend-service-entrypoint.sh"] +# 复制源代码(假设仓库根目录包含 frontend_nuxt) +# 构建上下文由 docker-compose 指向仓库根目录 +COPY ./frontend_nuxt/package*.json /app/ +RUN npm ci + +# 拷贝剩余代码 +COPY ./frontend_nuxt/ /app/ + +# 若存在环境样例文件,则在构建期复制为 .env(你也可以用 --build-arg 覆盖) +RUN if [ -f ".env.${NUXT_ENV}.example" ]; then cp ".env.${NUXT_ENV}.example" .env; fi + +# 构建 SSR:产物在 .output +RUN npm run build + +# ==== runner ==== +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production \ + NUXT_TELEMETRY_DISABLED=1 \ + PORT=3000 \ + HOST=0.0.0.0 + +# 复制构建产物 +COPY --from=builder /app/.output /app/.output + +# 健康检查(简洁起见,探测首页) +HEALTHCHECK --interval=10s --timeout=5s --retries=30 CMD wget -qO- http://127.0.0.1:${PORT}/ >/dev/null 2>&1 || exit 1 + +EXPOSE 3000 +CMD ["node", ".output/server/index.mjs"] From 22b813e40b2d1940068d38594fa9b3ceaf332ee1 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 14:16:10 +0800 Subject: [PATCH 027/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 56 ++++++++++++++------------------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 9366e165a..dc946fc08 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -26,7 +26,6 @@ services: retries: 30 start_period: 20s - # OpenSearch Service opensearch: build: @@ -53,10 +52,8 @@ services: restart: unless-stopped healthcheck: test: - [ - "CMD-SHELL", - "curl -fsS http://127.0.0.1:9200/_cluster/health >/dev/null", - ] + - CMD-SHELL + - curl -fsS http://127.0.0.1:9200/_cluster/health >/dev/null interval: 10s timeout: 5s retries: 30 @@ -68,8 +65,8 @@ services: image: opensearchproject/opensearch-dashboards:3.0.0 container_name: os-dashboards environment: - - OPENSEARCH_HOSTS=["http://opensearch:9200"] - - DISABLE_SECURITY_DASHBOARDS_PLUGIN=true + OPENSEARCH_HOSTS: '["http://opensearch:9200"]' + DISABLE_SECURITY_DASHBOARDS_PLUGIN: "true" ports: - "${OPENSEARCH_DASHBOARDS_PORT:-5601}:5601" depends_on: @@ -83,7 +80,7 @@ services: container_name: openisle-rabbitmq restart: unless-stopped environment: - RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_VHOST:-/} + RABBITMQ_DEFAULT_VHOST: "${RABBITMQ_VHOST:-/}" ports: - "${RABBITMQ_PORT:-5672}:5672" - "${RABBITMQ_MANAGEMENT_PORT:-15672}:15672" @@ -144,11 +141,7 @@ services: sh -c "apt-get update && apt-get install -y --no-install-recommends curl && mvn clean spring-boot:run -Dmaven.test.skip=true" healthcheck: - test: - [ - "CMD-SHELL", - "curl -fsS http://127.0.0.1:${SERVER_PORT:-8080}${SPRING_HEALTH_PATH:-/actuator/health} || exit 1", - ] + test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:${SERVER_PORT:-8080}${SPRING_HEALTH_PATH:-/actuator/health} || exit 1"] interval: 10s timeout: 5s retries: 30 @@ -178,11 +171,7 @@ services: sh -c "apt-get update && apt-get install -y --no-install-recommends curl && mvn clean spring-boot:run -Dmaven.test.skip=true" healthcheck: - test: - [ - "CMD-SHELL", - "curl -fsS http://127.0.0.1:${WEBSOCKET_PORT:-8082}${WS_HEALTH_PATH:-/actuator/health} || exit 1", - ] + test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:${WEBSOCKET_PORT:-8082}${WS_HEALTH_PATH:-/actuator/health} || exit 1"] interval: 10s timeout: 5s retries: 30 @@ -212,16 +201,15 @@ services: profiles: - dev - frontend_service: + frontend_service: build: - context: .. # 仓库根目录(包含 frontend_nuxt/) + context: .. dockerfile: docker/frontend-service.Dockerfile args: - # 由 .env(或 --env-file)提供,默认 staging;正式环境脚本会传 production NUXT_ENV: ${NUXT_ENV:-staging} container_name: openisle-frontend env_file: - - ../.env # 或者在脚本里指定 .env.staging / .env.production + - ../.env ports: - "${FRONTEND_PORT:-3000}:3000" depends_on: @@ -236,13 +224,11 @@ services: container_name: loopback-8080 # 监听“frontend_dev 容器自身的” 127.0.0.1:8080 → 转发到 springboot:8080 command: - [ - "-d", - "-d", - "-ly", - "TCP4-LISTEN:8080,bind=127.0.0.1,reuseaddr,fork", - "TCP4:springboot:8080", - ] + - -d + - -d + - -ly + - TCP4-LISTEN:8080,bind=127.0.0.1,reuseaddr,fork + - TCP4:springboot:8080 depends_on: springboot: condition: service_healthy @@ -260,13 +246,11 @@ services: container_name: loopback-8082 # 监听 127.0.0.1:8082 → 转发到 websocket-service:8082(WS 纯 TCP 可直接过) command: - [ - "-d", - "-d", - "-ly", - "TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork", - "TCP4:websocket-service:8082", - ] + - -d + - -d + - -ly + - TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork + - TCP4:websocket-service:8082 depends_on: websocket-service: condition: service_healthy From 263f2deeb1c89642795d991dda4b48c6c597984a Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 14:32:30 +0800 Subject: [PATCH 028/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index dc946fc08..04d7d2427 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -5,7 +5,7 @@ services: container_name: openisle-mysql restart: always env_file: - - ../.env + - ${ENV_FILE:-../.env} command: > --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_ai_ci @@ -103,7 +103,7 @@ services: container_name: openisle-redis restart: unless-stopped env_file: - - ../.env + - ${ENV_FILE:-../.env} ports: - "${REDIS_PORT:-6379}:6379" volumes: @@ -111,13 +111,13 @@ services: networks: - openisle-network - # Java spring boot service + # Java spring boot service (开发便捷镜像,后续可换成打包镜像) springboot: image: maven:3.9-eclipse-temurin-17 container_name: openisle-springboot working_dir: /app env_file: - - ../.env + - ${ENV_FILE:-../.env} environment: SPRING_HEALTH_PATH: ${SPRING_HEALTH_PATH:-/actuator/health} SERVER_PORT: ${SERVER_PORT:-8080} @@ -154,7 +154,7 @@ services: container_name: openisle-websocket working_dir: /app env_file: - - ../.env + - ${ENV_FILE:-../.env} environment: WS_HEALTH_PATH: ${WS_HEALTH_PATH:-/actuator/health} WEBSOCKET_PORT: ${WEBSOCKET_PORT:-8082} @@ -184,7 +184,7 @@ services: container_name: openisle-frontend-dev working_dir: /app env_file: - - ../.env + - ${ENV_FILE:-../.env} command: sh -c "npm install && npm run dev" volumes: - ../frontend_nuxt:/app @@ -209,7 +209,7 @@ services: NUXT_ENV: ${NUXT_ENV:-staging} container_name: openisle-frontend env_file: - - ../.env + - ${ENV_FILE:-../.env} ports: - "${FRONTEND_PORT:-3000}:3000" depends_on: From 180c45bf2dc104e0881f7c0c7c0cae603be3b2ff Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 14:52:27 +0800 Subject: [PATCH 029/105] =?UTF-8?q?fix:=20=E7=AB=AF=E5=8F=A3=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 04d7d2427..53f68ff10 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -12,7 +12,7 @@ services: --default-time-zone=+08:00 --skip-character-set-client-handshake ports: - - "${MYSQL_PORT:-3306}:3306" + - "${MYSQL_PORT:-3306}:${MYSQL_PORT:-3306}" volumes: - mysql-data:/var/lib/mysql - ../backend/src/main/resources/db/init:/docker-entrypoint-initdb.d:ro @@ -47,8 +47,8 @@ services: - ./data:/usr/share/opensearch/data - ./snapshots:/snapshots ports: - - "${OPENSEARCH_PORT:-9200}:9200" - - "${OPENSEARCH_METRICS_PORT:-9600}:9600" + - "${OPENSEARCH_PORT:-9200}:${OPENSEARCH_PORT:-9200}" + - "${OPENSEARCH_METRICS_PORT:-9600}:${OPENSEARCH_METRICS_PORT:-9600}" restart: unless-stopped healthcheck: test: @@ -68,7 +68,7 @@ services: OPENSEARCH_HOSTS: '["http://opensearch:9200"]' DISABLE_SECURITY_DASHBOARDS_PLUGIN: "true" ports: - - "${OPENSEARCH_DASHBOARDS_PORT:-5601}:5601" + - "${OPENSEARCH_DASHBOARDS_PORT:-5601}:${OPENSEARCH_DASHBOARDS_PORT:-5601}" depends_on: - opensearch restart: unless-stopped @@ -82,8 +82,8 @@ services: environment: RABBITMQ_DEFAULT_VHOST: "${RABBITMQ_VHOST:-/}" ports: - - "${RABBITMQ_PORT:-5672}:5672" - - "${RABBITMQ_MANAGEMENT_PORT:-15672}:15672" + - "${RABBITMQ_PORT:-5672}:${RABBITMQ_PORT:-5672}" + - "${RABBITMQ_MANAGEMENT_PORT:-15672}:${RABBITMQ_MANAGEMENT_PORT:-15672}" volumes: - rabbitmq-data:/var/lib/rabbitmq - ./rabbitmq/conf/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro @@ -105,7 +105,7 @@ services: env_file: - ${ENV_FILE:-../.env} ports: - - "${REDIS_PORT:-6379}:6379" + - "${REDIS_PORT:-6379}:${REDIS_PORT:-6379}" volumes: - redis-data:/data networks: From b62b9c691f07bf9569247af9f22e8e4afd98672a Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 14:54:57 +0800 Subject: [PATCH 030/105] =?UTF-8?q?fix:=20healthy=20check=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 53f68ff10..33f80912b 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -53,7 +53,7 @@ services: healthcheck: test: - CMD-SHELL - - curl -fsS http://127.0.0.1:9200/_cluster/health >/dev/null + - curl -fsS http://127.0.0.1:${OPENSEARCH_PORT:-9200}/_cluster/health >/dev/null interval: 10s timeout: 5s retries: 30 From 2457efd11d3aad395cc57d6fb91771c1bf83348f Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 15:02:36 +0800 Subject: [PATCH 031/105] =?UTF-8?q?Revert=20"fix:=20healthy=20check=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b62b9c691f07bf9569247af9f22e8e4afd98672a. --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 33f80912b..53f68ff10 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -53,7 +53,7 @@ services: healthcheck: test: - CMD-SHELL - - curl -fsS http://127.0.0.1:${OPENSEARCH_PORT:-9200}/_cluster/health >/dev/null + - curl -fsS http://127.0.0.1:9200/_cluster/health >/dev/null interval: 10s timeout: 5s retries: 30 From 9dc9ca9bd8ea0cf4f56b99464688ef3f6ef7662a Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 15:02:40 +0800 Subject: [PATCH 032/105] =?UTF-8?q?Revert=20"fix:=20=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E9=80=82=E9=85=8D"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 180c45bf2dc104e0881f7c0c7c0cae603be3b2ff. --- docker/docker-compose.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 53f68ff10..04d7d2427 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -12,7 +12,7 @@ services: --default-time-zone=+08:00 --skip-character-set-client-handshake ports: - - "${MYSQL_PORT:-3306}:${MYSQL_PORT:-3306}" + - "${MYSQL_PORT:-3306}:3306" volumes: - mysql-data:/var/lib/mysql - ../backend/src/main/resources/db/init:/docker-entrypoint-initdb.d:ro @@ -47,8 +47,8 @@ services: - ./data:/usr/share/opensearch/data - ./snapshots:/snapshots ports: - - "${OPENSEARCH_PORT:-9200}:${OPENSEARCH_PORT:-9200}" - - "${OPENSEARCH_METRICS_PORT:-9600}:${OPENSEARCH_METRICS_PORT:-9600}" + - "${OPENSEARCH_PORT:-9200}:9200" + - "${OPENSEARCH_METRICS_PORT:-9600}:9600" restart: unless-stopped healthcheck: test: @@ -68,7 +68,7 @@ services: OPENSEARCH_HOSTS: '["http://opensearch:9200"]' DISABLE_SECURITY_DASHBOARDS_PLUGIN: "true" ports: - - "${OPENSEARCH_DASHBOARDS_PORT:-5601}:${OPENSEARCH_DASHBOARDS_PORT:-5601}" + - "${OPENSEARCH_DASHBOARDS_PORT:-5601}:5601" depends_on: - opensearch restart: unless-stopped @@ -82,8 +82,8 @@ services: environment: RABBITMQ_DEFAULT_VHOST: "${RABBITMQ_VHOST:-/}" ports: - - "${RABBITMQ_PORT:-5672}:${RABBITMQ_PORT:-5672}" - - "${RABBITMQ_MANAGEMENT_PORT:-15672}:${RABBITMQ_MANAGEMENT_PORT:-15672}" + - "${RABBITMQ_PORT:-5672}:5672" + - "${RABBITMQ_MANAGEMENT_PORT:-15672}:15672" volumes: - rabbitmq-data:/var/lib/rabbitmq - ./rabbitmq/conf/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro @@ -105,7 +105,7 @@ services: env_file: - ${ENV_FILE:-../.env} ports: - - "${REDIS_PORT:-6379}:${REDIS_PORT:-6379}" + - "${REDIS_PORT:-6379}:6379" volumes: - redis-data:/data networks: From 181ac7bc8f0061dfe96774c6ee5bbfc2a1f43039 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 15:09:59 +0800 Subject: [PATCH 033/105] =?UTF-8?q?fix:=20=E5=AE=B9=E5=99=A8port=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 04d7d2427..e016c8a6a 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -121,6 +121,10 @@ services: environment: SPRING_HEALTH_PATH: ${SPRING_HEALTH_PATH:-/actuator/health} SERVER_PORT: ${SERVER_PORT:-8080} + RABBITMQ_PORT: 5672 + OPENSEARCH_PORT: 9200 + MYSQL_PORT: 3306 + REDIS_PORT: 6379 ports: - "${SERVER_PORT:-8080}:${SERVER_PORT:-8080}" volumes: @@ -159,6 +163,7 @@ services: WS_HEALTH_PATH: ${WS_HEALTH_PATH:-/actuator/health} WEBSOCKET_PORT: ${WEBSOCKET_PORT:-8082} SERVER_PORT: ${WEBSOCKET_PORT:-8082} + RABBITMQ_PORT: 5672 ports: - "${WEBSOCKET_PORT:-8082}:${WEBSOCKET_PORT:-8082}" volumes: From 18a6953ff794b29ef9a9b1b6986094a346fa00c0 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 15:15:11 +0800 Subject: [PATCH 034/105] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3opensearch?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index e016c8a6a..78318a754 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -28,6 +28,7 @@ services: # OpenSearch Service opensearch: + user: "1000:1000" build: context: . dockerfile: opensearch.Dockerfile From adfc05b9b2401527b5821febb16b31cbeecedb35 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Tue, 30 Sep 2025 20:11:45 +0800 Subject: [PATCH 035/105] feat: add admin point grants and history UI --- .../controller/AdminPointController.java | 35 +++++ .../openisle/dto/AdminGrantPointRequest.java | 12 ++ .../com/openisle/model/PointHistoryType.java | 1 + .../com/openisle/service/PointService.java | 16 +++ frontend_nuxt/pages/about/index.vue | 34 ++++- frontend_nuxt/pages/points.vue | 12 ++ frontend_nuxt/pages/settings.vue | 127 ++++++++++++++++++ 7 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 backend/src/main/java/com/openisle/controller/AdminPointController.java create mode 100644 backend/src/main/java/com/openisle/dto/AdminGrantPointRequest.java diff --git a/backend/src/main/java/com/openisle/controller/AdminPointController.java b/backend/src/main/java/com/openisle/controller/AdminPointController.java new file mode 100644 index 000000000..5352391aa --- /dev/null +++ b/backend/src/main/java/com/openisle/controller/AdminPointController.java @@ -0,0 +1,35 @@ +package com.openisle.controller; + +import com.openisle.dto.AdminGrantPointRequest; +import com.openisle.service.PointService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/admin/points") +@RequiredArgsConstructor +public class AdminPointController { + + private final PointService pointService; + + @PostMapping("/grant") + @SecurityRequirement(name = "JWT") + @Operation(summary = "Grant points", description = "Grant points to a user as administrator") + @ApiResponse(responseCode = "200", description = "Points granted") + public Map grant( + @RequestBody AdminGrantPointRequest request, + Authentication auth + ) { + String username = request.getUsername(); + int balance = pointService.grantPointByAdmin(auth.getName(), username, request.getAmount()); + return Map.of("username", username.trim(), "point", balance); + } +} diff --git a/backend/src/main/java/com/openisle/dto/AdminGrantPointRequest.java b/backend/src/main/java/com/openisle/dto/AdminGrantPointRequest.java new file mode 100644 index 000000000..c4503f0db --- /dev/null +++ b/backend/src/main/java/com/openisle/dto/AdminGrantPointRequest.java @@ -0,0 +1,12 @@ +package com.openisle.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AdminGrantPointRequest { + + private String username; + private int amount; +} diff --git a/backend/src/main/java/com/openisle/model/PointHistoryType.java b/backend/src/main/java/com/openisle/model/PointHistoryType.java index 689f73c9c..ed71dc461 100644 --- a/backend/src/main/java/com/openisle/model/PointHistoryType.java +++ b/backend/src/main/java/com/openisle/model/PointHistoryType.java @@ -13,4 +13,5 @@ public enum PointHistoryType { REDEEM, LOTTERY_JOIN, LOTTERY_REWARD, + ADMIN_GRANT, } diff --git a/backend/src/main/java/com/openisle/service/PointService.java b/backend/src/main/java/com/openisle/service/PointService.java index 0a8349a53..029cab314 100644 --- a/backend/src/main/java/com/openisle/service/PointService.java +++ b/backend/src/main/java/com/openisle/service/PointService.java @@ -43,6 +43,22 @@ public class PointService { return addPoint(user, 500, PointHistoryType.FEATURE, post, null, null); } + public int grantPointByAdmin(String adminName, String targetUsername, int amount) { + if (amount <= 0) { + throw new FieldException("amount", "积分必须为正数"); + } + if (targetUsername == null || targetUsername.isBlank()) { + throw new FieldException("username", "用户名不能为空"); + } + String normalizedUsername = targetUsername.trim(); + User admin = userRepository.findByUsername(adminName).orElseThrow(); + User target = userRepository + .findByUsername(normalizedUsername) + .orElseThrow(() -> new FieldException("username", "用户不存在")); + addPoint(target, amount, PointHistoryType.ADMIN_GRANT, null, null, admin); + return target.getPoint(); + } + public void processLotteryJoin(User participant, LotteryPost post) { int cost = post.getPointCost(); if (cost > 0) { diff --git a/frontend_nuxt/pages/about/index.vue b/frontend_nuxt/pages/about/index.vue index 5d669c2de..be77273b6 100644 --- a/frontend_nuxt/pages/about/index.vue +++ b/frontend_nuxt/pages/about/index.vue @@ -71,6 +71,16 @@ export default { label: '隐私政策', file: 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/about/privacy.md', }, + { + key: 'points', + label: '积分说明', + content: `# 积分说明 + +- 积分可用于兑换商品、参与抽奖等社区玩法。 +- 管理员可以通过后台新增的积分模块为用户发放奖励积分。 +- 每次发放都会记录在积分历史中,方便你查看积分来源。 +`, + }, { key: 'api', label: 'API与调试', @@ -88,11 +98,21 @@ export default { return `${token.value.slice(0, 20)}...${token.value.slice(-10)}` }) - const loadContent = async (file) => { - if (!file) return + const loadContent = async (tab) => { + if (!tab || tab.key === 'api') return + if (tab.content) { + isFetching.value = false + content.value = tab.content + return + } + if (!tab.file) { + isFetching.value = false + content.value = '' + return + } try { isFetching.value = true - const res = await fetch(file) + const res = await fetch(tab.file) if (res.ok) { content.value = await res.text() } else { @@ -110,15 +130,15 @@ export default { if (initTab && tabs.find((t) => t.key === initTab)) { selectedTab.value = initTab const tab = tabs.find((t) => t.key === initTab) - if (tab && tab.file) loadContent(tab.file) + if (tab) loadContent(tab) } else { - loadContent(tabs[0].file) + loadContent(tabs[0]) } }) watch(selectedTab, (name) => { const tab = tabs.find((t) => t.key === name) - if (tab && tab.file) loadContent(tab.file) + if (tab) loadContent(tab) router.replace({ query: { ...route.query, tab: name } }) }) @@ -127,6 +147,8 @@ export default { (name) => { if (name && name !== selectedTab.value && tabs.find((t) => t.key === name)) { selectedTab.value = name + const tab = tabs.find((t) => t.key === name) + if (tab) loadContent(tab) } }, ) diff --git a/frontend_nuxt/pages/points.vue b/frontend_nuxt/pages/points.vue index 1aace6a27..203faaa4e 100644 --- a/frontend_nuxt/pages/points.vue +++ b/frontend_nuxt/pages/points.vue @@ -184,6 +184,16 @@ }} 参与,获得 {{ item.amount }} 积分 + + 你目前的积分是 {{ item.balance }} @@ -229,6 +239,7 @@ const pointRules = [ '评论被点赞:每次 10 积分', '邀请好友加入可获得 500 积分/次,注意需要使用邀请链接注册', '文章被收录至精选:每次 500 积分', + '管理员赠送:特殊活动可由管理员手动赠送积分', ] const goods = ref([]) @@ -250,6 +261,7 @@ const iconMap = { LOTTERY_REWARD: 'fireworks', POST_LIKE_CANCELLED: 'clear-icon', COMMENT_LIKE_CANCELLED: 'clear-icon', + ADMIN_GRANT: 'paper-money-two', } const loadTrend = async () => { diff --git a/frontend_nuxt/pages/settings.vue b/frontend_nuxt/pages/settings.vue index 6da7c4fbe..15347d98f 100644 --- a/frontend_nuxt/pages/settings.vue +++ b/frontend_nuxt/pages/settings.vue @@ -65,6 +65,35 @@
注册模式
+
+
发放积分
+
+ + + +
+
{{ grantError }}
+
积分会立即发放给目标用户,并记录在积分历史中
+
保存中...
@@ -102,6 +131,10 @@ const registerMode = ref('DIRECT') const isLoadingPage = ref(false) const isSaving = ref(false) const frosted = ref(true) +const grantUsername = ref('') +const grantAmount = ref('') +const grantError = ref('') +const isGrantingPoints = ref(false) onMounted(async () => { isLoadingPage.value = true @@ -184,6 +217,55 @@ const loadAdminConfig = async () => { // ignore } } + +const grantPoint = async () => { + if (isGrantingPoints.value) return + const username = grantUsername.value.trim() + if (!username) { + grantError.value = '用户名不能为空' + toast.error(grantError.value) + return + } + const amount = Number(grantAmount.value) + if (!Number.isInteger(amount) || amount <= 0) { + grantError.value = '积分数量必须为正整数' + toast.error(grantError.value) + return + } + isGrantingPoints.value = true + try { + const token = getToken() + const res = await fetch(`${API_BASE_URL}/api/admin/points/grant`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ username, amount }), + }) + let data = null + try { + data = await res.json() + } catch (e) { + // ignore body parse errors + } + if (res.ok) { + toast.success(`已为 ${username} 发放 ${amount} 积分`) + grantUsername.value = '' + grantAmount.value = '' + grantError.value = '' + } else { + const message = data?.error || '发放失败' + grantError.value = message + toast.error(message) + } + } catch (e) { + grantError.value = '发放失败,请稍后再试' + toast.error(grantError.value) + } finally { + isGrantingPoints.value = false + } +} const save = async () => { isSaving.value = true @@ -323,6 +405,51 @@ const save = async () => { max-width: 200px; } +.grant-row { + max-width: 100%; +} + +.grant-form { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} + +.grant-input { + flex: 1 1 180px; +} + +.grant-input.amount { + max-width: 140px; +} + +.grant-button { + background-color: var(--primary-color); + color: #fff; + border: none; + border-radius: 8px; + padding: 8px 16px; + cursor: pointer; + transition: background-color 0.2s ease-in-out; +} + +.grant-button.disabled, +.grant-button:disabled { + cursor: not-allowed; + background-color: var(--primary-color-disabled); +} + +.grant-button:not(.disabled):hover { + background-color: var(--primary-color-hover); +} + +.grant-error-message { + color: red; + font-size: 14px; + margin-top: 8px; +} + .switch-row { flex-direction: row; align-items: center; From b49e20d0109927d763211c1778b729eb4c4a9d53 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 21:33:14 +0800 Subject: [PATCH 036/105] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=90=8D=E3=80=81=E5=8F=98=E9=87=8F=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/deploy_staging.sh | 57 ++++++++++++++++++++++++++++++++++++++ docker/docker-compose.yaml | 35 ++++++++++++++--------- 2 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 deploy/deploy_staging.sh diff --git a/deploy/deploy_staging.sh b/deploy/deploy_staging.sh new file mode 100644 index 000000000..f2d623259 --- /dev/null +++ b/deploy/deploy_staging.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 可用法: +# ./deploy-staging.sh +# ./deploy-staging.sh feature/docker +deploy_branch="${1:-feature/docker}" + +repo_dir="/opt/openisle/OpenIsle-staging" +compose_file="${repo_dir}/docker/docker-compose.yaml" +# 使用仓库根目录的 .env(CI 预先写好),也可以改成绝对路径 +env_file="${repo_dir}/.env" +project="openisle_staging" + +echo "👉 Enter repo..." +cd "$repo_dir" + +echo "👉 Syncing code & switching to branch: $deploy_branch" +git fetch --all --prune +git checkout -B "$deploy_branch" "origin/$deploy_branch" +git reset --hard "origin/$deploy_branch" + +echo "👉 Ensuring env file: $env_file" +if [ ! -f "$env_file" ]; then + echo "❌ ${env_file} not found. Create it based on .env.example (with staging domains)." + exit 1 +fi + +export COMPOSE_PROJECT_NAME="$project" +# 供 compose 内各 service 的 env_file 使用 +export ENV_FILE="$env_file" + +echo "👉 Validate compose..." +docker compose -f "$compose_file" --env-file "$env_file" config >/dev/null + +echo "👉 Pull base images (for image-based services)..." +docker compose -f "$compose_file" --env-file "$env_file" pull --ignore-pull-failures + +echo "👉 Build images (staging)..." +# 前端 + OpenSearch 都是自建镜像;--pull 更新其基础镜像 +docker compose -f "$compose_file" --env-file "$env_file" \ + build --pull \ + --build-arg NUXT_ENV=staging \ + frontend_service opensearch + +echo "👉 Recreate & start all target services (no dev profile)..." +docker compose -f "$compose_file" --env-file "$env_file" \ + up -d --force-recreate --remove-orphans \ + mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service + +echo "👉 Current status:" +docker compose -f "$compose_file" --env-file "$env_file" ps + +echo "👉 Pruning dangling images..." +docker image prune -f + +echo "✅ Staging stack deployed at $(date)" \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 78318a754..ade5aeaaf 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -2,7 +2,7 @@ services: # MySQL service mysql: image: mysql:8.0 - container_name: openisle-mysql + container_name: ${COMPOSE_PROJECT_NAME}-openisle-mysql restart: always env_file: - ${ENV_FILE:-../.env} @@ -32,7 +32,7 @@ services: build: context: . dockerfile: opensearch.Dockerfile - container_name: opensearch + container_name: ${COMPOSE_PROJECT_NAME}-opensearch environment: - cluster.name=os-single - node.name=os-node-1 @@ -45,8 +45,8 @@ services: memlock: { soft: -1, hard: -1 } nofile: { soft: 65536, hard: 65536 } volumes: - - ./data:/usr/share/opensearch/data - - ./snapshots:/snapshots + - ${OPENSEARCH_DATA_DIR:-./data}:/usr/share/opensearch/data + - ${OPENSEARCH_SNAPSHOT_DIR:-./snapshots}:/snapshots ports: - "${OPENSEARCH_PORT:-9200}:9200" - "${OPENSEARCH_METRICS_PORT:-9600}:9600" @@ -64,7 +64,7 @@ services: dashboards: image: opensearchproject/opensearch-dashboards:3.0.0 - container_name: os-dashboards + container_name: ${COMPOSE_PROJECT_NAME}-os-dashboards environment: OPENSEARCH_HOSTS: '["http://opensearch:9200"]' DISABLE_SECURITY_DASHBOARDS_PLUGIN: "true" @@ -78,7 +78,7 @@ services: rabbitmq: image: rabbitmq:3.13-management - container_name: openisle-rabbitmq + container_name: ${COMPOSE_PROJECT_NAME}-openisle-rabbitmq restart: unless-stopped environment: RABBITMQ_DEFAULT_VHOST: "${RABBITMQ_VHOST:-/}" @@ -101,7 +101,7 @@ services: redis: image: redis:7 - container_name: openisle-redis + container_name: ${COMPOSE_PROJECT_NAME}-openisle-redis restart: unless-stopped env_file: - ${ENV_FILE:-../.env} @@ -115,7 +115,7 @@ services: # Java spring boot service (开发便捷镜像,后续可换成打包镜像) springboot: image: maven:3.9-eclipse-temurin-17 - container_name: openisle-springboot + container_name: ${COMPOSE_PROJECT_NAME}-openisle-springboot working_dir: /app env_file: - ${ENV_FILE:-../.env} @@ -156,7 +156,7 @@ services: websocket-service: image: maven:3.9-eclipse-temurin-17 - container_name: openisle-websocket + container_name: ${COMPOSE_PROJECT_NAME}-openisle-websocket working_dir: /app env_file: - ${ENV_FILE:-../.env} @@ -187,7 +187,7 @@ services: frontend_dev: image: node:20 - container_name: openisle-frontend-dev + container_name: ${COMPOSE_PROJECT_NAME}-openisle-frontend-dev working_dir: /app env_file: - ${ENV_FILE:-../.env} @@ -213,7 +213,7 @@ services: dockerfile: docker/frontend-service.Dockerfile args: NUXT_ENV: ${NUXT_ENV:-staging} - container_name: openisle-frontend + container_name: ${COMPOSE_PROJECT_NAME}-openisle-frontend env_file: - ${ENV_FILE:-../.env} ports: @@ -227,7 +227,7 @@ services: loopback_8080: image: alpine/socat - container_name: loopback-8080 + container_name: ${COMPOSE_PROJECT_NAME}-loopback-8080 # 监听“frontend_dev 容器自身的” 127.0.0.1:8080 → 转发到 springboot:8080 command: - -d @@ -249,7 +249,7 @@ services: loopback_8082: image: alpine/socat - container_name: loopback-8082 + container_name: ${COMPOSE_PROJECT_NAME}-loopback-8082 # 监听 127.0.0.1:8082 → 转发到 websocket-service:8082(WS 纯 TCP 可直接过) command: - -d @@ -271,14 +271,23 @@ services: networks: openisle-network: + name: "${COMPOSE_PROJECT_NAME}_net" driver: bridge volumes: mysql-data: + name: "${COMPOSE_PROJECT_NAME}_mysql-data" maven-repo: + name: "${COMPOSE_PROJECT_NAME}_maven-repo" redis-data: + name: "${COMPOSE_PROJECT_NAME}_redis-data" rabbitmq-data: + name: "${COMPOSE_PROJECT_NAME}_rabbitmq-data" websocket-maven-repo: + name: "${COMPOSE_PROJECT_NAME}_websocket-maven-repo" frontend-node-modules: + name: "${COMPOSE_PROJECT_NAME}_frontend-node-modules" frontend-service-node-modules: + name: "${COMPOSE_PROJECT_NAME}_frontend-service-node-modules" frontend-static: + name: "${COMPOSE_PROJECT_NAME}_frontend-static" From a0ea63700f3e616b19e95083de62ae971b13876e Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 21:34:30 +0800 Subject: [PATCH 037/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/deploy.sh | 50 +++++++++++++++++++++++++++++++++++ deploy/deploy_staging.sh | 57 ---------------------------------------- 2 files changed, 50 insertions(+), 57 deletions(-) create mode 100644 deploy/deploy.sh delete mode 100644 deploy/deploy_staging.sh diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 000000000..25b9a3979 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 用法: +# ./deploy.sh staging +# ./deploy.sh prod +env="${1:-staging}" # staging | prod +project="openisle_${env}" + +repo_dir="/opt/openisle/OpenIsle-${env}" +compose_file="${repo_dir}/docker/docker-compose.yaml" +env_file="${repo_dir}/.env.${env}" + +echo "👉 Enter repo..." +cd "$repo_dir" + +echo "👉 Sync & checkout..." +git fetch --all --prune +git checkout -B "main" "origin/main" +git reset --hard "origin/main" + +echo "👉 Ensure env file: $env_file" +[ -f "$env_file" ] || { echo "❌ $env_file missing"; exit 1; } + +export COMPOSE_PROJECT_NAME="$project" +export ENV_FILE="$env_file" + +echo "👉 Validate compose..." +docker compose -f "$compose_file" --env-file "$env_file" config >/dev/null + +echo "👉 Pull images..." +docker compose -f "$compose_file" --env-file "$env_file" pull --ignore-pull-failures + +echo "👉 Build custom images..." +docker compose -f "$compose_file" --env-file "$env_file" \ + build --pull \ + --build-arg NUXT_ENV="$env" \ + frontend_service opensearch + +echo "👉 Up services..." +docker compose -f "$compose_file" --env-file "$env_file" up -d --force-recreate --remove-orphans \ + mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service + +echo "👉 Status" +docker compose -f "$compose_file" --env-file "$env_file" ps + +echo "👉 Prune dangling images" +docker image prune -f + +echo "✅ ${env} stack deployed at $(date)" diff --git a/deploy/deploy_staging.sh b/deploy/deploy_staging.sh deleted file mode 100644 index f2d623259..000000000 --- a/deploy/deploy_staging.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# 可用法: -# ./deploy-staging.sh -# ./deploy-staging.sh feature/docker -deploy_branch="${1:-feature/docker}" - -repo_dir="/opt/openisle/OpenIsle-staging" -compose_file="${repo_dir}/docker/docker-compose.yaml" -# 使用仓库根目录的 .env(CI 预先写好),也可以改成绝对路径 -env_file="${repo_dir}/.env" -project="openisle_staging" - -echo "👉 Enter repo..." -cd "$repo_dir" - -echo "👉 Syncing code & switching to branch: $deploy_branch" -git fetch --all --prune -git checkout -B "$deploy_branch" "origin/$deploy_branch" -git reset --hard "origin/$deploy_branch" - -echo "👉 Ensuring env file: $env_file" -if [ ! -f "$env_file" ]; then - echo "❌ ${env_file} not found. Create it based on .env.example (with staging domains)." - exit 1 -fi - -export COMPOSE_PROJECT_NAME="$project" -# 供 compose 内各 service 的 env_file 使用 -export ENV_FILE="$env_file" - -echo "👉 Validate compose..." -docker compose -f "$compose_file" --env-file "$env_file" config >/dev/null - -echo "👉 Pull base images (for image-based services)..." -docker compose -f "$compose_file" --env-file "$env_file" pull --ignore-pull-failures - -echo "👉 Build images (staging)..." -# 前端 + OpenSearch 都是自建镜像;--pull 更新其基础镜像 -docker compose -f "$compose_file" --env-file "$env_file" \ - build --pull \ - --build-arg NUXT_ENV=staging \ - frontend_service opensearch - -echo "👉 Recreate & start all target services (no dev profile)..." -docker compose -f "$compose_file" --env-file "$env_file" \ - up -d --force-recreate --remove-orphans \ - mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service - -echo "👉 Current status:" -docker compose -f "$compose_file" --env-file "$env_file" ps - -echo "👉 Pruning dangling images..." -docker image prune -f - -echo "✅ Staging stack deployed at $(date)" \ No newline at end of file From a6dd2bfbc234916b80d6be2138fa92ab057616e0 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 30 Sep 2025 21:40:14 +0800 Subject: [PATCH 038/105] =?UTF-8?q?Revert=20"fix:=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8D"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a0ea63700f3e616b19e95083de62ae971b13876e. --- deploy/deploy.sh | 50 ----------------------------------- deploy/deploy_staging.sh | 57 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 50 deletions(-) delete mode 100644 deploy/deploy.sh create mode 100644 deploy/deploy_staging.sh diff --git a/deploy/deploy.sh b/deploy/deploy.sh deleted file mode 100644 index 25b9a3979..000000000 --- a/deploy/deploy.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# 用法: -# ./deploy.sh staging -# ./deploy.sh prod -env="${1:-staging}" # staging | prod -project="openisle_${env}" - -repo_dir="/opt/openisle/OpenIsle-${env}" -compose_file="${repo_dir}/docker/docker-compose.yaml" -env_file="${repo_dir}/.env.${env}" - -echo "👉 Enter repo..." -cd "$repo_dir" - -echo "👉 Sync & checkout..." -git fetch --all --prune -git checkout -B "main" "origin/main" -git reset --hard "origin/main" - -echo "👉 Ensure env file: $env_file" -[ -f "$env_file" ] || { echo "❌ $env_file missing"; exit 1; } - -export COMPOSE_PROJECT_NAME="$project" -export ENV_FILE="$env_file" - -echo "👉 Validate compose..." -docker compose -f "$compose_file" --env-file "$env_file" config >/dev/null - -echo "👉 Pull images..." -docker compose -f "$compose_file" --env-file "$env_file" pull --ignore-pull-failures - -echo "👉 Build custom images..." -docker compose -f "$compose_file" --env-file "$env_file" \ - build --pull \ - --build-arg NUXT_ENV="$env" \ - frontend_service opensearch - -echo "👉 Up services..." -docker compose -f "$compose_file" --env-file "$env_file" up -d --force-recreate --remove-orphans \ - mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service - -echo "👉 Status" -docker compose -f "$compose_file" --env-file "$env_file" ps - -echo "👉 Prune dangling images" -docker image prune -f - -echo "✅ ${env} stack deployed at $(date)" diff --git a/deploy/deploy_staging.sh b/deploy/deploy_staging.sh new file mode 100644 index 000000000..f2d623259 --- /dev/null +++ b/deploy/deploy_staging.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 可用法: +# ./deploy-staging.sh +# ./deploy-staging.sh feature/docker +deploy_branch="${1:-feature/docker}" + +repo_dir="/opt/openisle/OpenIsle-staging" +compose_file="${repo_dir}/docker/docker-compose.yaml" +# 使用仓库根目录的 .env(CI 预先写好),也可以改成绝对路径 +env_file="${repo_dir}/.env" +project="openisle_staging" + +echo "👉 Enter repo..." +cd "$repo_dir" + +echo "👉 Syncing code & switching to branch: $deploy_branch" +git fetch --all --prune +git checkout -B "$deploy_branch" "origin/$deploy_branch" +git reset --hard "origin/$deploy_branch" + +echo "👉 Ensuring env file: $env_file" +if [ ! -f "$env_file" ]; then + echo "❌ ${env_file} not found. Create it based on .env.example (with staging domains)." + exit 1 +fi + +export COMPOSE_PROJECT_NAME="$project" +# 供 compose 内各 service 的 env_file 使用 +export ENV_FILE="$env_file" + +echo "👉 Validate compose..." +docker compose -f "$compose_file" --env-file "$env_file" config >/dev/null + +echo "👉 Pull base images (for image-based services)..." +docker compose -f "$compose_file" --env-file "$env_file" pull --ignore-pull-failures + +echo "👉 Build images (staging)..." +# 前端 + OpenSearch 都是自建镜像;--pull 更新其基础镜像 +docker compose -f "$compose_file" --env-file "$env_file" \ + build --pull \ + --build-arg NUXT_ENV=staging \ + frontend_service opensearch + +echo "👉 Recreate & start all target services (no dev profile)..." +docker compose -f "$compose_file" --env-file "$env_file" \ + up -d --force-recreate --remove-orphans \ + mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service + +echo "👉 Current status:" +docker compose -f "$compose_file" --env-file "$env_file" ps + +echo "👉 Pruning dangling images..." +docker image prune -f + +echo "✅ Staging stack deployed at $(date)" \ No newline at end of file From 25a7f1e13814e4ca3650fd199d0fe76a57996628 Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 1 Oct 2025 11:35:02 +0800 Subject: [PATCH 039/105] fix: add deploy --- deploy/deploy.sh | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 deploy/deploy.sh diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 000000000..f2d623259 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 可用法: +# ./deploy-staging.sh +# ./deploy-staging.sh feature/docker +deploy_branch="${1:-feature/docker}" + +repo_dir="/opt/openisle/OpenIsle-staging" +compose_file="${repo_dir}/docker/docker-compose.yaml" +# 使用仓库根目录的 .env(CI 预先写好),也可以改成绝对路径 +env_file="${repo_dir}/.env" +project="openisle_staging" + +echo "👉 Enter repo..." +cd "$repo_dir" + +echo "👉 Syncing code & switching to branch: $deploy_branch" +git fetch --all --prune +git checkout -B "$deploy_branch" "origin/$deploy_branch" +git reset --hard "origin/$deploy_branch" + +echo "👉 Ensuring env file: $env_file" +if [ ! -f "$env_file" ]; then + echo "❌ ${env_file} not found. Create it based on .env.example (with staging domains)." + exit 1 +fi + +export COMPOSE_PROJECT_NAME="$project" +# 供 compose 内各 service 的 env_file 使用 +export ENV_FILE="$env_file" + +echo "👉 Validate compose..." +docker compose -f "$compose_file" --env-file "$env_file" config >/dev/null + +echo "👉 Pull base images (for image-based services)..." +docker compose -f "$compose_file" --env-file "$env_file" pull --ignore-pull-failures + +echo "👉 Build images (staging)..." +# 前端 + OpenSearch 都是自建镜像;--pull 更新其基础镜像 +docker compose -f "$compose_file" --env-file "$env_file" \ + build --pull \ + --build-arg NUXT_ENV=staging \ + frontend_service opensearch + +echo "👉 Recreate & start all target services (no dev profile)..." +docker compose -f "$compose_file" --env-file "$env_file" \ + up -d --force-recreate --remove-orphans \ + mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service + +echo "👉 Current status:" +docker compose -f "$compose_file" --env-file "$env_file" ps + +echo "👉 Pruning dangling images..." +docker image prune -f + +echo "✅ Staging stack deployed at $(date)" \ No newline at end of file From a2b0cd1a476f230fd99ee737aa519c65ec3dbf4a Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 1 Oct 2025 11:36:55 +0800 Subject: [PATCH 040/105] =?UTF-8?q?fix:=20=E6=96=B0=E5=A2=9Edeploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/deploy.sh | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index f2d623259..d371dfec6 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -2,15 +2,14 @@ set -euo pipefail # 可用法: -# ./deploy-staging.sh -# ./deploy-staging.sh feature/docker +# ./deploy.sh +# ./deploy.sh feature/docker deploy_branch="${1:-feature/docker}" -repo_dir="/opt/openisle/OpenIsle-staging" +repo_dir="/opt/openisle/OpenIsle" compose_file="${repo_dir}/docker/docker-compose.yaml" -# 使用仓库根目录的 .env(CI 预先写好),也可以改成绝对路径 env_file="${repo_dir}/.env" -project="openisle_staging" +project="openisle" echo "👉 Enter repo..." cd "$repo_dir" @@ -22,7 +21,7 @@ git reset --hard "origin/$deploy_branch" echo "👉 Ensuring env file: $env_file" if [ ! -f "$env_file" ]; then - echo "❌ ${env_file} not found. Create it based on .env.example (with staging domains)." + echo "❌ ${env_file} not found. Create it based on .env.example (with domains)." exit 1 fi @@ -36,11 +35,11 @@ docker compose -f "$compose_file" --env-file "$env_file" config >/dev/null echo "👉 Pull base images (for image-based services)..." docker compose -f "$compose_file" --env-file "$env_file" pull --ignore-pull-failures -echo "👉 Build images (staging)..." +echo "👉 Build images ..." # 前端 + OpenSearch 都是自建镜像;--pull 更新其基础镜像 docker compose -f "$compose_file" --env-file "$env_file" \ build --pull \ - --build-arg NUXT_ENV=staging \ + --build-arg NUXT_ENV=production \ frontend_service opensearch echo "👉 Recreate & start all target services (no dev profile)..." @@ -54,4 +53,4 @@ docker compose -f "$compose_file" --env-file "$env_file" ps echo "👉 Pruning dangling images..." docker image prune -f -echo "✅ Staging stack deployed at $(date)" \ No newline at end of file +echo "✅ Stack deployed at $(date)" \ No newline at end of file From 12bc4058567dea5f6b25e66a631ec743e6343ff1 Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 1 Oct 2025 15:36:18 +0800 Subject: [PATCH 041/105] =?UTF-8?q?fix:=20=E6=97=B6=E5=8C=BA=E9=99=90?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index ade5aeaaf..d3bcab39f 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -114,6 +114,7 @@ services: # Java spring boot service (开发便捷镜像,后续可换成打包镜像) springboot: + TZ: "Asia/Shanghai" image: maven:3.9-eclipse-temurin-17 container_name: ${COMPOSE_PROJECT_NAME}-openisle-springboot working_dir: /app From 6f9570dc95b48d5042b6abc068d28bb3e765d0bb Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 1 Oct 2025 15:38:12 +0800 Subject: [PATCH 042/105] =?UTF-8?q?fix:=20=E6=97=B6=E5=8C=BA=E9=99=90?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index d3bcab39f..9211cd37a 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -114,13 +114,13 @@ services: # Java spring boot service (开发便捷镜像,后续可换成打包镜像) springboot: - TZ: "Asia/Shanghai" image: maven:3.9-eclipse-temurin-17 container_name: ${COMPOSE_PROJECT_NAME}-openisle-springboot working_dir: /app env_file: - ${ENV_FILE:-../.env} environment: + TZ: "Asia/Shanghai" SPRING_HEALTH_PATH: ${SPRING_HEALTH_PATH:-/actuator/health} SERVER_PORT: ${SERVER_PORT:-8080} RABBITMQ_PORT: 5672 From 737157e557292fcfed42a8ac11990c372d6c9eef Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 1 Oct 2025 16:08:53 +0800 Subject: [PATCH 043/105] fix: add timezone --- docker/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 9211cd37a..a1307b786 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -127,6 +127,7 @@ services: OPENSEARCH_PORT: 9200 MYSQL_PORT: 3306 REDIS_PORT: 6379 + JAVA_OPTS: "-Duser.timezone=Asia/Shanghai" ports: - "${SERVER_PORT:-8080}:${SERVER_PORT:-8080}" volumes: From 2cb8c12f65529b55ab8b04c70955b10c829a7006 Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 1 Oct 2025 16:33:14 +0800 Subject: [PATCH 044/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/deploy_staging.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/deploy_staging.sh b/deploy/deploy_staging.sh index f2d623259..ecdbd91a1 100644 --- a/deploy/deploy_staging.sh +++ b/deploy/deploy_staging.sh @@ -4,7 +4,7 @@ set -euo pipefail # 可用法: # ./deploy-staging.sh # ./deploy-staging.sh feature/docker -deploy_branch="${1:-feature/docker}" +deploy_branch="${1:-main}" repo_dir="/opt/openisle/OpenIsle-staging" compose_file="${repo_dir}/docker/docker-compose.yaml" From b98871bed9430f605dbc729a3fdcae676ed00373 Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 1 Oct 2025 16:40:23 +0800 Subject: [PATCH 045/105] =?UTF-8?q?fix:=20mysql=20=E6=97=B6=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 6d7c5bbe5..17cdbdaa9 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -4,7 +4,7 @@ server.port=${SERVER_PORT:8080} # for mysql logging.level.root=${LOG_LEVEL:INFO} logging.level.com.openisle.service.CosImageUploader=DEBUG -spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE} +spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useUnicode=yes&characterEncoding=UTF-8&useInformationSchema=true&useSSL=false&serverTimezone=UTC spring.datasource.username=${MYSQL_USER:root} spring.datasource.password=${MYSQL_PASSWORD:password} spring.jpa.hibernate.ddl-auto=update From 8f3642260976916564c7f9ba07cb41aa70b22889 Mon Sep 17 00:00:00 2001 From: tim Date: Thu, 2 Oct 2025 15:28:25 +0800 Subject: [PATCH 046/105] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E4=BC=A0?= =?UTF-8?q?=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 17cdbdaa9..bf8e62ea8 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -4,7 +4,7 @@ server.port=${SERVER_PORT:8080} # for mysql logging.level.root=${LOG_LEVEL:INFO} logging.level.com.openisle.service.CosImageUploader=DEBUG -spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useUnicode=yes&characterEncoding=UTF-8&useInformationSchema=true&useSSL=false&serverTimezone=UTC +spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useUnicode=yes&characterEncoding=UTF-8&useInformationSchema=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true spring.datasource.username=${MYSQL_USER:root} spring.datasource.password=${MYSQL_PASSWORD:password} spring.jpa.hibernate.ddl-auto=update From 00333744819b63721756beb4d77a41cea80b89b8 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Thu, 2 Oct 2025 21:42:52 +0800 Subject: [PATCH 047/105] Allow iframe embeds in markdown sanitizer --- frontend_nuxt/utils/markdown.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frontend_nuxt/utils/markdown.js b/frontend_nuxt/utils/markdown.js index 51bb327d1..42d46c0c4 100644 --- a/frontend_nuxt/utils/markdown.js +++ b/frontend_nuxt/utils/markdown.js @@ -157,6 +157,7 @@ const SANITIZE_CFG = { 'th', 'video', 'source', + 'iframe', ], // 允许的属性 allowedAttributes: { @@ -180,6 +181,16 @@ const SANITIZE_CFG = { 'crossorigin', ], source: ['src', 'type'], + iframe: [ + 'src', + 'title', + 'width', + 'height', + 'allow', + 'allowfullscreen', + 'frameborder', + 'referrerpolicy', + ], }, // 允许的类名(保留你的样式钩子) allowedClasses: { From 8df566a9c90a3a686236ab9034f1260d825aeab9 Mon Sep 17 00:00:00 2001 From: tim Date: Thu, 2 Oct 2025 22:00:08 +0800 Subject: [PATCH 048/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E4=B8=BAmain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index d371dfec6..78c3ae122 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -4,7 +4,7 @@ set -euo pipefail # 可用法: # ./deploy.sh # ./deploy.sh feature/docker -deploy_branch="${1:-feature/docker}" +deploy_branch="${1:-main}" repo_dir="/opt/openisle/OpenIsle" compose_file="${repo_dir}/docker/docker-compose.yaml" From 236245802447b30da88061945917f790185127c3 Mon Sep 17 00:00:00 2001 From: tim Date: Thu, 2 Oct 2025 22:25:15 +0800 Subject: [PATCH 049/105] =?UTF-8?q?fix:=20volumes=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index a1307b786..fb6d09cd0 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -45,8 +45,8 @@ services: memlock: { soft: -1, hard: -1 } nofile: { soft: 65536, hard: 65536 } volumes: - - ${OPENSEARCH_DATA_DIR:-./data}:/usr/share/opensearch/data - - ${OPENSEARCH_SNAPSHOT_DIR:-./snapshots}:/snapshots + - opensearch-data:/usr/share/opensearch/data + - opensearch-snapshots:/snapshots ports: - "${OPENSEARCH_PORT:-9200}:9200" - "${OPENSEARCH_METRICS_PORT:-9600}:9600" @@ -293,3 +293,7 @@ volumes: name: "${COMPOSE_PROJECT_NAME}_frontend-service-node-modules" frontend-static: name: "${COMPOSE_PROJECT_NAME}_frontend-static" + opensearch-data: + name: "${COMPOSE_PROJECT_NAME}_opensearch-data" + opensearch-snapshots: + name: "${COMPOSE_PROJECT_NAME}_opensearch-snapshots" From 994f4028fc9d44488023bd211b1fffc2407a52cf Mon Sep 17 00:00:00 2001 From: tim Date: Thu, 2 Oct 2025 22:29:35 +0800 Subject: [PATCH 050/105] =?UTF-8?q?fix:=20=E5=8F=96=E6=B6=88opensearch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/deploy.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 78c3ae122..2355b7ae5 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -45,7 +45,8 @@ docker compose -f "$compose_file" --env-file "$env_file" \ echo "👉 Recreate & start all target services (no dev profile)..." docker compose -f "$compose_file" --env-file "$env_file" \ up -d --force-recreate --remove-orphans \ - mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service + # mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service + mysql redis rabbitmq dashboards websocket-service springboot frontend_service echo "👉 Current status:" docker compose -f "$compose_file" --env-file "$env_file" ps From 83cf7439c9fa4a8d9d5809bbed9bb33e6f856dc4 Mon Sep 17 00:00:00 2001 From: tim Date: Thu, 2 Oct 2025 22:35:19 +0800 Subject: [PATCH 051/105] =?UTF-8?q?fix:=20=E5=88=A0=E9=99=A4dashboard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/deploy.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 2355b7ae5..36e0fc5e1 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -40,13 +40,12 @@ echo "👉 Build images ..." docker compose -f "$compose_file" --env-file "$env_file" \ build --pull \ --build-arg NUXT_ENV=production \ - frontend_service opensearch + frontend_service echo "👉 Recreate & start all target services (no dev profile)..." docker compose -f "$compose_file" --env-file "$env_file" \ - up -d --force-recreate --remove-orphans \ - # mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service - mysql redis rabbitmq dashboards websocket-service springboot frontend_service + up -d --force-recreate --remove-orphans --no-deps \ + mysql redis rabbitmq websocket-service springboot frontend_service echo "👉 Current status:" docker compose -f "$compose_file" --env-file "$env_file" ps From c95b2ebdc210eac11577fb6c198647cccc41846a Mon Sep 17 00:00:00 2001 From: tim Date: Fri, 3 Oct 2025 00:48:00 +0800 Subject: [PATCH 052/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9staging?= =?UTF-8?q?=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/deploy_staging.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/deploy/deploy_staging.sh b/deploy/deploy_staging.sh index ecdbd91a1..2f6a3d066 100644 --- a/deploy/deploy_staging.sh +++ b/deploy/deploy_staging.sh @@ -8,7 +8,6 @@ deploy_branch="${1:-main}" repo_dir="/opt/openisle/OpenIsle-staging" compose_file="${repo_dir}/docker/docker-compose.yaml" -# 使用仓库根目录的 .env(CI 预先写好),也可以改成绝对路径 env_file="${repo_dir}/.env" project="openisle_staging" @@ -41,12 +40,12 @@ echo "👉 Build images (staging)..." docker compose -f "$compose_file" --env-file "$env_file" \ build --pull \ --build-arg NUXT_ENV=staging \ - frontend_service opensearch + frontend_service echo "👉 Recreate & start all target services (no dev profile)..." docker compose -f "$compose_file" --env-file "$env_file" \ - up -d --force-recreate --remove-orphans \ - mysql redis rabbitmq opensearch dashboards websocket-service springboot frontend_service + up -d --force-recreate --remove-orphans --no-deps \ + mysql redis rabbitmq websocket-service springboot frontend_service echo "👉 Current status:" docker compose -f "$compose_file" --env-file "$env_file" ps From 57e6bcaa0cc931211963a1da92e073eee4fb5177 Mon Sep 17 00:00:00 2001 From: tim Date: Fri, 3 Oct 2025 00:58:24 +0800 Subject: [PATCH 053/105] Revert "feat: add admin point grants and history UI" This reverts commit adfc05b9b2401527b5821febb16b31cbeecedb35. --- .../controller/AdminPointController.java | 35 ----- .../openisle/dto/AdminGrantPointRequest.java | 12 -- .../com/openisle/model/PointHistoryType.java | 1 - .../com/openisle/service/PointService.java | 16 --- frontend_nuxt/pages/about/index.vue | 34 +---- frontend_nuxt/pages/points.vue | 12 -- frontend_nuxt/pages/settings.vue | 127 ------------------ 7 files changed, 6 insertions(+), 231 deletions(-) delete mode 100644 backend/src/main/java/com/openisle/controller/AdminPointController.java delete mode 100644 backend/src/main/java/com/openisle/dto/AdminGrantPointRequest.java diff --git a/backend/src/main/java/com/openisle/controller/AdminPointController.java b/backend/src/main/java/com/openisle/controller/AdminPointController.java deleted file mode 100644 index 5352391aa..000000000 --- a/backend/src/main/java/com/openisle/controller/AdminPointController.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.openisle.controller; - -import com.openisle.dto.AdminGrantPointRequest; -import com.openisle.service.PointService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/admin/points") -@RequiredArgsConstructor -public class AdminPointController { - - private final PointService pointService; - - @PostMapping("/grant") - @SecurityRequirement(name = "JWT") - @Operation(summary = "Grant points", description = "Grant points to a user as administrator") - @ApiResponse(responseCode = "200", description = "Points granted") - public Map grant( - @RequestBody AdminGrantPointRequest request, - Authentication auth - ) { - String username = request.getUsername(); - int balance = pointService.grantPointByAdmin(auth.getName(), username, request.getAmount()); - return Map.of("username", username.trim(), "point", balance); - } -} diff --git a/backend/src/main/java/com/openisle/dto/AdminGrantPointRequest.java b/backend/src/main/java/com/openisle/dto/AdminGrantPointRequest.java deleted file mode 100644 index c4503f0db..000000000 --- a/backend/src/main/java/com/openisle/dto/AdminGrantPointRequest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.openisle.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminGrantPointRequest { - - private String username; - private int amount; -} diff --git a/backend/src/main/java/com/openisle/model/PointHistoryType.java b/backend/src/main/java/com/openisle/model/PointHistoryType.java index ed71dc461..689f73c9c 100644 --- a/backend/src/main/java/com/openisle/model/PointHistoryType.java +++ b/backend/src/main/java/com/openisle/model/PointHistoryType.java @@ -13,5 +13,4 @@ public enum PointHistoryType { REDEEM, LOTTERY_JOIN, LOTTERY_REWARD, - ADMIN_GRANT, } diff --git a/backend/src/main/java/com/openisle/service/PointService.java b/backend/src/main/java/com/openisle/service/PointService.java index 029cab314..0a8349a53 100644 --- a/backend/src/main/java/com/openisle/service/PointService.java +++ b/backend/src/main/java/com/openisle/service/PointService.java @@ -43,22 +43,6 @@ public class PointService { return addPoint(user, 500, PointHistoryType.FEATURE, post, null, null); } - public int grantPointByAdmin(String adminName, String targetUsername, int amount) { - if (amount <= 0) { - throw new FieldException("amount", "积分必须为正数"); - } - if (targetUsername == null || targetUsername.isBlank()) { - throw new FieldException("username", "用户名不能为空"); - } - String normalizedUsername = targetUsername.trim(); - User admin = userRepository.findByUsername(adminName).orElseThrow(); - User target = userRepository - .findByUsername(normalizedUsername) - .orElseThrow(() -> new FieldException("username", "用户不存在")); - addPoint(target, amount, PointHistoryType.ADMIN_GRANT, null, null, admin); - return target.getPoint(); - } - public void processLotteryJoin(User participant, LotteryPost post) { int cost = post.getPointCost(); if (cost > 0) { diff --git a/frontend_nuxt/pages/about/index.vue b/frontend_nuxt/pages/about/index.vue index be77273b6..5d669c2de 100644 --- a/frontend_nuxt/pages/about/index.vue +++ b/frontend_nuxt/pages/about/index.vue @@ -71,16 +71,6 @@ export default { label: '隐私政策', file: 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/about/privacy.md', }, - { - key: 'points', - label: '积分说明', - content: `# 积分说明 - -- 积分可用于兑换商品、参与抽奖等社区玩法。 -- 管理员可以通过后台新增的积分模块为用户发放奖励积分。 -- 每次发放都会记录在积分历史中,方便你查看积分来源。 -`, - }, { key: 'api', label: 'API与调试', @@ -98,21 +88,11 @@ export default { return `${token.value.slice(0, 20)}...${token.value.slice(-10)}` }) - const loadContent = async (tab) => { - if (!tab || tab.key === 'api') return - if (tab.content) { - isFetching.value = false - content.value = tab.content - return - } - if (!tab.file) { - isFetching.value = false - content.value = '' - return - } + const loadContent = async (file) => { + if (!file) return try { isFetching.value = true - const res = await fetch(tab.file) + const res = await fetch(file) if (res.ok) { content.value = await res.text() } else { @@ -130,15 +110,15 @@ export default { if (initTab && tabs.find((t) => t.key === initTab)) { selectedTab.value = initTab const tab = tabs.find((t) => t.key === initTab) - if (tab) loadContent(tab) + if (tab && tab.file) loadContent(tab.file) } else { - loadContent(tabs[0]) + loadContent(tabs[0].file) } }) watch(selectedTab, (name) => { const tab = tabs.find((t) => t.key === name) - if (tab) loadContent(tab) + if (tab && tab.file) loadContent(tab.file) router.replace({ query: { ...route.query, tab: name } }) }) @@ -147,8 +127,6 @@ export default { (name) => { if (name && name !== selectedTab.value && tabs.find((t) => t.key === name)) { selectedTab.value = name - const tab = tabs.find((t) => t.key === name) - if (tab) loadContent(tab) } }, ) diff --git a/frontend_nuxt/pages/points.vue b/frontend_nuxt/pages/points.vue index 203faaa4e..1aace6a27 100644 --- a/frontend_nuxt/pages/points.vue +++ b/frontend_nuxt/pages/points.vue @@ -184,16 +184,6 @@ }} 参与,获得 {{ item.amount }} 积分 - - 你目前的积分是 {{ item.balance }}
@@ -239,7 +229,6 @@ const pointRules = [ '评论被点赞:每次 10 积分', '邀请好友加入可获得 500 积分/次,注意需要使用邀请链接注册', '文章被收录至精选:每次 500 积分', - '管理员赠送:特殊活动可由管理员手动赠送积分', ] const goods = ref([]) @@ -261,7 +250,6 @@ const iconMap = { LOTTERY_REWARD: 'fireworks', POST_LIKE_CANCELLED: 'clear-icon', COMMENT_LIKE_CANCELLED: 'clear-icon', - ADMIN_GRANT: 'paper-money-two', } const loadTrend = async () => { diff --git a/frontend_nuxt/pages/settings.vue b/frontend_nuxt/pages/settings.vue index 15347d98f..6da7c4fbe 100644 --- a/frontend_nuxt/pages/settings.vue +++ b/frontend_nuxt/pages/settings.vue @@ -65,35 +65,6 @@
注册模式
-
-
发放积分
-
- - - -
-
{{ grantError }}
-
积分会立即发放给目标用户,并记录在积分历史中
-
保存中...
@@ -131,10 +102,6 @@ const registerMode = ref('DIRECT') const isLoadingPage = ref(false) const isSaving = ref(false) const frosted = ref(true) -const grantUsername = ref('') -const grantAmount = ref('') -const grantError = ref('') -const isGrantingPoints = ref(false) onMounted(async () => { isLoadingPage.value = true @@ -217,55 +184,6 @@ const loadAdminConfig = async () => { // ignore } } - -const grantPoint = async () => { - if (isGrantingPoints.value) return - const username = grantUsername.value.trim() - if (!username) { - grantError.value = '用户名不能为空' - toast.error(grantError.value) - return - } - const amount = Number(grantAmount.value) - if (!Number.isInteger(amount) || amount <= 0) { - grantError.value = '积分数量必须为正整数' - toast.error(grantError.value) - return - } - isGrantingPoints.value = true - try { - const token = getToken() - const res = await fetch(`${API_BASE_URL}/api/admin/points/grant`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ username, amount }), - }) - let data = null - try { - data = await res.json() - } catch (e) { - // ignore body parse errors - } - if (res.ok) { - toast.success(`已为 ${username} 发放 ${amount} 积分`) - grantUsername.value = '' - grantAmount.value = '' - grantError.value = '' - } else { - const message = data?.error || '发放失败' - grantError.value = message - toast.error(message) - } - } catch (e) { - grantError.value = '发放失败,请稍后再试' - toast.error(grantError.value) - } finally { - isGrantingPoints.value = false - } -} const save = async () => { isSaving.value = true @@ -405,51 +323,6 @@ const save = async () => { max-width: 200px; } -.grant-row { - max-width: 100%; -} - -.grant-form { - display: flex; - flex-wrap: wrap; - gap: 10px; - align-items: center; -} - -.grant-input { - flex: 1 1 180px; -} - -.grant-input.amount { - max-width: 140px; -} - -.grant-button { - background-color: var(--primary-color); - color: #fff; - border: none; - border-radius: 8px; - padding: 8px 16px; - cursor: pointer; - transition: background-color 0.2s ease-in-out; -} - -.grant-button.disabled, -.grant-button:disabled { - cursor: not-allowed; - background-color: var(--primary-color-disabled); -} - -.grant-button:not(.disabled):hover { - background-color: var(--primary-color-hover); -} - -.grant-error-message { - color: red; - font-size: 14px; - margin-top: 8px; -} - .switch-row { flex-direction: row; align-items: center; From e8976a98d4698eb17b124be7faf45a539254de4a Mon Sep 17 00:00:00 2001 From: tim Date: Fri, 3 Oct 2025 16:43:38 +0800 Subject: [PATCH 054/105] =?UTF-8?q?fix:=20=E6=96=B0=E5=A2=9Enginx=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E4=BF=AE=E6=94=B9deploy=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-staging.yml | 2 +- .github/workflows/deploy.yml | 2 +- nginx/openisle | 174 +++++++++++++++++++++++++++ nginx/openisle-staging | 133 ++++++++++++++++++++ 4 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 nginx/openisle create mode 100644 nginx/openisle-staging diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index dde70459b..ef91f572e 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -23,7 +23,7 @@ jobs: host: ${{ secrets.SSH_HOST }} username: root key: ${{ secrets.SSH_KEY }} - script: bash /opt/openisle/deploy-staging.sh + script: bash /opt/openisle/OpenIsle/deploy_staging.sh deploy-docs: needs: build-and-deploy diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 28c013027..5a799da8e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -19,4 +19,4 @@ jobs: host: ${{ secrets.SSH_HOST }} username: root key: ${{ secrets.SSH_KEY }} - script: bash /opt/openisle/deploy.sh + script: bash /opt/openisle/OpenIsle/deploy.sh diff --git a/nginx/openisle b/nginx/openisle new file mode 100644 index 000000000..2ee2fe9fb --- /dev/null +++ b/nginx/openisle @@ -0,0 +1,174 @@ +server { + listen 443 ssl; + server_name open-isle.com www.open-isle.com; + + ssl_certificate /etc/letsencrypt/live/open-isle.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/open-isle.com/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + location / { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + add_header Cache-Control "no-store" always; + add_header X-Upstream $upstream_addr always; + } + + location ^~ /api/ws { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + + # 升级所需 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + # 统一透传这些头(你在 /api/ 有,/api/ws 也要有) + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_buffering off; + proxy_cache off; + } + + # 2) SockJS(包含 /info、/iframe.html、/.../websocket 等) + location ^~ /api/sockjs { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_buffering off; + proxy_cache off; + + # 如要同源 iframe 回退,下面两行二选一(或者交给 Spring Security 的 sameOrigin) + # proxy_hide_header X-Frame-Options; + # add_header X-Frame-Options "SAMEORIGIN" always; + } + + location /api/ { + proxy_pass http://127.0.0.1:8080/api/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always; + proxy_no_cache 1; + proxy_cache_bypass 1; + } + + # 通过 https://open-isle.com/rabbitmq/ 访问管理界面 + location ^~ /rabbitmq/ { + # 关键点:proxy_pass 以 "/" 结尾,保留后缀子路径映射 + proxy_pass http://127.0.0.1:15672/; + proxy_http_version 1.1; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + + # 把上游返回的绝对重定向 /... 改写为 /rabbitmq/... + proxy_redirect ~^(/.*)$ /rabbitmq$1; + + # 为了做 HTML/CSS/JS 内绝对路径替换,需要关闭压缩 + proxy_set_header Accept-Encoding ""; + + # 将页面中以 "/" 开头的 src/href 替换为 "/rabbitmq/..." + sub_filter_types text/html text/css application/javascript; + sub_filter 'href="/' 'href="/rabbitmq/'; + sub_filter 'src="/' 'src="/rabbitmq/'; + sub_filter_once off; + + # 建议对管理台再加一道保护(可选) + # auth_basic "RabbitMQ Console"; + # auth_basic_user_file /etc/nginx/.htpasswd; + } + + # 通过 https://open-isle.com/docker/ 访问 Portainer(上游是自签名 HTTPS) + location ^~ /docker/ { + proxy_pass https://127.0.0.1:19000/; # 末尾 / 保留子路径 + proxy_http_version 1.1; + + # 上游是自签证书,关闭校验(仅内网/自签场景) + proxy_ssl_verify off; + + # 透传头 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # WebSocket/事件流(Portainer 某些功能会用到) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_buffering off; + proxy_cache off; + + # 把上游返回的绝对重定向 /... 改写为 /docker/... + proxy_redirect ~^(/.*)$ /docker$1; + + # 为了替换 HTML/CSS/JS 中的绝对路径,需要关闭压缩 + proxy_set_header Accept-Encoding ""; + + # 将页面中以 "/" 开头的 src/href 替换为 "/docker/..." + sub_filter_types text/html text/css application/javascript; + sub_filter 'href="/' 'href="/docker/'; + sub_filter 'src="/' 'src="/docker/'; + sub_filter_once off; + + # 可选:再加一道基本认证 + # auth_basic "Portainer"; + # auth_basic_user_file /etc/nginx/.htpasswd; + } + + + # ---------- WEBSOCKET GATEWAY TO :8082 ---------- + location ^~ /websocket/ { + proxy_pass http://127.0.0.1:8082/; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_buffering off; + proxy_cache off; + add_header Cache-Control "no-store" always; + } +} +server { + listen 80; + server_name open-isle.com www.open-isle.com; + return 301 https://$host$request_uri; +} diff --git a/nginx/openisle-staging b/nginx/openisle-staging new file mode 100644 index 000000000..951a9b4cf --- /dev/null +++ b/nginx/openisle-staging @@ -0,0 +1,133 @@ +# 放在 http { } 里一次定义 +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 443 ssl; + server_name staging.open-isle.com www.staging.open-isle.com; + + + ssl_certificate /etc/letsencrypt/live/staging.open-isle.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/staging.open-isle.com/privkey.pem; + # ssl_certificate /etc/letsencrypt/live/open-isle.com/fullchain.pem; + # ssl_certificate_key /etc/letsencrypt/live/open-isle.com/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + # ---------- SSR ---------- + location / { + proxy_pass http://127.0.0.1:3001; + proxy_http_version 1.1; + + # 正确的升级头(仅在有 Upgrade 时) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + # 透传真实主机/协议/源 IP + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # 合理超时,避免 SSR 首屏慢查询导致 502/504 + proxy_read_timeout 120s; + proxy_send_timeout 120s; + + add_header Cache-Control "no-store" always; + add_header X-Upstream $upstream_addr always; + } + + # 1) 原生 WebSocket + location ^~ /api/ws { + proxy_pass http://127.0.0.1:8081; # 不要尾随 /,保留原样 URI + proxy_http_version 1.1; + + # 升级所需 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + # 统一透传这些头(你在 /api/ 有,/api/ws 也要有) + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_buffering off; + proxy_cache off; + } + + # 2) SockJS(包含 /info、/iframe.html、/.../websocket 等) + location ^~ /api/sockjs { + proxy_pass http://127.0.0.1:8081; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_buffering off; + proxy_cache off; + + # 如要同源 iframe 回退,下面两行二选一(或者交给 Spring Security 的 sameOrigin) + # proxy_hide_header X-Frame-Options; + # add_header X-Frame-Options "SAMEORIGIN" always; + } + + # ---------- API ---------- + location /api/ { + proxy_pass http://127.0.0.1:8081/api/; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_read_timeout 120s; + proxy_send_timeout 120s; + + add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always; + proxy_no_cache 1; + proxy_cache_bypass 1; + } + + # ---------- WEBSOCKET GATEWAY TO :8083 ---------- + location ^~ /websocket/ { + proxy_pass http://127.0.0.1:8083/; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_buffering off; + proxy_cache off; + add_header Cache-Control "no-store" always; + } + +} From 57a9a98da602c3978b20e93e569f27806ddc1284 Mon Sep 17 00:00:00 2001 From: tim Date: Fri, 3 Oct 2025 16:52:00 +0800 Subject: [PATCH 055/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9deploy?= =?UTF-8?q?=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-staging.yml | 2 +- .github/workflows/deploy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index ef91f572e..6f03af5ab 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -23,7 +23,7 @@ jobs: host: ${{ secrets.SSH_HOST }} username: root key: ${{ secrets.SSH_KEY }} - script: bash /opt/openisle/OpenIsle/deploy_staging.sh + script: bash /opt/openisle/OpenIsle/deploy/deploy_staging.sh deploy-docs: needs: build-and-deploy diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5a799da8e..288978aff 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -19,4 +19,4 @@ jobs: host: ${{ secrets.SSH_HOST }} username: root key: ${{ secrets.SSH_KEY }} - script: bash /opt/openisle/OpenIsle/deploy.sh + script: bash /opt/openisle/OpenIsle/deploy/deploy.sh From 85a67a6215c53ce32089de69a9563096f19faa37 Mon Sep 17 00:00:00 2001 From: tim Date: Sat, 4 Oct 2025 01:44:56 +0800 Subject: [PATCH 056/105] =?UTF-8?q?fix:=20=E6=9C=AC=E7=AB=99=E8=87=AA?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING.md | 272 +++++++++++++----------------------------------- 1 file changed, 70 insertions(+), 202 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe16759fa..115cbd81b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,25 +1,19 @@ - [前置工作](#前置工作) +- [前端极速调试(Docker 全量环境)](#前端极速调试docker-全量环境) - [启动后端服务](#启动后端服务) - [本地 IDEA](#本地-idea) - [配置环境变量](#配置环境变量) - [配置 IDEA 参数](#配置-idea-参数) - - [配置 MySQL](#配置-mysql) - - [配置 Redis](#配置-redis) - - [配置 RabbitMQ](#配置-rabbitmq) - - [Docker 环境](#docker-环境) - - [配置环境变量](#配置环境变量-1) - - [构建并启动镜像](#构建并启动镜像) - [启动前端服务](#启动前端服务) - - [配置环境变量](#配置环境变量-2) - - [安装依赖和运行](#安装依赖和运行) + - [连接预发或正式环境](#连接预发或正式环境) - [其他配置](#其他配置) - - [配置第三方登录以GitHub为例](#配置第三方登录以GitHub为例) - - [配置Resend邮箱服务](#配置Resend邮箱服务) + - [配置第三方登录以GitHub为例](#配置第三方登录以github为例) + - [配置Resend邮箱服务](#配置resend邮箱服务) - [API文档](#api文档) - [OpenAPI文档](#openapi文档) - [部署时间线以及文档时效性](#部署时间线以及文档时效性) - - [OpenAPI文档使用](#OpenAPI文档使用) - - [OpenAPI文档应用场景](#OpenAPI文档应用场景) + - [OpenAPI文档使用](#openapi文档使用) + - [OpenAPI文档应用场景](#openapi文档应用场景) ## 前置工作 @@ -35,6 +29,35 @@ cd OpenIsle - 前端开发环境 - Node.JS 20+ +## 前端极速调试(Docker 全量环境) + +想要最快速地同时体验前端和后端,可直接使用仓库提供的 Docker Compose。该方案会一次性拉起数据库、消息队列、搜索、后端、WebSocket 以及前端 Dev Server,适合需要全链路联调的场景。 + +1. 准备环境变量文件: + ```shell + cp .env.example .env + ``` + `.env.example` 是模板,可在 `.env` 中按需覆盖如端口、密钥等配置。确保 `NUXT_PUBLIC_API_BASE_URL`、`NUXT_PUBLIC_WEBSOCKET_URL` 等仍指向 `localhost`,方便前端直接访问容器映射端口。 +2. 启动 Dev Profile: + ```shell + docker compose \ + -f docker/docker-compose.yaml \ + --env-file .env \ + --profile dev up -d + ``` + 该命令会创建名为 `frontend_dev` 的容器并运行 `npm run dev`,浏览器访问 http://127.0.0.1:3000 即可查看页面。 +3. 查看服务状态: + ```shell + docker compose -f docker/docker-compose.yaml --env-file .env ps + docker compose -f docker/docker-compose.yaml --env-file .env logs -f frontend_dev + ``` +4. 停止所有容器: + ```shell + docker compose -f docker/docker-compose.yaml --env-file .env --profile dev down + ``` + +如需自定义 Node 依赖缓存、数据库持久化等,可参考 `docker/docker-compose.yaml` 中各卷的定义进行调整。 + ## 启动后端服务 启动后端服务有多种方式,选择一种即可。 @@ -52,37 +75,26 @@ IDEA 打开 `backend/` 文件夹。 #### 配置环境变量 -1. 生成环境变量文件 - +1. 生成环境变量文件: ```shell cp open-isle.env.example open-isle.env ``` + `open-isle.env` 才是实际被读取的文件。可在其中补充数据库、第三方服务等配置,`open-isle.env` 已被 Git 忽略,放心修改。 +2. 在 IDEA 中配置「Environment file」:将 `Run/Debug Configuration` 的 `Environment variables` 指向刚刚复制的 `open-isle.env`,即可让 IDE 读取该文件。 +3. 需要调整端口或功能开关时,优先修改 `open-isle.env`,例如: + ```ini + SERVER_PORT=8081 + LOG_LEVEL=DEBUG + ``` - `open-isle.env.example` 是环境变量模板,`open-isle.env` 才是真正读取的内容 - -2. 修改环境变量,留下需要的,比如你要开发 Google 登录业务,就需要谷歌相关的变量,数据库是一定要的 - - ![环境变量](assets/contributing/backend_img_7.png) - -3. 应用环境文件,选择刚刚的 `open-isle.env` - -可以在 `open-isle.env` 按需填写个性化的配置,该文件不会被 Git 追踪。比如你想把服务跑在 `8082`(默认为 `8080`),那么直接改 `open-isle.env` 即可: - -```ini -SERVER_PORT=8082 -``` - -另一种方式是修改 `.properities` 文件(但不建议),位于 `src/main/application.properties`,该配置同样来源于 `open-isle.env`,但修改 `.properties` 文件会被 Git 追踪。 +也可以修改 `src/main/resources/application.properties`,但该文件会被 Git 追踪,通常不推荐。 ![配置数据库](assets/contributing/backend_img_5.png) #### 配置 IDEA 参数 -- 设置 JDK 版本为 java 17 - -- 设置 VM Option,最好运行在其他端口,非 `8080`,这里设置 `8081` - 若上面在环境变量中设置了端口,那这里就不需要再额外设置 - +- 设置 JDK 版本为 Java 17。 +- 设置 VM Option,最好运行在其他端口(例如 `8081`)。若已经在 `open-isle.env` 中调整端口,可省略此步骤。 ```shell -Dserver.port=8081 ``` @@ -91,145 +103,10 @@ SERVER_PORT=8082 ![配置2](assets/contributing/backend_img_2.png) -#### 配置 MySQL - -> [!TIP] -> 如果不知道怎么配置数据库可以参考 [Docker 环境](#docker-环境) 章节 - -1. 本机配置 MySQL 服务(网上很多教程,忽略) - - 可以用 Laragon,自带 MySQL 包括 Nodejs,版本建议 `6.x`,`7` 以后需要 Lisence - - [下载地址](https://github.com/leokhoa/laragon/releases) - -2. 填写环境变量 - - ![环境变量](assets/contributing/backend_img_6.png) - - ```ini - MYSQL_URL=jdbc:mysql://<数据库地址>:<端口>/<数据库名>?useUnicode=yes&characterEncoding=UTF-8&useInformationSchema=true&useSSL=false&serverTimezone=UTC - MYSQL_USER=<数据库用户名> - MYSQL_PASSWORD=<数据库密码> - ``` - -3. 执行 [`db/init/init_script.sql`](backend/src/main/resources/db/init/init_script.sql) 脚本,导入基本的数据 - 管理员:**admin/123456** - 普通用户1:**user1/123456** - 普通用户2:**user2/123456** - - ![初始化脚本](assets/contributing/resources_img.png) - -#### 配置 Redis - -后端的登录态缓存、访问频控等都依赖 Redis,请确保本地有可用的 Redis 实例。 - -1. **启动 Redis 服务**(已有服务可跳过) - - ```bash - docker run --name openisle-redis -p 6379:6379 -d redis:7-alpine - ``` - - 该命令会在本机暴露 `6379` 端口。若你已有其他端口的 Redis,可以根据实际情况调整映射关系。 - -2. **在 `backend/open-isle.env` 中填写连接信息** - - ```ini - REDIS_HOST=127.0.0.1 - REDIS_PORT=6379 - # 可选:若需要切换逻辑库,可新增此变量,默认使用 0 号库 - REDIS_DATABASE=0 - ``` - - `application.properties` 中的默认值为 `localhost:6379`、数据库 `0`,如果你的环境恰好一致,也可以不额外填写;显式声明可以避免 IDE/运行时读取到意外配置。 - -3. **验证连接** - - ```bash - redis-cli -h 127.0.0.1 -p 6379 ping - ``` - - 启动后端后,日志中会出现 `Redis connection established ...`(来自 `RedisConnectionLogger`),说明已成功连通。 - -#### 配置 RabbitMQ - -消息通知和 WebSocket 推送链路依赖 RabbitMQ。后端会自动声明交换机与队列,确保本地 RabbitMQ 可用即可。 - -1. **启动 RabbitMQ 服务**(推荐包含管理界面) - - ```bash - docker run --name openisle-rabbitmq \ - -e RABBITMQ_DEFAULT_USER=openisle \ - -e RABBITMQ_DEFAULT_PASS=openisle \ - -p 5672:5672 -p 15672:15672 \ - -d rabbitmq:3.13-management - ``` - - 管理界面位于 http://127.0.0.1:15672 ,可用于查看队列、交换机等资源。 - -2. **同步填写后端与 WebSocket 服务的环境变量** - - ```ini - # backend/open-isle.env - RABBITMQ_HOST=127.0.0.1 - RABBITMQ_PORT=5672 - RABBITMQ_USERNAME=openisle - RABBITMQ_PASSWORD=openisle - - # 如果需要启动 websocket_service,也需要在 websocket_service.env 中保持一致 - ``` - - 如果沿用 RabbitMQ 默认的 `guest/guest`,可以不显式设置,Spring Boot 会回退到 `application.properties` 中的默认值 (`localhost:5672`、`guest/guest`、虚拟主机 `/`)。 - -3. **确认自动声明的资源** - - - 交换机:`openisle-exchange` - - 旧版兼容队列:`notifications-queue` - - 分片队列:`notifications-queue-0` ~ `notifications-queue-f`(共 16 个,对应路由键 `notifications.shard.0` ~ `notifications.shard.f`) - - 队列持久化默认开启,来自 `rabbitmq.queue.durable=true`,如需仅在本地短暂测试,可在 `application.properties` 中调整该配置。 - - 启动后端时可在日志中看到 `=== 开始主动声明 RabbitMQ 组件 ===` 与后续的声明结果,也可以在管理界面中查看是否创建成功。 - -完成 Redis 与 RabbitMQ 配置后,即可继续启动后端服务。 +完成环境变量和运行参数设置后,即可启动 Spring Boot 应用。 ![运行画面](assets/contributing/backend_img_4.png) -### Docker 环境 - -#### 配置环境变量 - -```shell -cd docker/ -``` - -主要配置两个 `.env` 文件 - -- `backend/open-isle.env`:后端环境变量,配置同上,见 [配置环境变量](#配置环境变量)。 -- `docker/.env`:Docker Compose 环境变量,主要配置 MySQL 相关 - ```shell - cp .env.example .env - ``` - -> [!TIP] -> 使用单独的 `.env` 文件是为了兼容线上环境或已启用 MySQL 服务的情况,如果只是想快速体验或者启动统一的环境,则推荐使用本方式。 - -在指定 `docker/.env` 后,`backend/open-isle.env` 中以下配置会被覆盖,这样就确保使用了同一份配置。 - -```ini -MYSQL_URL= -MYSQL_USER= -MYSQL_PASSWORD= -``` - -#### 构建并启动镜像 - -```shell -docker compose up -d -``` - -如果想了解启动过程发生了什么可以查看日志 - -```shell -docker compose logs -``` - ## 启动前端服务 > [!IMPORTANT] @@ -239,43 +116,34 @@ docker compose logs cd frontend_nuxt/ ``` -### 配置环境变量 - -前端可以依赖本机部署的后端,也可以直接调用线上的后端接口。 - -- 利用预发环境:**(⚠️ 强烈推荐只开发前端的朋友使用该环境)** - - ```shell - cp .env.staging.example .env - ``` - -- 利用生产环境 - - ```shell - cp .env.production.example .env - ``` - -- 利用本地环境 - - ```shell - cp .env.dev.example .env - ``` - -若依赖本机部署的后端,需要修改 `.env` 中的 `NUXT_PUBLIC_API_BASE_URL` 值与后端服务端口一致 - -### 安装依赖和运行 - -前端安装依赖并启动服务。 +安装依赖并启动开发服务器: ```shell -# 安装依赖 npm install --verbose - -# 运行前端服务 npm run dev ``` -如此一来,浏览器访问 http://127.0.0.1:3000 即可访问前端页面。 +默认情况下,浏览器访问 http://127.0.0.1:3000 即可访问前端页面。 + +### 连接预发或正式环境 + +前端默认读取 `.env` 中的接口地址,可通过修改以下变量快速切换到预发或正式环境: + +1. 复制对应模板: + ```shell + # 使用预发配置 + cp .env.staging.example .env + + # 或者使用正式环境配置 + cp .env.production.example .env + ``` +2. 按需覆盖关键变量: + ```ini + NUXT_PUBLIC_API_BASE_URL=https://www.staging.open-isle.com + NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com + ``` + 将 `staging` 替换为 `www` 即可连接正式环境。其他变量(如 OAuth Client ID、站点地址等)可根据需求调整。 +3. 已经存在 `.env` 时,可直接编辑上述变量并重启 `npm run dev` 让配置生效。 ## 其他配置 @@ -334,7 +202,7 @@ https://docs.open-isle.com ### OpenAPI文档使用 -- 预发环境/正式环境切换,可以通过如下位置切换API环境 +- 预发环境/正式环境切换,以通过如下位置切换API环境 ![CleanShot 2025-09-10 at 12 .08.00@2x.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/f9fb7a0f020d4a0e94159d7820783224.png) From 536979501e6e62859b70d64884659bd1be2bd359 Mon Sep 17 00:00:00 2001 From: tim Date: Sat, 4 Oct 2025 01:53:19 +0800 Subject: [PATCH 057/105] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING.md | 54 ++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 115cbd81b..dd3275e6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,24 +34,33 @@ cd OpenIsle 想要最快速地同时体验前端和后端,可直接使用仓库提供的 Docker Compose。该方案会一次性拉起数据库、消息队列、搜索、后端、WebSocket 以及前端 Dev Server,适合需要全链路联调的场景。 1. 准备环境变量文件: + ```shell cp .env.example .env ``` + `.env.example` 是模板,可在 `.env` 中按需覆盖如端口、密钥等配置。确保 `NUXT_PUBLIC_API_BASE_URL`、`NUXT_PUBLIC_WEBSOCKET_URL` 等仍指向 `localhost`,方便前端直接访问容器映射端口。 + 2. 启动 Dev Profile: + ```shell docker compose \ -f docker/docker-compose.yaml \ --env-file .env \ --profile dev up -d ``` + 该命令会创建名为 `frontend_dev` 的容器并运行 `npm run dev`,浏览器访问 http://127.0.0.1:3000 即可查看页面。 + 3. 查看服务状态: + ```shell docker compose -f docker/docker-compose.yaml --env-file .env ps docker compose -f docker/docker-compose.yaml --env-file .env logs -f frontend_dev ``` + 4. 停止所有容器: + ```shell docker compose -f docker/docker-compose.yaml --env-file .env --profile dev down ``` @@ -75,13 +84,8 @@ IDEA 打开 `backend/` 文件夹。 #### 配置环境变量 -1. 生成环境变量文件: - ```shell - cp open-isle.env.example open-isle.env - ``` - `open-isle.env` 才是实际被读取的文件。可在其中补充数据库、第三方服务等配置,`open-isle.env` 已被 Git 忽略,放心修改。 -2. 在 IDEA 中配置「Environment file」:将 `Run/Debug Configuration` 的 `Environment variables` 指向刚刚复制的 `open-isle.env`,即可让 IDE 读取该文件。 -3. 需要调整端口或功能开关时,优先修改 `open-isle.env`,例如: +1. 在 IDEA 中配置「Environment file」:将 `Run/Debug Configuration` 的 `Environment variables` 指向刚刚复制的 `.env`,即可让 IDE 读取该文件。 +2. 需要调整端口或功能开关时,优先修改 `.env`,例如: ```ini SERVER_PORT=8081 LOG_LEVEL=DEBUG @@ -89,7 +93,7 @@ IDEA 打开 `backend/` 文件夹。 也可以修改 `src/main/resources/application.properties`,但该文件会被 Git 追踪,通常不推荐。 -![配置数据库](assets/contributing/backend_img_5.png) +![backend_img_5.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/89658e5d5c0443a5939ef57ccfeab740.png) #### 配置 IDEA 参数 @@ -99,13 +103,13 @@ IDEA 打开 `backend/` 文件夹。 -Dserver.port=8081 ``` -![配置1](assets/contributing/backend_img_3.png) +![backend_img_3.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/6905f7cd4b694b1fa7214dd2b3ef9d81.png) -![配置2](assets/contributing/backend_img_2.png) +![backend_img_2.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/2bf8e139c21f4b529384ed68cef053da.png) 完成环境变量和运行参数设置后,即可启动 Spring Boot 应用。 -![运行画面](assets/contributing/backend_img_4.png) +![backend_img_4.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/474d995ddda34b6f80badffa58cec5b9.png) ## 启动前端服务 @@ -130,6 +134,7 @@ npm run dev 前端默认读取 `.env` 中的接口地址,可通过修改以下变量快速切换到预发或正式环境: 1. 复制对应模板: + ```shell # 使用预发配置 cp .env.staging.example .env @@ -137,12 +142,16 @@ npm run dev # 或者使用正式环境配置 cp .env.production.example .env ``` + 2. 按需覆盖关键变量: + ```ini NUXT_PUBLIC_API_BASE_URL=https://www.staging.open-isle.com NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com ``` + 将 `staging` 替换为 `www` 即可连接正式环境。其他变量(如 OAuth Client ID、站点地址等)可根据需求调整。 + 3. 已经存在 `.env` 时,可直接编辑上述变量并重启 `npm run dev` 让配置生效。 ## 其他配置 @@ -151,42 +160,41 @@ npm run dev - 修改 `application.properties` 配置 - ![后端配置](assets/contributing/backend_img.png) + ![backend_img.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/1dbb388cd1004e1d8822224cf87c9303.png) - 修改 `.env` 配置 - ![前端](assets/contributing/fontend_img.png) + ![fontend_img.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/3cc276e4795a407a90a47f2d77de2760.png) - 配置第三方登录回调地址 - ![github配置](assets/contributing/github_img.png) + ![github_img.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/14667457a15c4fbea9d91226797b7b59.png) - ![github配置2](assets/contributing/github_img_2.png) + ![github_img_2.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/7e901e1c648f4330be1d248379d699f1.png) ### 配置Resend邮箱服务 https://resend.com/emails 创建账号并登录 - `Domains` -> `Add Domain` - ![image-20250906150459400](assets/contributing/image-20250906150459400.png) - + ![image-20250906150459400.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/0168a039ca5e47239ab859f6049c4c93.png) - 填写域名 - ![image-20250906150541817](assets/contributing/image-20250906150541817.png) - + ![image-20250906150541817.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/d328cd89f21c4b8fb50c8213c9b784f0.png) - 等待一段时间后解析成功,创建 key `API Keys` -> `Create API Key`,输入名称,设置 `Permission` 为 `Sending access` **Key 只能查看一次,务必保存下来** - ![image-20250906150811572](assets/contributing/image-20250906150811572.png) - ![image-20250906150924975](assets/contributing/image-20250906150924975.png) - ![image-20250906150944130](assets/contributing/image-20250906150944130.png) + ![image-20250906150811572.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/d72959670b22451cb93e94d6e8316e05.png) + ![image-20250906150924975.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/b46f1a56f2d744a381d361ac8369a231.png) + ![image-20250906150944130.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/8eaba3fa791f4b10a5c24948812d306d.png) - 修改 `.env` 配置中的 `RESEND_API_KEY` 和 `RESEND_FROM_EMAIL` `RESEND_FROM_EMAIL`: **noreply@域名** `RESEND_API_KEY`:**刚刚复制的 Key** - ![image-20250906151218330](assets/contributing/image-20250906151218330.png) + ![image-20250906151218330.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/8319dc5037bb40b68ed35358d810552a.png) ## API文档 ### OpenAPI文档 + https://docs.open-isle.com ### 部署时间线以及文档时效性 From 024e52b7638d111ec94a97aaef83485c632334c0 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Sat, 4 Oct 2025 02:01:32 +0800 Subject: [PATCH 058/105] docs: update docker compose dev instructions --- CONTRIBUTING.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd3275e6c..7a9e9dbcf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,6 +43,13 @@ cd OpenIsle 2. 启动 Dev Profile: + ```shell + docker compose \ + -f docker/docker-compose.yaml \ + --env-file .env \ + --profile dev build + ``` + ```shell docker compose \ -f docker/docker-compose.yaml \ @@ -52,6 +59,15 @@ cd OpenIsle 该命令会创建名为 `frontend_dev` 的容器并运行 `npm run dev`,浏览器访问 http://127.0.0.1:3000 即可查看页面。 + 如需强制重新创建所有容器,可执行: + + ```shell + docker compose \ + -f docker/docker-compose.yaml \ + --env-file .env \ + --profile dev up -d --force-recreate + ``` + 3. 查看服务状态: ```shell From 55b680ef83aa5b9c561741ad23f3dc9a0670bbe5 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Sat, 4 Oct 2025 02:02:43 +0800 Subject: [PATCH 059/105] Update CONTRIBUTING.md --- CONTRIBUTING.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a9e9dbcf..af42f9c11 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,7 @@ cd OpenIsle 该命令会创建名为 `frontend_dev` 的容器并运行 `npm run dev`,浏览器访问 http://127.0.0.1:3000 即可查看页面。 - 如需强制重新创建所有容器,可执行: + 修改代码后,可以强制重新创建所有容器,执行: ```shell docker compose \ @@ -149,17 +149,7 @@ npm run dev 前端默认读取 `.env` 中的接口地址,可通过修改以下变量快速切换到预发或正式环境: -1. 复制对应模板: - - ```shell - # 使用预发配置 - cp .env.staging.example .env - - # 或者使用正式环境配置 - cp .env.production.example .env - ``` - -2. 按需覆盖关键变量: +1. 按需覆盖关键变量: ```ini NUXT_PUBLIC_API_BASE_URL=https://www.staging.open-isle.com @@ -168,7 +158,7 @@ npm run dev 将 `staging` 替换为 `www` 即可连接正式环境。其他变量(如 OAuth Client ID、站点地址等)可根据需求调整。 -3. 已经存在 `.env` 时,可直接编辑上述变量并重启 `npm run dev` 让配置生效。 +2. 已经存在 `.env` 时,可直接编辑上述变量并重启 `npm run dev` 让配置生效。 ## 其他配置 From 1d31284dba58482bd397b9fc3e397141e2ccc04c Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Sat, 4 Oct 2025 09:13:34 +0900 Subject: [PATCH 060/105] =?UTF-8?q?1.=E4=BF=AE=E5=A4=8D=E7=BD=AE=E9=A1=B6?= =?UTF-8?q?=E5=9B=BE=E6=A0=87=E4=B8=8D=E6=98=BE=E7=A4=BA=202.=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=8F=96=E6=B6=88=E7=BD=AE=E9=A1=B6=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/pages/posts/[id]/index.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend_nuxt/pages/posts/[id]/index.vue b/frontend_nuxt/pages/posts/[id]/index.vue index cbd70f071..8fd467b82 100644 --- a/frontend_nuxt/pages/posts/[id]/index.vue +++ b/frontend_nuxt/pages/posts/[id]/index.vue @@ -366,7 +366,11 @@ const changeLogIcon = (l) => { return 'unlock' } } else if (l.type === 'PINNED') { - return 'pin-icon' + if(l.newPinnedAt){ + return 'pin' + }else{ + return 'clear-icon' + } } else if (l.type === 'FEATURED') { if (l.newFeatured) { return 'star' From f36ed28185c930cdee25eb6ae455065a685b43d8 Mon Sep 17 00:00:00 2001 From: tim Date: Sat, 4 Oct 2025 12:26:38 +0800 Subject: [PATCH 061/105] =?UTF-8?q?Revert=20"fix:=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E8=B5=84=E6=BA=90=E5=9B=BE=E7=89=87"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 536979501e6e62859b70d64884659bd1be2bd359. # Conflicts: # CONTRIBUTING.md --- CONTRIBUTING.md | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index af42f9c11..ceca93510 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,15 +34,11 @@ cd OpenIsle 想要最快速地同时体验前端和后端,可直接使用仓库提供的 Docker Compose。该方案会一次性拉起数据库、消息队列、搜索、后端、WebSocket 以及前端 Dev Server,适合需要全链路联调的场景。 1. 准备环境变量文件: - ```shell cp .env.example .env ``` - `.env.example` 是模板,可在 `.env` 中按需覆盖如端口、密钥等配置。确保 `NUXT_PUBLIC_API_BASE_URL`、`NUXT_PUBLIC_WEBSOCKET_URL` 等仍指向 `localhost`,方便前端直接访问容器映射端口。 - 2. 启动 Dev Profile: - ```shell docker compose \ -f docker/docker-compose.yaml \ @@ -56,7 +52,6 @@ cd OpenIsle --env-file .env \ --profile dev up -d ``` - 该命令会创建名为 `frontend_dev` 的容器并运行 `npm run dev`,浏览器访问 http://127.0.0.1:3000 即可查看页面。 修改代码后,可以强制重新创建所有容器,执行: @@ -69,14 +64,11 @@ cd OpenIsle ``` 3. 查看服务状态: - ```shell docker compose -f docker/docker-compose.yaml --env-file .env ps docker compose -f docker/docker-compose.yaml --env-file .env logs -f frontend_dev ``` - 4. 停止所有容器: - ```shell docker compose -f docker/docker-compose.yaml --env-file .env --profile dev down ``` @@ -100,8 +92,13 @@ IDEA 打开 `backend/` 文件夹。 #### 配置环境变量 -1. 在 IDEA 中配置「Environment file」:将 `Run/Debug Configuration` 的 `Environment variables` 指向刚刚复制的 `.env`,即可让 IDE 读取该文件。 -2. 需要调整端口或功能开关时,优先修改 `.env`,例如: +1. 生成环境变量文件: + ```shell + cp open-isle.env.example open-isle.env + ``` + `open-isle.env` 才是实际被读取的文件。可在其中补充数据库、第三方服务等配置,`open-isle.env` 已被 Git 忽略,放心修改。 +2. 在 IDEA 中配置「Environment file」:将 `Run/Debug Configuration` 的 `Environment variables` 指向刚刚复制的 `open-isle.env`,即可让 IDE 读取该文件。 +3. 需要调整端口或功能开关时,优先修改 `open-isle.env`,例如: ```ini SERVER_PORT=8081 LOG_LEVEL=DEBUG @@ -109,7 +106,7 @@ IDEA 打开 `backend/` 文件夹。 也可以修改 `src/main/resources/application.properties`,但该文件会被 Git 追踪,通常不推荐。 -![backend_img_5.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/89658e5d5c0443a5939ef57ccfeab740.png) +![配置数据库](assets/contributing/backend_img_5.png) #### 配置 IDEA 参数 @@ -119,13 +116,13 @@ IDEA 打开 `backend/` 文件夹。 -Dserver.port=8081 ``` -![backend_img_3.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/6905f7cd4b694b1fa7214dd2b3ef9d81.png) +![配置1](assets/contributing/backend_img_3.png) -![backend_img_2.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/2bf8e139c21f4b529384ed68cef053da.png) +![配置2](assets/contributing/backend_img_2.png) 完成环境变量和运行参数设置后,即可启动 Spring Boot 应用。 -![backend_img_4.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/474d995ddda34b6f80badffa58cec5b9.png) +![运行画面](assets/contributing/backend_img_4.png) ## 启动前端服务 @@ -155,7 +152,6 @@ npm run dev NUXT_PUBLIC_API_BASE_URL=https://www.staging.open-isle.com NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com ``` - 将 `staging` 替换为 `www` 即可连接正式环境。其他变量(如 OAuth Client ID、站点地址等)可根据需求调整。 2. 已经存在 `.env` 时,可直接编辑上述变量并重启 `npm run dev` 让配置生效。 @@ -166,41 +162,42 @@ npm run dev - 修改 `application.properties` 配置 - ![backend_img.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/1dbb388cd1004e1d8822224cf87c9303.png) + ![后端配置](assets/contributing/backend_img.png) - 修改 `.env` 配置 - ![fontend_img.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/3cc276e4795a407a90a47f2d77de2760.png) + ![前端](assets/contributing/fontend_img.png) - 配置第三方登录回调地址 - ![github_img.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/14667457a15c4fbea9d91226797b7b59.png) + ![github配置](assets/contributing/github_img.png) - ![github_img_2.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/7e901e1c648f4330be1d248379d699f1.png) + ![github配置2](assets/contributing/github_img_2.png) ### 配置Resend邮箱服务 https://resend.com/emails 创建账号并登录 - `Domains` -> `Add Domain` - ![image-20250906150459400.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/0168a039ca5e47239ab859f6049c4c93.png) + ![image-20250906150459400](assets/contributing/image-20250906150459400.png) + - 填写域名 - ![image-20250906150541817.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/d328cd89f21c4b8fb50c8213c9b784f0.png) + ![image-20250906150541817](assets/contributing/image-20250906150541817.png) + - 等待一段时间后解析成功,创建 key `API Keys` -> `Create API Key`,输入名称,设置 `Permission` 为 `Sending access` **Key 只能查看一次,务必保存下来** - ![image-20250906150811572.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/d72959670b22451cb93e94d6e8316e05.png) - ![image-20250906150924975.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/b46f1a56f2d744a381d361ac8369a231.png) - ![image-20250906150944130.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/8eaba3fa791f4b10a5c24948812d306d.png) + ![image-20250906150811572](assets/contributing/image-20250906150811572.png) + ![image-20250906150924975](assets/contributing/image-20250906150924975.png) + ![image-20250906150944130](assets/contributing/image-20250906150944130.png) - 修改 `.env` 配置中的 `RESEND_API_KEY` 和 `RESEND_FROM_EMAIL` `RESEND_FROM_EMAIL`: **noreply@域名** `RESEND_API_KEY`:**刚刚复制的 Key** - ![image-20250906151218330.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/8319dc5037bb40b68ed35358d810552a.png) + ![image-20250906151218330](assets/contributing/image-20250906151218330.png) ## API文档 ### OpenAPI文档 - https://docs.open-isle.com ### 部署时间线以及文档时效性 From a83ddc40fe2949cbc94272e827914ff3f9e16fc1 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Sat, 4 Oct 2025 12:46:19 +0800 Subject: [PATCH 062/105] Update CONTRIBUTING.md --- CONTRIBUTING.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ceca93510..f003d5708 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -124,25 +124,7 @@ IDEA 打开 `backend/` 文件夹。 ![运行画面](assets/contributing/backend_img_4.png) -## 启动前端服务 - -> [!IMPORTANT] -> **⚠️ 环境要求:Node.js 版本最低 20.0.0(因为 Nuxt 框架要求)** - -```shell -cd frontend_nuxt/ -``` - -安装依赖并启动开发服务器: - -```shell -npm install --verbose -npm run dev -``` - -默认情况下,浏览器访问 http://127.0.0.1:3000 即可访问前端页面。 - -### 连接预发或正式环境 +## 前端连接预发或正式环境 前端默认读取 `.env` 中的接口地址,可通过修改以下变量快速切换到预发或正式环境: @@ -154,7 +136,6 @@ npm run dev ``` 将 `staging` 替换为 `www` 即可连接正式环境。其他变量(如 OAuth Client ID、站点地址等)可根据需求调整。 -2. 已经存在 `.env` 时,可直接编辑上述变量并重启 `npm run dev` 让配置生效。 ## 其他配置 From 4456997573b91813624e98a7c08a546e28a47af0 Mon Sep 17 00:00:00 2001 From: tim Date: Sat, 4 Oct 2025 14:52:42 +0800 Subject: [PATCH 063/105] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3GitHub?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index d3466c33d..270122d71 100644 --- a/.env.example +++ b/.env.example @@ -93,7 +93,7 @@ NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 # 线上 & 本地均可使用 NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com # 线上 -NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN +NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ # 本地 # NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN # 线上 & 本地均可使用 From 3e255c1288b02be260fd31c7399da7f72a973260 Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Sat, 4 Oct 2025 17:13:00 +0900 Subject: [PATCH 064/105] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E7=BD=91=E9=A1=B5iframe=E7=9A=84=E8=A7=86=E9=A2=91=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E8=B6=85=E5=87=BA=E5=AE=B9=E5=99=A8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/assets/global.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend_nuxt/assets/global.css b/frontend_nuxt/assets/global.css index f46edefbf..fb12189e0 100644 --- a/frontend_nuxt/assets/global.css +++ b/frontend_nuxt/assets/global.css @@ -341,6 +341,16 @@ body { .info-content-text pre { line-height: 1.5; } + + /*处理iframe视频标签*/ + .info-content-text iframe { + width: 100%; + max-width: 100%; + height: auto; + aspect-ratio: 16 / 9; /* 保持 16:9 比例 */ + border: none; + display: block; + } .d2h-file-name { font-size: 14px !important; From da0d26c8b57062cf2cf5f1d15be53271d95bff12 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Sun, 5 Oct 2025 17:21:45 +0800 Subject: [PATCH 065/105] Ensure pinned comments stay at top of post timeline --- .../controller/CommentController.java | 30 +++++++++++++++++-- .../com/openisle/dto/TimelineItemDto.java | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/openisle/controller/CommentController.java b/backend/src/main/java/com/openisle/controller/CommentController.java index d8fbe17ea..5de15888d 100644 --- a/backend/src/main/java/com/openisle/controller/CommentController.java +++ b/backend/src/main/java/com/openisle/controller/CommentController.java @@ -15,6 +15,7 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -131,6 +132,7 @@ public class CommentController { c.getId(), "comment", c.getCreatedAt(), + c.getPinnedAt(), c // payload 是 CommentDto ) ) @@ -145,17 +147,39 @@ public class CommentController { l.getId(), "log", l.getTime(), // 注意字段名不一样 + null, l // payload 是 PostChangeLogDto ) ) .toList() ); // 排序 - Comparator> comparator = Comparator.comparing(TimelineItemDto::getCreatedAt); + Comparator> pinnedOrderComparator = (a, b) -> { + LocalDateTime aPinned = a.getPinnedAt(); + LocalDateTime bPinned = b.getPinnedAt(); + if (aPinned == null && bPinned == null) { + return 0; + } + if (aPinned == null) { + return 1; + } + if (bPinned == null) { + return -1; + } + return bPinned.compareTo(aPinned); + }; + + Comparator> comparator = Comparator., Boolean>comparing( + item -> item.getPinnedAt() == null + ).thenComparing(pinnedOrderComparator); + + Comparator> createdAtComparator = Comparator.comparing( + TimelineItemDto::getCreatedAt + ); if (CommentSort.NEWEST.equals(sort)) { - comparator = comparator.reversed(); + createdAtComparator = createdAtComparator.reversed(); } - itemDtoList.sort(comparator); + itemDtoList.sort(comparator.thenComparing(createdAtComparator)); log.debug("listComments returning {} comments", itemDtoList.size()); return itemDtoList; } diff --git a/backend/src/main/java/com/openisle/dto/TimelineItemDto.java b/backend/src/main/java/com/openisle/dto/TimelineItemDto.java index d492e3181..52daaf5fa 100644 --- a/backend/src/main/java/com/openisle/dto/TimelineItemDto.java +++ b/backend/src/main/java/com/openisle/dto/TimelineItemDto.java @@ -15,5 +15,6 @@ public class TimelineItemDto { private Long id; private String kind; // "comment" | "log" private LocalDateTime createdAt; + private LocalDateTime pinnedAt; private T payload; // 泛型,具体类型由外部决定 } From 67bbe832a00fe03f723967825c33fb7de418ad12 Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 5 Oct 2025 17:43:45 +0800 Subject: [PATCH 066/105] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0GitHub?= =?UTF-8?q?=E9=A2=84=E5=8F=91=E7=8E=AF=E5=A2=83=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 270122d71..a470b902b 100644 --- a/.env.example +++ b/.env.example @@ -90,10 +90,12 @@ NUXT_PUBLIC_WEBSOCKET_URL=http://localhost:8082 # NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 -# 线上 & 本地均可使用 +# 线上 & 测试 (www.staging.open-isle.com) & 本地均可使用 NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com # 线上 NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ +# 测试环境 (www.staging.open-isle.com) +NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23li6GHPxx4MwipWnM # 本地 # NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN # 线上 & 本地均可使用 From 181237adee997a19f3591a5c7c6909e7fc043bcc Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 5 Oct 2025 17:45:40 +0800 Subject: [PATCH 067/105] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0GitHub?= =?UTF-8?q?=E9=A2=84=E5=8F=91=E7=8E=AF=E5=A2=83=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index a470b902b..964c34860 100644 --- a/.env.example +++ b/.env.example @@ -95,7 +95,8 @@ NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps. # 线上 NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ # 测试环境 (www.staging.open-isle.com) -NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23li6GHPxx4MwipWnM +# NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23li6GHPxx4MwipWnM + # 本地 # NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN # 线上 & 本地均可使用 From fa8ee113a21d9715f3659df2c1c7da7efa66b64f Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 5 Oct 2025 18:05:45 +0800 Subject: [PATCH 068/105] =?UTF-8?q?fix:=20=E8=AE=BE=E7=BD=AEtelegram?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 964c34860..a8a125a3d 100644 --- a/.env.example +++ b/.env.example @@ -80,15 +80,21 @@ WEBPUSH_PRIVATE_KEY= LOG_LEVEL=INFO # === Frontend (Nuxt) === - +# 本地开发 NUXT_PUBLIC_API_BASE_URL=http://localhost:8080 +# 线上环境 # NUXT_PUBLIC_API_BASE_URL=https://www.open-isle.com +# 测试环境 # NUXT_PUBLIC_API_BASE_URL=https://www.staging.open-isle.com +# 本地开发 NUXT_PUBLIC_WEBSOCKET_URL=http://localhost:8082 +# 线上环境 # NUXT_PUBLIC_WEBSOCKET_URL=https://www.open-isle.com +# 测试环境 # NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com +# 本地开发 NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 # 线上 & 测试 (www.staging.open-isle.com) & 本地均可使用 NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com @@ -96,13 +102,17 @@ NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps. NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ # 测试环境 (www.staging.open-isle.com) # NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23li6GHPxx4MwipWnM - # 本地 # NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN + # 线上 & 本地均可使用 NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779 + # 线上 & 本地均可使用 NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ + # 线上 NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135 +# 测试环境 (www.staging.open-isle.com) +# NUXT_PUBLIC_TELEGRAM_BOT_ID=7832207011 From 76b65a14009e63eb611e82d8299894a5bf4bd1f5 Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Sun, 5 Oct 2025 21:08:05 +0900 Subject: [PATCH 069/105] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=BB=E9=A1=B5?= =?UTF-8?q?=E5=90=84section=E9=97=B4=E8=B7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/pages/users/[id].vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend_nuxt/pages/users/[id].vue b/frontend_nuxt/pages/users/[id].vue index 67232896c..d566046cd 100644 --- a/frontend_nuxt/pages/users/[id].vue +++ b/frontend_nuxt/pages/users/[id].vue @@ -849,7 +849,8 @@ watch(selectedTab, async (val) => { display: flex; flex-direction: column; padding: 20px; - gap: 20px; + row-gap: 40px; /* 行间距 */ + column-gap: 20px; /* 列间距 */ } .summary-title { @@ -888,10 +889,10 @@ watch(selectedTab, async (val) => { } .summary-divider { - margin-top: 20px; display: flex; flex-direction: row; - gap: 20px; + row-gap: 40px; /* 行间距 */ + column-gap: 20px; /* 列间距 */ width: 100%; flex-wrap: wrap; } From d7d2fd5dcbf6a9c9f0cebdf55c430b226ad78c6d Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 7 Oct 2025 15:58:18 +0800 Subject: [PATCH 070/105] =?UTF-8?q?fix:=20=E4=BB=85=E6=94=AF=E9=85=8Dwebso?= =?UTF-8?q?cket=E6=89=93=E5=A4=B4=E5=9F=9F=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nginx/openisle | 48 ++---------------------------------------------- 1 file changed, 2 insertions(+), 46 deletions(-) diff --git a/nginx/openisle b/nginx/openisle index 2ee2fe9fb..612242399 100644 --- a/nginx/openisle +++ b/nginx/openisle @@ -18,51 +18,6 @@ server { add_header X-Upstream $upstream_addr always; } - location ^~ /api/ws { - proxy_pass http://127.0.0.1:8080; - proxy_http_version 1.1; - - # 升级所需 - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - - # 统一透传这些头(你在 /api/ 有,/api/ws 也要有) - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $host; - - proxy_read_timeout 300s; - proxy_send_timeout 300s; - proxy_buffering off; - proxy_cache off; - } - - # 2) SockJS(包含 /info、/iframe.html、/.../websocket 等) - location ^~ /api/sockjs { - proxy_pass http://127.0.0.1:8080; - proxy_http_version 1.1; - - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $host; - - proxy_read_timeout 300s; - proxy_send_timeout 300s; - proxy_buffering off; - proxy_cache off; - - # 如要同源 iframe 回退,下面两行二选一(或者交给 Spring Security 的 sameOrigin) - # proxy_hide_header X-Frame-Options; - # add_header X-Frame-Options "SAMEORIGIN" always; - } - location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_http_version 1.1; @@ -148,7 +103,7 @@ server { # ---------- WEBSOCKET GATEWAY TO :8082 ---------- location ^~ /websocket/ { - proxy_pass http://127.0.0.1:8082/; + proxy_pass http://127.0.0.1:8084/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; @@ -167,6 +122,7 @@ server { add_header Cache-Control "no-store" always; } } + server { listen 80; server_name open-isle.com www.open-isle.com; From e7f06787d25737691a319b20573979f6b46b17f6 Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 7 Oct 2025 15:59:23 +0800 Subject: [PATCH 071/105] =?UTF-8?q?fix:=20=E4=BB=85=E6=94=AF=E9=85=8Dwebso?= =?UTF-8?q?cket=E6=89=93=E5=A4=B4=E5=9F=9F=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index a8a125a3d..e042d515b 100644 --- a/.env.example +++ b/.env.example @@ -90,9 +90,9 @@ NUXT_PUBLIC_API_BASE_URL=http://localhost:8080 # 本地开发 NUXT_PUBLIC_WEBSOCKET_URL=http://localhost:8082 # 线上环境 -# NUXT_PUBLIC_WEBSOCKET_URL=https://www.open-isle.com +# NUXT_PUBLIC_WEBSOCKET_URL=https://www.open-isle.com/websocket # 测试环境 -# NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com +# NUXT_PUBLIC_WEBSOCKET_URL=https://www.staging.open-isle.com/websocket # 本地开发 NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000 From 343c4d3793019c2b89a486387d4b6b101420fc44 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:29:53 +0800 Subject: [PATCH 072/105] Add empty state to dropdown when no search results --- frontend_nuxt/components/Dropdown.vue | 93 +++++++++++++++------------ 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/frontend_nuxt/components/Dropdown.vue b/frontend_nuxt/components/Dropdown.vue index 0486041b8..6212529e5 100644 --- a/frontend_nuxt/components/Dropdown.vue +++ b/frontend_nuxt/components/Dropdown.vue @@ -62,26 +62,29 @@
@@ -99,26 +102,29 @@ @@ -384,6 +390,13 @@ export default { padding: 10px 0; } +.dropdown-empty { + padding: 20px; + text-align: center; + color: var(--muted-text-color, #8c8c8c); + font-size: 14px; +} + .dropdown-mobile-page { position: fixed; top: 0; From 35340319c68788c01d5d20940fde72f8a45e56ed Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 7 Oct 2025 16:38:04 +0800 Subject: [PATCH 073/105] =?UTF-8?q?fix:=20=E9=99=90=E5=AE=9Aprofile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index fb6d09cd0..56afacd53 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -226,6 +226,8 @@ services: websocket-service: condition: service_healthy restart: unless-stopped + profiles: ["staging", "prod"] + loopback_8080: image: alpine/socat From bbb0a11d49ba7ad7036283796a1b7422e0aa987b Mon Sep 17 00:00:00 2001 From: tim Date: Tue, 7 Oct 2025 18:00:37 +0800 Subject: [PATCH 074/105] =?UTF-8?q?fix:=20searchdropdown=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=A9=BAstate=E3=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/components/Dropdown.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend_nuxt/components/Dropdown.vue b/frontend_nuxt/components/Dropdown.vue index 6212529e5..a5bf3bed9 100644 --- a/frontend_nuxt/components/Dropdown.vue +++ b/frontend_nuxt/components/Dropdown.vue @@ -49,7 +49,11 @@
Date: Tue, 7 Oct 2025 18:02:06 +0800 Subject: [PATCH 075/105] =?UTF-8?q?Revert=20"feat:=20=E5=85=88=E6=8A=8A?= =?UTF-8?q?=E6=AF=8F=E6=97=A5=E5=AE=9A=E6=97=B6=E6=9E=84=E4=BB=B6=E7=BB=99?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=8E=89"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0119605649962cd430b8b73dad9eef9c789f0709. --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 288978aff..b477cd771 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,8 +2,8 @@ name: CI & CD on: workflow_dispatch: - # schedule: - # - cron: "0 19 * * *" # 每天 UTC 19:00,相当于北京时间凌晨3点 + schedule: + - cron: "0 19 * * *" # 每天 UTC 19:00,相当于北京时间凌晨3点 jobs: build-and-deploy: From 9957042746d000f19d74cdba4be6c3c4977ff0d7 Mon Sep 17 00:00:00 2001 From: immortal521 Date: Wed, 8 Oct 2025 01:19:38 +0800 Subject: [PATCH 076/105] fix: theme-toggle-flicker - remove unnecessary await nextTick in view transition - Simplify transition callback - Add fill: 'both' to transition style --- frontend_nuxt/utils/theme.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend_nuxt/utils/theme.js b/frontend_nuxt/utils/theme.js index 48c315efb..c0a384858 100644 --- a/frontend_nuxt/utils/theme.js +++ b/frontend_nuxt/utils/theme.js @@ -93,9 +93,8 @@ function getCircle(event) { function withViewTransition(event, applyFn, direction = true) { if (typeof document !== 'undefined' && document.startViewTransition) { - const transition = document.startViewTransition(async () => { + const transition = document.startViewTransition(() => { applyFn() - await nextTick() }) transition.ready @@ -111,6 +110,7 @@ function withViewTransition(event, applyFn, direction = true) { { duration: 400, easing: 'ease-in-out', + fill: 'both', pseudoElement: direction ? '::view-transition-new(root)' : '::view-transition-old(root)', From 1acd776d3bf05fd2d366cef295af835d9d883419 Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 8 Oct 2025 19:17:45 +0800 Subject: [PATCH 077/105] =?UTF-8?q?fix:=20=E5=90=AF=E5=8A=A8=E6=8E=92?= =?UTF-8?q?=E9=98=9F=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-docs.yml | 7 ++++++- .github/workflows/deploy-staging.yml | 11 ++++++++--- .github/workflows/deploy.yml | 9 +++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 2f1dac164..a60f63b00 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -11,12 +11,17 @@ on: permissions: contents: write +# 文档发布自己的排队锁,不影响服务器部署 +concurrency: + group: openisle-docs + cancel-in-progress: false + jobs: build-docs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 1 diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 6f03af5ab..e4d92a7f8 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -2,22 +2,27 @@ name: Staging CI & CD on: push: - branches: [main] + branches: [ "main" ] workflow_dispatch: permissions: contents: write +# 与生产部署共用同一把锁,确保服务器上始终串行(跨工作流也互斥) +concurrency: + group: openisle-server + cancel-in-progress: false + jobs: build-and-deploy: runs-on: ubuntu-latest environment: Deploy - if: ${{ !github.event.repository.fork }} # 只有非 fork 才执行 + if: ${{ !github.event.repository.fork }} steps: - uses: actions/checkout@v4 - - name: Deploy to Server + - name: Deploy to Server (staging) uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_HOST }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b477cd771..dca1c942e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,7 +3,12 @@ name: CI & CD on: workflow_dispatch: schedule: - - cron: "0 19 * * *" # 每天 UTC 19:00,相当于北京时间凌晨3点 + - cron: "0 19 * * *" # 每天 UTC 19:00(北京 03:00) + +# 与 Staging 共用同一把锁,避免两边同时在 8G 服务器上跑 +concurrency: + group: openisle-server + cancel-in-progress: false jobs: build-and-deploy: @@ -13,7 +18,7 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Deploy to Server + - name: Deploy to Server (prod) uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_HOST }} From 660d8ffe51b2a75ebc1476982581e92fb6deedfa Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Wed, 8 Oct 2025 21:31:36 +0900 Subject: [PATCH 078/105] =?UTF-8?q?https://github.com/nagisa77/OpenIsle/is?= =?UTF-8?q?sues/843=20=E5=AF=B9=E9=BD=90=E6=96=B9=E5=BC=8F=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/pages/index.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend_nuxt/pages/index.vue b/frontend_nuxt/pages/index.vue index 0b4b69de6..e49c0d387 100644 --- a/frontend_nuxt/pages/index.vue +++ b/frontend_nuxt/pages/index.vue @@ -537,16 +537,22 @@ watch([selectedCategory, selectedTags], ([newCategory, newTags]) => { .article-comments, .header-item.comments { width: 5%; + justify-content: flex-end; + text-align: right; } .article-views, .header-item.views { width: 5%; + justify-content: flex-end; + text-align: right; } .article-time, .header-item.activity { width: 10%; + justify-content: flex-end; + text-align: left; } .article-item-title { From a15065575d1c900a41b12380df4a1288c989dcab Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Thu, 9 Oct 2025 21:40:28 +0900 Subject: [PATCH 079/105] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=97=AE=E9=A2=98=20?= =?UTF-8?q?https://github.com/nagisa77/OpenIsle/issues/1057=20=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E5=8E=9F=E5=9B=A0:=E8=A1=8C=E5=8F=B7=E7=9A=84css?= =?UTF-8?q?=E9=99=90=E5=88=B6=E5=AE=BD=E5=BA=A6=EF=BC=8C=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E8=A1=8C=E6=95=B0=E8=B6=85=E8=BF=8799=E9=94=99=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/assets/global.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend_nuxt/assets/global.css b/frontend_nuxt/assets/global.css index fb12189e0..22c95c8a8 100644 --- a/frontend_nuxt/assets/global.css +++ b/frontend_nuxt/assets/global.css @@ -179,7 +179,9 @@ body { .info-content-text pre .line-numbers { counter-reset: line-number 0; - width: 2em; + white-space: nowrap; /* 禁止数字换行 */ + font-variant-numeric: tabular-nums; /* 数字等宽 */ + /* width: 2em; */ font-size: 13px; position: sticky; flex-shrink: 0; From 60fa6051b7733c3265eaa885b1213f930bed0dc6 Mon Sep 17 00:00:00 2001 From: sivdead <923396178@qq.com> Date: Fri, 10 Oct 2025 15:20:39 +0800 Subject: [PATCH 080/105] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=94=B3?= =?UTF-8?q?=E8=AF=B7=E6=B3=A8=E5=86=8C=E9=A1=B5=E9=9D=A2UI=20(#857)=20-=20?= =?UTF-8?q?=E5=B0=86=E5=AD=97=E7=AC=A6=E8=AE=A1=E6=95=B0=E7=A7=BB=E8=87=B3?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=A1=86=E5=86=85=E9=83=A8=E5=8F=B3=E4=B8=8B?= =?UTF-8?q?=E8=A7=92=20-=20=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA=E8=B7=9D?= =?UTF-8?q?=E7=A6=BB=E8=BE=93=E5=85=A5=E6=A1=868px=20-=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=B8=83=E5=B1=80=E7=BB=93=E6=9E=84=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8input-wrapper=E5=8C=85=E8=A3=B9=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E5=8C=BA=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/pages/signup-reason.vue | 37 +++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/frontend_nuxt/pages/signup-reason.vue b/frontend_nuxt/pages/signup-reason.vue index f6c0d1c15..1d2048cfa 100644 --- a/frontend_nuxt/pages/signup-reason.vue +++ b/frontend_nuxt/pages/signup-reason.vue @@ -5,11 +5,18 @@
为了我们社区的良性发展,请填写注册理由,我们将根据你的理由审核你的注册, 谢谢!
-
- -
{{ reason.length }}/20
+
+
+ +
{{ reason.length }}/20
+
+
{{ error }}
-
{{ error }}
@@ -38,8 +45,9 @@ onMounted(async () => { }) const submit = async () => { - if (!reason.value || reason.value.trim().length < 20) { - error.value = '请至少输入20个字' + const trimmedReason = reason.value.trim() + if (!trimmedReason || trimmedReason.length < 20) { + error.value = '请至少输入20个字符' return } @@ -98,16 +106,29 @@ const submit = async () => { width: 400px; } +.input-wrapper { + display: flex; + flex-direction: column; +} + +.reason-input-container { + position: relative; +} + .char-count { + position: absolute; + bottom: 8px; + right: 12px; font-size: 12px; color: #888; - width: 100%; - text-align: right; + background-color: transparent; + pointer-events: none; } .error-message { color: red; font-size: 14px; + margin-top: 8px; } .signup-page-button-primary { From 160570574c648a4212e033bf7589add3a11fe5d6 Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Sat, 11 Oct 2025 10:13:03 +0900 Subject: [PATCH 081/105] =?UTF-8?q?1.header=E8=8F=9C=E5=8D=95=E6=A0=8F?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E7=BB=9F=E4=B8=80=202.=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=9C=AA=E7=99=BB=E5=BD=95=E7=9A=84=E6=83=85=E5=86=B5=E4=B8=8B?= =?UTF-8?q?=E9=82=80=E8=AF=B7=E9=93=BE=E6=8E=A5=E7=8A=B6=E6=80=81=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/components/HeaderComponent.vue | 149 ++++++++++++++----- frontend_nuxt/plugins/iconpark.client.ts | 2 + 2 files changed, 110 insertions(+), 41 deletions(-) diff --git a/frontend_nuxt/components/HeaderComponent.vue b/frontend_nuxt/components/HeaderComponent.vue index 8b977c513..074eeb9b7 100644 --- a/frontend_nuxt/components/HeaderComponent.vue +++ b/frontend_nuxt/components/HeaderComponent.vue @@ -26,43 +26,59 @@
-
- -
- -
- -
- -
- - 邀请 - -
+ + +
+ + 搜索 +
+
+ + +
+ + 主题 +
+
+ + +
+ + +
+
+ -
- - {{ onlineCount }} +
+ + 在线 + {{ onlineCount }}
+ -
- +
+ + RSS
- + -
- +
+ + 发帖
+ -
- - {{ - unreadMessageCount - }} +
+ + 消息 + {{ unreadMessageCount }}
@@ -192,6 +208,7 @@ const copyInviteLink = async () => { const token = getToken() if (!token) { toast.error('请先登录') + isCopying.value = false // 🔥 修复:未登录时立即复原状态 return } try { @@ -333,7 +350,7 @@ onMounted(async () => { height: var(--header-height); background-color: var(--background-color-blur); backdrop-filter: var(--blur-10); - color: var(--header-text-color); + color: var(--primary-color); border-bottom: 1px solid var(--header-border-color); } @@ -376,6 +393,7 @@ onMounted(async () => { flex-direction: row; align-items: center; gap: 20px; + padding-right: 15px; } .micon { @@ -464,16 +482,14 @@ onMounted(async () => { cursor: pointer; } -.invite_text { - font-size: 12px; - cursor: pointer; - color: var(--primary-color); -} .invite_text:hover { + opacity: 0.8; text-decoration: underline; } +.invite_text, +.online-count, .rss-icon, .new-post-icon, .messages-icon { @@ -484,8 +500,8 @@ onMounted(async () => { .unread-badge { position: absolute; - top: -5px; - right: -10px; + top: -4px; + right: -6px; background-color: #ff4d4f; color: white; border-radius: 50%; @@ -500,8 +516,8 @@ onMounted(async () => { .unread-dot { position: absolute; - top: -2px; - right: -4px; + top: 0; + right: -1px; width: 8px; height: 8px; border-radius: 50%; @@ -513,14 +529,58 @@ onMounted(async () => { } .online-count { - font-size: 14px; - display: flex; - align-items: center; - gap: 5px; - color: var(--primary-color); cursor: default; } +/* === 统一图标按钮风格 === */ +.header-icon-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 4px; + font-size: 14px; + color: var(--primary-color); + cursor: pointer; + position: relative; + transition: color 0.25s ease, transform 0.15s ease, opacity 0.2s ease; +} + +.header-icon-item:hover { + opacity: 0.8; + transform: translateY(-1px); +} + +/* 点击时瞬间高亮 + 轻微缩放 */ +.header-icon-item:active { + color: var(--primary-color-hover); + transform: scale(0.92); +} + +.header-icon { + font-size: 20px; + line-height: 1; +} + +.header-label { + font-size: 12px; + line-height: 1; +} + +/* 在线人数的数字文字样式(无背景) */ +.header-badge { + position: absolute; + top: -4px; + right: -6px; + color: var(--primary-color); /* 🔹 使用主题主色 */ + background: none; /* 🔹 去掉背景 */ + font-size: 11px; /* 字体稍微大一点以便清晰 */ + font-weight: 600; /* 加一点权重让数字更醒目 */ + line-height: 1; + padding: 0; /* 去掉内边距 */ +} + + @keyframes rss-glow { 0% { text-shadow: 0 0 0px var(--primary-color); @@ -556,5 +616,12 @@ onMounted(async () => { .header-content-right { gap: 15px; } + /* 手机不显示文字 */ + .header-label { + display: none; + } + .header-badge { + display: none; + } } diff --git a/frontend_nuxt/plugins/iconpark.client.ts b/frontend_nuxt/plugins/iconpark.client.ts index 985cfa6ac..d0ab502d9 100644 --- a/frontend_nuxt/plugins/iconpark.client.ts +++ b/frontend_nuxt/plugins/iconpark.client.ts @@ -29,6 +29,7 @@ import { ApplicationMenu, Search, Copy, + CopyLink, Loading, Rss, MessageEmoji, @@ -111,6 +112,7 @@ export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.component('ApplicationMenu', ApplicationMenu) nuxtApp.vueApp.component('SearchIcon', Search) nuxtApp.vueApp.component('Copy', Copy) + nuxtApp.vueApp.component('CopyLink', CopyLink) nuxtApp.vueApp.component('Loading', Loading) nuxtApp.vueApp.component('Rss', Rss) nuxtApp.vueApp.component('MessageEmoji', MessageEmoji) From 98e2ea7ef8d4c65f6bd76d4d8ac5e133ca3c42aa Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Mon, 13 Oct 2025 09:47:58 +0900 Subject: [PATCH 082/105] =?UTF-8?q?icon=E5=AF=B9=E9=BD=90=20https://github?= =?UTF-8?q?.com/nagisa77/OpenIsle/issues/854?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/components/SearchDropdown.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend_nuxt/components/SearchDropdown.vue b/frontend_nuxt/components/SearchDropdown.vue index e8fde21d6..348701a7b 100644 --- a/frontend_nuxt/components/SearchDropdown.vue +++ b/frontend_nuxt/components/SearchDropdown.vue @@ -202,6 +202,7 @@ defineExpose({ } .result-body { + line-height: 1; display: flex; flex-direction: column; } From 303ec9b6c11ab298c5141a6f6da5557f248a2e07 Mon Sep 17 00:00:00 2001 From: smallclover <18363998103@163.com> Date: Mon, 13 Oct 2025 19:35:18 +0900 Subject: [PATCH 083/105] =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E8=B4=B4=E5=90=A7=E8=A1=A8=E6=83=85=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/pages/index.vue | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/frontend_nuxt/pages/index.vue b/frontend_nuxt/pages/index.vue index e49c0d387..9152c2927 100644 --- a/frontend_nuxt/pages/index.vue +++ b/frontend_nuxt/pages/index.vue @@ -75,8 +75,8 @@ {{ article.title }} - - {{ sanitizeDescription(article.description) }} + +