{"id":18992124,"url":"https://github.com/ecarry/photography-website","last_synced_at":"2026-02-08T08:19:39.934Z","repository":{"id":208461378,"uuid":"696129843","full_name":"ECarry/photography-website","owner":"ECarry","description":"An open-source Photograph travel Blog📸built using Next.js, Drizzle, Neon, Better auth, Shadcn/ui and tRPC.","archived":false,"fork":false,"pushed_at":"2025-03-27T15:34:31.000Z","size":101152,"stargazers_count":174,"open_issues_count":0,"forks_count":20,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-29T03:02:14.543Z","etag":null,"topics":["betterauth","cloudflare-r2","drizzle","neon","nextjs","reactquery","shadcnui","trpc"],"latest_commit_sha":null,"homepage":"https://p.ecarry.me","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ECarry.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-25T06:35:00.000Z","updated_at":"2025-03-28T16:50:09.000Z","dependencies_parsed_at":"2023-11-27T10:25:54.040Z","dependency_job_id":"feca5af1-3534-4e2f-87ce-dc0ef91fd4fd","html_url":"https://github.com/ECarry/photography-website","commit_stats":{"total_commits":378,"total_committers":1,"mean_commits":378.0,"dds":0.0,"last_synced_commit":"00c2a5a78a1467ad7d10b04fb6d8f6f380441cd7"},"previous_names":["ecarry/photography-website-nextjs14-full-stack","ecarry/photography-website-nextjs14","ecarry/photography-website-nextjs","ecarry/photography-website"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ECarry%2Fphotography-website","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ECarry%2Fphotography-website/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ECarry%2Fphotography-website/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ECarry%2Fphotography-website/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ECarry","download_url":"https://codeload.github.com/ECarry/photography-website/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247284942,"owners_count":20913704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["betterauth","cloudflare-r2","drizzle","neon","nextjs","reactquery","shadcnui","trpc"],"created_at":"2024-11-08T17:16:31.825Z","updated_at":"2026-02-08T08:19:39.927Z","avatar_url":"https://github.com/ECarry.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Photography Website\n\nA modern photography portfolio website built with Next.js 16, featuring interactive maps, photo management, and a comprehensive dashboard.\n\n## 📸 Screenshots\n\n![Home Page](screenshots/home.png)\n_Home Page_\n\n![Dashboard](screenshots/dashboard.png)\n_Dashboard_\n\n## 🚀 Features\n\n- **Next.js 16** with React 19 and React Compiler\n- **TanStack Query v5** for advanced data fetching and caching\n- **tRPC v11** for end-to-end type-safe APIs\n- **Interactive Maps** with Mapbox GL JS integration\n- **Photo Management** with EXIF data extraction and iPhone album integration\n- **Real-time Dashboard** with analytics and statistics\n- **Modern UI** built with Tailwind CSS and shadcn/ui components\n- **Authentication** powered by Better Auth\n- **Database** using Drizzle ORM with PostgreSQL\n- **File Storage** via S3-compatible storage\n\n## 📋 Prerequisites\n\nBefore deploying, ensure you have:\n\n- **Node.js 18+** or **Bun** runtime\n- **PostgreSQL database** (recommended: Neon, Supabase, or Vercel Postgres)\n- **S3-compatible storage** for image storage (AWS S3, Cloudflare R2, DigitalOcean Spaces, etc.)\n- **Mapbox** account for map features\n- **Vercel** account for deployment (or any Node.js hosting provider)\n\n## 🛠️ Deployment Guide\n\n### Step 1: Clone and Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/ECarry/photography-website.git\ncd photography-website\n\n# Install dependencies\nbun install\n# or\nnpm install\n```\n\n### Step 2: Environment Configuration\n\nCreate a `.env` file in the root directory:\n\n```bash\ncp .env.example .env\n```\n\nConfigure the following environment variables:\n\n#### Database Configuration\n\n```env\n# PostgreSQL connection string\nDATABASE_URL=postgresql://username:password@host:port/database_name?sslmode=require\n```\n\n**For Neon Database:**\n\n1. Create account at [neon.tech](https://neon.tech)\n2. Create a new project\n3. Copy the connection string from dashboard\n\n**For Supabase:**\n\n1. Create project at [supabase.com](https://supabase.com)\n2. Go to Settings \u003e Database\n3. Copy the connection string\n\n#### Authentication Configuration\n\n```env\n# Generate a random secret key (32+ characters)\nBETTER_AUTH_SECRET=your-super-secret-key-here\n\n# Your app's base URL\nBETTER_AUTH_URL=https://your-domain.com\nNEXT_PUBLIC_APP_URL=https://your-domain.com\n```\n\n#### S3-Compatible Storage Configuration\n\n```env\n# S3-compatible storage settings\nS3_ENDPOINT=https://your-s3-endpoint.com\nS3_BUCKET_NAME=your-bucket-name\nS3_PUBLIC_URL=https://your-custom-domain.com\nS3_ACCESS_KEY_ID=your-access-key\nS3_SECRET_ACCESS_KEY=your-secret-key\nNEXT_PUBLIC_S3_PUBLIC_URL=https://your-custom-domain.com\n```\n\n**Supported Storage Providers:**\n\n**Cloudflare R2:**\n\n```env\nS3_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com\nS3_BUCKET_NAME=your-bucket-name\nNEXT_PUBLIC_S3_PUBLIC_URL=https://your-custom-domain.com\n```\n\n**AWS S3:**\n\n```env\nS3_ENDPOINT=https://s3.amazonaws.com\nS3_BUCKET_NAME=your-aws-bucket\nNEXT_PUBLIC_S3_PUBLIC_URL=https://your-bucket.s3.amazonaws.com\n```\n\n**DigitalOcean Spaces:**\n\n```env\nS3_ENDPOINT=https://nyc3.digitaloceanspaces.com\nS3_BUCKET_NAME=your-space-name\nNEXT_PUBLIC_S3_PUBLIC_URL=https://your-space.nyc3.digitaloceanspaces.com\n```\n\n**MinIO (Self-hosted):**\n\n```env\nS3_ENDPOINT=http://localhost:9000\nS3_BUCKET_NAME=your-minio-bucket\nNEXT_PUBLIC_S3_PUBLIC_URL=http://localhost:9000/your-bucket\n```\n\n**Wasabi:**\n\n```env\nS3_ENDPOINT=https://s3.wasabisys.com\nS3_BUCKET_NAME=your-wasabi-bucket\nNEXT_PUBLIC_S3_PUBLIC_URL=https://your-bucket.s3.wasabisys.com\n```\n\n#### ⚠️ Image loader note (when not using Cloudflare R2)\n\nIf you are **not using Cloudflare R2** (or you don’t want to use the Cloudflare-specific custom image loader), make sure to **remove or update** the following in `next.config.*`:\n\n```js\nloader: 'custom',\nloaderFile: './src/lib/cloudflare-image-loader.ts',\n```\n\nOtherwise, Next.js image optimization/loading may fail locally or in production.\n\n**Setup Instructions:**\n\n1. Choose your preferred S3-compatible storage provider\n2. Create an account and set up a bucket\n3. Generate API credentials (Access Key ID and Secret Access Key)\n4. Configure the endpoint URL for your provider\n5. (Optional) Setup custom domain for public access\n\n#### Mapbox Configuration\n\n```env\n# Mapbox access token\nNEXT_PUBLIC_MAPBOX_ACCESS_TOKEN=pk.your-mapbox-token\n```\n\n**Get Mapbox Token:**\n\n1. Create account at [mapbox.com](https://mapbox.com)\n2. Go to Account \u003e Access Tokens\n3. Create a new token with appropriate scopes\n\n#### Admin User Configuration\n\n```env\n# Default admin user for seeding\nSEED_USER_EMAIL=admin@yourdomain.com\nSEED_USER_PASSWORD=your-secure-password\nSEED_USER_NAME=Admin User\n```\n\n### Step 3: Database Setup\n\n```bash\n# Push database schema\nbun run db:push\n\n# Create admin user\nbun run seed:user\n```\n\n### Step 4: Build and Test Locally\n\n```bash\n# Build the application\nbun run build\n\n# Test the production build locally\nbun run start\n```\n\nVisit `http://localhost:3000` to verify everything works correctly.\n\n### Step 5: Deploy to Vercel\n\n#### Option A: Deploy via Vercel CLI\n\n```bash\n# Install Vercel CLI\nnpm i -g vercel\n\n# Login to Vercel\nvercel login\n\n# Deploy\nvercel --prod\n```\n\n#### Option B: Deploy via Git Integration\n\n1. Push your code to GitHub/GitLab/Bitbucket\n2. Connect your repository to Vercel\n3. Configure environment variables in Vercel dashboard\n4. Deploy automatically on push\n\n#### Vercel Environment Variables Setup\n\nIn your Vercel dashboard, add all environment variables from your `.env` file:\n\n1. Go to Project Settings \u003e Environment Variables\n2. Add each variable with appropriate values for production\n3. Make sure to update URLs to use your production domain\n\n### Step 6: Post-Deployment Setup\n\n#### Database Migration (if needed)\n\n```bash\n# If you need to run migrations on production\nvercel env pull .env.local\nbun run db:push\n```\n\n#### Create Admin User (Production)\n\n```bash\n# Seed admin user in production\nvercel env pull .env.local\nbun run seed:user\n```\n\n#### Upgrade: Photo URL storage change (S3 Key)\n\n- **Change Summary**\n\n  - The database `url` field now stores the S3 object key (e.g., `photos/IMG_0001.jpg`) instead of a full public URL.\n  - **Benefit**: You can switch domains or CDNs freely by updating environment variables for the public base URL, without mass-updating the database.\n\n- **Migration Steps (run before production, recommended)**\n\n  1. **Backup your database** (strongly recommended).\n  2. Run the cleanup script to convert existing full URLs to S3 keys:\n     ```bash\n     bun run clean:photo-urls\n     ```\n  3. **Verify the result**: spot-check several records; `url` should look like a key such as `path/to/object.jpg`.\n  4. If you need to rollback, run:\n     ```bash\n     bun run rollback:photo-urls\n     ```\n\n- **Notes**\n  - Ensure your public access domain is configured via `S3_PUBLIC_URL` or `NEXT_PUBLIC_S3_PUBLIC_URL`. At runtime, the app combines this base URL with the key to form a full public URL.\n  - If you have custom prefixes or multiple buckets, validate the script behavior in a staging environment first.\n\n## 🐳 Docker Deployment\n\nThis project supports two Docker deployment modes: **Standalone** (self-hosted) and **Cloud** (managed services).\n\n### 1. Standalone Mode (Default)\n\nRun the entire stack (App, PostgreSQL, RustFS) locally. Ideal for testing and self-hosting.\n\n```bash\ndocker compose up -d\n# or explicitly:\ndocker compose -f docker-compose.standalone.yml up -d\n```\n\n- **App**: http://localhost:3000\n- **Postgres**: localhost:5432\n- **RustFS (S3)**: localhost:9000\n- **RustFS Console**: http://localhost:9001\n\n### 2. Cloud Mode\n\nRun only the App container, connecting to external services (e.g., Neon Postgres, AWS S3, Cloudflare R2).\n\n1.  Create a `.env` file with your credentials:\n    ```bash\n    DATABASE_PROVIDER=cloud\n    DATABASE_URL=\"postgres://...\"\n    BETTER_AUTH_SECRET=\"...\"\n    S3_ACCESS_KEY_ID=\"...\"\n    S3_SECRET_ACCESS_KEY=\"...\"\n    S3_BUCKET_NAME=\"...\"\n    S3_ENDPOINT=\"...\" # Optional\n    S3_PUBLIC_URL=\"...\"\n    ```\n2.  Start the service:\n    ```bash\n    docker compose -f docker-compose.cloud.yml up -d\n    ```\n\n### ⚠️ Important Note on Building\n\nThis project uses a **Runtime Build** strategy for Docker. The Next.js application is built _inside_ the container when it starts, not when the Docker image is built.\nThis ensures that Static Site Generation (SSG) can successfully fetch data from the database (which is only available at runtime in the Standalone mode).\n\n- **First Start**: Will take 1-2 minutes to compile.\n- **Restarts**: Will be fast (cached via `next_cache` volume).\n\n## 🛠 Tech Stack\n\n### Custom Domain Setup\n\n1. **Vercel Custom Domain:**\n\n   - Go to Project Settings \u003e Domains\n   - Add your custom domain\n   - Configure DNS records as instructed\n\n2. **Update Environment Variables:**\n   ```env\n   BETTER_AUTH_URL=https://your-custom-domain.com\n   NEXT_PUBLIC_APP_URL=https://your-custom-domain.com\n   ```\n\n### Performance Optimization\n\n1. **Enable Vercel Analytics:**\n\n   ```bash\n   npm install @vercel/analytics\n   ```\n\n2. **Configure Image Optimization:**\n   - Ensure S3-compatible storage is properly configured\n   - Set up custom domain for your storage bucket\n   - Configure CDN settings (CloudFlare, AWS CloudFront, etc.)\n\n### Security Considerations\n\n1. **Environment Variables:**\n\n   - Never commit `.env` files\n   - Use strong, unique secrets\n   - Rotate keys regularly\n\n2. **Database Security:**\n\n   - Use connection pooling\n   - Enable SSL connections\n   - Restrict database access by IP\n\n3. **File Upload Security:**\n   - Configure proper CORS settings\n   - Implement file type validation\n   - Set upload size limits\n\n## 📱 Mobile Optimization\n\nThe application is fully responsive and optimized for mobile devices:\n\n- **Progressive Web App** features\n- **Touch-friendly** interface\n- **Optimized images** with lazy loading\n- **Fast loading** with Next.js optimizations\n\n## 🐛 Troubleshooting\n\n### Common Issues\n\n#### Build Errors\n\n```bash\n# Clear Next.js cache\nrm -rf .next\n\n# Reinstall dependencies\nrm -rf node_modules\nbun install\n```\n\n#### Database Connection Issues\n\n- Verify `DATABASE_URL` is correct\n- Check database server status\n- Ensure SSL settings match requirements\n\n#### Image Upload Issues\n\n- Verify S3 storage credentials\n- Check CORS settings on your storage bucket\n- Ensure bucket permissions are correct\n- Verify endpoint URL is correct for your provider\n\n#### Map Not Loading\n\n- Verify Mapbox token is valid\n- Check token permissions and scopes\n- Ensure domain is authorized in Mapbox settings\n\n## 📚 Additional Resources\n\n- [Next.js Documentation](https://nextjs.org/docs)\n- [Vercel Deployment Guide](https://vercel.com/docs)\n- [Drizzle ORM Documentation](https://orm.drizzle.team)\n- [Better Auth Documentation](https://better-auth.com)\n- [AWS S3 Documentation](https://docs.aws.amazon.com/s3/)\n- [Cloudflare R2 Documentation](https://developers.cloudflare.com/r2/)\n- [DigitalOcean Spaces Documentation](https://docs.digitalocean.com/products/spaces/)\n- [Mapbox Documentation](https://docs.mapbox.com)\n\n## 🤝 Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Test thoroughly\n5. Submit a pull request\n\n## 💖 Support\n\nIf you find this project helpful, please give it a ⭐️ on GitHub!\n\n## ⭐️ Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=ECarry/photography-website\u0026type=Date)](https://star-history.com/#ECarry/photography-website\u0026Date)\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\n---\n\n**Need help?** Check the troubleshooting section above or open an issue in the repository.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fecarry%2Fphotography-website","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fecarry%2Fphotography-website","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fecarry%2Fphotography-website/lists"}