202610100007_owner_handles.js 1.94 KB
/**
 * 主人的 id(handle):Agent 的全名是「名字@主人 id」,如 小火子@yibo。
 * - 新表 vk_owner_handles(handle 全局唯一);给已有的人类用户按邮箱 @ 前的部分回填,重名的加数字后缀。
 * - vk_agents 增加 UNIQUE(owner_user_id, name):同一主人名下 Agent 不重名,全名因此全局唯一。
 */
const HANDLES = 'vk_owner_handles'

export async function up(db) {
  await db.schema.createTable(HANDLES, (t) => {
    t.string('user_id', 64).primary()
    t.string('handle', 32).notNullable()
    t.datetime('created_at', { precision: 3 }).notNullable()
    t.unique(['handle'], { indexName: 'uq_vk_owner_handles_handle' })
    t.check("handle regexp '^[a-z0-9][a-z0-9_-]{1,31}$'", [], 'ck_vk_owner_handles_format')
  })

  const users = await db('users').where((q) => q.whereNull('role').orWhereNot({ role: 'agent' })).orderBy('created_at', 'asc').select('id', 'email')
  const taken = new Set()
  const now = new Date()
  for (const user of users) {
    const base = (user.email.split('@')[0].toLowerCase().replace(/[^a-z0-9_-]/g, '-').replace(/^[^a-z0-9]+/, '') || 'user').slice(0, 24).padEnd(2, '0')
    let handle = base
    for (let n = 2; taken.has(handle); n += 1) handle = `${base}-${n}`
    taken.add(handle)
    await db(HANDLES).insert({ user_id: user.id, handle, created_at: now })
  }

  const duplicate = await db('vk_agents').whereNotNull('owner_user_id').groupBy('owner_user_id', 'name').havingRaw('COUNT(*) > 1').first('owner_user_id', 'name')
  if (duplicate) throw new Error(`Cannot make agent names unique per owner: owner_user_id=${duplicate.owner_user_id}, name=${duplicate.name}`)
  await db.schema.alterTable('vk_agents', (t) => t.unique(['owner_user_id', 'name'], { indexName: 'uq_vk_agents_owner_name' }))
}

export async function down(db) {
  await db.schema.alterTable('vk_agents', (t) => t.dropUnique(['owner_user_id', 'name'], 'uq_vk_agents_owner_name'))
  await db.schema.dropTable(HANDLES)
}