mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-24 23:22:31 +08:00
* feat: remove Name field from registration form - Remove name from signUpSchema; send empty string to satisfy DB NOT NULL - Reorder sign-up form: Email → Username → Password → Invite Code - createPersonalOrg falls back to username when name is empty - Add username to UserWithOrg type (server and frontend), typed as string - Sidebar and admin users table show username when name is empty - Update all e2e helpers and specs to remove Name field interactions Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f * fix: update signUpSchema tests to match new schema (email, username, password) Remove name field tests; add username validation tests. Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f --------- Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
76 lines
2.0 KiB
TypeScript
76 lines
2.0 KiB
TypeScript
import { and, count, desc, eq, sql } from 'drizzle-orm'
|
|
import { member, organization, user } from '../db/auth-schema'
|
|
import type { Database } from '../platform/interface'
|
|
|
|
export interface UserWithOrg {
|
|
id: string
|
|
name: string
|
|
username: string
|
|
email: string
|
|
role: string | null
|
|
banned: boolean | null
|
|
createdAt: Date
|
|
orgId: string | null
|
|
orgName: string | null
|
|
}
|
|
|
|
export async function listUsers(
|
|
db: Database,
|
|
page: number,
|
|
pageSize: number,
|
|
): Promise<{ items: UserWithOrg[]; total: number }> {
|
|
const offset = (page - 1) * pageSize
|
|
|
|
const countRows = await db.select({ total: count() }).from(user)
|
|
const total = countRows[0]?.total ?? 0
|
|
|
|
const rows = await db
|
|
.select({
|
|
id: user.id,
|
|
name: user.name,
|
|
username: user.username,
|
|
email: user.email,
|
|
role: user.role,
|
|
banned: user.banned,
|
|
createdAt: user.createdAt,
|
|
orgId: organization.id,
|
|
orgName: organization.name,
|
|
})
|
|
.from(user)
|
|
.leftJoin(member, eq(member.userId, user.id))
|
|
.leftJoin(
|
|
organization,
|
|
and(eq(organization.id, member.organizationId), eq(organization.slug, sql`'personal-' || ${user.id}`)),
|
|
)
|
|
.groupBy(user.id)
|
|
.orderBy(desc(user.createdAt))
|
|
.limit(pageSize)
|
|
.offset(offset)
|
|
|
|
const items: UserWithOrg[] = rows.map((row) => ({
|
|
...row,
|
|
username: row.username ?? '',
|
|
}))
|
|
|
|
return { items, total }
|
|
}
|
|
|
|
export async function setUserStatus(db: Database, userId: string, status: 'active' | 'disabled'): Promise<boolean> {
|
|
const existing = await db.select({ id: user.id }).from(user).where(eq(user.id, userId))
|
|
if (existing.length === 0) return false
|
|
|
|
await db
|
|
.update(user)
|
|
.set({ banned: status === 'disabled' })
|
|
.where(eq(user.id, userId))
|
|
return true
|
|
}
|
|
|
|
export async function deleteUser(db: Database, userId: string): Promise<boolean> {
|
|
const existing = await db.select({ id: user.id }).from(user).where(eq(user.id, userId))
|
|
if (existing.length === 0) return false
|
|
|
|
await db.delete(user).where(eq(user.id, userId))
|
|
return true
|
|
}
|