Activities of "maliming"

Hi,

We'll get the new release out as soon as we can — the fix is already merged.

In the meantime, restarting ABP Studio and reopening the solution before adding the next web module should let you continue. The crash depends on transient in-memory state that gets reset on a fresh start, so a clean restart between module additions avoids it.

Thanks

Hi,

Good point, you're absolutely right. We checked the docs and there's actually no dedicated page for AppUrlOptions / IAppUrlProvider at all — not just missing from the multi-tenancy section. It's a framework feature that deserves its own documentation.

We're going to add a dedicated Application URLs page covering AppUrlOptions (basic config, IAppUrlProvider, multi-tenant placeholders {0} / {{tenantId}} / {{tenantName}}, host fallback behavior, RedirectAllowedUrls), and cross-link it from the Domain/Subdomain Tenant Resolver section so it shows up exactly where you were looking for it.

I'll follow up here with the PR link once it's opened.

Thanks

Hi,

Yes — the CLI supports the DBMS choice directly on the modern microservice template via -dbms (--database-management-system). Accepted values are sqlserver, mysql, postgresql, oracle, sqlite. So you can run:

abp new YourSolutionName -t microservice --modern -u react -d ef -dbms postgresql --version 10.4.0-rc.1 <other options as before>

The template renders all the PostgreSQL-specific bits automatically: Npgsql package reference in the service csproj, etc/docker/containers/postgresql.yml docker-compose, the run profile entry under etc/abp-studio/run-profiles/Default.abprun.json, connection strings in each service's appsettings.json, and the AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true) in the affected DbContextFactory files (audit-logging and file-management) for timestamp compatibility. No manual edits needed.

For the Helm error — thanks for the update, glad it's no longer reproducing. If it shows up again, please share the full Studio CLI output / stack trace and we'll trace it from there.

Thanks

Hi,

Looking at the stack trace, the AuthServer page fails because ASP.NET Core DataProtection can't load Microsoft.Win32.Registry, 8.0.0.0. That's also why Swagger works but Blazor pages and the OpenIddict login page don't — Swagger doesn't touch DataProtection, while Blazor Server (ComponentStatePersistenceManager.PersistStateAsync) and the antiforgery cookie both go through IDataProtector.Protect and hit the same failing code path.

This is almost certainly a deployment / publish issue rather than an ABP code issue. The usual causes are:

  • PublishTrimmed / PublishSingleFile / PublishAot stripping the transitive dll
  • The deployment folder missing the file after a manual copy
  • A container base image (alpine / chiseled) without it
  • A corrupted or 0-byte Microsoft.Win32.Registry.dll

Before we ask for the full project, could you share these four small files from your published output? They are usually enough to pinpoint it:

  1. <YourApp>.AuthServer.deps.json
  2. <YourApp>.AuthServer.runtimeconfig.json
  3. A DLL list of your publish folder:
# Linux / container
find /path/to/publish -name "*.dll" | sort > dlls.txt

# Windows PowerShell
Get-ChildItem -Path C:\path\to\publish -Recurse -Filter *.dll |
  Select-Object -ExpandProperty FullName | Sort-Object > dlls.txt
  1. Your <YourApp>.AuthServer.csproj (or just the <PropertyGroup> and <ItemGroup> blocks)

It would also help if you can confirm:

  • Where it runs (local dotnet run, IIS, Linux container, Azure App Service, etc.)
  • If it's a container, the base image (e.g. mcr.microsoft.com/dotnet/aspnet:8.0, ...:8.0-alpine, chiseled)
  • Whether a fresh project from the same template reproduces it, or only this one

You can attach the files here, or email them to liming.ma@volosoft.com if you don't want to share them publicly.

Thanks

Hi,

Glad the workflow worked out for you. Quick answers in order:

Helm error when adding a microservice

When you add a microservice via ABP Studio, the wizard runs an "Adjust Helm Config" step only when an etc/helm/ directory exists in the solution (i.e. the solution was generated without --no-kubernetes-configuration). That step performs 6 sub-actions: adjusting build-all-images.ps1, values.<project>-local.yaml, gateways yaml, auth-server yaml, identity yaml, and creating the new Helm chart for the service. Any one of these failing fails the whole wizard.

Could you share the full error message / stack trace from the failing wizard run (the Studio CLI output, or ~/.abp/studio/logs/)? Without the specific error we can't tell which sub-action broke for you, and that's the info we need to pass to the Studio team for a fix. Your workaround (removing etc/helm/ so the step is skipped) is fine in the meantime if you don't need Helm.

Suite "Create user interface" default for React projects

You're right, this should be unchecked automatically. Suite currently doesn't recognize the new React UI — its UiFramework enum only covers MVC, Angular, Blazor (Server / WebApp), and MAUI Blazor, and its ProjectTemplateType doesn't distinguish modern templates from classic. The entity loader defaults ShouldCreateUserInterface to true without any UI-framework check, so when you create an entity in a React solution Suite still tries to generate UI for a framework it doesn't support, and that's what surfaces as the errors you saw.

Until Suite adds detection for the React UI / modern templates, you'll need to manually uncheck "Create user interface" each time. I'll pass this on to the Suite team so they can default it to unchecked for those projects.

lib/api/books.ts for reference

Here you go (this is what the file looks like when you generate with -scp):

/**
 * Books API - IBookAppService.
 */
import { api } from './axios'

export enum BookType {
  Undefined = 0,
  Adventure = 1,
  Biography = 2,
  Dystopia = 3,
  Fantastic = 4,
  Horror = 5,
  Science = 6,
  ScienceFiction = 7,
  Poetry = 8,
}

export const bookTypeOptions: { value: BookType; key: string }[] = [
  { value: BookType.Undefined, key: 'Enum:BookType.0' },
  { value: BookType.Adventure, key: 'Enum:BookType.1' },
  { value: BookType.Biography, key: 'Enum:BookType.2' },
  { value: BookType.Dystopia, key: 'Enum:BookType.3' },
  { value: BookType.Fantastic, key: 'Enum:BookType.4' },
  { value: BookType.Horror, key: 'Enum:BookType.5' },
  { value: BookType.Science, key: 'Enum:BookType.6' },
  { value: BookType.ScienceFiction, key: 'Enum:BookType.7' },
  { value: BookType.Poetry, key: 'Enum:BookType.8' },
]

export interface BookDto {
  id: string
  name?: string
  type: BookType
  publishDate?: string
  price: number
  creationTime?: string
}

export interface CreateUpdateBookDto {
  name: string
  type: BookType
  publishDate: string
  price: number
}

export interface PagedAndSortedResultRequestDto {
  maxResultCount?: number
  skipCount?: number
  sorting?: string
}

export interface PagedResultDto<T> {
  items: T[]
  totalCount: number
}

export async function getBooks(
  params: PagedAndSortedResultRequestDto = {}
): Promise<PagedResultDto<BookDto>> {
  const { data } = await api.get<PagedResultDto<BookDto>>('/app/book', {
    params: {
      maxResultCount: params.maxResultCount ?? 10,
      skipCount: params.skipCount ?? 0,
      sorting: params.sorting,
    },
  })
  return data
}

export async function getBook(id: string): Promise<BookDto> {
  const { data } = await api.get<BookDto>(`/app/book/${id}`)
  return data
}

export async function createBook(input: CreateUpdateBookDto): Promise<BookDto> {
  const { data } = await api.post<BookDto>('/app/book', input)
  return data
}

export async function updateBook(
  id: string,
  input: CreateUpdateBookDto
): Promise<BookDto> {
  const { data } = await api.put<BookDto>(`/app/book/${id}`, input)
  return data
}

export async function deleteBook(id: string): Promise<void> {
  await api.delete(`/app/book/${id}`)
}

The matching backend IBookAppService lives in your BooksService (or whichever service you put the sample in) and uses the standard ABP CRUD app service pattern. The path /app/book is the auto-generated route from BookAppService — adjust to match your own service's route prefix.

Generating TypeScript types / DTOs from the backend

There's no official ABP tool for generating TypeScript DTOs/types from your backend at the moment. abp generate-proxy only emits JS (-t js), Angular (-t ng), or C# (-t csharp) proxies — none of which fit the hand-written React API layer pattern.

What works well with the modern React UI's axios.ts setup is generating from the Swagger / OpenAPI document your backend already exposes. A few options:

  • openapi-typescript (https://github.com/openapi-ts/openapi-typescript) — generates type definitions only, no runtime code. Lightweight and fits the hand-written lib/api/*.ts pattern perfectly. Note the microservice template does NOT expose a single aggregated swagger.json at the gateway — each service has its own schema. The gateway proxies them at /swagger-json/<ClusterId>/swagger/v1/swagger.json (see the *Swagger routes in gateways/web/.../appsettings.json), so you'd run it once per service:
    # via the gateway (single port to remember)
    npx openapi-typescript http://localhost:<gateway-port>/swagger-json/Identity/swagger/v1/swagger.json -o src/lib/api/identity-schema.d.ts
    
    # or directly against each service
    npx openapi-typescript http://localhost:<service-port>/swagger/v1/swagger.json -o src/lib/api/<service>-schema.d.ts
    
    Then reference the generated types in your hand-written service files.
  • swagger-typescript-api (https://github.com/acacode/swagger-typescript-api) — generates both types and a fetch/axios client. More code generated, saves the API call boilerplate.
  • heyapi.dev — also a reasonable choice, similar territory to swagger-typescript-api.

If you want to keep the hand-written service files and only add types, openapi-typescript is the simplest fit. If you'd rather have a generated client, swagger-typescript-api or heyapi.dev both work.

Thanks

Hi,

Two clarifications on point 2:

1) The Permissions dialog

The Target dropdown (User / Role / Client) is ABP's standard resource permission management UI — it applies to all resource permissions across the framework, not just AI Workspace. ABP doesn't have a "Tenant" target type, so you didn't do anything wrong.

2) Per-tenant workspace usage

This is the deeper question. The AI Management module is designed as a host-level module — Workspace isn't a multi-tenant entity and the workspace lookup doesn't take the current tenant into account. So "different workspace per tenant" isn't something the current design directly supports, regardless of how permissions are granted. My earlier suggestion about scoping per tenant via the Consume permission doesn't really hold under this design.

This is an important design point, so we'd like to discuss it internally with the team before giving you a concrete recommendation. We'll get back to you shortly.

Thanks

Hi,

Thanks for the follow-up. We're confirming a couple of points internally and will get back to you with a complete answer shortly.

Thanks

Hi,

Glad you've pinpointed the Id reuse. Once those host shadow rows get fresh Ids (different from the tenant user Id), the leave flow will sync host and tenant correctly.

For Issue 2, technically a host admin can call Delete on the host Users page, but with Shared user accounts "delete" removes the global account. If that user is a member of other tenants those memberships disappear too, and the account is gone — usually not what you want when an employee leaves one company.

The intended action is the per-tenant Leave (soft, Leaved=true), which keeps the global account intact and lets you re-invite the user later. Today it is wired as a self-service action under Switch tenant → Leave. For the admin-driven case in v10.3 you can add a small AppService plus a UI action on the host Users page.

  1. Add the AppService:
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.Application.Services;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Identity;
using Volo.Abp.Identity.UserSharing;
using Volo.Abp.MultiTenancy;

namespace MyCompanyName.MyProjectName.Identity;

[Authorize(IdentityPermissions.Users.Delete)]
public class TenantUserManagementAppService : ApplicationService
{
    protected IdentityProUserManager UserManager { get; }
    protected UserSharingManager UserSharingManager { get; }
    protected IDataFilter DataFilter { get; }

    public TenantUserManagementAppService(
        IdentityProUserManager userManager,
        UserSharingManager userSharingManager,
        IDataFilter dataFilter)
    {
        UserManager = userManager;
        UserSharingManager = userSharingManager;
        DataFilter = dataFilter;
    }

    public virtual async Task RemoveUserFromTenantAsync(Guid tenantId, Guid userId)
    {
        string email;
        using (CurrentTenant.Change(tenantId))
        using (DataFilter.Disable<IIdentityUserLeavedFilter>())
        {
            var tenantUser = await UserManager.FindByIdAsync(userId.ToString());
            if (tenantUser == null)
            {
                throw new EntityNotFoundException(typeof(IdentityUser), userId);
            }
            email = tenantUser.Email;

            if (!tenantUser.Leaved)
            {
                tenantUser.SetLeaved(true);
                (await UserManager.UpdateAsync(tenantUser)).CheckErrors();
            }
        }

        using (CurrentTenant.Change(null))
        using (DataFilter.Disable<IMultiTenant>())
        using (DataFilter.Disable<IIdentityUserLeavedFilter>())
        {
            var hostShadows = await UserSharingManager.GetUsersByEmailFromHostAsync(email, includeLeaved: true);
            foreach (var shadow in hostShadows.Where(x => x.TenantId == tenantId && !x.Leaved))
            {
                shadow.SetLeaved(true);
                (await UserManager.UpdateWithoutValidationAsync(shadow)).CheckErrors();
            }
        }
    }
}
  1. Add an action on the host Users page. This inherits the Identity Pro UserManagement component and appends a "Remove from tenant" action that shows up only on rows where TenantId != null (i.e. host shadows for some tenant):
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using MyCompanyName.MyProjectName.Identity;
using Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Pro.Blazor.Pages.Identity;
using Volo.Abp.MultiTenancy;

namespace MyCompanyName.MyProjectName.Blazor.Pages.Identity;

[ExposeServices(typeof(UserManagement))]
[Dependency(ReplaceServices = true)]
public class CustomizedUserManagement : UserManagement
{
    [Inject]
    protected TenantUserManagementAppService TenantUserManagementAppService { get; set; } = default!;

    protected override async ValueTask SetEntityActionsAsync()
    {
        await base.SetEntityActionsAsync();

        var removeFromTenantAction = new EntityAction
        {
            Text = "Remove from tenant",
            Visible = (data) =>
            {
                var user = (IdentityUserDto)data;
                return MultiTenancyOptions.Value.UserSharingStrategy == TenantUserSharingStrategy.Shared
                       && CurrentTenant.Id == null
                       && user.TenantId.HasValue
                       && user.Id != CurrentUser.Id
                       && HasDeletePermission;
            },
            Clicked = async (data) =>
            {
                var user = (IdentityUserDto)data;
                await TenantUserManagementAppService.RemoveUserFromTenantAsync(user.TenantId!.Value, user.Id);
                await Notify.Success("The user has been removed from the tenant.");
                await GetEntitiesAsync();
                await InvokeAsync(StateHasChanged);
            },
            ConfirmationMessage = (data) =>
                $"Are you sure you want to remove the user '{((IdentityUserDto)data).UserName}' from this tenant?"
        };

        EntityActions.Get<UserManagement>().Add(removeFromTenantAction);
    }
}

A host admin sees the new action on each tenant-shadow row in the host Users page, removes the user from the chosen tenant, the global account stays, and the user can be re-invited later. The two-step flow also sidesteps the Id-equality check inside the framework's LeaveTenantAsync, so it works even on host shadow rows that still share the tenant user's Id.

The next release will ship a built-in Remove from tenant action for tenant admins on the tenant-side user list (with Identity.Users.Delete). Once you upgrade, the custom AppService and the host-side action above can be removed; if you still want a host-side "remove a user from a specific tenant" admin tool for ops/batch scenarios, you can keep this workaround as your own management feature.

Thanks

Hi,

Both issues come from the Shared User Accounts strategy.

Issue 1 – "Leave Tenant" doesn't update the Host record

For a tenant with a separate database, the host-side "shadow" row must have a different Id than the tenant user. That's how the framework detects a separate database: if the shadow Id differs from the tenant Id it updates the host row on Leave; if they're equal it assumes a shared database (same row, already updated) and skips it.

During the migration you most likely reused the same Id as the tenant user, so Leave skips the host update. New tenants work because the framework generates a fresh Id for the shadow. (The same Id collision also breaks social-login / passkey sync to the host, so it's worth fixing.)

To fix your data:

  1. For users who already left, set Leaved = 1 on the matching host row (by email + TenantId).
  2. Regenerate a new unique Id for the per-tenant host shadow rows so it no longer equals the tenant user Id:
-- Host database: the per-tenant shadow rows created during migration
SELECT Id, UserName, Email, TenantId, Leaved
FROM AbpUsers
WHERE TenantId IS NOT NULL;

Before re-keying, check these rows aren't referenced by AbpUserRoles / AbpUserLogins / AbpUserTokens in the host DB. Since the exact script depends on how your migration mapped users, feel free to send the table layout to liming.ma@volosoft.com and we'll help build it. We'll also improve this on our side so Leave no longer relies purely on the Id comparison.

Issue 2 – No "Delete" action in the Users list

This is by design with Shared accounts, not a permission issue. A user is a global account, so:

  • Delete the account is a host-level operation.
  • Remove a user from one tenant is done with Leave Tenant (soft removal, re-invitable).

That's why Delete is hidden in the tenant UI. One catch: the UI currently hides Delete whenever Shared accounts is on without checking host vs tenant, so even a host admin doesn't see it even though the host is allowed to delete – that part is a UI bug we'll fix.

Are you trying to delete as a host admin (remove the account) or inside a tenant (remove a user from that tenant)? For the tenant case, Leave Tenant is the intended action; for the host case, deletion is supported and the missing button is the bug we'll address.

Thanks

Hi,

You're right — GHSA-24c8-4792-22hx was published on 2026-05-12, after we shipped Scriban 7.0.0 in v10.4.0 (PR https://github.com/abpframework/abp/pull/25170 was merged on 2026-03-27, when 7.0.0 was still the latest stable with no known advisories). Scriban then released 7.2.0 with the fix on 2026-05-12, and 7.2.1 followed on 2026-05-24 with some extra hardening.

I'll bump ABP's Scriban reference to 7.2.1 in the next release.

In the meantime, you can clear the audit on your side immediately by pinning the version in your own Directory.Packages.props:

<PackageVersion Include="Scriban" Version="7.2.1" />

ABP modules that consume Scriban (Volo.Abp.TextTemplating.Scriban, Volo.Docs) declare it as a PackageReference without a fixed version, so NuGet resolves whatever you set here. No further changes needed.

Thanks

Showing 421 to 430 of 12915 entries
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on November 02, 2026, 06:56
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.