App can now go into maintenance mode locking out routing
This commit is contained in:
@@ -1,3 +1,19 @@
|
|||||||
const MAINTENANCE_MODE = Meteor.settings?.public?.maintenanceMode || false;
|
import { Migrations } from 'meteor/percolate:migrations';
|
||||||
|
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION.js';
|
||||||
|
|
||||||
|
if (Meteor.isServer){
|
||||||
|
Meteor.startup(()=>{
|
||||||
|
const dbVersion = Migrations.getVersion();
|
||||||
|
if (
|
||||||
|
!Meteor.settings.public.maintenanceMode &&
|
||||||
|
SCHEMA_VERSION !== dbVersion
|
||||||
|
){
|
||||||
|
Meteor.settings.public.maintenanceMode = {
|
||||||
|
reason: 'App data needs to be migrated to the latest version'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAINTENANCE_MODE = Meteor.settings.public.maintenanceMode;
|
||||||
export default MAINTENANCE_MODE;
|
export default MAINTENANCE_MODE;
|
||||||
|
|||||||
@@ -4,7 +4,13 @@
|
|||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<h4>Database version: {{ versions && versions.dbVersion }}</h4>
|
<h4>Current database version: {{ versions && versions.dbVersion }}</h4>
|
||||||
|
<h4 v-if="schemaVersion == versions.dbVersion ">
|
||||||
|
Database is up to date with latest version
|
||||||
|
</h4>
|
||||||
|
<h4 v-else>
|
||||||
|
Expected database version: {{ schemaVersion }}
|
||||||
|
</h4>
|
||||||
<h4>Git version: {{ versions && versions.gitVersion }}</h4>
|
<h4>Git version: {{ versions && versions.gitVersion }}</h4>
|
||||||
<v-alert
|
<v-alert
|
||||||
v-if="versionError"
|
v-if="versionError"
|
||||||
@@ -14,20 +20,18 @@
|
|||||||
</v-alert>
|
</v-alert>
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
|
:loading="loadingVersion"
|
||||||
@click="refreshVersions"
|
@click="refreshVersions"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-refresh</v-icon>
|
<v-icon>mdi-refresh</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-text-field
|
<br>
|
||||||
v-model="migrationInput"
|
|
||||||
label="Database version to migrate to"
|
|
||||||
/>
|
|
||||||
<v-btn
|
<v-btn
|
||||||
:disabled="!migrationInput"
|
:disabled="!(schemaVersion > (versions && versions.dbVersion))"
|
||||||
:loading="loadingMigration"
|
:loading="loadingMigration"
|
||||||
@click="migrate"
|
@click="migrate"
|
||||||
>
|
>
|
||||||
Migrate
|
Migrate to database version {{ schemaVersion }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-alert
|
<v-alert
|
||||||
v-if="migrateError"
|
v-if="migrateError"
|
||||||
@@ -45,6 +49,7 @@
|
|||||||
<script lang="js">
|
<script lang="js">
|
||||||
import getVersion from '/imports/migrations/methods/getVersion.js';
|
import getVersion from '/imports/migrations/methods/getVersion.js';
|
||||||
import migrateTo from '/imports/migrations/methods/migrateTo.js';
|
import migrateTo from '/imports/migrations/methods/migrateTo.js';
|
||||||
|
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data(){return {
|
data(){return {
|
||||||
@@ -54,25 +59,24 @@ export default {
|
|||||||
versionError: undefined,
|
versionError: undefined,
|
||||||
migrateError: undefined,
|
migrateError: undefined,
|
||||||
loadingMigration: false,
|
loadingMigration: false,
|
||||||
|
schemaVersion: SCHEMA_VERSION,
|
||||||
}},
|
}},
|
||||||
mounted(){
|
mounted(){
|
||||||
this.refreshVersions();
|
this.refreshVersions();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
refreshVersions(){
|
refreshVersions(){
|
||||||
|
this.loadingVersion = true;
|
||||||
getVersion.call((error, result) => {
|
getVersion.call((error, result) => {
|
||||||
|
this.loadingVersion = false;
|
||||||
this.versionError = error;
|
this.versionError = error;
|
||||||
this.versions = result;
|
this.versions = result;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
migrate(){
|
migrate(){
|
||||||
let version = this.migrationInput;
|
|
||||||
if (Number.isFinite(+version)){
|
|
||||||
version = +version;
|
|
||||||
}
|
|
||||||
this.loadingMigration = true;
|
this.loadingMigration = true;
|
||||||
migrateTo.call({
|
migrateTo.call({
|
||||||
version,
|
version: SCHEMA_VERSION,
|
||||||
}, error => {
|
}, error => {
|
||||||
this.loadingMigration = false;
|
this.loadingMigration = false;
|
||||||
this.migrateError = error;
|
this.migrateError = error;
|
||||||
|
|||||||
43
app/imports/ui/pages/Maintenance.vue
Normal file
43
app/imports/ui/pages/Maintenance.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<v-layout
|
||||||
|
style="height: 100%;"
|
||||||
|
column
|
||||||
|
align-center
|
||||||
|
justify-center
|
||||||
|
>
|
||||||
|
<h1
|
||||||
|
v-if="maintenanceMode"
|
||||||
|
class="ma-4 text-h3"
|
||||||
|
>
|
||||||
|
DiceCloud is currently under maintenance
|
||||||
|
</h1>
|
||||||
|
<template v-else>
|
||||||
|
<h1
|
||||||
|
class="ma-4 text-h3"
|
||||||
|
>
|
||||||
|
DiceCloud is live.
|
||||||
|
</h1>
|
||||||
|
<v-btn
|
||||||
|
color="accent"
|
||||||
|
to="/"
|
||||||
|
>
|
||||||
|
Home
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<h1
|
||||||
|
v-if="maintenanceMode && maintenanceMode.reason"
|
||||||
|
class="ma-4 text-h4"
|
||||||
|
>
|
||||||
|
{{ maintenanceMode.reason }}
|
||||||
|
</h1>
|
||||||
|
</v-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import MAINTENANCE_MODE from '/imports/constants/MAINTENANCE_MODE.js';
|
||||||
|
export default {
|
||||||
|
data(){return {
|
||||||
|
maintenanceMode: MAINTENANCE_MODE,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { RouterFactory, nativeScrollBehavior } from 'meteor/akryum:vue-router2';
|
import { RouterFactory, nativeScrollBehavior } from 'meteor/akryum:vue-router2';
|
||||||
import { acceptInviteToken } from '/imports/api/users/Invites.js';
|
import { acceptInviteToken } from '/imports/api/users/Invites.js';
|
||||||
|
import MAINTENANCE_MODE from '/imports/constants/MAINTENANCE_MODE.js';
|
||||||
// Components
|
// Components
|
||||||
const Home = () => import('/imports/ui/pages/Home.vue');
|
const Home = () => import('/imports/ui/pages/Home.vue');
|
||||||
const About = () => import('/imports/ui/pages/About.vue');
|
const About = () => import('/imports/ui/pages/About.vue');
|
||||||
@@ -26,6 +26,8 @@ const Tabletop = () => import('/imports/ui/pages/Tabletop.vue');
|
|||||||
const TabletopToolbar = () => import('/imports/ui/tabletop/TabletopToolbar.vue');
|
const TabletopToolbar = () => import('/imports/ui/tabletop/TabletopToolbar.vue');
|
||||||
const TabletopRightDrawer = () => import('/imports/ui/tabletop/TabletopRightDrawer.vue');
|
const TabletopRightDrawer = () => import('/imports/ui/tabletop/TabletopRightDrawer.vue');
|
||||||
const Admin = () => import('/imports/ui/pages/Admin.vue');
|
const Admin = () => import('/imports/ui/pages/Admin.vue');
|
||||||
|
const Maintenance = () => import('/imports/ui/pages/Maintenance.vue');
|
||||||
|
|
||||||
// Not found
|
// Not found
|
||||||
const NotFound = () => import('/imports/ui/pages/NotFound.vue');
|
const NotFound = () => import('/imports/ui/pages/NotFound.vue');
|
||||||
|
|
||||||
@@ -92,8 +94,8 @@ function claimInvite(to, from, next){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
RouterFactory.configure(factory => {
|
RouterFactory.configure(router => {
|
||||||
factory.addRoutes([{
|
router.addRoutes([{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
components: {
|
components: {
|
||||||
@@ -249,29 +251,40 @@ RouterFactory.configure(factory => {
|
|||||||
name: 'admin',
|
name: 'admin',
|
||||||
component: Admin,
|
component: Admin,
|
||||||
beforeEnter: ensureAdmin,
|
beforeEnter: ensureAdmin,
|
||||||
|
},{
|
||||||
|
path: '/maintenance',
|
||||||
|
name: 'maintenance',
|
||||||
|
component: Maintenance,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Not found route has lowest priority
|
// Not found route has lowest priority
|
||||||
RouterFactory.configure(factory => {
|
RouterFactory.configure(router => {
|
||||||
factory.addRoute({
|
router.addRoute({
|
||||||
path: '*',
|
path: '*',
|
||||||
component: NotFound,
|
component: NotFound,
|
||||||
});
|
});
|
||||||
}, -1);
|
}, -1);
|
||||||
|
|
||||||
|
function redirectIfMaintenance(to, from, next){
|
||||||
|
if (!MAINTENANCE_MODE) return next();
|
||||||
|
console.log(to);
|
||||||
|
if (to?.path === '/admin' || to?.path === '/maintenance') return next();
|
||||||
|
Tracker.autorun((computation) => {
|
||||||
|
if (userSubscription.ready()){
|
||||||
|
computation.stop();
|
||||||
|
const user = Meteor.user();
|
||||||
|
if (user && user.roles && user.roles.includes('admin')){
|
||||||
|
next({name: 'admin'})
|
||||||
|
} else {
|
||||||
|
next({name: 'maintenance'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Create the router instance
|
// Create the router instance
|
||||||
const router = routerFactory.create();
|
const router = routerFactory.create();
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach(redirectIfMaintenance);
|
||||||
let user = Meteor.user();
|
|
||||||
if (
|
|
||||||
to.path === '/sign-in' ||
|
|
||||||
(user && user.roles && user.roles.includes('admin'))
|
|
||||||
){
|
|
||||||
next();
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -56,7 +56,9 @@ self.addEventListener('fetch', (event) => {
|
|||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
caches.open(version).then(cache => cache.put(event.request, clonedResponse));
|
if (event.request.method !== 'POST') {
|
||||||
|
caches.open(version).then(cache => cache.put(event.request, clonedResponse));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ import '/imports/api/parenting/organizeMethods.js';
|
|||||||
import '/imports/api/users/patreon/updatePatreonOnLogin.js';
|
import '/imports/api/users/patreon/updatePatreonOnLogin.js';
|
||||||
import '/imports/migrations/server/index.js';
|
import '/imports/migrations/server/index.js';
|
||||||
import '/imports/migrations/methods/index.js'
|
import '/imports/migrations/methods/index.js'
|
||||||
|
import '/imports/constants/MAINTENANCE_MODE.js';
|
||||||
|
|||||||
Reference in New Issue
Block a user