├── .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 |
16 |
17 |
18 |
19 | Home
20 |
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 |
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 |
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 | File Name |
11 | Type |
12 | Size |
13 | |
14 |
15 |
16 |
17 |
18 | {fileList.map((file,index)=>(
19 |
20 | {file.fileName} |
21 | {file.fileType} |
22 | {(file.fileSize/1024/1024).toFixed(2)} |
23 |
24 | View
27 | |
28 |
29 | ))}
30 |
31 |
32 |
33 |
34 |
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 |
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 |
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 |
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 |
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 |
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 |
14 |
15 |
16 |
47 |
48 |
49 |
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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------