diff --git a/README.md b/README.md deleted file mode 100644 index 643b952a..00000000 --- a/README.md +++ /dev/null @@ -1,59 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Git Hooks - -项目使用 `.githooks/` 目录存放**仓库内**的 git hooks(**不**在 `.git/hooks/`)。**clone 后需手动启用**: - -```bash -git config core.hooksPath .githooks -``` - -### 启用的 Hook - -#### `post-receive` - -**触发场景**:`git push` 到本仓库时(在 **bare repo** 或 `receive.denyCurrentBranch=updateInstead` 的非 bare repo 上)。 - -**行为**:自动执行 `pnpm run build:start`,即 `next build && next start`。 - -**注意事项**: - -- 本仓库的 `.githooks/post-receive` **不**会**自动**生效 —— 必须在每个工作副本上跑一次上面的 `git config` 命令 -- `core.hooksPath` 是**本地** git config(写入 `.git/config`),**不**随仓库提交 -- `post-receive` 在**接收端**触发 —— 即 push 目标机器(生产 / 测试服务器)的 hook,不是本地开发机的 - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. -modify diff --git a/docs/backend/FRONTEND_PROACTIVE_WAKEUP_API.md b/docs/backend/FRONTEND_PROACTIVE_WAKEUP_API.md new file mode 100644 index 00000000..7c3358b2 --- /dev/null +++ b/docs/backend/FRONTEND_PROACTIVE_WAKEUP_API.md @@ -0,0 +1,206 @@ +# Elio Proactive Wakeup API + +Target environment: `pro` pre-release. + +Base URL: + +```text +https://proapi.banlv-ai.com +``` + +All endpoints require: + +```http +X-Admin-Token: +Content-Type: application/json +``` + +`ADMIN_VIEW_TOKEN` can only read rules. Updating rules or running scans requires `ADMIN_TOKEN`. + +## Status + +The proactive wakeup scheduler is deployed on `pro`, but real sending is guarded by environment flags: + +```text +ENABLE_PROACTIVE_WAKEUP=false +PROACTIVE_WAKEUP_DRY_RUN=true +``` + +With the current default, the hourly scheduler skips sending. The admin UI can still manually call dry-run for monitoring. + +## Rule Model + +```ts +type ProactiveRule = { + id: string; + rule_key: + | "global_anti_harassment" + | "festival" + | "social_post_share" + | "dormant_wakeup"; + category: "guardrail" | "festival" | "social_post" | "dormant"; + enabled: boolean; + priority: number; + trigger_conditions: Record; + behavior_strategy: Record; + frequency_limit: Record; + created_at: string; + updated_at: string; +}; +``` + +Default rules: + +| Rule | Purpose | Main Limits | +| --- | --- | --- | +| `festival` | Valentine, Christmas, birthday personalized wakeup | Max 1 per festival cycle | +| `social_post_share` | Share Elio latest Facebook post | Max 1 per FB post event | +| `dormant_wakeup` | 48h inactivity wakeup | Max 1 per dormant cycle | +| `global_anti_harassment` | Stop waking users after 7 idle days | Global suppression | + +## List Rules + +```http +GET /api/admin/proactive/rules +``` + +Example: + +```bash +curl -H "X-Admin-Token: $ADMIN_TOKEN" \ + https://proapi.banlv-ai.com/api/admin/proactive/rules +``` + +Response: + +```json +{ + "success": true, + "data": [ + { + "id": "8b14a3bf-6c01-4d0c-85b5-613399cb8f14", + "rule_key": "dormant_wakeup", + "category": "dormant", + "enabled": true, + "priority": 30, + "trigger_conditions": { + "min_idle_hours": 48, + "min_user_messages": 5 + }, + "behavior_strategy": { + "mode": "ai_dormant_wakeup", + "tone": "soft_missing", + "send_channel": "cozsweet" + }, + "frequency_limit": { + "cycle_scope": "dormant_period", + "max_per_cycle": 1 + }, + "created_at": "2026-06-29T07:38:46.155433+00:00", + "updated_at": "2026-06-29T10:52:11.041169+00:00" + } + ] +} +``` + +## Update Rule + +```http +PATCH /api/admin/proactive/rules/{rule_key} +``` + +Allowed request fields: + +```ts +type UpdateProactiveRuleRequest = { + enabled?: boolean; + priority?: number; + trigger_conditions?: Record; + behavior_strategy?: Record; + frequency_limit?: Record; +}; +``` + +Example: + +```bash +curl -X PATCH \ + -H "X-Admin-Token: $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"enabled":true}' \ + https://proapi.banlv-ai.com/api/admin/proactive/rules/dormant_wakeup +``` + +Response: + +```json +{ + "success": true, + "data": { + "rule_key": "dormant_wakeup", + "enabled": true, + "priority": 30, + "trigger_conditions": { + "min_idle_hours": 48, + "min_user_messages": 5 + } + } +} +``` + +## Run Scan + +```http +POST /api/admin/proactive/run?dry_run=true +``` + +Query parameters: + +| Name | Type | Default | Meaning | +| --- | --- | --- | --- | +| `dry_run` | boolean | `true` | `true` previews eligible candidates; `false` sends and writes delivery records only when `ENABLE_PROACTIVE_WAKEUP=true`. | + +Example: + +```bash +curl -X POST \ + -H "X-Admin-Token: $ADMIN_TOKEN" \ + "https://proapi.banlv-ai.com/api/admin/proactive/run?dry_run=true" +``` + +Current pro response with no eligible users: + +```json +{ + "success": true, + "data": { + "enabled": false, + "dryRun": true, + "candidates": 0, + "sent": 0, + "items": [] + } +} +``` + +When candidates exist, `items` contains rule/cycle context and, in dry-run mode, generated preview messages. + +## Frontend States + +Handle these states in the admin UI: + +| State | How to detect | UI suggestion | +| --- | --- | --- | +| Unauthorized | HTTP `403`, `message=admin token required` | Ask operator to enter an admin token. | +| No candidates | `success=true`, `data.candidates=0` | Show an empty state. | +| Dry-run preview | `data.dryRun=true`, `items.length>0` | Show candidate list and preview text. | +| Sending disabled | `data.enabled=false` | Display that real sending is off in environment config. | +| Send executed | `data.dryRun=false`, `data.sent>0` | Show sent count and delivery rows. | +| Partial failure | Any item with `status="failed"` | Show per-user failure reason and keep successful rows. | + +## Notes + +- User eligibility requires cozsweet/web user messages `>= 5` and last interaction older than the rule threshold. +- The global anti-harassment rule suppresses wakeups once the user has been idle for 7 days. +- Birthday wakeups require birthday data. The migration includes `users.birthday`; if the column is not present yet, birthday matching is skipped without breaking other rules. +- Facebook post sharing requires inserting an `elio_fb_post` row into `proactive_message_events`. diff --git a/docs/backend/FRONTEND_PROACTIVE_WAKEUP_API.zh-CN.md b/docs/backend/FRONTEND_PROACTIVE_WAKEUP_API.zh-CN.md new file mode 100644 index 00000000..cb4a7b7c --- /dev/null +++ b/docs/backend/FRONTEND_PROACTIVE_WAKEUP_API.zh-CN.md @@ -0,0 +1,206 @@ +# Elio 主动唤醒 API + +目标环境:`pro` 预发布环境。 + +基础地址: + +```text +https://proapi.banlv-ai.com +``` + +所有接口都需要: + +```http +X-Admin-Token: +Content-Type: application/json +``` + +`ADMIN_VIEW_TOKEN` 只能读取规则。更新规则或运行扫描需要使用 `ADMIN_TOKEN`。 + +## 状态 + +主动唤醒调度器已经部署在 `pro` 环境,但真实发送受环境变量控制: + +```text +ENABLE_PROACTIVE_WAKEUP=false +PROACTIVE_WAKEUP_DRY_RUN=true +``` + +在当前默认配置下,每小时调度器会跳过真实发送。管理后台仍然可以手动调用 dry-run,用于监控和预览。 + +## 规则模型 + +```ts +type ProactiveRule = { + id: string; + rule_key: + | "global_anti_harassment" + | "festival" + | "social_post_share" + | "dormant_wakeup"; + category: "guardrail" | "festival" | "social_post" | "dormant"; + enabled: boolean; + priority: number; + trigger_conditions: Record; + behavior_strategy: Record; + frequency_limit: Record; + created_at: string; + updated_at: string; +}; +``` + +默认规则: + +| 规则 | 用途 | 主要限制 | +| --- | --- | --- | +| `festival` | 情人节、圣诞节、生日等个性化唤醒 | 每个节日周期最多 1 次 | +| `social_post_share` | 分享 Elio 最新 Facebook 帖子 | 每个 Facebook 帖子事件最多 1 次 | +| `dormant_wakeup` | 48 小时未活跃用户唤醒 | 每个沉睡周期最多 1 次 | +| `global_anti_harassment` | 用户闲置 7 天后停止唤醒 | 全局抑制规则 | + +## 获取规则列表 + +```http +GET /api/admin/proactive/rules +``` + +示例: + +```bash +curl -H "X-Admin-Token: $ADMIN_TOKEN" \ + https://proapi.banlv-ai.com/api/admin/proactive/rules +``` + +响应: + +```json +{ + "success": true, + "data": [ + { + "id": "8b14a3bf-6c01-4d0c-85b5-613399cb8f14", + "rule_key": "dormant_wakeup", + "category": "dormant", + "enabled": true, + "priority": 30, + "trigger_conditions": { + "min_idle_hours": 48, + "min_user_messages": 5 + }, + "behavior_strategy": { + "mode": "ai_dormant_wakeup", + "tone": "soft_missing", + "send_channel": "cozsweet" + }, + "frequency_limit": { + "cycle_scope": "dormant_period", + "max_per_cycle": 1 + }, + "created_at": "2026-06-29T07:38:46.155433+00:00", + "updated_at": "2026-06-29T10:52:11.041169+00:00" + } + ] +} +``` + +## 更新规则 + +```http +PATCH /api/admin/proactive/rules/{rule_key} +``` + +允许的请求字段: + +```ts +type UpdateProactiveRuleRequest = { + enabled?: boolean; + priority?: number; + trigger_conditions?: Record; + behavior_strategy?: Record; + frequency_limit?: Record; +}; +``` + +示例: + +```bash +curl -X PATCH \ + -H "X-Admin-Token: $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"enabled":true}' \ + https://proapi.banlv-ai.com/api/admin/proactive/rules/dormant_wakeup +``` + +响应: + +```json +{ + "success": true, + "data": { + "rule_key": "dormant_wakeup", + "enabled": true, + "priority": 30, + "trigger_conditions": { + "min_idle_hours": 48, + "min_user_messages": 5 + } + } +} +``` + +## 运行扫描 + +```http +POST /api/admin/proactive/run?dry_run=true +``` + +查询参数: + +| 名称 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `dry_run` | boolean | `true` | `true` 表示预览符合条件的候选用户;`false` 仅在 `ENABLE_PROACTIVE_WAKEUP=true` 时执行真实发送并写入投递记录。 | + +示例: + +```bash +curl -X POST \ + -H "X-Admin-Token: $ADMIN_TOKEN" \ + "https://proapi.banlv-ai.com/api/admin/proactive/run?dry_run=true" +``` + +当前 `pro` 环境在没有符合条件用户时的响应: + +```json +{ + "success": true, + "data": { + "enabled": false, + "dryRun": true, + "candidates": 0, + "sent": 0, + "items": [] + } +} +``` + +当存在候选用户时,`items` 会包含规则、周期上下文;在 dry-run 模式下,还会包含生成的预览消息。 + +## 前端状态 + +管理后台需要处理以下状态: + +| 状态 | 检测方式 | UI 建议 | +| --- | --- | --- | +| 未授权 | HTTP `403`,`message=admin token required` | 提示操作人员输入 admin token。 | +| 无候选用户 | `success=true`,`data.candidates=0` | 展示空状态。 | +| Dry-run 预览 | `data.dryRun=true`,`items.length>0` | 展示候选用户列表和预览文本。 | +| 真实发送已禁用 | `data.enabled=false` | 提示当前环境配置已关闭真实发送。 | +| 已执行发送 | `data.dryRun=false`,`data.sent>0` | 展示发送数量和投递记录行。 | +| 部分失败 | 任意 item 的 `status="failed"` | 展示每个用户的失败原因,同时保留成功记录。 | + +## 备注 + +- 用户符合条件的前提是 cozsweet/web 用户消息数 `>= 5`,并且最后一次互动时间早于规则阈值。 +- 全局反骚扰规则会在用户闲置 7 天后抑制唤醒。 +- 生日唤醒需要生日数据。迁移中包含 `users.birthday`;如果该列尚不存在,生日匹配会被跳过,但不会影响其他规则。 +- Facebook 帖子分享需要向 `proactive_message_events` 插入一条 `elio_fb_post` 记录。 diff --git a/FRONTEND_VIP_CREDIT_API.md b/docs/backend/FRONTEND_VIP_CREDIT_API.md similarity index 100% rename from FRONTEND_VIP_CREDIT_API.md rename to docs/backend/FRONTEND_VIP_CREDIT_API.md diff --git a/PAYWALL_API.md b/docs/backend/PAYWALL_API.md similarity index 100% rename from PAYWALL_API.md rename to docs/backend/PAYWALL_API.md diff --git a/plans.md b/docs/backend/plans.md similarity index 100% rename from plans.md rename to docs/backend/plans.md