Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
move premium check to middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitrykulakovfrontend committed Nov 14, 2023
1 parent 835c809 commit 14d7b50
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 74 deletions.
67 changes: 40 additions & 27 deletions middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,31 @@ export async function middleware(req) {
const protocol = process.env.NODE_ENV === "development" ? "http" : "https";
const hostname = req.headers.get("host");
const reqPathName = req.nextUrl.pathname;
const sessionRequired = ["/account", "/api/account"];
const adminRequired = ["/admin", "/api/admin"];
const adminRequired = [ "/admin", "/api/admin" ];
// Trailing slash is necessary to catch URL /account/statistics/* but not index page
const premiumRequired = [ "/account/statistics/" ];
const adminUsers = process.env.ADMIN_USERS.split(",");
const hostedDomain = process.env.NEXT_PUBLIC_BASE_URL.replace(
/http:\/\/|https:\/\//,
"",
);
const hostedDomains = [hostedDomain, `www.${hostedDomain}`];

const hostedDomains = [ hostedDomain, `www.${hostedDomain}` ];
const sessionRequired = [ "/account", "/api/account" ];
if (
!sessionRequired
.concat(adminRequired)
.some((path) => reqPathName.startsWith(path))
) {
return NextResponse.next();
}
// if custom domain + on root path
if (!hostedDomains.includes(hostname) && reqPathName === "/") {
console.log(`custom domain used: "${hostname}"`);

let res;
let profile;
let url = `${
process.env.NEXT_PUBLIC_BASE_URL
}/api/search/${encodeURIComponent(hostname)}`;
let url = `${process.env.NEXT_PUBLIC_BASE_URL
}/api/search/${encodeURIComponent(hostname)}`;
try {
res = await fetch(url, {
method: "GET",
Expand Down Expand Up @@ -73,38 +80,44 @@ export async function middleware(req) {
console.error(`custom domain NOT matched "${hostname}"`);
}

// if not in sessionRequired or adminRequired, skip
if (
!sessionRequired
.concat(adminRequired)
.some((path) => reqPathName.startsWith(path))
) {
return NextResponse.next();
}

const session = await getToken({
// Check token existence or validity
const token = await getToken({
req: req,
secret: process.env.NEXTAUTH_SECRET,
});

// if no session reject request
if (!session) {
console.log(token)

// if no token reject request
if (!token) {
if (reqPathName.startsWith("/api")) {
return NextResponse.json({}, { status: 401 });
}
return NextResponse.redirect(new URL("/auth/signin", req.url));
}

const username = session.username;
// if admin request check user is allowed
if (adminRequired.some((path) => reqPathName.startsWith(path))) {
if (!adminUsers.includes(username)) {
if (reqPathName.startsWith("/api")) {
return NextResponse.json({}, { status: 401 });
}
return NextResponse.redirect(new URL("/404", req.url));
// Premium path
const isPremiumRoute = premiumRequired.some((path) => reqPathName.startsWith(path))
const isUserPremium = token.accountType === "premium"
if (isPremiumRoute && !isUserPremium) {
if (reqPathName.startsWith("/api")) {
return NextResponse.json({}, { status: 401 });
}
return NextResponse.redirect(new URL("/pricing", req.url))
}

// Admin Path
const username = token.username;
const isAdminRoute = adminRequired.some((path) => reqPathName.startsWith(path))
const isUserAdmin = adminUsers.includes(username)
if (isAdminRoute && !isUserAdmin) {
if (reqPathName.startsWith("/api")) {
return NextResponse.json({}, { status: 401 });
}
return NextResponse.redirect(new URL("/404", req.url));
}

// Allow request
return NextResponse.next();
}

12 changes: 2 additions & 10 deletions pages/account/statistics/link/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@ const DynamicChart = dynamic(
export async function getServerSideProps(context) {
const { req, res } = context;
const session = await getServerSession(req, res, authOptions);
if (session.accountType !== "premium") {
return {
redirect: {
destination: "/account/onboarding",
permanent: false,
},
};
}

const username = session.username;
const { status, profile } = await getUserApi(req, res, username);
Expand Down Expand Up @@ -75,8 +67,8 @@ export default function Statistics({ data }) {
)}

{data.stats.length > 0 && (
<div className="border mb-6 dark:border-primary-medium">
<div className="border-b border-primary-low bg-white dark:bg-primary-high dark:border-primary-medium px-4 py-5 mb-2 sm:px-6">
<div className="mb-6 border dark:border-primary-medium">
<div className="px-4 py-5 mb-2 bg-white border-b border-primary-low dark:bg-primary-high dark:border-primary-medium sm:px-6">
<h3 className="text-lg font-medium leading-6 text-primary-high">
Link clicks for {data.url}
</h3>
Expand Down
17 changes: 4 additions & 13 deletions pages/account/statistics/locations.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,6 @@ export async function getServerSideProps(context) {
const { req, res } = context;
const session = await getServerSession(req, res, authOptions);

if (session.accountType !== "premium") {
return {
redirect: {
destination: "/account/onboarding",
permanent: false,
},
};
}

const username = session.username;
const { status, profile } = await getUserApi(req, res, username);
if (status !== 200) {
Expand All @@ -50,7 +41,7 @@ export async function getServerSideProps(context) {
stats = Object.keys(profile.stats.countries)
.map((country) => ({
country,
value: profile.stats.countries[country],
value: profile.stats.countries[ country ],
}))
.sort((a, b) => b.value - a.value);
}
Expand Down Expand Up @@ -99,14 +90,14 @@ export default function Locations({ stats }) {
</th>
</tr>
</thead>
<tbody className="divide-y divide-primary-low dark:divide-primary-medium bg-white dark:bg-primary-high">
<tbody className="bg-white divide-y divide-primary-low dark:divide-primary-medium dark:bg-primary-high">
{stats &&
stats.map((item) => (
<tr key={item.country}>
<td className="md:whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-primary-high dark:text-primary-low sm:pl-6">
<td className="py-4 pl-4 pr-3 text-sm font-medium md:whitespace-nowrap text-primary-high dark:text-primary-low sm:pl-6">
{item.country}
</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-primary-medium dark:text-primary-low">
<td className="px-3 py-4 text-sm whitespace-nowrap text-primary-medium dark:text-primary-low">
{abbreviateNumber(item.value)}
</td>
</tr>
Expand Down
17 changes: 4 additions & 13 deletions pages/account/statistics/referers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,6 @@ export async function getServerSideProps(context) {
const { req, res } = context;
const session = await getServerSession(req, res, authOptions);

if (session.accountType !== "premium") {
return {
redirect: {
destination: "/account/onboarding",
permanent: false,
},
};
}

const username = session.username;
const { status, profile } = await getUserApi(req, res, username);
if (status !== 200) {
Expand All @@ -50,7 +41,7 @@ export async function getServerSideProps(context) {
stats = Object.keys(profile.stats.referers)
.map((referer) => ({
referer,
value: profile.stats.referers[referer],
value: profile.stats.referers[ referer ],
}))
.sort((a, b) => b.value - a.value);
}
Expand Down Expand Up @@ -99,14 +90,14 @@ export default function Locations({ stats }) {
</th>
</tr>
</thead>
<tbody className="divide-y divide-primary-low dark:divide-primary-medium bg-white dark:bg-primary-high">
<tbody className="bg-white divide-y divide-primary-low dark:divide-primary-medium dark:bg-primary-high">
{stats &&
stats.map((item) => (
<tr key={item.referer}>
<td className="md:whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-primary-high dark:text-primary-low sm:pl-6">
<td className="py-4 pl-4 pr-3 text-sm font-medium md:whitespace-nowrap text-primary-high dark:text-primary-low sm:pl-6">
{item.referer.replaceAll("|", ".")}
</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-primary-medium dark:text-primary-low">
<td className="px-3 py-4 text-sm whitespace-nowrap text-primary-medium dark:text-primary-low">
{abbreviateNumber(item.value)}
</td>
</tr>
Expand Down
14 changes: 10 additions & 4 deletions pages/api/auth/[...nextauth].js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ export const authOptions = {
token.id = profile.id;
token.username = profile.login;
}
const user = await User.findOne({ _id: token.sub });
if (user) {
token.accountType = user.type;
} else {
token.accountType = "free";
}
return token;
},
async session({ session, token }) {
Expand Down Expand Up @@ -122,13 +128,13 @@ export const authOptions = {
upsert: true,
},
);
const link = await Link.create([defaultLink(profile._id)], {
const link = await Link.create([ defaultLink(profile._id) ], {
new: true,
});
profile = await Profile.findOneAndUpdate(
{ username },
{
$push: { links: new ObjectId(link[0]._id) },
$push: { links: new ObjectId(link[ 0 ]._id) },
},
{ new: true },
);
Expand Down Expand Up @@ -159,13 +165,13 @@ export const authOptions = {
// add github link to profile if no links exist
if (profile.links.length === 0) {
logger.info("no links found for: ", username);
const link = await Link.create([defaultLink(profile._id)], {
const link = await Link.create([ defaultLink(profile._id) ], {
new: true,
});
await Profile.findOneAndUpdate(
{ username },
{
$push: { links: new ObjectId(link[0]._id) },
$push: { links: new ObjectId(link[ 0 ]._id) },
},
);
}
Expand Down
8 changes: 4 additions & 4 deletions tests/account/stats/location.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const premiumUser = {
name: "Automated Test Premium User",
email: "[email protected]",
username: "_test-profile-user-6",
type: "premium",
accountType: "premium",
};

test("Guest user cannot access premium locations stats", async ({
Expand All @@ -25,7 +25,7 @@ test("Logged in free user cannot access premium locations stats", async ({
const page = await context.newPage();
await page.goto("/account/statistics/locations");
await page.waitForLoadState("networkidle");
await expect(page).toHaveURL(/account\/onboarding/);
await expect(page).toHaveURL(/\/pricing/);
});

test("Logged in premium user can access premium locations stats", async ({
Expand All @@ -46,7 +46,7 @@ test.describe("accessibility tests (light)", () => {
const page = await context.newPage();
await page.goto("/account/statistics/locations");
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.withTags([ "wcag2a", "wcag2aa", "wcag21a", "wcag21aa" ])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Expand All @@ -62,7 +62,7 @@ test.describe("accessibility tests (dark)", () => {
const page = await context.newPage();
await page.goto("/account/statistics/locations");
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.withTags([ "wcag2a", "wcag2aa", "wcag21a", "wcag21aa" ])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Expand Down
6 changes: 3 additions & 3 deletions tests/account/stats/referer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test("Logged in free user cannot access premium referers stats", async ({
const page = await context.newPage();
await page.goto("/account/statistics/referers");
await page.waitForLoadState("networkidle");
await expect(page).toHaveURL(/account\/onboarding/);
await expect(page).toHaveURL(/\/pricing/);
});

test("Logged in premium user can access premium referers stats", async ({
Expand All @@ -44,7 +44,7 @@ test.describe("accessibility tests (light)", () => {
const page = await context.newPage();
await page.goto("/account/statistics/referers");
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.withTags([ "wcag2a", "wcag2aa", "wcag21a", "wcag21aa" ])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Expand All @@ -60,7 +60,7 @@ test.describe("accessibility tests (dark)", () => {
const page = await context.newPage();
await page.goto("/account/statistics/referers");
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.withTags([ "wcag2a", "wcag2aa", "wcag21a", "wcag21aa" ])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Expand Down

0 comments on commit 14d7b50

Please sign in to comment.