feat: 添加 Dockerfile、部署配置和 Nginx 配置
my-v0 / deploy (push) Successful in 48s

This commit is contained in:
2026-08-17 00:13:01 +08:00
parent d1d10f4583
commit 8d1fcd9041
8 changed files with 163 additions and 2 deletions
+8
View File
@@ -0,0 +1,8 @@
node_modules
dist
.env
.env.local
.git
.gitignore
.gitea
*.md
+104
View File
@@ -0,0 +1,104 @@
name: my-v0
run-name: '${{ gitea.actor }} is deploying my-v0'
on:
push:
env:
IMAGE_NAME: my-v0
BASE_IMAGE: my-v0-base
HOST_PORT: 22787
CONTAINER_PORT: 3000
DOCKER_BUILDKIT: '0'
jobs:
deploy:
runs-on:
- self-hosted
steps:
- name: Prepare environment
run: |
STEP_START=$(date +%s)
if ! command -v docker > /dev/null 2>&1; then
apk add docker-cli
fi
if ! command -v git > /dev/null 2>&1; then
apk add git
fi
if ! command -v node > /dev/null 2>&1; then
apk add nodejs
fi
docker --version
echo "BuildKit: ${DOCKER_BUILDKIT}"
echo "Environment preparation took $(( $(date +%s) - STEP_START ))s"
- name: Checkout repository
uses: actions/checkout@v4
- name: Build base image when needed
run: |
STEP_START=$(date +%s)
BASE_HASH=$(sha256sum Dockerfile.base | cut -c1-12)
CURRENT_HASH=$(docker inspect $BASE_IMAGE:latest --format '{{index .Config.Labels "base.hash"}}' 2>/dev/null || echo "none")
if [ "$BASE_HASH" != "$CURRENT_HASH" ]; then
docker build \
--label "base.hash=$BASE_HASH" \
-f Dockerfile.base \
-t $BASE_IMAGE:latest .
else
echo "Base image is current (hash: $BASE_HASH)"
fi
echo "Base image step took $(( $(date +%s) - STEP_START ))s"
- name: Build application image
run: |
STEP_START=$(date +%s)
docker build -t $IMAGE_NAME:latest .
docker images $IMAGE_NAME:latest --format "Image size: {{.Size}}"
echo "Application image step took $(( $(date +%s) - STEP_START ))s"
- name: Deploy container
run: |
STEP_START=$(date +%s)
if docker ps -a --filter "name=^${IMAGE_NAME}$" -q | grep -q .; then
docker stop --timeout 15 $IMAGE_NAME || true
docker rm $IMAGE_NAME || true
fi
docker run -d \
--name $IMAGE_NAME \
-p 127.0.0.1:$HOST_PORT:$CONTAINER_PORT \
-e TZ=Asia/Shanghai \
--restart unless-stopped \
$IMAGE_NAME:latest
MAX_WAIT=60
WAITED=0
while [ $WAITED -lt $MAX_WAIT ]; do
RUNNING_ID=$(docker ps --filter "name=^${IMAGE_NAME}$" --filter "status=running" -q)
HEALTH=$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' $IMAGE_NAME 2>/dev/null || echo "unknown")
if [ -n "$RUNNING_ID" ] && [ "$HEALTH" = "healthy" ]; then
break
fi
sleep 2
WAITED=$((WAITED + 2))
done
if docker ps --filter "name=^${IMAGE_NAME}$" --filter "status=running" -q | grep -q .; then
docker ps --filter "name=^${IMAGE_NAME}$" --format "Status: {{.Status}}"
echo "Startup wait: ${WAITED}s"
else
docker inspect $IMAGE_NAME --format 'ExitCode={{.State.ExitCode}} Error={{.State.Error}} StartedAt={{.State.StartedAt}} FinishedAt={{.State.FinishedAt}}' 2>/dev/null || true
docker logs --tail 30 $IMAGE_NAME || true
exit 1
fi
echo "Deployment step took $(( $(date +%s) - STEP_START ))s"
- name: Show startup logs
run: docker logs --tail 15 $IMAGE_NAME
+21
View File
@@ -0,0 +1,21 @@
FROM my-v0-base:latest AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 3000
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \
CMD wget -q --spider http://127.0.0.1:3000/my-v0/ || exit 1
CMD ["nginx", "-g", "daemon off;"]
+3
View File
@@ -0,0 +1,3 @@
FROM node:22-alpine
RUN npm install -g pnpm@10
+8
View File
@@ -2,6 +2,14 @@
Vue 3 + Vite 大屏低代码编辑器前端
## 技术栈
- **框架**Vue 3、Vite、TypeScript、Pinia、Vue Router
- **样式 / UI**Tailwind CSS、Element Plus、Iconify
- **画布**vue3-moveable(拖拽缩放)、vue3-selecto(框选)、vue3-sketch-ruler(标尺)、vue-draggable-plus(图层拖拽排序)
- **图表 / 编辑器**ECharts、Monaco Editor
- **数据**Axios、Mock.js
## 开发
Node `^22.18.0 || >=24.12.0`,包管理器用 pnpm。
-2
View File
@@ -24,8 +24,6 @@ declare module 'vue' {
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElOption: typeof import('element-plus/es')['ElOption']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
MonacoEditor: typeof import('./src/components/MonacoEditor/index.vue')['default']
+15
View File
@@ -0,0 +1,15 @@
server {
listen 3000;
server_name _;
root /usr/share/nginx/html;
index index.html;
location = /my-v0 {
return 301 /my-v0/;
}
location /my-v0/ {
rewrite ^/my-v0/(.*)$ /$1 break;
try_files $uri $uri/ /index.html;
}
}
+4
View File
@@ -9,6 +9,7 @@ import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig({
base: '/my-v0/',
plugins: [
vue(),
vueDevTools(),
@@ -25,4 +26,7 @@ export default defineConfig({
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
server: {
allowedHosts: ['www.qflink.xyz'],
},
})