fix(03): resolve tenant context for module activation
Controller now falls back to user.tenantId from JWT when req.tenantId is null (SUPER_ADMIN without x-tenant-id header). Also added error display to admin modules page. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -46,7 +46,7 @@ export class ModuleRegistryController {
|
|||||||
*/
|
*/
|
||||||
@Get('active')
|
@Get('active')
|
||||||
async findActive(@Req() req: Request) {
|
async findActive(@Req() req: Request) {
|
||||||
const tenantId = (req as any).tenantId;
|
const tenantId = (req as any).tenantId ?? (req as any).user?.tenantId;
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
throw new ForbiddenException('No tenant context');
|
throw new ForbiddenException('No tenant context');
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ export class ModuleRegistryController {
|
|||||||
@Param('moduleId') moduleId: string,
|
@Param('moduleId') moduleId: string,
|
||||||
@Req() req: Request,
|
@Req() req: Request,
|
||||||
) {
|
) {
|
||||||
const tenantId = (req as any).tenantId;
|
const tenantId = (req as any).tenantId ?? (req as any).user?.tenantId;
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
throw new ForbiddenException('No tenant context');
|
throw new ForbiddenException('No tenant context');
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ export class ModuleRegistryController {
|
|||||||
@Param('moduleId') moduleId: string,
|
@Param('moduleId') moduleId: string,
|
||||||
@Req() req: Request,
|
@Req() req: Request,
|
||||||
) {
|
) {
|
||||||
const tenantId = (req as any).tenantId;
|
const tenantId = (req as any).tenantId ?? (req as any).user?.tenantId;
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
throw new ForbiddenException('No tenant context');
|
throw new ForbiddenException('No tenant context');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export default function AdminModulesPage() {
|
|||||||
const [activations, setActivations] = useState<Map<string, boolean>>(new Map());
|
const [activations, setActivations] = useState<Map<string, boolean>>(new Map());
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [toggling, setToggling] = useState<string | null>(null);
|
const [toggling, setToggling] = useState<string | null>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const isAdmin = currentUser?.role === 'ADMIN' || currentUser?.role === 'SUPER_ADMIN';
|
const isAdmin = currentUser?.role === 'ADMIN' || currentUser?.role === 'SUPER_ADMIN';
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ export default function AdminModulesPage() {
|
|||||||
|
|
||||||
const toggleModule = async (moduleId: string, currentlyActive: boolean) => {
|
const toggleModule = async (moduleId: string, currentlyActive: boolean) => {
|
||||||
setToggling(moduleId);
|
setToggling(moduleId);
|
||||||
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const action = currentlyActive ? 'deactivate' : 'activate';
|
const action = currentlyActive ? 'deactivate' : 'activate';
|
||||||
const res = await fetch(`${API_URL}/modules/${moduleId}/${action}`, {
|
const res = await fetch(`${API_URL}/modules/${moduleId}/${action}`, {
|
||||||
@@ -89,9 +91,12 @@ export default function AdminModulesPage() {
|
|||||||
}
|
}
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
const body = await res.text().catch(() => '');
|
||||||
|
setError(`${res.status}: ${body}`);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch (err) {
|
||||||
// silently fail
|
setError(String(err));
|
||||||
} finally {
|
} finally {
|
||||||
setToggling(null);
|
setToggling(null);
|
||||||
}
|
}
|
||||||
@@ -117,6 +122,12 @@ export default function AdminModulesPage() {
|
|||||||
<p className="mt-1 text-sm text-muted-foreground">{t('description')}</p>
|
<p className="mt-1 text-sm text-muted-foreground">{t('description')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="rounded-md border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<p className="text-muted-foreground">{tCommon('loading')}</p>
|
<p className="text-muted-foreground">{tCommon('loading')}</p>
|
||||||
) : modules.length === 0 ? (
|
) : modules.length === 0 ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user