feat: 新增记账模块与 API Key 管理 — 支持收支记录、账户管理、N8N 外部集成
This commit is contained in:
+67
-3
@@ -18,6 +18,7 @@ TanStack Start fullstack web app with ORPC (contract-first RPC) and shadcn/ui.
|
||||
- **Hotkeys**: @tanstack/react-hotkeys (`useHotkey` for type-safe keyboard shortcuts)
|
||||
- **Animation**: motion (page transitions, staggered entrance, layout animation)
|
||||
- **Command Palette**: cmdk (via shadcn Command component, triggered by ⌘K)
|
||||
- **API Key**: @better-auth/api-key (API key auth for external integrations like N8N)
|
||||
- **Build**: Vite + Nitro
|
||||
|
||||
## Architecture Overview
|
||||
@@ -27,6 +28,8 @@ TanStack Start fullstack web app with ORPC (contract-first RPC) and shadcn/ui.
|
||||
```
|
||||
/ → Dashboard overview (greeting, quick bookmarks, module summary)
|
||||
/bookmarks → Bookmarks page (view mode + edit mode toggle)
|
||||
/finance → Finance page (transaction list with filters, account/category management)
|
||||
/settings → Settings page (API key management)
|
||||
/setup → One-time owner setup (first visit only, redirects to /login after)
|
||||
/login → Login page (redirects to /setup if no owner exists)
|
||||
```
|
||||
@@ -56,7 +59,7 @@ src/
|
||||
├── client/
|
||||
│ └── orpc.ts # ORPC client (isomorphic: SSR direct call / CSR fetch)
|
||||
├── components/
|
||||
│ ├── AppSidebar.tsx # Unified sidebar (reads module registry, collapsible)
|
||||
│ ├── AppSidebar.tsx # Unified sidebar (reads module registry, collapsible, includes Settings link in footer)
|
||||
│ ├── CommandPalette.tsx # ⌘K command palette (search bookmarks, engines, navigation)
|
||||
│ ├── Error.tsx # Error boundary fallback
|
||||
│ ├── NotFound.tsx # 404 fallback
|
||||
@@ -67,7 +70,19 @@ src/
|
||||
│ └── utils.ts # cn() utility
|
||||
├── modules/
|
||||
│ ├── registry.ts # ModuleMetadata interface + modules[]
|
||||
│ └── bookmarks/ # Bookmarks module (schema, contract, router, components/)
|
||||
│ ├── bookmarks/ # Bookmarks module (schema, contract, router, components/)
|
||||
│ └── finance/ # Finance module
|
||||
│ ├── index.ts # Module metadata
|
||||
│ ├── schema.ts # Drizzle tables (financeAccount, transactionCategory, transaction)
|
||||
│ ├── contract.ts # ORPC contracts (account, category, transaction CRUD + summary)
|
||||
│ ├── router.ts # ORPC handlers
|
||||
│ └── components/ # React UI components
|
||||
│ ├── AccountFormDialog.tsx
|
||||
│ ├── AccountManager.tsx
|
||||
│ ├── CategoryFormDialog.tsx
|
||||
│ ├── CategoryManager.tsx
|
||||
│ ├── TransactionFormDialog.tsx
|
||||
│ └── TransactionList.tsx
|
||||
├── cli/
|
||||
│ ├── index.ts # citty CLI entrypoint (bun run cli ...)
|
||||
│ └── commands/auth.ts # auth reset-password command
|
||||
@@ -76,7 +91,9 @@ src/
|
||||
│ ├── _protected.tsx # Auth guard + unified shell (SidebarProvider, AppSidebar, CommandPalette)
|
||||
│ ├── _protected/
|
||||
│ │ ├── index.tsx # Dashboard overview (greeting, quick bookmarks, module cards)
|
||||
│ │ └── bookmarks.tsx # Bookmarks page (view/edit toggle, Motion animations)
|
||||
│ │ ├── bookmarks.tsx # Bookmarks page (view/edit toggle, Motion animations)
|
||||
│ │ ├── finance.tsx # Finance page (transaction list, filters, account/category management)
|
||||
│ │ └── settings.tsx # Settings page (API key management)
|
||||
│ ├── login.tsx, setup.tsx
|
||||
│ └── api/ # $.ts (OpenAPI), auth.$.ts, health.ts, rpc.$.ts
|
||||
├── server/
|
||||
@@ -340,6 +357,49 @@ Kairos is a self-hosted single-user app. There is NO public registration. The fi
|
||||
- **ORPC middleware**: `authMiddleware` calls `auth.api.getSession({ headers })` → injects `context.user`
|
||||
- **Password reset**: Via server CLI only (`bun run cli auth reset-password`) — no web-based password recovery
|
||||
|
||||
### API Key Authentication
|
||||
|
||||
- **Plugin**: `@better-auth/api-key` with `enableSessionForAPIKeys: true`
|
||||
- **Auth middleware**: Unchanged — API keys automatically simulate sessions via Better Auth plugin
|
||||
- **Header**: `x-api-key: kairos_xxxxxxxx` (or `Authorization: Bearer kairos_xxxxxxxx`)
|
||||
- **Management**: Via `/settings` page UI (create, list, delete)
|
||||
- **Use case**: N8N workflow automation → parse emails → call ORPC API to create transactions
|
||||
- **Default prefix**: `kairos_`
|
||||
|
||||
## Finance Module
|
||||
|
||||
### Data Model
|
||||
|
||||
- **financeAccount**: Bank accounts, wallets, credit cards. Fields: name, type (checking/savings/credit/cash/investment/loan), currencyCode (ISO 4217, default CNY), initialBalance (integer, smallest currency unit), icon, isArchived, orderId
|
||||
- **transactionCategory**: Flat expense/income categories. Fields: name, icon, type (expense/income), orderId
|
||||
- **transaction**: Financial records. Fields: accountId, categoryId (nullable), type (expense/income), amount (integer, positive, smallest currency unit), description, note, date, source (manual/n8n/import), externalId (unique, for N8N dedup)
|
||||
|
||||
### Amount Convention
|
||||
|
||||
- Stored as **integer in smallest currency unit** (e.g., ¥120.30 = 12030)
|
||||
- Always **positive** — type field determines income vs expense
|
||||
- Display: `(amount / 100).toFixed(2)` with currency symbol
|
||||
|
||||
### N8N Integration
|
||||
|
||||
External services call ORPC API with API key:
|
||||
|
||||
```bash
|
||||
curl -X POST https://kairos.example/api/rpc/finance.transaction.create \
|
||||
-H "x-api-key: kairos_xxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"accountId":"...","type":"expense","amount":3500,"description":"午餐","date":"2026-04-01T12:30:00Z","source":"n8n","externalId":"email-abc123"}'
|
||||
```
|
||||
|
||||
- `externalId` enables idempotent deduplication — duplicate imports return existing transaction
|
||||
- `source: "n8n"` marks automated imports
|
||||
|
||||
### ORPC Endpoints
|
||||
|
||||
- `finance.account.list/create/update/remove/reorder`
|
||||
- `finance.category.list/create/update/remove/reorder`
|
||||
- `finance.transaction.list/create/update/remove/summary`
|
||||
|
||||
## Critical Rules
|
||||
|
||||
**DO:**
|
||||
@@ -367,3 +427,7 @@ Kairos is a self-hosted single-user app. There is NO public registration. The fi
|
||||
- Use `@/*` aliases in Drizzle schema files (drizzle-kit can't resolve them)
|
||||
- Add registration/signup functionality (single-owner model, enforced by `databaseHooks`)
|
||||
- Create separate admin pages — integrate view/edit modes in each module page
|
||||
- Use `authClient.apiKey` methods for API key management (not ORPC)
|
||||
- Store amount as float/decimal — amounts MUST be integer (smallest currency unit)
|
||||
- Duplicate `externalId` on transactions — must be unique (for N8N dedup)
|
||||
- Try to retrieve API key after creation — keys are shown only once
|
||||
|
||||
Reference in New Issue
Block a user