SvelteKit Maintenance Mode - javascript

Is there a good way to do display a maintenance page when visiting any route of my SvelteKit website?
My app is hosted on Vercel, for those who want to know.
What I've tried so far:
Set an environment variable called MAINTENANCE_MODE with a value 1 in Vercel.
For development purposes I've set this in my .env file to VITE_MAINTENANCE_MODE and called with import.meta.env.VITE_MAINTENANCE_MODE.
Then inside +layout.server.js I have the following code to redirect to /maintenance route
import { redirect } from "#sveltejs/kit";
export async function load({ url }) {
const { pathname } = url;
// Replace import.meta.env.VITE_MAINTENANCE_MODE with process.env.MAINTENANCE_MODE in Production
if (import.meta.env.VITE_MAINTENANCE_MODE == 1) {
if (pathname == "/maintenance") return;
throw redirect(307, "/maintenance");
  } else {
if (pathname == "/maintenance") {
throw redirect(307, "/");
    };
  };
};
What I've also tried is just throwing an error in +layout.server.js with the following:
import { error } from "#sveltejs/kit";
export async function load() {
if (import.meta.env.VITE_MAINTENANCE_MODE == 1) {
throw error(503, "Scheduled for maintenance");
  };
};
However this just uses SvelteKit's static fallback error page and not +error.svelte. I've tried creating src/error.html in the hope to create a custom error page for +layout.svelte but couldn't get it to work.
I would like to use a custom page to display "Down for maintenance", but I don't want to create an endpoint for every route in my app to check if the MAINTENANCE_MODE is set to 1.
Any help is appreciated

You could use a handle server hook, e.g. src/hooks.server.ts:
import { env } from '$env/dynamic/private';
import type { Handle } from '#sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
if (env.MAINTENANCE_MODE == '1' && event.routeId != '/maintenance')
return new Response(undefined, { status: 302, headers: { location: '/maintenance' } });
// <other logic>
// Default response
return await resolve(event);
}
And on the maintenance page you can prevent all further navigation:
import { beforeNavigate } from '$app/navigation';
beforeNavigate(async ({ cancel }) => {
cancel();
});
(Possibly add some periodic checks via fetch calls to navigate elsewhere once the site is back online.)

You can also use +layout.ts to hook up for the maintenance mode. You can even make this conditional for some parts of the site (have frontpage still up and running).
Here is the trick we use:
import type { LayoutLoad } from './$types';
import { chainsUnderMaintenance } from '$lib/config';
import { error } from '#sveltejs/kit';
export const load: LayoutLoad = ({ params }) => {
// Check chain maintenance status; if under maintenance, trigger error (see +error.svelte)
const chainName = chainsUnderMaintenance[<string>params.chain];
if (chainName) {
throw error(503, `Chain under maintenance: ${chainName}`);
}
};

Related

Check if data in URL is valid before navigate to page

I would like to configure my Angular component, so that the page only loads if the ID in the URL is valid. The point here is, that I want to protect the page from users manually entering a random URL, and accessing any page.
I have a component with lists.
If I click on the "Show Details", Angular navigates to the details page. I would like to only open this page, if the entered URL contains a valid ID. To achieve this, I call a service to gather all IDs into an array of strings. And then examine if the entered ID is a member of that array.
What I have tried:
list.component.ts:
ngOnInit() {
this.fetchLists();
}
fetchLists() {
from(this.listService.getGroups())
.pipe(
takeUntil(this.destroy$)
)
.subscribe({
next: (listUI: ListUI[]) => {
this.listData = listUI;
},
error: (error) => {
this.logger.debug(error.message);
this.certError = true;
}
});
}
details.component.ts:
ngOnInit() {
this.fetchListsAndIDs();
if (this.validIDsList.includes(listID)) {
this.router.navigateByUrl(`/groups/lists/${listID}/details`);
}
else {this.router.navigateByUrl(`/groups/lists`);}
}
fetchListsAndIDs() {
from(this.listService.getGroups())
.pipe(
takeUntil(this.destroy$)
)
.subscribe({
next: (listUI: ListUI[]) => {
const listData = listUI;
this.validIDsList = listData.map((lists) => lists.id);
},
error: (error) => {
this.logger.debug(error.message);
this.certError = true;
}
});
}
app.routing.module.ts
{
path: 'groups/lists/${listID}/details',
component: DetailsComponent
}
The page "groups/lists/99999999999/details" opens, with zero data, and "this.validIDsList" is undefined. Can someone please help me how to fix this?
You almost have the right code, but you missed the part that, this.fetchListsAndIDs() is executing an asynchronous observable, so your if..else block is executing before even the API call completes.
I would suggest, you include the if...else check inside the next() handler. I have reversed the conditions to check for NOT first, since you are already in details.components.ts which represents ``/groups/lists/${listID}/details) route, you should only redirect the user back to lists if id is not valid, else the component should continue with its work.
I added code to grab the listId from URL. It is missing in the code you posted in the question.
ngOnInit() {
this.listID = this.route.snapshot.paramMap.get('listID');
this.fetchListsAndIDs();
}
fetchListsAndIDs() {
from(this.listService.getGroups())
.pipe(
takeUntil(this.destroy$)
)
.subscribe({
next: (listUI: ListUI[]) => {
const listData = listUI;
this.validIDsList = listData.map((lists) => lists.id);
this.handleNavigation();
},
error: (error) => {
this.logger.debug(error.message);
this.certError = true;
}
});
}
handleNavigation() {
if (!this.validIDsList.includes(this.listID)) {
this.router.navigateByUrl(`/groups/lists`);
} else {
// call the function to continue with details component
}
}

How to use proxy with vite (vue frontend) and django rest framework

So, you know when you access a view with django rest api on the browser, you get an html page, and when you send something like an ajax request, you get the json? I'm trying to figure out how to mess with the proxy setting for vite, but I can't find a single decent documentation around it. I want to redirect '/' to 'http://localhost:8000/api', but there's really weird behavior going on.
If I have a route on localhost:8000/api, I can do:
//vite.config.js
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
//Focus here
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
rewrite: (path) => { console.log(path); return path.replace('/^\/api/', '') }
}
}
}
})
//todo-component.vue
export default {
data() {
return {
todos: []
}
},
components: {
TodoElement
},
beforeCreate() {
//Focus here as well
this.axios.get('/api').then((response) => {
this.todos = response.data
})
.catch((e) => {
console.error(e)
})
}
}
This will return the json response as expected. However, if I try to make it so that '/' routes to 'localhost:8000/api/', like this:
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
//change here
'/': {
target: 'http://localhost:8000/api',
changeOrigin: true,
rewrite: (path) => { console.log(path); return path.replace('/^\/api/', '') }
}
}
}
})
import TodoElement from "./todo-element.vue"
export default {
data() {
return {
todos: []
}
},
components: {
TodoElement
},
beforeCreate() {
//change here
this.axios.get('/').then((response) => {
this.todos = response.data
})
.catch((e) => {
console.error(e)
})
}
}
It just spews out the html version of the api view, but with no styling, with a bunch of errors
No idea what to do. If someone could explain how this proxy works, i'd really love it. I don't want to keep writing "api/", and it'd be really valuable if I can manage to understand how this works.
You are a bit confusing things and I will try to show you why.
If you redirect root path / to /api, every request sent to your app running at http://localhost:3000 will be forwarded to http://localhost:8000/api. It mean that you will not be able to serve anything from the running app, but you will get an answer from the configured endpoint (localhost:8000/api) for every request.
To understand easily what is going on, keep in mind that this vite config option (server.proxy) act like a reverse proxy. As example, I take the favicon.ico resource of your app.
With your current configuration, when from your browser you try to access your app, the /favicon.ico (and all other resources) is then loaded from http://localhost:8000/api/favicon.ico and not anymore from your app running at http://localhost:3000/favicon.ico.
This explain all the errors in the console. Again, for example, /static/rest_framework is loaded from http://localhost:8000/api/ and not http://localhost:3000/.
The documentation is quite clear, it's just a matter of understanding what a http-proxy is. To get more information you can head to https://github.com/http-party/node-http-proxy#core-concept

Ionic 4 Deeplink plugin return false route not matched

I am Implementing Deeplink in ionic 4 application. Application in getting launched but deeplink plugin always returns false;
app.routing.ts:
{
path: 'viewdiary/:id/public',
loadChildren: () => import('./pages/viewdiary/viewdiary.module').then( m => m.ViewdiaryPageModule)
},
app.compoent.ts:
setupDeepLink(){
this.deeplinks.route({
'/viewdiary/:id/public': 'viewdiary/:id/public'
}).subscribe((match)=>{
console.log('deeplink matched', match);
const internalPath = `${match.$route}/${match.$args['id']}/${match.$route}`;
console.log(internalPath);
this.zone.run(()=>{
this.general.goToPage(internalPath);
});
},
nomatch=>{
// nomatch.$link - the full link data
console.error("Got a deeplink that didn't match", nomatch);
})
};
My Public diary Page link is 'https://www.example.com/diary/12542/public';
it looks like a routing issue tried many thing changes names but nothing works. I am clueless what going wrong.
Figured out how to achieve it with the help of Another Answer on Stackoverflow
import { Platform, NavController } from '#ionic/angular';
constructor(public navCtrl: NavController){}
this.deeplinks.routeWithNavController(this.nav, {
'/viewdiary/:diary/:id/:public': 'viewdiary'
}).subscribe((match) => {
console.log('success' + JSON.stringify(match));
}, (noMatch) => {
console.log('error' + JSON.stringify(noMatch));
});
For me, this approach did not work either. So, in my application I handle deep links as follows:
const {App}: { App: AppPlugin } = Plugins;
...
export class AppComponent {
private setupDeepLinks(): void {
App.addListener('appUrlOpen', (data: any) => {
this.ngZone.run(() => {
// Example url: https://my-domain.com/tabs/tab2
// slug = /tabs/tab2
const slug = data.url.split('my-domain.com').pop();
if (slug) {
this.router.navigateByUrl(slug);
}
});
});
}
}
Or you can implement your own more complex logic inside the listener if needed

master-atul/react-native-exception-handler - does not capture errors in node_modules

I am only able to get this to work for issues at the application level, if an error is thrown from any package in the node_modules, it is not captured.
below is my GlobalErrorHandler, i have imported this into my app.js file at the top of my application so was expecting it to capture any exception in the application.
the expected behaviour would be for the this.test() exception to be thrown and from node_modules/AnotherUIDependency and captured by the globalErrorHandler
app.js
...
import { GlobalErrorHandler } from 'MASKED/globalErrorHandler'
...
globalErrorHandler.js
import { Alert } from 'react-native'
import { GlobalStrings } from 'MASKED'
import RNExitApp from 'react-native-exit-app'
import {
setJSExceptionHandler,
setNativeExceptionHandler
} from 'react-native-exception-handler'
errorHandler = (e, isFatal) => {
Alert.alert(
GlobalStrings.globalErrorHandler.title,
// eslint-disable-next-line no-undef
errorMessage(e, isFatal, __DEV__),
[
{
text: GlobalStrings.globalErrorHandler.exit,
onPress: () => {
RNExitApp.exitApp()
}
}
],
{ cancelable: false }
)
}
errorMessage = (e, isFatal, isDev) => {
let val = null
if (isDev) {
val =
GlobalStrings.globalErrorHandler.error +
`: ${isFatal ? GlobalStrings.globalErrorHandler.fatal : ''}` +
'\n\n' +
e.name +
':' +
e.message
} else {
if (!isFatal) {
val = GlobalStrings.globalErrorHandler.exceptionMessage
} else {
val = GlobalStrings.globalErrorHandler.nonFatal + ': ' + e
}
}
return val
}
setJSExceptionHandler(errorHandler, true)
setNativeExceptionHandler(errorString => {
Alert.alert(
GlobalStrings.globalErrorHandler.nativeExceptionMessage + ': ' + errorString
)
})
node_modules/AnotherUIDependency
...
export default class myComponent extends Component {
render(
return{
this.test() // expected globalErrorHandler capture
...
}
)
}
I think it may be my usage, however, i found that i had to import my globalErrorHandler at the top of the index file of each package in my project.
I found that even if i implemented it as the docs suggest, it was not truly global as it still would not capture node_module level exceptions. I would seem that the handler only picks up exceptions where it can follow the imports to the file where the exception occurs.
In my case, we have some weird referencing going on which i think is preventiung this. I have the same issue even if i use the React-Native ErrorUtils global
You're wrong about how to get the module in.
Usage
To catch JS_Exceptions
import {setJSExceptionHandler, getJSExceptionHandler} from 'react-native-exception-handler';
.
.
// For most use cases:
// registering the error handler (maybe u can do this in the index.android.js or index.ios.js)
setJSExceptionHandler((error, isFatal) => {
// This is your custom global error handler
// You do stuff like show an error dialog
// or hit google analytics to track crashes
// or hit a custom api to inform the dev team.
});
//=================================================
// ADVANCED use case:
const exceptionhandler = (error, isFatal) => {
// your error handler function
};
setJSExceptionHandler(exceptionhandler, allowInDevMode);
// - exceptionhandler is the exception handler function
// - allowInDevMode is an optional parameter is a boolean.
// If set to true the handler to be called in place of RED screen
// in development mode also.
// getJSExceptionHandler gives the currently set JS exception handler
const currentHandler = getJSExceptionHandler();
To catch Native_Exceptions
import { setNativeExceptionHandler } from "react-native-exception-handler";
//For most use cases:
setNativeExceptionHandler(exceptionString => {
// This is your custom global error handler
// You do stuff likehit google analytics to track crashes.
// or hit a custom api to inform the dev team.
//NOTE: alert or showing any UI change via JS
//WILL NOT WORK in case of NATIVE ERRORS.
});
//====================================================
// ADVANCED use case:
const exceptionhandler = exceptionString => {
// your exception handler code here
};
setNativeExceptionHandler(
exceptionhandler,
forceAppQuit,
executeDefaultHandler
);
// - exceptionhandler is the exception handler function
// - forceAppQuit is an optional ANDROID specific parameter that defines
// if the app should be force quit on error. default value is true.
// To see usecase check the common issues section.
// - executeDefaultHandler is an optional boolean (both IOS, ANDROID)
// It executes previous exception handlers if set by some other module.
// It will come handy when you use any other crash analytics module along with this one
// Default value is set to false. Set to true if you are using other analytics modules.

Navigo JS Router - Duplicate routing issue

Not sure what the issue is but my Navigo router is duplicating routes.
The Router:
this.Navigo.hooks({
before: (done, params) => {
// some tomfoolery
done();
}
});
this.Navigo.on({
'/:region/travel': (params) => {
// import Travel module
// some nonsense
},
'/:region/travel/car': (params) => {
// import TravelCar module
// some nonsense
}
)};
this.Navigo.resolve();
The Problem
this.Navigo.navigate('/london/travel/car');
Navigating to /london/travel/car is also triggering the route for /london/travel and thus causing all kinds of twaddle.
Is this standard behaviour? If not, what could be wrong?
I could rewrite the routes so they don't collide e.g. /london/travel-by-car, but I really don't want to if I can avoid it.
UPDATE 1:
I tried switching the order of routes but makes no difference. I did this by declaring the longest travel routes first, /:region/travel/car, and the smallest, /:region/travel, last.
UPDATE 2:
The more I look into this, the more I'm convinced this cannot be achieved with Navigo. Navigo do not support nested routes. If somebody could confirm that my routes are in fact 'nested', I will use an alternative routing library that does support them.
My code is a little different, but works the way you expect:
var router = new Navigo("/");
var render = (content) => (document.querySelector("#app").innerHTML = content);
router
.on('/:id', ({ data }) => {
setuserId(data.id)
if (verifiedUser) {
console.log("User verified");
} else {
console.log("User NOT verified");
}
rendertemplate(userDataURL(), "#landing-template", "#app")
})
.on('/:id/q', ({ data }) => {
// Example - flaging a send with 's' from 'SMS', perhaps a diff flow?
setuserId(data.id)
rendertemplate(userDataURL(), "#landing-template", "#app")
console.log("Source was a QRcode");
})
.on('/:id/q/t', ({ data }) => {
// Example - flaging a send with 's' from 'SMS', perhaps a diff flow?
setuserId(data.id)
rendertemplate(userDataURL(), "#landing-template", "#app")
console.log("Source was a QRcode in a Train");
})
This will give me a single discreet ".. verified"/"Source was a QRcode"/"Source was a QRcode in a Train" console.log response.
B

Categories

Resources