feat(core): auto detect proxy server on load (#3195)
* feat: auto detect proxy server on load * fix: opt-in for auto proxy server detection
This commit is contained in:
parent
2043c0b782
commit
614f1aea63
@ -1,11 +1,15 @@
|
||||
backend:
|
||||
name: proxy
|
||||
name: github
|
||||
branch: master
|
||||
proxy_url: http://localhost:8082/api/v1
|
||||
repo: owner/repo
|
||||
|
||||
publish_mode: editorial_workflow
|
||||
media_folder: static/media
|
||||
public_folder: /media
|
||||
|
||||
local_backend:
|
||||
url: http://localhost:8082/api/v1
|
||||
|
||||
collections:
|
||||
- name: posts
|
||||
label: Posts
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { fromJS } from 'immutable';
|
||||
import { applyDefaults } from '../config';
|
||||
import { applyDefaults, detectProxyServer } from '../config';
|
||||
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
|
||||
describe('config', () => {
|
||||
describe('applyDefaults', () => {
|
||||
@ -168,4 +170,66 @@ describe('config', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectProxyServer', () => {
|
||||
const assetFetchCalled = (url = 'http://localhost:8081/api/v1') => {
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
||||
expect(global.fetch).toHaveBeenCalledWith(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action: 'info' }),
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
delete window.location;
|
||||
});
|
||||
|
||||
it('should return undefined when not on localhost', async () => {
|
||||
window.location = { hostname: 'www.netlify.com' };
|
||||
global.fetch = jest.fn();
|
||||
await expect(detectProxyServer()).resolves.toBeUndefined();
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should return undefined when fetch returns an error', async () => {
|
||||
window.location = { hostname: 'localhost' };
|
||||
global.fetch = jest.fn().mockRejectedValue(new Error());
|
||||
await expect(detectProxyServer(true)).resolves.toBeUndefined();
|
||||
|
||||
assetFetchCalled();
|
||||
});
|
||||
|
||||
it('should return undefined when fetch returns an invalid response', async () => {
|
||||
window.location = { hostname: 'localhost' };
|
||||
global.fetch = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ json: jest.fn().mockResolvedValue({ repo: [] }) });
|
||||
await expect(detectProxyServer(true)).resolves.toBeUndefined();
|
||||
|
||||
assetFetchCalled();
|
||||
});
|
||||
|
||||
it('should return proxyUrl when fetch returns a valid response', async () => {
|
||||
window.location = { hostname: 'localhost' };
|
||||
global.fetch = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ json: jest.fn().mockResolvedValue({ repo: 'test-repo' }) });
|
||||
await expect(detectProxyServer(true)).resolves.toBe('http://localhost:8081/api/v1');
|
||||
|
||||
assetFetchCalled();
|
||||
});
|
||||
|
||||
it('should use local_backend url', async () => {
|
||||
const url = 'http://localhost:8082/api/v1';
|
||||
window.location = { hostname: 'localhost' };
|
||||
global.fetch = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ json: jest.fn().mockResolvedValue({ repo: 'test-repo' }) });
|
||||
await expect(detectProxyServer({ url })).resolves.toBe(url);
|
||||
|
||||
assetFetchCalled(url);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import yaml from 'js-yaml';
|
||||
import { Map, fromJS } from 'immutable';
|
||||
import { trimStart, get } from 'lodash';
|
||||
import { trimStart, get, isPlainObject } from 'lodash';
|
||||
import { authenticateUser } from 'Actions/auth';
|
||||
import * as publishModes from 'Constants/publishModes';
|
||||
import { validateConfig } from 'Constants/configSchema';
|
||||
@ -145,6 +145,31 @@ export function mergeConfig(config) {
|
||||
return { type: CONFIG_MERGE, payload: config };
|
||||
}
|
||||
|
||||
export async function detectProxyServer(localBackend) {
|
||||
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
|
||||
let proxyUrl;
|
||||
if (localBackend === true) {
|
||||
proxyUrl = 'http://localhost:8081/api/v1';
|
||||
} else if (isPlainObject(localBackend)) {
|
||||
proxyUrl = localBackend.url;
|
||||
}
|
||||
try {
|
||||
console.log(`Looking for Netlify CMS Proxy Server at '${proxyUrl}'`);
|
||||
const { repo } = await fetch(`${proxyUrl}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action: 'info' }),
|
||||
}).then(res => res.json());
|
||||
if (typeof repo === 'string') {
|
||||
console.log(`Detected Netlify CMS Proxy Server at '${proxyUrl}' with repo: '${repo}'`);
|
||||
return proxyUrl;
|
||||
}
|
||||
} catch {
|
||||
console.log(`Netlify CMS Proxy Server not detected at '${proxyUrl}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function loadConfig() {
|
||||
if (window.CMS_CONFIG) {
|
||||
return configDidLoad(fromJS(window.CMS_CONFIG));
|
||||
@ -155,17 +180,29 @@ export function loadConfig() {
|
||||
try {
|
||||
const preloadedConfig = getState().config;
|
||||
const configUrl = getConfigUrl();
|
||||
const isPreloaded = preloadedConfig && preloadedConfig.size > 1;
|
||||
const loadedConfig =
|
||||
preloadedConfig && preloadedConfig.get('load_config_file') === false
|
||||
? {}
|
||||
: await getConfig(configUrl, preloadedConfig && preloadedConfig.size > 1);
|
||||
: await getConfig(configUrl, isPreloaded);
|
||||
|
||||
/**
|
||||
* Merge any existing configuration so the result can be validated.
|
||||
*/
|
||||
const mergedConfig = mergePreloadedConfig(preloadedConfig, loadedConfig);
|
||||
let mergedConfig = mergePreloadedConfig(preloadedConfig, loadedConfig);
|
||||
|
||||
validateConfig(mergedConfig.toJS());
|
||||
|
||||
// detect running Netlify CMS proxy
|
||||
if (mergedConfig.has('local_backend')) {
|
||||
const proxyUrl = await detectProxyServer(mergedConfig.toJS().local_backend);
|
||||
if (proxyUrl) {
|
||||
mergedConfig = mergePreloadedConfig(mergedConfig, {
|
||||
backend: { name: 'proxy', proxy_url: proxyUrl },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const config = applyDefaults(mergedConfig);
|
||||
|
||||
dispatch(configDidLoad(config));
|
||||
|
@ -126,5 +126,31 @@ describe('config', () => {
|
||||
});
|
||||
}).toThrowError("'collections[0]' should be object");
|
||||
});
|
||||
|
||||
it('should throw if local_backend is not a boolean or plain object', () => {
|
||||
expect(() => {
|
||||
validateConfig(merge(validConfig, { local_backend: [] }));
|
||||
}).toThrowError("'local_backend' should be boolean");
|
||||
});
|
||||
|
||||
it('should throw if local_backend is a plain object but missing url property', () => {
|
||||
expect(() => {
|
||||
validateConfig(merge(validConfig, { local_backend: {} }));
|
||||
}).toThrowError("'local_backend' should be object");
|
||||
});
|
||||
|
||||
it('should not throw if local_backend is a boolean', () => {
|
||||
expect(() => {
|
||||
validateConfig(merge(validConfig, { local_backend: true }));
|
||||
}).not.toThrowError();
|
||||
});
|
||||
|
||||
it('should not throw if local_backend is a plain object with url property', () => {
|
||||
expect(() => {
|
||||
validateConfig(
|
||||
merge(validConfig, { local_backend: { url: 'http://localhost:8081/api/v1' } }),
|
||||
);
|
||||
}).not.toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -42,6 +42,18 @@ const getConfigSchema = () => ({
|
||||
},
|
||||
required: ['name'],
|
||||
},
|
||||
local_backend: {
|
||||
oneOf: [
|
||||
{ type: 'boolean' },
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: { type: 'string', examples: ['http://localhost:8081/api/v1'] },
|
||||
},
|
||||
required: ['url'],
|
||||
},
|
||||
],
|
||||
},
|
||||
locale: { type: 'string', examples: ['en', 'fr', 'de'] },
|
||||
site_url: { type: 'string', examples: ['https://example.com'] },
|
||||
display_url: { type: 'string', examples: ['https://example.com'] },
|
||||
|
@ -14,18 +14,20 @@ You can connect Netlify CMS to a local Git repository, instead of working with a
|
||||
|
||||
1. Navigate to a local Git repository configured with the CMS.
|
||||
2. Run `npx netlify-cms-proxy-server` from the root directory of the above repository.
|
||||
3. Update your `config.yml` to connect to the server:
|
||||
3. Add the `local_backend` configuration to your `config.yml`:
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
name: proxy
|
||||
proxy_url: http://localhost:8081/api/v1
|
||||
branch: master # optional, defaults to master
|
||||
# when using the default proxy server port
|
||||
local_backend: true
|
||||
|
||||
# when using a custom proxy server port
|
||||
local_backend:
|
||||
url: http://localhost:8082/api/v1
|
||||
```
|
||||
|
||||
4. Start you local development server (e.g. run `gatsby develop`).
|
||||
4. Start your local development server (e.g. run `gatsby develop`).
|
||||
|
||||
> `netlify-cms-proxy-server` runs an unauthenticated express server. As any client can send requests to the server, it should only be used for local development.
|
||||
**Note:** `netlify-cms-proxy-server` runs an unauthenticated express server. As any client can send requests to the server, it should only be used for local development.
|
||||
|
||||
## GitLab and BitBucket Editorial Workflow Support
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user