@mountainpass/addressr 2.1.3 → 2.1.5
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/README.md +72 -10
- package/lib/src/proxy-auth.js +53 -0
- package/lib/src/waycharter-server.js +4 -1
- package/lib/version.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
# Addressr
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**The only open-source, free self-hosted Australian address validation API.**
|
|
6
6
|
|
|
7
|
-
[
|
|
7
|
+
[Australian Address Validation, Search and Autocomplete](https://addressr.io) — [addressr.io](https://addressr.io)
|
|
8
8
|
|
|
9
|
-
[](https://github.com/mountain-pass/addressr/blob/master/LICENSE) [](https://www.npmjs.com/package/@mountainpass/addressr) [](https://www.npmjs.com/package/@mountainpass/addressr)
|
|
10
10
|
|
|
11
11
|
[](https://github.com/mountain-pass/addressr/issues) [](https://github.com/mountain-pass/addressr/pulls)
|
|
12
12
|
|
|
13
13
|
# About
|
|
14
14
|
|
|
15
|
-
Australian Address Validation, Search and Autocomplete powered by the Geocoded National Address File (G-NAF), Australia
|
|
15
|
+
Australian Address Validation, Search and Autocomplete powered by the Geocoded National Address File (G-NAF), Australia's **authoritative** address database with 15+ million addresses.
|
|
16
|
+
|
|
17
|
+
- **Validated addresses** from the official G-NAF source
|
|
18
|
+
- **Real-time autocomplete** with fuzzy matching
|
|
19
|
+
- **Locality, postcode, and state search** for area pickers
|
|
20
|
+
- **Geocoding** to latitude/longitude (optional)
|
|
21
|
+
- **Self-hosted or SaaS** — your choice, your data
|
|
22
|
+
- **Open source** — audit the code, customize as needed
|
|
23
|
+
|
|
24
|
+
# Why Addressr
|
|
25
|
+
|
|
26
|
+
| | Addressr | [Addressify](https://addressify.com.au/) | [AddressFinder](https://addressfinder.com.au/) | [Geoscape](https://geoscape.com.au/) | Google Maps |
|
|
27
|
+
| ---------------------------------- | -------- | ---------------------------------------- | ---------------------------------------------- | ------------------------------------ | ----------- |
|
|
28
|
+
| Self-hosted | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
29
|
+
| Open source | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
30
|
+
| Free tier (unlimited, self-hosted) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
31
|
+
| G-NAF data source | ✅ | ✅ | ✅ | ✅ (creator) | ❌ |
|
|
32
|
+
| Data sovereignty | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
33
|
+
| MCP integration for AI assistants | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
34
|
+
|
|
35
|
+
**Stop paying Google Maps for Australian addresses.** Stop locking your data into third-party SaaS. Addressr gives you unlimited address validation on your own infrastructure, or a cheap hosted API if you prefer.
|
|
16
36
|
|
|
17
37
|
# Quick Start
|
|
18
38
|
|
|
@@ -85,15 +105,15 @@ Run Addressr on your own infrastructure for full control over your data.
|
|
|
85
105
|
export ADDRESSR_INDEX_BACKOFF_MAX=10000
|
|
86
106
|
```
|
|
87
107
|
|
|
88
|
-
1. Optional
|
|
89
|
-
**NOTE:** with geocodes enabled, indexing takes much longer and needs much more memory. Only
|
|
108
|
+
1. Optional — enable geocodes by setting the following env vars for the data loader. In the third window run:
|
|
109
|
+
**NOTE:** with geocodes enabled, indexing takes much longer and needs much more memory. Only turn them on if you need them. You can always add them later.
|
|
90
110
|
|
|
91
111
|
```
|
|
92
112
|
export ADDRESSR_ENABLE_GEO=1
|
|
93
113
|
export NODE_OPTIONS=--max_old_space_size=8196
|
|
94
114
|
```
|
|
95
115
|
|
|
96
|
-
2. Optional
|
|
116
|
+
2. Optional — limit the addresses to a single state by setting the `COVERED_STATES` env var for the data loader.
|
|
97
117
|
This dramatically speeds up indexing. For example, in the third window run:
|
|
98
118
|
|
|
99
119
|
```
|
|
@@ -117,7 +137,8 @@ Run Addressr on your own infrastructure for full control over your data.
|
|
|
117
137
|
addressr-loader
|
|
118
138
|
```
|
|
119
139
|
|
|
120
|
-
6. OK, so we stretched the truth a bit with the "Quick Start" heading. The truth is that it takes quite a while to download, store and index the
|
|
140
|
+
6. OK, so we stretched the truth a bit with the "Quick Start" heading. The truth is that it takes quite a while to download, store and index the 15+ million addresses from [data.gov.au](http://data.gov.au/). So make a coffee, or tea, or find something else to do and come back in about an hour when it's done.
|
|
141
|
+
|
|
121
142
|
7. Search for an address using the command line
|
|
122
143
|
|
|
123
144
|
```
|
|
@@ -125,7 +146,24 @@ Run Addressr on your own infrastructure for full control over your data.
|
|
|
125
146
|
```
|
|
126
147
|
|
|
127
148
|
8. An updated G-NAF is released every 3 months. Put `addressr-loader` in a cron job or similar to keep addressr regularly updated
|
|
128
|
-
9. Wire
|
|
149
|
+
9. Wire your address form up to the address-server api.
|
|
150
|
+
|
|
151
|
+
# API Endpoints
|
|
152
|
+
|
|
153
|
+
Addressr exposes a HATEOAS REST API. Start at the root (`/`) and follow links to discover endpoints. A supplementary OpenAPI 3.x spec is available at `/api-docs`.
|
|
154
|
+
|
|
155
|
+
| Endpoint | Purpose | Example |
|
|
156
|
+
| ---------------------------- | ------------------------------------------------------------------ | --------------------------------- |
|
|
157
|
+
| `GET /addresses?q=` | Search and autocomplete addresses | `/addresses?q=1+george+st+sydney` |
|
|
158
|
+
| `GET /addresses/{pid}` | Get full address details (with links to locality, postcode, state) | `/addresses/GAOT_717882967` |
|
|
159
|
+
| `GET /localities?q=` | Search suburbs/localities by name | `/localities?q=lilydale` |
|
|
160
|
+
| `GET /localities/{pid}` | Get locality details (with links to postcode, state) | `/localities/loc9984d8beb142` |
|
|
161
|
+
| `GET /postcodes?q=` | Search postcodes (q optional) | `/postcodes?q=3140` |
|
|
162
|
+
| `GET /postcodes/{postcode}` | Get postcode with associated localities | `/postcodes/6798` |
|
|
163
|
+
| `GET /states?q=` | Search states/territories (q optional) | `/states?q=New` |
|
|
164
|
+
| `GET /states/{abbreviation}` | Get state details | `/states/NSW` |
|
|
165
|
+
| `GET /api-docs` | OpenAPI 3.x specification | `/api-docs` |
|
|
166
|
+
| `GET /health` | Health check | `/health` |
|
|
129
167
|
|
|
130
168
|
## How it Works
|
|
131
169
|
|
|
@@ -148,6 +186,30 @@ Run Addressr on your own infrastructure for full control over your data.
|
|
|
148
186
|
| ADDRESSR_ACCESS_CONTROL_EXPOSE_HEADERS | _non-blank_ | An `Access-Control-Expose-Headers` response header is returned with the value in the environment variable | |
|
|
149
187
|
| ADDRESSR_ACCESS_CONTROL_ALLOW_HEADERS | _blank_ | An `Access-Control-Allow-Headers` response header is **not** returned | ✅ |
|
|
150
188
|
| ADDRESSR_ACCESS_CONTROL_ALLOW_HEADERS | _non-blank_ | An `Access-Control-Allow-Headers` response header is returned with the value in the environment variable | |
|
|
189
|
+
| ADDRESSR_PROXY_AUTH_HEADER | _blank_ | No gateway auth header enforcement (self-hosted default) | ✅ |
|
|
190
|
+
| ADDRESSR_PROXY_AUTH_HEADER | _non-blank_ | Name of the header the origin requires — set alongside `ADDRESSR_PROXY_AUTH_VALUE` | |
|
|
191
|
+
| ADDRESSR_PROXY_AUTH_VALUE | _blank_ | No gateway auth header enforcement (self-hosted default) | ✅ |
|
|
192
|
+
| ADDRESSR_PROXY_AUTH_VALUE | _non-blank_ | Expected value the header must carry — set alongside `ADDRESSR_PROXY_AUTH_HEADER` | |
|
|
193
|
+
|
|
194
|
+
### Gateway auth header (optional)
|
|
195
|
+
|
|
196
|
+
By default Addressr does not enforce any proxy authentication — self-hosted npm and Docker deployments work with zero configuration.
|
|
197
|
+
|
|
198
|
+
If you front Addressr with an API gateway (RapidAPI, Kong, Tyk, Apigee, AWS API Gateway, nginx, Caddy, or your own Cloudflare Worker) and want the origin to reject traffic that bypasses your gateway, set both environment variables to the header name your gateway injects and the shared secret it forwards:
|
|
199
|
+
|
|
200
|
+
| Variable | Example | Notes |
|
|
201
|
+
| ---------------------------- | ------------------------- | ------------------------------------------- |
|
|
202
|
+
| `ADDRESSR_PROXY_AUTH_HEADER` | `X-RapidAPI-Proxy-Secret` | Header name your gateway forwards |
|
|
203
|
+
| `ADDRESSR_PROXY_AUTH_VALUE` | `<your-gateway-secret>` | Expected value; keep out of version control |
|
|
204
|
+
|
|
205
|
+
Behaviour:
|
|
206
|
+
|
|
207
|
+
- Both unset → no enforcement (default).
|
|
208
|
+
- Both set → requests without a matching header receive `401 Authentication required`.
|
|
209
|
+
- Exactly one set → the process exits at startup with a clear error (fails loud to prevent silent bypass).
|
|
210
|
+
- `/health` and `/api-docs` remain reachable without the header so uptime monitors and gateway OpenAPI imports keep working.
|
|
211
|
+
|
|
212
|
+
See [ADR 024](docs/decisions/024-origin-gateway-auth-header-enforcement.proposed.md) for the decision record.
|
|
151
213
|
|
|
152
214
|
NOTE: When adjusting PAGE_SIZE, you should take into account how quickly you want the initial results returned to the user. In many use cases, you want this to be as fast as possible. If you need show more results to the user, you are often better off leaving it a 8 and using the paging links to get more results while you are displaying the first 8.
|
|
153
215
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.proxyAuthMiddleware = proxyAuthMiddleware;
|
|
7
|
+
exports.validateProxyAuthConfig = validateProxyAuthConfig;
|
|
8
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
9
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
/* eslint-disable @eslint-community/eslint-comments/disable-enable-pair */
|
|
11
|
+
/* eslint-disable security/detect-object-injection -- env var names are compile-time constants, not user input */
|
|
12
|
+
|
|
13
|
+
const error = (0, _debug.default)('error');
|
|
14
|
+
error.log = console.error.bind(console);
|
|
15
|
+
const HEADER_VAR = 'ADDRESSR_PROXY_AUTH_HEADER';
|
|
16
|
+
const VALUE_VAR = 'ADDRESSR_PROXY_AUTH_VALUE';
|
|
17
|
+
|
|
18
|
+
// Closed list per ADR 024: /health for monitoring, /api-docs for gateway
|
|
19
|
+
// OpenAPI imports (ADR 023). Exact-match, not prefix.
|
|
20
|
+
const ALLOWLIST = new Set(['/health', '/api-docs']);
|
|
21
|
+
function isNonEmpty(value) {
|
|
22
|
+
return typeof value === 'string' && value.length > 0;
|
|
23
|
+
}
|
|
24
|
+
function validateProxyAuthConfig(environment = process.env) {
|
|
25
|
+
const headerSet = isNonEmpty(environment[HEADER_VAR]);
|
|
26
|
+
const valueSet = isNonEmpty(environment[VALUE_VAR]);
|
|
27
|
+
if (headerSet && !valueSet) {
|
|
28
|
+
throw new Error(`Proxy auth misconfigured: ${HEADER_VAR} is set but ${VALUE_VAR} is missing. Set both to enforce a gateway auth header, or unset both to disable enforcement.`);
|
|
29
|
+
}
|
|
30
|
+
if (valueSet && !headerSet) {
|
|
31
|
+
throw new Error(`Proxy auth misconfigured: ${VALUE_VAR} is set but ${HEADER_VAR} is missing. Set both to enforce a gateway auth header, or unset both to disable enforcement.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function proxyAuthMiddleware() {
|
|
35
|
+
return function proxyAuth(request, response, next) {
|
|
36
|
+
const headerName = process.env[HEADER_VAR];
|
|
37
|
+
const expected = process.env[VALUE_VAR];
|
|
38
|
+
if (!isNonEmpty(headerName) || !isNonEmpty(expected)) {
|
|
39
|
+
return next();
|
|
40
|
+
}
|
|
41
|
+
if (ALLOWLIST.has(request.path)) {
|
|
42
|
+
return next();
|
|
43
|
+
}
|
|
44
|
+
const presented = request.get ? request.get(headerName) : undefined;
|
|
45
|
+
if (presented === expected) {
|
|
46
|
+
return next();
|
|
47
|
+
}
|
|
48
|
+
error('proxy-auth rejected %s', request.path);
|
|
49
|
+
return response.status(401).json({
|
|
50
|
+
message: 'Authentication required'
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -12,6 +12,7 @@ var _waycharter = require("@mountainpass/waycharter");
|
|
|
12
12
|
var _addressService = require("../service/address-service");
|
|
13
13
|
var _version = require("../version");
|
|
14
14
|
var _nodeCrypto = _interopRequireDefault(require("node:crypto"));
|
|
15
|
+
var _proxyAuth = require("./proxy-auth");
|
|
15
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
17
|
//import connect from 'connect';
|
|
17
18
|
|
|
@@ -335,7 +336,7 @@ function buildOpenApiSpec(apiVersion) {
|
|
|
335
336
|
openapi: '3.0.3',
|
|
336
337
|
info: {
|
|
337
338
|
title: 'Addressr by Mountain Pass',
|
|
338
|
-
description: 'Free Australian Address Validation, Search and Autocomplete. This OpenAPI spec is supplementary — the HATEOAS link-driven API is the authoritative contract. Follow `related` Link headers to navigate between addresses, localities, postcodes and states.',
|
|
339
|
+
description: 'Free Australian Address Validation, Search and Autocomplete. This OpenAPI spec is supplementary — the HATEOAS link-driven API is the authoritative contract. Follow `related` Link headers to navigate between addresses, localities, postcodes and states.\n\nDirect requests to upstream hosts may be rejected when the operator has configured a gateway auth header. Consumers should always call Addressr through its published gateway endpoint; monitoring (`/health`) and spec discovery (`/api-docs`) remain openly reachable.',
|
|
339
340
|
version: apiVersion
|
|
340
341
|
},
|
|
341
342
|
servers: [{
|
|
@@ -657,6 +658,7 @@ error.log = console.error.bind(console);
|
|
|
657
658
|
let server;
|
|
658
659
|
const PAGE_SIZE = process.env.PAGE_SIZE || 8;
|
|
659
660
|
function startRest2Server() {
|
|
661
|
+
(0, _proxyAuth.validateProxyAuthConfig)();
|
|
660
662
|
app.use((_request, response, next) => {
|
|
661
663
|
if (process.env.ADDRESSR_ACCESS_CONTROL_ALLOW_ORIGIN !== undefined) {
|
|
662
664
|
response.append('Access-Control-Allow-Origin', process.env.ADDRESSR_ACCESS_CONTROL_ALLOW_ORIGIN);
|
|
@@ -669,6 +671,7 @@ function startRest2Server() {
|
|
|
669
671
|
}
|
|
670
672
|
next();
|
|
671
673
|
});
|
|
674
|
+
app.use((0, _proxyAuth.proxyAuthMiddleware)());
|
|
672
675
|
const waycharter = new _waycharter.WayCharter();
|
|
673
676
|
app.use(waycharter.router);
|
|
674
677
|
const addressesType = waycharter.registerCollection({
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mountainpass/addressr",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Australian Address Validation, Search and Autocomplete",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mountain Pass",
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
"dry-aged-deps": "^2.6.0",
|
|
208
208
|
"eslint": "^9.39.4",
|
|
209
209
|
"eslint-config-prettier": "^10.1.8",
|
|
210
|
-
"eslint-plugin-chai-friendly": "^1.
|
|
210
|
+
"eslint-plugin-chai-friendly": "^1.2.0",
|
|
211
211
|
"eslint-plugin-import-x": "^4.16.2",
|
|
212
212
|
"eslint-plugin-n": "^17.24.0",
|
|
213
213
|
"eslint-plugin-prettier": "^5.5.5",
|