openvibers/openvibe.search Commits
nginx: the Search Fabric's /api/v1/query and /api/v1/query/providers reach the app (GET or POST, the query zone's rate, never cached) (#120)
Changes
@@@ -110,6 +110,17 @@ server {proxy_cache_bypass 1;}++ # ── The Search Fabric (search.fabric.query): GET or POST /api/v1/query, GET /api/v1/query/providers. A step may reach++ # a paid provider, so the query shares the query zone's rate and Search adds its own per-actor limit (30/min).++ location ~ ^/api/v1/query(/providers)?$ {++ limit_except GET POST { deny all; }++ limit_req zone=ovsearch_query burst=20 nodelay;++ client_max_body_size 8k;++ proxy_pass http://127.0.0.1:4710;++ proxy_no_cache 1;++ proxy_cache_bypass 1;++ }++# One document by id: GET is public (ACL-checked by Search); PUT/DELETE are owner writes,# loopback only.location ^~ /api/v1/documents/ {@@@ -39,6 +39,10 @@ t('the query API is GET-only and never cached by nginx; unknown paths get a JSONconst q = block('location ~ ^/api/v1/(search|suggest)$');assert.match(q, /limit_except GET \{ deny all; \}/);assert.match(q, /proxy_no_cache 1;/);++ const f = block('location ~ ^/api/v1/query(/providers)?$');++ assert.match(f, /limit_except GET POST \{ deny all; \}/, 'the Search Fabric: GET or POST');++ assert.match(f, /limit_req zone=ovsearch_query/);++ assert.match(f, /proxy_no_cache 1;/, 'a personalised answer is never cached by nginx');const body = /return 404 '(\{.*\})';/.exec(conf)[1];assert.strictEqual(JSON.parse(body).code, 'search.not_found');});
@@ -110,6 +110,17 @@ server {
@@ -110,6 +110,17 @@ server {
proxy_cache_bypass 1;
proxy_cache_bypass 1;
}
}
+ # ── The Search Fabric (search.fabric.query): GET or POST /api/v1/query, GET /api/v1/query/providers. A step may reach
+ # a paid provider, so the query shares the query zone's rate and Search adds its own per-actor limit (30/min).
+ location ~ ^/api/v1/query(/providers)?$ {
+ limit_except GET POST { deny all; }
+ limit_req zone=ovsearch_query burst=20 nodelay;
+ client_max_body_size 8k;
+ proxy_pass http://127.0.0.1:4710;
+ proxy_no_cache 1;
+ proxy_cache_bypass 1;
+ }
+
# One document by id: GET is public (ACL-checked by Search); PUT/DELETE are owner writes,
# One document by id: GET is public (ACL-checked by Search); PUT/DELETE are owner writes,
# loopback only.
# loopback only.
location ^~ /api/v1/documents/ {
location ^~ /api/v1/documents/ {
@@ -39,6 +39,10 @@ t('the query API is GET-only and never cached by nginx; unknown paths get a JSON
@@ -39,6 +39,10 @@ t('the query API is GET-only and never cached by nginx; unknown paths get a JSON
const q = block('location ~ ^/api/v1/(search|suggest)$');
const q = block('location ~ ^/api/v1/(search|suggest)$');
assert.match(q, /limit_except GET \{ deny all; \}/);
assert.match(q, /limit_except GET \{ deny all; \}/);
assert.match(q, /proxy_no_cache 1;/);
assert.match(q, /proxy_no_cache 1;/);
+ const f = block('location ~ ^/api/v1/query(/providers)?$');
+ assert.match(f, /limit_except GET POST \{ deny all; \}/, 'the Search Fabric: GET or POST');
+ assert.match(f, /limit_req zone=ovsearch_query/);
+ assert.match(f, /proxy_no_cache 1;/, 'a personalised answer is never cached by nginx');
const body = /return 404 '(\{.*\})';/.exec(conf)[1];
const body = /return 404 '(\{.*\})';/.exec(conf)[1];
assert.strictEqual(JSON.parse(body).code, 'search.not_found');
assert.strictEqual(JSON.parse(body).code, 'search.not_found');
});
});