mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-06 18:31:00 +00:00
Checking for admin, subscribed, or purchased videoId in get video endpoint
This commit is contained in:
parent
fd7b7567fe
commit
14b62b3f4a
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
Warnings:
|
|
||||||
|
|
||||||
- A unique constraint covering the columns `[email]` on the table `User` will be added. If there are existing duplicate values, this will fail.
|
|
||||||
|
|
||||||
*/
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "User" ADD COLUMN "email" TEXT,
|
|
||||||
ADD COLUMN "emailVerified" TIMESTAMP(3),
|
|
||||||
ADD COLUMN "image" TEXT,
|
|
||||||
ADD COLUMN "name" TEXT,
|
|
||||||
ADD COLUMN "privkey" TEXT,
|
|
||||||
ALTER COLUMN "pubkey" DROP NOT NULL;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Session" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"sessionToken" TEXT NOT NULL,
|
|
||||||
"userId" TEXT NOT NULL,
|
|
||||||
"expires" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "VerificationToken" (
|
|
||||||
"identifier" TEXT NOT NULL,
|
|
||||||
"token" TEXT NOT NULL,
|
|
||||||
"expires" TIMESTAMP(3) NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Account" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"userId" TEXT NOT NULL,
|
|
||||||
"type" TEXT NOT NULL,
|
|
||||||
"provider" TEXT NOT NULL,
|
|
||||||
"providerAccountId" TEXT NOT NULL,
|
|
||||||
"refresh_token" TEXT,
|
|
||||||
"access_token" TEXT,
|
|
||||||
"expires_at" INTEGER,
|
|
||||||
"token_type" TEXT,
|
|
||||||
"scope" TEXT,
|
|
||||||
"id_token" TEXT,
|
|
||||||
"session_state" TEXT,
|
|
||||||
"oauth_token_secret" TEXT,
|
|
||||||
"oauth_token" TEXT,
|
|
||||||
|
|
||||||
CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -1,7 +1,12 @@
|
|||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE "User" (
|
CREATE TABLE "User" (
|
||||||
"id" TEXT NOT NULL,
|
"id" TEXT NOT NULL,
|
||||||
"pubkey" TEXT NOT NULL,
|
"pubkey" TEXT,
|
||||||
|
"privkey" TEXT,
|
||||||
|
"name" TEXT,
|
||||||
|
"email" TEXT,
|
||||||
|
"emailVerified" TIMESTAMP(3),
|
||||||
|
"image" TEXT,
|
||||||
"username" TEXT,
|
"username" TEXT,
|
||||||
"avatar" TEXT,
|
"avatar" TEXT,
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
@ -10,6 +15,43 @@ CREATE TABLE "User" (
|
|||||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Session" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"sessionToken" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"expires" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "VerificationToken" (
|
||||||
|
"identifier" TEXT NOT NULL,
|
||||||
|
"token" TEXT NOT NULL,
|
||||||
|
"expires" TIMESTAMP(3) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Account" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"type" TEXT NOT NULL,
|
||||||
|
"provider" TEXT NOT NULL,
|
||||||
|
"providerAccountId" TEXT NOT NULL,
|
||||||
|
"refresh_token" TEXT,
|
||||||
|
"access_token" TEXT,
|
||||||
|
"expires_at" INTEGER,
|
||||||
|
"token_type" TEXT,
|
||||||
|
"scope" TEXT,
|
||||||
|
"id_token" TEXT,
|
||||||
|
"session_state" TEXT,
|
||||||
|
"oauth_token_secret" TEXT,
|
||||||
|
"oauth_token" TEXT,
|
||||||
|
|
||||||
|
CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE "Role" (
|
CREATE TABLE "Role" (
|
||||||
"id" TEXT NOT NULL,
|
"id" TEXT NOT NULL,
|
||||||
@ -81,6 +123,7 @@ CREATE TABLE "Resource" (
|
|||||||
"userId" TEXT NOT NULL,
|
"userId" TEXT NOT NULL,
|
||||||
"price" INTEGER NOT NULL DEFAULT 0,
|
"price" INTEGER NOT NULL DEFAULT 0,
|
||||||
"noteId" TEXT,
|
"noteId" TEXT,
|
||||||
|
"videoId" TEXT,
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
@ -123,9 +166,24 @@ CREATE TABLE "CourseDraft" (
|
|||||||
-- CreateIndex
|
-- CreateIndex
|
||||||
CREATE UNIQUE INDEX "User_pubkey_key" ON "User"("pubkey");
|
CREATE UNIQUE INDEX "User_pubkey_key" ON "User"("pubkey");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||||
|
|
||||||
-- CreateIndex
|
-- CreateIndex
|
||||||
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
|
||||||
|
|
||||||
-- CreateIndex
|
-- CreateIndex
|
||||||
CREATE UNIQUE INDEX "Role_userId_key" ON "Role"("userId");
|
CREATE UNIQUE INDEX "Role_userId_key" ON "Role"("userId");
|
||||||
|
|
||||||
@ -135,6 +193,12 @@ CREATE UNIQUE INDEX "Course_noteId_key" ON "Course"("noteId");
|
|||||||
-- CreateIndex
|
-- CreateIndex
|
||||||
CREATE UNIQUE INDEX "Resource_noteId_key" ON "Resource"("noteId");
|
CREATE UNIQUE INDEX "Resource_noteId_key" ON "Resource"("noteId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
-- AddForeignKey
|
-- AddForeignKey
|
||||||
ALTER TABLE "Role" ADD CONSTRAINT "Role_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
ALTER TABLE "Role" ADD CONSTRAINT "Role_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
@ -138,6 +138,7 @@ model Resource {
|
|||||||
price Int @default(0)
|
price Int @default(0)
|
||||||
purchases Purchase[]
|
purchases Purchase[]
|
||||||
noteId String? @unique
|
noteId String? @unique
|
||||||
|
videoId String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
|
||||||
const HeroBanner = () => {
|
const HeroBanner = () => {
|
||||||
@ -6,6 +7,8 @@ const HeroBanner = () => {
|
|||||||
const [currentOption, setCurrentOption] = useState(0);
|
const [currentOption, setCurrentOption] = useState(0);
|
||||||
const [isFlipping, setIsFlipping] = useState(false);
|
const [isFlipping, setIsFlipping] = useState(false);
|
||||||
|
|
||||||
|
const windowWidth = useWindowWidth();
|
||||||
|
|
||||||
const getColorClass = (option) => {
|
const getColorClass = (option) => {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case 'Bitcoin': return 'text-orange-400';
|
case 'Bitcoin': return 'text-orange-400';
|
||||||
@ -31,15 +34,17 @@ const HeroBanner = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex justify-center items-center">
|
<div className="relative flex justify-center items-center">
|
||||||
|
{windowWidth && (
|
||||||
<Image
|
<Image
|
||||||
src="/images/plebdevs-banner.png"
|
src="/images/plebdevs-banner.png"
|
||||||
alt="Banner"
|
alt="Banner"
|
||||||
width={1920}
|
width={windowWidth <= 1920 ? 1920 : windowWidth}
|
||||||
height={1080}
|
height={1080}
|
||||||
quality={100}
|
quality={100}
|
||||||
className='opacity-70'
|
className='opacity-70'
|
||||||
/>
|
/>
|
||||||
<div className="w-[75vw] sm:w-[65vw] md:w-[45vw] lg:w-[45vw] xl:w-[45vw] absolute text-center text-white text-xl h-full flex flex-col justify-evenly">
|
)}
|
||||||
|
<div className="w-[75vw] sm:w-[65vw] md:w-[45vw] lg:w-[45vw] xl:w-[37vw] absolute text-center text-white text-xl h-full flex flex-col justify-evenly">
|
||||||
<p className='text-2xl md:text-4xl lg:text-5xl xl:text-5xl'>Learn how to code</p>
|
<p className='text-2xl md:text-4xl lg:text-5xl xl:text-5xl'>Learn how to code</p>
|
||||||
<p className='text-2xl md:text-4xl lg:text-5xl xl:text-5xl'>
|
<p className='text-2xl md:text-4xl lg:text-5xl xl:text-5xl'>
|
||||||
Build{' '}
|
Build{' '}
|
||||||
|
@ -121,7 +121,6 @@ export const authOptions = {
|
|||||||
return token;
|
return token;
|
||||||
},
|
},
|
||||||
async session({ session, token }) {
|
async session({ session, token }) {
|
||||||
console.log('SESSION', session);
|
|
||||||
// Add user from token to session
|
// Add user from token to session
|
||||||
session.user = token.user;
|
session.user = token.user;
|
||||||
session.jwt = token;
|
session.jwt = token;
|
||||||
|
@ -12,7 +12,15 @@ const s3Client = new S3Client({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
|
try {
|
||||||
|
// Check if the request method is GET
|
||||||
|
if (req.method !== 'GET') {
|
||||||
|
return res.status(405).json({ error: "Method Not Allowed" })
|
||||||
|
}
|
||||||
|
|
||||||
const session = await getServerSession(req, res, authOptions)
|
const session = await getServerSession(req, res, authOptions)
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
@ -21,11 +29,19 @@ export default async function handler(req, res) {
|
|||||||
|
|
||||||
const { videoKey } = req.query
|
const { videoKey } = req.query
|
||||||
|
|
||||||
if (!videoKey) {
|
if (!videoKey || typeof videoKey !== 'string') {
|
||||||
return res.status(400).json({ error: "Video key is required" })
|
return res.status(400).json({ error: "Invalid or missing video key" })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user is authorized to access the video
|
||||||
|
if (!session.user.role?.subscribed && session.user.pubkey !== AUTHOR_PUBKEY) {
|
||||||
|
const purchasedVideo = session.user.purchased?.find(purchase => purchase?.resource?.videoId === videoKey)
|
||||||
|
console.log("purchasedVideo", purchasedVideo)
|
||||||
|
if (!purchasedVideo) {
|
||||||
|
return res.status(403).json({ error: "Forbidden: You don't have access to this video" })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
const command = new GetObjectCommand({
|
const command = new GetObjectCommand({
|
||||||
Bucket: "plebdevs-bucket",
|
Bucket: "plebdevs-bucket",
|
||||||
Key: videoKey,
|
Key: videoKey,
|
||||||
@ -37,7 +53,7 @@ export default async function handler(req, res) {
|
|||||||
|
|
||||||
res.redirect(signedUrl)
|
res.redirect(signedUrl)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error generating signed URL:", error)
|
console.error("Error in get-video-url handler:", error)
|
||||||
res.status(500).json({ error: "Failed to generate video URL" })
|
res.status(500).json({ error: "Internal Server Error" })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,6 +10,7 @@ import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
|
|||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useNDKContext } from '@/context/NDKContext';
|
import { useNDKContext } from '@/context/NDKContext';
|
||||||
import ResourceDetails from '@/components/content/resources/ResourceDetails';
|
import ResourceDetails from '@/components/content/resources/ResourceDetails';
|
||||||
|
import {ProgressSpinner} from 'primereact/progressspinner';
|
||||||
import 'primeicons/primeicons.css';
|
import 'primeicons/primeicons.css';
|
||||||
|
|
||||||
const MDDisplay = dynamic(
|
const MDDisplay = dynamic(
|
||||||
@ -30,6 +31,8 @@ export default function Details() {
|
|||||||
const [paidResource, setPaidResource] = useState(false);
|
const [paidResource, setPaidResource] = useState(false);
|
||||||
const [decryptedContent, setDecryptedContent] = useState(null);
|
const [decryptedContent, setDecryptedContent] = useState(null);
|
||||||
const [authorView, setAuthorView] = useState(false);
|
const [authorView, setAuthorView] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
const {ndk, addSigner} = useNDKContext();
|
const {ndk, addSigner} = useNDKContext();
|
||||||
const { data: session, update } = useSession();
|
const { data: session, update } = useSession();
|
||||||
@ -75,7 +78,9 @@ export default function Details() {
|
|||||||
if (router.isReady) {
|
if (router.isReady) {
|
||||||
const { slug } = router.query;
|
const { slug } = router.query;
|
||||||
|
|
||||||
const fetchEvent = async (slug) => {
|
const fetchEvent = async (slug, retryCount = 0) => {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
try {
|
try {
|
||||||
await ndk.connect();
|
await ndk.connect();
|
||||||
|
|
||||||
@ -88,13 +93,33 @@ export default function Details() {
|
|||||||
if (event) {
|
if (event) {
|
||||||
setEvent(event);
|
setEvent(event);
|
||||||
if (user && user.pubkey === event.pubkey) {
|
if (user && user.pubkey === event.pubkey) {
|
||||||
|
const decryptedContent = await nip04.decrypt(privkey, pubkey, event.content);
|
||||||
|
setDecryptedContent(decryptedContent);
|
||||||
setAuthorView(true);
|
setAuthorView(true);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (retryCount < 1) {
|
||||||
|
// Wait for 2 seconds before retrying
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
|
return fetchEvent(slug, retryCount + 1);
|
||||||
|
} else {
|
||||||
|
setError("Event not found");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching event:', error);
|
console.error('Error fetching event:', error);
|
||||||
|
if (retryCount < 1) {
|
||||||
|
// Wait for 2 seconds before retrying
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
|
return fetchEvent(slug, retryCount + 1);
|
||||||
|
} else {
|
||||||
|
setError("Failed to fetch event. Please try again.");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ndk) {
|
if (ndk) {
|
||||||
fetchEvent(slug);
|
fetchEvent(slug);
|
||||||
}
|
}
|
||||||
@ -190,8 +215,21 @@ export default function Details() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <div className="mx-auto">
|
||||||
|
<ProgressSpinner />
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <div className="w-full mx-auto h-screen">
|
||||||
|
<div className="text-red-500 text-xl">{error}</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full px-24 pt-12 mx-auto mt-4 max-tab:px-0 max-mob:px-0 max-tab:pt-2 max-mob:pt-2'>
|
<div className='w-full px-24 pt-12 mx-auto mt-4 max-tab:px-0 max-mob:px-0 max-tab:pt-2 max-mob:pt-2'>
|
||||||
|
{processedEvent && (
|
||||||
<ResourceDetails
|
<ResourceDetails
|
||||||
processedEvent={processedEvent}
|
processedEvent={processedEvent}
|
||||||
topics={processedEvent.topics}
|
topics={processedEvent.topics}
|
||||||
@ -205,6 +243,7 @@ export default function Details() {
|
|||||||
handlePaymentSuccess={handlePaymentSuccess}
|
handlePaymentSuccess={handlePaymentSuccess}
|
||||||
handlePaymentError={handlePaymentError}
|
handlePaymentError={handlePaymentError}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
{authorView && (
|
{authorView && (
|
||||||
<div className='w-[75vw] mx-auto flex flex-row justify-end mt-12'>
|
<div className='w-[75vw] mx-auto flex flex-row justify-end mt-12'>
|
||||||
<div className='w-fit flex flex-row justify-between'>
|
<div className='w-fit flex flex-row justify-between'>
|
||||||
@ -224,7 +263,9 @@ export default function Details() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className='w-[75vw] mx-auto mt-12 p-12 border-t-2 border-gray-300 max-tab:p-0 max-mob:p-0 max-tab:max-w-[100vw] max-mob:max-w-[100vw]'>
|
<div className='w-[75vw] mx-auto mt-12 p-12 border-t-2 border-gray-300 max-tab:p-0 max-mob:p-0 max-tab:max-w-[100vw] max-mob:max-w-[100vw]'>
|
||||||
{renderContent()}
|
{
|
||||||
|
processedEvent && processedEvent.content && renderContent()
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -30,6 +30,7 @@ export default function Draft() {
|
|||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession();
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
|
const [videoId, setVideoId] = useState(null);
|
||||||
const { width, height } = useResponsiveImageDimensions();
|
const { width, height } = useResponsiveImageDimensions();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
@ -63,7 +64,7 @@ export default function Draft() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (draft) {
|
if (draft) {
|
||||||
const { unsignedEvent, type } = await buildEvent(draft);
|
const { unsignedEvent, type, videoId } = await buildEvent(draft);
|
||||||
|
|
||||||
const validationResult = validateEvent(unsignedEvent);
|
const validationResult = validateEvent(unsignedEvent);
|
||||||
if (validationResult !== true) {
|
if (validationResult !== true) {
|
||||||
@ -78,7 +79,7 @@ export default function Draft() {
|
|||||||
if (unsignedEvent) {
|
if (unsignedEvent) {
|
||||||
const published = await unsignedEvent.publish();
|
const published = await unsignedEvent.publish();
|
||||||
|
|
||||||
const saved = await handlePostResource(unsignedEvent);
|
const saved = await handlePostResource(unsignedEvent, videoId);
|
||||||
// if successful, delete the draft, redirect to profile
|
// if successful, delete the draft, redirect to profile
|
||||||
if (published && saved) {
|
if (published && saved) {
|
||||||
axios.delete(`/api/drafts/${draft.id}`)
|
axios.delete(`/api/drafts/${draft.id}`)
|
||||||
@ -104,7 +105,7 @@ export default function Draft() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePostResource = async (resource) => {
|
const handlePostResource = async (resource, videoId) => {
|
||||||
console.log('resourceeeeee:', resource.tags);
|
console.log('resourceeeeee:', resource.tags);
|
||||||
const dTag = resource.tags.find(tag => tag[0] === 'd')[1];
|
const dTag = resource.tags.find(tag => tag[0] === 'd')[1];
|
||||||
let price
|
let price
|
||||||
@ -133,6 +134,7 @@ export default function Draft() {
|
|||||||
userId: userResponse.data.id,
|
userId: userResponse.data.id,
|
||||||
price: Number(price),
|
price: Number(price),
|
||||||
noteId: nAddress,
|
noteId: nAddress,
|
||||||
|
videoId: videoId ? videoId : null
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await axios.post(`/api/resources`, payload);
|
const response = await axios.post(`/api/resources`, payload);
|
||||||
@ -167,6 +169,7 @@ export default function Draft() {
|
|||||||
const event = new NDKEvent(ndk);
|
const event = new NDKEvent(ndk);
|
||||||
let type;
|
let type;
|
||||||
let encryptedContent;
|
let encryptedContent;
|
||||||
|
let videoId;
|
||||||
|
|
||||||
console.log('Draft:', draft);
|
console.log('Draft:', draft);
|
||||||
console.log('NewDTag:', NewDTag);
|
console.log('NewDTag:', NewDTag);
|
||||||
@ -203,8 +206,10 @@ export default function Draft() {
|
|||||||
|
|
||||||
if (draft?.content.includes('.mp4') || draft?.content.includes('.mov') || draft?.content.includes('.avi') || draft?.content.includes('.wmv') || draft?.content.includes('.flv') || draft?.content.includes('.webm')) {
|
if (draft?.content.includes('.mp4') || draft?.content.includes('.mov') || draft?.content.includes('.avi') || draft?.content.includes('.wmv') || draft?.content.includes('.flv') || draft?.content.includes('.webm')) {
|
||||||
// todo update this for dev and prod
|
// todo update this for dev and prod
|
||||||
|
const extractedVideoId = draft.content.split('?videoKey=')[1].split('"')[0];
|
||||||
|
videoId = extractedVideoId;
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000"
|
const baseUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000"
|
||||||
const videoEmbed = `<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;max-width:100%;"><video src="${baseUrl}/api/get-video-url?videoKey=${encodeURIComponent(draft.content)}" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;" controls></video></div>`;
|
const videoEmbed = `<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;max-width:100%;"><video src="${baseUrl}/api/get-video-url?videoKey=${encodeURIComponent(extractedVideoId)}" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;" controls></video></div>`;
|
||||||
if (draft?.price) {
|
if (draft?.price) {
|
||||||
const encryptedVideoUrl = await nip04.encrypt(process.env.NEXT_PUBLIC_APP_PRIV_KEY, process.env.NEXT_PUBLIC_APP_PUBLIC_KEY, videoEmbed);
|
const encryptedVideoUrl = await nip04.encrypt(process.env.NEXT_PUBLIC_APP_PRIV_KEY, process.env.NEXT_PUBLIC_APP_PUBLIC_KEY, videoEmbed);
|
||||||
draft.content = encryptedVideoUrl;
|
draft.content = encryptedVideoUrl;
|
||||||
@ -234,7 +239,7 @@ export default function Draft() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { unsignedEvent: event, type };
|
return { unsignedEvent: event, type, videoId };
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -251,10 +256,13 @@ export default function Draft() {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<h1 className='text-4xl mt-6'>{draft?.title}</h1>
|
<h1 className='text-4xl mt-4'>{draft?.title}</h1>
|
||||||
<p className='text-xl mt-6'>{draft?.summary}</p>
|
<p className='text-xl mt-4'>{draft?.summary}</p>
|
||||||
|
{draft?.price && (
|
||||||
|
<p className='text-lg mt-4'>Price: {draft.price}</p>
|
||||||
|
)}
|
||||||
{draft?.additionalLinks && draft.additionalLinks.length > 0 && (
|
{draft?.additionalLinks && draft.additionalLinks.length > 0 && (
|
||||||
<div className='mt-6'>
|
<div className='mt-4'>
|
||||||
<h3 className='text-lg font-semibold mb-2'>External links:</h3>
|
<h3 className='text-lg font-semibold mb-2'>External links:</h3>
|
||||||
<ul className='list-disc list-inside'>
|
<ul className='list-disc list-inside'>
|
||||||
{draft.additionalLinks.map((link, index) => (
|
{draft.additionalLinks.map((link, index) => (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user