Allow adding custom javascript to the page. Closes #2604
This commit is contained in:
parent
053a33ac47
commit
19c228eaf6
@ -650,6 +650,22 @@ func SetCustomStyles(w http.ResponseWriter, r *http.Request) {
|
|||||||
controllers.WriteSimpleResponse(w, true, "custom styles updated")
|
controllers.WriteSimpleResponse(w, true, "custom styles updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCustomJavascript will set the Javascript string we insert into the page.
|
||||||
|
func SetCustomJavascript(w http.ResponseWriter, r *http.Request) {
|
||||||
|
customJavascript, success := getValueFromRequest(w, r)
|
||||||
|
if !success {
|
||||||
|
controllers.WriteSimpleResponse(w, false, "unable to update custom javascript")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := data.SetCustomJavascript(customJavascript.Value.(string)); err != nil {
|
||||||
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
controllers.WriteSimpleResponse(w, true, "custom styles updated")
|
||||||
|
}
|
||||||
|
|
||||||
// SetForbiddenUsernameList will set the list of usernames we do not allow to use.
|
// SetForbiddenUsernameList will set the list of usernames we do not allow to use.
|
||||||
func SetForbiddenUsernameList(w http.ResponseWriter, r *http.Request) {
|
func SetForbiddenUsernameList(w http.ResponseWriter, r *http.Request) {
|
||||||
type forbiddenUsernameListRequest struct {
|
type forbiddenUsernameListRequest struct {
|
||||||
|
@ -46,6 +46,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
SocialHandles: data.GetSocialHandles(),
|
SocialHandles: data.GetSocialHandles(),
|
||||||
NSFW: data.GetNSFW(),
|
NSFW: data.GetNSFW(),
|
||||||
CustomStyles: data.GetCustomStyles(),
|
CustomStyles: data.GetCustomStyles(),
|
||||||
|
CustomJavascript: data.GetCustomJavascript(),
|
||||||
AppearanceVariables: data.GetCustomColorVariableValues(),
|
AppearanceVariables: data.GetCustomColorVariableValues(),
|
||||||
},
|
},
|
||||||
FFmpegPath: ffmpeg,
|
FFmpegPath: ffmpeg,
|
||||||
@ -138,6 +139,7 @@ type webConfigResponse struct {
|
|||||||
StreamTitle string `json:"streamTitle"` // What's going on with the current stream
|
StreamTitle string `json:"streamTitle"` // What's going on with the current stream
|
||||||
SocialHandles []models.SocialHandle `json:"socialHandles"`
|
SocialHandles []models.SocialHandle `json:"socialHandles"`
|
||||||
CustomStyles string `json:"customStyles"`
|
CustomStyles string `json:"customStyles"`
|
||||||
|
CustomJavascript string `json:"customJavascript"`
|
||||||
AppearanceVariables map[string]string `json:"appearanceVariables"`
|
AppearanceVariables map[string]string `json:"appearanceVariables"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
controllers/customJavascript.go
Normal file
13
controllers/customJavascript.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/owncast/owncast/core/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServeCustomJavascript will serve optional custom Javascript.
|
||||||
|
func ServeCustomJavascript(w http.ResponseWriter, r *http.Request) {
|
||||||
|
js := data.GetCustomJavascript()
|
||||||
|
w.Write([]byte(js))
|
||||||
|
}
|
@ -44,6 +44,7 @@ const (
|
|||||||
chatDisabledKey = "chat_disabled"
|
chatDisabledKey = "chat_disabled"
|
||||||
externalActionsKey = "external_actions"
|
externalActionsKey = "external_actions"
|
||||||
customStylesKey = "custom_styles"
|
customStylesKey = "custom_styles"
|
||||||
|
customJavascriptKey = "custom_javascript"
|
||||||
videoCodecKey = "video_codec"
|
videoCodecKey = "video_codec"
|
||||||
blockedUsernamesKey = "blocked_usernames"
|
blockedUsernamesKey = "blocked_usernames"
|
||||||
publicKeyKey = "public_key"
|
publicKeyKey = "public_key"
|
||||||
@ -560,6 +561,21 @@ func GetCustomStyles() string {
|
|||||||
return style
|
return style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCustomJavascript will save a string with Javascript to insert into the page.
|
||||||
|
func SetCustomJavascript(styles string) error {
|
||||||
|
return _datastore.SetString(customJavascriptKey, styles)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomJavascript will return a string with Javascript to insert into the page.
|
||||||
|
func GetCustomJavascript() string {
|
||||||
|
style, err := _datastore.GetString(customJavascriptKey)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
|
||||||
// SetVideoCodec will set the codec used for video encoding.
|
// SetVideoCodec will set the codec used for video encoding.
|
||||||
func SetVideoCodec(codec string) error {
|
func SetVideoCodec(codec string) error {
|
||||||
return _datastore.SetString(videoCodecKey, codec)
|
return _datastore.SetString(videoCodecKey, codec)
|
||||||
|
@ -39,6 +39,9 @@ func Start() error {
|
|||||||
http.HandleFunc("/preview.gif", controllers.GetPreview)
|
http.HandleFunc("/preview.gif", controllers.GetPreview)
|
||||||
http.HandleFunc("/logo", controllers.GetLogo)
|
http.HandleFunc("/logo", controllers.GetLogo)
|
||||||
|
|
||||||
|
// Custom Javascript
|
||||||
|
http.HandleFunc("/customjavascript", controllers.ServeCustomJavascript)
|
||||||
|
|
||||||
// Return a single emoji image.
|
// Return a single emoji image.
|
||||||
http.HandleFunc(config.EmojiDir, controllers.GetCustomEmojiImage)
|
http.HandleFunc(config.EmojiDir, controllers.GetCustomEmojiImage)
|
||||||
|
|
||||||
@ -315,6 +318,9 @@ func Start() error {
|
|||||||
// set custom style css
|
// set custom style css
|
||||||
http.HandleFunc("/api/admin/config/customstyles", middleware.RequireAdminAuth(admin.SetCustomStyles))
|
http.HandleFunc("/api/admin/config/customstyles", middleware.RequireAdminAuth(admin.SetCustomStyles))
|
||||||
|
|
||||||
|
// set custom style javascript
|
||||||
|
http.HandleFunc("/api/admin/config/customjavascript", middleware.RequireAdminAuth(admin.SetCustomJavascript))
|
||||||
|
|
||||||
// Video playback metrics
|
// Video playback metrics
|
||||||
http.HandleFunc("/api/admin/metrics/video", middleware.RequireAdminAuth(admin.GetVideoPlaybackMetrics))
|
http.HandleFunc("/api/admin/metrics/video", middleware.RequireAdminAuth(admin.GetVideoPlaybackMetrics))
|
||||||
|
|
||||||
|
@ -132,6 +132,8 @@ const newFederationConfig = {
|
|||||||
const newHideViewerCount = !defaultHideViewerCount;
|
const newHideViewerCount = !defaultHideViewerCount;
|
||||||
|
|
||||||
const overriddenWebsocketHost = 'ws://lolcalhost.biz';
|
const overriddenWebsocketHost = 'ws://lolcalhost.biz';
|
||||||
|
const customCSS = randomString();
|
||||||
|
const customJavascript = randomString();
|
||||||
|
|
||||||
test('verify default config values', async (done) => {
|
test('verify default config values', async (done) => {
|
||||||
const res = await request.get('/api/config');
|
const res = await request.get('/api/config');
|
||||||
@ -315,6 +317,16 @@ test('set custom style values', async (done) => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('set custom css', async (done) => {
|
||||||
|
await sendAdminRequest('config/customstyles', customCSS);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('set custom javascript', async (done) => {
|
||||||
|
await sendAdminRequest('config/customjavascript', customJavascript);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
test('enable directory', async (done) => {
|
test('enable directory', async (done) => {
|
||||||
const res = await sendAdminRequest('config/directoryenabled', true);
|
const res = await sendAdminRequest('config/directoryenabled', true);
|
||||||
done();
|
done();
|
||||||
@ -367,6 +379,7 @@ test('verify updated config values', async (done) => {
|
|||||||
expect(res.body.logo).toBe('/logo');
|
expect(res.body.logo).toBe('/logo');
|
||||||
expect(res.body.socialHandles).toStrictEqual(newSocialHandles);
|
expect(res.body.socialHandles).toStrictEqual(newSocialHandles);
|
||||||
expect(res.body.socketHostOverride).toBe(overriddenWebsocketHost);
|
expect(res.body.socketHostOverride).toBe(overriddenWebsocketHost);
|
||||||
|
expect(res.body.customStyles).toBe(customCSS);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -393,6 +406,9 @@ test('verify updated admin configuration', async (done) => {
|
|||||||
expect(res.body.instanceDetails.socialHandles).toStrictEqual(
|
expect(res.body.instanceDetails.socialHandles).toStrictEqual(
|
||||||
newSocialHandles
|
newSocialHandles
|
||||||
);
|
);
|
||||||
|
expect(res.body.instanceDetails.customStyles).toBe(customCSS);
|
||||||
|
expect(res.body.instanceDetails.customJavascript).toBe(customJavascript);
|
||||||
|
|
||||||
expect(res.body.forbiddenUsernames).toStrictEqual(newForbiddenUsernames);
|
expect(res.body.forbiddenUsernames).toStrictEqual(newForbiddenUsernames);
|
||||||
expect(res.body.streamKeys).toStrictEqual(newStreamKeys);
|
expect(res.body.streamKeys).toStrictEqual(newStreamKeys);
|
||||||
expect(res.body.socketHostOverride).toBe(overriddenWebsocketHost);
|
expect(res.body.socketHostOverride).toBe(overriddenWebsocketHost);
|
||||||
|
118
web/components/admin/EditCustomJavascript.tsx
Normal file
118
web/components/admin/EditCustomJavascript.tsx
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import React, { useState, useEffect, useContext, FC } from 'react';
|
||||||
|
import { Typography, Button } from 'antd';
|
||||||
|
import CodeMirror from '@uiw/react-codemirror';
|
||||||
|
import { bbedit } from '@uiw/codemirror-theme-bbedit';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
|
||||||
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
|
import {
|
||||||
|
postConfigUpdateToAPI,
|
||||||
|
RESET_TIMEOUT,
|
||||||
|
API_CUSTOM_JAVASCRIPT,
|
||||||
|
} from '../../utils/config-constants';
|
||||||
|
import {
|
||||||
|
createInputStatus,
|
||||||
|
StatusState,
|
||||||
|
STATUS_ERROR,
|
||||||
|
STATUS_PROCESSING,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
} from '../../utils/input-statuses';
|
||||||
|
import { FormStatusIndicator } from './FormStatusIndicator';
|
||||||
|
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const EditCustomJavascript: FC = () => {
|
||||||
|
const [content, setContent] = useState('/* Enter custom Javascript here */');
|
||||||
|
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||||
|
const [hasChanged, setHasChanged] = useState(false);
|
||||||
|
|
||||||
|
const serverStatusData = useContext(ServerStatusContext);
|
||||||
|
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
||||||
|
|
||||||
|
const { instanceDetails } = serverConfig;
|
||||||
|
const { customJavascript: initialContent } = instanceDetails;
|
||||||
|
|
||||||
|
let resetTimer = null;
|
||||||
|
|
||||||
|
// Clear out any validation states and messaging
|
||||||
|
const resetStates = () => {
|
||||||
|
setSubmitStatus(null);
|
||||||
|
setHasChanged(false);
|
||||||
|
clearTimeout(resetTimer);
|
||||||
|
resetTimer = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// posts all the tags at once as an array obj
|
||||||
|
async function handleSave() {
|
||||||
|
setSubmitStatus(createInputStatus(STATUS_PROCESSING));
|
||||||
|
await postConfigUpdateToAPI({
|
||||||
|
apiPath: API_CUSTOM_JAVASCRIPT,
|
||||||
|
data: { value: content },
|
||||||
|
onSuccess: (message: string) => {
|
||||||
|
setFieldInConfigState({
|
||||||
|
fieldName: 'customJavascript',
|
||||||
|
value: content,
|
||||||
|
path: 'instanceDetails',
|
||||||
|
});
|
||||||
|
setSubmitStatus(createInputStatus(STATUS_SUCCESS, message));
|
||||||
|
},
|
||||||
|
onError: (message: string) => {
|
||||||
|
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setContent(initialContent);
|
||||||
|
}, [instanceDetails]);
|
||||||
|
|
||||||
|
const onCSSValueChange = React.useCallback(value => {
|
||||||
|
setContent(value);
|
||||||
|
if (value !== initialContent && !hasChanged) {
|
||||||
|
setHasChanged(true);
|
||||||
|
} else if (value === initialContent && hasChanged) {
|
||||||
|
setHasChanged(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="edit-custom-css">
|
||||||
|
<Title level={3} className="section-title">
|
||||||
|
Customize your page styling with CSS
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<p className="description">
|
||||||
|
Customize the look and feel of your Owncast instance by overriding the CSS styles of various
|
||||||
|
components on the page. Refer to the{' '}
|
||||||
|
<a href="https://owncast.online/docs/website/" rel="noopener noreferrer" target="_blank">
|
||||||
|
CSS & Components guide
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
<p className="description">
|
||||||
|
Please input plain CSS text, as this will be directly injected onto your page during load.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<CodeMirror
|
||||||
|
value={content}
|
||||||
|
placeholder="/* Enter custom Javascript here */"
|
||||||
|
theme={bbedit}
|
||||||
|
height="200px"
|
||||||
|
extensions={[javascript()]}
|
||||||
|
onChange={onCSSValueChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div className="page-content-actions">
|
||||||
|
{hasChanged && (
|
||||||
|
<Button type="primary" onClick={handleSave}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<FormStatusIndicator status={submitStatus} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -5,6 +5,7 @@ import Head from 'next/head';
|
|||||||
import { FC, useEffect, useRef } from 'react';
|
import { FC, useEffect, useRef } from 'react';
|
||||||
import { Layout } from 'antd';
|
import { Layout } from 'antd';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
import Script from 'next/script';
|
||||||
import {
|
import {
|
||||||
ClientConfigStore,
|
ClientConfigStore,
|
||||||
isChatAvailableSelector,
|
isChatAvailableSelector,
|
||||||
@ -133,6 +134,8 @@ export const Main: FC = () => {
|
|||||||
<PushNotificationServiceWorker />
|
<PushNotificationServiceWorker />
|
||||||
<TitleNotifier name={name} />
|
<TitleNotifier name={name} />
|
||||||
<Theme />
|
<Theme />
|
||||||
|
<Script strategy="afterInteractive" src="/customjavascript" />
|
||||||
|
|
||||||
<Layout ref={layoutRef} className={styles.layout}>
|
<Layout ref={layoutRef} className={styles.layout}>
|
||||||
<Header name={title || name} chatAvailable={isChatAvailable} chatDisabled={chatDisabled} />
|
<Header name={title || name} chatAvailable={isChatAvailable} chatDisabled={chatDisabled} />
|
||||||
<Content />
|
<Content />
|
||||||
|
@ -43,6 +43,10 @@ module.exports = withBundleAnalyzer(
|
|||||||
source: '/thumbnail.jpg',
|
source: '/thumbnail.jpg',
|
||||||
destination: 'http://localhost:8080/thumbnail.jpg', // Proxy to Backend to work around CORS.
|
destination: 'http://localhost:8080/thumbnail.jpg', // Proxy to Backend to work around CORS.
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
source: '/customjavascript',
|
||||||
|
destination: 'http://localhost:8080/customjavascript', // Proxy to Backend to work around CORS.
|
||||||
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
pageExtensions: ['tsx'],
|
pageExtensions: ['tsx'],
|
||||||
|
1
web/package-lock.json
generated
1
web/package-lock.json
generated
@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "4.8.0",
|
"@ant-design/icons": "4.8.0",
|
||||||
"@codemirror/lang-css": "6.0.1",
|
"@codemirror/lang-css": "6.0.1",
|
||||||
|
"@codemirror/lang-javascript": "^6.1.2",
|
||||||
"@codemirror/lang-markdown": "6.0.5",
|
"@codemirror/lang-markdown": "6.0.5",
|
||||||
"@codemirror/language-data": "6.1.0",
|
"@codemirror/language-data": "6.1.0",
|
||||||
"@fontsource/open-sans": "4.5.13",
|
"@fontsource/open-sans": "4.5.13",
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "4.8.0",
|
"@ant-design/icons": "4.8.0",
|
||||||
"@codemirror/lang-css": "6.0.1",
|
"@codemirror/lang-css": "6.0.1",
|
||||||
|
"@codemirror/lang-javascript": "^6.1.2",
|
||||||
"@codemirror/lang-markdown": "6.0.5",
|
"@codemirror/lang-markdown": "6.0.5",
|
||||||
"@codemirror/language-data": "6.1.0",
|
"@codemirror/language-data": "6.1.0",
|
||||||
"@fontsource/open-sans": "4.5.13",
|
"@fontsource/open-sans": "4.5.13",
|
||||||
@ -53,11 +54,11 @@
|
|||||||
"ua-parser-js": "1.0.32",
|
"ua-parser-js": "1.0.32",
|
||||||
"video.js": "7.20.3",
|
"video.js": "7.20.3",
|
||||||
"xstate": "4.35.2",
|
"xstate": "4.35.2",
|
||||||
"yaml": "2.2.1"
|
"yaml": "2.2.1",
|
||||||
|
"@next/bundle-analyzer": "^13.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.20.12",
|
"@babel/core": "7.20.12",
|
||||||
"style-dictionary": "3.7.2",
|
|
||||||
"@mdx-js/react": "2.2.1",
|
"@mdx-js/react": "2.2.1",
|
||||||
"@storybook/addon-a11y": "6.5.15",
|
"@storybook/addon-a11y": "6.5.15",
|
||||||
"@storybook/addon-actions": "6.5.15",
|
"@storybook/addon-actions": "6.5.15",
|
||||||
@ -73,8 +74,6 @@
|
|||||||
"@storybook/preset-scss": "1.0.3",
|
"@storybook/preset-scss": "1.0.3",
|
||||||
"@storybook/react": "6.5.15",
|
"@storybook/react": "6.5.15",
|
||||||
"@storybook/testing-library": "0.0.13",
|
"@storybook/testing-library": "0.0.13",
|
||||||
"storybook-addon-designs": "6.3.1",
|
|
||||||
"storybook-addon-fetch-mock": "1.0.1",
|
|
||||||
"@svgr/webpack": "6.5.1",
|
"@svgr/webpack": "6.5.1",
|
||||||
"@types/chart.js": "2.9.37",
|
"@types/chart.js": "2.9.37",
|
||||||
"@types/classnames": "2.3.1",
|
"@types/classnames": "2.3.1",
|
||||||
@ -108,10 +107,12 @@
|
|||||||
"sass": "1.57.1",
|
"sass": "1.57.1",
|
||||||
"sass-loader": "13.2.0",
|
"sass-loader": "13.2.0",
|
||||||
"sb": "6.5.15",
|
"sb": "6.5.15",
|
||||||
|
"storybook-addon-designs": "6.3.1",
|
||||||
|
"storybook-addon-fetch-mock": "1.0.1",
|
||||||
"storybook-dark-mode": "2.0.5",
|
"storybook-dark-mode": "2.0.5",
|
||||||
"storybook-preset-less": "1.1.3",
|
"storybook-preset-less": "1.1.3",
|
||||||
|
"style-dictionary": "3.7.2",
|
||||||
"style-loader": "3.3.1",
|
"style-loader": "3.3.1",
|
||||||
"typescript": "4.9.4",
|
"typescript": "4.9.4"
|
||||||
"@next/bundle-analyzer": "^13.1.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import GeneralConfig from '../../../../components/admin/config/general/GeneralCo
|
|||||||
import AppearanceConfig from '../../../../components/admin/config/general/AppearanceConfig';
|
import AppearanceConfig from '../../../../components/admin/config/general/AppearanceConfig';
|
||||||
|
|
||||||
import { AdminLayout } from '../../../../components/layouts/AdminLayout';
|
import { AdminLayout } from '../../../../components/layouts/AdminLayout';
|
||||||
|
import { EditCustomJavascript } from '../../../../components/admin/EditCustomJavascript';
|
||||||
|
|
||||||
export default function PublicFacingDetails() {
|
export default function PublicFacingDetails() {
|
||||||
return (
|
return (
|
||||||
@ -23,6 +24,11 @@ export default function PublicFacingDetails() {
|
|||||||
key: '2',
|
key: '2',
|
||||||
children: <AppearanceConfig />,
|
children: <AppearanceConfig />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: `Custom Scripting`,
|
||||||
|
key: '3',
|
||||||
|
children: <EditCustomJavascript />,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,6 +29,7 @@ export interface ConfigDirectoryFields {
|
|||||||
|
|
||||||
export interface ConfigInstanceDetailsFields {
|
export interface ConfigInstanceDetailsFields {
|
||||||
customStyles: string;
|
customStyles: string;
|
||||||
|
customJavascript: string;
|
||||||
extraPageContent: string;
|
extraPageContent: string;
|
||||||
logo: string;
|
logo: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -11,6 +11,7 @@ export const RESET_TIMEOUT = 3000;
|
|||||||
// CONFIG API ENDPOINTS
|
// CONFIG API ENDPOINTS
|
||||||
export const API_CUSTOM_CONTENT = '/pagecontent';
|
export const API_CUSTOM_CONTENT = '/pagecontent';
|
||||||
export const API_CUSTOM_CSS_STYLES = '/customstyles';
|
export const API_CUSTOM_CSS_STYLES = '/customstyles';
|
||||||
|
export const API_CUSTOM_JAVASCRIPT = '/customjavascript';
|
||||||
export const API_FFMPEG = '/ffmpegpath';
|
export const API_FFMPEG = '/ffmpegpath';
|
||||||
export const API_INSTANCE_URL = '/serverurl';
|
export const API_INSTANCE_URL = '/serverurl';
|
||||||
export const API_LOGO = '/logo';
|
export const API_LOGO = '/logo';
|
||||||
|
@ -12,6 +12,7 @@ export const initialServerConfigState: ConfigDetails = {
|
|||||||
adminPassword: '',
|
adminPassword: '',
|
||||||
instanceDetails: {
|
instanceDetails: {
|
||||||
customStyles: '',
|
customStyles: '',
|
||||||
|
customJavascript: '',
|
||||||
extraPageContent: '',
|
extraPageContent: '',
|
||||||
logo: '',
|
logo: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user