mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-06 18:31:00 +00:00
Added basic cron job, endpoint, and vercel setup just wrong intervals right now, also added admin and subscriptionExpiredAt into Role schema so I can show user a special message after their subscription expired
This commit is contained in:
parent
fb02ea79af
commit
e69d974ad7
@ -1,4 +1,3 @@
|
|||||||
// next.config.js
|
|
||||||
const removeImports = require("next-remove-imports")();
|
const removeImports = require("next-remove-imports")();
|
||||||
|
|
||||||
module.exports = removeImports({
|
module.exports = removeImports({
|
||||||
@ -9,4 +8,12 @@ module.exports = removeImports({
|
|||||||
webpack(config, options) {
|
webpack(config, options) {
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
async rewrites() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: '/api/cron',
|
||||||
|
destination: '/api/cron',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
});
|
});
|
@ -15,8 +15,10 @@ CREATE TABLE "Role" (
|
|||||||
"id" TEXT NOT NULL,
|
"id" TEXT NOT NULL,
|
||||||
"userId" TEXT NOT NULL,
|
"userId" TEXT NOT NULL,
|
||||||
"subscribed" BOOLEAN NOT NULL DEFAULT false,
|
"subscribed" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"admin" BOOLEAN NOT NULL DEFAULT false,
|
||||||
"subscriptionStartDate" TIMESTAMP(3),
|
"subscriptionStartDate" TIMESTAMP(3),
|
||||||
"lastPaymentAt" TIMESTAMP(3),
|
"lastPaymentAt" TIMESTAMP(3),
|
||||||
|
"subscriptionExpiredAt" TIMESTAMP(3),
|
||||||
"nwc" TEXT,
|
"nwc" TEXT,
|
||||||
|
|
||||||
CONSTRAINT "Role_pkey" PRIMARY KEY ("id")
|
CONSTRAINT "Role_pkey" PRIMARY KEY ("id")
|
@ -27,8 +27,10 @@ model Role {
|
|||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id])
|
||||||
userId String @unique
|
userId String @unique
|
||||||
subscribed Boolean @default(false)
|
subscribed Boolean @default(false)
|
||||||
|
admin Boolean @default(false)
|
||||||
subscriptionStartDate DateTime?
|
subscriptionStartDate DateTime?
|
||||||
lastPaymentAt DateTime?
|
lastPaymentAt DateTime?
|
||||||
|
subscriptionExpiredAt DateTime?
|
||||||
nwc String?
|
nwc String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +104,26 @@ const SubscriptionPaymentButtons = ({ onSuccess, onError, onRecurringSubscriptio
|
|||||||
const newNWCUrl = newNwc.getNostrWalletConnectUrl();
|
const newNWCUrl = newNwc.getNostrWalletConnectUrl();
|
||||||
|
|
||||||
if (newNWCUrl) {
|
if (newNWCUrl) {
|
||||||
|
const nwc = new webln.NostrWebLNProvider({
|
||||||
|
nostrWalletConnectUrl: newNWCUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
await nwc.enable();
|
||||||
|
|
||||||
|
const invoice = await fetchInvoice();
|
||||||
|
|
||||||
|
if (!invoice || !invoice.paymentRequest) {
|
||||||
|
showToast('error', 'NWC', `Failed to fetch invoice from ${lnAddress}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentResponse = await nwc.sendPayment(invoice.paymentRequest);
|
||||||
|
|
||||||
|
if (!paymentResponse || !paymentResponse?.preimage) {
|
||||||
|
showToast('error', 'NWC', 'Payment failed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const subscriptionResponse = await axios.put('/api/users/subscription', {
|
const subscriptionResponse = await axios.put('/api/users/subscription', {
|
||||||
userId: session.user.id,
|
userId: session.user.id,
|
||||||
isSubscribed: true,
|
isSubscribed: true,
|
||||||
|
@ -111,12 +111,14 @@ export const updateUserSubscription = async (userId, isSubscribed, nwc) => {
|
|||||||
subscriptionStartDate: isSubscribed ? now : null,
|
subscriptionStartDate: isSubscribed ? now : null,
|
||||||
lastPaymentAt: isSubscribed ? now : null,
|
lastPaymentAt: isSubscribed ? now : null,
|
||||||
nwc: nwc ? nwc : null,
|
nwc: nwc ? nwc : null,
|
||||||
|
subscriptionExpiredAt: null,
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
subscribed: isSubscribed,
|
subscribed: isSubscribed,
|
||||||
subscriptionStartDate: isSubscribed ? { set: now } : { set: null },
|
subscriptionStartDate: isSubscribed ? { set: now } : { set: null },
|
||||||
lastPaymentAt: isSubscribed ? now : { set: null },
|
lastPaymentAt: isSubscribed ? now : { set: null },
|
||||||
nwc: nwc ? nwc : null,
|
nwc: nwc ? nwc : null,
|
||||||
|
subscriptionExpiredAt: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -126,3 +128,38 @@ export const updateUserSubscription = async (userId, isSubscribed, nwc) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const checkAndUpdateExpiredSubscriptions = async () => {
|
||||||
|
const now = new Date();
|
||||||
|
const oneHourAgo = new Date(now.getTime() - 1 * 60 * 60 * 1000);
|
||||||
|
const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);
|
||||||
|
|
||||||
|
const expiredSubscriptions = await prisma.role.findMany({
|
||||||
|
where: {
|
||||||
|
subscribed: true,
|
||||||
|
lastPaymentAt: {
|
||||||
|
lt: fiveMinutesAgo
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
userId: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatePromises = expiredSubscriptions.map(({ userId }) =>
|
||||||
|
prisma.role.update({
|
||||||
|
where: { userId },
|
||||||
|
data: {
|
||||||
|
subscribed: false,
|
||||||
|
subscriptionStartDate: null,
|
||||||
|
lastPaymentAt: null,
|
||||||
|
nwc: null,
|
||||||
|
subscriptionExpiredAt: now,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await prisma.$transaction(updatePromises);
|
||||||
|
|
||||||
|
return expiredSubscriptions.length;
|
||||||
|
};
|
||||||
|
20
src/pages/api/users/subscription/cron.js
Normal file
20
src/pages/api/users/subscription/cron.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { checkAndUpdateExpiredSubscriptions } from "@/db/models/userModels";
|
||||||
|
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
if (req.headers.authorization !== `Bearer ${process.env.CRON_SECRET}`) {
|
||||||
|
return res.status(401).json({ error: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method === 'POST') {
|
||||||
|
try {
|
||||||
|
const updatedCount = await checkAndUpdateExpiredSubscriptions();
|
||||||
|
res.status(200).json({ message: `Cron job completed successfully. Updated ${updatedCount} subscriptions.` });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Cron job error:', error);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.setHeader('Allow', ['POST']);
|
||||||
|
res.status(405).end(`Method ${req.method} Not Allowed`);
|
||||||
|
}
|
||||||
|
}
|
@ -20,12 +20,18 @@ const Profile = () => {
|
|||||||
const [subscribeModalVisible, setSubscribeModalVisible] = useState(false); // Add this state
|
const [subscribeModalVisible, setSubscribeModalVisible] = useState(false); // Add this state
|
||||||
const [subscribed, setSubscribed] = useState(false);
|
const [subscribed, setSubscribed] = useState(false);
|
||||||
const [subscribedUntil, setSubscribedUntil] = useState(null);
|
const [subscribedUntil, setSubscribedUntil] = useState(null);
|
||||||
|
const [subscriptionExpiredAt, setSubscriptionExpiredAt] = useState(null);
|
||||||
|
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status, update } = useSession();
|
||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
const { ndk } = useNDKContext();
|
const { ndk } = useNDKContext();
|
||||||
const menu = useRef(null);
|
const menu = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Refetch the session when the component mounts
|
||||||
|
update();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session && session.user) {
|
if (session && session.user) {
|
||||||
setUser(session.user);
|
setUser(session.user);
|
||||||
@ -35,6 +41,10 @@ const Profile = () => {
|
|||||||
// The user is subscribed until the date in subscribedAt + 30 days
|
// The user is subscribed until the date in subscribedAt + 30 days
|
||||||
const subscribedUntil = new Date(subscribedAt.getTime() + 30 * 24 * 60 * 60 * 1000);
|
const subscribedUntil = new Date(subscribedAt.getTime() + 30 * 24 * 60 * 60 * 1000);
|
||||||
setSubscribedUntil(subscribedUntil);
|
setSubscribedUntil(subscribedUntil);
|
||||||
|
if (session.user.role.subscriptionExpiredAt) {
|
||||||
|
const expiredAt = new Date(session.user.role.subscriptionExpiredAt)
|
||||||
|
setSubscriptionExpiredAt(expiredAt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [session]);
|
}, [session]);
|
||||||
@ -96,13 +106,14 @@ const Profile = () => {
|
|||||||
<BitcoinConnectButton />
|
<BitcoinConnectButton />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-1/2 mx-auto my-4 justify-between items-center border-2 border-gray-700 bg-[#121212] p-8 rounded-md">
|
<div className="flex flex-col w-1/2 mx-auto my-4 justify-between items-center border-2 border-gray-700 bg-[#121212] p-8 rounded-md">
|
||||||
{subscribed ? (
|
{subscribed && (
|
||||||
<>
|
<>
|
||||||
<Message severity="success" text="Subscribed!" />
|
<Message severity="success" text="Subscribed!" />
|
||||||
<p className="mt-8">Thank you for your support 🎉</p>
|
<p className="mt-8">Thank you for your support 🎉</p>
|
||||||
<p className="text-sm text-gray-400">Pay-as-you-go subscription will renew on {subscribedUntil.toLocaleDateString()}</p>
|
<p className="text-sm text-gray-400">Pay-as-you-go subscription will renew on {subscribedUntil.toLocaleDateString()}</p>
|
||||||
</>
|
</>
|
||||||
) : (
|
)}
|
||||||
|
{(!subscribed && !subscriptionExpiredAt) && (
|
||||||
<>
|
<>
|
||||||
<Message severity="info" text="You currently have no active subscription" />
|
<Message severity="info" text="You currently have no active subscription" />
|
||||||
<Button
|
<Button
|
||||||
@ -112,6 +123,16 @@ const Profile = () => {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{subscriptionExpiredAt && (
|
||||||
|
<>
|
||||||
|
<Message severity="warn" text={`Your subscription expired on ${subscriptionExpiredAt.toLocaleDateString()}`} />
|
||||||
|
<Button
|
||||||
|
label="Subscribe"
|
||||||
|
className="w-auto mt-8 text-[#f8f8ff]"
|
||||||
|
onClick={openSubscribeModal} // Add this onClick handler
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!session || !session?.user || !ndk ? (
|
{!session || !session?.user || !ndk ? (
|
||||||
|
9
vercel.json
Normal file
9
vercel.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"crons": [
|
||||||
|
{
|
||||||
|
"path": "/api/users/subscription/cron",
|
||||||
|
"schedule": "0 * * * *"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user