Skip to content

Repository files navigation

OpenShare

OpenShare

Share your photos, videos & files securely with one simple link.

Next.js React TypeScript Tailwind CSS Prisma PostgreSQL Cloudinary License


✨ Features

  • Any file, any size — Upload photos, videos, PDFs and more. No accounts required for the people you share with.
  • One link — Upload once, get a single shareable link instantly.
  • Password protection — Gate your link behind a password so only the people you choose can open it.
  • Links that expire — Every share link self-destructs after 24 hours. No cleanup, no clutter.
  • Auth built in — Email/password and Google OAuth via Better Auth.
  • Direct-to-cloud uploads — Files stream straight to Cloudinary with signed uploads; they never touch our server.
  • Open source — Free to use, free to self-host. Your files, your server, your rules.

📸 Screenshots

Drop screenshots of the landing page and dashboard here, e.g.

| Landing page | Dashboard |
|:---:|:---:|
| ![Landing](./screenshots/landing.png) | ![Dashboard](./screenshots/dashboard.png) |

🏗️ Architecture

High-level flow of the app — auth, uploads and storage:

flowchart TB
    subgraph Client["Browser (Client)"]
        UI["Next.js UI<br/>/  /login  /dashboard"]
    end

    subgraph Server["OpenShare Server (Next.js)"]
        NEXT["Next.js App Router"]
        API["POST /api/upload<br/>(auth-gated)"]
        AUTH["Better Auth<br/>email/password + Google OAuth"]
    end

    subgraph Data["Data Layer"]
        PRISMA["Prisma ORM"]
        PG[("PostgreSQL (Neon)<br/>users · sessions · files")]
    end

    CLOUD[("Cloudinary<br/>file storage")]

    UI --> NEXT
    NEXT --> AUTH
    AUTH --> PRISMA --> PG
    UI --> API
    API --> CLOUD
    UI -- "signed upload (direct)" --> CLOUD
    CLOUD -- "secure_url" --> UI
Loading

An editable Excalidraw version of this diagram is available at docs/architecture.excalidraw — open it at excalidraw.com (drag & drop the file) to tweak it or export a PNG.

How sharing works

  1. The user uploads files in the dashboard.
  2. The client calls POST /api/upload which checks the session and returns a Cloudinary upload signature (never exposes the API secret to the browser).
  3. The client uploads the files directly to Cloudinary with that signature.
  4. Cloudinary returns a secure_url for each file.
  5. File metadata — shareToken, optional passwordHash and expiresAt — is stored in PostgreSQL via Prisma.

🧰 Tech Stack

Layer Technology
Framework Next.js 16 (App Router) + React 19
Language TypeScript
Styling Tailwind CSS v4 + shadcn/ui
Auth Better Auth (email/password + Google OAuth)
Database PostgreSQL via Neon
ORM Prisma with driver adapter
File storage Cloudinary signed uploads
Package manager Bun

🚀 Getting Started

Prerequisites

1. Clone & install

git clone https://github.com/your-username/openshare.git
cd openshare
bun install

2. Set up environment variables

Copy the following into a .env file at the project root:

# Auth (generate a strong secret with: openssl rand -base64 32)
BETTER_AUTH_SECRET=your-secret-here
BETTER_AUTH_URL=http://localhost:3000

# PostgreSQL
DATABASE_URL="postgresql://user:password@host:port/dbname?sslmode=require"

# Google OAuth (optional — remove socialProviders if unused)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# Cloudinary
CLOUDINARY_CLOUD_NAME=your-cloud-name
CLOUDINARY_API_KEY=your-api-key
CLOUDINARY_API_SECRET=your-api-secret

3. Set up the database

bunx prisma generate
bunx prisma db push

4. Run it

bun run dev

Open http://localhost:3000 🎉

📦 Scripts

Command Description
bun run dev Start the dev server
bun run build Generate Prisma client + production build
bun run start Start the production server
bun run lint Run ESLint
bunx prisma studio Browse the database

📁 Project Structure

openshare/
├── app/
│   ├── api/
│   │   ├── auth/[...all]/route.ts   # Better Auth handler
│   │   └── upload/route.ts          # Signed upload signature endpoint
│   ├── dashboard/page.tsx           # Dashboard (client shell)
│   ├── login/page.tsx               # Login / register
│   ├── page.tsx                     # Landing page
│   ├── layout.tsx
│   └── globals.css
├── components/
│   ├── ui/                          # shadcn/ui primitives
│   ├── hero.tsx                     # Landing hero
│   ├── features.tsx                 # Landing feature section
│   ├── footer.tsx                   # Landing footer
│   ├── navbar.tsx
│   ├── DashboardShell.tsx           # Upload + share link UI
│   └── LoginPage.tsx
├── lib/
│   ├── auth.ts                      # Better Auth server config
│   ├── auth-client.ts               # Better Auth client
│   ├── cloudinary.ts                # Cloudinary config
│   └── upload.ts                    # Signed upload helper
├── prisma/
│   ├── schema.prisma                # User, Session, File models
│   └── migrations/
├── docs/
│   └── architecture.excalidraw      # Editable architecture diagram
└── public/

🗺️ Roadmap

  • Public share page to view/download shared files
  • Upload history + revoke links
  • Custom link expiry duration
  • Email notifications
  • Rate limiting & abuse protection

🤝 Contributing

Pull requests are welcome! Open an issue or submit a PR. For major changes, please open an issue first to discuss what you'd like to change.

📄 License

MIT


Made with ❤️ by OpenShare contributors

About

Share your photos, videos, & files through a secure link with OpenShare.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages