Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a60ba4dab | |||
| 39c33273ef | |||
| 2a8365a8be | |||
| 13bacc6491 |
@@ -14,7 +14,6 @@ backend/phpstan.neon
|
||||
backend/phpunit.xml
|
||||
backend/.php_cs.cache
|
||||
backend/.php-cs-fixer.cache
|
||||
backend/composer.lock
|
||||
|
||||
# ==================== 前端 (Vue3/Vite) ====================
|
||||
frontend/node_modules/
|
||||
@@ -152,7 +151,6 @@ $RECYCLE.BIN/
|
||||
|
||||
# ==================== Docker ====================
|
||||
docker-compose.override.yml
|
||||
.dockerignore
|
||||
|
||||
# ==================== 版本控制 ====================
|
||||
*.patch
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
**
|
||||
!app/
|
||||
!bin/
|
||||
!config/
|
||||
!migrations/
|
||||
!composer.*
|
||||
!entrypoint.sh
|
||||
+31
-40
@@ -1,54 +1,45 @@
|
||||
# Default Dockerfile
|
||||
#
|
||||
# @link https://www.hyperf.io
|
||||
# @document https://hyperf.wiki
|
||||
# @contact group@hyperf.io
|
||||
# @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
FROM docker.io/hyperf/hyperf:8.3-alpine-v3.19-swoole
|
||||
|
||||
FROM hyperf/hyperf:8.3-alpine-v3.19-swoole
|
||||
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
|
||||
LABEL org.opencontainers.image.title="datahub-backend" \
|
||||
org.opencontainers.image.vendor="WPIC" \
|
||||
org.opencontainers.image.licenses="MIT" \
|
||||
org.opencontainers.image.source="https://192.168.30.181:3000/wpic-dev/datahub"
|
||||
|
||||
##
|
||||
# ---------- env settings ----------
|
||||
##
|
||||
# --build-arg timezone=Asia/Shanghai
|
||||
ARG timezone
|
||||
|
||||
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
|
||||
ARG TIMEZONE=Asia/Shanghai
|
||||
ENV TIMEZONE=${TIMEZONE} \
|
||||
APP_ENV=prod \
|
||||
SCAN_CACHEABLE=(true)
|
||||
|
||||
# update
|
||||
RUN set -ex \
|
||||
# show php version and extensions
|
||||
&& php -v \
|
||||
&& php -m \
|
||||
&& php --ri swoole \
|
||||
# ---------- some config ----------
|
||||
&& cd /etc/php* \
|
||||
# - config PHP
|
||||
&& { \
|
||||
RUN apk add --no-cache php83-intl php83-pspell
|
||||
|
||||
RUN set -eux; \
|
||||
php -v; php -m; php --ri swoole; \
|
||||
cd /etc/php*; \
|
||||
{ \
|
||||
echo "upload_max_filesize=128M"; \
|
||||
echo "post_max_size=128M"; \
|
||||
echo "memory_limit=1G"; \
|
||||
echo "memory_limit=2G"; \
|
||||
echo "date.timezone=${TIMEZONE}"; \
|
||||
} | tee conf.d/99_overrides.ini \
|
||||
# - config timezone
|
||||
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
|
||||
&& echo "${TIMEZONE}" > /etc/timezone \
|
||||
# ---------- clear works ----------
|
||||
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
|
||||
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
|
||||
} | tee conf.d/99_overrides.ini; \
|
||||
ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime; \
|
||||
echo "${TIMEZONE}" > /etc/timezone; \
|
||||
rm -rf /var/cache/apk/* /tmp/* /usr/share/man
|
||||
|
||||
WORKDIR /opt/www
|
||||
WORKDIR /var/www
|
||||
|
||||
# Composer Cache
|
||||
# COPY ./composer.* /opt/www/
|
||||
# RUN composer install --no-dev --no-scripts
|
||||
COPY composer.json composer.lock ./
|
||||
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist --no-interaction
|
||||
|
||||
COPY . /opt/www
|
||||
RUN composer install --no-dev -o && php bin/hyperf.php
|
||||
COPY . .
|
||||
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
|
||||
|
||||
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"]
|
||||
|
||||
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 "$@"
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
"vue-router": "^4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.56.1",
|
||||
"@playwright/test": "~1.50.0",
|
||||
"@tsconfig/node22": "^22.0.2",
|
||||
"@types/jsdom": "^27.0.0",
|
||||
"@types/node": "^22.18.11",
|
||||
|
||||
Reference in New Issue
Block a user