cabloy 5.1.239 → 5.1.241
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cabloy-version +1 -1
- package/CHANGELOG.md +19 -0
- package/package.json +1 -1
- package/repo-docs/backend/openapi-guide.md +18 -0
- package/repo-docs/backend/rate-limit-guide.md +54 -9
- package/repo-e2e/specs/home-user-account.spec.ts +53 -8
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +16 -141
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/dto/operatorContext.ts +4 -1
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/service/operator.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/test/operatorAccess.test.ts +1 -0
- package/vona/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +1 -0
- package/vona/src/suite/a-home/modules/home-user/src/controller/passport.ts +1 -0
- package/vona/src/suite/a-home/modules/home-user/src/dto/accountCurrent.ts +3 -0
- package/vona/src/suite/a-home/modules/home-user/src/dto/accountProfileUpdate.ts +9 -2
- package/vona/src/suite/a-home/modules/home-user/src/dto/passportUser.ts +3 -0
- package/vona/src/suite/a-home/modules/home-user/src/entity/user.ts +3 -0
- package/vona/src/suite/a-home/modules/home-user/src/service/account.ts +7 -3
- package/vona/src/suite/a-home/modules/home-user/src/service/userAdapter.ts +1 -0
- package/vona/src/suite/a-home/modules/home-user/test/account.test.ts +133 -8
- package/vona/src/suite/a-home/modules/home-user/test/accountOpenapi.test.ts +32 -5
- package/vona/src/suite/a-home/modules/home-user/test/passport.test.ts +159 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-hmr/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-hmr/src/service/hmr.ts +5 -12
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +54 -8
- package/vona/src/suite-vendor/a-vona/modules/a-permission/test/permission.test.ts +228 -16
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/.metadata/index.ts +26 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/bean/middlewareSystem.aggregate.ts +58 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/service/rateLimit.ts +25 -5
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/types/rateLimit.ts +5 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/test/aggregate.test.ts +247 -0
- package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/user.ts +1 -0
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/api/openapi/schemas.ts +68 -52
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/api/openapi/types.ts +530 -344
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/page/dashboard/controller.tsx +7 -1
- package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +6 -1
- package/zova/src/suite/a-home/modules/home-base/src/config/locale/en-us.ts +1 -0
- package/zova/src/suite/a-home/modules/home-base/src/config/locale/zh-cn.ts +1 -0
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.user.tsx +1 -1
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.user.tsx +3 -1
- package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +4 -0
- package/zova/src/suite/a-home/modules/home-user/src/config/locale/en-us.ts +1 -0
- package/zova/src/suite/a-home/modules/home-user/src/config/locale/zh-cn.ts +1 -0
- package/zova/src/suite/a-home/modules/home-user/src/model/account.ts +2 -1
- package/zova/src/suite/a-home/modules/home-user/src/page/account/controller.tsx +17 -3
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.241
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.241
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Display the user's display name.
|
|
8
|
+
- Add MCP permission support.
|
|
9
|
+
- Add guard-only permission checks.
|
|
10
|
+
|
|
11
|
+
### Improvements
|
|
12
|
+
|
|
13
|
+
- Update the OpenAPI guide.
|
|
14
|
+
|
|
15
|
+
## 5.1.240
|
|
16
|
+
|
|
17
|
+
### Improvements
|
|
18
|
+
|
|
19
|
+
- Enhance rate limiting.
|
|
20
|
+
- Update hot module replacement (HMR) handling.
|
|
21
|
+
|
|
3
22
|
## 5.1.239
|
|
4
23
|
|
|
5
24
|
### Improvements
|
package/package.json
CHANGED
|
@@ -48,6 +48,24 @@ Representative capabilities include generating JSON for:
|
|
|
48
48
|
|
|
49
49
|
This is important because OpenAPI is treated as a first-class framework service, not as an external afterthought.
|
|
50
50
|
|
|
51
|
+
## MCP mapping and normal ingress admission
|
|
52
|
+
|
|
53
|
+
In Cabloy Basic, method/class `@mcp.tool()` metadata opts registered Controller Actions into MCP mapping using their existing request/response contracts. It is independent of ordinary OpenAPI publication: `@Api.exclude()` is not an MCP authorization boundary, and publishing an HTTP action does not opt it into MCP. Decorator opt-in alone does **not** expose an action on normal `/mcp`.
|
|
54
|
+
|
|
55
|
+
Normal shared ingress is owned by `cabloy-mcp` and accepts recognized dev/test/prod modes through the same OAuth and business-authorization boundary. Production is not rejected solely by mode; unknown modes, not-ready, closing, disabled or invalid required authority configuration still fail closed. Dedicated protected audit, audit-delivery gating and deployment-wide fine-grained quotas are deferred for this two-read MVP, not delivered capabilities or production prerequisites. Enabled-by-default behavior is unchanged: only literal `CABLOY_MCP_ENABLED=false` disables it. Construction admission permits exactly `training-student.controller.student` actions `lookup` and `summary`, for both listing and known-name direct calls. Optional training absent means an empty catalog, without fallback. The three synthetic catalog tools and anonymous standalone entry belong only to the `mcp-test` launcher, not shared `/mcp`.
|
|
56
|
+
|
|
57
|
+
OAuth scope does not grant business authority. Lookup and summary retain independent student select/view permissions, current-instance reads and minimized output; summary's six-field projection does not change its HTTP DTO. Calls retain ordinary Action guards with `innerAccess: false`.
|
|
58
|
+
|
|
59
|
+
Repository-relative source pointers:
|
|
60
|
+
|
|
61
|
+
- Opt-in metadata: `vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/decorator/mcp.ts`
|
|
62
|
+
- Contract mapper: `vona/src/suite/cabloy-mcp/modules/cabloy-mcp/src/lib/controllerTools.ts`
|
|
63
|
+
- Normal ingress middleware: `vona/src/suite/cabloy-mcp/modules/cabloy-mcp/src/bean/middlewareSystem.mcp.ts`
|
|
64
|
+
- Ingress checks: `vona/src/suite/cabloy-mcp/modules/cabloy-mcp/src/lib/mcpTransport.ts`
|
|
65
|
+
- Authenticated exchange and settlement: `vona/src/suite/cabloy-mcp/modules/cabloy-mcp/src/lib/businessTransport.ts`
|
|
66
|
+
- Exact admission and discovery: `vona/src/suite/cabloy-mcp/modules/cabloy-mcp/src/lib/sharedBusinessTools.ts`
|
|
67
|
+
- Application-close drain: `vona/src/suite/cabloy-mcp/modules/cabloy-mcp/src/monkey.ts`
|
|
68
|
+
|
|
51
69
|
## Validation and OpenAPI are linked
|
|
52
70
|
|
|
53
71
|
A major theme is that the same validation-oriented declaration surface also drives OpenAPI metadata generation.
|
|
@@ -2,11 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
`a-ratelimit`
|
|
5
|
+
`a-ratelimit` provides two independent admission controls:
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- The identity interceptor, `a-ratelimit:rateLimit`, limits matched Vona HTTP API actions across all application workers. It runs after Passport has resolved an authenticated or anonymous subject, and before request-body parsing, pipes, local middleware, and controller code.
|
|
8
|
+
- The aggregate system middleware, `a-ratelimit:aggregate`, limits incoming HTTP requests across the application before the application/instance readiness middleware and routing, but after the HTTP executor's initial instance lookup. It is disabled by default and requires explicit configuration.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
The identity interceptor protects controller APIs only; its route matching and instance isolation do not describe the aggregate middleware. Keep volumetric and network-edge protection in the reverse proxy, WAF, or gateway even when aggregate admission is enabled. See [Controller AOP Guide](/backend/controller-aop-guide) for the general request-stage diagram and the distinction between a local aspect and a route override of an existing global onion.
|
|
11
|
+
|
|
12
|
+
The aggregate middleware documented here is a Cabloy Basic runtime addition. Cabloy Start is maintained separately and does not automatically receive this runtime change; shared documentation does not imply aggregate middleware availability in Start.
|
|
13
|
+
|
|
14
|
+
## Aggregate application admission
|
|
15
|
+
|
|
16
|
+
Enable the system middleware through application configuration, starting in observe mode:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
config.onions.middlewareSystem['a-ratelimit:aggregate'] = {
|
|
20
|
+
enable: true,
|
|
21
|
+
rateLimit: {
|
|
22
|
+
mode: 'observe',
|
|
23
|
+
limit: 100,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`100` is an example limit, not a measured application capacity or a recommended production value. An enabled aggregate middleware requires an explicit valid `limit` and a `mode` of either `'observe'` or `'enforce'`; there is no enabled default quota. Review observed traffic and Redis health before changing `mode` to `'enforce'`.
|
|
29
|
+
|
|
30
|
+
### Coverage and ordering
|
|
31
|
+
|
|
32
|
+
The aggregate middleware runs after `httpLog` and before health handling, the application/instance readiness middleware, routing, and authentication. The HTTP listener enters the executor first: it resolves the instance name and, when applicable, loads the instance record before dispatching system middleware. Aggregate admission does not protect that initial lookup or any instance registration it triggers, and requests that fail before middleware dispatch are not counted.
|
|
33
|
+
|
|
34
|
+
By default, aggregate admission covers all requests that reach the system middleware chain, including API, SSR, static-file, preflight, health, and unknown-route requests. Standard system middleware `match` and `ignore` options can narrow this coverage; they match the incoming `ctx.path`, including the API prefix where present, before routing. Traffic served entirely by a proxy or CDN never reaches this counter. Other programs, background work, and later WebSocket messages are outside its coverage.
|
|
35
|
+
|
|
36
|
+
Aggregate admission is independent of the identity interceptor. Both can be enabled, and a controller/action identity policy with `enable: false` does not bypass aggregate admission. There are no built-in aggregate path exemptions: unless explicitly excluded through system middleware matching, saturation can reject health probes and control requests before their handlers run. Account for this operational consequence before enforcing the quota.
|
|
37
|
+
|
|
38
|
+
### Shared counter and fixed windows
|
|
39
|
+
|
|
40
|
+
The aggregate counter uses the `limiter` Redis client and the application Redis prefix. It is shared across users, routes, instances, and workers rather than partitioned by identity or active instance. Multiple hosts using the same prefix and Redis share the quota; this is not physical-host discovery or an automatic per-host capacity limit. Keep custom application prefixes unique for applications or environments that require independent quotas.
|
|
41
|
+
|
|
42
|
+
The window is fixed at `1000` ms and aligned to one-second boundaries. Attempts count toward the window, including requests already rejected for exceeding the limit. As with other fixed-window counters, traffic can burst around a window boundary; the limit is not a rolling-window guarantee.
|
|
43
|
+
|
|
44
|
+
### Modes, responses, and logs
|
|
45
|
+
|
|
46
|
+
In `observe` mode, requests over the limit log `rate_limit.would_reject` and still continue to the next middleware. Redis failures log `rate_limit.redis_error` and fail open, allowing the request to continue.
|
|
47
|
+
|
|
48
|
+
In `enforce` mode, an over-limit request logs `rate_limit.rejected` and returns HTTP `429` with `Retry-After`. A Redis failure logs `rate_limit.redis_error` and returns HTTP `503` instead of allowing unbounded admission. Both responses are direct JSON bodies with the shape `{ code, message }`, even for requests expecting HTML; they do not enter the normal page/error rendering path.
|
|
49
|
+
|
|
50
|
+
Aggregate admission does not add success `RateLimit-*` headers. The identity interceptor's response headers remain unchanged when a request reaches that interceptor.
|
|
51
|
+
|
|
52
|
+
Aggregate logs use the same event names as the identity interceptor, with `scope: 'aggregate'`. Redis failures expose only a sanitized error class, not a raw error message. Do not include raw identities, routes, full URLs, or Redis keys in aggregate logs or metric labels.
|
|
53
|
+
|
|
54
|
+
## Identity interceptor enablement and rollout
|
|
10
55
|
|
|
11
56
|
The interceptor ships with `enable: false` to preserve compatibility. Enable the global interceptor through standard onion configuration and begin in observe mode:
|
|
12
57
|
|
|
@@ -36,9 +81,9 @@ config.onions.interceptor['a-ratelimit:rateLimit'] = {
|
|
|
36
81
|
};
|
|
37
82
|
```
|
|
38
83
|
|
|
39
|
-
Use instance configuration when tenants require different policy values. Each
|
|
84
|
+
Use instance configuration when tenants require different identity policy values. Each identity interceptor key includes the active instance, so one instance never consumes another instance’s identity quota. The aggregate middleware instead shares one application-prefix quota across instances.
|
|
40
85
|
|
|
41
|
-
##
|
|
86
|
+
## Identity route policies
|
|
42
87
|
|
|
43
88
|
Use `Core.rateLimit(...)` on a controller for its baseline policy, or on an action for a stricter policy, an intentional shared bucket, or an explicit exemption:
|
|
44
89
|
|
|
@@ -64,7 +109,7 @@ async requestReset() {}
|
|
|
64
109
|
health() {}
|
|
65
110
|
```
|
|
66
111
|
|
|
67
|
-
`enable`, matching, and ordering remain outer interceptor options. The nested `rateLimit` object contains the quota policy, so partial controller/action overrides inherit the remaining policy fields through normal Vona onion merging. Interceptor options use normal Vona precedence: application config, active-instance config, controller/action decorators, then controlled dynamic onion overrides used by tests. A controller/action can enable a narrower policy while the global interceptor is disabled, or set `enable: false` as a reviewed exemption after global activation.
|
|
112
|
+
`enable`, matching, and ordering remain outer interceptor options. The nested `rateLimit` object contains the quota policy, so partial controller/action overrides inherit the remaining policy fields through normal Vona onion merging. Interceptor options use normal Vona precedence: application config, active-instance config, controller/action decorators, then controlled dynamic onion overrides used by tests. A controller/action can enable a narrower policy while the global interceptor is disabled, or set `enable: false` as a reviewed exemption after global activation. These identity policy fields belong to `a-ratelimit:rateLimit` at the post-Passport interceptor stage, not to another middleware. The aggregate system middleware has its own independent `rateLimit` configuration described above.
|
|
68
113
|
|
|
69
114
|
## Identity and Redis storage
|
|
70
115
|
|
|
@@ -72,11 +117,11 @@ The default `key: 'identity'` combines the proxy-trusted `ctx.ip` with the authe
|
|
|
72
117
|
|
|
73
118
|
Do not parse `X-Forwarded-For` in application code. Vona resolves `ctx.ip` according to its configured proxy trust settings. The trusted reverse proxy must replace client-supplied forwarding headers before forwarding a request.
|
|
74
119
|
|
|
75
|
-
The
|
|
120
|
+
The identity interceptor always uses the dedicated `limiter` Redis client; this infrastructure choice is not a route-policy field. It registers one atomic Lua fixed-window counter command during module startup and invokes that command for each evaluated request. Identity Redis keys contain the instance, policy, normalized route, window start, and a SHA-256 digest of the identity; raw IP addresses and subject IDs are not stored in key text.
|
|
76
121
|
|
|
77
122
|
Fixed windows provide one low-cost atomic operation per request. A limit can burst around a window boundary; choose lower limits or shorter windows for especially sensitive actions.
|
|
78
123
|
|
|
79
|
-
## HTTP response contract
|
|
124
|
+
## Identity HTTP response contract
|
|
80
125
|
|
|
81
126
|
In enforce mode, evaluated requests expose RFC 9333 fields when `headers` is enabled:
|
|
82
127
|
|
|
@@ -90,7 +135,7 @@ If the limiter Redis operation fails during enforce mode, Cabloy returns `503`,
|
|
|
90
135
|
|
|
91
136
|
## Operations
|
|
92
137
|
|
|
93
|
-
The primary structured events are `rate_limit.would_reject`, `rate_limit.rejected`, and `rate_limit.redis_error`.
|
|
138
|
+
The primary structured events are `rate_limit.would_reject`, `rate_limit.rejected`, and `rate_limit.redis_error`. Identity interceptor events contain only route/policy/instance and error-class information. Aggregate events carry `scope: 'aggregate'` and omit route/identity detail, with only a sanitized error class for Redis failures. Do not add raw identities, full URLs, or Redis keys to logs or metric labels.
|
|
94
139
|
|
|
95
140
|
The `limiter` client may initially use the same Redis deployment as other Vona clients, but it is separately configured so production can isolate its endpoint, timeout, and capacity. Ensure Redis remains available before globally setting `enable: true` with `rateLimit.mode: 'enforce'`.
|
|
96
141
|
|
|
@@ -383,7 +383,7 @@ test(
|
|
|
383
383
|
);
|
|
384
384
|
|
|
385
385
|
test(
|
|
386
|
-
'ATP-ACCOUNT-WEB-01:
|
|
386
|
+
'ATP-ACCOUNT-WEB-01: display name refreshes the Web menu without changing the login username',
|
|
387
387
|
{ tag: ['@account', '@web', '@flow'] },
|
|
388
388
|
async ({ page, request }, testInfo) => {
|
|
389
389
|
let account: RegisteredAccount | undefined;
|
|
@@ -424,14 +424,27 @@ test(
|
|
|
424
424
|
.poll(() => avatarPreview.evaluate(image => (image as HTMLImageElement).naturalWidth))
|
|
425
425
|
.toBeGreaterThan(0);
|
|
426
426
|
|
|
427
|
-
const
|
|
428
|
-
await
|
|
427
|
+
const username = page.getByRole('textbox', { name: 'User name', exact: true });
|
|
428
|
+
await expect(username).toHaveValue(account.username);
|
|
429
|
+
await expect(username).toHaveAttribute('readonly', '');
|
|
430
|
+
const displayName = page
|
|
429
431
|
.getByRole('group', { name: 'Display name', exact: true })
|
|
430
|
-
.getByRole('textbox')
|
|
431
|
-
|
|
432
|
+
.getByRole('textbox');
|
|
433
|
+
await expect(displayName).toHaveValue('');
|
|
434
|
+
const profileName = `e2e-fixture-home-account-profile-${testInfo.workerIndex}-${crypto.randomUUID()}`;
|
|
435
|
+
await displayName.fill(` ${profileName} `);
|
|
432
436
|
const timezone = page
|
|
433
437
|
.getByRole('group', { name: 'Time zone', exact: true })
|
|
434
438
|
.getByRole('textbox');
|
|
439
|
+
const inputWidths = await Promise.all(
|
|
440
|
+
[username, displayName, timezone].map(input =>
|
|
441
|
+
input.evaluate(element => element.getBoundingClientRect().width),
|
|
442
|
+
),
|
|
443
|
+
);
|
|
444
|
+
expect(inputWidths[0]).toBeGreaterThan(0);
|
|
445
|
+
for (const width of inputWidths.slice(1)) {
|
|
446
|
+
expect(width).toBeCloseTo(inputWidths[0], 0);
|
|
447
|
+
}
|
|
435
448
|
const browserTimezone = await page.evaluate(
|
|
436
449
|
() => Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
437
450
|
);
|
|
@@ -447,9 +460,21 @@ test(
|
|
|
447
460
|
const profileResponse = waitForApiResponse(page, 'PATCH', accountProfileApiPath);
|
|
448
461
|
const refreshedPassportResponse = waitForApiResponse(page, 'GET', passportCurrentApiPath);
|
|
449
462
|
await page.getByRole('button', { name: 'Save profile', exact: true }).click();
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
expect((await
|
|
463
|
+
const savedProfileResponse = await profileResponse;
|
|
464
|
+
expect(savedProfileResponse.ok()).toBeTruthy();
|
|
465
|
+
expect((await savedProfileResponse.json()).data).toMatchObject({
|
|
466
|
+
name: account.username,
|
|
467
|
+
displayName: profileName,
|
|
468
|
+
});
|
|
469
|
+
expect(savedProfileResponse.request().postDataJSON()).not.toHaveProperty('name');
|
|
470
|
+
const savedPassportResponse = await refreshedPassportResponse;
|
|
471
|
+
expect(savedPassportResponse.ok()).toBeTruthy();
|
|
472
|
+
expect((await savedPassportResponse.json()).data.user).toMatchObject({
|
|
473
|
+
name: account.username,
|
|
474
|
+
displayName: profileName,
|
|
475
|
+
});
|
|
476
|
+
await expect(username).toHaveValue(account.username);
|
|
477
|
+
await expect(displayName).toHaveValue(profileName);
|
|
453
478
|
await expect(page.getByText('Profile saved.', { exact: true })).toBeVisible();
|
|
454
479
|
await expect
|
|
455
480
|
.poll(() =>
|
|
@@ -473,6 +498,26 @@ test(
|
|
|
473
498
|
await expect(page).toHaveURL(accountPath);
|
|
474
499
|
await expect(page.getByRole('heading', { name: 'Account Settings' })).toBeVisible();
|
|
475
500
|
await expect(userMenu).not.toHaveAttribute('open', '');
|
|
501
|
+
await expect(username).toHaveValue(account.username);
|
|
502
|
+
await expect(displayName).toHaveValue(profileName);
|
|
503
|
+
const originalLogin = await loginAsAccount(request, account.username, account.password);
|
|
504
|
+
expect(String(originalLogin.id)).toBe(String(account.id));
|
|
505
|
+
|
|
506
|
+
await displayName.fill(' ');
|
|
507
|
+
const clearedProfileResponse = waitForApiResponse(page, 'PATCH', accountProfileApiPath);
|
|
508
|
+
const clearedPassportResponse = waitForApiResponse(page, 'GET', passportCurrentApiPath);
|
|
509
|
+
await page.getByRole('button', { name: 'Save profile', exact: true }).click();
|
|
510
|
+
const clearedProfile = await clearedProfileResponse;
|
|
511
|
+
expect(clearedProfile.ok()).toBeTruthy();
|
|
512
|
+
expect(clearedProfile.request().postDataJSON()).toMatchObject({ displayName: null });
|
|
513
|
+
const clearedProfileBody = (await clearedProfile.json()).data;
|
|
514
|
+
expect(clearedProfileBody.name).toBe(account.username);
|
|
515
|
+
expect(clearedProfileBody.displayName).toBeFalsy();
|
|
516
|
+
expect((await clearedPassportResponse).ok()).toBeTruthy();
|
|
517
|
+
await expect(displayName).toHaveValue('');
|
|
518
|
+
await expect(username).toHaveValue(account.username);
|
|
519
|
+
await expect(userMenu.locator('summary')).toContainText(account.username);
|
|
520
|
+
await expect(userMenu.locator('summary')).not.toContainText(profileName);
|
|
476
521
|
expect(pageErrors).toEqual([]);
|
|
477
522
|
expect(consoleErrors).toEqual([]);
|
|
478
523
|
} finally {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.120",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
|
|
6
6
|
"keywords": [
|
package/vona/pnpm-lock.yaml
CHANGED
|
@@ -995,11 +995,11 @@ importers:
|
|
|
995
995
|
specifier: npm:@cabloy/zod@4.3.8
|
|
996
996
|
version: '@cabloy/zod@4.3.8'
|
|
997
997
|
zova:
|
|
998
|
-
specifier: ^5.1.
|
|
999
|
-
version: 5.1.
|
|
998
|
+
specifier: ^5.1.177
|
|
999
|
+
version: 5.1.177(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
|
|
1000
1000
|
zova-jsx:
|
|
1001
|
-
specifier: ^1.1.
|
|
1002
|
-
version: 1.1.
|
|
1001
|
+
specifier: ^1.1.95
|
|
1002
|
+
version: 1.1.95(typescript@5.9.3)
|
|
1003
1003
|
zova-module-a-api:
|
|
1004
1004
|
specifier: ^5.1.23
|
|
1005
1005
|
version: 5.1.23
|
|
@@ -1061,8 +1061,8 @@ importers:
|
|
|
1061
1061
|
specifier: ^5.1.36
|
|
1062
1062
|
version: 5.1.36
|
|
1063
1063
|
zova-module-a-ssr:
|
|
1064
|
-
specifier: ^5.1.
|
|
1065
|
-
version: 5.1.
|
|
1064
|
+
specifier: ^5.1.42
|
|
1065
|
+
version: 5.1.42
|
|
1066
1066
|
zova-module-a-ssrhmr:
|
|
1067
1067
|
specifier: ^5.1.24
|
|
1068
1068
|
version: 5.1.24
|
|
@@ -1079,8 +1079,8 @@ importers:
|
|
|
1079
1079
|
specifier: ^5.1.39
|
|
1080
1080
|
version: 5.1.39
|
|
1081
1081
|
zova-module-a-zova:
|
|
1082
|
-
specifier: ^5.1.
|
|
1083
|
-
version: 5.1.
|
|
1082
|
+
specifier: ^5.1.114
|
|
1083
|
+
version: 5.1.114(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
|
|
1084
1084
|
zova-module-pay-mock:
|
|
1085
1085
|
specifier: ^5.0.6
|
|
1086
1086
|
version: 5.0.6
|
|
@@ -1124,11 +1124,11 @@ importers:
|
|
|
1124
1124
|
specifier: npm:@cabloy/zod@4.3.8
|
|
1125
1125
|
version: '@cabloy/zod@4.3.8'
|
|
1126
1126
|
zova:
|
|
1127
|
-
specifier: ^5.1.
|
|
1128
|
-
version: 5.1.
|
|
1127
|
+
specifier: ^5.1.177
|
|
1128
|
+
version: 5.1.177(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
|
|
1129
1129
|
zova-jsx:
|
|
1130
|
-
specifier: ^1.1.
|
|
1131
|
-
version: 1.1.
|
|
1130
|
+
specifier: ^1.1.95
|
|
1131
|
+
version: 1.1.95(typescript@5.9.3)
|
|
1132
1132
|
zova-module-a-api:
|
|
1133
1133
|
specifier: ^5.1.23
|
|
1134
1134
|
version: 5.1.23
|
|
@@ -1190,8 +1190,8 @@ importers:
|
|
|
1190
1190
|
specifier: ^5.1.36
|
|
1191
1191
|
version: 5.1.36
|
|
1192
1192
|
zova-module-a-ssr:
|
|
1193
|
-
specifier: ^5.1.
|
|
1194
|
-
version: 5.1.
|
|
1193
|
+
specifier: ^5.1.42
|
|
1194
|
+
version: 5.1.42
|
|
1195
1195
|
zova-module-a-ssrhmr:
|
|
1196
1196
|
specifier: ^5.1.24
|
|
1197
1197
|
version: 5.1.24
|
|
@@ -1208,8 +1208,8 @@ importers:
|
|
|
1208
1208
|
specifier: ^5.1.39
|
|
1209
1209
|
version: 5.1.39
|
|
1210
1210
|
zova-module-a-zova:
|
|
1211
|
-
specifier: ^5.1.
|
|
1212
|
-
version: 5.1.
|
|
1211
|
+
specifier: ^5.1.114
|
|
1212
|
+
version: 5.1.114(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
|
|
1213
1213
|
zova-module-pay-mock:
|
|
1214
1214
|
specifier: ^5.0.6
|
|
1215
1215
|
version: 5.0.6
|
|
@@ -9412,15 +9412,9 @@ packages:
|
|
|
9412
9412
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
|
9413
9413
|
engines: {node: '>=10'}
|
|
9414
9414
|
|
|
9415
|
-
zova-core@5.1.94:
|
|
9416
|
-
resolution: {integrity: sha512-puo3pqa4/ngwZj//Md457XMloZ4FRaoJlmwf+zyPyxKL7GoV81BaJeUPi9szzY8aM9EkoWXBhSVA0cp6mTXBhw==}
|
|
9417
|
-
|
|
9418
9415
|
zova-core@5.1.95:
|
|
9419
9416
|
resolution: {integrity: sha512-nvBb3zVdJEQ9SWi+cl5j2PLs/T0FRRIULfHqU3SBFUn3jFSZFizniRlak+qoSq3mzj+LGD61BfnFJQb2JJ8+Zg==}
|
|
9420
9417
|
|
|
9421
|
-
zova-jsx@1.1.94:
|
|
9422
|
-
resolution: {integrity: sha512-4QGgNE5J32PXYCgHBt8JBrTRp8CXADgZzIBUsovQmKfIwtMY6DK5+zPQeWBVqHoV5vK95RKhRPwdAKFGOLupeg==}
|
|
9423
|
-
|
|
9424
9418
|
zova-jsx@1.1.95:
|
|
9425
9419
|
resolution: {integrity: sha512-gzmR01Qt4oBrMCejH4W0fqfFx62L+XQOfOROs6mBVdFYGmbyXzy/lDXtTH4v4WjlrmVbuoAyZE/BrYmNxn2swA==}
|
|
9426
9420
|
|
|
@@ -9484,9 +9478,6 @@ packages:
|
|
|
9484
9478
|
zova-module-a-routertabs@5.1.36:
|
|
9485
9479
|
resolution: {integrity: sha512-QYkeK7ql0B2AdFcCj9a+oIyqZi7oW/Ay/YHMFSseRgoRU/dTQnK79/v/DWB8+JluxY0tWMakLDcSq7bIolRdgg==}
|
|
9486
9480
|
|
|
9487
|
-
zova-module-a-ssr@5.1.41:
|
|
9488
|
-
resolution: {integrity: sha512-2p19mMzc9MjvYDdEbV6qjOngxuM7vdzwA1+Z4+/81pOfptU7nBobqDaDUNZxblHEoiIHrDM8NCeghPBO6Fz2sg==}
|
|
9489
|
-
|
|
9490
9481
|
zova-module-a-ssr@5.1.42:
|
|
9491
9482
|
resolution: {integrity: sha512-XaZcUHwDxwyQi4VNLK6CxkdG4lV5aUNgPNkiLjN3LWh9l2yS0u8CQqGV8+gEKcpOOj7XhZIubu4gb4GUNNQoGw==}
|
|
9492
9483
|
|
|
@@ -9505,9 +9496,6 @@ packages:
|
|
|
9505
9496
|
zova-module-a-zod@5.1.39:
|
|
9506
9497
|
resolution: {integrity: sha512-nyVgDGt7Xly/8ccGau/ZRck3PJfo+9WFMR4EudXo2YBFMahNeXUegTyUPeSV6DfJDUjCqpy2V060PipBVJ5QzQ==}
|
|
9507
9498
|
|
|
9508
|
-
zova-module-a-zova@5.1.113:
|
|
9509
|
-
resolution: {integrity: sha512-PuasOjYgRz8F94oVPSjQepwVxL+LySauwJQKNzly+yD7YCbQJcHy6tw7IejwcaSTjlBe3M1Iy6IWX7IwrOrfyw==}
|
|
9510
|
-
|
|
9511
9499
|
zova-module-a-zova@5.1.114:
|
|
9512
9500
|
resolution: {integrity: sha512-H16jQ2YRw3Q9KSO7RDKMl+QRFZSZ7J/AkUUD71BTQA1oDLY+zBLuKTABwicEgS3S44R4qJC79rR/yuTXLlZnkw==}
|
|
9513
9501
|
|
|
@@ -9523,15 +9511,9 @@ packages:
|
|
|
9523
9511
|
zova-module-rest-resource@5.1.57:
|
|
9524
9512
|
resolution: {integrity: sha512-xpx42J+ZCx3KGzFZPQBHfUUDxgYTPlQGrUTrN0/SCY8PD/qYu77kqQ29+B5B9WcUeSnSAzUtbZx0UzzFia1slQ==}
|
|
9525
9513
|
|
|
9526
|
-
zova-suite-a-zova@5.1.175:
|
|
9527
|
-
resolution: {integrity: sha512-cjN97iaDZ3WhvOtu27YnqfZpEnnKptLGqqpllIhAtOFaTXtuMGiGVTmH7Q1tc/k9OPEHzvvCThsYKRxzYbR5DQ==}
|
|
9528
|
-
|
|
9529
9514
|
zova-suite-a-zova@5.1.176:
|
|
9530
9515
|
resolution: {integrity: sha512-w7JQ0cutO3zaZosxfsSLn9/mcY5deftZWTGkiPJTTG6I6eGGZlFJRDCQpu+dBd8eYI66xe4kstdH3KGhLSt74A==}
|
|
9531
9516
|
|
|
9532
|
-
zova@5.1.176:
|
|
9533
|
-
resolution: {integrity: sha512-7E/Kc9QkuB6ckDX2ual9DFmiVrcPy/umDVlC+QlRFy5A30L8D5/WNKzWxmBubXYsXWmsAd/VZQUQPwditMmNkA==}
|
|
9534
|
-
|
|
9535
9517
|
zova@5.1.177:
|
|
9536
9518
|
resolution: {integrity: sha512-35jZVs08Qn4cybwNqu1/PE3qz1J7Vi64y7AgTD98rVv5uOnDPkuwIIIVgMYZllV/C3kCsWPBLoC3tz/UG4SEcA==}
|
|
9537
9519
|
|
|
@@ -15880,37 +15862,6 @@ snapshots:
|
|
|
15880
15862
|
|
|
15881
15863
|
yocto-queue@0.1.0: {}
|
|
15882
15864
|
|
|
15883
|
-
zova-core@5.1.94(typescript@5.9.3):
|
|
15884
|
-
dependencies:
|
|
15885
|
-
'@cabloy/compose': link:packages-utils/compose
|
|
15886
|
-
'@cabloy/extend': link:packages-utils/extend
|
|
15887
|
-
'@cabloy/json5': link:packages-utils/json5
|
|
15888
|
-
'@cabloy/localeutil': link:packages-utils/localeutil
|
|
15889
|
-
'@cabloy/logger': 1.1.25
|
|
15890
|
-
'@cabloy/module-info': 2.0.3
|
|
15891
|
-
'@cabloy/utils': link:packages-utils/utils
|
|
15892
|
-
'@cabloy/vue-compiler-sfc': 3.5.14
|
|
15893
|
-
'@cabloy/vue-reactivity': 3.5.16
|
|
15894
|
-
'@cabloy/vue-router': 4.4.18(vue@3.5.43(typescript@5.9.3))
|
|
15895
|
-
'@cabloy/vue-runtime-core': 3.5.57
|
|
15896
|
-
'@cabloy/vue-runtime-dom': 3.5.13
|
|
15897
|
-
'@cabloy/vue-server-renderer': 3.5.18(vue@3.5.43(typescript@5.9.3))
|
|
15898
|
-
'@cabloy/word-utils': 2.1.14
|
|
15899
|
-
'@cabloy/zod-errors-custom': link:packages-utils/zod-errors-custom
|
|
15900
|
-
'@cabloy/zod-openapi': link:packages-utils/zod-openapi
|
|
15901
|
-
'@cabloy/zod-query': link:packages-utils/zod-query
|
|
15902
|
-
'@types/js-cookie': 3.0.6
|
|
15903
|
-
'@types/ms': 2.1.0
|
|
15904
|
-
'@vue/shared': 3.5.36
|
|
15905
|
-
deep-equal: 2.2.3
|
|
15906
|
-
ms: 2.1.3
|
|
15907
|
-
reflect-metadata: 0.2.2
|
|
15908
|
-
vue: 3.5.43(typescript@5.9.3)
|
|
15909
|
-
vue-plugin-render-freeze: 2.0.4(vue@3.5.43(typescript@5.9.3))
|
|
15910
|
-
zod: '@cabloy/zod@4.3.8'
|
|
15911
|
-
transitivePeerDependencies:
|
|
15912
|
-
- typescript
|
|
15913
|
-
|
|
15914
15865
|
zova-core@5.1.95(patch_hash=44197c3f359a73a1a5bb3dc0a8a97e705a55b30f9a434717aa24d75a96f2c6b6)(typescript@5.9.3):
|
|
15915
15866
|
dependencies:
|
|
15916
15867
|
'@cabloy/compose': link:packages-utils/compose
|
|
@@ -15942,17 +15893,6 @@ snapshots:
|
|
|
15942
15893
|
transitivePeerDependencies:
|
|
15943
15894
|
- typescript
|
|
15944
15895
|
|
|
15945
|
-
zova-jsx@1.1.94(typescript@5.9.3):
|
|
15946
|
-
dependencies:
|
|
15947
|
-
'@cabloy/compose': link:packages-utils/compose
|
|
15948
|
-
'@cabloy/utils': link:packages-utils/utils
|
|
15949
|
-
'@cabloy/word-utils': 2.1.14
|
|
15950
|
-
typestyle: 2.4.0
|
|
15951
|
-
vue: 3.5.43(typescript@5.9.3)
|
|
15952
|
-
zova-core: 5.1.94(typescript@5.9.3)
|
|
15953
|
-
transitivePeerDependencies:
|
|
15954
|
-
- typescript
|
|
15955
|
-
|
|
15956
15896
|
zova-jsx@1.1.95(typescript@5.9.3):
|
|
15957
15897
|
dependencies:
|
|
15958
15898
|
'@cabloy/compose': link:packages-utils/compose
|
|
@@ -16033,11 +15973,6 @@ snapshots:
|
|
|
16033
15973
|
|
|
16034
15974
|
zova-module-a-routertabs@5.1.36: {}
|
|
16035
15975
|
|
|
16036
|
-
zova-module-a-ssr@5.1.41:
|
|
16037
|
-
dependencies:
|
|
16038
|
-
devalue: 5.9.4
|
|
16039
|
-
ms: 2.1.3
|
|
16040
|
-
|
|
16041
15976
|
zova-module-a-ssr@5.1.42:
|
|
16042
15977
|
dependencies:
|
|
16043
15978
|
devalue: 5.9.4
|
|
@@ -16071,22 +16006,6 @@ snapshots:
|
|
|
16071
16006
|
'@cabloy/zod-query': link:packages-utils/zod-query
|
|
16072
16007
|
zod: '@cabloy/zod@4.3.8'
|
|
16073
16008
|
|
|
16074
|
-
zova-module-a-zova@5.1.113(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
|
|
16075
|
-
dependencies:
|
|
16076
|
-
'@cabloy/compose': link:packages-utils/compose
|
|
16077
|
-
'@cabloy/deps': link:packages-utils/deps
|
|
16078
|
-
'@cabloy/json5': link:packages-utils/json5
|
|
16079
|
-
'@cabloy/module-info': 2.0.3
|
|
16080
|
-
'@cabloy/utils': link:packages-utils/utils
|
|
16081
|
-
'@cabloy/vue-router': 4.4.18(vue@3.5.43(typescript@5.9.3))
|
|
16082
|
-
'@cabloy/word-utils': 2.1.14
|
|
16083
|
-
defu: 6.1.7
|
|
16084
|
-
luxon: 3.7.2
|
|
16085
|
-
zova-jsx: 1.1.94(typescript@5.9.3)
|
|
16086
|
-
transitivePeerDependencies:
|
|
16087
|
-
- typescript
|
|
16088
|
-
- vue
|
|
16089
|
-
|
|
16090
16009
|
zova-module-a-zova@5.1.114(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
|
|
16091
16010
|
dependencies:
|
|
16092
16011
|
'@cabloy/compose': link:packages-utils/compose
|
|
@@ -16111,39 +16030,6 @@ snapshots:
|
|
|
16111
16030
|
|
|
16112
16031
|
zova-module-rest-resource@5.1.57: {}
|
|
16113
16032
|
|
|
16114
|
-
zova-suite-a-zova@5.1.175(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
|
|
16115
|
-
dependencies:
|
|
16116
|
-
zova-module-a-api: 5.1.23
|
|
16117
|
-
zova-module-a-app: 5.1.27
|
|
16118
|
-
zova-module-a-bean: 5.1.37
|
|
16119
|
-
zova-module-a-behavior: 5.1.28
|
|
16120
|
-
zova-module-a-behaviors: 5.1.23
|
|
16121
|
-
zova-module-a-command: 5.1.36
|
|
16122
|
-
zova-module-a-fetch: 5.1.26(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)
|
|
16123
|
-
zova-module-a-form: 5.1.55(vue@3.5.43(typescript@5.9.3))
|
|
16124
|
-
zova-module-a-icon: 5.1.28
|
|
16125
|
-
zova-module-a-interceptor: 5.1.32
|
|
16126
|
-
zova-module-a-logger: 5.1.29
|
|
16127
|
-
zova-module-a-meta: 5.1.23
|
|
16128
|
-
zova-module-a-model: 5.1.39(vue@3.5.43(typescript@5.9.3))
|
|
16129
|
-
zova-module-a-openapi: 5.1.59
|
|
16130
|
-
zova-module-a-router: 5.1.43
|
|
16131
|
-
zova-module-a-routerstack: 5.1.30
|
|
16132
|
-
zova-module-a-routertabs: 5.1.36
|
|
16133
|
-
zova-module-a-ssr: 5.1.41
|
|
16134
|
-
zova-module-a-ssrhmr: 5.1.24
|
|
16135
|
-
zova-module-a-ssrserver: 5.1.27
|
|
16136
|
-
zova-module-a-style: 5.1.36
|
|
16137
|
-
zova-module-a-table: 5.1.45(vue@3.5.43(typescript@5.9.3))
|
|
16138
|
-
zova-module-a-zod: 5.1.39
|
|
16139
|
-
zova-module-a-zova: 5.1.113(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
|
|
16140
|
-
transitivePeerDependencies:
|
|
16141
|
-
- '@vue/composition-api'
|
|
16142
|
-
- debug
|
|
16143
|
-
- supports-color
|
|
16144
|
-
- typescript
|
|
16145
|
-
- vue
|
|
16146
|
-
|
|
16147
16033
|
zova-suite-a-zova@5.1.176(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
|
|
16148
16034
|
dependencies:
|
|
16149
16035
|
zova-module-a-api: 5.1.23
|
|
@@ -16177,17 +16063,6 @@ snapshots:
|
|
|
16177
16063
|
- typescript
|
|
16178
16064
|
- vue
|
|
16179
16065
|
|
|
16180
|
-
zova@5.1.176(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
|
|
16181
|
-
dependencies:
|
|
16182
|
-
zova-core: 5.1.94(typescript@5.9.3)
|
|
16183
|
-
zova-suite-a-zova: 5.1.175(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
|
|
16184
|
-
transitivePeerDependencies:
|
|
16185
|
-
- '@vue/composition-api'
|
|
16186
|
-
- debug
|
|
16187
|
-
- supports-color
|
|
16188
|
-
- typescript
|
|
16189
|
-
- vue
|
|
16190
|
-
|
|
16191
16066
|
zova@5.1.177(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
|
|
16192
16067
|
dependencies:
|
|
16193
16068
|
zova-core: 5.1.95(patch_hash=44197c3f359a73a1a5bb3dc0a8a97e705a55b30f9a434717aa24d75a96f2c6b6)(typescript@5.9.3)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
2
|
|
|
3
|
-
import { Api } from 'vona-module-a-openapiutils';
|
|
3
|
+
import { Api, v } from 'vona-module-a-openapiutils';
|
|
4
4
|
import { Dto } from 'vona-module-a-web';
|
|
5
5
|
|
|
6
6
|
export interface IDtoOptionsOperatorContext extends IDecoratorDtoOptions {}
|
|
@@ -18,4 +18,7 @@ export class DtoOperatorContext {
|
|
|
18
18
|
|
|
19
19
|
@Api.field()
|
|
20
20
|
userName: string;
|
|
21
|
+
|
|
22
|
+
@Api.field(v.optional())
|
|
23
|
+
displayName?: string;
|
|
21
24
|
}
|
|
@@ -47,6 +47,7 @@ describe('operatorAccess.test.ts', () => {
|
|
|
47
47
|
assert.equal(result.instanceName, app.ctx.instanceName);
|
|
48
48
|
assert.equal(result.userId, app.bean.passport.current!.user!.id.toString());
|
|
49
49
|
assert.equal(result.userName, app.bean.passport.current!.user!.name);
|
|
50
|
+
assert.equal(result.displayName, app.bean.passport.current!.user!.displayName);
|
|
50
51
|
} finally {
|
|
51
52
|
await app.bean.passport.signout();
|
|
52
53
|
}
|
|
@@ -32,6 +32,7 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate, IMetaVe
|
|
|
32
32
|
table.comment(entityUser.$comment.$table);
|
|
33
33
|
table.basicFields();
|
|
34
34
|
table.string(entityUser.name, 255).comment(entityUser.$comment.name);
|
|
35
|
+
table.string(entityUser.displayName, 100).comment(entityUser.$comment.displayName);
|
|
35
36
|
table.string(entityUser.avatar, 255).comment(entityUser.$comment.avatar);
|
|
36
37
|
table.string(entityUser.email, 255).comment(entityUser.$comment.email);
|
|
37
38
|
table.string(entityUser.mobile, 255).comment(entityUser.$comment.mobile);
|
|
@@ -29,8 +29,15 @@ const avatarSchema = avatar.openapi({
|
|
|
29
29
|
|
|
30
30
|
@Dto<IDtoOptionsAccountProfileUpdate>()
|
|
31
31
|
export class DtoAccountProfileUpdate {
|
|
32
|
-
@Api.field(
|
|
33
|
-
|
|
32
|
+
@Api.field(
|
|
33
|
+
v.title($locale('AccountDisplayName')),
|
|
34
|
+
z.preprocess(
|
|
35
|
+
// Preserve an explicit clear before optional-string coercion treats '' as omitted.
|
|
36
|
+
value => (value === '' ? null : value),
|
|
37
|
+
z.string().trim().max(100).nullable().optional(),
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
displayName?: string | null;
|
|
34
41
|
|
|
35
42
|
@Api.field(v.title($locale('AccountAvatar')), v.optional(), v.nullable(), avatarSchema)
|
|
36
43
|
avatar?: string | null;
|
|
@@ -15,6 +15,9 @@ export class EntityUser extends EntityBase implements IUser {
|
|
|
15
15
|
@Api.field(v.title($locale('UserName')))
|
|
16
16
|
name: string;
|
|
17
17
|
|
|
18
|
+
@Api.field(v.title($locale('AccountDisplayName')), z.string().trim().max(100).optional())
|
|
19
|
+
displayName?: string;
|
|
20
|
+
|
|
18
21
|
@Api.field(v.title($locale('UserAvatar')), v.optional())
|
|
19
22
|
avatar?: string;
|
|
20
23
|
|