cabloy 5.1.107 → 5.1.109
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 +27 -0
- package/cabloy-docs/.vitepress/config.mjs +6 -0
- package/cabloy-docs/backend/multi-instance-and-instance-resolution.md +18 -0
- package/cabloy-docs/backend/runtime-and-flavors.md +2 -2
- package/cabloy-docs/backend/scripts.md +3 -2
- package/cabloy-docs/frontend/ssr-env.md +20 -0
- package/cabloy-docs/frontend/table-resource-crud-cookbook.md +33 -1
- package/cabloy-docs/fullstack/deploy-cloudflare-docker.md +136 -0
- package/package.json +1 -1
- package/vona/docker-compose-original/config/nginx/conf.d/nginx.conf +1 -1
- package/vona/env/.env +1 -1
- package/vona/pnpm-lock.yaml +16 -16
- package/vona/src/backend/config/config/config.ts +1 -1
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +2 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +2 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +50 -1
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +21 -1
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +47 -1
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +47 -1
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +60 -2
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +27 -2
- package/zova/src/suite/cabloy-basic/modules/basic-form/src/.metadata/component/blockFormLayout.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-form/src/.metadata/index.ts +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-form/src/component/blockFormLayout/controller.tsx +165 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/.metadata/component/blockFilterActions.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/.metadata/index.ts +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockFilter/controller.tsx +61 -31
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockFilterActions/controller.tsx +51 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/types/page.ts +10 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockForm/controller.tsx +9 -2
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +5 -3
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/form/controller.tsx +44 -3
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/form/render.tsx +22 -4
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/formField/controller.tsx +3 -2
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/lib/formLayout.ts +203 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/lib/index.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/formField.ts +7 -4
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/formLayout.ts +59 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/index.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/action.ts +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/resource/formLayout.ts +53 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/resource/index.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssr.ts +1 -1
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.109
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.109
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Add DTO-driven form layouts.
|
|
8
|
+
- Add configurable filter form layouts.
|
|
9
|
+
- Add a student view form layout.
|
|
10
|
+
- Display form error counts on layout tabs.
|
|
11
|
+
- Allow form layout IDs to be optional.
|
|
12
|
+
|
|
13
|
+
### Improvements
|
|
14
|
+
|
|
15
|
+
- Upgrade TanStack Vue Form.
|
|
16
|
+
- Replace the deprecated TanStack Form store hook.
|
|
17
|
+
- Localize form layout tab state.
|
|
18
|
+
- Clarify DTO render field typing boundaries in the documentation.
|
|
19
|
+
|
|
20
|
+
## 5.1.108
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
- Improve Docker domain deployment support.
|
|
25
|
+
|
|
26
|
+
### Improvements
|
|
27
|
+
|
|
28
|
+
- Clarify shared deployment guidance.
|
|
29
|
+
|
|
3
30
|
## 5.1.107
|
|
4
31
|
|
|
5
32
|
### Features
|
|
@@ -41,6 +41,12 @@ const fullstackGroups = [
|
|
|
41
41
|
{ text: 'Suites and Modules', link: '/fullstack/suites-and-modules' },
|
|
42
42
|
],
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
text: 'Fullstack / Deployment',
|
|
46
|
+
items: [
|
|
47
|
+
{ text: 'Docker + Cloudflare Deployment', link: '/fullstack/deploy-cloudflare-docker' },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
44
50
|
{
|
|
45
51
|
text: 'Fullstack / Tutorials',
|
|
46
52
|
items: [
|
|
@@ -84,6 +84,24 @@ A practical fallback rule is:
|
|
|
84
84
|
|
|
85
85
|
- subdomain-based instance resolution only becomes relevant if custom getter, query-field, and header-field resolution did not already determine the instance name
|
|
86
86
|
|
|
87
|
+
## Subdomain mapping and the default instance
|
|
88
|
+
|
|
89
|
+
`SERVER_SUBDOMAINOFFSET` controls how many labels at the right side of a hostname are treated as the base domain. Its default value is `2`, which fits a two-label base domain such as `cabloy.test` or `example.com`.
|
|
90
|
+
|
|
91
|
+
With that default, Vona derives the instance name from the remaining labels:
|
|
92
|
+
|
|
93
|
+
| Hostname | Derived instance name |
|
|
94
|
+
| --------------------- | --------------------- |
|
|
95
|
+
| `cabloy.test` | `''` |
|
|
96
|
+
| `acme.cabloy.test` | `acme` |
|
|
97
|
+
| `eu.acme.cabloy.test` | `acme.eu` |
|
|
98
|
+
|
|
99
|
+
The last example is intentionally `acme.eu`: Koa reverses the remaining hostname labels before Vona joins `ctx.subdomains` with `.`.
|
|
100
|
+
|
|
101
|
+
The empty name `''` is the configured default instance. It is not an implicit fallback for every hostname: it can be disabled or removed. Each non-empty subdomain-derived name, including a deeper name such as `acme.eu`, must refer to an explicitly configured and enabled instance. Requests for an unknown, disabled, or deleted instance do not fall back to the default instance.
|
|
102
|
+
|
|
103
|
+
If the public base domain uses a different label shape, set an explicit `SERVER_SUBDOMAINOFFSET` override that matches that domain before relying on subdomain instance resolution.
|
|
104
|
+
|
|
87
105
|
## Instance edge cases to remember
|
|
88
106
|
|
|
89
107
|
A few current-runtime edge cases are worth remembering:
|
|
@@ -52,7 +52,6 @@ cd vona && npm run build
|
|
|
52
52
|
cd vona && npm run build:docker
|
|
53
53
|
cd vona && npm run start
|
|
54
54
|
cd vona && npm run start:one
|
|
55
|
-
cd vona && npm run start:docker
|
|
56
55
|
```
|
|
57
56
|
|
|
58
57
|
In the current repo, those scripts map naturally to:
|
|
@@ -83,9 +82,10 @@ cd vona && npm run play
|
|
|
83
82
|
cd vona && npm run build
|
|
84
83
|
cd vona && npm run build:docker
|
|
85
84
|
cd vona && npm run start
|
|
86
|
-
cd vona && npm run start:docker
|
|
87
85
|
```
|
|
88
86
|
|
|
87
|
+
`build:docker` produces Docker-oriented output. Run that output through the Docker Compose workflow rather than a direct Vona `start:docker` command.
|
|
88
|
+
|
|
89
89
|
These scripts show that the same `prod` mode can still branch into different output/runtime shapes through flavor.
|
|
90
90
|
|
|
91
91
|
## What the CLI injects automatically
|
|
@@ -52,14 +52,15 @@ From the root repository:
|
|
|
52
52
|
npm run start
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
Vona also supports
|
|
55
|
+
Vona also supports its normal and single-process start modes inside its own package:
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
cd vona && npm run start
|
|
59
59
|
cd vona && npm run start:one
|
|
60
|
-
cd vona && npm run start:docker
|
|
61
60
|
```
|
|
62
61
|
|
|
62
|
+
For Docker output, use the Docker Compose workflow after `npm run build:docker`; Docker does not use a direct Vona start command.
|
|
63
|
+
|
|
63
64
|
## Test and typecheck
|
|
64
65
|
|
|
65
66
|
From the root repository:
|
|
@@ -17,9 +17,29 @@ Representative variables include:
|
|
|
17
17
|
- `SSR_BODYREADYOBSERVER`
|
|
18
18
|
- `SSR_API_BASE_URL`
|
|
19
19
|
- `SSR_PROD_PORT`
|
|
20
|
+
- `SSR_TRANSFERCACHE`
|
|
21
|
+
- `SSR_TRANSFERCACHE_EXPIRES`
|
|
20
22
|
|
|
21
23
|
These affect areas such as cookie-driven SSR behavior, theme defaults, body-load observation, server-side API targeting, and SSR production port behavior.
|
|
22
24
|
|
|
25
|
+
## SSR response cache control
|
|
26
|
+
|
|
27
|
+
`SSR_TRANSFERCACHE` and `SSR_TRANSFERCACHE_EXPIRES` control the `Cache-Control` response header that Zova adds after an SSR page is rendered.
|
|
28
|
+
|
|
29
|
+
- `SSR_TRANSFERCACHE=false` disables this header path.
|
|
30
|
+
- Any other value enables it in the current configuration normalization.
|
|
31
|
+
- `SSR_TRANSFERCACHE_EXPIRES=0` emits `Cache-Control: no-cache, no-store, must-revalidate`.
|
|
32
|
+
- A positive number of seconds or an `ms`-style duration such as `10m` emits `Cache-Control: public, max-age=<seconds>`.
|
|
33
|
+
|
|
34
|
+
Cabloy Basic uses different flavor defaults:
|
|
35
|
+
|
|
36
|
+
| Flavor | Settings | SSR response header |
|
|
37
|
+
| ------ | --------------------------------------------------------- | ------------------------------------- |
|
|
38
|
+
| Web | `SSR_TRANSFERCACHE=true`, `SSR_TRANSFERCACHE_EXPIRES=10m` | `public, max-age=600` |
|
|
39
|
+
| Admin | `SSR_TRANSFERCACHE=true`, `SSR_TRANSFERCACHE_EXPIRES=0` | `no-cache, no-store, must-revalidate` |
|
|
40
|
+
|
|
41
|
+
A route can override the flavor default through its SSR route metadata. For Cloudflare cache-rule alignment that preserves these origin headers, see [Docker + Cloudflare Deployment](/fullstack/deploy-cloudflare-docker).
|
|
42
|
+
|
|
23
43
|
## Theme implications of `SSR_COOKIE`
|
|
24
44
|
|
|
25
45
|
`SSR_COOKIE` is not only a storage choice. It also changes what SSR can guarantee about theme-sensitive output.
|
|
@@ -162,8 +162,8 @@ This block uses `ZForm` with:
|
|
|
162
162
|
|
|
163
163
|
- `schema={$$page.schemaFilter}`
|
|
164
164
|
- `schemaScene="filter"`
|
|
165
|
-
- inline layout
|
|
166
165
|
- page-owned filter data
|
|
166
|
+
- field-level `formFieldLayout.inline: true` by default
|
|
167
167
|
|
|
168
168
|
Its job is not to duplicate the list query logic.
|
|
169
169
|
|
|
@@ -178,6 +178,38 @@ A practical rule is:
|
|
|
178
178
|
- if you need to refine which filter fields exist, start from backend filter-side metadata and schema
|
|
179
179
|
- if you need to refine how filter submission affects the list, inspect `blockFilter` and `blockPage.onFilter(...)`
|
|
180
180
|
|
|
181
|
+
### Use blocks for a structural filter layout
|
|
182
|
+
|
|
183
|
+
A bare `basic-page:blockFilter` keeps the default schema field rendering and adds Search/Reset controls automatically. For a structured filter, compose the existing form layout block with the filter-specific action block:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
ZovaRender.block('basic-page:blockFilter', {
|
|
187
|
+
formFieldLayout: { inline: false },
|
|
188
|
+
blocks: [
|
|
189
|
+
ZovaRender.block('basic-form:blockFormLayout', {
|
|
190
|
+
formLayout: {
|
|
191
|
+
children: [
|
|
192
|
+
{
|
|
193
|
+
type: 'section',
|
|
194
|
+
columns: { default: 1, md: 2 },
|
|
195
|
+
children: [
|
|
196
|
+
{ type: 'field', name: 'name' },
|
|
197
|
+
{ type: 'field', name: 'level' },
|
|
198
|
+
{ type: 'field', name: 'createdAt', span: { default: 1, md: 2 } },
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
}),
|
|
204
|
+
ZovaRender.block('basic-page:blockFilterActions'),
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`basic-form:blockFormLayout` only places schema fields. `basic-page:blockFilterActions` owns Search/Reset placement and invokes the filter command surface supplied through the form scope, so it preserves filter normalization and page-query behavior. A nonempty `blocks` list replaces the automatic body and footer; include the action block explicitly to make the filter operable.
|
|
210
|
+
|
|
211
|
+
`ZForm.inline` is no longer a form API. Use `formFieldLayout.inline` for field-level compact layout, or use blocks plus `basic-form:blockFormLayout` for structural layout.
|
|
212
|
+
|
|
181
213
|
## Step 5: Let `blockToolbarBulk` own bulk-action display
|
|
182
214
|
|
|
183
215
|
The standard bulk toolbar block is:
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Docker + Cloudflare Deployment
|
|
2
|
+
|
|
3
|
+
<Badge type="tip" text="Common" />
|
|
4
|
+
|
|
5
|
+
This guide deploys Cabloy Basic or Cabloy Start in two stages: first run the fullstack application on a cloud server with Docker Compose, then put Cloudflare orange-cloud proxying in front of that origin. It covers the Vona-integrated Zova Web and Admin SSR runtime shared by both editions.
|
|
6
|
+
|
|
7
|
+
Run all commands from the repository for the edition you are using. This guide documents the shared deployment contract; verify edition-specific flavor names, SSR site baselines, project assets, and repository details in the active edition source.
|
|
8
|
+
|
|
9
|
+
Cloudflare Pages and Cloudflare Workers are not supported deployment targets in this guide.
|
|
10
|
+
|
|
11
|
+
## Deployment model
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
Browser
|
|
15
|
+
-> Cloudflare orange cloud
|
|
16
|
+
-> origin Nginx in Docker Compose
|
|
17
|
+
-> Vona app with Web/Admin SSR
|
|
18
|
+
-> PostgreSQL and Redis
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The Docker Compose origin is the application deployment. Cloudflare provides DNS, edge TLS, proxying, and response caching in front of that origin.
|
|
22
|
+
|
|
23
|
+
## Stage 1: deploy the Docker origin
|
|
24
|
+
|
|
25
|
+
On the cloud server, clone and initialize the repository for your edition, then build and start its Docker flavor:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm run init
|
|
29
|
+
npm run build:docker
|
|
30
|
+
cd vona/docker-compose
|
|
31
|
+
sudo COMPOSE_BAKE=true docker-compose build
|
|
32
|
+
sudo docker-compose up -d
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The Compose stack contains the application, Nginx, PostgreSQL, and Redis. Treat database passwords, persistent volumes, backups, firewall rules, operating-system updates, and server hardening as production operator responsibilities.
|
|
36
|
+
|
|
37
|
+
In the repository for the edition you are using, `vona/docker-compose/` is the generated and ignored deployment directory, while `vona/docker-compose-original/` is the tracked source template. Initialization copies the template only when the generated directory does not already exist, so upgrading the repository does not automatically update an existing generated Nginx configuration. Reconcile or regenerate that local deployment configuration deliberately, and verify the current template in your edition repository.
|
|
38
|
+
|
|
39
|
+
## Local preflight with `cabloy.test`
|
|
40
|
+
|
|
41
|
+
Before using a public domain, test the same hostname model locally. Use the reserved test domain `cabloy.test`, not a public domain such as `test.com`.
|
|
42
|
+
|
|
43
|
+
Map every hostname that you want to test in the local hosts file, or use local DNS. For example:
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
127.0.0.1 cabloy.test acme.cabloy.test eu.acme.cabloy.test
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`SERVER_SUBDOMAINOFFSET` defaults to `2`. It treats the rightmost two labels as the base domain:
|
|
50
|
+
|
|
51
|
+
| Hostname | Derived instance |
|
|
52
|
+
| --------------------- | --------------------------- |
|
|
53
|
+
| `cabloy.test` | `''` (the default instance) |
|
|
54
|
+
| `acme.cabloy.test` | `acme` |
|
|
55
|
+
| `eu.acme.cabloy.test` | `acme.eu` |
|
|
56
|
+
|
|
57
|
+
The default instance is an explicitly configured empty-name instance. It can be disabled or removed. Each non-empty derived instance name must also be explicitly configured and enabled before its hostname can serve application traffic. A missing, disabled, or deleted named instance does not fall back to the default instance.
|
|
58
|
+
|
|
59
|
+
The Docker Nginx template preserves both the incoming `Host` and `X-Forwarded-Host`. This is necessary because Vona uses the forwarded host for host-sensitive URLs and subdomain instance resolution. Test through Nginx, not only by calling the app container directly.
|
|
60
|
+
|
|
61
|
+
For the instance configuration shape and full resolution order, see [Multi-Instance and Instance Resolution](/backend/multi-instance-and-instance-resolution).
|
|
62
|
+
|
|
63
|
+
## Origin HTTPS prerequisite
|
|
64
|
+
|
|
65
|
+
Cloudflare TLS has two separate connections:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
Visitor <-> Cloudflare edge
|
|
69
|
+
Cloudflare <-> origin Nginx
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The shared Docker origin baseline listens on port 80 only. Verify the current Nginx template in your edition repository. Before enabling Cloudflare **Full (strict)**, provide a separately managed TLS-capable origin layer and a valid certificate that matches the origin hostname. For example, this can be an Nginx or ingress configuration managed by the deployment environment.
|
|
73
|
+
|
|
74
|
+
Use Cloudflare **Full (strict)** for the production connection to the origin. Do not use Flexible mode: it leaves the Cloudflare-to-origin connection unencrypted and can produce incorrect scheme and security behavior. Enabling the orange cloud alone does not add HTTPS to the origin.
|
|
75
|
+
|
|
76
|
+
For current Cloudflare requirements, see [Full (strict)](https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/full-strict/).
|
|
77
|
+
|
|
78
|
+
## Stage 2: put Cloudflare in front of the origin
|
|
79
|
+
|
|
80
|
+
After the origin is reachable with its TLS configuration:
|
|
81
|
+
|
|
82
|
+
1. Create the DNS record for the base hostname, such as `example.com`, pointing to the origin server.
|
|
83
|
+
2. Enable the record's orange-cloud proxy status.
|
|
84
|
+
3. If subdomain instance routing is required, add the required instance records or a suitable wildcard record, such as `*.example.com`.
|
|
85
|
+
4. Keep the origin proxy configuration forwarding the original `Host` and `X-Forwarded-Host` values to Vona.
|
|
86
|
+
5. Set the deployment environment to the public URL when it must produce canonical absolute URLs:
|
|
87
|
+
|
|
88
|
+
```dotenv
|
|
89
|
+
SERVER_SERVE_PROTOCOL=https
|
|
90
|
+
SERVER_SERVE_HOST=example.com
|
|
91
|
+
SERVER_SUBDOMAINOFFSET=2
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The base hostname resolves to the empty-name/default instance. For example, `acme.example.com` resolves to `acme`; a deeper hostname such as `eu.acme.example.com` resolves to `acme.eu` and requires that explicitly configured instance.
|
|
95
|
+
|
|
96
|
+
For current Cloudflare proxy-record behavior, see [Proxied DNS records](https://developers.cloudflare.com/dns/manage-dns-records/reference/proxied-dns-records/).
|
|
97
|
+
|
|
98
|
+
## Cloudflare Cache Rule
|
|
99
|
+
|
|
100
|
+
Configure one Cache Rule for the SSR origin:
|
|
101
|
+
|
|
102
|
+
1. **If incoming requests match:** All incoming requests.
|
|
103
|
+
2. **Cache eligibility:** Eligible for cache.
|
|
104
|
+
3. **Edge TTL:** Use the origin `Cache-Control` header when present; bypass cache when it is absent.
|
|
105
|
+
|
|
106
|
+
Do not add another Cache Rule or response-header override that replaces the origin `Cache-Control` value for these routes.
|
|
107
|
+
|
|
108
|
+
This rule is intentionally small. It makes a response eligible for caching but still lets the SSR response header decide whether Cloudflare stores it. A `no-store` response remains non-cacheable, and normal Cloudflare cacheability safeguards still apply.
|
|
109
|
+
|
|
110
|
+
For current Cache Rule settings, see [Cache Rules settings](https://developers.cloudflare.com/cache/how-to/cache-rules/settings/).
|
|
111
|
+
|
|
112
|
+
## SSR cache contract
|
|
113
|
+
|
|
114
|
+
Zova SSR writes the cache contract during rendering through `SSR_TRANSFERCACHE` and `SSR_TRANSFERCACHE_EXPIRES`. The current public Cabloy Basic baseline uses these defaults:
|
|
115
|
+
|
|
116
|
+
| Flavor | Default settings | SSR response header | Cloudflare result with this rule |
|
|
117
|
+
| ------ | --------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------- |
|
|
118
|
+
| Web | `SSR_TRANSFERCACHE=true`, `SSR_TRANSFERCACHE_EXPIRES=10m` | `Cache-Control: public, max-age=600` | Eligible to cache for ten minutes, subject to Cloudflare policy |
|
|
119
|
+
| Admin | `SSR_TRANSFERCACHE=true`, `SSR_TRANSFERCACHE_EXPIRES=0` | `Cache-Control: no-cache, no-store, must-revalidate` | Not stored |
|
|
120
|
+
|
|
121
|
+
For Cabloy Start, verify the effective Web and Admin values in the licensed Start repository before creating the Cloudflare rule. The rule design remains the same: preserve and follow the origin `Cache-Control` response instead of replacing it.
|
|
122
|
+
|
|
123
|
+
A route can override the flavor default through SSR route metadata. The Cloudflare rule follows the response contract; it does not replace it.
|
|
124
|
+
|
|
125
|
+
For the public Basic environment-variable details, see [SSR Environment Variables](/frontend/ssr-env).
|
|
126
|
+
|
|
127
|
+
## Deployment checks
|
|
128
|
+
|
|
129
|
+
After the origin is running, verify the following before relying on Cloudflare traffic:
|
|
130
|
+
|
|
131
|
+
1. The base hostname returns the expected default instance.
|
|
132
|
+
2. Each configured subdomain resolves to its expected enabled instance.
|
|
133
|
+
3. Nginx forwards the browser hostname instead of the literal value `localhost`.
|
|
134
|
+
4. Web SSR returns the `Cache-Control` header expected from the active edition and flavor configuration. In the current Basic baseline, it is `public, max-age=600`.
|
|
135
|
+
5. Admin SSR returns the `Cache-Control` header expected from the active edition and flavor configuration. In the current Basic baseline, it is `no-cache, no-store, must-revalidate`.
|
|
136
|
+
6. After Cloudflare proxying is enabled, responses intended to be public-cacheable can be cached at the edge while responses marked `no-store` are not stored.
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@ server {
|
|
|
12
12
|
proxy_http_version 1.1;
|
|
13
13
|
proxy_set_header X-Real-IP $remote_addr;
|
|
14
14
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
15
|
-
proxy_set_header X-Forwarded-Host $
|
|
15
|
+
proxy_set_header X-Forwarded-Host $http_host;
|
|
16
16
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
17
17
|
proxy_set_header Host $http_host;
|
|
18
18
|
proxy_set_header X-NginX-Proxy true;
|
package/vona/env/.env
CHANGED
package/vona/pnpm-lock.yaml
CHANGED
|
@@ -501,8 +501,8 @@ importers:
|
|
|
501
501
|
specifier: npm:@cabloy/zod@4.3.8
|
|
502
502
|
version: '@cabloy/zod@4.3.8'
|
|
503
503
|
zova:
|
|
504
|
-
specifier: ^5.1.
|
|
505
|
-
version: 5.1.
|
|
504
|
+
specifier: ^5.1.136
|
|
505
|
+
version: 5.1.136(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
|
|
506
506
|
zova-jsx:
|
|
507
507
|
specifier: ^1.1.78
|
|
508
508
|
version: 1.1.78(typescript@5.9.3)
|
|
@@ -573,8 +573,8 @@ importers:
|
|
|
573
573
|
specifier: npm:@cabloy/zod@4.3.8
|
|
574
574
|
version: '@cabloy/zod@4.3.8'
|
|
575
575
|
zova:
|
|
576
|
-
specifier: ^5.1.
|
|
577
|
-
version: 5.1.
|
|
576
|
+
specifier: ^5.1.136
|
|
577
|
+
version: 5.1.136(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
|
|
578
578
|
zova-jsx:
|
|
579
579
|
specifier: ^1.1.78
|
|
580
580
|
version: 1.1.78(typescript@5.9.3)
|
|
@@ -1625,7 +1625,7 @@ importers:
|
|
|
1625
1625
|
src/suite-vendor/a-test:
|
|
1626
1626
|
dependencies:
|
|
1627
1627
|
vona-module-test-file:
|
|
1628
|
-
specifier: ^5.0.
|
|
1628
|
+
specifier: ^5.0.6
|
|
1629
1629
|
version: link:modules/test-file
|
|
1630
1630
|
|
|
1631
1631
|
src/suite-vendor/a-test/modules/test-auth:
|
|
@@ -8014,8 +8014,8 @@ packages:
|
|
|
8014
8014
|
zova-module-a-ssrhmr@5.1.22:
|
|
8015
8015
|
resolution: {integrity: sha512-K9D0DqCPuABvESyuAY8tQ7RukMpMEaMol0SV08ixVyed7Zd/b+Ugh49WEC2b5Y5iZCJliW9JJekvzS9Y0DilTA==}
|
|
8016
8016
|
|
|
8017
|
-
zova-module-a-ssrserver@5.1.
|
|
8018
|
-
resolution: {integrity: sha512
|
|
8017
|
+
zova-module-a-ssrserver@5.1.23:
|
|
8018
|
+
resolution: {integrity: sha512-+hpTTRfJP9mVCOueXYlAtt4W82ihQbQDRjJ7TjGVN/pIV3oL9Ca3I491wkd6I3Avq9dT7rjB/iKWs29F6ic3lA==}
|
|
8019
8019
|
|
|
8020
8020
|
zova-module-a-style@5.1.32:
|
|
8021
8021
|
resolution: {integrity: sha512-2D7XKTWdqY0/Z7jPJQ31Fikl2wNxifF5R/RexfemeJtrJBYap46mpA++BhxgawfOgMDQ8mpT5iQfS5Ep9TILlw==}
|
|
@@ -8032,11 +8032,11 @@ packages:
|
|
|
8032
8032
|
zova-module-rest-resource@5.1.41:
|
|
8033
8033
|
resolution: {integrity: sha512-7Y8rLmHdw0pynlWqsyHPWuCWFLwr/0HsNvfef64cPw91LRujf/7OlulVWqH8ZoFNioZZUpfumB+t/+mICoYfFg==}
|
|
8034
8034
|
|
|
8035
|
-
zova-suite-a-zova@5.1.
|
|
8036
|
-
resolution: {integrity: sha512-
|
|
8035
|
+
zova-suite-a-zova@5.1.135:
|
|
8036
|
+
resolution: {integrity: sha512-Fa3U2NegdnFR+NkHeY4V5ejicUK0Th5dKs9Anx20jfpao/p91mCuu3kni88rB9j4U3ztUyqZ/q6k683Y1vATcg==}
|
|
8037
8037
|
|
|
8038
|
-
zova@5.1.
|
|
8039
|
-
resolution: {integrity: sha512-
|
|
8038
|
+
zova@5.1.136:
|
|
8039
|
+
resolution: {integrity: sha512-Xf9ze/weAFo/kHTXjeJgB21RH1kjQC28F3pSBuKtGtzu87oxkdiG2meGKDWyP/n3cCvPs+BzOgRUSgQlsXZnXw==}
|
|
8040
8040
|
|
|
8041
8041
|
zwitch@1.0.5:
|
|
8042
8042
|
resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==}
|
|
@@ -13969,7 +13969,7 @@ snapshots:
|
|
|
13969
13969
|
'@cabloy/socket': link:packages-utils/socket
|
|
13970
13970
|
debounce: 3.0.0
|
|
13971
13971
|
|
|
13972
|
-
zova-module-a-ssrserver@5.1.
|
|
13972
|
+
zova-module-a-ssrserver@5.1.23:
|
|
13973
13973
|
dependencies:
|
|
13974
13974
|
ms: 2.1.3
|
|
13975
13975
|
|
|
@@ -14010,7 +14010,7 @@ snapshots:
|
|
|
14010
14010
|
|
|
14011
14011
|
zova-module-rest-resource@5.1.41: {}
|
|
14012
14012
|
|
|
14013
|
-
zova-suite-a-zova@5.1.
|
|
14013
|
+
zova-suite-a-zova@5.1.135(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
|
|
14014
14014
|
dependencies:
|
|
14015
14015
|
zova-module-a-api: 5.1.21
|
|
14016
14016
|
zova-module-a-app: 5.1.24
|
|
@@ -14032,7 +14032,7 @@ snapshots:
|
|
|
14032
14032
|
zova-module-a-routertabs: 5.1.33
|
|
14033
14033
|
zova-module-a-ssr: 5.1.29
|
|
14034
14034
|
zova-module-a-ssrhmr: 5.1.22
|
|
14035
|
-
zova-module-a-ssrserver: 5.1.
|
|
14035
|
+
zova-module-a-ssrserver: 5.1.23
|
|
14036
14036
|
zova-module-a-style: 5.1.32
|
|
14037
14037
|
zova-module-a-table: 5.1.37(vue@3.5.39(typescript@5.9.3))
|
|
14038
14038
|
zova-module-a-zod: 5.1.36
|
|
@@ -14044,10 +14044,10 @@ snapshots:
|
|
|
14044
14044
|
- typescript
|
|
14045
14045
|
- vue
|
|
14046
14046
|
|
|
14047
|
-
zova@5.1.
|
|
14047
|
+
zova@5.1.136(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
|
|
14048
14048
|
dependencies:
|
|
14049
14049
|
zova-core: 5.1.78(patch_hash=ff659aa5f659be32501f56841b72f148c24acf1546f79cce551586e27fcde2ce)(typescript@5.9.3)
|
|
14050
|
-
zova-suite-a-zova: 5.1.
|
|
14050
|
+
zova-suite-a-zova: 5.1.135(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
|
|
14051
14051
|
transitivePeerDependencies:
|
|
14052
14052
|
- '@vue/composition-api'
|
|
14053
14053
|
- debug
|
|
@@ -49,7 +49,7 @@ export default async function (app: VonaApplication) {
|
|
|
49
49
|
|
|
50
50
|
// server
|
|
51
51
|
const publicDir = env.SERVER_PUBLICDIR || getPublicPathPhysicalRoot(app);
|
|
52
|
-
const subdomainOffset = Number.parseInt(env.SERVER_SUBDOMAINOFFSET || '
|
|
52
|
+
const subdomainOffset = Number.parseInt(env.SERVER_SUBDOMAINOFFSET || '2');
|
|
53
53
|
const workers = Number.parseInt(env.SERVER_WORKERS!);
|
|
54
54
|
config.server = {
|
|
55
55
|
keys: (env.SERVER_KEYS || '').split(','),
|
|
@@ -16,7 +16,56 @@ export interface IDtoOptionsStudentCreate extends IDecoratorDtoOptions {}
|
|
|
16
16
|
blocks: [
|
|
17
17
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
18
18
|
blocks: [
|
|
19
|
-
ZovaRender.block('basic-pageentry:blockForm'
|
|
19
|
+
ZovaRender.block('basic-pageentry:blockForm', {
|
|
20
|
+
blocks: [
|
|
21
|
+
ZovaRender.block('basic-form:blockFormLayout', {
|
|
22
|
+
formLayout: {
|
|
23
|
+
children: [
|
|
24
|
+
{
|
|
25
|
+
type: 'tabs',
|
|
26
|
+
children: [
|
|
27
|
+
{
|
|
28
|
+
type: 'tab',
|
|
29
|
+
title: $locale('BasicInformation'),
|
|
30
|
+
children: [
|
|
31
|
+
{
|
|
32
|
+
type: 'group',
|
|
33
|
+
title: $locale('StudentProfile'),
|
|
34
|
+
children: [
|
|
35
|
+
{
|
|
36
|
+
type: 'section',
|
|
37
|
+
columns: { default: 1, md: 2 },
|
|
38
|
+
children: [
|
|
39
|
+
{ type: 'field', name: 'name' },
|
|
40
|
+
{ type: 'field', name: 'mobile' },
|
|
41
|
+
{ type: 'field', name: 'imageId' },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'tab',
|
|
50
|
+
title: $locale('TrainingRecords'),
|
|
51
|
+
children: [
|
|
52
|
+
{ type: 'field', name: 'level' },
|
|
53
|
+
{
|
|
54
|
+
type: 'section',
|
|
55
|
+
children: [{ type: 'field', name: 'trainingRecords' }],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
// blocks: [
|
|
66
|
+
// ZovaRender.block('')
|
|
67
|
+
// ],
|
|
68
|
+
}),
|
|
20
69
|
ZovaRender.block('basic-pageentry:blockToolbarRow', {
|
|
21
70
|
actions: [
|
|
22
71
|
ZovaRender.formActionRow('basic-form:actionSubmit', {
|
|
@@ -15,7 +15,27 @@ export interface IDtoOptionsStudentSelectResItem extends IDecoratorDtoOptions {}
|
|
|
15
15
|
blocks: [
|
|
16
16
|
ZovaRender.block('basic-page:blockPage', {
|
|
17
17
|
blocks: [
|
|
18
|
-
ZovaRender.block('basic-page:blockFilter'
|
|
18
|
+
ZovaRender.block('basic-page:blockFilter', {
|
|
19
|
+
formFieldLayout: { inline: false },
|
|
20
|
+
blocks: [
|
|
21
|
+
ZovaRender.block('basic-form:blockFormLayout', {
|
|
22
|
+
formLayout: {
|
|
23
|
+
children: [
|
|
24
|
+
{
|
|
25
|
+
type: 'section',
|
|
26
|
+
columns: { default: 1, md: 2 },
|
|
27
|
+
children: [
|
|
28
|
+
{ type: 'field', name: 'name' },
|
|
29
|
+
{ type: 'field', name: 'level' },
|
|
30
|
+
{ type: 'field', name: 'createdAt', span: { default: 1, md: 2 } },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
ZovaRender.block('basic-page:blockFilterActions'),
|
|
37
|
+
],
|
|
38
|
+
}),
|
|
19
39
|
ZovaRender.block('basic-page:blockToolbarBulk', {
|
|
20
40
|
actions: [ZovaRender.tableActionBulk('basic-table:actionCreate')],
|
|
21
41
|
}),
|
|
@@ -16,7 +16,53 @@ export interface IDtoOptionsStudentUpdate extends IDecoratorDtoOptions {}
|
|
|
16
16
|
blocks: [
|
|
17
17
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
18
18
|
blocks: [
|
|
19
|
-
ZovaRender.block('basic-pageentry:blockForm'
|
|
19
|
+
ZovaRender.block('basic-pageentry:blockForm', {
|
|
20
|
+
blocks: [
|
|
21
|
+
ZovaRender.block('basic-form:blockFormLayout', {
|
|
22
|
+
formLayout: {
|
|
23
|
+
children: [
|
|
24
|
+
{
|
|
25
|
+
type: 'tabs',
|
|
26
|
+
children: [
|
|
27
|
+
{
|
|
28
|
+
type: 'tab',
|
|
29
|
+
title: $locale('BasicInformation'),
|
|
30
|
+
children: [
|
|
31
|
+
{
|
|
32
|
+
type: 'group',
|
|
33
|
+
title: $locale('StudentProfile'),
|
|
34
|
+
children: [
|
|
35
|
+
{
|
|
36
|
+
type: 'section',
|
|
37
|
+
columns: { default: 1, md: 2 },
|
|
38
|
+
children: [
|
|
39
|
+
{ type: 'field', name: 'name' },
|
|
40
|
+
{ type: 'field', name: 'mobile' },
|
|
41
|
+
{ type: 'field', name: 'imageId' },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'tab',
|
|
50
|
+
title: $locale('TrainingRecords'),
|
|
51
|
+
children: [
|
|
52
|
+
{ type: 'field', name: 'level' },
|
|
53
|
+
{
|
|
54
|
+
type: 'section',
|
|
55
|
+
children: [{ type: 'field', name: 'trainingRecords' }],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
}),
|
|
20
66
|
ZovaRender.block('basic-pageentry:blockToolbarRow', {
|
|
21
67
|
actions: [
|
|
22
68
|
ZovaRender.formActionRow('basic-form:actionSubmit', {
|