feat(operator): partner-style tenant provisioning wizard + admin invite
ci / tc_portal (push) Has been skipped
ci / changes (push) Successful in 4s
ci / tc_booking (push) Has been skipped
ci / tc_website (push) Has been skipped
ci / tc_platform_api (push) Successful in 22s
ci / tc_operator (push) Successful in 24s
ci / build_portal (push) Has been skipped
ci / build_booking (push) Has been skipped
ci / test_platform_api (push) Successful in 32s
ci / build_operator (push) Successful in 31s
ci / build_platform_api (push) Successful in 15s
ci / deploy (push) Successful in 41s
ci / tc_portal (push) Has been skipped
ci / changes (push) Successful in 4s
ci / tc_booking (push) Has been skipped
ci / tc_website (push) Has been skipped
ci / tc_platform_api (push) Successful in 22s
ci / tc_operator (push) Successful in 24s
ci / build_portal (push) Has been skipped
ci / build_booking (push) Has been skipped
ci / test_platform_api (push) Successful in 32s
ci / build_operator (push) Successful in 31s
ci / build_platform_api (push) Successful in 15s
ci / deploy (push) Successful in 41s
The minimal create modal silently dropped adminName/adminEmail — the invite only existed in the partner wizard's server path. Operator now gets the same 5-step wizard UX (organization, domain, first admin, plan with live price catalog, review) composed client-side: POST /tenants creates + provisions, then POST /users/invite-tenant-admin (new, operator-only — lives in UsersModule because UsersModule already imports TenantsModule and the reverse would be circular) runs the same inviteTenantAdmin flow the partner gets, and the result view hands over the single-use recovery link or temp password. Tenant detail page gains an Invite admin action for retries/successors. PLATFORM_TENANT_SLUG back to 'dezky' (the recreated company tenant) + config-rev bump to roll platform-api.
This commit is contained in:
@@ -20,6 +20,7 @@ import type { AuthentikJwtPayload } from '../auth/jwt-payload.interface.js'
|
||||
import { OperatorGuard } from '../auth/operator.guard.js'
|
||||
import { CreateUserDto } from './dto/create-user.dto.js'
|
||||
import { InviteOperatorDto } from './dto/invite-operator.dto.js'
|
||||
import { InviteTenantAdminDto } from './dto/invite-tenant-admin.dto.js'
|
||||
import { UpdateUserDto } from './dto/update-user.dto.js'
|
||||
import { UsersService } from './users.service.js'
|
||||
|
||||
@@ -89,6 +90,24 @@ export class UsersController {
|
||||
})
|
||||
}
|
||||
|
||||
// Operator-only: invite (or attach) a tenant's first admin — the operator
|
||||
// counterpart of the partner wizard's adminName/adminEmail step. Returns
|
||||
// the recovery link / temp password the operator shares manually.
|
||||
@Post('invite-tenant-admin')
|
||||
@UseGuards(OperatorGuard)
|
||||
async inviteTenantAdmin(
|
||||
@Body() dto: InviteTenantAdminDto,
|
||||
@CurrentUser() jwt: AuthentikJwtPayload,
|
||||
@Req() req: Parameters<typeof clientIp>[0],
|
||||
) {
|
||||
const actor = await this.actor.resolve(jwt)
|
||||
return this.users.inviteTenantAdminBySlug(
|
||||
dto.tenantSlug,
|
||||
{ name: dto.name, email: dto.email },
|
||||
{ userId: String(actor._id), email: actor.email, ip: clientIp(req) },
|
||||
)
|
||||
}
|
||||
|
||||
@Get()
|
||||
async findAll(@CurrentUser() jwt: AuthentikJwtPayload) {
|
||||
const actor = await this.actor.resolve(jwt)
|
||||
|
||||
Reference in New Issue
Block a user