Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 104 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Mifos X Web App is a modern single-page application (SPA) built on top of the Mi

## 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)
- [Slack Channel](https://app.slack.com/client/T0F5GHE8Y/CJJGJLN10)
- [Jira Board of Mifos](https://mifosforge.jira.com/jira/your-work)
Expand All @@ -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:**
- **Option A: Use existing remote server**
- Use the sandbox (MariaDB) at https://sandbox.mifos.community ** System is restored every 6 hours **
- Use the demo (MariaDB) at https://demo.mifos.community
- Use the demo (Keycloak) at https://oauth.mifos.community
- Use the demo (2FA) at https://2fa.mifos.community
- Use the demo (Oidc) at https://oidc.mifos.community
- Use the demo (Postgres) at https://elephant.mifos.community
- Configure to your server by updating API URLs in environment files
- 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)](https://demo.mifos.community)
- Use the [demo (Keycloak)](https://oauth.mifos.community)
- Use the [demo (2FA)](https://2fa.mifos.community)
- Use the [demo (Oidc)](https://oidc.mifos.community)
- Use the [demo (Postgres)](https://elephant.mifos.community)
- Configure to your server by updating API URLs in environment files

- **Option B: Install local Fineract server**

Expand Down Expand Up @@ -132,6 +132,101 @@ When using the development server with basic authentication:
- **Build for production:** `ng build --configuration production` or `npm run build:prod`
- **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

### Environment Variables for Docker
Expand Down Expand Up @@ -166,7 +261,7 @@ Available languages:
| French | fr | fr-FR.json |
| Italian | it | it-IT.json |
| Korean | ko | ko-KO.json |
| Lithuanian | li | li-LI.json |
| Lithuanian | lt | lt-LT.json |
| Latvian | lv | lv-LV.json |
| Nepali | ne | ne-NE.json |
| Portuguese | pt | pt-PT.json |
Expand Down
68 changes: 57 additions & 11 deletions docs/backend-proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,72 @@ The interesting part is there:
```js
const proxyConfig = [
{
context: '/api',
pathRewrite: { '^/api': '' },
target: 'https://api.chucknorris.io',
changeOrigin: true
context: ['/fineract-provider'],
target: 'https://sandbox.mifos.community',
pathRewrite: { '^/fineract-provider': '' },
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`
[documentation](https://github.com/chimurai/http-proxy-middleware#options).

### Corporate proxy support

To allow external API calls redirection through a corporate proxy, you will also find a `setupForCorporateProxy()`
function in the proxy configuration file. By default, this method configures a corporate proxy agent based on the
`HTTP_PROXY` environment variable, see the [corporate proxy documentation](corporate-proxy.md) for more details.
The repository configures a helper function in `proxy.conf.js` named `setupForProxy` which will attach an
`HttpsProxyAgent` to proxy entries when the `HTTP_PROXY` (or `http_proxy`) environment variable is present. This lets
the dev server forward requests through a corporate proxy when required.

**Behavior:**

- **When `HTTP_PROXY` is set:** Logs `"Using proxy server: <url>"` and configures the agent for corporate proxy forwarding
- **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

If you need to, you can further customize this function to fit the network of your working environment.
See `proxy.conf.js` for the exact implementation.

If your corporate proxy use a custom SSL certificate, your may need to add the `secure: false` option to your
backend proxy configuration.
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.
31 changes: 26 additions & 5 deletions proxy.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,41 @@

const { HttpsProxyAgent } = require('https-proxy-agent');

/*
/* IMPORTANT:
* API proxy configuration.
* 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.
* For more details and options, see https://github.com/angular/angular-cli#proxy-to-backend
*/
const proxyConfig = [
{
context: '/api',
pathRewrite: { '^/api': '' },
target: 'https://api.chucknorris.io',
context: ['/fineract-provider'],
target: 'https://sandbox.mifos.community',
pathRewrite: { '^/fineract-provider': '' },
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` .
];

/*
Expand Down
37 changes: 37 additions & 0 deletions proxy.localhost.conf.js
Original file line number Diff line number Diff line change
@@ -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'));
}
}
}
];
Loading