2026-02-03 10:38:46 +01:00
2026-02-03 10:38:46 +01:00
2026-02-03 10:38:46 +01:00
2026-02-03 10:38:46 +01:00
2026-02-03 10:38:46 +01:00
2026-02-03 10:38:46 +01:00
2026-02-03 10:38:46 +01:00
2026-02-03 10:38:46 +01:00

Event Video Upload Application

Web application for event organizers to collect video uploads from attendees. Admins create events with shareable URLs; attendees upload videos to Google Drive folders per event.

Stack

  • Backend: Laravel 12 (PHP 8.3+), MySQL 8.0+, Sanctum, Google Drive API v3, Queue (database)
  • Admin: Vue 3 + TypeScript + Vite + Bootstrap 5
  • Upload: Vue 3 + TypeScript + Vite + Tailwind CSS + Uppy

Getting Started

  1. Clone and install

    • Backend: cd api && composer install
    • Admin: cd admin && npm install
    • Upload: cd upload && npm install
  2. Configure

    • Copy api/.env.example to api/.env
    • Set MySQL (DB_*), SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:5174, SESSION_DOMAIN=localhost
    • Set Google OAuth: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI
    • Run php artisan key:generate in api/
  3. Database

    • Create MySQL database (e.g. event_uploader)
    • In api/: php artisan migrate
  4. Admin user

    • cd api && php artisan make:admin --email=admin@example.com --password=secret
  5. Run (requires 4 separate terminal windows)

    • API: cd api && php artisan serve
    • Queue Worker (REQUIRED for uploads): cd api && php artisan queue:work
    • Admin: cd admin && npm run dev (port 5173)
    • Upload: cd upload && npm run dev (port 5174)

    ⚠️ Important: The queue worker MUST be running for file uploads to process to Google Drive. Without it, uploads will stay in "pending" status.

  6. Use

MySQL: Docker vs Homebrew

Docker Homebrew
Setup Requires Docker Desktop. One docker run or docker compose up. brew install mysql and create DB.
Best for Same MySQL version everywhere; no system install; easy reset. Quick local use; no Docker needed.
Reset docker compose down -v for a clean DB. Manual drop/recreate or reinstall.

Recommendation: Use Docker if you already have it or want a reproducible setup; use Homebrew if you prefer a single local MySQL and no containers.


Option A: MySQL with Docker

A docker-compose.yml in the project root runs only MySQL (API and frontends run on your machine).

  1. Start MySQL (from project root):

    docker compose up -d
    

    Wait for the healthcheck (a few seconds), then continue.

  2. Configure the API (api/.env):

    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=event_uploader
    DB_USERNAME=root
    DB_PASSWORD=secret
    APP_URL=http://localhost:8000
    SESSION_DOMAIN=localhost
    SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:5174
    
  3. Then: cd api && php artisan migrate && php artisan make:admin --email=admin@test.com --password=secret, and run API, queue, admin, and upload (see step 4 in Option B).

Stop: docker compose down
Stop and wipe DB: docker compose down -v


Option B: MySQL with Homebrew (macOS)

  1. Install MySQL (if needed):

    brew install mysql
    brew services start mysql
    

    Or run once: mysql.server start.

  2. Create the database and user (optional; root with no password works for local):

    mysql -u root -e "CREATE DATABASE IF NOT EXISTS event_uploader;"
    

    If MySQL has a root password, use -p and enter it.

  3. Configure the API (api/.env):

    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=event_uploader
    DB_USERNAME=root
    DB_PASSWORD=
    APP_URL=http://localhost:8000
    SESSION_DOMAIN=localhost
    SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:5174
    

    Set DB_PASSWORD if your MySQL user has a password.

  4. Bootstrap and run (from project root):

    cd api && composer install && php artisan key:generate && php artisan migrate
    php artisan make:admin --email=admin@test.com --password=secret
    php artisan serve &
    php artisan queue:work &
    cd ../admin && npm install && npm run dev &
    cd ../upload && npm install && npm run dev
    

    Or run each in a separate terminal: API (port 8000), queue worker, admin (5173), upload (5174).

  5. Test the flow:

    • Open http://localhost:5173, log in as admin@test.com / secret.
    • Create an event (Google Drive can be skipped at first; you can set it up later).
    • Copy the upload URL and open it in another tab (e.g. http://localhost:5174/events/your-slug).
    • Upload a small test file; the queue worker will process it (Google Drive upload will fail until OAuth is configured).

Tip: Keep the queue worker running in a dedicated terminal so uploads are processed. Without it, files stay in "pending" until the job runs.

Start everything (one command)

From the project root:

./start.sh

Starts API (8000), queue worker, admin (5173), and upload (5174). Press Ctrl+C to stop all.

If you use Docker for MySQL:

./start.sh --with-docker

(Starts docker compose up -d first, then the four apps.)

First time: run chmod +x start.sh if the script is not executable.

Development Commands

# Backend
cd api && composer install && php artisan migrate && php artisan serve

# Admin
cd admin && npm install && npm run dev

# Upload
cd upload && npm install && npm run dev

# Queue
cd api && php artisan queue:work

# Create admin
cd api && php artisan make:admin --email=admin@example.com --password=secret

# Tests
cd api && ./vendor/bin/pest

Pushing to Gitea

This repo pushes to Gitea (1Password SSH key). One-time setup:

  1. Run from project root: ./scripts/setup-gitea-ssh.sh (creates ~/.ssh/gitea-1password-only).
  2. In 1Password: Settings → Developer → enable Use the SSH agent.

Then you can push from Cursor or run ./gitea-push.sh. For commit signing and first-push tips, see GITEA-SETUP.md.

Project Structure

  • api/ Laravel API (auth, events, Google Drive, public upload)
  • admin/ Vue 3 admin SPA (events, uploads, Google Drive)
  • upload/ Vue 3 public upload SPA (event page, password gate, Uppy)

Troubleshooting

Uploads stuck in "pending" status

Problem: Files upload successfully but stay in "pending" status and never appear in Google Drive.

Solution: The queue worker is not running. Start it with:

cd api && php artisan queue:work

You can verify the queue worker is processing jobs by checking the logs or running:

cd api && php artisan queue:failed  # Check for failed jobs

419 Page Expired error on upload

Problem: Getting a 419 error when uploading files from the public upload page.

Solution: This was fixed by excluding public upload endpoints from CSRF verification. Make sure your api/bootstrap/app.php includes the CSRF exception configuration.

Google Drive callback fails with "Route [login] not defined"

Problem: After authorizing Google Drive, you get redirected to an error page.

Solution: This was fixed by moving the Google Drive callback route outside the auth:sanctum middleware group and using web middleware instead. The callback now handles authentication internally.

No folders appear when selecting a Shared Drive

Problem: When you click on a Shared Drive, you see all folders from all drives instead of just that drive's folders.

Solution: This was fixed by updating the Google Drive API query to use '{$driveId}' in parents when listing root folders of a Shared Drive.

Description
No description provided
Readme 232 KiB
Languages
PHP 38.8%
Blade 30.9%
Vue 20%
TypeScript 5.8%
CSS 2%
Other 2.5%