←~/progetti/dit/README.md
Dit
{ “日期”: “2026-05-14”, “状态”: “live”, “技术栈模块”: 11 }
人与人之间的最小存在信号 — 无聊天,无消息,只有存在
Dit 是一款移动应用(iOS 和 Android),允许在人与人之间交换最小的存在信号。没有文本,没有图片,没有语音消息 — 只有两个受摩尔斯电码启发的原语:Dit (·) 表示"我在这里",Dah (—) 表示"已收到"。该应用适合任何想要与重要的人保持联系而不受对话压力的人。
## 概念
Dit 基于一个简单的观察:有时你想向某人表明你在想他们 — 而无需制定消息。受到旧习惯的启发,即打电话给某人并在他们接听之前挂断 — 一个免费信号,意思是"想你"或"给我回电"。
·Dit (·) — 向另一个人发送的短信号,可配置生命周期为 1 小时、6 小时或 24 小时
·Dah (—) — 作为响应的确认,表示"已收到"或"我也在这里"
·每对用户没有同时活动的 Dit — 一次一个信号,不再多
·通知音调以摩尔斯电码编码:·· (字母 I = 我) 用于 Dit,··— (字母 U = 你) 用于 Dah
## 核心功能
该应用包括以下功能区域:
·身份验证:电子邮件 + 密码,电子邮件 OTP(无密码),Google OAuth,Facebook OAuth
·联系人系统:按用户名搜索,单方面和静默保存,私人昵称,显示名称解析(昵称 → 姓名 → 用户名)
·实时传递:当接收者在线时,信号通过 WebSocket 立即传递
·推送通知:自定义摩尔斯音调,通知中的直接操作按钮(无需打开应用即可发送 Dah),过期前的计划提醒
·多设备:所有设备上的通知,跨设备关闭同步,注销时删除令牌
·隐私:个人资料可见性(所有人/联系人/无),用户阻止并立即和不可逆地删除数据,无阻止指示的通用 404 响应
·Ping 时间线:两个用户之间所有信号的可视化序列图表示,使用 Skia 在 GPU 上渲染
## 架构
四个独立服务,仅通过共享基础设施通信 — 没有服务直接调用另一个服务:
·dit-api (NestJS + Fastify):用于身份验证、用户、联系人、ping 创建、推送令牌管理的 REST API
·dit-ping (Go):用于实时传递、Dah/Ignore 处理、TTL 监控、存在管理的 WebSocket 引擎
·dit-notifications-worker (Node.js + BullMQ):通过 FCM 推送通知传递,计划提醒,令牌清理
·dit-mobile (React Native + Expo):iOS 和 Android 的跨平台客户端
## 技术栈
技术选择反映产品需求:
·移动端:React Native 0.83+,Expo SDK 55(Bare Workflow),expo-router,React Native Skia,Reanimated 4,Unistyles v3,Zustand,Notifee
·后端 API:NestJS 11,Fastify,BetterAuth,Prisma,ioredis,BullMQ
·实时引擎:Go 1.23+,Fiber v3,nhooyr.io/websocket,go-redis v9,pgx v5
·通知工作器:Node.js,BullMQ,Firebase Admin SDK,Zod
·数据库:PostgreSQL 16 + TimescaleDB 2(具有时间序列分区的 Ping Hypertable)
·缓存和队列:Redis 7(存在、Pub/Sub、BullMQ 队列、TTL 状态)
·身份验证:BetterAuth(自托管),双令牌系统:会话 cookie(REST)+ 短期 HS256 JWT(WebSocket)
·电子邮件:Brevo SMTP 用于事务性电子邮件
·推送:Firebase Cloud Messaging 用于 Android 和 iOS(FCM 到 APNs 代理)
## 设计系统
一个连贯的视觉系统贯穿整个应用:
·主色:有毒绿色(#2EF080 深色,#00B84E 浅色)
·双字体系统:DM Mono 用于身份时刻(时间戳、代码、信号),System UI 用于正文文本
·动画:专门使用 Spring-Physics(Reanimated 4),四个配置的预设
·DitPressable:唯一的交互组件,结合 Gesture Handler + Reanimated + Haptics
·PingTimeline:Skia 画布,带有 Dot-and-Thread 设计,脉冲光环,带工具提示的可点击点
·DitGlow:操作栏下方的 Skia 径向渐变效果,带脉冲动画
·浮动标签栏:药丸形状,带模糊背景和弹簧动画图标
## 重要设计决策
塑造产品和架构的决策:
·两个独立的后端,没有直接的运行时接触:dit-api 通过 REST 创建 ping,dit-ping 通过 WebSocket 处理状态更新 — 仅通过 Redis Pub/Sub 通信
·JWT 独立验证:Go 服务使用相同的密钥验证令牌,无需调用 NestJS API — 零运行时耦合
·ProfileFormatterService 作为唯一的可见性执行器:每个带有用户数据的 API 响应都通过此服务
·阻止返回通用 404:被阻止的用户无法区分"被阻止"和"帐户已删除"
·两个平台的 FCM:Firebase Admin SDK 自动路由 — Android 直接使用 FCM,iOS 使用 FCM 到 APNs 代理
·TimescaleDB 用于 Ping:时间序列分区支持高效的基于时间的查询和自动数据清理
## 我正在学习的内容
开发具有四个独立服务的实时系统的见解:
·从第一天起就定义服务之间的明确所有权边界 — 谁向哪个表写入什么
·Redis Pub/Sub 作为异构服务(TypeScript 和 Go)之间的通信总线,无需直接接触
·隐私作为架构约束:通用错误,集中可见性,破坏性阻止
·优先选择 Spring 动画而不是 withTiming — 感知质量的差异很大
·通知声音作为品牌元素:摩尔斯电码音调使应用在没有视觉标记的情况下立即可识别
Dit 已在 Google Play 商店上线。一个总部位于意大利的欧洲产品。Say nothing. Mean everything.
// 技术栈
[“react-native”, “expo”, “nestjs”, “go”, “postgres”, “redis”, “timescaledb”, “websocket”, “mobile”, “real-time”, “presence”, ]