2 Commits
7 changed files with 208 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
.git
.agents
.codex
node_modules
tests
.env
.env.*
!.env.example
!.env.docker.example
data/*
!data/.gitkeep
*.log
npm-debug.log*
Dockerfile*
compose*.yml
compose*.yaml
README.md
LICENSE
+22
View File
@@ -0,0 +1,22 @@
# 首次启动前请复制为 .env.docker,并替换下面两个值。
# TOTP_ENCRYPTION_KEY 必须至少 32 个字符,部署后不得随意更换。
TOTP_ENCRYPTION_KEY=replace-with-a-random-secret-of-at-least-32-characters
INITIAL_ADMIN_USERNAME=admin
INITIAL_ADMIN_PASSWORD=replace-with-a-strong-admin-password
INITIAL_ADMIN_DISPLAY_NAME=系统管理员
# 下列公开站点信息可按需修改。
PUBLIC_SITE_NAME=海州市教育考试中心
PUBLIC_SITE_CODE=HZ-EDU-032
PUBLIC_SITE_PHONE=0518-8602 3158
PUBLIC_SITE_ADDRESS=江苏省连云港市海州区文教路 18
PUBLIC_SITE_EMAIL=
PUBLIC_SITE_HERO_EYEBROW=HAIZHOU EXAMINATION SERVICE
PUBLIC_SITE_HERO_TITLE=一个报名号,
PUBLIC_SITE_HERO_HIGHLIGHT=贯穿每一次考试。
PUBLIC_SITE_HERO_DESCRIPTION=使用学校下发的报名号登录,完成密码更新和个人信息核验后,即可办理所有考试事项。
PUBLIC_SITE_FOOTER_NOTICE=本平台展示数据仅用于系统演示
# 可选 Redis 缓存;容器外的 Redis 不应填写 127.0.0.1。
# REDIS_URL=redis://redis-host:6379/0
+64
View File
@@ -0,0 +1,64 @@
name: Build and publish Docker image
on:
push:
tags:
- "v*"
workflow_dispatch:
jobs:
publish:
name: Test, build and publish
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: https://github.com/actions/checkout@v4
- name: Set up Node.js
uses: https://github.com/actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Set up QEMU
uses: https://github.com/docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: https://github.com/docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: https://github.com/docker/login-action@v3
with:
registry: docker.io
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Generate image tags and labels
id: metadata
uses: https://github.com/docker/metadata-action@v5
with:
images: docker.io/${{ vars.DOCKERHUB_IMAGE }}
flavor: latest=false
tags: |
type=raw,value=latest,enable=${{ gitea.ref == 'refs/heads/master' }}
type=sha,format=short,prefix=sha-
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push image
uses: https://github.com/docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
cache-from: type=registry,ref=docker.io/${{ vars.DOCKERHUB_IMAGE }}:buildcache
cache-to: type=registry,ref=docker.io/${{ vars.DOCKERHUB_IMAGE }}:buildcache,mode=max
+1
View File
@@ -5,4 +5,5 @@ data/*.sqlite-shm
data/*.sqlite-wal
node_modules/
.env
.env.docker
*.log
+25
View File
@@ -0,0 +1,25 @@
FROM node:22-bookworm-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts \
&& npm cache clean --force
COPY --chown=node:node . .
ENV NODE_ENV=production \
HOST=0.0.0.0 \
PORT=4173 \
DATABASE_CLIENT=sqlite \
SQLITE_PATH=/app/data/exam.sqlite
EXPOSE 4173
VOLUME ["/app/data"]
USER node
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD ["node", "-e", "fetch('http://127.0.0.1:' + (process.env.PORT || '4173') + '/api/public/home').then(response => { if (!response.ok) process.exit(1); }).catch(() => process.exit(1));"]
CMD ["node", "server.mjs"]
+53
View File
@@ -94,6 +94,59 @@ npm start
本地开发无需额外配置,首次运行会自动创建 `data/exam.sqlite` 和完整关系型数据库结构,但不会导入学校、考生、考试或报名测试数据。首次建库只写入系统基础配置和一个超级管理员;账号、密码和显示名可通过 `INITIAL_ADMIN_USERNAME``INITIAL_ADMIN_PASSWORD``INITIAL_ADMIN_DISPLAY_NAME` 设置。当前数据库结构版本为 v17;v16 数据库会自动增加 TOTP 字段,低于 v15 的开发库会提示重建。
### Docker
项目根目录包含生产镜像和 Docker Compose 配置。默认使用 SQLite,数据库保存在命名卷 `exam-information-data` 中,因此重建容器不会丢失数据。
先创建容器环境文件,并将其中的 TOTP 主密钥和初始管理员密码替换为安全随机值:
```powershell
Copy-Item .env.docker.example .env.docker
```
然后构建并启动:
```powershell
docker compose up --build --detach
```
启动完成后访问 <http://127.0.0.1:4173>。查看状态和日志可运行:
```powershell
docker compose ps
docker compose logs --follow app
```
停止服务使用 `docker compose down`;该命令会保留数据库卷。只有明确需要删除全部 SQLite 数据时才使用 `docker compose down --volumes`
也可以只构建镜像:
```powershell
docker build --tag hengzhun-exam-system:local .
```
镜像默认监听 `0.0.0.0:4173`,以非 root 用户运行,并通过 `/api/public/home` 执行健康检查。需要连接 MySQL 或 Redis 时,用运行环境变量覆盖 `DATABASE_CLIENT``DATABASE_URL`/`MYSQL_*``REDIS_URL`;此时 SQLite 数据卷可以移除。
#### Gitea Actions 自动发布到 Docker Hub
工作流位于 `.gitea/workflows/docker-publish.yml`。它会先安装依赖并运行测试,然后构建 `linux/amd64``linux/arm64` 双架构镜像并推送到 Docker Hub。
使用前需要完成以下配置:
1. 在 Docker Hub 创建目标仓库,并创建具有该仓库 Read & Write 权限的访问令牌。
2. 在 Gitea 仓库的 Actions Variables 中添加 `DOCKERHUB_IMAGE`,值为不带 registry 和 tag 的完整镜像名,例如 `yourname/exam-information-system`
3. 在 Gitea 仓库的 Actions Secrets 中添加 `DOCKERHUB_USERNAME``DOCKERHUB_TOKEN`。前者填写 Docker Hub 用户名,后者填写访问令牌,不要填写账户密码。
4. 确保 Gitea Actions 已启用,并且 `ubuntu-latest` Runner 能访问 Docker daemon、GitHub 和 Docker Hub。双架构构建还需要 Runner 允许 QEMU 注册步骤运行。
推送到 `master` 后会发布 `latest``sha-<短提交号>`;推送形如 `v1.2.3` 的 Git 标签后会发布 `1.2.3``1.2` 和对应的提交标签。例如:
```powershell
git tag v1.2.3
git push origin v1.2.3
```
工作流也支持从 Gitea Actions 页面手动运行。构建缓存保存为同一 Docker Hub 仓库中的 `buildcache` 标签,以加快后续构建。
需要清空并重建空业务库时运行 `npm run reset-db`。需要测试数据时再手动运行 `npm run seed-test-data`;导入脚本会读取项目根目录的 `.env`,并根据 `DATABASE_CLIENT` 选择 SQLite 或 MySQL。它会生成 4 所学校、360 名批量考生及 360 条不同状态的报名数据,并明确不生成考场编排计划和准考证。省市区县下拉数据位于 `src/data/china-regions.mjs`,当前版本为国家地名信息库截至 2025-12-31 的三级快照,并补入和康县(653228)与和安县(653229);从新版 CSV 更新时可运行 `node scripts/build-regions.mjs <CSV路径> src/data/china-regions.mjs`
## 数据库配置
+22
View File
@@ -0,0 +1,22 @@
services:
app:
build:
context: .
image: hengzhun-exam-system:local
init: true
restart: unless-stopped
env_file:
- .env.docker
environment:
NODE_ENV: production
HOST: 0.0.0.0
PORT: 4173
DATABASE_CLIENT: sqlite
SQLITE_PATH: /app/data/exam.sqlite
ports:
- "4173:4173"
volumes:
- exam-information-data:/app/data
volumes:
exam-information-data: