chore(server): drop flux_grant_batch schema and tables
Why
- Code, routes, service, worker, tests, and ai-context references for the
legacy flux_grant_batch flow were removed in the previous commit. The
schema file and the corresponding production tables were intentionally
left for a separate DDL-only PR (this one) so the destructive change is
easy to time and roll back.
What
- Delete src/schemas/flux-grant-batch.ts.
- Drop the re-export line in src/schemas/index.ts.
- drizzle-kit generate produced drizzle/0011_open_unus.sql:
DROP TABLE flux_grant_batch CASCADE;
DROP TABLE flux_grant_batch_recipient CASCADE;
CASCADE removes the 6 associated indexes in one shot.
- docs/ai-context/architecture-overview.md updated: the dead-code
reminder now points at the migration and explains the rollback story.
Also rolls in a pre-existing local move that was sitting uncommitted:
src/libs/{auth,env,request-auth,ws-auth}.test.ts → src/libs/tests/...
(aligning with the libs/tests/eventa-hono-adapter.test.ts placement that
was already on HEAD).
Deployment
- pnpm typecheck: passes.
- DO NOT run pnpm db:push on prod from this branch automatically. The
drop is intentionally a separate operator action that requires picking
a deploy window where no instance is still on an older image that
could try to read flux_grant_batch. Until 0011 is applied to prod the
table sits as an orphaned shell — safe to leave indefinitely.
This commit is contained in:
@@ -160,4 +160,4 @@ Redis 在这里同时承担:
|
||||
## 当前值得注意的实现信号
|
||||
|
||||
- `/api/v1/openai` 当前开放:`POST /chat/completions`、`POST /chat/completion`、`POST /audio/speech`、`GET /audio/voices`。`handleTranscription` 路由尚未挂载。
|
||||
- `flux_grant_batch` schema 已被简化版 `admin-flux-grants` 取代。代码层(service / route / worker / tests)已删。但 `src/schemas/flux-grant-batch.ts` 仍在并跟随 `schemas/index.ts` 导出,对应生产 DB 表 `flux_grant_batch` / `flux_grant_batch_recipient` 也仍在。下次清理要删 schema 文件 + 生成 drop table migration,属破坏性 DDL,需单独 PR 处理。
|
||||
- `flux_grant_batch` schema 已被简化版 `admin-flux-grants` 取代,代码 + schema 都已清理。`drizzle/0011_open_unus.sql` 是 drop migration(`DROP TABLE flux_grant_batch / flux_grant_batch_recipient CASCADE`,顺带清掉 6 个 index)。这条 DDL 是不可逆破坏,需要操作员在合适的部署窗口手动 `pnpm db:push` 推到 prod;只要 prod DB 还没 apply 0011,回滚 server image 不会丢数据。
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP TABLE "flux_grant_batch" CASCADE;--> statement-breakpoint
|
||||
DROP TABLE "flux_grant_batch_recipient" CASCADE;
|
||||
@@ -3003,4 +3003,4 @@
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -85,6 +85,13 @@
|
||||
"when": 1779100599148,
|
||||
"tag": "0011_common_doctor_octopus",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"version": "7",
|
||||
"when": 1779118725550,
|
||||
"tag": "0012_clumsy_the_stranger",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
import { sql } from 'drizzle-orm'
|
||||
import { bigint, index, integer, pgTable, text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core'
|
||||
|
||||
import { nanoid } from '../utils/id'
|
||||
|
||||
/**
|
||||
* Admin-issued FLUX grant batch (e.g. promotional rounds, customer
|
||||
* compensation rounds, manual top-ups). One row = one batch operation.
|
||||
*
|
||||
* Naming: this table represents the **container** for a single
|
||||
* "send N FLUX to M users" operation, nothing more. It is not a generic
|
||||
* marketing campaign abstraction — adding optional codes / discounts /
|
||||
* referral rewards in future is a different schema, not a column on this one.
|
||||
*
|
||||
* - `type` is a comment-only enum mirroring `flux_transaction.type`. v1 only
|
||||
* supports `'promo'`. Adding a new value here means also extending the
|
||||
* ledger type set.
|
||||
* - `created_by_user_id` is bare text (no FK to user.id) for the same reason
|
||||
* ledger entries are: better-auth hard-deletes user rows and we want the
|
||||
* audit trail to outlive the operator's account.
|
||||
*
|
||||
* status state machine:
|
||||
* created → running → completed (all granted)
|
||||
* created → running → failed_partial (some failed/skipped, no more pending)
|
||||
*/
|
||||
export const fluxGrantBatch = pgTable('flux_grant_batch', {
|
||||
id: text('id').primaryKey().$defaultFn(() => nanoid()),
|
||||
name: text('name').notNull(),
|
||||
type: text('type').notNull(), // 'promo'
|
||||
amount: bigint('amount', { mode: 'number' }).notNull(),
|
||||
description: text('description'),
|
||||
status: text('status').notNull(), // 'created' | 'running' | 'completed' | 'failed_partial'
|
||||
createdByUserId: text('created_by_user_id').notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
startedAt: timestamp('started_at'),
|
||||
completedAt: timestamp('completed_at'),
|
||||
}, table => [
|
||||
index('flux_grant_batch_status_idx').on(table.status),
|
||||
index('flux_grant_batch_created_by_idx').on(table.createdByUserId),
|
||||
])
|
||||
|
||||
/**
|
||||
* Per-recipient row for a flux grant batch. One row per input email.
|
||||
* Resolution (email → userId) happens at batch creation time, so worker
|
||||
* execution is a pure "lookup pending → call creditFlux" loop.
|
||||
*
|
||||
* - `input_email` preserves the operator's original input verbatim
|
||||
* (case included) for auditability. Lookup uses LOWER(email) match.
|
||||
* - `user_id` is nullable: NULL means email did not match any user
|
||||
* (errorReason='not_found') or the row is a duplicate that we kept
|
||||
* only for audit (errorReason='duplicate_in_input').
|
||||
* - `flux_transaction_id` is back-filled after a successful grant so
|
||||
* reports can join the ledger row directly.
|
||||
*
|
||||
* Partial index on `status='pending'` keeps the worker's polling query
|
||||
* cheap regardless of how many granted rows accumulate.
|
||||
*/
|
||||
export const fluxGrantBatchRecipient = pgTable('flux_grant_batch_recipient', {
|
||||
id: text('id').primaryKey().$defaultFn(() => nanoid()),
|
||||
batchId: text('batch_id').notNull(),
|
||||
inputEmail: text('input_email').notNull(),
|
||||
userId: text('user_id'),
|
||||
status: text('status').notNull(), // 'pending' | 'granted' | 'skipped' | 'failed'
|
||||
errorReason: text('error_reason'),
|
||||
fluxTransactionId: text('flux_transaction_id'),
|
||||
attemptCount: integer('attempt_count').notNull().default(0),
|
||||
lastAttemptedAt: timestamp('last_attempted_at'),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
}, table => [
|
||||
index('flux_grant_batch_recipient_batch_status_idx').on(table.batchId, table.status),
|
||||
index('flux_grant_batch_recipient_pending_idx')
|
||||
.on(table.status, table.lastAttemptedAt)
|
||||
.where(sql`status = 'pending'`),
|
||||
uniqueIndex('flux_grant_batch_recipient_batch_email_uniq').on(table.batchId, table.inputEmail),
|
||||
])
|
||||
@@ -2,7 +2,6 @@ export * from './accounts'
|
||||
export * from './characters'
|
||||
export * from './chats'
|
||||
export * from './flux'
|
||||
export * from './flux-grant-batch'
|
||||
export * from './flux-transaction'
|
||||
export * from './llm-request-log'
|
||||
export * from './providers'
|
||||
|
||||
Reference in New Issue
Block a user