diff --git a/src/api/models/Node.ts b/src/api/models/Node.ts index 52de1c5..65060c4 100644 --- a/src/api/models/Node.ts +++ b/src/api/models/Node.ts @@ -4,6 +4,7 @@ export class Node extends BaseModel { public id = -1; public name = ''; public description = ''; + public maintenanceMode = false; public connection = { fqdn: '', display: '', @@ -17,7 +18,6 @@ export class Node extends BaseModel { // Admin only props public public = false; - public maintenanceMode = false; public serversCount?: number; public limits = { cpu: -1, diff --git a/src/api/services/admin/nodes.ts b/src/api/services/admin/nodes.ts index 3464dd7..7b98fc0 100644 --- a/src/api/services/admin/nodes.ts +++ b/src/api/services/admin/nodes.ts @@ -16,7 +16,7 @@ interface UpdateNodeRequest { description?: string; public: boolean; display_fqdn?: string; - maintenance_node: boolean; + maintenance_mode: boolean; location_id: number; cpu: number; diff --git a/src/api/services/client/server.ts b/src/api/services/client/server.ts index 658e1ce..d86631a 100644 --- a/src/api/services/client/server.ts +++ b/src/api/services/client/server.ts @@ -10,8 +10,30 @@ interface WebsocketResponse { interface UpdateDetailsRequest { server_name: string; } +interface ServerResponse { + attributes: { + relationships: { + node: { + attributes: { + maintenance_mode: boolean; + }; + }; + }; + }; +} + class ServerService { + + getNodeMaintenanceMode(): Promise { + return RequestService.get('/servers/:server', { include: ['node'] }) + .then((response: ServerResponse) => response.attributes.relationships.node.attributes.maintenance_mode) + .catch(error => { + console.error('Error getting node maintenance mode:', error); + throw error; + }); + } + getWebsocket(): Promise { return RequestService.get('/servers/:server/websocket'); } diff --git a/src/locales/en/navigation.json b/src/locales/en/navigation.json index d196411..dd160c2 100644 --- a/src/locales/en/navigation.json +++ b/src/locales/en/navigation.json @@ -4,6 +4,11 @@ "description": "Couldn't find the page you were looking for." }, + "maintenance": { + "title": "Maintenance", + "description": "The Node hosting this server is currently in Maintenance Mode. Please try again later or contact support for more information." + }, + "login": { "index": { "title": "Login", diff --git a/src/middlewares/index.ts b/src/middlewares/index.ts index d0b6328..0f71d8c 100644 --- a/src/middlewares/index.ts +++ b/src/middlewares/index.ts @@ -6,3 +6,4 @@ export { default as Permission } from './permission'; export { default as Feature } from './feature'; export { default as Require2FA } from './require_2fa'; export { ModelBindings } from './model_bindings'; +export { default as MaintenanceMode } from './maintenance_mode'; diff --git a/src/middlewares/maintenance_mode.ts b/src/middlewares/maintenance_mode.ts new file mode 100644 index 0000000..8deaa9d --- /dev/null +++ b/src/middlewares/maintenance_mode.ts @@ -0,0 +1,20 @@ +import { RouteLocationNormalized } from 'vue-router'; +import ServerService from '~/api/services/client/server'; + +class MaintenanceMode implements Middleware { + name() { + return 'maintenance_node'; + } + + async run(to: RouteLocationNormalized) { + const maintenance = await ServerService.getNodeMaintenanceMode(); + + if (to.name !== 'MaintenanceView' && maintenance) { + return { + name: 'MaintenanceView', + }; + } + } +} + +export default new MaintenanceMode(); \ No newline at end of file diff --git a/src/routes.ts b/src/routes.ts index 4f7fd56..41eb72a 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -1,8 +1,8 @@ import { RouteRecordRaw } from 'vue-router'; import { Router, state } from '~/core'; -import { Admin, Authenticated, Guest, Permission, Feature, Require2FA, ModelBindings, MFAInProgress } from '~/middlewares'; +import { Admin, Authenticated, Guest, Permission, Feature, Require2FA, ModelBindings, MFAInProgress, MaintenanceMode } from '~/middlewares'; import { GenericLayout, PopupLayout, Passthrough, TabberPassthrough } from '~/views'; -import { NotFoundView } from '~/views/errors'; +import { NotFoundView, MaintenanceView } from '~/views/errors'; declare module 'vue-router' { interface RouteMeta { @@ -65,7 +65,7 @@ export const routes: RouteRecordRaw[] = [ }, ], }, - { // Backwards compatability with v1 + { // Backwards compatibility with v1 name: 'login.sso', path: '/auth/login/sso', meta: { @@ -118,7 +118,7 @@ export const routes: RouteRecordRaw[] = [ } ], }, - { // Redirect for backwards compatability with v1 + { // Redirect for backwards compatibility with v1 name: 'account.sso', path: 'account/sso', meta: { @@ -145,7 +145,7 @@ export const routes: RouteRecordRaw[] = [ path: '/server/:server', component: GenericLayout, meta: { - middlewares: [Authenticated, Require2FA, new ModelBindings('client')], + middlewares: [Authenticated, Require2FA, MaintenanceMode, new ModelBindings('client')], showChildrenInNavbar: true }, children: [ @@ -825,7 +825,7 @@ export const routes: RouteRecordRaw[] = [ ] }, - { // Backwards compatibility with WHMCS's go go Service button + { // Backwards compatibility with WHMCS's go to Service button name: 'admin.management.servers.view', path: 'view/:id', meta: { @@ -1092,6 +1092,11 @@ export const routes: RouteRecordRaw[] = [ path: '/:catchAll(.*)', component: NotFoundView, }, + { + name: 'maintenance', + path: '/maintenance', + component: MaintenanceView, + } ]; routes.forEach(Router.addRoute); diff --git a/src/views/errors/Maintenance.vue b/src/views/errors/Maintenance.vue new file mode 100644 index 0000000..6358107 --- /dev/null +++ b/src/views/errors/Maintenance.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/views/errors/index.ts b/src/views/errors/index.ts index c2c9aba..45fdb16 100644 --- a/src/views/errors/index.ts +++ b/src/views/errors/index.ts @@ -1 +1,2 @@ -export { default as NotFoundView } from './404.vue'; \ No newline at end of file +export { default as NotFoundView } from './404.vue'; +export { default as MaintenanceView } from './Maintenance.vue'; \ No newline at end of file