build: package an executable war, and document runtime UI config

This commit is contained in:
LincPay
2026-08-15 17:01:48 +05:30
parent 351da2f67d
commit bfbb933fe1
3 changed files with 121 additions and 18 deletions
+68 -16
View File
@@ -4,11 +4,50 @@ Two artifacts, one host.
| Artifact | Built from | Output |
|---|---|---|
| API | `UdyamLinc-Backend` | `target/udyamlinc-backend.jar` (~80 MB, executable) |
| API | `UdyamLinc-Backend` | `target/udyamlinc-backend.war` (~80 MB) |
| UI | `UdyamLinc-UI` | `dist/` (static files, ~880 kB) |
The UI bundle is built with `VITE_UDYAM_API_BASE_URL=/api/v1/udyam` — a **relative**
path. It therefore contains no hostname, serves correctly from any domain, and is
**The war is also executable.** `spring-boot-maven-plugin` repackages it with the
provided-scope Tomcat under `WEB-INF/lib-provided`, so one artifact both runs on its own
and deploys into a container:
```bash
java -jar udyamlinc-backend.war # embeds Tomcat 11 — recommended
# or drop it into a servlet container — see §2c
```
### Pointing the UI at the API without rebuilding
The bundle reads its API base from **`config.js`**, served unbundled from the static
root. Deploy the backend first, then edit one line on the server — no rebuild, no
redeploy of the UI:
```js
// config.js, at the web root next to index.html
window.__UDYAM_CONFIG__ = {
apiBaseUrl: '', // empty = same-origin /api/v1/udyam (the default)
}
```
Set it only when the API is on another host:
```js
apiBaseUrl: 'https://udyamlinc-api.lincpay.tech/api/v1/udyam'
```
Resolution order is `config.js` → the build-time `VITE_UDYAM_API_BASE_URL` →
`/api/v1/udyam`. An empty string means "unset", so the shipped file needs no edit for a
same-origin deployment.
Two things that bite:
- **`config.js` must never be cached.** The nginx and httpd configs below send
`no-store` for it. Cached, it pins the old URL for everyone who has already loaded
the app, and nothing on the server side shows it.
- **A cross-origin value needs `UDYAM_CORS_ALLOWED_ORIGINS`** set to the UI's origin, or
the browser blocks every call while the API itself looks perfectly healthy.
The UI bundle is otherwise built with a **relative** default. It therefore contains no hostname, serves correctly from any domain, and is
same-origin with the API. That last point is the one worth keeping: same-origin means
CORS never applies, and CORS is the single largest source of "worked locally, broken on
the server" in a split deployment.
@@ -153,7 +192,7 @@ Virtual host:
<LocationMatch "^/assets/">
Header set Cache-Control "public, max-age=2592000, immutable"
</LocationMatch>
<LocationMatch "^/index\.html$">
<LocationMatch "^/(index\.html|config\.js)$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
</LocationMatch>
</VirtualHost>
@@ -187,21 +226,34 @@ RewriteRule ^(.*)$ /index.html
The API still runs as its own `java -jar` process on 8082, and Tomcat proxies to it —
or you put httpd/nginx in front of both.
### Deploying the API *as a WAR* into Tomcat — do not do this today
### Deploying the API as a WAR into Tomcat
It is possible but it is not a same-day change, and the constraint is hard rather than
stylistic:
Supported — the build produces a war and `UdyamLincApplication` extends
`SpringBootServletInitializer`. Two conditions, and both are hard:
- This project is **Spring Boot 4.0.1**, which is built on Jakarta EE 11 / Servlet 6.1.
That requires **Tomcat 11**. On Tomcat 9 or 10 the application does not start —
Tomcat 9 is `javax.*` and cannot load a `jakarta.*` application at all.
- It needs `<packaging>war</packaging>`, `spring-boot-starter-tomcat` moved to
`provided`, and `UdyamLincApplication` extending `SpringBootServletInitializer`.
- The context path moves from the configured `/api/v1/udyam` to the WAR's deployed
name, so the proxy rules above stop matching.
**1. Tomcat 11 or newer.** Spring Boot 4.0.1 is built on Jakarta EE 11 / Servlet 6.1.
Tomcat 9 is `javax.*` and cannot load a `jakarta.*` application at all; Tomcat 10 is
short of the servlet level. Check before deploying:
The jar already embeds Tomcat 11, so running it directly gives the same container with
none of that risk. **Recommendation: keep the jar.**
```bash
$CATALINA_HOME/bin/version.sh # or version.bat
```
**2. Deploy it as `api#v1#udyam.war`.** Under a container `server.servlet.context-path`
is ignored — the file name decides the path, and Tomcat reads `#` as a separator. So:
```bash
cp udyamlinc-backend.war $CATALINA_HOME/webapps/api#v1#udyam.war
```
serves `/api/v1/udyam`, which is what the UI's relative API base expects. Deployed under
any other name every endpoint moves and the UI gets 404s that look like a broken build.
Environment variables are read the same way; set them in `setenv.sh` or the service
definition rather than per-shell.
**Still, running the war directly is the lower-risk option** — it embeds Tomcat 11, so
neither condition can bite, and the same file works both ways.
---