How to differentiate two routes in router spring webflux - kotlin

I would like to have search alongside getting gizmos by uuid:
router {
GET("/gizmos/{uuid}", handler::getGizmo)
GET("/gizmos/search", handler::search)
}
It turns out that accessing search redirects to uuid route.

Related

How to protect multiple routes from unauthorized access in Next.js using next-auth

I am using Next.js and I have a folder learning inside my pages folder. Now, this learning folder has about 10 pages.
All these pages need to redirect to the index page if the user is not logged in. The following code does the job, but is there any other way to protect multiple pages, so that I don't need to add this same code again and again to all the pages ?
export async function getServerSideProps(context) {
//redirect to index page if not logged in
const session = await unstable_getServerSession(context.req, context.res, authOptions);
if (!session) {
return {
redirect: {
destination: '/',
permanent: false
}
}
}
}
I believe you are confused between protecting pages and protecting API ROUTES.
If you simply want to protect pages, you can indeed use middleware
However, if you wish to protect API Routes (e.g prevent a user from deleting data using your API endpoint and postman), I believe you need to use this unstable_getServerSession
Except creating reusable function, it's true that I didn't find anywhere in the doc how to set it for multiple paths in one folder only...
you can use middleware. docs: https://next-auth.js.org/configuration/nextjs#middleware
Create a middleware.ts (or .js) file at the root or in the src directory (same level as your pages).
If you only want to secure certain pages, export a config object with a matcher:
export { default } from "next-auth/middleware"
// otherwise your app would require authentication for all
export const config = { matcher: ["/dashboard"] }
Now you will still be able to visit every page, but only /dashboard
will require authentication.
If a user is not logged in, the default behavior is to redirect them
to the sign-in page.
that example is from the docs. You can also write a custom middleware
import { NextResponse } from "next/server";
export function middleware(req) {
const sessionCookie = req.cookies.get("session");
}
// you could add more if logic for other pages
if (req.nextUrl.pathname.startsWith("/admin ")) {
if (!sessionCookie) {
return NextResponse.redirect("/home");
}
}

Custom PageLayoutComponent

To have specific layout for some pages at our project we create few custom PageLayoutComponent's. Some contfiguration example:
{
// #ts-ignore
path: null,
canActivate: [CmsPageGuard],
component: CartPageLayoutComponent,
data: {
cxRoute: 'cart',
cxContext: {
[ORDER_ENTRIES_CONTEXT]: ActiveCartOrderEntriesContextToken,
},
},
},
All work fine with storefront until you will not try to select specific page at smartedit. As result it not use our custom CartPageLayoutComponent, but will use PageLayoutComponent for rendering.
Probably this is because it's not a normal route navigation. Can somebody from spartacus team suggest how this bug can be fixed?
Probably this is because it's not a normal route navigation
I believe your Route should be recognized normally, there is nothing special in adding a custom Angular Route.
So I guess there is something special about the page or URL of Spartacus Storefront that runs in your SmartEdit.
It's hard to tell the reason of your problem without more debugging.
You said your app works as expected when run differently (locally?), but when used in SmartEdit, then there is a problem. Please identify factors that makes the run SmartEdit different from your (local?) run. And try to isolate them. Guesses from top of my head:
production vs dev mode of the build?
exact URL of the cart page?
any difference in configuration between a local version and deployed one to be used in SmartEdit?
I would also add some debugging code to better know which routes configs are available and which one is used for the current route. For debugging purposes please add the following constructor logic in your AppModule:
export class AppModule {
// on every page change, log:
// - active url
// - the Route object that was matched with the active URL
// - all the Route objects provided to Angular Router
constructor(router: Router) {
router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
console.log({
activeUrl: router.url,
activeRouteConfig:
router.routerState.snapshot.root.firstChild.routeConfig,
allRoutesConfigs: router.config,
});
}
});
}
}
The pages opened in SmartEdit have the same route of cx-preview (e.g. to open faq page in smartedit, request is https://localhost:4200/electronics-spa/en/USD/cx-preview?cmsTicketId=xxxxx. backend can get page id from cmsTicketId). If you want to change the page layout, you can consider use PageLayoutHandler. Spartacus has some PageLayoutHandlers, e.g.
{
provide: PAGE_LAYOUT_HANDLER,
useExisting: CartPageLayoutHandler,
multi: true,
},

What are the implications of migrating from createWebHashHistory() to createWebHistory()?

I have an application that I released that uses createWebHashHistory() to manage URLs. For example, a user visits the following URL to visit something called the earthquakes channel:
https://app.radar.chat/#/channel/earthquakes
I would like to switch over to using createWebHistory() to manage URLs instead (SEO, social media previews, iOS universal links configuration, etc). With that in mind, I would like it if my new URL structure looks like this:
https://app.radar.chat/channel/earthquakes
I know that to support this change I need to make a server change. The easiest way is to have the server redirect incoming requests to an index.html file (this is documented extensively on the Vue website).
However, there are URLs in the wild that link to these old pages, URLs that have been printed and that can never be updated.
Is there a convenient mechanism to continue to support the old hash-based URLs while having the non-hashed URLs be the new default?
In your router config, you could add a global beforeEach hook on the index path that resolves to the hash path in the URL if it exists:
// router.js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [⋯]
})
router.beforeEach((to, from, next) => {
if (to.path === '/' && to.hash.startsWith('#/')) {
next(to.hash.substr(1))
} else {
next()
}
})
export default router
demo

Cannot figure out how vue-router works

I have a Vue.js project with the following router:
import Vue from 'vue';
import Router from 'vue-router';
import Overview from '#/components/Overview';
import Experiment from '#/components/ForExperiment';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
redirect: 'test',
},
{
path: '/overview',
component: Overview,
},
{
path: '/overview/from/:from/to/:to',
name: 'overview',
component: Overview,
},
//... some other urls goes here.
{
path: '/test',
name: 'test',
component: Experiment,
},
],
});
If I open http://localhost:8080 in a browser I am redirected to http://localhost:8080/#/test. Why not just http://localhost:8080/test? Where does the '#' symbol come from?
And why if I open http://localhost:8080/test am I redirected to http://localhost:8080/test#/test?
And what is even more strange, if I open http://localhost:8080/overview I am redirected to http://localhost:8080/overview#/test, so the Overview component is not displayed.
What can cause these strange effects?
Vue router has different modes. The default mode when a browser is detected is hash. The current route is determined by the hash part of the url. The upside of this approach is that no serverside configuration is required. All urls point at the same resource (e.g. the route), which you can make your index.html file.
You can change this mode to history. The history mode uses the history api of the browser. It will only work in recent browsers, but support should not be an issue at this point. It will also require server side configuration in that you need to internally rewrite your router urls to the same file. If you would not do that, refreshing the page will show a 404 page instead of the page you want to see.
vue-router default mode is hash mode, that is why you see a # on your URL. It uses the URL hash to simulate a full URL without reloading the page if it changes.
To get rid of the hash, we can use vue-router history mode. Change the mode like so:
const router = new VueRouter({
mode: 'history',
routes: [...]
})
This leverages the History API.
If you want to use the history mode, you'll need to change your server configuration. Vue Router docs has some examples here.
The vue router defaults to hash mode. For your url to go to http://localhost:8080/test you need to go into history mode. This is done because by default web servers are not setup to redirect all requests to one html file. hash mode is used to per the docs:
The default mode for vue-router is hash mode - it uses the URL hash to simulate a full URL so that the page won't be reloaded when the URL changes.
Change your router to this to get history mode. But you will need to configure NGINX or Apache2 to redirect all requests to your vue code
const router = new VueRouter({
mode: 'history', // Add this to your router
routes: [...]
})

Is there a more comprehensive way to handle authentication in Angular2 rc3 than guards?

I have an existing Angular2 app where login/authentication was handled by creating an AuthRouterOutletDirective that extended the RouterOutlet. This made it easy to use componentInstruction in the activate function to check if a user was logged in, if not redirect them to our login portal (for various reasons I have no control over this is through a separate app, which has it's own login screen and then sends back a token that we would save in the Angular2 app) before navigating to the next component.
I just upgraded to the router 3.0.0-alpha.8 and I see that this is no longer an option and it has been replaced by creating an authGuard and and using the canActivate method to handle authentication (I'm referring to this section of the documentation). The problem I'm struggling with is it seems like this is designed for apps where only a small number of routes are protected, and you can just add canActivate: [AuthGuard] to each route that requires authentication in the RouterConfig.
The problem I'm having is that every single route needs to be protected by authentication. There also needs to be continuous checking (unless there is a better fix) because we also (again due in part to the external login service we have to use) will log out the users and clear their token and the next time you navigate to a new route (regardless of what the route is) it should redirect you to login again. I understand that I could just add canActivate: [AuthGuard] to every single route but that seems like an insane and tedious fix, especially for apps with a large amount of routes, all of which require a user to be authenticated to view.
I've been searching for a fix but it seems like, perhaps in part because the upgrade is fairly new, that all the resources are for how to implement these AuthGuards on just one or two routes. I've been digging through the source code (specifically here) to see if there is another method I can extend that's more comprehensive than canActivate or a more universal way to have every route include a particular guard but I can't seem to find anything. Any advice on how best to implement this would be very much appreciated! Thanks.
Referring to my comment, you can add extra guard like that:
import {provideRouter, RouterConfig, Route, CanActivate, ActivatedRouteSnapshot} from '#angular/router';
import {Injectable} from "#angular/core";
#Injectable()
class UniversalGuard implements CanActivate {
canActivate(route: ActivatedRouteSnapshot) {
console.log('applying can activate');
return true;
}
}
let routes: RouterConfig = [
{path: 'path1', component: Component1} as Route,
{path: 'path2', component: Component2} as Route,
{path: 'path3', component: Component3} as Route
];
const routeAugumenter = route => {
let guards = route.canActivate || [];
guards.push(UniversalGuard);
route.canActivate = guards;
if (route.children) {
route.children = route.children.map(routeAugumenter);
}
return route;
};
let augumentedRoutes = routes.map(routeAugumenter);
console.log(augumentedRoutes);
export const APP_ROUTER_PROVIDERS = [
provideRouter(augumentedRoutes), UniversalGuard
];
I have not tested it with child routes, but should work as well.
Edit: updated information how to inject services into UniversalGuard.
If your guard needs some services injected, you inject them in the constructor, as usually. You have to provide these services (and the services they depend on, etc.) in Angular bootstrap call rather than in the components.