Compare commits
12 Commits
312abdc34b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 008fb370d5 | |||
| 444907aac4 | |||
| aa63d3db1c | |||
| 418c04980f | |||
| 9107cf4651 | |||
| 7a60ba4dab | |||
| 39c33273ef | |||
| 2a8365a8be | |||
| 13bacc6491 | |||
| 05ac848293 | |||
| d3f83023ea | |||
| 9d49b72abf |
@@ -0,0 +1,180 @@
|
|||||||
|
name: build-and-push
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: 192.168.30.181:3000
|
||||||
|
REPO_PATH: wpic-dev/datahub
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# Pre-gate: tag must point to a commit reachable from master
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
guard-master-only:
|
||||||
|
runs-on: podman
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Verify tag is on master
|
||||||
|
run: |
|
||||||
|
git fetch origin master:refs/remotes/origin/master
|
||||||
|
if ! git branch -r --contains "${{ gitea.sha }}" | grep -qE '(^|\s)origin/master(\s|$)'; then
|
||||||
|
echo "::error::Tag ${{ gitea.ref_name }} (commit ${{ gitea.sha }}) is NOT on master branch. Refusing to build."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "OK: tag ${{ gitea.ref_name }} is on master"
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# Test gates
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
test-backend:
|
||||||
|
runs-on: podman
|
||||||
|
needs: [guard-master-only]
|
||||||
|
container:
|
||||||
|
image: docker.io/hyperf/hyperf:8.3-alpine-v3.19-swoole
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: composer install (incl. dev for phpstan)
|
||||||
|
run: cd backend && composer install --no-interaction --no-progress
|
||||||
|
- name: PHP syntax check (parallel)
|
||||||
|
run: |
|
||||||
|
cd backend && find app config bin migrations -name '*.php' -print0 \
|
||||||
|
| xargs -0 -n1 -P4 php -l
|
||||||
|
- name: phpstan static analysis
|
||||||
|
run: cd backend && composer analyse
|
||||||
|
|
||||||
|
test-frontend-build:
|
||||||
|
runs-on: podman
|
||||||
|
needs: [guard-master-only]
|
||||||
|
container:
|
||||||
|
image: docker.io/library/node:22-alpine
|
||||||
|
env:
|
||||||
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: npm ci
|
||||||
|
run: cd frontend && npm ci --no-audit --no-fund
|
||||||
|
- name: lint (pre-existing baseline tolerated)
|
||||||
|
run: cd frontend && npm run lint
|
||||||
|
continue-on-error: true
|
||||||
|
- name: type-check (vue-tsc)
|
||||||
|
run: cd frontend && npm run type-check
|
||||||
|
- name: unit tests (pre-existing baseline tolerated)
|
||||||
|
run: cd frontend && npm run test:unit -- --run
|
||||||
|
continue-on-error: true
|
||||||
|
- name: build (vite)
|
||||||
|
run: cd frontend && npm run build
|
||||||
|
- name: upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: frontend-build
|
||||||
|
path: |
|
||||||
|
frontend/dist
|
||||||
|
frontend/node_modules
|
||||||
|
|
||||||
|
test-frontend-e2e:
|
||||||
|
runs-on: podman
|
||||||
|
needs: [test-frontend-build]
|
||||||
|
container:
|
||||||
|
image: mcr.microsoft.com/playwright:v1.60.0-noble
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: download artifacts
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: frontend-build
|
||||||
|
path: frontend
|
||||||
|
- name: playwright smoke (chromium only)
|
||||||
|
run: cd frontend && CI=true npx playwright test --project=chromium
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# Build & push (4 images, parallel where possible)
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
build-backend:
|
||||||
|
runs-on: podman
|
||||||
|
needs: [test-backend]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Login to Gitea Registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DATAHUB_CI_CD }}" | \
|
||||||
|
podman login -u "${{ gitea.actor }}" --password-stdin ${{ env.REGISTRY }}
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
podman build --pull --layers \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/backend:${{ gitea.ref_name }} \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/backend:stable \
|
||||||
|
-f backend/Dockerfile \
|
||||||
|
backend/
|
||||||
|
- name: Push
|
||||||
|
run: |
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/backend:${{ gitea.ref_name }}
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/backend:stable
|
||||||
|
|
||||||
|
build-frontend:
|
||||||
|
runs-on: podman
|
||||||
|
needs: [test-frontend-e2e]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Login to Gitea Registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DATAHUB_CI_CD }}" | \
|
||||||
|
podman login -u "${{ gitea.actor }}" --password-stdin ${{ env.REGISTRY }}
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
podman build --pull --layers \
|
||||||
|
--ulimit nofile=65536:65536 \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/frontend:${{ gitea.ref_name }} \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/frontend:stable \
|
||||||
|
-f frontend/Dockerfile \
|
||||||
|
frontend/
|
||||||
|
- name: Push
|
||||||
|
run: |
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/frontend:${{ gitea.ref_name }}
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/frontend:stable
|
||||||
|
|
||||||
|
build-timescaledb2:
|
||||||
|
runs-on: podman
|
||||||
|
needs: [guard-master-only]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Login to Gitea Registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DATAHUB_CI_CD }}" | \
|
||||||
|
podman login -u "${{ gitea.actor }}" --password-stdin ${{ env.REGISTRY }}
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
podman build --pull --layers \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/timescaledb2:${{ gitea.ref_name }} \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/timescaledb2:stable \
|
||||||
|
-f docs/tmp/deploy-ref/ci-cd/03-timescaledb-image/Containerfile \
|
||||||
|
docs/tmp/deploy-ref/ci-cd/03-timescaledb-image/
|
||||||
|
- name: Push
|
||||||
|
run: |
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/timescaledb2:${{ gitea.ref_name }}
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/timescaledb2:stable
|
||||||
|
|
||||||
|
build-rabbitmq3:
|
||||||
|
runs-on: podman
|
||||||
|
needs: [guard-master-only]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Login to Gitea Registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DATAHUB_CI_CD }}" | \
|
||||||
|
podman login -u "${{ gitea.actor }}" --password-stdin ${{ env.REGISTRY }}
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
podman build --pull --layers \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/rabbitmq3:${{ gitea.ref_name }} \
|
||||||
|
-t ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/rabbitmq3:stable \
|
||||||
|
-f docs/tmp/deploy-ref/ci-cd/04-rabbitmq-image/Containerfile \
|
||||||
|
docs/tmp/deploy-ref/ci-cd/04-rabbitmq-image/
|
||||||
|
- name: Push
|
||||||
|
run: |
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/rabbitmq3:${{ gitea.ref_name }}
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/rabbitmq3:stable
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
name: rollback
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
target_tag:
|
||||||
|
description: 'Target release tag to rollback to (e.g. v1.2.3)'
|
||||||
|
required: true
|
||||||
|
component:
|
||||||
|
description: 'Component (backend / frontend / timescaledb2 / rabbitmq3 / all)'
|
||||||
|
required: true
|
||||||
|
default: 'all'
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: 192.168.30.181:3000
|
||||||
|
REPO_PATH: wpic-dev/datahub
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
retag:
|
||||||
|
runs-on: podman
|
||||||
|
steps:
|
||||||
|
- name: Validate target_tag format
|
||||||
|
run: |
|
||||||
|
if [[ ! "${{ inputs.target_tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||||
|
echo "::error::target_tag must match semver pattern v<MAJOR>.<MINOR>.<PATCH>[-suffix]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Validate component
|
||||||
|
run: |
|
||||||
|
case "${{ inputs.component }}" in
|
||||||
|
backend|frontend|timescaledb2|rabbitmq3|all) ;;
|
||||||
|
*)
|
||||||
|
echo "::error::component must be one of: backend / frontend / timescaledb2 / rabbitmq3 / all"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
- name: Login to Gitea Registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DATAHUB_CI_CD }}" | \
|
||||||
|
podman login -u "${{ gitea.actor }}" --password-stdin ${{ env.REGISTRY }}
|
||||||
|
|
||||||
|
- name: Re-tag stable to target release
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
components_to_rollback=""
|
||||||
|
if [[ "${{ inputs.component }}" == "all" ]]; then
|
||||||
|
components_to_rollback="backend frontend timescaledb2 rabbitmq3"
|
||||||
|
else
|
||||||
|
components_to_rollback="${{ inputs.component }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for img in $components_to_rollback; do
|
||||||
|
echo "=== Rolling back $img to ${{ inputs.target_tag }} ==="
|
||||||
|
podman pull ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/$img:${{ inputs.target_tag }}
|
||||||
|
podman tag ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/$img:${{ inputs.target_tag }} \
|
||||||
|
${{ env.REGISTRY }}/${{ env.REPO_PATH }}/$img:stable
|
||||||
|
podman push ${{ env.REGISTRY }}/${{ env.REPO_PATH }}/$img:stable
|
||||||
|
echo "✓ $img:stable now points to ${{ inputs.target_tag }}"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Rollback complete. wpic-virt podman-auto-update.timer will pull the new"
|
||||||
|
echo ":stable digest within ~5 minutes and restart affected containers."
|
||||||
@@ -14,7 +14,6 @@ backend/phpstan.neon
|
|||||||
backend/phpunit.xml
|
backend/phpunit.xml
|
||||||
backend/.php_cs.cache
|
backend/.php_cs.cache
|
||||||
backend/.php-cs-fixer.cache
|
backend/.php-cs-fixer.cache
|
||||||
backend/composer.lock
|
|
||||||
|
|
||||||
# ==================== 前端 (Vue3/Vite) ====================
|
# ==================== 前端 (Vue3/Vite) ====================
|
||||||
frontend/node_modules/
|
frontend/node_modules/
|
||||||
@@ -25,9 +24,6 @@ frontend/coverage/
|
|||||||
frontend/*.local
|
frontend/*.local
|
||||||
frontend/.eslintcache
|
frontend/.eslintcache
|
||||||
frontend/*.tsbuildinfo
|
frontend/*.tsbuildinfo
|
||||||
frontend/package-lock.json
|
|
||||||
frontend/pnpm-lock.yaml
|
|
||||||
frontend/yarn.lock
|
|
||||||
|
|
||||||
# 前端日志文件
|
# 前端日志文件
|
||||||
frontend/logs/
|
frontend/logs/
|
||||||
@@ -152,7 +148,6 @@ $RECYCLE.BIN/
|
|||||||
|
|
||||||
# ==================== Docker ====================
|
# ==================== Docker ====================
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
.dockerignore
|
|
||||||
|
|
||||||
# ==================== 版本控制 ====================
|
# ==================== 版本控制 ====================
|
||||||
*.patch
|
*.patch
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
**
|
||||||
|
!app/
|
||||||
|
!bin/
|
||||||
|
!config/
|
||||||
|
!migrations/
|
||||||
|
!composer.*
|
||||||
|
!entrypoint.sh
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
APP_NAME=skeleton
|
APP_NAME=dataghub
|
||||||
APP_ENV=dev
|
APP_ENV=dev
|
||||||
|
|
||||||
DB_DRIVER=mysql
|
DB_DRIVER=mysql
|
||||||
@@ -17,7 +17,7 @@ AMQP_ADMIN_USER="user"
|
|||||||
AMQP_ADMIN_PASSWORD="pass"
|
AMQP_ADMIN_PASSWORD="pass"
|
||||||
AMQP_USER="user_tmall"
|
AMQP_USER="user_tmall"
|
||||||
AMQP_PASSWORD="change_me_tmall"
|
AMQP_PASSWORD="change_me_tmall"
|
||||||
AMQP_VHOST="dataflow"
|
AMQP_VHOST="datahub"
|
||||||
# 消息最大重试次数(默认3次),超过后进入错误队列
|
# 消息最大重试次数(默认3次),超过后进入错误队列
|
||||||
AMQP_MAX_RETRIES=3
|
AMQP_MAX_RETRIES=3
|
||||||
# 调试模式:消费者处理每条消息的延迟秒数(默认0,设置为2可方便在mq:status中观察)
|
# 调试模式:消费者处理每条消息的延迟秒数(默认0,设置为2可方便在mq:status中观察)
|
||||||
|
|||||||
+31
-40
@@ -1,54 +1,45 @@
|
|||||||
# Default Dockerfile
|
FROM docker.io/hyperf/hyperf:8.3-alpine-v3.19-swoole
|
||||||
#
|
|
||||||
# @link https://www.hyperf.io
|
|
||||||
# @document https://hyperf.wiki
|
|
||||||
# @contact group@hyperf.io
|
|
||||||
# @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
|
||||||
|
|
||||||
FROM hyperf/hyperf:8.3-alpine-v3.19-swoole
|
LABEL org.opencontainers.image.title="datahub-backend" \
|
||||||
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
|
org.opencontainers.image.vendor="WPIC" \
|
||||||
|
org.opencontainers.image.licenses="MIT" \
|
||||||
|
org.opencontainers.image.source="https://192.168.30.181:3000/wpic-dev/datahub"
|
||||||
|
|
||||||
##
|
ARG TIMEZONE=Asia/Shanghai
|
||||||
# ---------- env settings ----------
|
ENV TIMEZONE=${TIMEZONE} \
|
||||||
##
|
|
||||||
# --build-arg timezone=Asia/Shanghai
|
|
||||||
ARG timezone
|
|
||||||
|
|
||||||
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
|
|
||||||
APP_ENV=prod \
|
APP_ENV=prod \
|
||||||
SCAN_CACHEABLE=(true)
|
SCAN_CACHEABLE=(true)
|
||||||
|
|
||||||
# update
|
RUN apk add --no-cache php83-intl php83-pspell
|
||||||
RUN set -ex \
|
|
||||||
# show php version and extensions
|
RUN set -eux; \
|
||||||
&& php -v \
|
php -v; php -m; php --ri swoole; \
|
||||||
&& php -m \
|
cd /etc/php*; \
|
||||||
&& php --ri swoole \
|
{ \
|
||||||
# ---------- some config ----------
|
|
||||||
&& cd /etc/php* \
|
|
||||||
# - config PHP
|
|
||||||
&& { \
|
|
||||||
echo "upload_max_filesize=128M"; \
|
echo "upload_max_filesize=128M"; \
|
||||||
echo "post_max_size=128M"; \
|
echo "post_max_size=128M"; \
|
||||||
echo "memory_limit=1G"; \
|
echo "memory_limit=2G"; \
|
||||||
echo "date.timezone=${TIMEZONE}"; \
|
echo "date.timezone=${TIMEZONE}"; \
|
||||||
} | tee conf.d/99_overrides.ini \
|
} | tee conf.d/99_overrides.ini; \
|
||||||
# - config timezone
|
ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime; \
|
||||||
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
|
echo "${TIMEZONE}" > /etc/timezone; \
|
||||||
&& echo "${TIMEZONE}" > /etc/timezone \
|
rm -rf /var/cache/apk/* /tmp/* /usr/share/man
|
||||||
# ---------- clear works ----------
|
|
||||||
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
|
|
||||||
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
|
|
||||||
|
|
||||||
WORKDIR /opt/www
|
WORKDIR /var/www
|
||||||
|
|
||||||
# Composer Cache
|
COPY composer.json composer.lock ./
|
||||||
# COPY ./composer.* /opt/www/
|
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist --no-interaction
|
||||||
# RUN composer install --no-dev --no-scripts
|
|
||||||
|
|
||||||
COPY . /opt/www
|
COPY . .
|
||||||
RUN composer install --no-dev -o && php bin/hyperf.php
|
RUN composer dump-autoload --optimize --classmap-authoritative --no-dev
|
||||||
|
|
||||||
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
EXPOSE 9501
|
EXPOSE 9501
|
||||||
|
|
||||||
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
|
||||||
|
CMD wget -qO- http://127.0.0.1:9501/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
|
CMD ["php", "/var/www/bin/hyperf.php", "start"]
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ ${YELLOW}用法:${NC}
|
|||||||
$0 <command> [options]
|
$0 <command> [options]
|
||||||
|
|
||||||
${YELLOW}命令:${NC}
|
${YELLOW}命令:${NC}
|
||||||
${GREEN}init${NC} 从数据库读取所有启用的平台,完全重建 MQ 配置
|
${GREEN}init${NC} 从数据库读取所有启用的平台,完全重建 MQ 配置
|
||||||
- 删除现有 VHost 及所有资源
|
- 删除现有 VHost 及所有资源
|
||||||
- 从 platforms 表读取 enabled=true 的平台
|
- 从 platforms 表读取 enabled=true 的平台
|
||||||
- 创建所有 Exchange、Queue、Binding
|
- 创建所有 Exchange、Queue、Binding
|
||||||
@@ -71,9 +71,9 @@ ${YELLOW}命令:${NC}
|
|||||||
- 从 mq_user.php 中移除配置
|
- 从 mq_user.php 中移除配置
|
||||||
示例: $0 remove shopee
|
示例: $0 remove shopee
|
||||||
|
|
||||||
${GREEN}list${NC} 列出当前 MQ 中已配置的平台
|
${GREEN}list${NC} 列出当前 MQ 中已配置的平台
|
||||||
|
|
||||||
${GREEN}version${NC} 显示 RabbitMQ 服务器版本信息
|
${GREEN}version${NC} 显示 RabbitMQ 服务器版本信息
|
||||||
|
|
||||||
${GREEN}reset-password${NC} 重置指定用户的密码
|
${GREEN}reset-password${NC} 重置指定用户的密码
|
||||||
--user <name> 用户名称 (consumer/ops/平台名)
|
--user <name> 用户名称 (consumer/ops/平台名)
|
||||||
|
|||||||
Generated
+10920
File diff suppressed because it is too large
Load Diff
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Datahub backend container entrypoint.
|
||||||
|
# Flow: wait_tcp pg/mq -> migrate --force -> app:install -> exec swoole.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
wait_tcp() {
|
||||||
|
host="$1"
|
||||||
|
port="$2"
|
||||||
|
label="$3"
|
||||||
|
timeout="${4:-90}"
|
||||||
|
echo "[entrypoint] waiting for ${label} @ ${host}:${port} (up to ${timeout}s)"
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt "$timeout" ]; do
|
||||||
|
if php -r "exit(@fsockopen('${host}', ${port}, \$e, \$s, 1) ? 0 : 1);" 2>/dev/null; then
|
||||||
|
echo "[entrypoint] ${label} reachable"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "[entrypoint] ${label} @ ${host}:${port} unreachable after ${timeout}s" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
DB_HOST="${DB_HOST:-host.containers.internal}"
|
||||||
|
DB_PORT="${DB_PORT:-5432}"
|
||||||
|
AMQP_HOST="${AMQP_HOST:-host.containers.internal}"
|
||||||
|
AMQP_PORT="${AMQP_PORT:-5672}"
|
||||||
|
WAIT_TIMEOUT="${ENTRYPOINT_WAIT_TIMEOUT:-90}"
|
||||||
|
|
||||||
|
wait_tcp "${DB_HOST}" "${DB_PORT}" "postgres" "${WAIT_TIMEOUT}"
|
||||||
|
wait_tcp "${AMQP_HOST}" "${AMQP_PORT}" "rabbitmq" "${WAIT_TIMEOUT}"
|
||||||
|
|
||||||
|
echo "[entrypoint] running migrate --force"
|
||||||
|
php /var/www/bin/hyperf.php migrate --force
|
||||||
|
|
||||||
|
echo "[entrypoint] running app:install"
|
||||||
|
php /var/www/bin/hyperf.php app:install
|
||||||
|
|
||||||
|
echo "[entrypoint] handing off to: $*"
|
||||||
|
exec "$@"
|
||||||
+586
-593
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
playwright-report
|
||||||
|
test-results
|
||||||
|
|
||||||
|
e2e
|
||||||
|
.eslintcache
|
||||||
|
*.tsbuildinfo
|
||||||
|
__screenshots__
|
||||||
|
.vite
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.local
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
README.md
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Stage 1: builder
|
||||||
|
# ============================================================
|
||||||
|
FROM docker.io/library/node:22-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci --no-audit --no-fund
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ARG VITE_API_BASE_URL
|
||||||
|
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Stage 2: runtime
|
||||||
|
# ============================================================
|
||||||
|
FROM docker.io/library/nginx:1.27-alpine
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.title="datahub-frontend" \
|
||||||
|
org.opencontainers.image.vendor="WPIC" \
|
||||||
|
org.opencontainers.image.licenses="MIT" \
|
||||||
|
org.opencontainers.image.source="https://192.168.30.181:3000/wpic-dev/datahub"
|
||||||
|
|
||||||
|
ARG TIMEZONE=Asia/Shanghai
|
||||||
|
ENV TIMEZONE=${TIMEZONE}
|
||||||
|
|
||||||
|
RUN apk add --no-cache tzdata && \
|
||||||
|
ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime && \
|
||||||
|
echo "${TIMEZONE}" > /etc/timezone
|
||||||
|
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||||
|
CMD wget -qO- http://127.0.0.1/ -O /dev/null || exit 1
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
test('homepage loads and SPA bootstraps', async ({ page }) => {
|
||||||
|
const response = await page.goto('/')
|
||||||
|
expect(response, 'page.goto should return a response').not.toBeNull()
|
||||||
|
expect(response!.status(), 'response status < 500').toBeLessThan(500)
|
||||||
|
|
||||||
|
await page.waitForLoadState('domcontentloaded')
|
||||||
|
|
||||||
|
const title = await page.title()
|
||||||
|
expect(title.length, 'page title is non-empty').toBeGreaterThan(0)
|
||||||
|
|
||||||
|
const appRoot = page.locator('#app')
|
||||||
|
await expect(appRoot, '#app root mounts').toBeAttached()
|
||||||
|
})
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
|
||||||
|
|
||||||
// See here how to get started:
|
|
||||||
// https://playwright.dev/docs/intro
|
|
||||||
test('visits the app root url', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.locator('h1')).toHaveText('You did it!');
|
|
||||||
})
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript
|
||||||
|
text/xml application/xml application/xml+rss text/javascript
|
||||||
|
image/svg+xml;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
|
||||||
|
# API 反代到 backend Swoole HTTP server
|
||||||
|
# 依赖 datahub.network + ContainerName=datahub-backend (Round 06 quadlet 落实)
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://datahub-backend:9501;
|
||||||
|
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 60s;
|
||||||
|
proxy_connect_timeout 10s;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(?:css|js|woff2?|ttf|eot|svg|png|jpg|jpeg|gif|ico|webp)$ {
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+8514
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@
|
|||||||
"format": "prettier --write src/"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@antv/g2plot": "^2.4.35",
|
"@antv/g2plot": "^2.3.32",
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
"@tailwindcss/vite": "^4.1.16",
|
||||||
"ant-design-vue": "^4.2.6",
|
"ant-design-vue": "^4.2.6",
|
||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.20",
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.56.1",
|
"@playwright/test": "^1.60.0",
|
||||||
"@tsconfig/node22": "^22.0.2",
|
"@tsconfig/node22": "^22.0.2",
|
||||||
"@types/jsdom": "^27.0.0",
|
"@types/jsdom": "^27.0.0",
|
||||||
"@types/node": "^22.18.11",
|
"@types/node": "^22.18.11",
|
||||||
|
|||||||
@@ -101,6 +101,12 @@ export default defineConfig({
|
|||||||
/**
|
/**
|
||||||
* Use the dev server by default for faster feedback loop.
|
* Use the dev server by default for faster feedback loop.
|
||||||
* Use the preview server on CI for more realistic testing.
|
* Use the preview server on CI for more realistic testing.
|
||||||
|
*
|
||||||
|
* CI mode assumes `dist/` is already built by an upstream node container
|
||||||
|
* (Step 1 of the two-step e2e flow); preview only serves dist here, never
|
||||||
|
* builds. See docs/tmp/deploy-ref/ci-cd/02-frontend-image/design.md §7.0.1
|
||||||
|
* for the two-container workflow contract.
|
||||||
|
*
|
||||||
* Playwright will re-use the local server if there is already a dev-server running.
|
* Playwright will re-use the local server if there is already a dev-server running.
|
||||||
*/
|
*/
|
||||||
command: process.env.CI ? 'npm run preview' : 'npm run dev',
|
command: process.env.CI ? 'npm run preview' : 'npm run dev',
|
||||||
|
|||||||
-1
@@ -1 +0,0 @@
|
|||||||
{"version":"4.1.4","results":[[":frontend/src/pages/users/__tests__/index.spec.ts",{"duration":0,"failed":true}]]}
|
|
||||||
Reference in New Issue
Block a user