Skip to content

Commit e396002

Browse files
committed
refactor(data-access): move tag/saved-reply/custom-field logic onto repository pattern
Introduces dedicated tag and saved-reply repositories under packages/database/src/repositories, hashes workspace tokens (new tokenHash column + index), and centralizes the owner quota/trial gate into authorize-workspace-access.ts shared by server actions and oRPC auth. Relocates the affected business- logic tests from apps/builder/__tests__ into package-level __tests__ per repository convention.
1 parent 3278c0e commit e396002

62 files changed

Lines changed: 43074 additions & 3007 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

β€Ž.agents/skills/drizzle-database/SKILL.mdβ€Ž

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,47 @@ import { myModel } from "@chatbotx.io/database/schema"
210210
const item = await findOrFail({ table: myModel, where: { id } })
211211
```
212212

213+
## Repository Convention
214+
215+
Repositories live in `packages/database/src/repositories/<name>/` and are **pure data access**: they receive params, run Drizzle queries, return rows. No caching, no business rules, no audit β€” those belong to the service in `packages/business`.
216+
217+
**Shape (mandatory for new repositories; migrate old ones when touched):**
218+
219+
```
220+
packages/database/src/repositories/<name>/
221+
repository.ts β†’ class <Name>Repository { ... } + export const <name>Repository = new <Name>Repository()
222+
index.ts β†’ export { <name>Repository } from "./repository" + exported param/row types
223+
```
224+
225+
```typescript
226+
// packages/database/src/repositories/tag/repository.ts
227+
import { type DatabaseClient, db } from "../../client"
228+
import { tagModel } from "../../schema"
229+
230+
export type ListTagsParams = { workspaceId: string; page?: number | null }
231+
232+
class TagRepository {
233+
async list(params: ListTagsParams, tx: DatabaseClient = db) {
234+
return await tx.query.tagModel.findMany({ where: { workspaceId: params.workspaceId } })
235+
}
236+
}
237+
238+
export const tagRepository = new TagRepository()
239+
```
240+
241+
Rules:
242+
243+
- **Class + singleton**, not object literals and not loose exported functions. Uniform shape means uniform mocking (`vi.spyOn(tagRepository, "list")`) and room for a shared base (tx injection, tenant scoping, sharding) without rewriting callers.
244+
- **Stateless.** No fields other than what a constructor injects; never hold a cache or request state inside a repository.
245+
- **Method names are short verbs without the resource name**: `list`, `findById`, `findByKey`, `create`, `update`, `delete`, `count`, `incrementX`. `tagRepository.list(...)`, never `tagRepository.listTags(...)`.
246+
- **Accept `tx: DatabaseClient = db` as the last parameter** so the same method works inside and outside `db.transaction()`.
247+
- **Import internally with relative paths** (`../../client`, `../../schema`, `../../utils`) β€” never `@chatbotx.io/database/*` from inside the package.
248+
- **Register once** in `packages/database/src/repositories/index.ts` with `export * from "./<name>"`.
249+
250+
Cache placement: `withCache` / `invalidateCacheTags` (from `@chatbotx.io/redis`) are called in the **service**, wrapping the repository call. The service is the only layer that knows every write path and therefore the only layer that can invalidate correctly.
251+
252+
Create a repository only when the query is reused across services, is complex enough to deserve isolated tests, or needs to swap its data source (e.g. sharding β€” see `message/`). A service with a handful of one-line queries may call `db` directly; do not add a repository as pure indirection.
253+
213254
## Soft-delete convention
214255

215256
`Workspace.scheduledDeletionAt` is the repository's soft-delete convention:

β€Ž.agents/skills/orpc-api/SKILL.mdβ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@ description: >-
1212

1313
- **oRPC** serves both **RPC** (`/rpc`) and **OpenAPI** (`/api`) endpoints
1414
- Base context: `{ headers, user?, workspace? }`
15-
- Two auth stacks: `authorizedAPI` (session) and `workspaceTokenAuthAPI` (header token)
15+
- Three auth stacks: `authorizedAPI` (session), `workspaceTokenAuthAPI` (workspace bearer/query token), and `channelApiTokenAPI` (per-inbox channel API bearer token)
1616
- Routers are plain objects of procedures, composed via object spreading
1717

1818
## Auth Stacks
1919

2020
Defined in `apps/builder/src/orpc.ts`:
2121

2222
- **`authorizedAPI`**: `base` β†’ error mapping β†’ `authMiddleware` (session/cookie auth)
23-
- **`workspaceTokenAuthAPI`**: `base` β†’ error mapping β†’ `workspaceTokenAuthMidddleware` (Authorization: Bearer header)
23+
- **`workspaceTokenAuthAPI`**: `base` β†’ error mapping β†’ `workspaceTokenAuthMidddleware` (Authorization: Bearer header, with a legacy `?token=` query fallback and a deprecated plaintext-token dual-read; see `middlewares/workspace-token-auth.ts`)
24+
- **`channelApiTokenAPI`**: `base` β†’ error mapping β†’ `channelApiTokenAuthMidddleware` (Authorization: Bearer header only β€” no query fallback; token is looked up by hash, never plaintext; scoped to a single inbox, not a whole workspace; see `middlewares/channel-api-token-auth.ts`)
2425

2526
Workspace-scoped procedures add `workspaceAuthorizedMidddleware` per-procedure.
2627

β€Ž.agents/skills/public-api-tooling/SKILL.mdβ€Ž

Lines changed: 0 additions & 80 deletions
This file was deleted.

β€Ž.env.exampleβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ NEXT_PUBLIC_BUILDER_URL=http://localhost:3123
168168
SCHEDULER_BUCKET_RANGE=0-255
169169

170170
# Increase Node.js heap for large workloads. Set per-process, not globally.
171-
# NODE_OPTIONS="--max_old_space_size=4096"
171+
# NODE_OPTIONS="--max_old_space_size=8192"
172172

173173
# ─────────────────────────────────────────────
174174
# Logging

β€ŽAGENTS.mdβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ This file summarizes how **ChatbotX** (this repository) is structured and how to
2323
| `apps/cli` | Command-line client (`chatbotx-cli`). |
2424
| `apps/mcp-server` | MCP server exposing public API surfaces. |
2525
| `apps/javascript-executor` | Internal HTTP service that executes flow-step JavaScript in isolated-vm. |
26-
| `packages/*` | Shared libraries: `database` (Drizzle + PostgreSQL), `ui`, `public-apis`, `sdk`, `worker-config`, `ai`, etc. |
26+
| `packages/*` | Shared libraries: `database` (Drizzle + PostgreSQL), `ui`, `sdk`, `worker-config`, `ai`, etc. |
2727
| `integrations/*` | Channel and vendor integrations (WhatsApp, Messenger, Telegram, Zalo, TikTok, webchat, SMTP, OpenAI, Google Sheets, …). |
2828

2929
## Stack (high level)

β€Žapps/builder/__tests__/delete-tag-action.test.tsβ€Ž

Lines changed: 0 additions & 175 deletions
This file was deleted.

0 commit comments

Comments
Β (0)