@qelos/plugin-netlify-api 1.0.1 → 1.0.3
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 +24 -5
- package/functions/qelos-api-proxy.ts +29 -8
- package/index.js +30 -11
- package/manifest.yml +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,14 +4,29 @@ Netlify build plugin for Qelos frontend apps: proxies `/api/*` to your Qelos API
|
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
7
|
-
In your site’s `netlify.toml
|
|
7
|
+
In your site’s `netlify.toml`, add the plugin next to your existing `[build]` / `[build.environment]` (same idea as a manual `[functions]` + `[[redirects]]` + `api-proxy.ts`, but without checking in the function or redirect yourself):
|
|
8
8
|
|
|
9
9
|
```toml
|
|
10
|
+
[build]
|
|
11
|
+
command = "npm run build"
|
|
12
|
+
publish = "dist"
|
|
13
|
+
|
|
14
|
+
[build.environment]
|
|
15
|
+
# Used by the injected proxy at runtime (and by your build if you read it in app config)
|
|
16
|
+
QELOS_API_IP = "http://159.203.152.168"
|
|
17
|
+
|
|
10
18
|
[[plugins]]
|
|
11
19
|
package = "@qelos/plugin-netlify-api"
|
|
12
20
|
```
|
|
13
21
|
|
|
14
|
-
|
|
22
|
+
Minimal variant (defaults API URL inside the plugin if you omit env):
|
|
23
|
+
|
|
24
|
+
```toml
|
|
25
|
+
[[plugins]]
|
|
26
|
+
package = "@qelos/plugin-netlify-api"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Optional: set the API URL (default `http://159.203.152.168`) and/or enable bypass-admin header:
|
|
15
30
|
|
|
16
31
|
```toml
|
|
17
32
|
[[plugins]]
|
|
@@ -19,14 +34,18 @@ Optional: set the API URL (default `http://159.203.152.168`):
|
|
|
19
34
|
|
|
20
35
|
[plugins.inputs]
|
|
21
36
|
api_url = "http://159.203.152.168"
|
|
37
|
+
bypass_admin = true
|
|
22
38
|
```
|
|
23
39
|
|
|
24
|
-
|
|
40
|
+
- **api_url** — Target for the `/api/*` proxy. You can also set **`QELOS_API_IP`** or **`API_HOST`** in `[build.environment]` or in the Netlify UI (same role as `API_HOST` in a hand-written `netlify/functions/api-proxy.ts`).
|
|
41
|
+
- **bypass_admin** — If `true`, the proxy adds the header `x-bypass-admin: true` to every request so that admin users are scoped as regular users. See root docs: `documentation/docs/plugins/api-proxy.md` and `documentation/docs/sdk/token_refresh.md`.
|
|
25
42
|
|
|
26
43
|
## What it does
|
|
27
44
|
|
|
28
45
|
- **Environment:** Sets `QELOS_API_IP` for the build (and for the proxy function at runtime).
|
|
29
46
|
- **Redirect:** Adds a rewrite so `/api/*` is served by `/.netlify/functions/qelos-api-proxy` (status 200, force).
|
|
30
|
-
- **Function:**
|
|
47
|
+
- **Function:** Copies `qelos-api-proxy.ts` from the package into your Netlify **functions directory** (default `netlify/functions`, or `[functions] directory` in `netlify.toml`) during `onPreBuild`, so the bundler sees a normal function file. Forwards requests using `QELOS_API_IP` / `API_HOST` (full URL or hostname).
|
|
48
|
+
|
|
49
|
+
No need to add redirects or commit the proxy yourself; the plugin writes it each build. Optional: add `netlify/functions/qelos-api-proxy.ts` to `.gitignore` if you run local Netlify builds and do not want the copy tracked.
|
|
31
50
|
|
|
32
|
-
|
|
51
|
+
**Parity with a manual Netlify function** (e.g. `[[redirects]]` → `/.netlify/functions/api-proxy` and a function using `process.env.API_HOST`): use this plugin instead of those entries; set `QELOS_API_IP` or `API_HOST` to the same value you used for `API_HOST`, and set `bypass_admin = true` under `[plugins.inputs]` if your old proxy always sent `x-bypass-admin: true`.
|
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
import http from "http";
|
|
2
|
+
import https from "https";
|
|
2
3
|
import type { Handler } from "@netlify/functions";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
/** Same-origin proxy target; mirrors manual setups that use API_HOST (e.g. ai-words api-proxy). */
|
|
6
|
+
const raw =
|
|
7
|
+
process.env.QELOS_API_IP ?? process.env.API_HOST ?? "159.203.152.168";
|
|
8
|
+
|
|
9
|
+
let PROXY_HOST: string;
|
|
10
|
+
let PROXY_PORT: number;
|
|
11
|
+
let requestImpl: typeof http.request;
|
|
12
|
+
|
|
13
|
+
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
14
|
+
const u = new URL(raw);
|
|
15
|
+
PROXY_HOST = u.hostname;
|
|
16
|
+
PROXY_PORT = u.port
|
|
17
|
+
? parseInt(u.port, 10)
|
|
18
|
+
: u.protocol === "https:"
|
|
19
|
+
? 443
|
|
20
|
+
: 80;
|
|
21
|
+
requestImpl = u.protocol === "https:" ? https.request : http.request;
|
|
22
|
+
} else {
|
|
23
|
+
PROXY_HOST = raw;
|
|
24
|
+
PROXY_PORT = 80;
|
|
25
|
+
requestImpl = http.request;
|
|
26
|
+
}
|
|
27
|
+
const ADD_BYPASS_ADMIN_HEADER =
|
|
28
|
+
process.env.QELOS_BYPASS_ADMIN_HEADER === "true";
|
|
11
29
|
|
|
12
30
|
export const handler: Handler = (event) =>
|
|
13
31
|
new Promise((resolve) => {
|
|
@@ -17,6 +35,9 @@ export const handler: Handler = (event) =>
|
|
|
17
35
|
if (val) headers[key] = val;
|
|
18
36
|
}
|
|
19
37
|
headers["host"] = event.headers["host"] ?? PROXY_HOST;
|
|
38
|
+
if (ADD_BYPASS_ADMIN_HEADER) {
|
|
39
|
+
headers["x-bypass-admin"] = "true";
|
|
40
|
+
}
|
|
20
41
|
// Lambda buffers the body, so chunked encoding is not applicable
|
|
21
42
|
delete headers["transfer-encoding"];
|
|
22
43
|
|
|
@@ -28,7 +49,7 @@ export const handler: Handler = (event) =>
|
|
|
28
49
|
? Buffer.from(event.body, event.isBase64Encoded ? "base64" : "utf8")
|
|
29
50
|
: undefined;
|
|
30
51
|
|
|
31
|
-
const proxyReq =
|
|
52
|
+
const proxyReq = requestImpl(
|
|
32
53
|
{
|
|
33
54
|
hostname: PROXY_HOST,
|
|
34
55
|
port: PROXY_PORT,
|
package/index.js
CHANGED
|
@@ -1,31 +1,50 @@
|
|
|
1
|
-
const
|
|
1
|
+
const fs = require('node:fs/promises');
|
|
2
|
+
const path = require('node:path');
|
|
2
3
|
|
|
3
|
-
const DEFAULT_API_URL =
|
|
4
|
+
const DEFAULT_API_URL = 'http://159.203.152.168';
|
|
5
|
+
|
|
6
|
+
function resolveFunctionsDirectory(netlifyConfig) {
|
|
7
|
+
const dir = netlifyConfig.functions?.directory ?? 'netlify/functions';
|
|
8
|
+
return path.isAbsolute(dir) ? dir : path.join(process.cwd(), dir);
|
|
9
|
+
}
|
|
4
10
|
|
|
5
11
|
module.exports = {
|
|
6
|
-
async onPreBuild({ netlifyConfig,
|
|
7
|
-
const apiUrl =
|
|
12
|
+
async onPreBuild({ netlifyConfig, inputs }) {
|
|
13
|
+
const apiUrl =
|
|
14
|
+
inputs.api_url ?? process.env.QELOS_API_IP ?? DEFAULT_API_URL;
|
|
15
|
+
const bypassAdmin =
|
|
16
|
+
inputs.bypass_admin === true || inputs.bypass_admin === 'true';
|
|
8
17
|
|
|
9
18
|
// Ensure build.environment exists
|
|
10
19
|
if (!netlifyConfig.build.environment) {
|
|
11
20
|
netlifyConfig.build.environment = {};
|
|
12
21
|
}
|
|
13
22
|
netlifyConfig.build.environment.QELOS_API_IP = apiUrl;
|
|
23
|
+
if (bypassAdmin) {
|
|
24
|
+
netlifyConfig.build.environment.QELOS_BYPASS_ADMIN_HEADER = 'true';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!netlifyConfig.functions) {
|
|
28
|
+
netlifyConfig.functions = { directory: 'netlify/functions' };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Copy the proxy into the site's functions dir so Netlify bundles it like any
|
|
32
|
+
// checked-in function (utils.functions.add from node_modules is unreliable for .ts).
|
|
33
|
+
const destDir = resolveFunctionsDirectory(netlifyConfig);
|
|
34
|
+
const destFile = path.join(destDir, 'qelos-api-proxy.ts');
|
|
35
|
+
const srcFile = path.join(__dirname, 'functions', 'qelos-api-proxy.ts');
|
|
36
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
37
|
+
await fs.copyFile(srcFile, destFile);
|
|
14
38
|
|
|
15
39
|
// Add redirect: /api/* -> Netlify function (proxy)
|
|
16
40
|
if (!netlifyConfig.redirects) {
|
|
17
41
|
netlifyConfig.redirects = [];
|
|
18
42
|
}
|
|
19
43
|
netlifyConfig.redirects.push({
|
|
20
|
-
from:
|
|
21
|
-
to:
|
|
44
|
+
from: '/api/*',
|
|
45
|
+
to: '/.netlify/functions/qelos-api-proxy',
|
|
22
46
|
status: 200,
|
|
23
47
|
force: true,
|
|
24
48
|
});
|
|
25
|
-
|
|
26
|
-
// Inject the proxy function (must run in onPreBuild so it's bundled)
|
|
27
|
-
const pluginRoot = __dirname;
|
|
28
|
-
const functionPath = path.join(pluginRoot, "functions", "qelos-api-proxy.ts");
|
|
29
|
-
await utils.functions.add(functionPath);
|
|
30
49
|
},
|
|
31
50
|
};
|
package/manifest.yml
CHANGED
|
@@ -4,3 +4,7 @@ inputs:
|
|
|
4
4
|
required: false
|
|
5
5
|
description: "Qelos API base URL (e.g. http://159.203.152.168). Used as QELOS_API_IP and for /api/* proxy."
|
|
6
6
|
default: "http://159.203.152.168"
|
|
7
|
+
- name: bypass_admin
|
|
8
|
+
required: false
|
|
9
|
+
description: "If true, the proxy adds header x-bypass-admin: true to every request (so admins are scoped as regular users)."
|
|
10
|
+
default: false
|