diff --git a/lib/ui.ts b/lib/ui.ts
index 96968bc..93b4060 100644
--- a/lib/ui.ts
+++ b/lib/ui.ts
@@ -33,6 +33,7 @@ import * as localCache from "./local-cache";
import { PermissionSet } from "./types";
import { authSimple } from "./ui/api-functions";
import * as init from "./init";
+import { version as VERSION } from "../package.json";
declare module "koa" {
interface Request {
@@ -64,6 +65,7 @@ koa.use(async (ctx, next) => {
const configSnapshot = await localCache.getCurrentSnapshot();
ctx.state.configSnapshot = configSnapshot;
ctx.set("X-Config-Snapshot", configSnapshot);
+ ctx.set("GenieACS-Version", VERSION);
return next();
});
@@ -143,6 +145,10 @@ koa.use(async (ctx, next) => {
koa.use(koaBodyParser());
router.use("/api", api.routes(), api.allowedMethods());
+router.get("/status", ctx => {
+ ctx.body = "OK";
+});
+
router.get("/init", async ctx => {
const status = await init.getStatus();
if (Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length) {
@@ -207,6 +213,7 @@ router.get("/", async ctx => {
ui: localCache.getUiConfig(ctx.state.configSnapshot)
})};
window.configSnapshot = ${JSON.stringify(ctx.state.configSnapshot)};
+ window.genieacsVersion = ${JSON.stringify(VERSION)};
window.username = ${JSON.stringify(
ctx.state.user ? ctx.state.user.username : ""
)};
diff --git a/ui/app.ts b/ui/app.ts
index 136b24f..5aee77c 100644
--- a/ui/app.ts
+++ b/ui/app.ts
@@ -45,6 +45,7 @@ declare global {
username: string;
clientConfig: any;
configSnapshot: string;
+ genieacsVersion: string;
}
}
diff --git a/ui/config.ts b/ui/config.ts
index 454b1e5..b3ab39b 100644
--- a/ui/config.ts
+++ b/ui/config.ts
@@ -19,3 +19,4 @@
export default window.clientConfig;
export const configSnapshot = window.configSnapshot;
+export const genieacsVersion = window.genieacsVersion;
diff --git a/ui/dynamic-loader.ts b/ui/dynamic-loader.ts
index 34964af..7d2a875 100644
--- a/ui/dynamic-loader.ts
+++ b/ui/dynamic-loader.ts
@@ -17,9 +17,27 @@
* along with GenieACS. If not, see .
*/
+import * as notifications from "./notifications";
+
export let codeMirror;
export let yaml;
+let note;
+
+function onError(): void {
+ if (!note) {
+ note = notifications.push(
+ "error",
+ "Error loading JS resource, please reload the page",
+ {
+ Reload: () => {
+ window.location.reload();
+ }
+ }
+ );
+ }
+}
+
export function loadCodeMirror(): Promise {
if (codeMirror) return Promise.resolve();
@@ -36,7 +54,10 @@ export function loadCodeMirror(): Promise {
codeMirror = modules[0];
resolve();
})
- .catch(reject);
+ .catch(err => {
+ onError();
+ reject(err);
+ });
});
}
@@ -49,6 +70,9 @@ export function loadYaml(): Promise {
yaml = module;
resolve();
})
- .catch(reject);
+ .catch(err => {
+ onError();
+ reject(err);
+ });
});
}
diff --git a/ui/store.ts b/ui/store.ts
index f040a03..b714a36 100644
--- a/ui/store.ts
+++ b/ui/store.ts
@@ -23,13 +23,13 @@ import { or, and, not, evaluate, subset } from "../lib/common/expression";
import memoize from "../lib/common/memoize";
import { QueryOptions, Expression } from "../lib/types";
import * as notifications from "./notifications";
-import { configSnapshot } from "./config";
+import { configSnapshot, genieacsVersion } from "./config";
const memoizedStringify = memoize(stringify);
const memoizedEvaluate = memoize(evaluate);
let fulfillTimestamp = 0;
-let refreshNotification;
+let connectionNotification, configNotification, versionNotification;
const queries = {
filter: new WeakMap(),
@@ -89,6 +89,71 @@ class QueryResponse {
}
}
+function checkConnection(): void {
+ m.request({
+ url: "/status",
+ method: "GET",
+ background: true,
+ extract: xhr => {
+ if (xhr.status !== 200) {
+ if (!connectionNotification) {
+ connectionNotification = notifications.push(
+ "warning",
+ "Server is unreachable",
+ {}
+ );
+ }
+ } else {
+ if (connectionNotification) {
+ notifications.dismiss(connectionNotification);
+ connectionNotification = null;
+ }
+
+ const configChanged =
+ xhr.getResponseHeader("x-config-snapshot") !== configSnapshot;
+ const versionChanged =
+ xhr.getResponseHeader("genieacs-version") !== genieacsVersion;
+
+ if (!configNotification !== !configChanged) {
+ if (configNotification) {
+ notifications.dismiss(configNotification);
+ configNotification = null;
+ } else {
+ configNotification = notifications.push(
+ "warning",
+ "Configuration has been modified, please reload the page",
+ {
+ Reload: () => {
+ window.location.reload();
+ }
+ }
+ );
+ }
+ }
+
+ if (!versionNotification !== !versionChanged) {
+ if (versionNotification) {
+ notifications.dismiss(versionNotification);
+ versionNotification = null;
+ } else {
+ versionNotification = notifications.push(
+ "warning",
+ "Server has been updated, please reload the page",
+ {
+ Reload: () => {
+ window.location.reload();
+ }
+ }
+ );
+ }
+ }
+ }
+ }
+ });
+}
+
+setInterval(checkConnection, 3000);
+
export async function xhrRequest(
options: { url: string } & m.RequestOptions<{}>
): Promise {
@@ -99,21 +164,6 @@ export async function xhrRequest(
xhr: XMLHttpRequest,
_options?: { url: string } & m.RequestOptions<{}>
): any => {
- if (
- !refreshNotification &&
- configSnapshot !== xhr.getResponseHeader("x-config-snapshot")
- ) {
- refreshNotification = notifications.push(
- "warning",
- "Configuration has been modified, please reload the page",
- {
- Reload: () => {
- window.location.reload();
- }
- }
- );
- }
-
if (typeof extract === "function") return extract(xhr, _options);
let response: any;