Skip to main content

5 posts tagged with "全栈开发"

View All Tags

潮汐账本(Neon 版本)

· 2 min read
Bowen Zhang
本文作者

基于 Neon 官方 Vercel Marketplace 模板迁移的个人记账 Web 应用。

技术组合:Next.js 16 + Vercel + Neon Postgres + Drizzle ORM + Better Auth。项目保留原有的移动端优先账本界面、可用的金额键盘、账单本地解析和导入预览;认证与数据访问已替换为 Better Auth 会话和服务端 Neon API,不依赖 Supabase。

当前功能

  • 响应式首页、账户、计划和报表界面;
  • 支出 / 收入 / 转账记账面板,数字、小数点、退格、清空与保存可用;
  • Better Auth 邮箱 + 密码注册、登录与会话;
  • 首次访问账本 API 时自动建立“日常账本”、微信/支付宝/现金账户和基础分类;
  • 手动记账通过 POST /api/ledger 写入 Neon;首页从 GET /api/ledger 读取真实流水、收入、支出、结余和最近流水;
  • 微信/支付宝 CSV、XLSX、ZIP 文件在浏览器本地解析并预览,原始文件默认不上传;
  • Drizzle migration 已生成,包含 Better Auth 的用户/会话表和账本领域表。

目录说明

src/lib/auth/ Better Auth 配置与生成的认证 schema
src/lib/db/ Neon + Drizzle 数据库连接
src/features/ledger/schema.ts 账本、账户、分类、流水、预算、导入批次表
src/features/ledger/server.ts 默认账本初始化与服务端数据库操作
src/app/api/ledger/ 受 Better Auth 会话保护的账本 API
src/features/importers/ 微信/支付宝/通用账单本地解析
drizzle/ 由 Drizzle Kit 生成、需要应用到 Neon 的 migration

Neon 初始化

  1. 在 Neon 创建一个项目,并创建 development 分支;
  2. 从 Neon 的 Connection Details 获取 pooled DATABASE_URL
  3. 复制 .env.example.env.local,并填写:
DATABASE_URL="postgresql://..."
BETTER_AUTH_SECRET="至少 32 个随机字符"
BETTER_AUTH_BASE_URL="http://localhost:5001"

PowerShell 生成本地开发密钥:

[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 }))
  1. 安装依赖并应用 migration:
npm install
npm run db:migrate
npm run dev
  1. 打开 http://localhost:5001,先创建账号,再登录并记录第一笔账。

Vercel 部署

在 Vercel 项目中安装 Neon Integration 并连接 Neon 的 production/main 分支。然后在 Vercel 的 Production、Preview、Development 环境中配置:

DATABASE_URL=
BETTER_AUTH_SECRET=
BETTER_AUTH_BASE_URL=

Production 的 BETTER_AUTH_BASE_URL 必须是实际线上地址,例如:

BETTER_AUTH_BASE_URL="https://你的实际域名"

每个 Neon 分支使用独立连接串。推荐:

main 正式数据
development 本地开发
preview-* 功能 / Vercel Preview 验证

数据库变更工作流

不要手工修改已经应用到 Neon 的 drizzle/*.sql 文件。后续改数据结构时:

修改 Drizzle schema
→ npm run db:generate
→ 审查 drizzle/ 下新生成的 SQL
→ 先在 Neon development 分支执行 npm run db:migrate
→ 验证后再应用到 main

下一阶段

  • 让月历和趋势图完全按真实 Neon 流水聚合;
  • 补齐分类管理和预算管理;
  • 将账单导入确认结果批量写入 transactions,并实现交易号去重、批次撤销;
  • 为 Preview 部署自动创建/绑定 Neon 分支。

X11 预瞄信息展示应用

· 2 min read
Bowen Zhang
本文作者

实时对接车辆 CarProperty 信号,展示预瞄路面特征、垂向加速度等底盘数据。支持 Web 开发预览与 Android(SS3/SS4 平台)原生部署。

技术栈

技术
前端框架Vue 3.4 + TypeScript + Vite
UIQuasar 2.17
图表ECharts 5.5 + vue-echarts
3D 场景Three.js 0.183
状态管理Pinia
Android 桥接Capacitor 6.1
监控Sentry(Vue + Android 双端)

路由

路径页面说明
/domain-control/preview-infoPreviewInfo预瞄信息主页面(默认)
/hu-cps-toolHUCpsToolHU CPS 调试工具
/car-tests/ss3/car-plugin-testCarPluginTestSS3 CarPlugin 测试
/car-tests/ss3/vehicle-property-testVehiclePropertyTestSS3 VehicleProperty 测试
/car-tests/ss4/car-property-client-testCarPropertyClientTestSS4 CarPropertyClient 测试
/car-tests/ss4/car-diag-testCarDiagTestSS4 诊断测试
/car-tests/ss4/vehicle-property-mapper-testVehiclePropertyMapperTestSS4 属性映射测试
/car-tests/ss4/signal-explorerSignalExplorer信号探索器(默认)
/car-tests/vbs/vbs-testVBSTestVBS 测试

常用命令

# Web 开发
npm run dev # 启动开发服务器(localhost:3012)
npm run build # 生产构建 + 打包 dist.zip
npm run lint # TypeScript + ESLint 检查(0 warnings)

# Android 调试运行(USB,需先启动 npm run dev)
npm run runAndroid # SS4,USB 连接 localhost
npm run runAndroidWIFI # SS4,WiFi 模式
npm run runAndroid:ss3 # 切换 SS3 后运行
npm run runAndroid:ss4 # 切换 SS4 后运行

# Android 打包
npm run buildAndroid # Release APK(自动从 .env.production 注入 Sentry token)
npm run buildAndroid:debug # Debug APK
npm run buildAndroid:ss3 # 切换 SS3 后打包
npm run buildAndroid:ss4 # 切换 SS4 后打包

# 平台切换(单独使用)
npm run switch:ss3 # 切换到 SS3(8155)平台
npm run switch:ss4 # 切换到 SS4(8295)平台

# Capacitor
npm run sync # 同步 web 资产到 Android
npm run openAndroid # 在 Android Studio 中打开

环境变量

敏感配置统一放在 .env.production(已 gitignore,不入库),模板如下:

# 应用配置
VITE_SENTRY_DSN=<your-dsn>
VITE_AUTH_ENV=prod # ontest | prod
VITE_NODE_ENV=production
VITE_APP_PLATFORM=ss4 # ss3 | ss4(由 switch 脚本自动更新)
VITE_BLOB_HANDLE_UPLOAD_URL=<url>
VITE_FUXI_DATA_API=<url>

# Sentry — Web/Vite 构建(sentryVitePlugin)
SENTRY_VUE_AUTH_TOKEN=<token>
SENTRY_VUE_ORG=<org>
SENTRY_VUE_PROJECT=<project>

# Sentry — Android Gradle(由 scripts/build-android.js 注入)
SENTRY_ANDROID_AUTH_TOKEN=<token>
SENTRY_ANDROID_ORG=<org>
SENTRY_ANDROID_PROJECT=<project>

CI/CD 直接设置对应环境变量即可,无需 .env.production 文件。

ADB 调试

npm run adb:logcat # 实时查看应用日志(USB)
npm run adb:logcat:ss4 # SS4 模拟器日志(localhost:5559)
npm run adb:init:ss4 # 初始化 SS4 ADB 转发(5559→5557)
npm run adb:stop # 强制停止应用进程
npm run adb:list # 列出已安装的 fuxi 包

Android 构建说明

  • APK 输出:android/app/build/outputs/apk/
  • Release 命名:x11previewinfo-{version}-{date}-{gitHash}-{flavor}-release.apk
  • Debug 命名:app-s-debug.apk
  • 仅支持 arm64-v8a,minSdkVersion 24
  • Sentry token 通过 scripts/build-android.js.env.production 读取后注入 Gradle 进程

Sentry 监控

项目入口
Web (Vue)vue-x11-preview-infoSentry Dashboard
Androidandroid-x11-preview-info同上

参考文档

20-20-20 护眼助手

· One min read
Bowen Zhang
本文作者

基于 React + TypeScript + Capacitor 8 的 Android 护眼提醒应用,遵循 20-20-20 护眼法则:每 20 分钟提醒用户远眺 20 英尺外 20 秒。

功能

  • 专注计时 — 倒计时模式(默认 20 分钟),时间到自动切换休息阶段
  • 休息提醒 — 20 秒休息倒计时,通过系统通知提醒远眺
  • 可调时长 — 支持自定义专注时长(分钟 + 秒),设置存储于本地
  • 后台校准 — 应用切到后台后自动校准已流逝时间
  • 周期统计 — 显示已完成专注周期数

技术栈

技术
框架React 19 + TypeScript 6
构建Vite 8 + @vitejs/plugin-react
样式Tailwind CSS 4
移动端壳Capacitor 8(Android)
原生能力LocalNotifications、App lifecycle
代码检查oxlint

开发

npm install
npm run dev # 开发服务器(http://localhost:4000)
npm run build # 生产构建
npm run lint # oxlint 检查
npm run preview # 预览构建产物

Android 构建

npm run run:android # 热重载运行到 Android 设备
npm run build:android # 构建 release APK
npm run build:android:debug # 构建 debug APK

Release APK 输出:android/app/build/outputs/apk/release/EyeCare202020-v{version}-release.apk

签名配置通过项目根目录 keystore.properties 文件(不纳入版本控制)。

项目结构

src/
├── main.tsx # 入口,挂载 React 应用 + 初始化 VConsole 调试面板
├── App.tsx # 主计时器组件(SVG 环形进度条、阶段切换、后台恢复)
├── Settings.tsx # 设置页面(专注时长调节、localStorage 持久化)
└── index.css # Tailwind CSS 入口

android/ # Capacitor Android 原生项目

许可

MIT

English Read

· 6 min read
Bowen Zhang
本文作者

A full-stack EPUB reader: look up words while reading, review with Ebbinghaus intervals (1→30 days), plus study stats, leaderboards, and an English / Chinese UI.

Try it online: https://english-read.bitbw.top/


Overview

English Read is a Next.js 15 full-stack web application combining an online EPUB reader with an SRS (Spaced Repetition System) vocabulary learning workflow. Upload EPUBs or browse a shared public library, look up words while reading, and review them with an Ebbinghaus-curve-based flashcard system.

Features

  • EPUB Reader — paginated reading, font size control, reading progress auto-saved
  • Personal Library & Public Library — upload to your shelf; browse and add books from a shared public catalog
  • Vocabulary — collect unknown words from the reader; optional review plan view
  • Spaced Repetition Review — Ebbinghaus intervals: 1d → 2d → 4d → 7d → 15d → 30d → mastered; phrase (multi-word) review distractors can use Vercel AI Gateway when configured; single-word paths may not need it
  • Study Statistics — reading time, average speed (wpm), words reviewed, errors, and review time; bar charts plus daily breakdown; filter by last 7 / 14 / 30 days or a custom range (learning timezone)
  • Leaderboard — community popular public books; reading, review, and overall user rankings (time, speed, streak, study score, etc.); opt out of user boards in Settings
  • Dictionary & Translation — English definitions (Free Dictionary API) + Chinese translation (MyMemory); optional Google Cloud Translation fallback; cached 24 h
  • Authentication — GitHub and Google OAuth; email/password; phone OTP (Aliyun SMS) via NextAuth v5 with JWT sessions
  • Internationalization — English / Chinese UI (next-intl, cookie-based, URL unchanged)
  • Dark Mode — system-aware theme toggle
  • Observability — Sentry error monitoring; optional PostHog and Vercel Analytics

Tech Stack

LayerTechnology
FrameworkNext.js 15 + React 19 (App Router)
LanguageTypeScript (strict)
UIshadcn/ui v4 (@base-ui/react), Tailwind CSS v4
AuthNextAuth v5 (OAuth + Credentials + phone OTP), JWT sessions
DatabaseDrizzle ORM + Neon serverless PostgreSQL
File StorageVercel Blob
i18nnext-intl ^4.9.1
EPUB Engineepubjs
Errors@sentry/nextjs
AnalyticsOptional: PostHog (posthog-js), @vercel/analytics

Getting Started

Prerequisites

  • Node.js 18+ (Node 20 LTS recommended for local dev and production)
  • npm — this repo ships package-lock.json; npm is the documented package manager (yarn/pnpm work only if you align lockfiles yourself)

Installation

git clone <repo-url>
cd english-read
npm install

Environment Variables

Create .env.local in the project root. Minimum for local auth + DB + uploads:

AUTH_SECRET=
AUTH_URL=http://localhost:5000
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
POSTGRES_URL= # Neon pooled connection
POSTGRES_URL_NON_POOLING= # Neon direct connection (used by drizzle-kit)
BLOB_READ_WRITE_TOKEN= # Vercel Blob

Generate AUTH_SECRET with npx auth secret.

Optional integrations (SMS login, AI Gateway for phrase review distractors, translation fallback, analytics) are documented in [.env.example](#).

Database Setup

npx drizzle-kit generate # Generate migration files from schema
npm run db:migrate # Apply migrations (alias for drizzle-kit migrate)
# or: npx drizzle-kit migrate
npx drizzle-kit studio # Open Drizzle Studio (DB browser)

Development

npm run dev # Dev server at http://localhost:5000
npm run build # Production build (includes ESLint)
npm run start # Start production server after build
npm run lint # ESLint only
npx tsc --noEmit # TypeScript check only

Project Structure

src/
├── app/
│ ├── (app)/ # Authenticated shell (Sidebar + Topbar)
│ │ ├── dashboard/
│ │ ├── dashboard/stats/ # Study statistics
│ │ ├── leaderboard/ # Community & user rankings
│ │ ├── library/ # Personal books + upload
│ │ ├── library/store/ # Public library browse & contribute
│ │ ├── vocabulary/ # Word list
│ │ ├── vocabulary/review/ # SRS flashcards
│ │ ├── vocabulary/plan/ # Review planning
│ │ ├── read/[bookId]/ # Full-screen EPUB reader
│ │ └── settings/
│ ├── (auth)/ # login, signup, error (no app shell)
│ ├── api/
│ └── dev/ # Internal/dev-only pages (optional)
├── components/
│ ├── reader/
│ └── ui/
├── lib/
│ ├── db/ # Drizzle schema, migrations, db client
│ ├── srs.ts # Spaced repetition intervals
│ ├── blob.ts # Vercel Blob uploads
│ ├── auth.ts # NextAuth config
│ ├── review-quiz.ts # Review / distractor logic (example)
│ └── … # reading-time, phone-auth, dictionary helpers, etc.
├── i18n/
└── middleware.ts
messages/
├── en.json
└── zh.json

The src/lib/ listing above is not exhaustive — browse the folder for the full set of helpers (e.g. reading-time.ts, phone-auth.ts, aliyun-dypns.ts).

Architecture Notes

Authentication

middleware.ts protects /dashboard, /leaderboard, /library, /read, /vocabulary, and /settings. The app session is JWT (session.strategy: "jwt"); the sessions table remains for the Drizzle adapter / OAuth linking, not as the primary per-request session store. Providers include GitHub, Google, email/password (hashed with bcrypt), and phone OTP (requires Aliyun env vars). Logged-in users hitting /login or /signup are redirected to /dashboard.

EPUB Reader

EpubReader is dynamically imported with { ssr: false }. Paginated flow with pixel dimensions from getBoundingClientRect(); touch swipe is registered on each iframe view.window for multi-iframe layouts.

Spaced Repetition

Stages: 0→1d, 1→2d, 2→4d, 3→7d, 4→15d, 5→30d, 6+→mastered. “Forgot” resets to stage 0. The review queue uses vocabulary.nextReviewAt ≤ now.

UI Components

shadcn/ui v4 on @base-ui/react (not Radix):

  • No asChild — use render={<Link href="..." />}
  • In Server Components, import buttonVariants from @/components/ui/button-variants

Deployment guide

Hosting target is Vercel. Recommended flow: create Storage → register OAuth apps → copy env into .env.local → run npm run db:migrate once against Neon → connect the GitHub repo and deploy. Migration SQL lives in src/lib/db/migrations/ ([drizzle.config.ts](#) uses POSTGRES_URL_NON_POOLING). Optional env keys are listed in [.env.example](#).

1. Create Vercel Storage

Open the Vercel Dashboard → select or create your project → Storage, then provision:

Postgres (Neon)

FieldValue
TypePostgres
PurposeApp data: users, books, vocabulary, reviews, etc.
Auto-injected envPOSTGRES_URL, POSTGRES_URL_NON_POOLING

Link the database to your Vercel project so these variables appear under Settings → Environment Variables (or copy them from the Storage UI for local .env.local).

Blob

FieldValue
TypeBlob
Suggested nameenglish-read-epub
PurposeUploaded EPUB files
Auto-injected envBLOB_READ_WRITE_TOKEN

2. Create OAuth applications

Google

  1. Open Google Cloud Console.

  2. APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client IDs.

  3. Application type: Web application.

  4. Under Authorized redirect URIs, add both local and production callbacks:

    http://localhost:5000/api/auth/callback/google
    https://your-domain.vercel.app/api/auth/callback/google
  5. Save and copy Client ID and Client Secret.

GitHub

  1. GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.
  2. Local development:
    • Homepage URL: http://localhost:5000
    • Authorization callback URL: http://localhost:5000/api/auth/callback/github
  3. Production: use a separate OAuth app or change the callback to https://your-domain.vercel.app/api/auth/callback/github.
  4. Copy Client ID and generate a Client Secret.

3. Configure .env.local

In the project root, create .env.local (same keys as Vercel Production unless you use Preview-specific values):

# Auth.js
AUTH_SECRET= # run: npx auth secret
AUTH_URL=http://localhost:5000

# Google OAuth
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

# GitHub OAuth
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

# Neon — from Vercel Storage / project env
POSTGRES_URL=
POSTGRES_URL_NON_POOLING=

# Vercel Blob
BLOB_READ_WRITE_TOKEN=

Paste the output of npx auth secret into AUTH_SECRET.

4. Apply database migrations

[drizzle.config.ts](#) reads .env.local (via @next/env) and applies migrations from src/lib/db/migrations/ using POSTGRES_URL_NON_POOLING.

  • First deploy / empty database: with POSTGRES_URL_NON_POOLING set (local file pointing at your Neon DB), run:

    npm run db:migrate

    Do not run npx drizzle-kit generate on the server unless you are changing the schema—committed migration files are the source of truth.

  • When you change src/lib/db/schema.ts locally: run npx drizzle-kit generate, commit the new files under src/lib/db/migrations/, deploy, then run npm run db:migrate against each environment that should pick up the change.

Optional: npx drizzle-kit studio opens Drizzle Studio against the same connection.

5. Run locally

npm run dev

Open http://localhost:5000.

6. Deploy on Vercel

Option A — Git integration (recommended)
Import the GitHub repository in Vercel, select the framework (Next.js), add Environment Variables for Production (and Preview if needed), then push to main (or your production branch). Build command is npm run build by default.

Option B — Vercel CLI

npm i -g vercel # if needed
vercel # preview
vercel --prod # production

7. After deployment

  1. Environment variables — Mirror .env.local in Vercel → SettingsEnvironment Variables. Set AUTH_URL to your production site origin (e.g. https://your-domain.vercel.app), not http://localhost:5000.

  2. OAuth — Add production redirect URIs in Google Cloud Console and GitHub OAuth App settings (see §2).

  3. Optional product features — Names and comments match [.env.example](#). Where to obtain:

    VariablesPurposeWhere to obtain
    AI_GATEWAY_API_KEYPhrase (multi-word) review distractors via GET /api/review/similar-words (required for that branch; single-word review may use other sources)Vercel DashboardAIAI Gateway → API keys · Vercel AI Gateway
    GOOGLE_TRANSLATE_API_KEYMachine translation fallback for /api/dictionaryGoogle Cloud Console → enable Cloud Translation APIAPIs & ServicesCredentials → Create API key
    NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOSTPostHog client analyticsPostHogProject settingsProject API Key; host = your region’s ingestion URL (regions, e.g. https://us.i.posthog.com)
    ALIBABA_CLOUD_ACCESS_KEY_ID, ALIBABA_CLOUD_ACCESS_KEY_SECRET, ALIYUN_SMS_SIGN_NAME, ALIYUN_SMS_TEMPLATE_CODE, …Aliyun SMS OTP (DYPNS) loginAccessKey: RAM; SMS / phone verification: 号码认证控制台 (sign & template); RAM needs dypns:SendSmsVerifyCode / CheckSmsVerifyCode; notes in [docs/阿里云/](#)
    SENTRY_AUTH_TOKENUpload source maps during next build (clearer stacks)sentry.io → your organization → SettingsDeveloper SettingsAuth Tokens · Next.js source maps

    Sentry runtime — DSN is embedded; errors are reported when NODE_ENV === "production". SENTRY_AUTH_TOKEN is only required if you want source maps uploaded on Vercel builds.

8. Database tables (overview)

TablePurpose
usersUser profiles (Auth.js); email, phone, password hash
accountsOAuth provider links (Auth.js)
sessionsAuth.js Drizzle adapter table; app uses JWT for the live session, not DB-backed sessions per request
verification_tokensEmail verification tokens (Auth.js)
public_library_booksShared catalog entries
booksPersonal shelf; EPUB blob URL and reading progress
reading_daily_timePer-day reading time (seconds)
vocabularySaved words and SRS schedule
review_logsEach review outcome (remembered / forgotten)

DesktopMobile

next-serverless

· 6 min read
Bowen Zhang
本文作者

基于 Next.js 15 + TypeScript 构建的 Serverless API 服务,集成 NeonDB(PostgreSQL)、Pusher 实时推送、Vercel Blob 文件存储和飞书通知。

快速开始

npm run dev
# or
pnpm dev

访问 http://localhost:3000 查看首页。

环境变量

DATABASE_URL= # NeonDB PostgreSQL 连接字符串(pooled,推荐用于大多数场景)
DATABASE_URL_UNPOOLED= # NeonDB PostgreSQL 连接字符串(不经 pgbouncer,用于需要直连的场景)
BLOB_READ_WRITE_TOKEN= # Vercel Blob 读写 Token

# Pusher(服务端触发事件;前端只需 key + cluster,见下文「前端接入 Pusher」)
PUSHER_APP_ID= # Pusher App ID
PUSHER_KEY= # 前端订阅用公钥(可暴露到浏览器)
PUSHER_SECRET= # 仅服务端,切勿写入前端
PUSHER_CLUSTER= # 例如 ap3

前端接入 Pusher(与本项目配合)

本仓库在 PUT /api/generic/update 更新成功且表名在服务端白名单内时,会调用 pusher.trigger 推送事件。前端使用与后端同一 Pusher 应用key + cluster 即可订阅(不要PUSHER_SECRET 放到前端)。

约定(与 lib/pusher.ts 一致)

项目说明
Channel 名与数据库表名相同(例如 FuxiKuangBiao
事件名当前为 updated(仅 update 接口会触发)
白名单ENABLED_TABLES 中的表会推送;默认包含 FuxiKuangBiao。其它表不会收到事件
Payload{ id, tableName, ...本次 PUT 请求体里除 id/tableName 外的更新字段 }

POST /api/generic/createDELETE /api/generic/delete 内 Pusher 调用当前为注释状态,创建/删除不会推送

安装依赖(前端项目)

npm install pusher-js
# 或 pnpm / yarn 等价安装

最小示例(浏览器 / Vue / React 均可)

YOUR_PUSHER_KEYYOUR_CLUSTER 换成与本服务环境变量 PUSHER_KEYPUSHER_CLUSTER 相同的值(与 lib/pusher.ts 中配置一致)。

import Pusher from 'pusher-js';

const pusher = new Pusher('YOUR_PUSHER_KEY', {
cluster: 'YOUR_CLUSTER',
forceTLS: true,
});

// 与操作的表名一致;仅白名单表在更新时会有事件
const channel = pusher.subscribe('FuxiKuangBiao');

channel.bind('updated', (data: Record<string, unknown>) => {
// data 含 id、tableName 及更新的字段,可据此刷新列表或合并状态
console.log('row updated', data);
});

// 组件卸载时取消订阅,避免泄漏
// channel.unbind_all();
// pusher.unsubscribe('FuxiKuangBiao');

联调注意

  1. 同一应用:前端 key/cluster 必须与部署本 API 时使用的 Pusher 应用一致。
  2. 公开 Channel:当前为公共 channel(表名字符串)。若改为 private- / presence- 前缀,需在服务端配置 Channel authorization,本仓库未内置该接口。
  3. 扩展更多表:在 lib/pusher.tsENABLED_TABLES 中加入表名并重新部署后,对该表执行 PUT /api/generic/update 才会向同名 channel 发 updated

API 接口文档

通用 CRUD 接口 (/api/generic)

这组接口支持对任意数据库表进行增删改查,通过 tableName 参数指定操作的表(默认为 FuxiData)。


POST /api/generic/create — 创建记录

请求体

{
"tableName": "FuxiData",
"field1": "value1",
"field2": "value2"
}
字段类型必填说明
tableNamestring表名,默认 FuxiData
其他字段any写入数据库的字段和值

响应

{
"success": true,
"message": "Record created successfully",
"data": { "id": 1, "field1": "value1", "field2": "value2" }
}

错误码: 400 无字段 / 500 数据库错误


DELETE /api/generic/delete — 删除记录

支持 URL 查询参数或 JSON 请求体两种方式传参。

URL 参数方式

DELETE /api/generic/delete?id=1&tableName=FuxiData

请求体方式

{
"id": 1,
"tableName": "FuxiData"
}
字段类型必填说明
idstring | number记录 ID
tableNamestring表名,默认 FuxiData

响应

{
"success": true,
"message": "Record deleted successfully",
"data": { "id": 1, "field1": "value1" }
}

错误码: 400 缺少 id / 404 记录不存在 / 500 数据库错误


GET /api/generic/query — 查询记录

支持 GET(URL 参数)和 POST(请求体)两种方式。

GET 请求

GET /api/generic/query?tableName=FuxiData&orderBy=id&order=DESC&limit=10&offset=0

POST 请求体

{
"tableName": "FuxiData",
"filters": [
{ "field": "type", "operator": "=", "value": "sensor" },
{ "field": "name", "operator": "ILIKE", "value": "%test%" }
],
"logic": "AND",
"orderBy": "id",
"order": "DESC",
"limit": 10,
"offset": 0
}

参数说明

参数类型必填说明
tableNamestring表名,默认 FuxiData
filtersFilter[]过滤条件数组(GET 时传 JSON 字符串)
logicAND | OR多条件逻辑关系,默认 AND
orderBystring排序字段
orderASC | DESC排序方向,默认 DESC
limitnumber每页条数
offsetnumber偏移量

Filter 结构

{
field: string;
operator: '=' | '<>' | '>' | '>=' | '<' | '<=' | 'LIKE' | 'ILIKE' | 'NOT LIKE' | 'IN' | 'IS NULL' | 'IS NOT NULL';
value: any;
}

响应

{
"success": true,
"data": [...],
"pagination": {
"total": 100,
"limit": 10,
"offset": 0,
"hasMore": true
}
}

错误码: 400 filters 格式错误 / 500 数据库错误


PUT /api/generic/update — 更新记录

请求体

{
"id": 1,
"tableName": "FuxiData",
"field1": "new_value"
}
字段类型必填说明
idstring | number记录 ID
tableNamestring表名,默认 FuxiData
其他字段any要更新的字段和新值

响应

{
"success": true,
"message": "Record updated successfully",
"data": { "id": 1, "field1": "new_value" }
}

错误码: 400 缺少 id 或无更新字段 / 404 记录不存在 / 500 数据库错误

Pusher:对 lib/pusher.tsENABLED_TABLES 白名单内的表,更新成功后会向 channel = 表名 触发事件 updated。前端订阅方式见上文「前端接入 Pusher(与本项目配合)」。


FuxiData 专用接口 (/api/fuxi-data)

这组接口专门操作 FuxiData 表,该表结构为:id(自增主键)、data(JSON 字段)、type(类型标签)、time(东八区时间戳)。


POST /api/fuxi-data/save-data — 保存数据

请求体

{
"data": { "key": "value", "nested": { "foo": "bar" } },
"type": "sensor"
}
字段类型必填说明
dataany要保存的 JSON 数据对象
typestring数据类型标签,默认 default

响应

{
"success": true,
"message": "Data saved successfully",
"data": {
"id": 123,
"data": { "key": "value" },
"type": "sensor",
"time": "2024-03-31T08:30:00.000Z"
}
}

错误码: 400 缺少 data / 500 数据库错误


GET /api/fuxi-data/get-data — 获取数据

三种查询模式,优先级依次为:按 id 查单条 > 按 type 查列表 > 分页查摘要。

按 id 查询(返回完整记录)

GET /api/fuxi-data/get-data?id=123

按 type 查询(支持时间范围)

GET /api/fuxi-data/get-data?type=sensor&startTime=2024-01-01&endTime=2024-12-31

分页查摘要(仅返回 id、time、type)

GET /api/fuxi-data/get-data?limit=10&offset=0

参数说明

参数类型说明
idstring记录 ID,提供时直接返回单条
typestring数据类型,提供时返回该类型所有记录
startTimestring时间范围开始(与 type 配合使用)
endTimestring时间范围结束(与 type 配合使用)
limitstring分页大小,默认 10,最大 100
offsetstring分页偏移,默认 0

错误码: 404 按 id 查询时记录不存在 / 500 数据库错误


PUT /api/fuxi-data/update-data — 更新数据

请求体

{
"id": 123,
"data": { "key": "updated_value" },
"type": "new_type"
}
字段类型必填说明
idstring | number记录 ID
dataany新的 JSON 数据
typestring更新数据类型,不提供则保留原值

time 字段自动更新为东八区当前时间。

响应

{
"success": true,
"message": "Data updated successfully",
"data": {
"id": 123,
"data": { "key": "updated_value" },
"type": "new_type",
"time": "2024-03-31T09:00:00.000Z"
}
}

错误码: 400 缺少 id 或 data / 404 记录不存在 / 500 数据库错误


GET /api/fuxi-data/list-tables — 列出数据库表

列出数据库 public schema 下的所有表及其元信息。

GET /api/fuxi-data/list-tables

响应

{
"success": true,
"message": "Tables retrieved successfully",
"data": {
"totalTables": 2,
"tables": [
{
"schemaname": "public",
"tablename": "FuxiData",
"tableowner": "postgres",
"hasindexes": true,
"hasrules": false,
"hastriggers": false,
"rowsecurity": false,
"rowCount": 1000
}
]
}
}

文件上传接口 (/api/blob)


POST /api/blob/client-upload — 客户端文件上传

基于 Vercel Blob 的客户端直传接口。

请求体: HandleUploadBody(由 @vercel/blob/client 客户端 SDK 自动处理)

限制

项目限制
支持格式PNG、JPEG、WebP
最大文件大小15 MB

响应

{
"type": "success",
"blob": {
"pathname": "/my-file-abc123.png",
"contentType": "image/png",
"url": "https://...",
"downloadUrl": "https://..."
}
}

客户端用法(示例)

import { upload } from '@vercel/blob/client';

const blob = await upload('my-file.png', file, {
access: 'public',
handleUploadUrl: '/api/blob/client-upload',
});

错误码: 400 上传失败


Webhook 接口 (/api/webhooks)


POST /api/webhooks/sentry-feishu — Sentry 错误转飞书通知

接收 Sentry Webhook 事件,格式化后自动发送飞书富文本卡片消息。

Sentry 配置: 在 Sentry 项目设置中将 Webhook URL 设置为该接口地址。

请求体(由 Sentry 自动发送)

{
"data": {
"event": {
"title": "TypeError: Cannot read property 'foo' of undefined",
"datetime": "2024-03-31T08:00:00Z",
"tags": [["device", "desktop"], ["os", "Windows 10"], ["browser", "Chrome 122"]],
"url": "https://your-app.com/page",
"web_url": "https://sentry.io/organizations/xxx/issues/yyy/"
}
}
}

飞书卡片内容

  • 错误发生时间
  • 环境标签(device / os / browser)
  • 错误页面 URL
  • 错误标题
  • 跳转到 Sentry 详情的按钮

响应

{
"message": "ok",
"data": { "code": 0, "msg": "ok" },
"status": 200,
"ok": true
}

错误码: 500 Webhook 处理失败


测试接口


GET /api/hello — Hello 测试

GET /api/hello?name=World

响应

{ "message": "Hello World!" }

技术栈

技术版本用途
Next.js15框架
TypeScript5类型检查
NeonDB1.0.1PostgreSQL 无服务器数据库
Pusher5.2.0实时事件推送
Vercel Blob2.3.2文件存储
TailwindCSS4样式