├── .github └── workflows │ └── nextjs.yml ├── .gitignore ├── README.md ├── app ├── (auth) │ └── (routes) │ │ ├── sign-in │ │ └── [[...sign-in]] │ │ │ └── page.jsx │ │ └── sign-up │ │ └── [[...sign-up]] │ │ └── page.jsx ├── (dashboard) │ ├── (routes) │ │ ├── file-preview │ │ │ └── [fileId] │ │ │ │ ├── _components │ │ │ │ ├── FileInfo.js │ │ │ │ └── FileShareForm.js │ │ │ │ └── page.js │ │ ├── files │ │ │ ├── _components │ │ │ │ ├── FileList.js │ │ │ │ └── TotalFileCard.js │ │ │ └── page.js │ │ ├── upgrade │ │ │ └── page.js │ │ └── upload │ │ │ ├── _components │ │ │ ├── AlertMsg.js │ │ │ ├── CompleteCheck.js │ │ │ ├── FilePreview.js │ │ │ ├── ProgressBar.js │ │ │ └── UploadForm.js │ │ │ └── page.js │ ├── _components │ │ ├── SideNav.js │ │ └── TopHeader.js │ └── layout.js ├── _components │ ├── Header.js │ ├── Hero.js │ ├── Toast.js │ └── email-template.js ├── _utils │ ├── Constant.js │ ├── GenerateRandomString.js │ └── GlobalApi.js ├── api │ └── send │ │ └── route.ts ├── f │ └── [fileId] │ │ ├── _componets │ │ └── FileItemC.js │ │ └── page.js ├── favicon.ico ├── globals.css ├── layout.js └── page.js ├── context └── ToastContext.js ├── firebaseConfig.js ├── jsconfig.json ├── middleware.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── download-file.gif ├── file.png ├── logo.svg ├── logo1.svg ├── next.svg ├── vercel.svg └── verified.gif ├── tailwind.config.js └── tsconfig.json /.github/workflows/nextjs.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Next.js site to GitHub Pages 2 | # 3 | # To get started with Next.js see: https://nextjs.org/docs/getting-started 4 | # 5 | name: Deploy Next.js site to Pages 6 | 7 | on: 8 | # Runs on pushes targeting the default branch 9 | push: 10 | branches: ["main"] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 16 | permissions: 17 | contents: read 18 | pages: write 19 | id-token: write 20 | 21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 23 | concurrency: 24 | group: "pages" 25 | cancel-in-progress: false 26 | 27 | jobs: 28 | # Build job 29 | build: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Detect package manager 35 | id: detect-package-manager 36 | run: | 37 | if [ -f "${{ github.workspace }}/yarn.lock" ]; then 38 | echo "manager=yarn" >> $GITHUB_OUTPUT 39 | echo "command=install" >> $GITHUB_OUTPUT 40 | echo "runner=yarn" >> $GITHUB_OUTPUT 41 | exit 0 42 | elif [ -f "${{ github.workspace }}/package.json" ]; then 43 | echo "manager=npm" >> $GITHUB_OUTPUT 44 | echo "command=ci" >> $GITHUB_OUTPUT 45 | echo "runner=npx --no-install" >> $GITHUB_OUTPUT 46 | exit 0 47 | else 48 | echo "Unable to determine package manager" 49 | exit 1 50 | fi 51 | - name: Setup Node 52 | uses: actions/setup-node@v4 53 | with: 54 | node-version: "20" 55 | cache: ${{ steps.detect-package-manager.outputs.manager }} 56 | - name: Setup Pages 57 | uses: actions/configure-pages@v4 58 | with: 59 | # Automatically inject basePath in your Next.js configuration file and disable 60 | # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). 61 | # 62 | # You may remove this line if you want to manage the configuration yourself. 63 | static_site_generator: next 64 | - name: Restore cache 65 | uses: actions/cache@v3 66 | with: 67 | path: | 68 | .next/cache 69 | # Generate a new cache whenever packages or source files change. 70 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} 71 | # If source files changed but packages didn't, rebuild from a prior cache. 72 | restore-keys: | 73 | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- 74 | - name: Install dependencies 75 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} 76 | - name: Build with Next.js 77 | run: ${{ steps.detect-package-manager.outputs.runner }} next build 78 | - name: Static HTML export with Next.js 79 | run: ${{ steps.detect-package-manager.outputs.runner }} next export 80 | - name: Upload artifact 81 | uses: actions/upload-pages-artifact@v2 82 | with: 83 | path: ./out 84 | 85 | # Deployment job 86 | deploy: 87 | environment: 88 | name: github-pages 89 | url: ${{ steps.deployment.outputs.page_url }} 90 | runs-on: ubuntu-latest 91 | needs: build 92 | steps: 93 | - name: Deploy to GitHub Pages 94 | id: deployment 95 | uses: actions/deploy-pages@v3 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /app/(auth)/(routes)/sign-in/[[...sign-in]]/page.jsx: -------------------------------------------------------------------------------- 1 | import { SignIn } from "@clerk/nextjs"; 2 | import Image from "next/image"; 3 | 4 | export default function Page() { 5 | return ( 6 |
7 |
8 |
11 | Night 16 | 17 |
18 | 19 | Home 20 | 26 | 30 | 31 | 32 | 33 |

34 | Welcome to File Sharing App 🦑 35 |

36 | 37 |

38 | Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eligendi nam 39 | dolorum aliquam, quibusdam aperiam voluptatum. 40 |

41 |
42 |
43 | 44 |
47 |
48 |
49 | 53 | Home 54 | logo 57 | 58 | 59 |

62 | Welcome to File Sharing App 🦑 63 |

64 | 65 |

66 | Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eligendi 67 | nam dolorum aliquam, quibusdam aperiam voluptatum. 68 |

69 |
70 | 71 | 72 | 73 |
74 |
75 |
76 |
77 | ); 78 | } -------------------------------------------------------------------------------- /app/(auth)/(routes)/sign-up/[[...sign-up]]/page.jsx: -------------------------------------------------------------------------------- 1 | import { SignUp } from "@clerk/nextjs"; 2 | 3 | export default function Page() { 4 | return ; 5 | } -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/file-preview/[fileId]/_components/FileInfo.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import React, { useEffect, useState } from 'react' 3 | 4 | function FileInfo({file}) { 5 | const [fileType,setFileType]=useState(); 6 | useEffect(()=>{ 7 | file&&setFileType(file?.fileType.split('/')[0]); 8 | console.log(fileType); 9 | },[file]) 10 | return file&&( 11 |
14 | logo 19 |
20 |

{file.fileName}

21 |

{file.fileType} / {file.fileSize}

22 | 23 |
24 |
25 | ) 26 | } 27 | 28 | export default FileInfo -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/file-preview/[fileId]/_components/FileShareForm.js: -------------------------------------------------------------------------------- 1 | import { Copy, CopyIcon } from 'lucide-react' 2 | import React, { useContext, useState } from 'react' 3 | import GlobalApi from './../../../../../_utils/GlobalApi' 4 | import { useUser } from '@clerk/nextjs'; 5 | import Toast from '../../../../../_components/Toast'; 6 | 7 | function FileShareForm({ file,onPasswordSave }) { 8 | const [isPasswordEnable,setIsEnablePassword]=useState(false); 9 | const [password,setPassword]=useState(''); 10 | const [email,setEmail]=useState(''); 11 | const [toast,setToast]=useState(); 12 | const {user}=useUser(); 13 | const sendEmail=()=>{ 14 | 15 | setToast({ 16 | status:'Info', 17 | msg:'Sending Email...!' 18 | }) 19 | const data={ 20 | emailToSend:email, 21 | userName:user?.fullName, 22 | fileName:file.fileName, 23 | fileSize:file.fileSize, 24 | fileType:file.fileType, 25 | shortUrl:file?.shortUrl 26 | } 27 | GlobalApi.SendEmail(data).then(resp=>{ 28 | console.log(resp); 29 | setToast({ 30 | status:'success', 31 | msg:'Email Sent Successfully!' 32 | }) 33 | }) 34 | } 35 | 36 | const onCopyClick=()=>{ 37 | navigator.clipboard.writeText(file.shortUrl); 38 | setToast({ 39 | status:'Copied', 40 | msg:'Url Copied!' 41 | }) 42 | 43 | } 44 | return file && ( 45 |
46 |
47 | 48 |
49 | 52 | onCopyClick()} /> 54 |
55 | 56 |
57 |
58 | setIsEnablePassword(e.target.checked)}/> 61 | 62 |
63 | 64 | {isPasswordEnable? 65 |
66 |
67 | setPassword(e.target.value)}/> 71 |
72 | 77 |
:null} 78 | 79 |
80 | 81 |
82 | setEmail(e.target.value)} 87 | /> 88 |
89 | 95 |
96 | 97 | {toast?.status? setToast(null)} 100 | />:null} 101 |
102 | ) 103 | } 104 | 105 | export default FileShareForm -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/file-preview/[fileId]/page.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import app from './../../../../../firebaseConfig'; 3 | import { doc, getDoc, getFirestore, updateDoc } from 'firebase/firestore'; 4 | import { ArrowLeftSquare } from 'lucide-react'; 5 | import Link from 'next/link'; 6 | import React, { useEffect, useState } from 'react' 7 | import FileInfo from './_components/FileInfo'; 8 | import FileShareForm from './_components/FileShareForm'; 9 | 10 | function FilePreview({params}) { 11 | const db = getFirestore(app); 12 | const [file,setFile]=useState(); 13 | useEffect(()=>{ 14 | console.log(params?.fileId) 15 | params?.fileId&&getFileInfo(); 16 | },[]) 17 | 18 | const getFileInfo=async()=>{ 19 | const docRef = doc(db, "uploadedFile", params?.fileId); 20 | const docSnap = await getDoc(docRef); 21 | if (docSnap.exists()) { 22 | console.log("Document data:", docSnap.data()); 23 | setFile(docSnap.data()); 24 | } else { 25 | // docSnap.data() will be undefined in this case 26 | console.log("No such document!"); 27 | } 28 | } 29 | 30 | const onPasswordSave=async(password)=>{ 31 | const docRef=doc(db,"uploadedFile",params?.fileId); 32 | await updateDoc(docRef,{ 33 | password:password 34 | }); 35 | 36 | } 37 | return ( 38 |
39 | 40 | Go to Upload 41 |
42 | 43 | onPasswordSave(password)}/> 45 |
46 |
47 | ) 48 | } 49 | 50 | export default FilePreview -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/files/_components/FileList.js: -------------------------------------------------------------------------------- 1 | import { Link } from '@react-email/components' 2 | import React from 'react' 3 | 4 | function FileList({fileList}) { 5 | return fileList&&( 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {fileList.map((file,index)=>( 19 | 20 | 21 | 22 | 23 | 28 | 29 | ))} 30 | 31 | 32 | 33 | 34 |
File Name TypeSize
{file.fileName}{file.fileType}{(file.fileSize/1024/1024).toFixed(2)} 24 | View 27 |
35 |
36 | ) 37 | } 38 | 39 | export default FileList -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/files/_components/TotalFileCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function TotalFileCard({totalFile}) { 4 | return ( 5 |
7 |

Total File : {totalFile}

8 | 9 |
10 | ) 11 | } 12 | 13 | export default TotalFileCard -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/files/page.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { UserButton, useUser } from '@clerk/nextjs' 3 | import React, { useEffect, useState } from 'react' 4 | import { collection, getDocs, getFirestore, query, where } from "firebase/firestore"; 5 | import app from './../../../../firebaseConfig' 6 | import TotalFileCard from './_components/TotalFileCard' 7 | import FileList from './_components/FileList' 8 | import Link from 'next/link'; 9 | function Files() { 10 | const db = getFirestore(app); 11 | const {user}=useUser(); 12 | const [fileList,setFileList]=useState([]); 13 | useEffect(()=>{ 14 | user&&getAllUserFiles(); 15 | },[user]) 16 | const getAllUserFiles=async()=>{ 17 | 18 | const q = query(collection(db, "uploadedFile"), 19 | where("userEmail", "==", user.primaryEmailAddress.emailAddress)); 20 | 21 | const querySnapshot = await getDocs(q); 22 | setFileList([]) 23 | querySnapshot.forEach((doc) => { 24 | // doc.data() is never undefined for query doc snapshots 25 | console.log(doc.id, " => ", doc.data()); 26 | setFileList(fileList=>[...fileList,doc.data()]) 27 | }); 28 | } 29 | return ( 30 |
31 |

My Files

32 | 33 | {fileList.length==0? 34 | <> 35 |

You dont have any File

36 | 38 | Upload Now 39 | 40 | :<> 41 | 42 | 43 | 44 | 45 | } 46 |
47 | ) 48 | } 49 | 50 | export default Files -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/upgrade/page.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Upgrade() { 4 | return ( 5 |
Upgrade
6 | ) 7 | } 8 | 9 | export default Upgrade -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/upload/_components/AlertMsg.js: -------------------------------------------------------------------------------- 1 | import { AlertCircle } from 'lucide-react' 2 | import React from 'react' 3 | 4 | function AlertMsg({msg}) { 5 | return ( 6 |
8 | 9 | {msg} 10 |
11 | ) 12 | } 13 | 14 | export default AlertMsg -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/upload/_components/CompleteCheck.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import React from 'react' 3 | 4 | function CompleteCheck() { 5 | return ( 6 |
12 | verified 15 |

File 16 | Uploaded Successfully

17 |
18 | ) 19 | } 20 | 21 | export default CompleteCheck -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/upload/_components/FilePreview.js: -------------------------------------------------------------------------------- 1 | import { X } from 'lucide-react' 2 | import Image from 'next/image' 3 | import React from 'react' 4 | 5 | function FilePreview({file,removeFile}) { 6 | return ( 7 |
10 |
11 | file 13 |
14 |

{file.name}

15 |

{file?.type} / {(file.size/1024/1024).toFixed(2)}MB

17 |
18 |
19 | removeFile()}/> 21 |
22 | ) 23 | } 24 | 25 | export default FilePreview -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/upload/_components/ProgressBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function ProgressBar({progress=40}) { 4 | return ( 5 |
7 |
10 | {`${Number(progress).toFixed(0)}%`} 11 |
12 | 13 |
14 | ) 15 | } 16 | 17 | export default ProgressBar -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/upload/_components/UploadForm.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import AlertMsg from './AlertMsg'; 3 | import FilePreview from './FilePreview'; 4 | import ProgressBar from './ProgressBar'; 5 | 6 | function UploadForm({uploadBtnClick,progress}) { 7 | const [file, setFile] = useState(); 8 | const [errorMsg, setErrorMsg] = useState(); 9 | const onFileSelect = (file) => { 10 | console.log(file) 11 | if (file && file.size > 2000000) { 12 | console.log("Size is Greate than 2 MB"); 13 | setErrorMsg('Maximum File Upload Size is 2MB') 14 | return; 15 | } 16 | setErrorMsg(null) 17 | setFile(file) 18 | } 19 | return ( 20 |
21 | 22 |
23 | 49 |
50 | {errorMsg ? : null} 51 | {file? setFile(null)}/>:null} 52 | 53 | {progress>0? : 54 | } 58 | 59 |
60 | ) 61 | } 62 | 63 | export default UploadForm -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/upload/page.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React, { useEffect, useState } from 'react' 3 | import UploadForm from './_components/UploadForm' 4 | import app from './../../../../firebaseConfig' 5 | import { getDownloadURL, getStorage, ref, uploadBytesResumable } from "firebase/storage"; 6 | import CompleteCheck from './_components/CompleteCheck'; 7 | import { doc, getFirestore, setDoc } from "firebase/firestore"; 8 | import { useUser } from '@clerk/nextjs'; 9 | import { generateRandomString } from './../../../_utils/GenerateRandomString'; 10 | import { useRouter } from 'next/navigation'; 11 | function Upload() { 12 | 13 | const {user}=useUser(); 14 | const [progress,setProgress]=useState(); 15 | const router=useRouter(); 16 | const storage=getStorage(app) 17 | const db = getFirestore(app); 18 | const [fileDocId,setFileDocId]=useState(); 19 | const [uploadCompleted,setUploadCompleted]=useState(false); 20 | const uploadFile=(file)=>{ 21 | const metadata = { 22 | contentType: file.type 23 | }; 24 | const storageRef = ref(storage, 'file-upload/'+file?.name); 25 | const uploadTask = uploadBytesResumable(storageRef, file, file.type); 26 | uploadTask.on('state_changed', 27 | (snapshot) => { 28 | // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded 29 | const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; 30 | console.log('Upload is ' + progress + '% done'); 31 | setProgress(progress); 32 | progress==100&&getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { 33 | console.log('File available at', downloadURL); 34 | saveInfo(file,downloadURL); 35 | }); 36 | }, ) 37 | } 38 | 39 | const saveInfo=async(file,fileUrl)=>{ 40 | const docId=generateRandomString().toString(); 41 | setFileDocId(docId) 42 | await setDoc(doc(db, "uploadedFile", docId), { 43 | fileName:file?.name, 44 | fileSize:file?.size, 45 | fileType:file?.type, 46 | fileUrl:fileUrl, 47 | userEmail:user?.primaryEmailAddress.emailAddress, 48 | userName:user?.fullName, 49 | password:'', 50 | id:docId, 51 | shortUrl:process.env.NEXT_PUBLIC_BASE_URL+docId 52 | }); 53 | 54 | } 55 | 56 | useEffect(()=>{ 57 | console.log("Trigger") 58 | 59 | progress==100&& setTimeout(()=>{ 60 | setUploadCompleted(true); 61 | },2000) 62 | },[progress==100]); 63 | 64 | useEffect(()=>{ 65 | uploadCompleted&& 66 | setTimeout(()=>{ 67 | setUploadCompleted(false); 68 | console.log("FileDocId",fileDocId) 69 | router.push('/file-preview/'+fileDocId); 70 | },2000) 71 | },[uploadCompleted==true]) 72 | return ( 73 |
74 | {!uploadCompleted?
75 |

Start 76 | Uploading 77 | File and Share it

78 | uploadFile(file)} 80 | progress={progress} 81 | /> 82 |
: 83 | } 84 |
85 | ) 86 | } 87 | 88 | export default Upload -------------------------------------------------------------------------------- /app/(dashboard)/_components/SideNav.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { File, Shield, Upload } from 'lucide-react' 3 | import Image from 'next/image' 4 | import Link from 'next/link' 5 | import React, { useState } from 'react' 6 | 7 | function SideNav({closeSideBar}) { 8 | const menuList=[ 9 | { 10 | id:1, 11 | name:'Upload', 12 | icon:Upload, 13 | path:'/upload' 14 | }, 15 | { 16 | id:2, 17 | name:'Files', 18 | icon:File, 19 | path:'/files' 20 | }, 21 | { 22 | id:3, 23 | name:'Upgrade', 24 | icon:Shield, 25 | path:'/upgrade' 26 | }, 27 | 28 | ] 29 | 30 | const [activeIndex,setActiveIndex]=useState(0); 31 | return ( 32 |
33 |
34 | 35 | logo 37 | 38 |
39 |
40 | {menuList.map((item,index)=>( 41 | 42 | 53 | 54 | ))} 55 |
56 |
57 | ) 58 | } 59 | 60 | export default SideNav -------------------------------------------------------------------------------- /app/(dashboard)/_components/TopHeader.js: -------------------------------------------------------------------------------- 1 | import { UserButton } from '@clerk/nextjs' 2 | import { AlignJustify } from 'lucide-react' 3 | import Image from 'next/image' 4 | import React from 'react' 5 | 6 | function TopHeader({setToggleBar}) { 7 | return ( 8 |
11 | setToggleBar(true)}/> 13 | logo 18 | 19 |
20 | ) 21 | } 22 | 23 | export default TopHeader -------------------------------------------------------------------------------- /app/(dashboard)/layout.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React, { useState } from 'react' 3 | import SideNav from './_components/SideNav' 4 | import TopHeader from './_components/TopHeader' 5 | import Toast from '../_components/Toast'; 6 | 7 | function layout({ children }) { 8 | const [toggle, setToggle] = useState(false); 9 | 10 | return ( 11 | // 12 |
13 |
15 | setToggle(false)}/> 16 |
17 | 18 | {toggle ?
20 | setToggle(false)}/> 21 |
: null} 22 | 23 |
24 | 25 | setToggle(true)} /> 26 | {children} 27 | {/* */} 28 |
29 | 30 |
31 | 32 | ) 33 | } 34 | 35 | export default layout -------------------------------------------------------------------------------- /app/_components/Header.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import React from 'react' 3 | 4 | function Header() { 5 | return ( 6 |
7 |
8 |
13 | logo 14 | 15 |
16 | 47 | 48 |
49 |
50 | 57 | Get Started 58 | 59 | 60 | 61 |
62 | 63 | 82 |
83 |
84 |
85 |
86 |
87 | ) 88 | } 89 | 90 | export default Header -------------------------------------------------------------------------------- /app/_components/Hero.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Constant from '../_utils/Constant' 3 | 4 | function Hero() { 5 | return ( 6 |
7 |
11 |
12 |

13 | Upload, Save 14 | and easily Share your files in one place 15 |

16 | 17 |

18 | {Constant.desc} 19 |

20 | 21 | 41 |
42 |
43 |
44 | ) 45 | } 46 | 47 | export default Hero -------------------------------------------------------------------------------- /app/_components/Toast.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useState } from 'react' 2 | 3 | 4 | function Toast({toast,closeToast}) { 5 | // const {toast,setToast}=useContext(ToastContext); 6 | 7 | // useEffect(()=>{ 8 | // toast?.status&&setTimeout(()=>{ 9 | // setToast(null) 10 | // },3000) 11 | // },[toast]); 12 | 13 | 14 | return toast?.status&&( 15 | 16 |
20 |
21 | 22 | 30 | 35 | 36 | 37 | 38 |
39 | {toast?.status} 41 | 42 |

{toast?.msg}

43 |
44 | 45 | 60 |
61 |
62 | 63 | ) 64 | } 65 | 66 | export default Toast -------------------------------------------------------------------------------- /app/_components/email-template.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { 3 | Body, 4 | Button, 5 | Container, 6 | Column, 7 | Head, 8 | Heading, 9 | Html, 10 | Img, 11 | Preview, 12 | Row, 13 | Section, 14 | Text, 15 | } from '@react-email/components'; 16 | 17 | export const EmailTemplate = ({ 18 | responce, 19 | }) => ( 20 |
21 | 22 | 23 | File Shared With You 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 39 | 40 | Hi {responce?.emailToSend?.split("@")[0]}, 41 | 42 | 50 | {responce?.userName} Share file with you 51 | 52 | 53 | 54 | File Name: {responce.fileName} 55 | 56 | 57 | 58 | File Size: {(responce.fileSize/1024/1024).toFixed(2)} 59 | 60 | 61 | 62 | File Type: {responce.fileType} 63 | 64 | 65 | 72 | *Access and Download file on your own risk 73 | 74 | 75 | 76 | 77 | Now You can also share file with Tubegurji FileSharo App 78 | 79 | 80 | Click Below Button to Access your file 81 | 82 | 83 | 84 | 85 | Click To Download 86 | 87 | 88 | 91 | 92 | 93 | 94 |
95 | 96 | 97 |
98 | 99 |
100 | 101 | 108 | © 2024 | Tubegurji @2024 Copyrights 109 | U.S.A. | www.Tubeguruji.com 110 | 111 |
112 | 113 | 114 |
115 | ); 116 | 117 | const main = { 118 | backgroundColor: '#fff', 119 | fontFamily: 120 | '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif', 121 | }; 122 | 123 | const paragraph = { 124 | fontSize: 16, 125 | }; 126 | 127 | const logo = { 128 | padding: '30px 20px', 129 | }; 130 | 131 | const containerButton = { 132 | display: 'flex', 133 | justifyContent: 'center', 134 | width: '100%', 135 | }; 136 | 137 | const button = { 138 | backgroundColor: '#e00707', 139 | padding: '12px 30px', 140 | borderRadius: 3, 141 | color: '#FFF', 142 | fontWeight: 'bold', 143 | border: '1px solid rgb(0,0,0, 0.1)', 144 | cursor: 'pointer', 145 | }; 146 | 147 | const content = { 148 | border: '1px solid rgb(0,0,0, 0.1)', 149 | borderRadius: '3px', 150 | overflow: 'hidden', 151 | }; 152 | 153 | const boxInfos = { 154 | padding: '20px 40px', 155 | }; 156 | 157 | const containerImageFooter = { 158 | padding: '45px 0 0 0', 159 | }; 160 | -------------------------------------------------------------------------------- /app/_utils/Constant.js: -------------------------------------------------------------------------------- 1 | export default{ 2 | desc:'Drag and drop your file directly on our cloud and share it with your friends secuarely with password and send it on email', 3 | } -------------------------------------------------------------------------------- /app/_utils/GenerateRandomString.js: -------------------------------------------------------------------------------- 1 | export const generateRandomString=()=>{ 2 | const charaters='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 3 | let result=''; 4 | for (let i=0;i<6;i++) 5 | { 6 | result+=charaters.charAt(Math.floor(Math.random()*charaters.length)); 7 | 8 | } 9 | return result; 10 | } -------------------------------------------------------------------------------- /app/_utils/GlobalApi.js: -------------------------------------------------------------------------------- 1 | const { default: axios } = require("axios"); 2 | 3 | const SendEmail=(data)=>axios.post('/api/send',data); 4 | 5 | export default{ 6 | SendEmail 7 | } -------------------------------------------------------------------------------- /app/api/send/route.ts: -------------------------------------------------------------------------------- 1 | import { EmailTemplate } from './../../_components/email-template'; 2 | import { NextResponse } from 'next/server'; 3 | import { Resend } from 'resend'; 4 | 5 | const resend = new Resend(process.env.RESEND_API_KEY); 6 | 7 | export async function POST(req) { 8 | const responce=await req.json(); 9 | try { 10 | const data = await resend.emails.send({ 11 | from: 'tubeguruji-app@tubeguruji-app.tubeguruji.com', 12 | to: [responce.emailToSend], 13 | subject: responce?.userName+" share file with You", 14 | react: EmailTemplate({ responce }), 15 | }); 16 | 17 | return NextResponse.json(data); 18 | } catch (error) { 19 | return NextResponse.json({ error }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/f/[fileId]/_componets/FileItemC.js: -------------------------------------------------------------------------------- 1 | import { Download } from 'lucide-react' 2 | import Image from 'next/image' 3 | import React, { useState } from 'react' 4 | 5 | function FileItemC({file}) { 6 | const [password,setPassword]=useState(''); 7 | return file&&( 8 |
9 |
10 | 11 |
12 |

13 | {file.userName} 14 | Shared the file with You

15 |

Find File details below

16 | download 20 | 21 |

22 | {file.fileName} ⚡ {file.fileType} ⚡ {(file.fileSize/1024/1024).toFixed(2)} MB

23 |
24 | 25 | {file.password.length>3? setPassword(e.target.value)} 29 | placeholder='Enter password to access '/>:null} 30 | 31 | 39 |

*Term and Condition apply

40 | 41 |
42 |
43 | ) 44 | } 45 | 46 | export default FileItemC -------------------------------------------------------------------------------- /app/f/[fileId]/page.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React, { useEffect, useState } from 'react' 3 | import { app } from '../../../firebaseConfig'; 4 | import { doc, getDoc, getFirestore } from 'firebase/firestore'; 5 | import FileItemC from './_componets/FileItemC' 6 | import Link from 'next/link'; 7 | import Image from 'next/image'; 8 | function FileView({params}) { 9 | const db = getFirestore(app); 10 | const [file,setFile]=useState(); 11 | useEffect(()=>{ 12 | // console.log(params.fileId) 13 | params.fileId&&getFileInfo() 14 | 15 | },[]) 16 | 17 | const getFileInfo=async()=>{ 18 | const docRef = doc(db, "uploadedFile", params?.fileId); 19 | const docSnap = await getDoc(docRef); 20 | if (docSnap.exists()) { 21 | console.log("Document data:", docSnap.data()); 22 | setFile(docSnap.data()); 23 | } else { 24 | // docSnap.data() will be undefined in this case 25 | console.log("No such document!"); 26 | } 27 | } 28 | return ( 29 |
32 | 33 | logo 37 | 38 | 39 |
40 | ) 41 | } 42 | 43 | export default FileView -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrs301/file-sharing-nextjs/85d7b4c5df9183bb16671004797d4e0cb674135f/app/favicon.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | 8 | } 9 | 10 | @media (prefers-color-scheme: dark) { 11 | :root { 12 | --foreground-rgb: 255, 255, 255; 13 | --background-start-rgb: 0, 0, 0; 14 | --background-end-rgb: 0, 0, 0; 15 | } 16 | } 17 | 18 | body { 19 | color: rgb(var(--foreground-rgb)); 20 | background: linear-gradient( 21 | to bottom, 22 | transparent, 23 | rgb(var(--background-end-rgb)) 24 | ) 25 | rgb(var(--background-start-rgb)); 26 | } 27 | -------------------------------------------------------------------------------- /app/layout.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { Inter,Outfit } from 'next/font/google' 3 | import './globals.css' 4 | import { ClerkProvider } from '@clerk/nextjs' 5 | import Toast from './_components/Toast' 6 | import { useState } from 'react' 7 | import ToastContext from './../context/ToastContext' 8 | const inter = Outfit({ subsets: ['latin'] }) 9 | 10 | // export const metadata = { 11 | // title: 'Create Next App', 12 | // description: 'Generated by create next app', 13 | // } 14 | 15 | export default function RootLayout({ children }) { 16 | const [toast,setToast]=useState(''); 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | {children} 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /app/page.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import Header from './_components/Header' 3 | import Hero from './_components/Hero' 4 | 5 | export default function Home() { 6 | return ( 7 |
8 |
9 | 10 |
11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /context/ToastContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | export const ToastContext=createContext(null); -------------------------------------------------------------------------------- /firebaseConfig.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from "firebase/app"; 3 | import { getAnalytics } from "firebase/analytics"; 4 | // TODO: Add SDKs for Firebase products that you want to use 5 | // https://firebase.google.com/docs/web/setup#available-libraries 6 | 7 | // Your web app's Firebase configuration 8 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional 9 | const firebaseConfig = { 10 | apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, 11 | authDomain: "apps-e02e4.firebaseapp.com", 12 | projectId: "apps-e02e4", 13 | storageBucket: "apps-e02e4.appspot.com", 14 | messagingSenderId: "931728242416", 15 | appId: "1:931728242416:web:4ed67a990f659857293435", 16 | measurementId: "G-XST719ZNZ2" 17 | }; 18 | 19 | // Initialize Firebase 20 | const app = initializeApp(firebaseConfig); 21 | 22 | export default app; 23 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { authMiddleware } from "@clerk/nextjs"; 2 | 3 | // This example protects all routes including api/trpc routes 4 | // Please edit this to allow other routes to be public as needed. 5 | // See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware 6 | export default authMiddleware({ 7 | publicRoutes:['/','/api/(.*)','/f/(.*)'] 8 | }); 9 | 10 | export const config = { 11 | matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'], 12 | }; 13 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images:{ 4 | domains:['firebasestorage.googleapis.com'], 5 | unoptimized: true, 6 | } 7 | } 8 | 9 | module.exports = nextConfig 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-sharing-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@clerk/nextjs": "^4.27.2", 13 | "@react-email/components": "^0.0.11", 14 | "axios": "^1.6.2", 15 | "firebase": "^10.7.0", 16 | "init": "^0.1.2", 17 | "lucide-react": "^0.294.0", 18 | "next": "14.0.3", 19 | "react": "^18", 20 | "react-dom": "^18", 21 | "react-email": "^1.9.5", 22 | "resend": "^2.0.0" 23 | }, 24 | "devDependencies": { 25 | "@types/react": "18.2.39", 26 | "autoprefixer": "^10.0.1", 27 | "postcss": "^8", 28 | "tailwindcss": "^3.3.0", 29 | "typescript": "5.3.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/download-file.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrs301/file-sharing-nextjs/85d7b4c5df9183bb16671004797d4e0cb674135f/public/download-file.gif -------------------------------------------------------------------------------- /public/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrs301/file-sharing-nextjs/85d7b4c5df9183bb16671004797d4e0cb674135f/public/file.png -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /public/logo1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/verified.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrs301/file-sharing-nextjs/85d7b4c5df9183bb16671004797d4e0cb674135f/public/verified.gif -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 5 | './components/**/*.{js,ts,jsx,tsx,mdx}', 6 | './app/**/*.{js,ts,jsx,tsx,mdx}', 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 12 | 'gradient-conic': 13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | colors:{ 16 | primary:'#007dfc' 17 | } 18 | }, 19 | }, 20 | plugins: [], 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "dom", 5 | "dom.iterable", 6 | "esnext" 7 | ], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "strict": false, 11 | "noEmit": true, 12 | "incremental": true, 13 | "esModuleInterop": true, 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "jsx": "preserve", 19 | "plugins": [ 20 | { 21 | "name": "next" 22 | } 23 | ] 24 | }, 25 | "include": [ 26 | "next-env.d.ts", 27 | ".next/types/**/*.ts", 28 | "**/*.ts", 29 | "**/*.tsx" 30 | ], 31 | "exclude": [ 32 | "node_modules" 33 | ] 34 | } 35 | --------------------------------------------------------------------------------