build: package an executable war, and document runtime UI config
This commit is contained in:
+68
-16
@@ -4,11 +4,50 @@ Two artifacts, one host.
|
|||||||
|
|
||||||
| Artifact | Built from | Output |
|
| 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) |
|
| UI | `UdyamLinc-UI` | `dist/` (static files, ~880 kB) |
|
||||||
|
|
||||||
The UI bundle is built with `VITE_UDYAM_API_BASE_URL=/api/v1/udyam` — a **relative**
|
**The war is also executable.** `spring-boot-maven-plugin` repackages it with the
|
||||||
path. It therefore contains no hostname, serves correctly from any domain, and is
|
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
|
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
|
CORS never applies, and CORS is the single largest source of "worked locally, broken on
|
||||||
the server" in a split deployment.
|
the server" in a split deployment.
|
||||||
@@ -153,7 +192,7 @@ Virtual host:
|
|||||||
<LocationMatch "^/assets/">
|
<LocationMatch "^/assets/">
|
||||||
Header set Cache-Control "public, max-age=2592000, immutable"
|
Header set Cache-Control "public, max-age=2592000, immutable"
|
||||||
</LocationMatch>
|
</LocationMatch>
|
||||||
<LocationMatch "^/index\.html$">
|
<LocationMatch "^/(index\.html|config\.js)$">
|
||||||
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
||||||
</LocationMatch>
|
</LocationMatch>
|
||||||
</VirtualHost>
|
</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 —
|
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.
|
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
|
Supported — the build produces a war and `UdyamLincApplication` extends
|
||||||
stylistic:
|
`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.
|
**1. Tomcat 11 or newer.** Spring Boot 4.0.1 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; Tomcat 10 is
|
||||||
Tomcat 9 is `javax.*` and cannot load a `jakarta.*` application at all.
|
short of the servlet level. Check before deploying:
|
||||||
- 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.
|
|
||||||
|
|
||||||
The jar already embeds Tomcat 11, so running it directly gives the same container with
|
```bash
|
||||||
none of that risk. **Recommendation: keep the jar.**
|
$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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,18 @@
|
|||||||
<groupId>com.lincfusion</groupId>
|
<groupId>com.lincfusion</groupId>
|
||||||
<artifactId>udyamlinc-backend</artifactId>
|
<artifactId>udyamlinc-backend</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<!--
|
||||||
|
war, but still executable. spring-boot-maven-plugin repackages a war with
|
||||||
|
the provided-scope container under WEB-INF/lib-provided, so the one
|
||||||
|
artifact both runs with `java -jar` and deploys into a servlet container.
|
||||||
|
Nothing is given up by choosing war here.
|
||||||
|
|
||||||
|
If deploying into Tomcat rather than running it directly, that container
|
||||||
|
must be Tomcat 11+: Spring Boot 4 is Jakarta EE 11 / Servlet 6.1, and
|
||||||
|
Tomcat 9 (javax.*) cannot load it. Deploy the file as api#v1#udyam.war so
|
||||||
|
the container path matches the context path this app expects.
|
||||||
|
-->
|
||||||
|
<packaging>war</packaging>
|
||||||
|
|
||||||
<name>udyamlinc-backend</name>
|
<name>udyamlinc-backend</name>
|
||||||
<description>UdyamLinc — predictive cash-flow intelligence and early warning for rural microenterprises (NABARD Hackathon)</description>
|
<description>UdyamLinc — predictive cash-flow intelligence and early warning for rural microenterprises (NABARD Hackathon)</description>
|
||||||
@@ -31,6 +42,17 @@
|
|||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
provided, so a servlet container supplies its own Tomcat instead of
|
||||||
|
the app carrying a second one into it. The repackaged war still holds
|
||||||
|
these under WEB-INF/lib-provided, so `java -jar` keeps working.
|
||||||
|
-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Security -->
|
<!-- Security -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.lincfusion.udyamlinc;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UdyamLinc — predictive cash-flow intelligence and early warning for rural
|
* UdyamLinc — predictive cash-flow intelligence and early warning for rural
|
||||||
@@ -13,9 +15,36 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||||||
* ingestion pipeline are separate components and are not implemented here.
|
* ingestion pipeline are separate components and are not implemented here.
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class UdyamLincApplication {
|
public class UdyamLincApplication extends SpringBootServletInitializer {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(UdyamLincApplication.class, args);
|
SpringApplication.run(UdyamLincApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point when a servlet container starts this instead of {@code main}.
|
||||||
|
*
|
||||||
|
* <p>The artifact is packaged as a war so it can be deployed to Tomcat, but
|
||||||
|
* {@code spring-boot-maven-plugin} still repackages it as executable — the
|
||||||
|
* provided-scope container lands in {@code WEB-INF/lib-provided}, which the
|
||||||
|
* launcher puts on the classpath. So {@code java -jar udyamlinc-backend.war}
|
||||||
|
* and dropping the same file into {@code webapps/} are both supported, from
|
||||||
|
* one build.
|
||||||
|
*
|
||||||
|
* <p><b>Tomcat 11 or newer.</b> Spring Boot 4 is built on Jakarta EE 11 and
|
||||||
|
* Servlet 6.1. Tomcat 9 is {@code javax.*} and cannot load this at all;
|
||||||
|
* Tomcat 10 is short of the servlet level. Running the war directly embeds
|
||||||
|
* Tomcat 11 and sidesteps the question entirely, which is why that remains
|
||||||
|
* the recommendation.
|
||||||
|
*
|
||||||
|
* <p><b>The context path moves.</b> {@code server.servlet.context-path} is
|
||||||
|
* ignored under a container — the deployed file name decides it. Tomcat reads
|
||||||
|
* {@code #} as a path separator, so the war must be deployed as
|
||||||
|
* {@code api#v1#udyam.war} to serve {@code /api/v1/udyam}. Deployed under any
|
||||||
|
* other name every endpoint moves and the UI's relative API base misses.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
|
||||||
|
return builder.sources(UdyamLincApplication.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user