@pranaysahith/decap-cms-core 3.9.1
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 +9 -0
- package/dist/@pranaysahith/decap-cms-core.js +52 -0
- package/dist/@pranaysahith/decap-cms-core.js.LICENSE.txt +141 -0
- package/dist/@pranaysahith/decap-cms-core.js.map +1 -0
- package/dist/decap-cms-core.js +47 -0
- package/dist/decap-cms-core.js.LICENSE.txt +116 -0
- package/dist/decap-cms-core.js.map +1 -0
- package/dist/esm/actions/auth.js +97 -0
- package/dist/esm/actions/collections.js +15 -0
- package/dist/esm/actions/config.js +493 -0
- package/dist/esm/actions/deploys.js +79 -0
- package/dist/esm/actions/editorialWorkflow.js +480 -0
- package/dist/esm/actions/entries.js +865 -0
- package/dist/esm/actions/media.js +147 -0
- package/dist/esm/actions/mediaLibrary.js +552 -0
- package/dist/esm/actions/notifications.js +21 -0
- package/dist/esm/actions/search.js +149 -0
- package/dist/esm/actions/status.js +74 -0
- package/dist/esm/actions/waitUntil.js +32 -0
- package/dist/esm/backend.js +1082 -0
- package/dist/esm/bootstrap.js +101 -0
- package/dist/esm/components/App/App.js +289 -0
- package/dist/esm/components/App/Header.js +172 -0
- package/dist/esm/components/App/NotFoundPage.js +19 -0
- package/dist/esm/components/Collection/Collection.js +198 -0
- package/dist/esm/components/Collection/CollectionControls.js +46 -0
- package/dist/esm/components/Collection/CollectionSearch.js +222 -0
- package/dist/esm/components/Collection/CollectionTop.js +68 -0
- package/dist/esm/components/Collection/ControlButton.js +17 -0
- package/dist/esm/components/Collection/Entries/Entries.js +73 -0
- package/dist/esm/components/Collection/Entries/EntriesCollection.js +241 -0
- package/dist/esm/components/Collection/Entries/EntriesSearch.js +113 -0
- package/dist/esm/components/Collection/Entries/EntryCard.js +177 -0
- package/dist/esm/components/Collection/Entries/EntryListing.js +143 -0
- package/dist/esm/components/Collection/FilterControl.js +33 -0
- package/dist/esm/components/Collection/FolderRenameControl.js +403 -0
- package/dist/esm/components/Collection/GroupControl.js +33 -0
- package/dist/esm/components/Collection/NestedCollection.js +308 -0
- package/dist/esm/components/Collection/Sidebar.js +91 -0
- package/dist/esm/components/Collection/SortControl.js +59 -0
- package/dist/esm/components/Collection/ViewStyleControl.js +38 -0
- package/dist/esm/components/Editor/Editor.js +466 -0
- package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +395 -0
- package/dist/esm/components/Editor/EditorControlPane/EditorControlPane.js +254 -0
- package/dist/esm/components/Editor/EditorControlPane/Widget.js +374 -0
- package/dist/esm/components/Editor/EditorInterface.js +386 -0
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreview.js +47 -0
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewContent.js +66 -0
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewPane.js +288 -0
- package/dist/esm/components/Editor/EditorPreviewPane/PreviewHOC.js +27 -0
- package/dist/esm/components/Editor/EditorToolbar.js +536 -0
- package/dist/esm/components/Editor/EntryPathEditor.js +272 -0
- package/dist/esm/components/Editor/withWorkflow.js +56 -0
- package/dist/esm/components/EditorWidgets/Unknown/UnknownControl.js +18 -0
- package/dist/esm/components/EditorWidgets/Unknown/UnknownPreview.js +20 -0
- package/dist/esm/components/EditorWidgets/index.js +4 -0
- package/dist/esm/components/MediaLibrary/EmptyMessage.js +22 -0
- package/dist/esm/components/MediaLibrary/MediaLibrary.js +446 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryButtons.js +93 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryCard.js +99 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryCardGrid.js +198 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryHeader.js +32 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryModal.js +156 -0
- package/dist/esm/components/MediaLibrary/MediaLibrarySearch.js +51 -0
- package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +123 -0
- package/dist/esm/components/UI/DragDrop.js +67 -0
- package/dist/esm/components/UI/ErrorBoundary.js +173 -0
- package/dist/esm/components/UI/FileUploadButton.js +27 -0
- package/dist/esm/components/UI/Modal.js +104 -0
- package/dist/esm/components/UI/Notifications.js +62 -0
- package/dist/esm/components/UI/SettingsDropdown.js +107 -0
- package/dist/esm/components/UI/index.js +6 -0
- package/dist/esm/components/Workflow/Workflow.js +133 -0
- package/dist/esm/components/Workflow/WorkflowCard.js +128 -0
- package/dist/esm/components/Workflow/WorkflowList.js +204 -0
- package/dist/esm/constants/collectionTypes.js +2 -0
- package/dist/esm/constants/collectionViews.js +2 -0
- package/dist/esm/constants/commitProps.js +2 -0
- package/dist/esm/constants/configSchema.js +644 -0
- package/dist/esm/constants/fieldInference.js +57 -0
- package/dist/esm/constants/publishModes.js +18 -0
- package/dist/esm/constants/validationErrorTypes.js +6 -0
- package/dist/esm/formats/formats.js +83 -0
- package/dist/esm/formats/frontmatter.js +146 -0
- package/dist/esm/formats/helpers.js +12 -0
- package/dist/esm/formats/json.js +8 -0
- package/dist/esm/formats/toml.js +32 -0
- package/dist/esm/formats/yaml.js +51 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/integrations/index.js +28 -0
- package/dist/esm/integrations/providers/algolia/implementation.js +174 -0
- package/dist/esm/integrations/providers/assetStore/implementation.js +165 -0
- package/dist/esm/lib/consoleError.js +3 -0
- package/dist/esm/lib/formatters.js +191 -0
- package/dist/esm/lib/i18n.js +367 -0
- package/dist/esm/lib/phrases.js +6 -0
- package/dist/esm/lib/polyfill.js +8 -0
- package/dist/esm/lib/registry.js +329 -0
- package/dist/esm/lib/serializeEntryValues.js +67 -0
- package/dist/esm/lib/stega.js +142 -0
- package/dist/esm/lib/textHelper.js +9 -0
- package/dist/esm/lib/urlHelper.js +111 -0
- package/dist/esm/mediaLibrary.js +37 -0
- package/dist/esm/reducers/auth.js +27 -0
- package/dist/esm/reducers/collections.js +428 -0
- package/dist/esm/reducers/combinedReducer.js +8 -0
- package/dist/esm/reducers/config.js +29 -0
- package/dist/esm/reducers/cursors.js +31 -0
- package/dist/esm/reducers/deploys.js +45 -0
- package/dist/esm/reducers/editorialWorkflow.js +83 -0
- package/dist/esm/reducers/entries.js +568 -0
- package/dist/esm/reducers/entryDraft.js +212 -0
- package/dist/esm/reducers/globalUI.js +25 -0
- package/dist/esm/reducers/index.js +66 -0
- package/dist/esm/reducers/integrations.js +53 -0
- package/dist/esm/reducers/mediaLibrary.js +252 -0
- package/dist/esm/reducers/medias.js +68 -0
- package/dist/esm/reducers/notifications.js +23 -0
- package/dist/esm/reducers/search.js +92 -0
- package/dist/esm/reducers/status.js +30 -0
- package/dist/esm/redux/index.js +7 -0
- package/dist/esm/redux/middleware/waitUntilAction.js +48 -0
- package/dist/esm/routing/history.js +12 -0
- package/dist/esm/types/diacritics.d.js +0 -0
- package/dist/esm/types/global.d.js +1 -0
- package/dist/esm/types/immutable.js +7 -0
- package/dist/esm/types/redux.js +14 -0
- package/dist/esm/types/tomlify-j0.4.d.js +0 -0
- package/dist/esm/valueObjects/AssetProxy.js +44 -0
- package/dist/esm/valueObjects/EditorComponent.js +34 -0
- package/dist/esm/valueObjects/Entry.js +20 -0
- package/index.d.ts +618 -0
- package/package.json +106 -0
- package/src/__tests__/backend.spec.js +1161 -0
- package/src/actions/__tests__/config.spec.js +1009 -0
- package/src/actions/__tests__/editorialWorkflow.spec.js +216 -0
- package/src/actions/__tests__/entries.spec.js +596 -0
- package/src/actions/__tests__/media.spec.ts +171 -0
- package/src/actions/__tests__/mediaLibrary.spec.js +327 -0
- package/src/actions/__tests__/search.spec.js +209 -0
- package/src/actions/auth.ts +127 -0
- package/src/actions/collections.ts +18 -0
- package/src/actions/config.ts +565 -0
- package/src/actions/deploys.ts +104 -0
- package/src/actions/editorialWorkflow.ts +567 -0
- package/src/actions/entries.ts +1055 -0
- package/src/actions/media.ts +139 -0
- package/src/actions/mediaLibrary.ts +574 -0
- package/src/actions/notifications.ts +36 -0
- package/src/actions/search.ts +221 -0
- package/src/actions/status.ts +99 -0
- package/src/actions/waitUntil.ts +49 -0
- package/src/backend.ts +1400 -0
- package/src/bootstrap.js +104 -0
- package/src/components/App/App.js +286 -0
- package/src/components/App/Header.js +266 -0
- package/src/components/App/NotFoundPage.js +23 -0
- package/src/components/Collection/Collection.js +210 -0
- package/src/components/Collection/CollectionControls.js +58 -0
- package/src/components/Collection/CollectionSearch.js +243 -0
- package/src/components/Collection/CollectionTop.js +81 -0
- package/src/components/Collection/ControlButton.js +27 -0
- package/src/components/Collection/Entries/Entries.js +82 -0
- package/src/components/Collection/Entries/EntriesCollection.js +277 -0
- package/src/components/Collection/Entries/EntriesSearch.js +102 -0
- package/src/components/Collection/Entries/EntryCard.js +246 -0
- package/src/components/Collection/Entries/EntryListing.js +151 -0
- package/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +163 -0
- package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +46 -0
- package/src/components/Collection/FilterControl.js +39 -0
- package/src/components/Collection/GroupControl.js +39 -0
- package/src/components/Collection/NestedCollection.js +330 -0
- package/src/components/Collection/Sidebar.js +136 -0
- package/src/components/Collection/SortControl.js +68 -0
- package/src/components/Collection/ViewStyleControl.js +50 -0
- package/src/components/Collection/__tests__/Collection.spec.js +75 -0
- package/src/components/Collection/__tests__/NestedCollection.spec.js +445 -0
- package/src/components/Collection/__tests__/Sidebar.spec.js +87 -0
- package/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap +144 -0
- package/src/components/Collection/__tests__/__snapshots__/NestedCollection.spec.js.snap +550 -0
- package/src/components/Collection/__tests__/__snapshots__/Sidebar.spec.js.snap +312 -0
- package/src/components/Editor/Editor.js +497 -0
- package/src/components/Editor/EditorControlPane/EditorControl.js +452 -0
- package/src/components/Editor/EditorControlPane/EditorControlPane.js +269 -0
- package/src/components/Editor/EditorControlPane/Widget.js +384 -0
- package/src/components/Editor/EditorInterface.js +444 -0
- package/src/components/Editor/EditorPreviewPane/EditorPreview.js +40 -0
- package/src/components/Editor/EditorPreviewPane/EditorPreviewContent.js +74 -0
- package/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js +333 -0
- package/src/components/Editor/EditorPreviewPane/PreviewHOC.js +33 -0
- package/src/components/Editor/EditorToolbar.js +691 -0
- package/src/components/Editor/__tests__/Editor.spec.js +221 -0
- package/src/components/Editor/__tests__/EditorToolbar.spec.js +120 -0
- package/src/components/Editor/__tests__/__snapshots__/Editor.spec.js.snap +45 -0
- package/src/components/Editor/__tests__/__snapshots__/EditorToolbar.spec.js.snap +4233 -0
- package/src/components/Editor/withWorkflow.js +61 -0
- package/src/components/EditorWidgets/Unknown/UnknownControl.js +17 -0
- package/src/components/EditorWidgets/Unknown/UnknownPreview.js +19 -0
- package/src/components/EditorWidgets/index.js +5 -0
- package/src/components/MediaLibrary/EmptyMessage.js +28 -0
- package/src/components/MediaLibrary/MediaLibrary.js +411 -0
- package/src/components/MediaLibrary/MediaLibraryButtons.js +136 -0
- package/src/components/MediaLibrary/MediaLibraryCard.js +128 -0
- package/src/components/MediaLibrary/MediaLibraryCardGrid.js +199 -0
- package/src/components/MediaLibrary/MediaLibraryHeader.js +48 -0
- package/src/components/MediaLibrary/MediaLibraryModal.js +200 -0
- package/src/components/MediaLibrary/MediaLibrarySearch.js +61 -0
- package/src/components/MediaLibrary/MediaLibraryTop.js +143 -0
- package/src/components/MediaLibrary/__tests__/MediaLibraryButtons.spec.js +45 -0
- package/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js +49 -0
- package/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap +264 -0
- package/src/components/UI/DragDrop.js +66 -0
- package/src/components/UI/ErrorBoundary.js +214 -0
- package/src/components/UI/FileUploadButton.js +24 -0
- package/src/components/UI/Modal.js +112 -0
- package/src/components/UI/Notifications.tsx +83 -0
- package/src/components/UI/SettingsDropdown.js +103 -0
- package/src/components/UI/__tests__/ErrorBoundary.spec.js +57 -0
- package/src/components/UI/index.js +6 -0
- package/src/components/Workflow/Workflow.js +169 -0
- package/src/components/Workflow/WorkflowCard.js +177 -0
- package/src/components/Workflow/WorkflowList.js +272 -0
- package/src/constants/__tests__/configSchema.spec.js +611 -0
- package/src/constants/collectionTypes.ts +2 -0
- package/src/constants/collectionViews.js +2 -0
- package/src/constants/commitProps.ts +2 -0
- package/src/constants/configSchema.js +441 -0
- package/src/constants/fieldInference.tsx +78 -0
- package/src/constants/publishModes.ts +22 -0
- package/src/constants/validationErrorTypes.js +6 -0
- package/src/formats/__tests__/formats.spec.js +87 -0
- package/src/formats/__tests__/frontmatter.spec.js +429 -0
- package/src/formats/__tests__/toml.spec.js +9 -0
- package/src/formats/__tests__/yaml.spec.js +162 -0
- package/src/formats/formats.ts +97 -0
- package/src/formats/frontmatter.ts +150 -0
- package/src/formats/helpers.ts +14 -0
- package/src/formats/json.ts +9 -0
- package/src/formats/toml.ts +33 -0
- package/src/formats/yaml.ts +58 -0
- package/src/index.js +8 -0
- package/src/integrations/index.js +35 -0
- package/src/integrations/providers/algolia/implementation.js +176 -0
- package/src/integrations/providers/assetStore/implementation.js +148 -0
- package/src/lib/__tests__/formatters.spec.js +751 -0
- package/src/lib/__tests__/i18n.spec.js +792 -0
- package/src/lib/__tests__/phrases.spec.js +119 -0
- package/src/lib/__tests__/registry.spec.js +261 -0
- package/src/lib/__tests__/serializeEntryValues.spec.js +22 -0
- package/src/lib/__tests__/urlHelper.spec.js +138 -0
- package/src/lib/consoleError.js +7 -0
- package/src/lib/formatters.ts +286 -0
- package/src/lib/i18n.ts +454 -0
- package/src/lib/phrases.js +8 -0
- package/src/lib/polyfill.js +9 -0
- package/src/lib/registry.js +312 -0
- package/src/lib/serializeEntryValues.js +75 -0
- package/src/lib/stega.ts +145 -0
- package/src/lib/textHelper.js +11 -0
- package/src/lib/urlHelper.ts +128 -0
- package/src/mediaLibrary.ts +51 -0
- package/src/reducers/__tests__/auth.spec.ts +38 -0
- package/src/reducers/__tests__/collections.spec.js +610 -0
- package/src/reducers/__tests__/config.spec.js +38 -0
- package/src/reducers/__tests__/entries.spec.js +694 -0
- package/src/reducers/__tests__/entryDraft.spec.js +297 -0
- package/src/reducers/__tests__/globalUI.js +43 -0
- package/src/reducers/__tests__/integrations.spec.ts +76 -0
- package/src/reducers/__tests__/mediaLibrary.spec.js +154 -0
- package/src/reducers/__tests__/medias.spec.ts +49 -0
- package/src/reducers/auth.ts +46 -0
- package/src/reducers/collections.ts +535 -0
- package/src/reducers/combinedReducer.ts +11 -0
- package/src/reducers/config.ts +38 -0
- package/src/reducers/cursors.js +36 -0
- package/src/reducers/deploys.ts +52 -0
- package/src/reducers/editorialWorkflow.ts +163 -0
- package/src/reducers/entries.ts +819 -0
- package/src/reducers/entryDraft.js +260 -0
- package/src/reducers/globalUI.ts +45 -0
- package/src/reducers/index.ts +82 -0
- package/src/reducers/integrations.ts +59 -0
- package/src/reducers/mediaLibrary.ts +296 -0
- package/src/reducers/medias.ts +66 -0
- package/src/reducers/notifications.ts +52 -0
- package/src/reducers/search.ts +111 -0
- package/src/reducers/status.ts +40 -0
- package/src/redux/index.ts +18 -0
- package/src/redux/middleware/waitUntilAction.ts +64 -0
- package/src/routing/__tests__/history.spec.ts +49 -0
- package/src/routing/history.ts +17 -0
- package/src/types/diacritics.d.ts +1 -0
- package/src/types/global.d.ts +8 -0
- package/src/types/immutable.ts +49 -0
- package/src/types/redux.ts +827 -0
- package/src/types/tomlify-j0.4.d.ts +13 -0
- package/src/valueObjects/AssetProxy.ts +48 -0
- package/src/valueObjects/EditorComponent.js +38 -0
- package/src/valueObjects/Entry.ts +63 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { currentBackend } from '../backend';
|
|
2
|
+
import { addNotification, clearNotifications } from './notifications';
|
|
3
|
+
|
|
4
|
+
import type { Credentials, User } from 'decap-cms-lib-util';
|
|
5
|
+
import type { ThunkDispatch } from 'redux-thunk';
|
|
6
|
+
import type { AnyAction } from 'redux';
|
|
7
|
+
import type { State } from '../types/redux';
|
|
8
|
+
|
|
9
|
+
export const AUTH_REQUEST = 'AUTH_REQUEST';
|
|
10
|
+
export const AUTH_SUCCESS = 'AUTH_SUCCESS';
|
|
11
|
+
export const AUTH_FAILURE = 'AUTH_FAILURE';
|
|
12
|
+
export const AUTH_REQUEST_DONE = 'AUTH_REQUEST_DONE';
|
|
13
|
+
export const USE_OPEN_AUTHORING = 'USE_OPEN_AUTHORING';
|
|
14
|
+
export const LOGOUT = 'LOGOUT';
|
|
15
|
+
|
|
16
|
+
export function authenticating() {
|
|
17
|
+
return {
|
|
18
|
+
type: AUTH_REQUEST,
|
|
19
|
+
} as const;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function authenticate(userData: User) {
|
|
23
|
+
return {
|
|
24
|
+
type: AUTH_SUCCESS,
|
|
25
|
+
payload: userData,
|
|
26
|
+
} as const;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function authError(error: Error) {
|
|
30
|
+
return {
|
|
31
|
+
type: AUTH_FAILURE,
|
|
32
|
+
error: 'Failed to authenticate',
|
|
33
|
+
payload: error,
|
|
34
|
+
} as const;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function doneAuthenticating() {
|
|
38
|
+
return {
|
|
39
|
+
type: AUTH_REQUEST_DONE,
|
|
40
|
+
} as const;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function useOpenAuthoring() {
|
|
44
|
+
return {
|
|
45
|
+
type: USE_OPEN_AUTHORING,
|
|
46
|
+
} as const;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function logout() {
|
|
50
|
+
return {
|
|
51
|
+
type: LOGOUT,
|
|
52
|
+
} as const;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check if user data token is cached and is valid
|
|
56
|
+
export function authenticateUser() {
|
|
57
|
+
return (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
|
|
58
|
+
const state = getState();
|
|
59
|
+
const backend = currentBackend(state.config);
|
|
60
|
+
dispatch(authenticating());
|
|
61
|
+
return Promise.resolve(backend.currentUser())
|
|
62
|
+
.then(user => {
|
|
63
|
+
if (user) {
|
|
64
|
+
if (user.useOpenAuthoring) {
|
|
65
|
+
dispatch(useOpenAuthoring());
|
|
66
|
+
}
|
|
67
|
+
dispatch(authenticate(user));
|
|
68
|
+
} else {
|
|
69
|
+
dispatch(doneAuthenticating());
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
.catch((error: Error) => {
|
|
73
|
+
dispatch(authError(error));
|
|
74
|
+
dispatch(logoutUser());
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function loginUser(credentials: Credentials) {
|
|
80
|
+
return (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
|
|
81
|
+
const state = getState();
|
|
82
|
+
const backend = currentBackend(state.config);
|
|
83
|
+
|
|
84
|
+
dispatch(authenticating());
|
|
85
|
+
return backend
|
|
86
|
+
.authenticate(credentials)
|
|
87
|
+
.then(user => {
|
|
88
|
+
if (user.useOpenAuthoring) {
|
|
89
|
+
dispatch(useOpenAuthoring());
|
|
90
|
+
}
|
|
91
|
+
dispatch(authenticate(user));
|
|
92
|
+
})
|
|
93
|
+
.catch((error: Error) => {
|
|
94
|
+
console.error(error);
|
|
95
|
+
dispatch(
|
|
96
|
+
addNotification({
|
|
97
|
+
message: {
|
|
98
|
+
details: error.message,
|
|
99
|
+
key: 'ui.toast.onFailToAuth',
|
|
100
|
+
},
|
|
101
|
+
type: 'error',
|
|
102
|
+
dismissAfter: 8000,
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
dispatch(authError(error));
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function logoutUser() {
|
|
111
|
+
return (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
|
|
112
|
+
const state = getState();
|
|
113
|
+
const backend = currentBackend(state.config);
|
|
114
|
+
Promise.resolve(backend.logout()).then(() => {
|
|
115
|
+
dispatch(logout());
|
|
116
|
+
dispatch(clearNotifications());
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export type AuthAction = ReturnType<
|
|
122
|
+
| typeof authenticating
|
|
123
|
+
| typeof authenticate
|
|
124
|
+
| typeof authError
|
|
125
|
+
| typeof doneAuthenticating
|
|
126
|
+
| typeof logout
|
|
127
|
+
>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { history } from '../routing/history';
|
|
2
|
+
import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper';
|
|
3
|
+
|
|
4
|
+
export function searchCollections(query: string, collection: string) {
|
|
5
|
+
if (collection) {
|
|
6
|
+
history.push(`/collections/${collection}/search/${query}`);
|
|
7
|
+
} else {
|
|
8
|
+
history.push(`/search/${query}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function showCollection(collectionName: string) {
|
|
13
|
+
history.push(getCollectionUrl(collectionName));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createNewEntry(collectionName: string) {
|
|
17
|
+
history.push(getNewEntryUrl(collectionName));
|
|
18
|
+
}
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
import yaml from 'yaml';
|
|
2
|
+
import { fromJS } from 'immutable';
|
|
3
|
+
import deepmerge from 'deepmerge';
|
|
4
|
+
import { produce } from 'immer';
|
|
5
|
+
import trimStart from 'lodash/trimStart';
|
|
6
|
+
import trim from 'lodash/trim';
|
|
7
|
+
import isEmpty from 'lodash/isEmpty';
|
|
8
|
+
|
|
9
|
+
import { SIMPLE as SIMPLE_PUBLISH_MODE } from '../constants/publishModes';
|
|
10
|
+
import { validateConfig } from '../constants/configSchema';
|
|
11
|
+
import { selectDefaultSortableFields } from '../reducers/collections';
|
|
12
|
+
import { getIntegrations, selectIntegration } from '../reducers/integrations';
|
|
13
|
+
import { resolveBackend } from '../backend';
|
|
14
|
+
import { I18N, I18N_FIELD, I18N_STRUCTURE } from '../lib/i18n';
|
|
15
|
+
import { FILES, FOLDER } from '../constants/collectionTypes';
|
|
16
|
+
|
|
17
|
+
import type { ThunkDispatch } from 'redux-thunk';
|
|
18
|
+
import type { AnyAction } from 'redux';
|
|
19
|
+
import type {
|
|
20
|
+
CmsCollection,
|
|
21
|
+
CmsConfig,
|
|
22
|
+
CmsField,
|
|
23
|
+
CmsFieldBase,
|
|
24
|
+
CmsFieldObject,
|
|
25
|
+
CmsFieldList,
|
|
26
|
+
CmsI18nConfig,
|
|
27
|
+
CmsPublishMode,
|
|
28
|
+
CmsLocalBackend,
|
|
29
|
+
State,
|
|
30
|
+
} from '../types/redux';
|
|
31
|
+
|
|
32
|
+
export const CONFIG_REQUEST = 'CONFIG_REQUEST';
|
|
33
|
+
export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
|
|
34
|
+
export const CONFIG_FAILURE = 'CONFIG_FAILURE';
|
|
35
|
+
|
|
36
|
+
function isObjectField(field: CmsField): field is CmsFieldBase & CmsFieldObject {
|
|
37
|
+
return 'fields' in (field as CmsFieldObject);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isFieldList(field: CmsField): field is CmsFieldBase & CmsFieldList {
|
|
41
|
+
return 'types' in (field as CmsFieldList) || 'field' in (field as CmsFieldList);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function traverseFieldsJS<Field extends CmsField>(
|
|
45
|
+
fields: Field[],
|
|
46
|
+
updater: <T extends CmsField>(field: T) => T,
|
|
47
|
+
): Field[] {
|
|
48
|
+
return fields.map(field => {
|
|
49
|
+
const newField = updater(field);
|
|
50
|
+
if (isObjectField(newField)) {
|
|
51
|
+
return { ...newField, fields: traverseFieldsJS(newField.fields, updater) };
|
|
52
|
+
} else if (isFieldList(newField) && newField.field) {
|
|
53
|
+
return { ...newField, field: traverseFieldsJS([newField.field], updater)[0] };
|
|
54
|
+
} else if (isFieldList(newField) && newField.types) {
|
|
55
|
+
return { ...newField, types: traverseFieldsJS(newField.types, updater) };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return newField;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getConfigUrl() {
|
|
63
|
+
const validTypes: { [type: string]: string } = {
|
|
64
|
+
'text/yaml': 'yaml',
|
|
65
|
+
'application/x-yaml': 'yaml',
|
|
66
|
+
};
|
|
67
|
+
const configLinkEl = document.querySelector<HTMLLinkElement>('link[rel="cms-config-url"]');
|
|
68
|
+
if (configLinkEl && validTypes[configLinkEl.type] && configLinkEl.href) {
|
|
69
|
+
console.log(`Using config file path: "${configLinkEl.href}"`);
|
|
70
|
+
return configLinkEl.href;
|
|
71
|
+
}
|
|
72
|
+
return 'config.yml';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function setDefaultPublicFolderForField<T extends CmsField>(field: T) {
|
|
76
|
+
if ('media_folder' in field && !('public_folder' in field)) {
|
|
77
|
+
return { ...field, public_folder: field.media_folder };
|
|
78
|
+
}
|
|
79
|
+
return field;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Mapping between existing camelCase and its snake_case counterpart
|
|
83
|
+
const WIDGET_KEY_MAP = {
|
|
84
|
+
dateFormat: 'date_format',
|
|
85
|
+
timeFormat: 'time_format',
|
|
86
|
+
pickerUtc: 'picker_utc',
|
|
87
|
+
editorComponents: 'editor_components',
|
|
88
|
+
valueType: 'value_type',
|
|
89
|
+
valueField: 'value_field',
|
|
90
|
+
searchFields: 'search_fields',
|
|
91
|
+
displayFields: 'display_fields',
|
|
92
|
+
optionsLength: 'options_length',
|
|
93
|
+
} as const;
|
|
94
|
+
|
|
95
|
+
function setSnakeCaseConfig<T extends CmsField>(field: T) {
|
|
96
|
+
const deprecatedKeys = Object.keys(WIDGET_KEY_MAP).filter(
|
|
97
|
+
camel => camel in field,
|
|
98
|
+
) as ReadonlyArray<keyof typeof WIDGET_KEY_MAP>;
|
|
99
|
+
|
|
100
|
+
const snakeValues = deprecatedKeys.map(camel => {
|
|
101
|
+
const snake = WIDGET_KEY_MAP[camel];
|
|
102
|
+
console.warn(
|
|
103
|
+
`Field ${field.name} is using a deprecated configuration '${camel}'. Please use '${snake}'`,
|
|
104
|
+
);
|
|
105
|
+
return { [snake]: (field as unknown as Record<string, unknown>)[camel] };
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return Object.assign({}, field, ...snakeValues) as T;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function setI18nField<T extends CmsField>(field: T) {
|
|
112
|
+
if (field[I18N] === true) {
|
|
113
|
+
return { ...field, [I18N]: I18N_FIELD.TRANSLATE };
|
|
114
|
+
} else if (field[I18N] === false || !field[I18N]) {
|
|
115
|
+
return { ...field, [I18N]: I18N_FIELD.NONE };
|
|
116
|
+
}
|
|
117
|
+
return field;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function getI18nDefaults(
|
|
121
|
+
collectionOrFileI18n: boolean | CmsI18nConfig,
|
|
122
|
+
defaultI18n: CmsI18nConfig,
|
|
123
|
+
) {
|
|
124
|
+
if (typeof collectionOrFileI18n === 'boolean') {
|
|
125
|
+
return defaultI18n;
|
|
126
|
+
} else {
|
|
127
|
+
const locales = collectionOrFileI18n.locales || defaultI18n.locales;
|
|
128
|
+
const defaultLocale = collectionOrFileI18n.default_locale || locales[0];
|
|
129
|
+
const mergedI18n: CmsI18nConfig = deepmerge(defaultI18n, collectionOrFileI18n);
|
|
130
|
+
mergedI18n.locales = locales;
|
|
131
|
+
mergedI18n.default_locale = defaultLocale;
|
|
132
|
+
throwOnMissingDefaultLocale(mergedI18n);
|
|
133
|
+
return mergedI18n;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function setI18nDefaultsForFields(collectionOrFileFields: CmsField[], hasI18n: boolean) {
|
|
138
|
+
if (hasI18n) {
|
|
139
|
+
return traverseFieldsJS(collectionOrFileFields, setI18nField);
|
|
140
|
+
} else {
|
|
141
|
+
return traverseFieldsJS(collectionOrFileFields, field => {
|
|
142
|
+
const newField = { ...field };
|
|
143
|
+
delete newField[I18N];
|
|
144
|
+
return newField;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function throwOnInvalidFileCollectionStructure(i18n?: CmsI18nConfig) {
|
|
150
|
+
if (i18n && i18n.structure !== I18N_STRUCTURE.SINGLE_FILE) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`i18n configuration for files collections is limited to ${I18N_STRUCTURE.SINGLE_FILE} structure`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function throwOnMissingDefaultLocale(i18n?: CmsI18nConfig) {
|
|
158
|
+
if (i18n && i18n.default_locale && !i18n.locales.includes(i18n.default_locale)) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`i18n locales '${i18n.locales.join(', ')}' are missing the default locale ${
|
|
161
|
+
i18n.default_locale
|
|
162
|
+
}`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function hasIntegration(config: CmsConfig, collection: CmsCollection) {
|
|
168
|
+
// TODO remove fromJS when Immutable is removed from the integrations state slice
|
|
169
|
+
const integrations = getIntegrations(fromJS(config));
|
|
170
|
+
const integration = selectIntegration(integrations, collection.name, 'listEntries');
|
|
171
|
+
return !!integration;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function normalizeSortableFields(
|
|
175
|
+
sortableFields: (
|
|
176
|
+
| string
|
|
177
|
+
| { field: string; label?: string; default_sort?: boolean | 'asc' | 'desc' }
|
|
178
|
+
)[],
|
|
179
|
+
) {
|
|
180
|
+
return sortableFields.map(field => {
|
|
181
|
+
if (typeof field === 'string') {
|
|
182
|
+
return { field, default_sort: undefined };
|
|
183
|
+
}
|
|
184
|
+
return field;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function normalizeConfig(config: CmsConfig) {
|
|
189
|
+
const { collections = [] } = config;
|
|
190
|
+
|
|
191
|
+
const normalizedCollections = collections.map(collection => {
|
|
192
|
+
const { fields, files } = collection;
|
|
193
|
+
|
|
194
|
+
let normalizedCollection = collection;
|
|
195
|
+
if (fields) {
|
|
196
|
+
const normalizedFields = traverseFieldsJS(fields, setSnakeCaseConfig);
|
|
197
|
+
normalizedCollection = { ...normalizedCollection, fields: normalizedFields };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (files) {
|
|
201
|
+
const normalizedFiles = files.map(file => {
|
|
202
|
+
const normalizedFileFields = traverseFieldsJS(file.fields, setSnakeCaseConfig);
|
|
203
|
+
return { ...file, fields: normalizedFileFields };
|
|
204
|
+
});
|
|
205
|
+
normalizedCollection = { ...normalizedCollection, files: normalizedFiles };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (normalizedCollection.sortableFields) {
|
|
209
|
+
const { sortableFields, ...rest } = normalizedCollection;
|
|
210
|
+
normalizedCollection = { ...rest, sortable_fields: sortableFields };
|
|
211
|
+
|
|
212
|
+
console.warn(
|
|
213
|
+
`Collection ${collection.name} is using a deprecated configuration 'sortableFields'. Please use 'sortable_fields'`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Normalize sortable_fields to consistent object format
|
|
218
|
+
if (normalizedCollection.sortable_fields) {
|
|
219
|
+
normalizedCollection = {
|
|
220
|
+
...normalizedCollection,
|
|
221
|
+
sortable_fields: normalizeSortableFields(normalizedCollection.sortable_fields),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return normalizedCollection;
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return { ...config, collections: normalizedCollections };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function applyDefaults(originalConfig: CmsConfig) {
|
|
232
|
+
return produce(originalConfig, config => {
|
|
233
|
+
config.publish_mode = config.publish_mode || SIMPLE_PUBLISH_MODE;
|
|
234
|
+
config.slug = config.slug || {};
|
|
235
|
+
config.collections = config.collections || [];
|
|
236
|
+
|
|
237
|
+
// Use `site_url` as default `display_url`.
|
|
238
|
+
if (!config.display_url && config.site_url) {
|
|
239
|
+
config.display_url = config.site_url;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Use media_folder as default public_folder.
|
|
243
|
+
const defaultPublicFolder = `/${trimStart(config.media_folder, '/')}`;
|
|
244
|
+
if (!('public_folder' in config)) {
|
|
245
|
+
config.public_folder = defaultPublicFolder;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// default values for the slug config
|
|
249
|
+
if (!('encoding' in config.slug)) {
|
|
250
|
+
config.slug.encoding = 'unicode';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!('clean_accents' in config.slug)) {
|
|
254
|
+
config.slug.clean_accents = false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!('sanitize_replacement' in config.slug)) {
|
|
258
|
+
config.slug.sanitize_replacement = '-';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const i18n = config[I18N];
|
|
262
|
+
|
|
263
|
+
if (i18n) {
|
|
264
|
+
i18n.default_locale = i18n.default_locale || i18n.locales[0];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
throwOnMissingDefaultLocale(i18n);
|
|
268
|
+
|
|
269
|
+
const backend = resolveBackend(config);
|
|
270
|
+
|
|
271
|
+
for (const collection of config.collections) {
|
|
272
|
+
if (!('publish' in collection)) {
|
|
273
|
+
collection.publish = true;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let collectionI18n = collection[I18N];
|
|
277
|
+
|
|
278
|
+
if (i18n && collectionI18n) {
|
|
279
|
+
collectionI18n = getI18nDefaults(collectionI18n, i18n);
|
|
280
|
+
collection[I18N] = collectionI18n;
|
|
281
|
+
} else {
|
|
282
|
+
collectionI18n = undefined;
|
|
283
|
+
delete collection[I18N];
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (collection.fields) {
|
|
287
|
+
collection.fields = setI18nDefaultsForFields(collection.fields, Boolean(collectionI18n));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const { folder, files, view_filters, view_groups, meta } = collection;
|
|
291
|
+
|
|
292
|
+
if (folder) {
|
|
293
|
+
collection.type = FOLDER;
|
|
294
|
+
|
|
295
|
+
if (collection.path && !collection.media_folder) {
|
|
296
|
+
// default value for media folder when using the path config
|
|
297
|
+
collection.media_folder = '';
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if ('media_folder' in collection && !('public_folder' in collection)) {
|
|
301
|
+
collection.public_folder = collection.media_folder;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (collection.fields) {
|
|
305
|
+
collection.fields = traverseFieldsJS(collection.fields, setDefaultPublicFolderForField);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
collection.folder = trim(folder, '/');
|
|
309
|
+
|
|
310
|
+
if (meta && meta.path) {
|
|
311
|
+
const metaPath = meta.path;
|
|
312
|
+
const metaField = {
|
|
313
|
+
name: 'path',
|
|
314
|
+
meta: true,
|
|
315
|
+
required: true,
|
|
316
|
+
...metaPath,
|
|
317
|
+
// Ensure index_file is a string to satisfy CmsFieldMeta
|
|
318
|
+
index_file: metaPath.index_file ?? '',
|
|
319
|
+
};
|
|
320
|
+
collection.fields = [metaField, ...(collection.fields || [])];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (files) {
|
|
325
|
+
collection.type = FILES;
|
|
326
|
+
|
|
327
|
+
throwOnInvalidFileCollectionStructure(collectionI18n);
|
|
328
|
+
|
|
329
|
+
delete collection.nested;
|
|
330
|
+
delete collection.meta;
|
|
331
|
+
|
|
332
|
+
for (const file of files) {
|
|
333
|
+
file.file = trimStart(file.file, '/');
|
|
334
|
+
|
|
335
|
+
if ('media_folder' in file && !('public_folder' in file)) {
|
|
336
|
+
file.public_folder = file.media_folder;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (file.fields) {
|
|
340
|
+
file.fields = traverseFieldsJS(file.fields, setDefaultPublicFolderForField);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
let fileI18n = file[I18N];
|
|
344
|
+
|
|
345
|
+
if (fileI18n && collectionI18n) {
|
|
346
|
+
fileI18n = getI18nDefaults(fileI18n, collectionI18n);
|
|
347
|
+
file[I18N] = fileI18n;
|
|
348
|
+
} else {
|
|
349
|
+
fileI18n = undefined;
|
|
350
|
+
delete file[I18N];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
throwOnInvalidFileCollectionStructure(fileI18n);
|
|
354
|
+
|
|
355
|
+
if (file.fields) {
|
|
356
|
+
file.fields = setI18nDefaultsForFields(file.fields, Boolean(fileI18n));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (!collection.sortable_fields) {
|
|
362
|
+
collection.sortable_fields = selectDefaultSortableFields(
|
|
363
|
+
// TODO remove fromJS when Immutable is removed from the collections state slice
|
|
364
|
+
fromJS(collection),
|
|
365
|
+
backend,
|
|
366
|
+
hasIntegration(config, collection),
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
collection.view_filters = (view_filters || []).map(filter => {
|
|
371
|
+
return {
|
|
372
|
+
...filter,
|
|
373
|
+
id: `${filter.field}__${filter.pattern}`,
|
|
374
|
+
};
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
collection.view_groups = (view_groups || []).map(group => {
|
|
378
|
+
return {
|
|
379
|
+
...group,
|
|
380
|
+
id: `${group.field}__${group.pattern}`,
|
|
381
|
+
};
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
if (config.editor && !collection.editor) {
|
|
385
|
+
collection.editor = { preview: config.editor.preview };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function parseConfig(data: string) {
|
|
392
|
+
const config = yaml.parse(data, { maxAliasCount: -1, prettyErrors: true, merge: true });
|
|
393
|
+
if (
|
|
394
|
+
typeof window !== 'undefined' &&
|
|
395
|
+
typeof window.CMS_ENV === 'string' &&
|
|
396
|
+
config[window.CMS_ENV]
|
|
397
|
+
) {
|
|
398
|
+
const configKeys = Object.keys(config[window.CMS_ENV]) as ReadonlyArray<keyof CmsConfig>;
|
|
399
|
+
for (const key of configKeys) {
|
|
400
|
+
config[key] = config[window.CMS_ENV][key] as CmsConfig[keyof CmsConfig];
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return config as Partial<CmsConfig>;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async function getConfigYaml(file: string, hasManualConfig: boolean) {
|
|
407
|
+
const response = await fetch(file, { credentials: 'same-origin' }).catch(error => error as Error);
|
|
408
|
+
if (response instanceof Error || response.status !== 200) {
|
|
409
|
+
if (hasManualConfig) {
|
|
410
|
+
return {};
|
|
411
|
+
}
|
|
412
|
+
const message = response instanceof Error ? response.message : response.status;
|
|
413
|
+
throw new Error(`Failed to load config.yml (${message})`);
|
|
414
|
+
}
|
|
415
|
+
const contentType = response.headers.get('Content-Type') || 'Not-Found';
|
|
416
|
+
const isYaml = contentType.indexOf('yaml') !== -1;
|
|
417
|
+
if (!isYaml) {
|
|
418
|
+
console.log(`Response for ${file} was not yaml. (Content-Type: ${contentType})`);
|
|
419
|
+
if (hasManualConfig) {
|
|
420
|
+
return {};
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return parseConfig(await response.text());
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export function configLoaded(config: CmsConfig) {
|
|
427
|
+
return {
|
|
428
|
+
type: CONFIG_SUCCESS,
|
|
429
|
+
payload: config,
|
|
430
|
+
} as const;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function configLoading() {
|
|
434
|
+
return {
|
|
435
|
+
type: CONFIG_REQUEST,
|
|
436
|
+
} as const;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export function configFailed(err: Error) {
|
|
440
|
+
return {
|
|
441
|
+
type: CONFIG_FAILURE,
|
|
442
|
+
error: 'Error loading config',
|
|
443
|
+
payload: err,
|
|
444
|
+
} as const;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export async function detectProxyServer(localBackend?: boolean | CmsLocalBackend) {
|
|
448
|
+
const allowedHosts = [
|
|
449
|
+
'localhost',
|
|
450
|
+
'127.0.0.1',
|
|
451
|
+
...(typeof localBackend === 'boolean' ? [] : localBackend?.allowed_hosts || []),
|
|
452
|
+
];
|
|
453
|
+
|
|
454
|
+
if (!allowedHosts.includes(location.hostname) || !localBackend) {
|
|
455
|
+
return {};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const defaultUrl = 'http://localhost:8081/api/v1';
|
|
459
|
+
const proxyUrl =
|
|
460
|
+
localBackend === true
|
|
461
|
+
? defaultUrl
|
|
462
|
+
: localBackend.url || defaultUrl.replace('localhost', location.hostname);
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
console.log(`Looking for Decap CMS Proxy Server at '${proxyUrl}'`);
|
|
466
|
+
const res = await fetch(`${proxyUrl}`, {
|
|
467
|
+
method: 'POST',
|
|
468
|
+
headers: { 'Content-Type': 'application/json' },
|
|
469
|
+
body: JSON.stringify({ action: 'info' }),
|
|
470
|
+
});
|
|
471
|
+
const { repo, publish_modes, type } = (await res.json()) as {
|
|
472
|
+
repo?: string;
|
|
473
|
+
publish_modes?: CmsPublishMode[];
|
|
474
|
+
type?: string;
|
|
475
|
+
};
|
|
476
|
+
if (typeof repo === 'string' && Array.isArray(publish_modes) && typeof type === 'string') {
|
|
477
|
+
console.log(`Detected Decap CMS Proxy Server at '${proxyUrl}' with repo: '${repo}'`);
|
|
478
|
+
return { proxyUrl, publish_modes, type };
|
|
479
|
+
} else {
|
|
480
|
+
console.log(`Decap CMS Proxy Server not detected at '${proxyUrl}'`);
|
|
481
|
+
return {};
|
|
482
|
+
}
|
|
483
|
+
} catch {
|
|
484
|
+
console.log(`Decap CMS Proxy Server not detected at '${proxyUrl}'`);
|
|
485
|
+
return {};
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function getPublishMode(config: CmsConfig, publishModes?: CmsPublishMode[], backendType?: string) {
|
|
490
|
+
if (config.publish_mode && publishModes && !publishModes.includes(config.publish_mode)) {
|
|
491
|
+
const newPublishMode = publishModes[0];
|
|
492
|
+
console.log(
|
|
493
|
+
`'${config.publish_mode}' is not supported by '${backendType}' backend, switching to '${newPublishMode}'`,
|
|
494
|
+
);
|
|
495
|
+
return newPublishMode;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return config.publish_mode;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export async function handleLocalBackend(originalConfig: CmsConfig) {
|
|
502
|
+
if (!originalConfig.local_backend) {
|
|
503
|
+
return originalConfig;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const {
|
|
507
|
+
proxyUrl,
|
|
508
|
+
publish_modes: publishModes,
|
|
509
|
+
type: backendType,
|
|
510
|
+
} = await detectProxyServer(originalConfig.local_backend);
|
|
511
|
+
|
|
512
|
+
if (!proxyUrl) {
|
|
513
|
+
return originalConfig;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return produce(originalConfig, config => {
|
|
517
|
+
config.backend.name = 'proxy';
|
|
518
|
+
config.backend.proxy_url = proxyUrl;
|
|
519
|
+
|
|
520
|
+
if (config.publish_mode) {
|
|
521
|
+
config.publish_mode = getPublishMode(config, publishModes, backendType);
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function loadConfig(manualConfig: Partial<CmsConfig> = {}, onLoad: () => unknown) {
|
|
527
|
+
if (window.CMS_CONFIG) {
|
|
528
|
+
return configLoaded(window.CMS_CONFIG);
|
|
529
|
+
}
|
|
530
|
+
return async (dispatch: ThunkDispatch<State, {}, AnyAction>) => {
|
|
531
|
+
dispatch(configLoading());
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
const configUrl = getConfigUrl();
|
|
535
|
+
const hasManualConfig = !isEmpty(manualConfig);
|
|
536
|
+
const configYaml =
|
|
537
|
+
manualConfig.load_config_file === false
|
|
538
|
+
? {}
|
|
539
|
+
: await getConfigYaml(configUrl, hasManualConfig);
|
|
540
|
+
|
|
541
|
+
// Merge manual config into the config.yml one
|
|
542
|
+
const mergedConfig = deepmerge(configYaml, manualConfig);
|
|
543
|
+
|
|
544
|
+
validateConfig(mergedConfig);
|
|
545
|
+
|
|
546
|
+
const withLocalBackend = await handleLocalBackend(mergedConfig);
|
|
547
|
+
const normalizedConfig = normalizeConfig(withLocalBackend);
|
|
548
|
+
|
|
549
|
+
const config = applyDefaults(normalizedConfig);
|
|
550
|
+
|
|
551
|
+
dispatch(configLoaded(config));
|
|
552
|
+
|
|
553
|
+
if (typeof onLoad === 'function') {
|
|
554
|
+
onLoad();
|
|
555
|
+
}
|
|
556
|
+
} catch (err) {
|
|
557
|
+
dispatch(configFailed(err));
|
|
558
|
+
throw err;
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export type ConfigAction = ReturnType<
|
|
564
|
+
typeof configLoading | typeof configLoaded | typeof configFailed
|
|
565
|
+
>;
|