# 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** - Open http://localhost:5173, log in, connect Google Drive, create an event, copy the upload URL - Share the upload URL (http://localhost:5174/events/{slug}); attendees upload there ## 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): ```bash docker compose up -d ``` Wait for the healthcheck (a few seconds), then continue. 2. **Configure the API** (`api/.env`): ```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): ```bash 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): ```bash 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`): ```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): ```bash 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: ```bash ./start.sh ``` Starts API (8000), queue worker, admin (5173), and upload (5174). Press **Ctrl+C** to stop all. If you use Docker for MySQL: ```bash ./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 ```bash # 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](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: ```bash cd api && php artisan queue:work ``` You can verify the queue worker is processing jobs by checking the logs or running: ```bash 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.