# aiclass-showcase **Repository Path**: lonelymj/aiclass-showcase ## Basic Information - **Project Name**: aiclass-showcase - **Description**: AI少年班学员作品展示 - **Primary Language**: JavaScript - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-18 - **Last Updated**: 2026-08-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 学员作品展示网站 基于 **Next.js 15 (App Router) + React 19 + PostgreSQL** 的学员作品展示应用,**Docker Compose 一键部署**。 - 前台:展示学员的 **官网 / HTML 游戏 / AI 图片 / AI 视频** 四类作品 - 后台:网页化管理界面,在线新增、编辑、删除作品并上传文件 --- ## 快速开始(Docker Compose,推荐) ### 环境要求 - Docker + Docker Compose(仅此而已,无需本地 Node / PostgreSQL) ### 启动 ```bash # 1. (可选)复制并修改配置,重点是后台密码 cp .env.example .env # 2. 构建并启动(app + postgres 两个容器) docker compose up -d --build # 3. 访问 # 前台 http://localhost:3000 # 后台 http://localhost:3000/admin ``` ### 常用命令 ```bash docker compose ps # 查看状态 docker compose logs -f app # 查看应用日志 docker compose logs -f db # 查看数据库日志 docker compose restart app # 重启应用 docker compose down # 停止(数据保留在卷中) docker compose down -v # 停止并删除数据卷(清空数据!) ``` ### 数据持久化 | 数据 | 存储位置 | |---|---| | PostgreSQL 数据 | 命名卷 `pgdata` | | 后台上传的文件 | 命名卷 `uploads`(容器内 `/app/uploads`,经 `/files/...` 路由访问) | 容器重启 / 重新构建镜像,数据都不会丢失;只有 `docker compose down -v` 才会清空。 ### 配置项(`.env`) | 变量 | 默认值 | 说明 | |---|---|---| | `ADMIN_PASSWORD` | `admin123` | **后台管理密码,生产环境务必修改** | | `APP_PORT` | `3000` | 对外端口 | | `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | `showcase` / `showcase_pass` / `showcase` | 数据库账号(仅首次初始化生效) | --- ## 本地开发(不用 Docker 跑应用) ```bash # 1. 只启动数据库 docker compose up -d db # 2. 配置连接串并启动开发服务器 npm install # Windows PowerShell: $env:DATABASE_URL="postgres://showcase:showcase_pass@localhost:5432/showcase"; npm run dev # Linux / macOS / Git Bash: DATABASE_URL=postgres://showcase:showcase_pass@localhost:5432/showcase npm run dev ``` > 本地开发需要 Node.js ≥ 18(建议 22 LTS)。 > `docker compose up db` 需要把数据库端口映射出来,在 `docker-compose.yml` 的 db 服务下加一行 `ports: ["5432:5432"]` 即可(默认未暴露,仅供容器内访问)。 ⚠️ **同一时间只运行一种模式**(`dev` 和 `start` 共用 `.next`,同时跑会互相覆盖导致 500)。 --- ## 地址 | 页面 | 地址 | 说明 | |---|---|---| | 前台(作品列表) | http://localhost:3000 | 访客访问 | | **管理后台** | http://localhost:3000/admin | 需登录 | ## 前台功能 - 作品卡片:**预览图 + 作品标题 + 学员名称**,顶部按类型筛选 - **官网类**:点击卡片直接**新窗口打开** - **游戏类**:详情页 iframe 内嵌**直接可玩** - **AI 图片**:详情页**轮播图**(箭头 / 圆点 / 自动播放 / 触摸滑动) - **AI 视频**:详情页**点击播放**(带封面海报) ## 管理后台 访问 `/admin`,默认密码 `admin123`(通过 `ADMIN_PASSWORD` 环境变量修改)。 ### 上传作品流程 1. 登录后台 → 「+ 新增作品」 2. 选择类型,填写标题、学员名称、作品介绍 3. 按类型上传文件: | 类型 | 上传内容 | 限制 | |---|---|---| | 官网 | 单个自包含 HTML | ≤5MB | | 游戏 | 单个自包含 HTML | ≤5MB | | AI 图片 | 多张图片(png/jpg/webp/gif) | 每张 ≤20MB | | AI 视频 | mp4 / webm / mov | ≤200MB | | 封面预览图 | 单张图片(可选;图片类默认取第一张) | ≤20MB | 4. 点击「发布作品」,前台立即可见 其他:登录态为 httpOnly Cookie(7 天);删除作品不删除已上传文件;编辑时类型不可更改。 --- ## 目录结构 ``` ├── app/ │ ├── layout.jsx # 根布局 + metadata │ ├── page.jsx # 前台列表页(服务端读库,动态渲染) │ ├── globals.css # 全部样式 │ ├── work/[id]/page.jsx # 前台详情页(游戏/轮播/视频按类型渲染) │ ├── files/[...path]/route.js # uploads/ 文件访问路由(防路径穿越) │ ├── admin/ # 后台:列表 / login / new / edit/[id] │ └── api/ # login / logout / works(CRUD) / upload ├── components/ │ ├── WorksGrid.jsx # 前台卡片网格(客户端筛选) │ ├── Carousel.jsx # 图片轮播 │ └── admin/ # WorkForm / LoginForm / DeleteButton / LogoutButton ├── lib/ │ ├── db.js # PostgreSQL 数据层(pg Pool,自动建表 + 种子导入) │ └── auth.js # 密码校验 + Cookie 会话 ├── data/works.js # 初始作品种子数据(仅表为空时导入) ├── public/ # 构建期静态资源(初始作品素材) ├── Dockerfile # 多阶段构建(standalone 产物) ├── docker-compose.yml # app + postgres 编排 ├── .env.example # 配置模板 ├── legacy-static/ # 最早的纯静态版存档 └── tools/ # 素材生成脚本 ``` ## API 一览(供二次开发) | 方法 | 路径 | 鉴权 | 说明 | |---|---|---|---| | POST | `/api/login` | 否 | `{password}` → 种 Cookie | | POST | `/api/logout` | 否 | 清除 Cookie | | POST | `/api/works` | 是 | 创建作品(JSON) | | PUT | `/api/works/:id` | 是 | 更新作品 | | DELETE | `/api/works/:id` | 是 | 删除作品 | | POST | `/api/upload` | 是 | multipart 上传,`kind=image/video/html` | | GET | `/files/*` | 否 | 读取上传的文件 | --- ## 部署到服务器 ```bash git clone && cd aiclass-showcase cp .env.example .env # 修改 ADMIN_PASSWORD 等 docker compose up -d --build ``` - 应用镜像基于 `node:22-alpine`,使用 Next.js `standalone` 输出,体积小、启动快 - app 容器通过健康检查等待 PostgreSQL 就绪后才启动 - 反向代理(Nginx / Caddy)转发到 `APP_PORT` 即可上 HTTPS ## 常见问题 **Q:`docker compose up` 拉取镜像失败(docker.io 超时)?** A:配置镜像加速器:编辑 `~/.docker/daemon.json`,加入 `"registry-mirrors": ["https://docker.m.daocloud.io"]`,重启 Docker 后重试。 **Q:页面 500,报 `routes-manifest.json` 找不到?** A:本机同时跑了 `npm run dev` 和 `npm start`,`.next` 被互相覆盖。停掉全部后重新 build。 **Q:修改 `data/works.js` 没生效?** A:种子数据只在数据库表为空时导入一次。之后的作品管理请用后台,或 `docker compose down -v` 清空后重启重新导入。 **Q:浏览器控制台 hydration mismatch 警告?** A:浏览器扩展注入属性导致,已通过 `suppressHydrationWarning` 处理,不影响使用。