diff --git a/Dockerfile b/Dockerfile index 09211830a5..486282fe50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,7 @@ COPY --from=builder /app/apps/client/build ./client/ # Prepare Backend COPY --from=builder /app/apps/server/dist/ ./server/ COPY --from=builder /app/apps/server/src/external/ ./external/ +COPY --from=builder /app/apps/server/src/user/ ./user/ # Export default ports EXPOSE 4001/tcp 8888/udp 9999/udp @@ -32,4 +33,4 @@ CMD ["node", "server/docker.cjs"] # Build and run commands # !!! Note that this command needs pre-build versions of the UI and server apps # docker buildx build . -t getontime/ontime -# docker run -p 4001:4001 -p 8888:8888/udp -p 9999:9999/udp -v ./ontime-db:/external/db/ -v ./ontime-styles:/external/styles/ getontime/ontime +# docker run -p 4001:4001 -p 8888:8888/udp -p 9999:9999/udp -v ./ontime-db:/data/ getontime/ontime diff --git a/apps/client/src/common/api/constants.ts b/apps/client/src/common/api/constants.ts index 3c0873a9c9..dafbf1a976 100644 --- a/apps/client/src/common/api/constants.ts +++ b/apps/client/src/common/api/constants.ts @@ -32,5 +32,6 @@ export const projectDataURL = `${serverURL}/project`; export const rundownURL = `${serverURL}/events`; export const ontimeURL = `${serverURL}/ontime`; -export const stylesPath = 'external/styles/override.css'; -export const overrideStylesURL = `${serverURL}/${stylesPath}`; +export const userAssetsPath = 'user'; +export const cssOverridePath = 'styles/override.css'; +export const overrideStylesURL = `${serverURL}/${userAssetsPath}/${cssOverridePath}`; diff --git a/apps/electron/package.json b/apps/electron/package.json index 8cf4ff550d..d4bb95f593 100644 --- a/apps/electron/package.json +++ b/apps/electron/package.json @@ -118,6 +118,13 @@ "filter": [ "**/*" ] + }, + { + "from": "../server/src/user/", + "to": "extraResources/user/", + "filter": [ + "**/*" + ] } ] } diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index 129f02adb4..dc1c40ced3 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -76,7 +76,9 @@ app.use('/data', appRouter); // router for application data app.use('/api', integrationRouter); // router for integrations // serve static external files -app.use('/external/', express.static(publicDir.externalDir)); +app.use('/external', express.static(publicDir.externalDir)); +app.use('/user', express.static(publicDir.userDir)); + // if the user reaches to the root, we show a 404 app.use('/external', (req, res) => { res.status(404).send(`${req.originalUrl} not found`); diff --git a/apps/server/src/external/README.md b/apps/server/src/external/README.md new file mode 100644 index 0000000000..f38532fdb8 --- /dev/null +++ b/apps/server/src/external/README.md @@ -0,0 +1,9 @@ +# External + +This folder contains resources which will be distributed as part of Ontime's infrastructure. +Folders added here would be available at +``` bash +http://:/external/ +``` + +https://docs.getontime.no/features/custom-views/ diff --git a/apps/server/src/external/demo/README.md b/apps/server/src/external/demo/README.md new file mode 100644 index 0000000000..5b5bb1a2c1 --- /dev/null +++ b/apps/server/src/external/demo/README.md @@ -0,0 +1,3 @@ +## Demo + +This is a demo application which demonstrates how to create a custom view leveraging a websocket client to get data from Ontime. diff --git a/apps/server/src/external/styles/override.css b/apps/server/src/external/styles/override.css deleted file mode 100644 index 0543940103..0000000000 --- a/apps/server/src/external/styles/override.css +++ /dev/null @@ -1,39 +0,0 @@ -:root { - --background-color-override: #ececec; - --color-override: #101010; - --secondary-color-override: #404040; - --accent-color-override: #fa5656; - --label-color-override: #6c6c6c; - --timer-color-override: #202020; - --card-background-color-override: #fff; - --card-background-color-blink-override: #339e4e; - --font-family-override: "Open Sans"; - --font-family-bold-override: "Arial Black"; - --timer-progress-bg-override: #fff; - --timer-progress-override: #202020; - - --external-color-override: #161616; - - --cuesheet-running-bg-override: #D20300; - - --operator-running-bg-override: #D20300; - --operator-highlight-override: #FFAB33; - - --studio-active: #101010; - --studio-idle: #cfcfcf; - --studio-active-label: #101010; - --studio-idle-label: #595959; - --studio-overtime: #101010; - - --lowerThird-font-family-override: 'Times New Roman'; - --lowerThird-top-font-weight-override: bold; - --lowerThird-bottom-font-weight-override: bold; - --lowerThird-top-font-style-override: normal; - --lowerThird-bottom-font-style-override: italic; - --lowerThird-line-height-override: 1vh; - --lowerThird-text-align-override: center; -} - -.timer { - color: black !important; -} diff --git a/apps/server/src/services/sheet-service/SheetService.ts b/apps/server/src/services/sheet-service/SheetService.ts index 02016bf70d..bc237d6ba0 100644 --- a/apps/server/src/services/sheet-service/SheetService.ts +++ b/apps/server/src/services/sheet-service/SheetService.ts @@ -11,14 +11,13 @@ import { sheets, sheets_v4 } from '@googleapis/sheets'; import { Credentials, OAuth2Client } from 'google-auth-library'; import got from 'got'; -import { publicDir } from '../../setup/index.js'; -import { ensureDirectory } from '../../utils/fileManagement.js'; -import { cellRequestFromEvent, type ClientSecret, getA1Notation, validateClientSecret } from './sheetUtils.js'; import { parseExcel } from '../../utils/parser.js'; import { logger } from '../../classes/Logger.js'; import { parseRundown } from '../../utils/parserFunctions.js'; import { getRundown } from '../rundown-service/rundownUtils.js'; +import { cellRequestFromEvent, type ClientSecret, getA1Notation, validateClientSecret } from './sheetUtils.js'; + const sheetScope = 'https://www.googleapis.com/auth/spreadsheets'; const codesUrl = 'https://oauth2.googleapis.com/device/code'; const tokenUrl = 'https://oauth2.googleapis.com/token'; @@ -57,7 +56,6 @@ function reset() { */ export function init() { reset(); - ensureDirectory(publicDir.sheetsDir); } /** diff --git a/apps/server/src/setup/config.ts b/apps/server/src/setup/config.ts index ede8593e14..625df64a3a 100644 --- a/apps/server/src/setup/config.ts +++ b/apps/server/src/setup/config.ts @@ -17,6 +17,7 @@ export const config = { directory: 'sheets', }, restoreFile: 'ontime.restore', + user: 'user', styles: { directory: 'styles', filename: 'override.css', diff --git a/apps/server/src/setup/index.ts b/apps/server/src/setup/index.ts index 0699532552..fc9194f6ed 100644 --- a/apps/server/src/setup/index.ts +++ b/apps/server/src/setup/index.ts @@ -44,6 +44,15 @@ export function getAppDataPath(): string { /** * 1. Paths relative to the installation (or source in development) * ------------------------------------------------------------------ + * src/ + * ├─ projects/ + * ├─ external/ + * │ ├─ demo/ + * ├─ user/ + * │ ├─ styles/ + * │ │ ├─ override.css + * │ ├─ logo/ + * │ │ ├─ logo.png */ /** resolve file URL in both CJS and ESM (build and dev) */ @@ -64,12 +73,12 @@ export const srcDir = { /** Path to the react app */ clientDir: isProduction ? join(srcDirectory, 'client/') : join(srcDirectory, '../../client/build/'), /** Path to the demo app */ - demoDir: join(srcDirectory, '/external/demo/'), + demoDir: join(srcDirectory, 'external', config.demo.directory), } as const; export const srcFiles = { /** Path to bundled CSS */ - cssOverride: join(srcDir.root, '/external/styles/', config.styles.filename), + cssOverride: join(srcDir.root, config.user, config.styles.directory, config.styles.filename), }; /** @@ -91,8 +100,6 @@ const externalsStartDirectory = isProduction ? resolvePublicDirectory : join(src export const publicDir = { root: resolvePublicDirectory, - /** path to sheets folder */ - sheetsDir: join(resolvePublicDirectory, config.sheets.directory), /** path to crash reports folder */ crashDir: join(resolvePublicDirectory, config.crash), /** path to projects folder */ @@ -109,8 +116,10 @@ export const publicDir = { isProduction ? '/external/' : '', // move to external folder in production config.demo.directory, ), + /** path to user folder */ + userDir: join(resolvePublicDirectory, config.user), /** path to external styles override */ - stylesDir: join(externalsStartDirectory, config.styles.directory), + stylesDir: join(resolvePublicDirectory, config.user, config.styles.directory), } as const; /** @@ -123,4 +132,4 @@ export const publicFiles = { restoreFile: join(publicDir.root, config.restoreFile), /** path to CSS override file */ cssOverride: join(publicDir.stylesDir, config.styles.filename), -}; +} as const; diff --git a/apps/server/src/user/README.md b/apps/server/src/user/README.md new file mode 100644 index 0000000000..b8f56977ec --- /dev/null +++ b/apps/server/src/user/README.md @@ -0,0 +1,5 @@ +# user + +The user folder contains assets that are used in Ontime's UI runtime. + +This is likely something you should not change manually. diff --git a/apps/server/src/user/styles/README.md b/apps/server/src/user/styles/README.md new file mode 100644 index 0000000000..e33451d47a --- /dev/null +++ b/apps/server/src/user/styles/README.md @@ -0,0 +1,6 @@ +# styles + +The styles folder contain the user's custom stylesheet which is used for theming in Ontime. +The file should be named `override.css`. + +https://docs.getontime.no/features/custom-styling/ diff --git a/apps/server/src/user/styles/override.css b/apps/server/src/user/styles/override.css new file mode 100644 index 0000000000..1c33108189 --- /dev/null +++ b/apps/server/src/user/styles/override.css @@ -0,0 +1,77 @@ +/** + * This CSS file allows user customisation of the UI + * We expose some CSS properties to facilitate this (see below in :root) + * In the cases where this is missing, you can add your selectors here + */ + +:root { + /** Background colour for the views */ + --background-color-override: #ececec; + + /** Main text colour for the views */ + --color-override: #101010; + + /** Text colour for the views */ + --secondary-color-override: #404040; + + /** Accent text colour, used on active elements */ + --accent-color-override: #fa5656; + + /** Label text colour, used on active elements */ + --label-color-override: #6c6c6c; + + /** Timer text colour */ + --timer-color-override: #202020; + + /** Background for card elements on background */ + --card-background-color-override: #fff; + + /** Background highlight for blink behaviour, used only in /backstage */ + --card-background-color-blink-override: #339e4e; + + /** Font used for all text in views */ + --font-family-override: "Open Sans"; + + /** Font used for clock in /minimal and /clock views */ + --font-family-bold-override: "Arial Black"; + + /** Colour used for progress bar background */ + --timer-progress-bg-override: #fff; + + /** Colour used for progress bar progress */ + --timer-progress-override: #202020; + + /** Colour used for external message and aux timer in /timer */ + --external-color-override: #161616; + + /** View specific features: /cuesheet */ + --cuesheet-running-bg-override: #D20300; + + /** View specific features: /op */ + --operator-running-bg-override: #D20300; + --operator-highlight-override: #FFAB33; + + /** View specific features: /studio */ + --studio-active: #101010; + --studio-idle: #cfcfcf; + --studio-active-label: #101010; + --studio-idle-label: #595959; + --studio-overtime: #101010; + + /** View specific features: /lower */ + --lowerThird-font-family-override: 'Times New Roman'; + --lowerThird-top-font-weight-override: bold; + --lowerThird-bottom-font-weight-override: bold; + --lowerThird-top-font-style-override: normal; + --lowerThird-bottom-font-style-override: italic; + --lowerThird-line-height-override: 1vh; + --lowerThird-text-align-override: center; +} + +/** + * You can inspect the page in your browser and add the selectors here. + * In the below example, we change the colour of the overlay message in the stage-timer view. + */ +.stage-timer > .message-overlay--active > div { + color: red; +} diff --git a/apps/server/test-db/README.md b/apps/server/test-db/README.md new file mode 100644 index 0000000000..8187f0697f --- /dev/null +++ b/apps/server/test-db/README.md @@ -0,0 +1,3 @@ +# DO NOT DELETE +You may be tempted to delete this +The file is used in the file upload test suite in playwright \ No newline at end of file