WEB-547 feat(proxy): forward /fineract-provider to sandbox; add localhost sample and README MD034 fixes (#2958)
This commit is contained in:
@@ -12,7 +12,7 @@ Mifos X Web App is a modern single-page application (SPA) built on top of the Mi
|
|||||||
|
|
||||||
## Quick Links
|
## Quick Links
|
||||||
|
|
||||||
- [Live Demo](https://sandbox.mifos.community/#/login) (Updated nightly ** System is restored every 6 hours **)
|
- [Live Demo](https://sandbox.mifos.community/#/login) (Updated nightly — sandbox data is reset every 6 hours; test data and transient state may be cleared.)
|
||||||
- [GitHub Repository](https://github.com/openMF/web-app)
|
- [GitHub Repository](https://github.com/openMF/web-app)
|
||||||
- [Slack Channel](https://app.slack.com/client/T0F5GHE8Y/CJJGJLN10)
|
- [Slack Channel](https://app.slack.com/client/T0F5GHE8Y/CJJGJLN10)
|
||||||
- [Jira Board of Mifos](https://mifosforge.jira.com/jira/your-work)
|
- [Jira Board of Mifos](https://mifosforge.jira.com/jira/your-work)
|
||||||
@@ -31,13 +31,13 @@ Before installing the web app, you need to set up the Fineract backend server:
|
|||||||
|
|
||||||
1. **Choose ONE of these backend options:**
|
1. **Choose ONE of these backend options:**
|
||||||
- **Option A: Use existing remote server**
|
- **Option A: Use existing remote server**
|
||||||
- Use the sandbox (MariaDB) at https://sandbox.mifos.community ** System is restored every 6 hours **
|
- Use the [sandbox (MariaDB)](https://sandbox.mifos.community) — sandbox data is reset every 6 hours; test data and transient state may be cleared.
|
||||||
- Use the demo (MariaDB) at https://demo.mifos.community
|
- Use the [demo (MariaDB)](https://demo.mifos.community)
|
||||||
- Use the demo (Keycloak) at https://oauth.mifos.community
|
- Use the [demo (Keycloak)](https://oauth.mifos.community)
|
||||||
- Use the demo (2FA) at https://2fa.mifos.community
|
- Use the [demo (2FA)](https://2fa.mifos.community)
|
||||||
- Use the demo (Oidc) at https://oidc.mifos.community
|
- Use the [demo (Oidc)](https://oidc.mifos.community)
|
||||||
- Use the demo (Postgres) at https://elephant.mifos.community
|
- Use the [demo (Postgres)](https://elephant.mifos.community)
|
||||||
- Configure to your server by updating API URLs in environment files
|
- Configure to your server by updating API URLs in environment files
|
||||||
|
|
||||||
- **Option B: Install local Fineract server**
|
- **Option B: Install local Fineract server**
|
||||||
|
|
||||||
@@ -132,6 +132,101 @@ When using the development server with basic authentication:
|
|||||||
- **Build for production:** `ng build --configuration production` or `npm run build:prod`
|
- **Build for production:** `ng build --configuration production` or `npm run build:prod`
|
||||||
- **Get Angular CLI help:** `ng help`
|
- **Get Angular CLI help:** `ng help`
|
||||||
|
|
||||||
|
## Proxy Configuration
|
||||||
|
|
||||||
|
The web app includes a proxy configuration (`proxy.conf.js`) that allows you to forward API requests to a remote Fineract backend during local development. This helps avoid CORS issues and enables you to work against production-like environments.
|
||||||
|
|
||||||
|
### Using the Sandbox Proxy (Default)
|
||||||
|
|
||||||
|
By default, the proxy forwards `/fineract-provider` requests to the Mifos sandbox environment:
|
||||||
|
|
||||||
|
- **Target:** `https://sandbox.mifos.community`
|
||||||
|
- **API Endpoint:** `https://apis.mifos.community` (exposed in the sandbox)
|
||||||
|
- **System Reset:** Sandbox test data and transient state are reset every 6 hours (expect data to be periodically cleared).
|
||||||
|
|
||||||
|
**Sandbox Environment Variables:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FINERACT_API_URLS=https://apis.mifos.community
|
||||||
|
FINERACT_API_URL=https://apis.mifos.community
|
||||||
|
FINERACT_API_PROVIDER=/fineract-provider/api
|
||||||
|
FINERACT_API_ACTUATOR=/fineract-provider
|
||||||
|
FINERACT_API_VERSION=/v1
|
||||||
|
FINERACT_PLATFORM_TENANT_IDENTIFIER=default
|
||||||
|
MIFOS_DEFAULT_LANGUAGE=en-US
|
||||||
|
MIFOS_SUPPORTED_LANGUAGES=cs-CS,de-DE,en-US,es-MX,fr-FR,it-IT,ko-KO,lt-LT,lv-LV,ne-NE,pt-PT,sw-SW
|
||||||
|
MIFOS_PRELOAD_CLIENTS=true
|
||||||
|
MIFOS_DEFAULT_CHAR_DELIMITER=,
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a Local Fineract Instance
|
||||||
|
|
||||||
|
To proxy to a local Fineract server instead:
|
||||||
|
|
||||||
|
Use the provided localhost proxy file (recommended for `ng serve`):
|
||||||
|
|
||||||
|
1. Start the dev server with the localhost proxy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng serve --proxy-config proxy.localhost.conf.js
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Ensure your local Fineract instance is running on `http://localhost:8443`.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- `proxy.localhost.conf.js` forwards `/fineract-provider` to your local backend to avoid CORS during development.
|
||||||
|
- The `HttpsProxyAgent` / `setupForProxy` logic (present in `proxy.conf.js`) is only necessary when an upstream corporate/HTTP proxy must be used (set via `HTTP_PROXY`/`http_proxy`). It is not required for a direct `localhost` backend.
|
||||||
|
|
||||||
|
### Proxy Features
|
||||||
|
|
||||||
|
- **CORS Avoidance:** Eliminates cross-origin issues during local development
|
||||||
|
- **Error Handling:** Gracefully handles proxy failures with detailed logging
|
||||||
|
- **Corporate Proxy Support:** Maintains support for corporate proxy agents via `HTTP_PROXY` environment variable
|
||||||
|
- **Debug Logging:** All proxy requests are logged for troubleshooting
|
||||||
|
|
||||||
|
The proxy is configured to work with Fineract endpoints as described in this section.
|
||||||
|
|
||||||
|
### Testing the Proxy
|
||||||
|
|
||||||
|
To verify the proxy is working correctly, start the development server (`ng serve`) and test with curl:
|
||||||
|
|
||||||
|
**Successful proxy request:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -i "http://localhost:4200/fineract-provider/api/v1/runreports/FullClientReport?R_officeId=1&output-type=HTML&R_loanOfficerId=-1"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: HTTP 200 response with proxied data from the sandbox. Server console shows:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[Proxy] Proxying: GET /fineract-provider/api/v1/runreports/... -> https://sandbox.mifos.community/api/v1/runreports/...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Simulated proxy error (backend unreachable):**
|
||||||
|
|
||||||
|
If the backend is unreachable or returns an error, the proxy returns HTTP 502:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop your Fineract backend (if using localhost) or test with an invalid target
|
||||||
|
# The proxy will log the error and return:
|
||||||
|
```
|
||||||
|
|
||||||
|
Server console (example):
|
||||||
|
|
||||||
|
```text
|
||||||
|
[Proxy] Error while proxying request: GET /fineract-provider/... -> https://sandbox.mifos.community - ECONNREFUSED
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP response:
|
||||||
|
|
||||||
|
```http
|
||||||
|
HTTP/1.1 502 Bad Gateway
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
Proxy error: connect ECONNREFUSED
|
||||||
|
```
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
### Environment Variables for Docker
|
### Environment Variables for Docker
|
||||||
@@ -166,7 +261,7 @@ Available languages:
|
|||||||
| French | fr | fr-FR.json |
|
| French | fr | fr-FR.json |
|
||||||
| Italian | it | it-IT.json |
|
| Italian | it | it-IT.json |
|
||||||
| Korean | ko | ko-KO.json |
|
| Korean | ko | ko-KO.json |
|
||||||
| Lithuanian | li | li-LI.json |
|
| Lithuanian | lt | lt-LT.json |
|
||||||
| Latvian | lv | lv-LV.json |
|
| Latvian | lv | lv-LV.json |
|
||||||
| Nepali | ne | ne-NE.json |
|
| Nepali | ne | ne-NE.json |
|
||||||
| Portuguese | pt | pt-PT.json |
|
| Portuguese | pt | pt-PT.json |
|
||||||
|
|||||||
+57
-11
@@ -18,26 +18,72 @@ The interesting part is there:
|
|||||||
```js
|
```js
|
||||||
const proxyConfig = [
|
const proxyConfig = [
|
||||||
{
|
{
|
||||||
context: '/api',
|
context: ['/fineract-provider'],
|
||||||
pathRewrite: { '^/api': '' },
|
target: 'https://sandbox.mifos.community',
|
||||||
target: 'https://api.chucknorris.io',
|
pathRewrite: { '^/fineract-provider': '' },
|
||||||
changeOrigin: true
|
changeOrigin: true,
|
||||||
|
secure: true,
|
||||||
|
logLevel: 'debug',
|
||||||
|
onProxyReq: function (proxyReq, req, res) {
|
||||||
|
// Log the rewritten path so the console shows the actual forwarded URL
|
||||||
|
const rewrittenPath = (req.url || '').replace(/^\/fineract-provider/, '');
|
||||||
|
console.log('[Proxy] Proxying:', req.method, req.url, '->', this.target + rewrittenPath);
|
||||||
|
},
|
||||||
|
onError: function (err, req, res) {
|
||||||
|
// Log proxy errors and return HTTP 502 to the client
|
||||||
|
console.error('[Proxy] Error while proxying request:', req.method, req.url, '->', this.target, '-', err.message);
|
||||||
|
if (res && !res.headersSent) {
|
||||||
|
res.writeHead(502, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Proxy error: ' + err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
This is where you can setup one or more proxy rules.
|
This forwards requests sent to `/fineract-provider/...` on the dev server to the Fineract backend root (for example,
|
||||||
|
`/fineract-provider/1.0/...` becomes `https://sandbox.mifos.community/1.0/...`). Use `pathRewrite` to strip the prefix
|
||||||
|
because the sandbox exposes Fineract APIs at the root path.
|
||||||
|
|
||||||
|
**Error Handling:**
|
||||||
|
|
||||||
|
The `onError` handler captures proxy failures (network errors, backend unreachable, timeouts, etc.) and:
|
||||||
|
|
||||||
|
- Logs detailed error information to the server console (method, URL, target, error message)
|
||||||
|
- Returns HTTP 502 (Bad Gateway) to the client with a plain-text error message
|
||||||
|
|
||||||
|
Example server console output when proxy fails:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[Proxy] Error while proxying request: GET /fineract-provider/api/v1/clients -> https://sandbox.mifos.community - ECONNREFUSED
|
||||||
|
```
|
||||||
|
|
||||||
|
Example client response:
|
||||||
|
|
||||||
|
```http
|
||||||
|
HTTP/1.1 502 Bad Gateway
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
Proxy error: connect ECONNREFUSED
|
||||||
|
```
|
||||||
|
|
||||||
|
You can add multiple rules or change the `target` to point to other environments (demo, localhost, etc.).
|
||||||
|
|
||||||
For the complete set of options, see the `http-proxy-middleware`
|
For the complete set of options, see the `http-proxy-middleware`
|
||||||
[documentation](https://github.com/chimurai/http-proxy-middleware#options).
|
[documentation](https://github.com/chimurai/http-proxy-middleware#options).
|
||||||
|
|
||||||
### Corporate proxy support
|
### Corporate proxy support
|
||||||
|
|
||||||
To allow external API calls redirection through a corporate proxy, you will also find a `setupForCorporateProxy()`
|
The repository configures a helper function in `proxy.conf.js` named `setupForProxy` which will attach an
|
||||||
function in the proxy configuration file. By default, this method configures a corporate proxy agent based on the
|
`HttpsProxyAgent` to proxy entries when the `HTTP_PROXY` (or `http_proxy`) environment variable is present. This lets
|
||||||
`HTTP_PROXY` environment variable, see the [corporate proxy documentation](corporate-proxy.md) for more details.
|
the dev server forward requests through a corporate proxy when required.
|
||||||
|
|
||||||
If you need to, you can further customize this function to fit the network of your working environment.
|
**Behavior:**
|
||||||
|
|
||||||
If your corporate proxy use a custom SSL certificate, your may need to add the `secure: false` option to your
|
- **When `HTTP_PROXY` is set:** Logs `"Using proxy server: <url>"` and configures the agent for corporate proxy forwarding
|
||||||
backend proxy configuration.
|
- **When `HTTP_PROXY` is not set:** Logs `"No proxy server configured. API requests will not be proxied."` — note that this refers only to corporate proxy forwarding; the Angular dev server proxy (forwarding `/fineract-provider` to the sandbox) still works normally
|
||||||
|
|
||||||
|
See `proxy.conf.js` for the exact implementation.
|
||||||
|
|
||||||
|
If your corporate proxy uses a custom SSL certificate you may need to set `secure: false` on the specific proxy entry
|
||||||
|
or configure your environment to trust the corporate CA.
|
||||||
|
|||||||
+26
-5
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||||
|
|
||||||
/*
|
/* IMPORTANT:
|
||||||
* API proxy configuration.
|
* API proxy configuration.
|
||||||
* This allows you to proxy HTTP request like `http.get('/api/stuff')` to another server/port.
|
* This allows you to proxy HTTP request like `http.get('/api/stuff')` to another server/port.
|
||||||
* This is especially useful during app development to avoid CORS issues while running a local server.
|
* This is especially useful during app development to avoid CORS issues while running a local server.
|
||||||
@@ -10,12 +10,33 @@ const { HttpsProxyAgent } = require('https-proxy-agent');
|
|||||||
*/
|
*/
|
||||||
const proxyConfig = [
|
const proxyConfig = [
|
||||||
{
|
{
|
||||||
context: '/api',
|
context: ['/fineract-provider'],
|
||||||
pathRewrite: { '^/api': '' },
|
target: 'https://sandbox.mifos.community',
|
||||||
target: 'https://api.chucknorris.io',
|
pathRewrite: { '^/fineract-provider': '' },
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false
|
secure: true,
|
||||||
|
logLevel: 'debug',
|
||||||
|
onProxyReq: function (proxyReq, req, res) {
|
||||||
|
const rewrittenPath = (req.url || '').replace(/^\/fineract-provider/, '');
|
||||||
|
console.log('[Proxy] Proxying:', req.method, req.url, '->', this.target + rewrittenPath);
|
||||||
|
},
|
||||||
|
onError: function (err, req, res) {
|
||||||
|
console.error(
|
||||||
|
'[Proxy] Error while proxying request:',
|
||||||
|
req && req.method,
|
||||||
|
req && req.url,
|
||||||
|
'->',
|
||||||
|
this.target,
|
||||||
|
'-',
|
||||||
|
err && err.message
|
||||||
|
);
|
||||||
|
if (res && !res.headersSent) {
|
||||||
|
res.writeHead(502, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Proxy error: ' + (err && err.message ? err.message : 'Unknown error'));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// For local development use `proxy.localhost.conf js` .
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy configuration for running the app against a local Fineract instance.
|
||||||
|
* Usage:
|
||||||
|
* ng serve --proxy-config proxy.localhost.conf.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
context: ['/fineract-provider'],
|
||||||
|
target: 'http://localhost:8443',
|
||||||
|
pathRewrite: { '^/fineract-provider': '' },
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
logLevel: 'debug',
|
||||||
|
onProxyReq: function (proxyReq, req, res) {
|
||||||
|
const rewrittenPath = (req.url || '').replace(/^\/fineract-provider/, '');
|
||||||
|
console.log('[Proxy] Proxying:', req.method, req.url, '->', this.target + rewrittenPath);
|
||||||
|
},
|
||||||
|
onError: function (err, req, res) {
|
||||||
|
console.error(
|
||||||
|
'[Proxy] Error while proxying request:',
|
||||||
|
req && req.method,
|
||||||
|
req && req.url,
|
||||||
|
'->',
|
||||||
|
this.target,
|
||||||
|
'-',
|
||||||
|
err && err.message
|
||||||
|
);
|
||||||
|
if (res && !res.headersSent) {
|
||||||
|
res.writeHead(502, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Proxy error: ' + (err && err.message ? err.message : 'Unknown error'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user