这个网站一开始不是为了做一个“模板作品集”,而是想把项目、文章、实验室和访问登记都放进同一个个人系统里。所以技术栈选择不只是看页面好不好写,还要看部署、私有下载、访问日志和后续扩展是否顺手。
最后的公开架构很简单:前端是 Next.js 15、React 19、TypeScript 和 Tailwind;部署在 Vercel;访客登记和访问事件放在 Supabase Postgres;简历和预生成 Lab 结果放在 Supabase private Storage;飞书机器人只做 best-effort 提醒。本地 FastAPI 和真实 Runner 保留给开发验证,不参与公开站运行。

为什么不是纯静态页
如果只是展示几张图和几段介绍,静态页就够了。但我需要访问登记、私有下载、Cookie 授权、下载事件记录和服务端资源白名单。Next.js Route Handlers 刚好能把这些逻辑放在同源服务端里,浏览器不需要直接碰 Supabase Secret,也不需要单独起一个公开 API 服务。
Browser
-> Vercel Next.js
-> same-origin Route Handlers
-> Supabase Postgres
-> Supabase private Storage
-> Feishu notification
买域名其实是最有仪式感的一步
域名是在阿里云买的。买完之后最直观的感受是:一个项目从本地文件夹变成一个真实地址,心理上会突然严肃很多。后面按部署平台的提示配置 DNS,把域名解析到站点,等解析生效,再看到 ggrruu.cn 打开自己的页面,这一步比写很多代码都更像“我真的把它发布出来了”。

- 先在阿里云确认域名归属和 DNS 管理入口。
- 在部署平台添加自定义域名,拿到需要配置的记录。
- 回到阿里云 DNS 控制台添加或调整解析记录。
- 等待解析生效,再检查 HTTPS、根域名和跳转行为。
- 上线后继续检查环境变量、私有资源路径和访问流程。
上线检查单
确认自定义域名解析与 HTTPS;核对生产环境变量只存在于服务端;检查受保护资源未进入 public;验证数据库写入失败时不会放行;确认下载事件写入成功后才返回短时 signed URL。
Supabase 不是数据库摆设,而是访问边界

我没有把简历和结果文件直接放在 public 目录里,而是放进 private Storage。访客先填写姓名、公司、职位和隐私同意,写入 Supabase 成功后才签发访问 Cookie;下载前还会记录事件,再返回短时 signed URL。这个链路看起来比公开 PDF 麻烦,但它更接近真实产品里对受保护资源的处理方式。
也有一些东西我刻意没用
我没有把公开站接到 Render 或 Cloudflare Tunnel,也没有上 Supabase Auth、扫码登录、SMTP 和完整账号体系。作品集网站的目标不是做一个复杂后台,而是在免费或低成本平台上,保留足够真实的访问、日志和下载边界。复杂度要花在能证明能力的地方。
技术栈本身也是作品集的一部分
现在回看,Next.js 负责页面和服务端入口,Vercel 负责发布和域名,Supabase 负责持久化与私有文件,飞书负责提醒,本地 FastAPI 负责真实工具验证。它们拼在一起,不只是“用了哪些平台”,而是在展示一个个人项目如何同时考虑前端表达、后端边界、隐私、安全和后续演示。