Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
tuj committed Apr 25, 2024
2 parents 52b8f8f + 96dd619 commit 68991fe
Show file tree
Hide file tree
Showing 19 changed files with 195 additions and 85 deletions.
2 changes: 2 additions & 0 deletions .docker/vhost.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ server {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://node:3000;
add_header Cache-Control "public, max-age=604800";
expires 7d;
}
}
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

All notable changes to this project will be documented in this file.

## [2.0.2] - 2024-04-25

- [#123](https://github.com/os2display/display-client/pull/123)
- Ensured real ip is logged in nginx.
- [#124](https://github.com/os2display/display-client/pull/124)
- Changed to apply max-age 7d to all files and added cache busting to config.json and release.json.
- Added "loginCheckTimeout", "configFetchInterval", "refreshTokenTimeout", "releaseTimestampIntervalTimeout" to config.json.
- Simplified config.json.
- [#122](https://github.com/os2display/display-client/pull/122)
- Added max-age and expires 1 hour to config.json and release.json.
- [#121](https://github.com/os2display/display-client/pull/120)
- Add releaseVersion, releaseTimestamp and screenId searchParams when starting app.

## [2.0.1] - 2024-04-10

- [#120](https://github.com/os2display/display-client/pull/120)
Expand Down
48 changes: 24 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@ See [https://github.com/os2display/display-docs/blob/main/client.md](https://git

## Config
The client can be configured by creating `public/config.json` with relevant values.
See `public/example_config.json` for values.

```json
{
"apiEndpoint": "",
"authenticationEndpoint": "/v2/authentication/screen",
"authenticationRefreshTokenEndpoint": "/v2/authentication/token/refresh",
"dataStrategy": {
"type": "pull",
"config": {
"interval": 30000,
"endpoint": ""
}
},
"colorScheme": {
"type": "library",
"lat": 56.0,
"lng": 10.0
},
"schedulingInterval": 60000,
"debug": false
}
```
All endpoint should be configured with out a trailing slash. The endpoints `apiEndpoint` and `dataStrategy.config.endpoint` can be
See `public/example_config.json` for example values.

Values explained:

* apiEndpoint - The endpoint where the API is located.
* loginCheckTimeout - How often (milliseconds) should the screen check for
status when it is not logged in, and waiting for being activated in the
administration.
* configFetchInterval - How often (milliseconds) should a fresh
config.json be fetched.
* refreshTokenTimeout - How often (milliseconds) should it be checked
whether the token needs to be refreshed?
* releaseTimestampIntervalTimeout - How often (milliseconds) should the
code check if a new release has been deployed, and reload if true?
* dataStrategy.config.interval - How often (milliseconds) should data be fetched
for the logged in screen?
* colorScheme.lat - Where is the screen located? Used for darkmode.
* colorScheme.lng - Where is the screen located? Used for darkmode.
* schedulingInterval - How often (milliseconds) should scheduling for the
screen be checked.
* debug - Should the screen be in debug mode? If true, the cursor will be
invisible.

All endpoint should be configured without a trailing slash. The endpoints `apiEndpoint` can be
left empty if the api is hosted from the root of the same domain as the client. E.g. if the api is at https://example.org and the client is at
https://example.org/client

Expand Down
9 changes: 5 additions & 4 deletions infrastructure/itkdev/etc/confd/templates/config.tmpl
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"apiEndpoint": "{{ getenv "APP_API_ENDPOINT" "" }}",
"authenticationEndpoint": "{{ getenv "APP_API_AUTHENTICATION_ENDPOINT" "/v2/authentication/token" }}",
"authenticationRefreshTokenEndpoint": "{{ getenv "APP_API_AUTHENTICATION_REFRESH_ENDPOINT" "/v2/authentication/token/refresh" }}",
"loginCheckTimeout": {{ getenv "APP_LOGIN_CHECK_TIMEOUT" "20000" }},
"configFetchInterval": {{ getenv "APP_CONFIG_FETCH_INTERVAL" "600000" }},
"refreshTokenTimeout": {{ getenv "APP_REFRESH_TOKEN_TIMEOUT" "60000" }},
"releaseTimestampIntervalTimeout": {{ getenv "APP_RELEASE_TIMESTAMP_INTERVAL_TIMEOUT" "600000" }},
"dataStrategy": {
"type": "pull",
"config": {
"interval": {{ getenv "APP_DATA_PULL_INTERVAL" "30000" }},
"endpoint": "{{ getenv "APP_API_PATH" "" }}"
"interval": {{ getenv "APP_DATA_PULL_INTERVAL" "30000" }}
}
},
"colorScheme": {
Expand Down
2 changes: 2 additions & 0 deletions infrastructure/itkdev/etc/confd/templates/default.conf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ server {
rewrite ^{{ getenv "APP_SCREEN_CLIENT_PATH" "/" }}(.*) /$1 break;
index index.html;
autoindex off;
add_header Cache-Control "public, max-age=604800";
expires 7d;
try_files $uri $uri/ =404;
}

Expand Down
6 changes: 5 additions & 1 deletion infrastructure/itkdev/etc/confd/templates/nginx.conf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
set_real_ip_from 172.16.0.0/8;
real_ip_recursive on;
real_ip_header X-Forwarded-For;

log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

Expand Down
23 changes: 23 additions & 0 deletions infrastructure/os2display/Readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
# OS2display image build

This folder contains the infrastructure files for building the `os2display/display-client` image.

## Environment variables that can be set

### config.json

* APP_API_ENDPOINT - The endpoint where the API can be called.
* APP_LOGIN_CHECK_TIMEOUT - How often (milliseconds) should the screen check for
status when it is not logged in, and waiting for being activated in the
administration.
* APP_CONFIG_FETCH_INTERVAL - How often (milliseconds) should a fresh
config.json be fetched.
* APP_REFRESH_TOKEN_TIMEOUT - How often (milliseconds) should it be checked
whether the token needs to be refreshed?
* APP_RELEASE_TIMESTAMP_INTERVAL_TIMEOUT - How often (milliseconds) should the
code check if a new release has been deployed, and reload if true?
* APP_DATA_PULL_INTERVAL - How often (milliseconds) should data be fetched for
the logged in screen?
* APP_CLIENT_LATITUDE - Where is the screen located? Used for darkmode.
* APP_CLIENT_LONGITUDE - Where is the screen located? Used for darkmode.
* APP_SCHEDULING_INTERVAL - How often (milliseconds) should scheduling for the
screen be checked.
* APP_DEBUG - Should the screen be in debug mode? If true, the cursor will be
invisible.
9 changes: 5 additions & 4 deletions infrastructure/os2display/etc/confd/templates/config.tmpl
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"apiEndpoint": "{{ getenv "APP_API_ENDPOINT" "/" }}",
"authenticationEndpoint": "{{ getenv "APP_API_AUTHENTICATION_ENDPOINT" "/v2/authentication/token" }}",
"authenticationRefreshTokenEndpoint": "{{ getenv "APP_API_AUTHENTICATION_REFRESH_ENDPOINT" "/v2/authentication/token/refresh" }}",
"loginCheckTimeout": {{ getenv "APP_LOGIN_CHECK_TIMEOUT" "20000" }},
"configFetchInterval": {{ getenv "APP_CONFIG_FETCH_INTERVAL" "600000" }},
"refreshTokenTimeout": {{ getenv "APP_REFRESH_TOKEN_TIMEOUT" "60000" }},
"releaseTimestampIntervalTimeout": {{ getenv "APP_RELEASE_TIMESTAMP_INTERVAL_TIMEOUT" "600000" }},
"dataStrategy": {
"type": "pull",
"config": {
"interval": {{ getenv "APP_DATA_PULL_INTERVAL" "30000" }},
"endpoint": "{{ getenv "APP_API_PATH" "/" }}"
"interval": {{ getenv "APP_DATA_PULL_INTERVAL" "30000" }}
}
},
"colorScheme": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ server {
rewrite ^{{ getenv "APP_SCREEN_CLIENT_PATH" "/" }}(.*) /$1 break;
index index.html;
autoindex off;
add_header Cache-Control "public, max-age=604800";
expires 7d;
try_files $uri $uri/ =404;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
set_real_ip_from 172.16.0.0/8;
real_ip_recursive on;
real_ip_header X-Forwarded-For;

log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

Expand Down
19 changes: 13 additions & 6 deletions public/example_config.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"apiEndpoint": "",
"authenticationEndpoint": "/v2/authentication/screen",
"authenticationRefreshTokenEndpoint": "/v2/authentication/token/refresh",
"apiEndpoint": "https://os2display.example.org",
"loginCheckTimeout": 20000,
"configFetchInterval": 90000,
"refreshTokenTimeout": 900000,
"releaseTimestampIntervalTimeout": 600000,
"dataStrategy": {
"type": "pull",
"config": {
"interval": 30000,
"endpoint": ""
"interval": 30000
}
},
"colorScheme": {
Expand All @@ -15,5 +16,11 @@
"lng": 10.0
},
"schedulingInterval": 60000,
"debug": false
"debug": false,
"logging": [
{
"transport": "console",
"level": "info"
}
]
}
68 changes: 55 additions & 13 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Logger from './logger/logger';
import './app.scss';
import localStorageKeys from './local-storage-keys';
import fallback from './assets/fallback.png';
import idFromPath from './id-from-path';

/**
* App component.
Expand All @@ -20,9 +21,9 @@ function App() {
localStorageKeys.FALLBACK_IMAGE
);

const loginCheckTimeout = 15 * 1000;
const refreshTimeout = 60 * 1000;
const releaseTimestampIntervalTimeout = 1000 * 60 * 5;
const loginCheckTimeoutDefault = 20 * 1000;
const refreshTokenTimeoutDefault = 60 * 1000 * 15;
const releaseTimestampIntervalTimeoutDefault = 1000 * 60 * 10;

const [running, setRunning] = useState(false);
const [screen, setScreen] = useState('');
Expand Down Expand Up @@ -96,7 +97,7 @@ function App() {
Logger.log('info', 'Refreshing token.');

ConfigLoader.loadConfig().then((config) => {
fetch(config.authenticationRefreshTokenEndpoint, {
fetch(`${config.apiEndpoint}/v2/authentication/token/refresh`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down Expand Up @@ -167,8 +168,13 @@ function App() {
})
);

// Start refresh token interval.
refreshTokenIntervalRef.current = setInterval(checkToken, refreshTimeout);
ConfigLoader.loadConfig().then((config) => {
// Start refresh token interval.
refreshTokenIntervalRef.current = setInterval(
checkToken,
config.refreshTokenTimeout ?? refreshTokenTimeoutDefault
);
});
};

const checkLogin = () => {
Expand All @@ -181,7 +187,7 @@ function App() {
startContent(localScreenId);
} else {
ConfigLoader.loadConfig().then((config) => {
fetch(config.authenticationEndpoint, {
fetch(`${config.apiEndpoint}/v2/authentication/screen`, {
method: 'POST',
mode: 'cors',
credentials: 'include',
Expand Down Expand Up @@ -224,15 +230,21 @@ function App() {
clearTimeout(timeoutRef.current);
}

timeoutRef.current = setTimeout(checkLogin, loginCheckTimeout);
timeoutRef.current = setTimeout(
checkLogin,
config.loginCheckTimeout ?? loginCheckTimeoutDefault
);
}
})
.catch(() => {
if (timeoutRef.current !== null) {
clearTimeout(timeoutRef.current);
}

timeoutRef.current = setTimeout(checkLogin, loginCheckTimeout);
timeoutRef.current = setTimeout(
checkLogin,
config.loginCheckTimeout ?? loginCheckTimeoutDefault
);
});
});
}
Expand Down Expand Up @@ -302,6 +314,24 @@ function App() {
};

useEffect(() => {
const currentUrl = new URL(window.location.href);

// Make sure have releaseVersion and releaseTimestamp set in url parameters.
if (
!currentUrl.searchParams.has('releaseVersion') ||
!currentUrl.searchParams.has('releaseTimestamp')
) {
ReleaseLoader.loadConfig().then((release) => {
currentUrl.searchParams.set(
'releaseTimestamp',
release.releaseTimestamp
);
currentUrl.searchParams.set('releaseVersion', release.releaseVersion);

window.history.replaceState(null, '', currentUrl);
});
}

document.addEventListener('screen', screenHandler);
document.addEventListener('reauthenticate', reauthenticateHandler);
document.addEventListener('contentEmpty', contentEmpty);
Expand All @@ -312,10 +342,13 @@ function App() {

checkForUpdates();

releaseTimestampIntervalRef.current = setInterval(
checkForUpdates,
releaseTimestampIntervalTimeout
);
ConfigLoader.loadConfig().then((config) => {
releaseTimestampIntervalRef.current = setInterval(
checkForUpdates,
config.releaseTimestampIntervalTimeout ??
releaseTimestampIntervalTimeoutDefault
);
});

return function cleanup() {
Logger.log('info', 'Unmounting App.');
Expand All @@ -341,6 +374,15 @@ function App() {
}, []);

useEffect(() => {
// Append screenId to current url for easier debugging. If errors are logged in the API's standard http log this
// makes it easy to see what screen client has made the http call by putting the screen id in the referer http
// header.
if (screen && screen['@id']) {
const url = new URL(window.location.href);
url.searchParams.set('screenId', idFromPath(screen['@id']));
window.history.replaceState(null, '', url);
}

ConfigLoader.loadConfig().then((config) => {
const token = localStorage.getItem(localStorageKeys.API_TOKEN);
const tenantKey = localStorage.getItem(localStorageKeys.TENANT_KEY);
Expand Down
Loading

0 comments on commit 68991fe

Please sign in to comment.