From 076e603c9368927b0a8f8a81b5f5b2aff11dca71 Mon Sep 17 00:00:00 2001 From: mengdaoshizhongxinyang Date: Mon, 5 Oct 2026 15:27:42 +0800 Subject: [PATCH] feat: add Vite host and Gitea CI for project57 embed --- .dockerignore | 21 ++ .env.example | 2 + .gitea/workflows/ci.yml | 255 +++++++++++++++++++++++ .gitignore | 4 + Dockerfile | 5 + README.md | 33 +++ index.html | 33 +++ nginx.conf | 16 ++ package.json | 14 ++ pnpm-lock.yaml | 440 ++++++++++++++++++++++++++++++++++++++++ src/main.ts | 66 ++++++ vite.config.ts | 12 ++ 12 files changed, 901 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 index.html create mode 100644 nginx.conf create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/main.ts create mode 100644 vite.config.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..14f4a4c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +node_modules +.pnpm-store +.pnpm-debug.log +.git +.gitignore +.gitattributes +.gitea +.github +.env +.env.local +.env.*.local +.vscode +.idea +*.swp +.DS_Store +Thumbs.db +*.log +README.md +*.md +tmp +temp diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..535e744 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# A 站(project57)源站,用于加载 aurora.js 以及图片 / API +VITE_A_ORIGIN=http://localhost:3000 diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..0ae655c --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,255 @@ +# 对齐 project0(projectBase)CI:Gitea + Docker + K8s +# 本仓为 Vite 静态宿主,构建产物为 dist/,镜像用 nginx 监听 3000 +name: CI Pipeline + +on: + push: + branches: [master] + workflow_dispatch: + +jobs: + check-commit: + runs-on: ubuntu-latest + outputs: + should_build: ${{ steps.check.outputs.should_build }} + should_deploy: ${{ steps.check.outputs.should_deploy }} + should_deploy_only: ${{ steps.check.outputs.should_deploy_only }} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + lfs: false + fetch-depth: 1 + + - name: Check commit message + id: check + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "should_build=true" >> $GITHUB_OUTPUT + echo "should_deploy=true" >> $GITHUB_OUTPUT + echo "should_deploy_only=false" >> $GITHUB_OUTPUT + echo "✅ 手动触发" + else + head_msg="${{ github.event.head_commit.message }}" + echo "检查提交消息: $head_msg" + + if echo "$head_msg" | grep -qE '\[deploy-only\]'; then + echo "should_build=false" >> $GITHUB_OUTPUT + echo "should_deploy=true" >> $GITHUB_OUTPUT + echo "should_deploy_only=true" >> $GITHUB_OUTPUT + echo "✅ 找到 [deploy-only] 标记,只执行部署(跳过构建)" + elif echo "$head_msg" | grep -qE '\[build\]|\[deploy\]'; then + echo "should_build=true" >> $GITHUB_OUTPUT + echo "should_deploy_only=false" >> $GITHUB_OUTPUT + echo "✅ 找到 [build] 或 [deploy] 标记" + + if echo "$head_msg" | grep -qE '\[deploy\]'; then + echo "should_deploy=true" >> $GITHUB_OUTPUT + echo "✅ 找到 [deploy] 标记,将执行构建和部署" + else + echo "should_deploy=false" >> $GITHUB_OUTPUT + echo "✅ 只找到 [build] 标记,只执行构建" + fi + else + echo "should_build=false" >> $GITHUB_OUTPUT + echo "should_deploy=false" >> $GITHUB_OUTPUT + echo "should_deploy_only=false" >> $GITHUB_OUTPUT + echo "ℹ️ 提交消息不包含 [build]、[deploy] 或 [deploy-only] 标记,跳过工作流" + fi + fi + + build: + needs: check-commit + if: needs.check-commit.outputs.should_build == 'true' + runs-on: ubuntu-latest + outputs: + image_ref: ${{ steps.push.outputs.image_ref }} + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + lfs: false + persist-credentials: true + token: ${{ secrets.LFS_PAT || secrets.GITHUB_TOKEN }} + + - name: Force LFS Pull with Custom Auth + run: | + HOST="git.bigmonk.org" + REPO_PATH="${{ github.repository }}" + + if [ -z "${{ secrets.LFS_PAT }}" ]; then + echo "❌ 错误: 未配置 LFS_PAT Secret" + exit 1 + fi + + GIT_TOKEN="${{ secrets.LFS_PAT }}" + git config --local --unset credential.helper || true + git config --global --unset credential.helper || true + + AUTH_REMOTE="https://x-access-token:${GIT_TOKEN}@${HOST}/${REPO_PATH}.git" + AUTH_LFS_URL="https://x-access-token:${GIT_TOKEN}@${HOST}/${REPO_PATH}.git/info/lfs" + + git remote set-url origin "${AUTH_REMOTE}" + git config --local lfs.url "${AUTH_LFS_URL}" + + git lfs install + git lfs pull + echo "✅ LFS files pulled successfully" + + - name: Setup Node.js and pnpm + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install pnpm + run: | + corepack enable + corepack prepare pnpm@12.4.2 --activate + + - name: Configure npm cache proxy and retries + run: | + echo "NPM_CONFIG_REGISTRY=http://172.17.0.1:4873" >> $GITHUB_ENV + echo "npm_config_registry=http://172.17.0.1:4873" >> $GITHUB_ENV + echo "PNPM_CONFIG_REGISTRY=http://172.17.0.1:4873" >> $GITHUB_ENV + pnpm config set registry http://172.17.0.1:4873 + pnpm config set fetch-retries 6 + pnpm config set fetch-retry-factor 2 + pnpm config set fetch-retry-mintimeout 20000 + pnpm config set fetch-retry-maxtimeout 120000 + pnpm config set network-concurrency 8 + pnpm config set child-concurrency 4 + + - name: Install dependencies + run: | + pnpm install --frozen-lockfile --prefer-offline \ + || pnpm install --frozen-lockfile --prefer-offline --registry=https://registry.npmjs.org + + - name: Build Vite application + env: + VITE_A_ORIGIN: ${{ secrets.VITE_A_ORIGIN }} + run: | + if [ -z "${VITE_A_ORIGIN}" ]; then + echo "❌ 未配置 Secret VITE_A_ORIGIN(A 站源站,如 https://swissicapital.net)" + exit 1 + fi + echo "VITE_A_ORIGIN=${VITE_A_ORIGIN}" + pnpm build + echo "✅ Build completed" + if [ -d "dist" ] && [ -f "dist/index.html" ]; then + echo "✅ dist/index.html 存在" + else + echo "❌ dist 构建产物不存在" + exit 1 + fi + + - name: Verify Docker access + run: docker info + + - name: Build Docker image + run: | + export REGISTRY="104.225.236.253:30500" + export REPO_NAME="${{ github.event.repository.name }}" + export IMAGE_LATEST="${REGISTRY}/${REPO_NAME}:latest" + export IMAGE_SHA="${REGISTRY}/${REPO_NAME}:${GITHUB_SHA}" + docker build -t "${IMAGE_LATEST}" -t "${IMAGE_SHA}" . + echo "Image built: ${IMAGE_LATEST}" + echo "Image built: ${IMAGE_SHA}" + + - name: Push to registry (SHA + latest, deploy by digest) + id: push + run: | + set -euo pipefail + export REGISTRY="104.225.236.253:30500" + export REPO_NAME="${{ github.event.repository.name }}" + export IMAGE_LATEST="${REGISTRY}/${REPO_NAME}:latest" + export IMAGE_SHA="${REGISTRY}/${REPO_NAME}:${GITHUB_SHA}" + + docker push "${IMAGE_SHA}" + docker push "${IMAGE_LATEST}" + + IMAGE_REF="${IMAGE_SHA}" + DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${IMAGE_SHA}" 2>/dev/null || true) + if [ -n "${DIGEST}" ]; then + IMAGE_REF="${DIGEST}" + fi + echo "image_ref=${IMAGE_REF}" >> "${GITHUB_OUTPUT}" + echo "✅ Pushed ${IMAGE_SHA} and ${IMAGE_LATEST}" + echo "✅ Rollout ref (digest preferred): ${IMAGE_REF}" + + deploy: + needs: [check-commit, build] + if: needs.check-commit.outputs.should_deploy == 'true' && needs.check-commit.outputs.should_deploy_only != 'true' + runs-on: ubuntu-latest + + steps: + - name: Deploy to K8s + run: | + if [ -z "${{ secrets.KUBECONFIG_BASE64 }}" ]; then + echo "INFO: KUBECONFIG_BASE64 not configured. Deployment skipped." + exit 0 + fi + + mkdir -p ~/.kube + echo "${{ secrets.KUBECONFIG_BASE64 }}" | base64 -d > ~/.kube/config + chmod 600 ~/.kube/config + + kubectl cluster-info || { echo "ERROR: Failed to connect to K8s cluster"; exit 1; } + + export REPO_NAME="${{ github.event.repository.name }}" + export IMAGE_REF="${{ needs.build.outputs.image_ref }}" + export NAMESPACE="ns-${REPO_NAME}" + export DEPLOYMENT="${REPO_NAME}-deploy" + + echo "Deploying ${IMAGE_REF} to ${NAMESPACE}/${DEPLOYMENT}" + kubectl patch "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --type='json' -p='[ + {"op":"replace","path":"/spec/strategy/rollingUpdate/maxSurge","value":"100%"}, + {"op":"replace","path":"/spec/strategy/rollingUpdate/maxUnavailable","value":0}, + {"op":"replace","path":"/spec/template/spec/containers/0/image","value":"'"${IMAGE_REF}"'"}, + {"op":"replace","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"IfNotPresent"}, + {"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/api/health"}, + {"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/timeoutSeconds","value":5}, + {"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/httpGet/path","value":"/api/health"}, + {"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/timeoutSeconds","value":5} + ]' + kubectl rollout status "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --timeout=180s + echo "✅ Deployment completed successfully" + + deploy-only: + needs: check-commit + if: needs.check-commit.outputs.should_deploy_only == 'true' + runs-on: ubuntu-latest + + steps: + - name: Deploy to K8s (without build) + run: | + if [ -z "${{ secrets.KUBECONFIG_BASE64 }}" ]; then + echo "INFO: KUBECONFIG_BASE64 not configured. Deployment skipped." + exit 0 + fi + + mkdir -p ~/.kube + echo "${{ secrets.KUBECONFIG_BASE64 }}" | base64 -d > ~/.kube/config + chmod 600 ~/.kube/config + + kubectl cluster-info || { echo "ERROR: Failed to connect to K8s cluster"; exit 1; } + + export REGISTRY="104.225.236.253:30500" + export REPO_NAME="${{ github.event.repository.name }}" + export IMAGE_LATEST="${REGISTRY}/${REPO_NAME}:latest" + export NAMESPACE="ns-${REPO_NAME}" + export DEPLOYMENT="${REPO_NAME}-deploy" + + echo "Deploying existing image ${IMAGE_LATEST} to ${NAMESPACE}/${DEPLOYMENT}" + kubectl patch "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --type='json' -p='[ + {"op":"replace","path":"/spec/strategy/rollingUpdate/maxSurge","value":"100%"}, + {"op":"replace","path":"/spec/strategy/rollingUpdate/maxUnavailable","value":0}, + {"op":"replace","path":"/spec/template/spec/containers/0/image","value":"'"${IMAGE_LATEST}"'"}, + {"op":"replace","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"Always"}, + {"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/api/health"}, + {"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/timeoutSeconds","value":5}, + {"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/httpGet/path","value":"/api/health"}, + {"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/timeoutSeconds","value":5} + ]' + kubectl rollout status "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --timeout=180s + echo "✅ Deployment completed (using existing image)" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a167bc6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.env +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1b2122c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +# CI 中先 pnpm build 产出 dist/,镜像只负责托管静态文件 +FROM nginx:1.27-alpine +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY dist /usr/share/nginx/html +EXPOSE 3000 diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d97163 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# project57-x + +B 站宿主:跨域加载 A 站(project57)打包的前台 SPA。 + +CI 对齐 **project0**(本地 `projectBase`):提交信息带 `[deploy]` 会构建镜像并部署到 K8s。 +本仓是 Vite 静态站,构建产物为 `dist/`,Docker 用 nginx 监听 **3000**,并提供 `/api/health` 给探针。 + +仓库 Secrets 需配置(与 project0 相同,外加 A 站地址): + +- `LFS_PAT` +- `KUBECONFIG_BASE64` +- `VITE_A_ORIGIN`:A 站线上源站,如 `https://swissicapital.net` + +## 本地运行 + +1. A 站先打包嵌入脚本并启动: + +```bash +cd ../project57 +pnpm build:embed +pnpm dev +``` + +2. 本仓库安装并启动(默认 http://localhost:4173): + +```bash +pnpm install +pnpm dev +``` + +3. 若 A 站端口不是 `3000`,复制 `.env.example` 为 `.env`,改 `VITE_A_ORIGIN`。 + +页面会加载 `${VITE_A_ORIGIN}/embed/aurora.js`,图片和联系表单也走 A 站。路由为 hash,例如 `http://localhost:4173/#/kontakt`。 diff --git a/index.html b/index.html new file mode 100644 index 0000000..8adf9f2 --- /dev/null +++ b/index.html @@ -0,0 +1,33 @@ + + + + + + project57-x + + + +
+
+ + + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..5995db5 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,16 @@ +server { + listen 3000; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location = /api/health { + default_type text/plain; + add_header Content-Type text/plain; + return 200 "ok"; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..be1b399 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "project57-x", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview --port 3000 --strictPort --host" + }, + "devDependencies": { + "vite": "^8.3.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..447dce5 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,440 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + vite: + specifier: ^8.3.2 + version: 8.3.2 + +packages: + + '@oxc-project/types@0.152.0': + resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} + + '@rolldown/binding-android-arm-eabi@1.2.12': + resolution: {integrity: sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.12': + resolution: {integrity: sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.12': + resolution: {integrity: sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.12': + resolution: {integrity: sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.12': + resolution: {integrity: sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + resolution: {integrity: sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.12': + resolution: {integrity: sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.12': + resolution: {integrity: sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + resolution: {integrity: sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.12': + resolution: {integrity: sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.12': + resolution: {integrity: sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.12': + resolution: {integrity: sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.12': + resolution: {integrity: sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.12': + resolution: {integrity: sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.12': + resolution: {integrity: sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + rolldown@1.2.12: + resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + vite@8.3.2: + resolution: {integrity: sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + +snapshots: + + '@oxc-project/types@0.152.0': {} + + '@rolldown/binding-android-arm-eabi@1.2.12': + optional: true + + '@rolldown/binding-android-arm64@1.2.12': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.12': + optional: true + + '@rolldown/binding-darwin-x64@1.2.12': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.12': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.12': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.12': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.12': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.12': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.12': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + detect-libc@2.1.2: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fsevents@2.3.3: + optional: true + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + nanoid@3.3.19: {} + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.2 + + rolldown@1.2.12: + dependencies: + '@oxc-project/types': 0.152.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.12 + '@rolldown/binding-android-arm64': 1.2.12 + '@rolldown/binding-darwin-arm64': 1.2.12 + '@rolldown/binding-darwin-x64': 1.2.12 + '@rolldown/binding-freebsd-x64': 1.2.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.12 + '@rolldown/binding-linux-arm64-gnu': 1.2.12 + '@rolldown/binding-linux-arm64-musl': 1.2.12 + '@rolldown/binding-linux-ppc64-gnu': 1.2.12 + '@rolldown/binding-linux-s390x-gnu': 1.2.12 + '@rolldown/binding-linux-x64-gnu': 1.2.12 + '@rolldown/binding-linux-x64-musl': 1.2.12 + '@rolldown/binding-openharmony-arm64': 1.2.12 + '@rolldown/binding-win32-arm64-msvc': 1.2.12 + '@rolldown/binding-win32-x64-msvc': 1.2.12 + + source-map-js@1.2.2: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + vite@8.3.2: + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.12 + tinyglobby: 0.2.17 + optionalDependencies: + fsevents: 2.3.3 diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..f9c86eb --- /dev/null +++ b/src/main.ts @@ -0,0 +1,66 @@ +const A_ORIGIN = (import.meta.env.VITE_A_ORIGIN || "http://localhost:3000").replace( + /\/$/, + "" +); + +const errorEl = document.getElementById("aurora-error"); + +function showError(message: string) { + if (!errorEl) return; + errorEl.style.display = "block"; + errorEl.textContent = message; +} + +function loadScript(src: string): Promise { + return new Promise((resolve, reject) => { + const script = document.createElement("script"); + script.src = src; + script.async = true; + script.onload = () => resolve(); + script.onerror = () => reject(new Error(`无法加载 ${src}`)); + document.head.appendChild(script); + }); +} + +async function boot() { + try { + await loadScript(`${A_ORIGIN}/embed/aurora.js`); + } catch { + showError( + `无法加载 A 站脚本:${A_ORIGIN}/embed/aurora.js。请先在 project57 执行 pnpm build:embed 并启动 A 站,或修改 .env 中的 VITE_A_ORIGIN。` + ); + return; + } + + const api = window.AuroraSite; + if (!api?.mount) { + showError("A 站脚本已加载,但未找到 AuroraSite.mount。"); + return; + } + + api.mount("#aurora-root", { + assetBase: A_ORIGIN, + apiBase: A_ORIGIN, + cssUrl: `${A_ORIGIN}/embed/aurora.css`, + history: "hash", + }); +} + +boot(); + +declare global { + interface Window { + AuroraSite?: { + mount: ( + target: string | Element, + options?: { + assetBase?: string; + apiBase?: string; + cssUrl?: string; + history?: "hash" | "browser"; + basename?: string; + } + ) => { unmount: () => void }; + }; + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..5240b91 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vite"; + +export default defineConfig({ + server: { + port: 4173, + strictPort: true, + }, + preview: { + port: 4173, + strictPort: true, + }, +});