Skip to content

Commit dd9f984

Browse files
Added Keycloak integration (#34)
1 parent d833a45 commit dd9f984

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1921
-716
lines changed

docker/Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@ COPY frontend/ ./
1414
# Define build arguments
1515
ARG ML_SERVICE_URL
1616
ARG ML_WEB_ROOT_PATH
17+
ARG ML_OR_KEYCLOAK_URL
18+
ARG ML_OR_URL
1719

1820
RUN ML_SERVICE_URL=${ML_SERVICE_URL:-/services/ml-forecast} \
1921
ML_WEB_ROOT_PATH=${ML_WEB_ROOT_PATH:-/services/ml-forecast/ui} \
22+
ML_OR_KEYCLOAK_URL=${ML_OR_KEYCLOAK_URL:-/auth} \
23+
ML_OR_URL=${ML_OR_URL:-} \
2024
npm run build:prod
2125

2226
# --- Python Build Phase -------------------------------------------------------

docker/docker-compose.yml

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,21 @@ services:
66
context: ..
77
dockerfile: docker/Dockerfile
88
args:
9-
ML_SERVICE_URL: ${ML_SERVICE_URL:-/services/ml-forecast} # Url to reach the back-end service, should be the same as ML_API_ROOT_PATH
10-
ML_WEB_ROOT_PATH: ${ML_WEB_ROOT_PATH:-/services/ml-forecast/ui} # Public path for the front-end (e.g. when behind a reverse proxy)
9+
ML_OR_URL: ${ML_OR_URL} # OpenRemote URL
10+
ML_OR_KEYCLOAK_URL: ${ML_OR_KEYCLOAK_URL} # OpenRemote Keycloak URL
11+
ML_SERVICE_URL: ${ML_SERVICE_URL} # Url to reach the back-end service, should be the same as ML_API_ROOT_PATH
12+
ML_WEB_ROOT_PATH: ${ML_WEB_ROOT_PATH} # Public path for the front-end (e.g. when behind a reverse proxy)
1113
container_name: service-ml-forecast
1214
ports:
1315
- "8000:8000"
1416
environment:
15-
- ML_LOG_LEVEL=${ML_LOG_LEVEL:-INFO} # Log level to use
16-
- ML_ENVIRONMENT=${ML_ENVIRONMENT:-production} # Environment to run the service in
17-
- ML_API_ROOT_PATH=${ML_API_ROOT_PATH:-/services/ml-forecast} # Public path for the back-end (e.g. when behind a reverse proxy)
18-
- ML_OR_URL=${ML_OR_URL:-http://host.docker.internal:8080} # OpenRemote URL
19-
- ML_OR_KEYCLOAK_URL=${ML_OR_KEYCLOAK_URL:-http://host.docker.internal:8081} # OpenRemote Keycloak URL
20-
- ML_OR_SERVICE_USER=${ML_OR_SERVICE_USER:-serviceuser} # OpenRemote service user
21-
- ML_OR_SERVICE_USER_SECRET=${ML_OR_SERVICE_USER_SECRET:-secret} # OpenRemote service user secret
17+
- ML_LOG_LEVEL=${ML_LOG_LEVEL} # Log level to use
18+
- ML_ENVIRONMENT=${ML_ENVIRONMENT} # Environment to run the service in
19+
- ML_API_ROOT_PATH=${ML_API_ROOT_PATH} # Public path for the back-end (e.g. when behind a reverse proxy)
20+
- ML_OR_URL=${ML_OR_URL} # OpenRemote URL
21+
- ML_OR_KEYCLOAK_URL=${ML_OR_KEYCLOAK_URL} # OpenRemote Keycloak URL
22+
- ML_OR_SERVICE_USER=${ML_OR_SERVICE_USER} # OpenRemote service user
23+
- ML_OR_SERVICE_USER_SECRET=${ML_OR_SERVICE_USER_SECRET} # OpenRemote service user secret
2224
volumes:
2325
# Model storage
2426
- ../deployment/data/models:/app/deployment/data/models

frontend/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<!-- Prevent index.html from being cached -->
7+
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
8+
<meta http-equiv="Pragma" content="no-cache" />
9+
<meta http-equiv="Expires" content="0" />
610
<link rel="icon" href="<%= templateRootPath %>/assets/images/logo.svg" />
711
<style>
812
/* Global styles */

frontend/package-lock.json

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"serve": "cross-env rspack serve",
8-
"build:dev": "cross-env rspack build --mode development",
9-
"build:prod": "cross-env SERVICE_URL=${SERVICE_URL:-/services/ml-forecast} ML_WEB_ROOT_PATH=${ML_WEB_ROOT_PATH:-/services/ml-forecast/ui} rspack build --mode production",
8+
"build:prod": "cross-env ML_OR_KEYCLOAK_URL=${ML_OR_KEYCLOAK_URL:-/auth} ML_OR_URL=${ML_OR_URL} ML_SERVICE_URL=${ML_SERVICE_URL:-/services/ml-forecast} ML_WEB_ROOT_PATH=${ML_WEB_ROOT_PATH:-/services/ml-forecast/ui} rspack build --mode production",
109
"build:analyze": "rspack build --mode production --analyze",
1110
"lint": "eslint && prettier . --check",
1211
"format": "prettier . --write"

frontend/rspack.config.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ const __filename = fileURLToPath(import.meta.url);
77
const __dirname = path.dirname(__filename);
88

99
const isProduction = process.env.NODE_ENV === 'production';
10-
const serviceUrl = process.env.ML_SERVICE_URL || 'http://localhost:8000'; // Default to default dev backend
1110
const rootPath = process.env.ML_WEB_ROOT_PATH;
11+
const serviceUrl = process.env.ML_SERVICE_URL || 'http://localhost:8000'; // Default to default service backend
12+
const keycloakUrl = process.env.ML_OR_KEYCLOAK_URL || 'http://localhost:8081/auth'; // Default to openremote keycloak address
13+
const openremoteUrl = process.env.ML_OR_URL !== undefined ? process.env.ML_OR_URL : 'http://localhost:8080'; // Default to openremote url
1214

1315
export default {
1416
mode: isProduction ? 'production' : 'development',
@@ -20,7 +22,6 @@ export default {
2022
filename: `bundle.[contenthash].js`,
2123
clean: true,
2224
path: path.resolve(__dirname, 'dist'),
23-
// prefix for the bundle, use root path or fallback to '/'
2425
publicPath: rootPath ? rootPath : '/'
2526
},
2627
resolve: {
@@ -65,7 +66,9 @@ export default {
6566
patterns: [{ from: 'assets', to: 'assets' }]
6667
}),
6768
new rspack.DefinePlugin({
68-
'process.env.ML_SERVICE_URL': JSON.stringify(serviceUrl)
69+
'process.env.ML_SERVICE_URL': JSON.stringify(serviceUrl),
70+
'process.env.ML_OR_KEYCLOAK_URL': JSON.stringify(keycloakUrl),
71+
'process.env.ML_OR_URL': JSON.stringify(openremoteUrl)
6972
})
7073
],
7174
devServer: {

frontend/src/common/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,8 @@
1818
export const APP_OUTLET = document.querySelector('#outlet') as HTMLElement;
1919
export const IS_DEVELOPMENT = process.env.NODE_ENV === 'development';
2020
export const ML_SERVICE_URL = (process.env.ML_SERVICE_URL || '').replace(/\/$/, '');
21+
export const ML_OR_URL = process.env.ML_OR_URL || '';
22+
export const ML_OR_KEYCLOAK_URL = process.env.ML_OR_KEYCLOAK_URL || '';
23+
24+
// Returns true if the app is embedded in an iframe
25+
export const IS_EMBEDDED = window.top !== window.self;

frontend/src/common/theme.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,13 @@
1818
import { IconSet, IconSets, OrIconSet, createSvgIconSet } from '@openremote/or-icon';
1919
import { html } from 'lit';
2020
import * as Core from '@openremote/core';
21-
import { APIService } from '../services/api-service';
2221
import { getRootPath } from './util';
22+
import { manager } from '@openremote/core';
2323

2424
/**
25-
* Base theme settings
26-
*/
27-
const BASE_THEME = {
28-
color1: Core.DefaultColor1,
29-
color2: Core.DefaultColor2,
30-
color3: Core.DefaultColor3,
31-
color4: Core.DefaultColor4,
32-
color5: Core.DefaultColor5,
33-
color6: Core.DefaultColor6
34-
};
35-
36-
/**
37-
* Setup the MDI-Icons for or-icon element
25+
* Initialize icon sets, overriding the default createMdiIconSet with a custom implementation to allow custom urls
26+
* Creates a custom MDI icon set that uses static font files from the assets directory
27+
* and registers both MDI and OR icon sets for use with or-icon elements
3828
*/
3929
export function setupORIcons() {
4030
function createMdiIconSet(): IconSet {
@@ -67,6 +57,18 @@ export function setupORIcons() {
6757
IconSets.addIconSet('or', createSvgIconSet(OrIconSet.size, OrIconSet.icons));
6858
}
6959

60+
/**
61+
* Base theme settings
62+
*/
63+
const BASE_THEME = {
64+
color1: Core.DefaultColor1,
65+
color2: Core.DefaultColor2,
66+
color3: Core.DefaultColor3,
67+
color4: Core.DefaultColor4,
68+
color5: Core.DefaultColor5,
69+
color6: Core.DefaultColor6
70+
};
71+
7072
/**
7173
* Theme settings
7274
*/
@@ -92,9 +94,11 @@ export async function setRealmTheme(realm: string) {
9294
}
9395

9496
try {
95-
const config = await APIService.getOpenRemoteRealmConfig(realm);
96-
if (config && config.styles) {
97-
const cssString = config.styles;
97+
const config = (await manager.rest.api.ConfigurationResource.getManagerConfig()).data;
98+
const styles = config.realms?.[realm]?.styles;
99+
100+
if (styles) {
101+
const cssString = styles;
98102
const colorRegex = /--or-app-color(\d+):\s*(#[0-9a-fA-F]{6})/g;
99103
let match: RegExpExecArray | null;
100104

@@ -128,7 +132,6 @@ export async function setRealmTheme(realm: string) {
128132
} catch {
129133
console.warn('Was unable to retrieve realm specific theme settings, falling back to default');
130134
}
131-
132135
setTheme(theme);
133136
}
134137

frontend/src/common/util.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,24 @@
2424
export function getRootPath() {
2525
const scriptElement = document.querySelector('script[src*="bundle"]');
2626

27-
if (scriptElement && scriptElement.getAttribute('src')) {
27+
if (scriptElement?.getAttribute('src')) {
2828
const scriptPath = new URL(scriptElement.getAttribute('src')!, window.location.href).pathname;
2929
// Positive lookahead to match everything up to bundle.js
30-
const match = scriptPath.match(/(.*?)(?=bundle)/);
31-
return match ? (match[1].endsWith('/') ? match[1].slice(0, -1) : match[1]) : '';
30+
const regex = /(.*?)(?=bundle)/;
31+
const match = regex.exec(scriptPath);
32+
let rootPath = '';
33+
if (match) {
34+
rootPath = match[1].endsWith('/') ? match[1].slice(0, -1) : match[1];
35+
}
36+
return rootPath;
3237
}
3338
return '';
3439
}
40+
41+
/**
42+
* Get the realm search param from the url (?realm=)
43+
* @returns The realm search param
44+
*/
45+
export const getRealmSearchParam = () => {
46+
return new URLSearchParams(window.location.search).get('realm');
47+
};

frontend/src/components/configs-table.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
import { OrMwcTable, TableColumn, TableConfig, TableRow } from '@openremote/or-mwc-components/or-mwc-table';
1919
import { css, html, TemplateResult } from 'lit';
2020
import { customElement, property } from 'lit/decorators.js';
21-
import { BasicAsset, ModelConfig } from '../services/models';
21+
import { ModelConfig } from '../services/models';
2222
import { getRootPath } from '../common/util';
2323
import { Router } from '@vaadin/router';
2424
import { InputType } from '@openremote/or-mwc-components/or-mwc-input';
25+
import * as Model from '@openremote/model';
2526

2627
@customElement('configs-table')
2728
export class ConfigsTable extends OrMwcTable {
@@ -88,7 +89,7 @@ export class ConfigsTable extends OrMwcTable {
8889
public modelConfigs: ModelConfig[] = [];
8990

9091
@property({ type: Array })
91-
public configAssets: BasicAsset[] = [];
92+
public configAssets: Model.Asset[] = [];
9293

9394
@property({ type: String })
9495
public realm: string = '';

0 commit comments

Comments
 (0)