跳转至

系统架构

只读盘点快照

本文档为 inklet 技术栈的只读盘点快照,更新日期 2026-07-13。它记录当时代码库的实际状态,供团队共享与新人 onboarding,不应被当作实时事实——版本升级或组件调整后需重新盘点更新。

inklet 是一个云连接的电子墨水屏信息面板平台:后端把结构化数据渲染成 800×480 的黑白/4 级灰点阵图,经 AWS IoT Core 推送到墨水屏设备显示。仓库是一个 多组件 monorepo:Go 后端 + Python 渲染/AI worker + React 前端 + 设备端(Python)/模拟器(Node) + AWS IoT 基建脚本。

核心链路一句话:结构化数据 → render_task/SQS → Jinja2/WeasyPrint 渲染 → Floyd–Steinberg 抖动打包 → S3 → MQTT 通知设备拉取显示。

系统概览

┌──────────────┐     HTTPS      ┌──────────────┐     PostgreSQL     ┌─────────┐
│  portal-web  │ ──────────────▶│   backend    │ ──────────────────▶│   RDS   │
│  (React SPA) │                │  (Go / Chi)  │                    └─────────┘
└──────────────┘                └──┬────────┬──┘
                                   │ SQS     │ MQTT (X.509 mTLS)
                          ┌────────┴───┐ ┌───┴────────┐
                          │  worker    │ │ AWS IoT    │
                          │ (Python)   │ │ Core       │
                          │ 渲染/AI    │ └───┬────────┘
                          └────┬───────┘     │ MQTT (X.509 mTLS)
                               │ S3      ┌───┴────────┐
                          ┌────┴───┐     │  e-ink     │
                          │   S3   │◀────│  devices   │
                          └────────┘拉取 └────────────┘

顶层仓库结构

目录 / 仓库 角色 语言 / 栈 用途
backend/ 服务端 API Go 1.24 / chi / GORM 设备接入、认证、推送、render_task 生命周期、计费
worker/ 后台 worker 群 Python 3.12 渲染管线 + AI 摘要/视觉 + 内容分析 Agent + 抓取
html_template/ 模板库 HTML + Jinja2 + CSS templates/<slug>/{config.json, template.html, schema.json}
portal-web/ Web 前端 + 部署编排 React 19 / Vite / TS 用户控制台 + docker-compose/nginx/caddy/certbot
eink-hw/(即 sim-hw) 设备端固件(模拟) Python 3.11 一进程=一台设备,AWS IoT provisioning + 心跳 + 显示
eink-sim/(即 sim-dashboard) 墨水屏模拟器 Node/TS Fastify + React 把 1bpp 帧缓冲在浏览器里渲染成墨水屏效果
awsiot/ AWS IoT 基建脚本 Bash + JSON Fleet Provisioning by Claim、策略、claim 证书
fake-backend/ 本地云端 mock Python 3.11 订阅心跳→回下行命令,测端到端闭环
InkletDemo/ 3D 空间演示 React / Three.js 房间 3D 模型里可视化设备安放位置(非核心链路)
docs/ 文档站 MkDocs (Material) 架构 / API / 运维文档(本文档所在)

后端(Go)— backend/

  • 语言 / 框架:Go 1.24,HTTP 路由 chi/v5(+ go-chi/cors),标准 net/http,端口 4000。入口 cmd/server/main.go
  • 数据库PostgreSQL 16,ORM 用 GORMgorm.io/driver/postgres,底层 pgx v5),迁移靠 GORM AutoMigrate + 少量裸 SQL 建索引。主要表:users / oauth_accounts / sessions / subscriptions / devices / device_commands / files / raw_upload_items / html_templates / pushes / render_tasks / content_blocks
  • 队列AWS SQS(自封装 queue.SQSQueue),四条队列:SQS_PREPARE_URL(item 准备)/ SQS_RENDER_URL(渲染)/ SQS_ANALYZER_URL(内容分析)/ SQS_SUMMARY_URL。render 流程:后端建 render_task(READY) → 投 render 队列 → worker 渲染完回调 POST /internal/pushes/{id}/rendered → 后端把 push 置为设备当前并经 MQTT 通知设备。
  • 对象存储AWS S3(aws-sdk-go-v2),预签名上传/下载 URL,可选 CloudFront 签名(RSA)。桶由 S3_BUCKET / S3_REGION / S3_ENDPOINT(可指 minio)配。
  • IoT / 设备MQTT 3.1.1 over TLS(mTLS X.509),库 eclipse/paho.mqtt.golang,接 AWS IoT Core。30s 一次离线检查(2min 阈值)。设备绑定三路:NFC 签名 / 6 位 claim code(10min) / NFC 上的 JWT。
  • 对外 API:REST/JSON,JWT(HS256, golang-jwt) Bearer 鉴权 + OAuth(Google/Apple)。主要路由组:/auth(注册登录刷新 / OAuth / me / sessions)、/auth/subscriptionStripe 订阅/结账/门户/发票 + webhook)、/api/devices(设备/健康/状态/昵称/绑定解绑/命令/push 队列/自定义图上传)、/api/raw-items/api/files/{id}/download/api/analyze/trigger/internal/*(worker 回调,X-Internal-Token)。
  • internal/ 分包auth / billing / config / database / file / iot / item / middleware / model / pkg / queue / storage

MQTT 主题结构

主题 方向 用途
inklet/dev/{thing}/up/heartbeat 设备 → 后端 定时心跳
inklet/dev/{thing}/up/state 设备 → 后端 设备状态上报
inklet/dev/{thing}/up/request_claim 设备 → 后端 请求配对码
inklet/dev/{thing}/down/cmd 后端 → 设备 命令下发(text、claim_code、bound、unbound)

渲染 worker(Python)— worker/

  • 打包:Python 3.12(pyproject/setuptools),入口 inklet-workerworkers/cli.py)。多模式单进程:render / summary / download / analyze,SQS 轮询 + ThreadPool 并发。Docker:python:3.12-slim + Pango/Cairo/gdk-pixbuf + fonts-dejavu-core(无 CJK 字体)
  • 渲染管线workers/render_worker.py):取 render_task(JOIN html_templates + pushes)→ 若 CUSTOM_IMAGE 走 S3 下载,否则用 config.title 字符串匹配定位模板目录 → Jinja2(autoescape=False) 渲染 → WeasyPrint HTML→PDF → PyMuPDF(fitz) 栅格化(96dpi)PNG → Pillow 缩放 800×480(LANCZOS) → 灰度 → Floyd–Steinberg 抖动 → 打 1bpp(image2.raw) + 2bpp(4 级灰 image4.raw) + png → S3 render/{user}/{device}/{push}/ → 写 files + 更新 pushes/render_tasks
  • AI 侧summary_worker(文本摘要 + gpt-4o 视觉描述,输出 JSON)/ download_workerfirecrawl 抓链接)/ analyze_workerOpenAI Agents SDK 智能体循环,工具读 notes/templates/devices 并 create_push)。LLM 统一走 litellm(默认 OpenAI,可切 Anthropic),模型由 LLM_MODEL / LLM_VISION_MODEL / AGENT_MODEL 配;prompts 在 worker/prompts/
  • 与后端共享同一 PostgreSQL(psycopg2, DATABASE_URL)和 SQS 队列。

Web 前端 — portal-web/portal-web/

  • 框架React 19 + TypeScript + Vite 6,样式 Tailwind CSS 4,路由 react-router 7,toast 用 sonner,包管理 pnpm。
  • 功能:设备/显示屏管理(列表/详情/昵称)、推送内容队列、Knowledge(AI 摘要的知识条目)、订阅计费、账户/会话/OAuth 回调、明暗主题。
  • 构建/部署:多阶段 Dockerfile(Node 22 pnpm build → Nginx Alpine,端口 3000)。域名 portal.iminklet.com→3000、auth.iminklet.com→4000(Caddy/Nginx 反代 + Let's Encrypt certbot)。
  • 另有 eink-sim(墨水屏模拟器前端)和 InkletDemo(Three.js 3D 演示)两个独立 Vite/React 应用,属工具/演示,非用户控制台。

设备 / 固件侧 — eink-hw/(模拟实现)

  • Python 3.11,依赖 awsiotsdk/awscrt(MQTT) + Pillow。一进程模拟一台 800×480 1bpp 墨水屏;首启走 AWS IoT Fleet Provisioning by Claim 拿永久证书,稳态循环发心跳(30s)、收下行命令、渲染文字并 HTTP POST 帧缓冲(48000B) 给 eink-sim
  • 协议:对云 MQTT(AWS IoT Core);对模拟器 HTTP/WebSocket。真实硬件面板目标 800×480 单色。
  • awsiot/ 是 Bash 脚本一次性建好 IoT 角色/策略/provisioning 模板/claim 证书。

设备生命周期

出厂 → NFC 标签写入签名后的 hw_id
开机 → Fleet Provisioning(claim 证书 → 设备证书)
心跳循环 → 后端在数据库中注册设备
用户绑定设备(NFC 或配对码)
后端下发命令 → 设备渲染到显示屏

部署与运行

  • docker-composeportal-web/docker-compose*.yml):postgres:16 + backend(ghcr.io/inklet-2026/backend) + portal-web;prod 版加内网、挂密钥、仅绑 127.0.0.1、restart 策略。
  • 外部服务:PostgreSQL(dev 容器 / prod RDS)、AWS S3(+CloudFront)、AWS SQS、AWS IoT Core(MQTT)、OAuth(Google/Apple)、Stripe(计费)、OpenAI/Anthropic(LLM)、Firecrawl(抓取)。
  • CI/CD:GitHub Actions release.yml(backend/worker/portal-web 各一份),v* tag 触发 → 构建/测试 → 推 ghcr.io/inklet-2026 镜像(version + latest)。无强制 pre-merge 检查,发布走手动 tag。
  • 部署详见 portal-web/DEPLOY.md,本地联调见 RUNBOOK.md

已知约束与风险点

  • WeasyPrint 引擎:CSS 2.1+,无 JS,flex/grid 支持不完整——模板需按此约束写。
  • 容器无 CJK 字体:worker 镜像只装 DejaVu,中文会渲成豆腐块(影响中文内容渲染,另行处理)。
  • 画布固定 800×480:溢出即裁,模板必须按此尺寸设计。
  • 发布无强制 pre-merge 检查:走手动 v* tag,缺门禁——合并前质量靠人工把关。

关键依赖版本表

以下为盘点时(2026-07-13)从 go.mod / pyproject.toml / package.json / Dockerfile 直接读取的当前锁定版本,供 reviewer 对齐,不穷举传递依赖。

后端 backend/(Go)

组件 / 库 版本
Go 1.24.0
go-chi/chi v5.2.1
gorm.io/gorm v1.25.12
gorm.io/driver/postgres v1.5.11
jackc/pgx v5.5.5(indirect)
aws-sdk-go-v2(core) v1.41.7
aws-sdk-go-v2/service/s3 v1.101.0
aws-sdk-go-v2/service/sqs v1.42.27
eclipse/paho.mqtt.golang v1.5.1
golang-jwt/jwt v5.2.2
stripe/stripe-go v82.1.0
golang.org/x/oauth2 v0.29.0
PostgreSQL(运行时) 16

渲染 worker worker/(Python)

组件 / 库 版本
Python(运行时镜像) 3.12-slim(requires-python >=3.11
Jinja2 >=3.1
Pillow >=10.0
WeasyPrint 未固定(Dockerfile pip install weasyprint,取最新)
PyMuPDF 未固定(Dockerfile pip install pymupdf,取最新)
boto3 >=1.35
psycopg2-binary >=2.9
litellm >=1.40
openai-agents >=0.17.3
firecrawl-py >=1.0
cryptography >=43

Web 前端 portal-web/portal-web/(React)

组件 / 库 版本
react / react-dom ^19.2.4
vite ^6.3.5
tailwindcss / @tailwindcss/vite ^4
react-router-dom ^7.6.1
typescript ^5
sonner ^2.0.7

设备端 eink-hw/(Python)与模拟器 eink-sim/(Node)

组件 / 库 版本
eink-hw · Python >=3.11
eink-hw · awsiotsdk >=1.21.0
eink-hw · awscrt >=0.20.0
eink-hw · Pillow >=10.0
eink-sim · fastify ^5.1.0
eink-sim · @fastify/websocket ^11.0.1
eink-sim · pngjs ^7.0.0
eink-sim · react / react-dom ^19.0.0
eink-sim · vite ^7.0.0
eink-sim · typescript ^5.7.2

各角色该掌握的栈

  • 后端工程师:Go 1.24 / chi / GORM+Postgres / aws-sdk-go-v2(S3·SQS) / paho-MQTT(AWS IoT) / JWT+OAuth / Stripe。核心在 backend/internal/{iot,queue,storage,billing,auth}
  • 渲染 worker 工程师:Python 3.12 / Jinja2 / WeasyPrint / PyMuPDF / Pillow(抖动 · 1bpp/2bpp)/ SQS(boto3) / psycopg2;AI 部分 litellm / OpenAI Agents / firecrawl。牢记 WeasyPrint 限制 + 容器无 CJK 字体 + 画布固定 800×480。
  • 前端工程师:React 19 / TS / Vite / Tailwind4 / react-router;对接后端 REST + JWT。
  • 设备 / 固件工程师:Python(awsiotsdk) + AWS IoT Fleet Provisioning + MQTT 主题约定;模拟器侧 Node/Fastify + React + WebSocket(1bpp 帧协议)。
  • 运维:docker-compose + Nginx/Caddy + certbot;AWS(IoT/S3/SQS/CloudFront)、RDS Postgres;GHCR + tag 触发的 GitHub Actions;awsiot/ 基建脚本。
  • Reviewer / 新人:先读本文档 + RUNBOOK.md + portal-web/DEPLOY.md,核心链路见文首一句话。