Continue typescript integration

This commit is contained in:
Michael
2021-08-24 11:57:13 +02:00
parent 5a24246d09
commit a7c614bcc9
22 changed files with 278 additions and 85 deletions

View File

@@ -12,13 +12,14 @@ import Image from 'next/image';
import PaymentModal from './PaymentModal';
import Plan from 'public/plan.svg';
import { PriceIds } from 'utils/priceList';
import { Session } from '@supabase/gotrue-js';
import { supabase } from '../utils/supabaseClient';
import { toast } from 'react-toastify';
import { useRouter } from 'next/router';
type DashboardProps = {
profile: { username: string; website: string; avatar_url: string };
session: { user: { email: string } };
session: Session;
plan: string;
};
@@ -92,7 +93,7 @@ const Dashboard = ({ profile, session, plan }: DashboardProps): JSX.Element => {
className="input input-primary input-bordered input-sm flex-1 text-base-100"
id="email"
type="text"
value={session.user.email}
value={session.user?.email}
disabled
/>
</div>

129
package-lock.json generated
View File

@@ -34,6 +34,7 @@
"@next/eslint-plugin-next": "^11.0.1",
"@types/cors": "^2.8.12",
"@types/express-rate-limit": "^5.1.3",
"@types/micro": "^7.3.6",
"@typescript-eslint/eslint-plugin": "^4.29.1",
"autoprefixer": "^10.3.1",
"cypress": "^8.2.0",
@@ -988,6 +989,12 @@
"@types/node": "*"
}
},
"node_modules/@types/component-emitter": {
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz",
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==",
"dev": true
},
"node_modules/@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@@ -1008,6 +1015,15 @@
"resolved": "https://registry.npmjs.org/@types/debounce-promise/-/debounce-promise-3.1.4.tgz",
"integrity": "sha512-9SEVY3nsz+uMN2DwDocftB5TAgZe7D0cOzxxRhpotWs6T4QFqRaTXpXbOSzbk31/7iYcfCkJJPwWGzTxyuGhCg=="
},
"node_modules/@types/engine.io": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.7.tgz",
"integrity": "sha512-qNjVXcrp+1sS8YpRUa714r0pgzOwESdW5UjHL7D/2ZFdBX0BXUXtg1LUrp+ylvqbvMcMWUy73YpRoxPN2VoKAQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/express": {
"version": "4.17.13",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
@@ -1046,6 +1062,16 @@
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
"dev": true
},
"node_modules/@types/micro": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/@types/micro/-/micro-7.3.6.tgz",
"integrity": "sha512-rZHvZ3+Ev3cGJJSy/wtSiXZmafU8guI07PHXf4ku9sQLfDuFALHMCiV+LuH4VOaeMMMnRs8nqxU392gxfn661g==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/socket.io": "2.1.13"
}
},
"node_modules/@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -1125,6 +1151,27 @@
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
"dev": true
},
"node_modules/@types/socket.io": {
"version": "2.1.13",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.13.tgz",
"integrity": "sha512-JRgH3nCgsWel4OPANkhH8TelpXvacAJ9VeryjuqCDiaVDMpLysd6sbt0dr6Z15pqH3p2YpOT3T1C5vQ+O/7uyg==",
"dev": true,
"dependencies": {
"@types/engine.io": "*",
"@types/node": "*",
"@types/socket.io-parser": "*"
}
},
"node_modules/@types/socket.io-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/socket.io-parser/-/socket.io-parser-3.0.0.tgz",
"integrity": "sha512-Ry/rbTE6HQNL9eu3LpL1Ocup5VexXu1bSSGlSho/IR5LuRc8YvxwSNJ3JxqTltVJEATLbZkMQETSbxfKNgp4Ew==",
"deprecated": "This is a stub types definition. socket.io-parser provides its own type definitions, so you do not need this installed.",
"dev": true,
"dependencies": {
"socket.io-parser": "*"
}
},
"node_modules/@types/websocket": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.4.tgz",
@@ -2281,6 +2328,12 @@
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
},
"node_modules/component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -7331,6 +7384,20 @@
"node": ">=8"
}
},
"node_modules/socket.io-parser": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
"dev": true,
"dependencies": {
"@types/component-emitter": "^1.2.10",
"component-emitter": "~1.3.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/source-map": {
"version": "0.8.0-beta.0",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
@@ -9198,6 +9265,12 @@
"@types/node": "*"
}
},
"@types/component-emitter": {
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz",
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==",
"dev": true
},
"@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@@ -9218,6 +9291,15 @@
"resolved": "https://registry.npmjs.org/@types/debounce-promise/-/debounce-promise-3.1.4.tgz",
"integrity": "sha512-9SEVY3nsz+uMN2DwDocftB5TAgZe7D0cOzxxRhpotWs6T4QFqRaTXpXbOSzbk31/7iYcfCkJJPwWGzTxyuGhCg=="
},
"@types/engine.io": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.7.tgz",
"integrity": "sha512-qNjVXcrp+1sS8YpRUa714r0pgzOwESdW5UjHL7D/2ZFdBX0BXUXtg1LUrp+ylvqbvMcMWUy73YpRoxPN2VoKAQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/express": {
"version": "4.17.13",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
@@ -9256,6 +9338,16 @@
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
"dev": true
},
"@types/micro": {
"version": "7.3.6",
"resolved": "https://registry.npmjs.org/@types/micro/-/micro-7.3.6.tgz",
"integrity": "sha512-rZHvZ3+Ev3cGJJSy/wtSiXZmafU8guI07PHXf4ku9sQLfDuFALHMCiV+LuH4VOaeMMMnRs8nqxU392gxfn661g==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/socket.io": "2.1.13"
}
},
"@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -9335,6 +9427,26 @@
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
"dev": true
},
"@types/socket.io": {
"version": "2.1.13",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.13.tgz",
"integrity": "sha512-JRgH3nCgsWel4OPANkhH8TelpXvacAJ9VeryjuqCDiaVDMpLysd6sbt0dr6Z15pqH3p2YpOT3T1C5vQ+O/7uyg==",
"dev": true,
"requires": {
"@types/engine.io": "*",
"@types/node": "*",
"@types/socket.io-parser": "*"
}
},
"@types/socket.io-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/socket.io-parser/-/socket.io-parser-3.0.0.tgz",
"integrity": "sha512-Ry/rbTE6HQNL9eu3LpL1Ocup5VexXu1bSSGlSho/IR5LuRc8YvxwSNJ3JxqTltVJEATLbZkMQETSbxfKNgp4Ew==",
"dev": true,
"requires": {
"socket.io-parser": "*"
}
},
"@types/websocket": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.4.tgz",
@@ -10182,6 +10294,12 @@
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -14023,6 +14141,17 @@
"is-fullwidth-code-point": "^3.0.0"
}
},
"socket.io-parser": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
"dev": true,
"requires": {
"@types/component-emitter": "^1.2.10",
"component-emitter": "~1.3.0",
"debug": "~4.3.1"
}
},
"source-map": {
"version": "0.8.0-beta.0",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",

View File

@@ -38,6 +38,7 @@
"@next/eslint-plugin-next": "^11.0.1",
"@types/cors": "^2.8.12",
"@types/express-rate-limit": "^5.1.3",
"@types/micro": "^7.3.6",
"@typescript-eslint/eslint-plugin": "^4.29.1",
"autoprefixer": "^10.3.1",
"cypress": "^8.2.0",
@@ -57,4 +58,4 @@
"prettier": "^2.3.2",
"tailwindcss": "^2.2.7"
}
}
}

View File

@@ -1,5 +1,6 @@
import './global.css';
import { AppProps } from 'next/app';
import { AuthProvider } from 'utils/AuthContext';
import { DefaultSeo } from 'next-seo';
@@ -8,7 +9,7 @@ Next-seo is integrated by default, if you want more information and how to
setup more elements, visit their Github page https://github.com/garmeeh/next-seo
*/
function MyApp({ Component, pageProps }) {
function MyApp({ Component, pageProps }: AppProps): JSX.Element {
return (
<>
<AuthProvider>

View File

@@ -1,12 +1,20 @@
import Document, { Head, Html, Main, NextScript } from 'next/document';
import Document, {
DocumentContext,
DocumentInitialProps,
Head,
Html,
Main,
NextScript,
} from 'next/document';
class MyDocument extends Document {
static async getInitialProps(ctx) {
static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
return initialProps;
}
render() {
render(): JSX.Element {
// This will set the initial theme, saved in localstorage
const setInitialTheme = `
function getUserPreference() {

View File

@@ -3,8 +3,10 @@
* With SupaNexTail, we use SSR with the Dashboard page (pages/dashboard.js)
*/
import type { NextApiRequest, NextApiResponse } from 'next';
import { supabase } from 'utils/supabaseClient';
export default function handler(req, res) {
export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
supabase.auth.api.setAuthCookie(req, res);
}

View File

@@ -1,13 +0,0 @@
import { supabase } from 'utils/supabaseClient';
// Example of how to verify and get user data server-side.
const getUser = async (req, res) => {
const { token } = req.headers;
const { data: user, error } = await supabase.auth.api.getUser(token);
if (error) return res.status(401).json({ error: error.message });
return res.status(200).json(user);
};
export default getUser;

21
pages/api/getUser.ts Normal file
View File

@@ -0,0 +1,21 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { supabase } from 'utils/supabaseClient';
// Example of how to verify and get user data server-side.
const getUser = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
const token = req.headers.token;
if (typeof token !== 'string') {
return res.status(401).json({ error: 'Missing auth token.' });
}
if (token) {
const { data: user, error } = await supabase.auth.api.getUser(token);
if (error) return res.status(401).json({ error: error.message });
return res.status(200).json(user);
}
};
export default getUser;

View File

@@ -2,19 +2,22 @@
This is a simple contact form for SupaNexTail
Using Sendgrid.
*/
import type { NextApiRequest, NextApiResponse } from 'next';
import sgMail from '@sendgrid/mail';
export default async function handler(req, res) {
const sendGrid = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
if (req.method === 'POST') {
sgMail.setApiKey(process.env.SENDGRID_SECRET);
sgMail.setApiKey(process.env.SENDGRID_SECRET || '');
const msg = {
to: process.env.SENDGRID_MAILTO, // Change to your recipient
from: process.env.SENDGRID_MAILFROM, // Change to your verified sender
to: process.env.SENDGRID_MAILTO || '', // Change to your recipient
from: process.env.SENDGRID_MAILFROM || '', // Change to your verified sender
subject: `[${process.env.NEXT_PUBLIC_TITLE}] New message from ${req.body.name}`,
text: req.body.message,
reply_to: req.body.email,
};
sgMail
.send(msg)
.then(() => {
@@ -24,8 +27,9 @@ export default async function handler(req, res) {
console.error(error);
res.status(500).send({
message: 'There was an issue with your email... please retry',
err,
error,
});
});
}
}
};
export default sendGrid;

View File

@@ -1,9 +1,9 @@
/* Dont forget to create your customer portal on Stripe
https://dashboard.stripe.com/test/settings/billing/portal */
import * as Stripe from 'stripe';
import type { NextApiRequest, NextApiResponse } from 'next';
import Cors from 'cors';
import Stripe from 'stripe';
import initMiddleware from 'utils/init-middleware';
import rateLimit from 'express-rate-limit';
@@ -21,9 +21,11 @@ const limiter = initMiddleware(
);
// Set your secret key. Remember to switch to your live secret key in production.
// See your keys here: https://dashboard.stripe.com/apikeys
const stripe = new Stripe(process.env.STRIPE_SECRET);
const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
apiVersion: '2020-08-27',
});
export default async function handler(req, res) {
export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
await cors(req, res);
await limiter(req, res);
if (req.method === 'POST') {

View File

@@ -6,9 +6,10 @@ If you want to test it locally, you'll need the stripe CLI and use this command
stripe listen --forward-to localhost:3000/api/stripe/stripe-webhook
*/
import * as Stripe from 'stripe';
import type { NextApiRequest, NextApiResponse } from 'next';
import Cors from 'cors';
import Stripe from 'stripe';
import { buffer } from 'micro';
import { createClient } from '@supabase/supabase-js';
import initMiddleware from 'utils/init-middleware';
@@ -29,7 +30,10 @@ const cors = initMiddleware(
// Init Supabase Admin
const supabase = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.SUPABASE_ADMIN_KEY);
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL || '',
process.env.SUPABASE_ADMIN_KEY || ''
);
// Rate limiter : The user can only create one list every 20 seconds (avoid spam)
@@ -42,24 +46,24 @@ const limiter = initMiddleware(
// Set your secret key. Remember to switch to your live secret key in production.
// See your keys here: https://dashboard.stripe.com/apikeys
const stripe = new Stripe(process.env.STRIPE_SECRET);
const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
apiVersion: '2020-08-27',
maxNetworkRetries: 2,
});
export default async function handler(req, res) {
export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
await cors(req, res);
await limiter(req, res);
stripe.setMaxNetworkRetries(2);
if (req.method === 'POST') {
// Retrieve the event by verifying the signature using the raw body and secret.
let event;
let event: Stripe.Event;
const buf = await buffer(req);
const sig = req.headers['stripe-signature'] as string;
try {
event = stripe.webhooks.constructEvent(
buf,
req.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK
);
event = stripe.webhooks.constructEvent(buf, sig, process.env.STRIPE_WEBHOOK || '');
} catch (err) {
console.log(err);
console.log(`⚠️ Webhook signature verification failed.`);
@@ -67,7 +71,14 @@ export default async function handler(req, res) {
return res.send(400);
}
// Extract the object from the event.
const dataObject = event.data.object;
const dataObject = event.data.object as {
client_reference_id: string;
customer: string;
metadata: {
priceId: string;
};
subscription: string;
};
// Handle the event
// Review important events for Billing webhooks
@@ -75,18 +86,16 @@ export default async function handler(req, res) {
// Remove comment to see the various objects sent for this sample
switch (event.type) {
case 'checkout.session.completed':
const { data: subscriptions, error } = await supabase
const { data: subscriptions } = await supabase
.from('subscriptions')
.select('*')
.eq('id', dataObject.client_reference_id);
console.log(dataObject);
if (subscriptions.length == 0) {
const { data, error } = await supabase
if (subscriptions?.length == 0) {
await supabase
.from('profiles')
.update({ customerId: dataObject.customer })
.eq('id', dataObject.client_reference_id);
if (error) console.log(error);
await supabase
.from('subscriptions')
@@ -100,8 +109,8 @@ export default async function handler(req, res) {
},
])
.then()
.catch((err) => console.log(err));
} else if (subscriptions.length > 0) {
.then(null, (err) => console.log('err: ', err)); // catch
} else if (subscriptions?.length && subscriptions?.length > 0) {
await supabase
.from('subscriptions')
.update({
@@ -112,7 +121,7 @@ export default async function handler(req, res) {
})
.eq('id', dataObject.client_reference_id)
.then()
.catch((err) => console.log(err));
.then(null, (err) => console.log('err: ', err)); // catch
}
break;
case 'customer.subscription.deleted':
@@ -121,7 +130,7 @@ export default async function handler(req, res) {
.update({ paid_user: false })
.eq('customer_id', dataObject.customer)
.then()
.catch((err) => console.log(err));
.then(null, (err) => console.log('err: ', err)); // catch
break;
case 'invoice.payment_failed':
// If the payment fails or the customer does not have a valid payment method,

View File

@@ -2,7 +2,7 @@ import Contact from 'components/Contact';
import Layout from 'components/Layout';
import { NextSeo } from 'next-seo';
const ContactPage = () => (
const ContactPage = (): JSX.Element => (
<>
<NextSeo
title={`${process.env.NEXT_PUBLIC_TITLE} | Contact`}

View File

@@ -1,18 +1,31 @@
import * as Stripe from 'stripe';
import { useEffect, useState } from 'react';
import Dashboard from '../components/Dashboard';
import Head from 'next/head';
import Layout from 'components/Layout';
import type { NextPageContext } from 'next';
import Stripe from 'stripe';
import { createClient } from '@supabase/supabase-js';
import { supabase } from '../utils/supabaseClient';
import { useRouter } from 'next/router';
const DashboardPage = ({ user, plan, profile }) => {
const [session, setSession] = useState(null);
const DashboardPage = ({
user,
plan,
profile,
}: {
user: {
id: string;
};
plan: string;
profile: {
username: string;
website: string;
avatar_url: string;
};
}): JSX.Element => {
const [session, setSession] = useState(supabase.auth.session());
const router = useRouter();
useEffect(() => {
// If a user is not logged in, return to the homepage
if (!user) {
@@ -39,24 +52,35 @@ const DashboardPage = ({ user, plan, profile }) => {
<div className="text-center">You are not logged in</div>
) : (
<>
<Dashboard key={user.id} session={session} plan={plan} profile={profile} />
{session && (
<Dashboard
key={user.id || undefined}
session={session}
plan={plan}
profile={profile}
/>
)}
</>
)}
</Layout>
</div>
);
};
export async function getServerSideProps({ req }) {
export async function getServerSideProps(context: NextPageContext) {
const supabaseAdmin = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.SUPABASE_ADMIN_KEY
process.env.NEXT_PUBLIC_SUPABASE_URL || '',
process.env.SUPABASE_ADMIN_KEY || ''
);
const { user } = await supabaseAdmin.auth.api.getUserByCookie(req);
const stripe = new Stripe(process.env.STRIPE_SECRET);
const { user } = await supabaseAdmin.auth.api.getUserByCookie(context.req);
const stripe = new Stripe(process.env.STRIPE_SECRET || '', {
apiVersion: '2020-08-27',
maxNetworkRetries: 2,
});
// If the user exist, you will retrieve the user profile and if he/she's a paid user
if (user) {
const { data: plan, error } = await supabaseAdmin
const { data: plan } = await supabaseAdmin
.from('subscriptions')
.select('plan')
.eq('id', user.id)
@@ -65,7 +89,7 @@ export async function getServerSideProps({ req }) {
// Check the subscription plan. If it doesnt exist, return null
const subscription = plan?.plan ? await stripe.subscriptions.retrieve(plan.plan) : null;
const { data: profile, errorProfile } = await supabaseAdmin
const { data: profile } = await supabaseAdmin
.from('profiles')
.select(`username, website, avatar_url`)
.eq('id', user.id)
@@ -74,7 +98,7 @@ export async function getServerSideProps({ req }) {
return {
props: {
user,
plan: subscription?.plan?.id ? subscription.plan.id : null,
plan: subscription?.items.data[0].price.id ? subscription?.items.data[0].price.id : null,
profile,
},
};
@@ -86,5 +110,6 @@ export async function getServerSideProps({ req }) {
}
// If there is a user, return it.
return null;
}
export default DashboardPage;

View File

@@ -7,7 +7,7 @@ import Head from 'next/head';
import Landing from 'components/Landing';
import Layout from 'components/Layout';
const Home = () => (
const Home = (): JSX.Element => (
<>
<Head>
<title>{`Welcome to ${process.env.NEXT_PUBLIC_TITLE} 👋`}</title>

View File

@@ -9,8 +9,8 @@ import Login from 'components/UI/Login';
import { NextSeo } from 'next-seo';
import { useAuth } from 'utils/AuthContext';
const LoginPage = () => {
const { signUp, signIn, signOut, resetPassword } = useAuth();
const LoginPage = (): JSX.Element => {
const { signIn, resetPassword } = useAuth();
return (
<>
<NextSeo
@@ -20,7 +20,7 @@ const LoginPage = () => {
<Layout>
<div className="flex flex-wrap justify-evenly w-full mt-20">
<Login signUp={signUp} signIn={signIn} signOut={signOut} resetPassword={resetPassword} />
<Login signIn={signIn} resetPassword={resetPassword} />
</div>
</Layout>
</>

View File

@@ -4,7 +4,7 @@ import Layout from 'components/Layout';
import { NextSeo } from 'next-seo';
import Pricing from 'components/Pricing';
const PricingPage = () => (
const PricingPage = (): JSX.Element => (
<>
<NextSeo
title={`${process.env.NEXT_PUBLIC_TITLE} | Pricing`}

View File

@@ -4,7 +4,7 @@ import Layout from 'components/Layout';
import { NextSeo } from 'next-seo';
import PrivacyPolicy from 'components/PrivacyPolicy';
const PrivacyPage = () => (
const PrivacyPage = (): JSX.Element => (
<>
<NextSeo
title={`${process.env.NEXT_PUBLIC_TITLE} | Privacy Policy`}

View File

@@ -9,7 +9,7 @@ import AuthText from 'components/AuthText';
import Layout from 'components/Layout';
import { NextSeo } from 'next-seo';
const SignUpPage = () => (
const SignUpPage = (): JSX.Element => (
<>
<NextSeo
title={`${process.env.NEXT_PUBLIC_TITLE} | Auth`}

View File

@@ -4,7 +4,7 @@ import Layout from 'components/Layout';
import { NextSeo } from 'next-seo';
import Terms from 'components/Terms';
const TermsPage = () => (
const TermsPage = (): JSX.Element => (
<>
<NextSeo
title={`${process.env.NEXT_PUBLIC_TITLE} | Terms and conditions`}

View File

@@ -1,9 +1,12 @@
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
export default function initMiddleware(middleware) {
return (req, res) =>
import type { NextApiRequest, NextApiResponse } from 'next';
export default function initMiddleware(middleware: any) {
return (req: NextApiRequest, res: NextApiResponse) =>
new Promise((resolve, reject) => {
middleware(req, res, (result) => {
middleware(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}

View File

@@ -1,12 +1,12 @@
/**
* This is a singleton to ensure we only instantiate Stripe once.
*/
import { loadStripe } from '@stripe/stripe-js';
import { Stripe, loadStripe } from '@stripe/stripe-js';
let stripePromise = null;
const getStripe = () => {
let stripePromise: Promise<Stripe | null>;
const getStripe = (): Promise<Stripe | null> => {
if (!stripePromise) {
stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY);
stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY || '');
}
return stripePromise;
};

View File

@@ -3,11 +3,11 @@ import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
export const supabase = createClient(supabaseUrl || '', supabaseAnonKey || '');
// Check if a user has a paid plan
export const getSub = async () => {
const { data: subscriptions, error } = await supabase
const { data: subscriptions } = await supabase
.from('subscriptions')
.select('paid_user, plan')
.single();